core, ui: short bio/description for contact and group profiles (#6055)

* core: short bio/description for contact and group profiles

* use short description in commands

* fix tests

* query plans

* ios: show contact and group profile descriptions

* android, desktop: short description/bio

* ui: layout for chat info, correct copy

* comment out fields to add/edit short descriptions

* remove short description from events

* postgres schema
This commit is contained in:
Evgeny
2025-07-11 21:28:10 +01:00
committed by GitHub
parent a7f8866360
commit a5768e903d
55 changed files with 810 additions and 563 deletions
+47 -37
View File
@@ -111,7 +111,7 @@ struct ChatInfoView: View {
@State private var sendReceiptsUserDefault = true
@State private var progressIndicator = false
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
enum ChatInfoViewAlert: Identifiable {
case clearChatAlert
case networkStatusAlert
@@ -135,7 +135,7 @@ struct ChatInfoView: View {
}
}
}
var body: some View {
NavigationView {
ZStack {
@@ -146,12 +146,12 @@ struct ChatInfoView: View {
.onTapGesture {
aliasTextFieldFocused = false
}
localAliasTextEdit()
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
.padding(.bottom, 18)
GeometryReader { g in
HStack(alignment: .center, spacing: 8) {
let buttonWidth = g.size.width / 4
@@ -169,7 +169,7 @@ struct ChatInfoView: View {
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 8))
if let customUserProfile = customUserProfile {
Section(header: Text("Incognito").foregroundColor(theme.colors.secondary)) {
HStack {
@@ -180,7 +180,7 @@ struct ChatInfoView: View {
}
}
}
Section {
if let code = connectionCode { verifyCodeButton(code) }
contactPreferencesButton()
@@ -203,19 +203,19 @@ struct ChatInfoView: View {
// }
}
.disabled(!contact.ready || !contact.active)
Section {
ChatTTLOption(chat: chat, progressIndicator: $progressIndicator)
} footer: {
Text("Delete chat messages from your device.")
}
if let conn = contact.activeConn {
Section {
infoRow(Text(String("E2E encryption")), conn.connPQEnabled ? "Quantum resistant" : "Standard")
}
}
if let contactLink = contact.contactLink {
Section {
SimpleXLinkQRCode(uri: contactLink)
@@ -232,7 +232,7 @@ struct ChatInfoView: View {
.foregroundColor(theme.colors.secondary)
}
}
if contact.ready && contact.active {
Section(header: Text("Servers").foregroundColor(theme.colors.secondary)) {
networkStatusRow()
@@ -261,12 +261,12 @@ struct ChatInfoView: View {
}
}
}
Section {
clearChatButton()
deleteContactButton()
}
if developerTools {
Section(header: Text("For console").foregroundColor(theme.colors.secondary)) {
infoRow("Local name", chat.chatInfo.localDisplayName)
@@ -291,7 +291,7 @@ struct ChatInfoView: View {
.navigationBarHidden(true)
.disabled(progressIndicator)
.opacity(progressIndicator ? 0.6 : 1)
if progressIndicator {
ProgressView().scaleEffect(2)
}
@@ -303,7 +303,7 @@ struct ChatInfoView: View {
sendReceiptsUserDefault = currentUser.sendRcptsContacts
}
sendReceipts = SendReceipts.fromBool(contact.chatSettings.sendRcpts, userDefault: sendReceiptsUserDefault)
Task {
do {
let (stats, profile) = try await apiContactInfo(chat.chatInfo.apiId)
@@ -342,7 +342,7 @@ struct ChatInfoView: View {
}
}
.actionSheet(item: $actionSheet) { $0.actionSheet }
.sheet(item: $sheet) {
.sheet(item: $sheet) {
if #available(iOS 16.0, *) {
$0.content
.presentationDetents([.fraction($0.fraction)])
@@ -361,41 +361,51 @@ struct ChatInfoView: View {
}
}
}
private func contactInfoHeader() -> some View {
VStack(spacing: 8) {
let cInfo = chat.chatInfo
ChatInfoImage(chat: chat, size: 192, color: Color(uiColor: .tertiarySystemFill))
.padding(.vertical, 12)
// show actual display name, alias can be edited in this view
let displayName = contact.profile.displayName.trimmingCharacters(in: .whitespacesAndNewlines)
let fullName = cInfo.fullName.trimmingCharacters(in: .whitespacesAndNewlines)
if contact.verified {
(
Text(Image(systemName: "checkmark.shield"))
.foregroundColor(theme.colors.secondary)
.font(.title2)
+ textSpace
+ Text(contact.profile.displayName)
+ Text(displayName)
.font(.largeTitle)
)
.multilineTextAlignment(.center)
.lineLimit(2)
.padding(.bottom, 2)
} else {
Text(contact.profile.displayName)
Text(displayName)
.font(.largeTitle)
.multilineTextAlignment(.center)
.lineLimit(2)
.padding(.bottom, 2)
}
if cInfo.fullName != "" && cInfo.fullName != cInfo.displayName && cInfo.fullName != contact.profile.displayName {
if fullName != "" && fullName != displayName && fullName != cInfo.displayName.trimmingCharacters(in: .whitespacesAndNewlines) {
Text(cInfo.fullName)
.font(.title2)
.multilineTextAlignment(.center)
.lineLimit(3)
.padding(.bottom, 2)
}
if let descr = cInfo.shortDescr?.trimmingCharacters(in: .whitespacesAndNewlines), descr != "" {
Text(descr)
.font(.subheadline)
.multilineTextAlignment(.center)
.lineLimit(4)
}
}
.frame(maxWidth: .infinity, alignment: .center)
}
private func localAliasTextEdit() -> some View {
TextField("Set contact name…", text: $localAlias)
.disableAutocorrection(true)
@@ -412,7 +422,7 @@ struct ChatInfoView: View {
.multilineTextAlignment(.center)
.foregroundColor(theme.colors.secondary)
}
private func setContactAlias() {
Task {
do {
@@ -475,7 +485,7 @@ struct ChatInfoView: View {
)
}
}
private func contactPreferencesButton() -> some View {
NavigationLink {
ContactPreferencesView(
@@ -491,7 +501,7 @@ struct ChatInfoView: View {
Label("Contact preferences", systemImage: "switch.2")
}
}
private func sendReceiptsOption() -> some View {
WrappedPicker(selection: $sendReceipts) {
ForEach([.yes, .no, .userDefault(sendReceiptsUserDefault)]) { (opt: SendReceipts) in
@@ -504,7 +514,7 @@ struct ChatInfoView: View {
setSendReceipts()
}
}
private func setSendReceipts() {
var chatSettings = chat.chatInfo.chatSettings ?? ChatSettings.defaults
chatSettings.sendRcpts = sendReceipts.bool()
@@ -524,7 +534,7 @@ struct ChatInfoView: View {
.foregroundColor(.orange)
}
}
private func synchronizeConnectionButtonForce() -> some View {
Button {
alert = .syncConnectionForceAlert
@@ -533,7 +543,7 @@ struct ChatInfoView: View {
.foregroundColor(.red)
}
}
private func networkStatusRow() -> some View {
HStack {
Text("Network status")
@@ -546,14 +556,14 @@ struct ChatInfoView: View {
serverImage()
}
}
private func serverImage() -> some View {
let status = networkModel.contactNetworkStatus(contact)
return Image(systemName: status.imageName)
.foregroundColor(status == .connected ? .green : theme.colors.secondary)
.font(.system(size: 12))
}
private func deleteContactButton() -> some View {
Button(role: .destructive) {
deleteContactDialog(
@@ -569,7 +579,7 @@ struct ChatInfoView: View {
.foregroundColor(Color.red)
}
}
private func clearChatButton() -> some View {
Button() {
alert = .clearChatAlert
@@ -578,7 +588,7 @@ struct ChatInfoView: View {
.foregroundColor(Color.orange)
}
}
private func clearChatAlert() -> Alert {
Alert(
title: Text("Clear conversation?"),
@@ -592,14 +602,14 @@ struct ChatInfoView: View {
secondaryButton: .cancel()
)
}
private func networkStatusAlert() -> Alert {
Alert(
title: Text("Network status"),
message: Text(networkModel.contactNetworkStatus(contact).statusExplanation)
)
}
private func switchContactAddress() {
Task {
do {
@@ -618,7 +628,7 @@ struct ChatInfoView: View {
}
}
}
private func abortSwitchContactAddress() {
Task {
do {
@@ -636,7 +646,7 @@ struct ChatInfoView: View {
}
}
}
private func savePreferences() {
Task {
do {
@@ -668,7 +678,7 @@ struct ChatTTLOption: View {
}
let defaultTTL = ChatTTL.userDefault(ChatModel.shared.chatItemTTL)
Text(defaultTTL.text).tag(defaultTTL)
if case .chat(let ttl) = chatItemTTL, case .seconds = ttl {
Text(ttl.deleteAfterText).tag(chatItemTTL)
}
@@ -1136,13 +1146,13 @@ func setChatTTL(_ ttl: ChatTTL, hasPreviousTTL: Bool, onCancel: @escaping () ->
} else {
NSLocalizedString("Enable automatic message deletion?", comment: "alert title")
}
let message = if ttl.neverExpires {
NSLocalizedString("Messages in this chat will never be deleted.", comment: "alert message")
} else {
NSLocalizedString("This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted.", comment: "alert message")
}
showAlert(title, message: message) {
[
UIAlertAction(
@@ -75,12 +75,12 @@ struct GroupChatInfoView: View {
List {
groupInfoHeader()
.listRowBackground(Color.clear)
localAliasTextEdit()
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
.padding(.bottom, 18)
infoActionButtons()
.padding(.horizontal)
.frame(maxWidth: .infinity)
@@ -124,7 +124,7 @@ struct GroupChatInfoView: View {
Text(label)
.foregroundColor(theme.colors.secondary)
}
Section {
if members.filter({ $0.wrapped.memberCurrent }).count <= SMALL_GROUPS_RCPS_MEM_LIMIT {
sendReceiptsOption()
@@ -181,7 +181,7 @@ struct GroupChatInfoView: View {
leaveGroupButton()
}
}
if developerTools {
Section(header: Text("For console").foregroundColor(theme.colors.secondary)) {
infoRow("Local name", chat.chatInfo.localDisplayName)
@@ -193,7 +193,7 @@ struct GroupChatInfoView: View {
.navigationBarHidden(true)
.disabled(progressIndicator)
.opacity(progressIndicator ? 0.6 : 1)
if progressIndicator {
ProgressView().scaleEffect(2)
}
@@ -234,19 +234,29 @@ struct GroupChatInfoView: View {
private func groupInfoHeader() -> some View {
VStack {
let cInfo = chat.chatInfo
// show actual display name, alias can be edited in this view
let displayName = (cInfo.groupInfo?.groupProfile.displayName ?? cInfo.displayName).trimmingCharacters(in: .whitespacesAndNewlines)
let fullName = cInfo.fullName.trimmingCharacters(in: .whitespacesAndNewlines)
ChatInfoImage(chat: chat, size: 192, color: Color(uiColor: .tertiarySystemFill))
.padding(.top, 12)
.padding()
Text(cInfo.groupInfo?.groupProfile.displayName ?? cInfo.displayName)
Text(displayName)
.font(.largeTitle)
.multilineTextAlignment(.center)
.lineLimit(4)
.padding(.bottom, 2)
if cInfo.fullName != "" && cInfo.fullName != cInfo.displayName {
if fullName != "" && fullName != displayName && fullName != cInfo.displayName.trimmingCharacters(in: .whitespacesAndNewlines) {
Text(cInfo.fullName)
.font(.title2)
.multilineTextAlignment(.center)
.lineLimit(8)
.lineLimit(3)
.padding(.bottom, 2)
}
if let descr = cInfo.shortDescr?.trimmingCharacters(in: .whitespacesAndNewlines), descr != "" {
Text(descr)
.font(.subheadline)
.multilineTextAlignment(.center)
.lineLimit(4)
}
}
.frame(maxWidth: .infinity, alignment: .center)
@@ -268,7 +278,7 @@ struct GroupChatInfoView: View {
.multilineTextAlignment(.center)
.foregroundColor(theme.colors.secondary)
}
private func setGroupAlias() {
Task {
do {
@@ -282,7 +292,7 @@ struct GroupChatInfoView: View {
}
}
}
func infoActionButtons() -> some View {
GeometryReader { g in
let buttonWidth = g.size.width / 4
@@ -829,11 +839,11 @@ struct GroupPreferencesButton: View {
@State var preferences: FullGroupPreferences
@State var currentPreferences: FullGroupPreferences
var creatingGroup: Bool = false
private var label: LocalizedStringKey {
groupInfo.businessChat == nil ? "Group preferences" : "Chat preferences"
}
var body: some View {
NavigationLink {
GroupPreferencesView(
@@ -851,7 +861,7 @@ struct GroupPreferencesButton: View {
creatingGroup ? "Save" : "Save and notify group members",
comment: "alert button"
)
if groupInfo.fullGroupPreferences != preferences {
showAlert(
title: NSLocalizedString("Save preferences?", comment: "alert title"),
@@ -869,7 +879,7 @@ struct GroupPreferencesButton: View {
}
}
}
private func savePreferences() {
Task {
do {
@@ -444,29 +444,39 @@ struct GroupMemberInfoView: View {
MemberProfileImage(mem, size: 192, color: Color(uiColor: .tertiarySystemFill))
.padding(.top, 12)
.padding()
// show alias if set, alias cannot be edited in this view
let displayName = mem.displayName.trimmingCharacters(in: .whitespacesAndNewlines)
let fullName = mem.fullName.trimmingCharacters(in: .whitespacesAndNewlines)
if mem.verified {
(
Text(Image(systemName: "checkmark.shield"))
.foregroundColor(theme.colors.secondary)
.font(.title2)
+ textSpace
+ Text(mem.displayName)
+ Text(displayName)
.font(.largeTitle)
)
.multilineTextAlignment(.center)
.lineLimit(2)
.padding(.bottom, 2)
} else {
Text(mem.displayName)
Text(displayName)
.font(.largeTitle)
.multilineTextAlignment(.center)
.lineLimit(2)
.padding(.bottom, 2)
}
if mem.fullName != "" && mem.fullName != mem.displayName {
if fullName != "" && fullName != displayName && fullName != mem.memberProfile.displayName.trimmingCharacters(in: .whitespacesAndNewlines) {
Text(mem.fullName)
.font(.title2)
.multilineTextAlignment(.center)
.lineLimit(3)
.padding(.bottom, 2)
}
if let descr = mem.memberProfile.shortDescr?.trimmingCharacters(in: .whitespacesAndNewlines), descr != "" {
Text(descr)
.font(.subheadline)
.multilineTextAlignment(.center)
.lineLimit(4)
}
}
@@ -55,6 +55,8 @@ struct GroupProfileView: View {
if fullName != "" && fullName != groupProfile.displayName {
TextField("Group full name (optional)", text: $groupProfile.fullName)
}
// TODO enable in v6.4.1, limit to 160 characters
// TextField("Short description", text: Binding(get: {groupProfile.shortDescr ?? ""}, set: {groupProfile.shortDescr = $0}))
} footer: {
Text("Group profile is stored on members' devices, not on the servers.")
}
@@ -29,6 +29,7 @@ struct CreateProfile: View {
@Environment(\.dismiss) var dismiss
@EnvironmentObject var theme: AppTheme
@State private var displayName: String = ""
@State private var profileBio: String = ""
@FocusState private var focusDisplayName
@State private var alert: UserProfileAlert?
@@ -37,6 +38,8 @@ struct CreateProfile: View {
Section {
TextField("Enter your name…", text: $displayName)
.focused($focusDisplayName)
// TODO enable in v6.4.1, limit to 160 characters
// TextField("Bio", text: $profileBio)
Button {
createProfile()
} label: {
@@ -80,9 +83,11 @@ struct CreateProfile: View {
private func createProfile() {
hideKeyboard()
let shortDescr: String? = if profileBio.isEmpty { nil } else { profileBio }
let profile = Profile(
displayName: displayName.trimmingCharacters(in: .whitespaces),
fullName: ""
fullName: "",
shortDescr: shortDescr
)
let m = ChatModel.shared
do {
@@ -43,6 +43,8 @@ struct UserProfile: View {
if let user = chatModel.currentUser, showFullName(user) {
TextField("Full name (optional)", text: $profile.fullName)
}
// TODO enable in v6.4.1, limit to 160 characters
// TextField("Bio", text: Binding(get: {profile.shortDescr ?? ""}, set: {profile.shortDescr = $0}))
} footer: {
Text("Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile.")
}
+8 -8
View File
@@ -178,8 +178,8 @@
64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */; };
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829982D54AEED006B9E89 /* libgmp.a */; };
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829992D54AEEE006B9E89 /* libffi.a */; };
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-Hjmcl0BGDg19a5be1DeLnp-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-Hjmcl0BGDg19a5be1DeLnp-ghc9.6.3.a */; };
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-Hjmcl0BGDg19a5be1DeLnp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-Hjmcl0BGDg19a5be1DeLnp.a */; };
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-H9b1PUqJfdC45BY0VzYHX-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-H9b1PUqJfdC45BY0VzYHX-ghc9.6.3.a */; };
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-H9b1PUqJfdC45BY0VzYHX.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-H9b1PUqJfdC45BY0VzYHX.a */; };
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */; };
64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */; };
64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */; };
@@ -543,8 +543,8 @@
64C3B0202A0D359700E19930 /* CustomTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimePicker.swift; sourceTree = "<group>"; };
64C829982D54AEED006B9E89 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
64C829992D54AEEE006B9E89 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-Hjmcl0BGDg19a5be1DeLnp-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.5.1-Hjmcl0BGDg19a5be1DeLnp-ghc9.6.3.a"; sourceTree = "<group>"; };
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-Hjmcl0BGDg19a5be1DeLnp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.5.1-Hjmcl0BGDg19a5be1DeLnp.a"; sourceTree = "<group>"; };
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-H9b1PUqJfdC45BY0VzYHX-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.5.1-H9b1PUqJfdC45BY0VzYHX-ghc9.6.3.a"; sourceTree = "<group>"; };
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-H9b1PUqJfdC45BY0VzYHX.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.5.1-H9b1PUqJfdC45BY0VzYHX.a"; sourceTree = "<group>"; };
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
64D0C2BF29F9688300B38D5F /* UserAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressView.swift; sourceTree = "<group>"; };
64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressLearnMore.swift; sourceTree = "<group>"; };
@@ -704,8 +704,8 @@
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */,
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */,
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */,
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-Hjmcl0BGDg19a5be1DeLnp-ghc9.6.3.a in Frameworks */,
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-Hjmcl0BGDg19a5be1DeLnp.a in Frameworks */,
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-H9b1PUqJfdC45BY0VzYHX-ghc9.6.3.a in Frameworks */,
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-H9b1PUqJfdC45BY0VzYHX.a in Frameworks */,
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -790,8 +790,8 @@
64C829992D54AEEE006B9E89 /* libffi.a */,
64C829982D54AEED006B9E89 /* libgmp.a */,
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */,
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-Hjmcl0BGDg19a5be1DeLnp-ghc9.6.3.a */,
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-Hjmcl0BGDg19a5be1DeLnp.a */,
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-H9b1PUqJfdC45BY0VzYHX-ghc9.6.3.a */,
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.5.1-H9b1PUqJfdC45BY0VzYHX.a */,
);
path = Libraries;
sourceTree = "<group>";
+45 -2
View File
@@ -32,6 +32,7 @@ public struct User: Identifiable, Decodable, UserLike, NamedChat, Hashable {
public var displayName: String { get { profile.displayName } }
public var fullName: String { get { profile.fullName } }
public var shortDescr: String? { profile.shortDescr }
public var image: String? { get { profile.image } }
public var localAlias: String { get { "" } }
@@ -109,12 +110,14 @@ public struct Profile: Codable, NamedChat, Hashable {
public init(
displayName: String,
fullName: String,
shortDescr: String? = nil,
image: String? = nil,
contactLink: String? = nil,
preferences: Preferences? = nil
) {
self.displayName = displayName
self.fullName = fullName
self.shortDescr = shortDescr
self.image = image
self.contactLink = contactLink
self.preferences = preferences
@@ -122,6 +125,7 @@ public struct Profile: Codable, NamedChat, Hashable {
public var displayName: String
public var fullName: String
public var shortDescr: String?
public var image: String?
public var contactLink: String?
public var preferences: Preferences?
@@ -142,6 +146,7 @@ public struct LocalProfile: Codable, NamedChat, Hashable {
profileId: Int64,
displayName: String,
fullName: String,
shortDescr: String? = nil,
image: String? = nil,
contactLink: String? = nil,
preferences: Preferences? = nil,
@@ -150,6 +155,7 @@ public struct LocalProfile: Codable, NamedChat, Hashable {
self.profileId = profileId
self.displayName = displayName
self.fullName = fullName
self.shortDescr = shortDescr
self.image = image
self.contactLink = contactLink
self.preferences = preferences
@@ -159,6 +165,7 @@ public struct LocalProfile: Codable, NamedChat, Hashable {
public var profileId: Int64
public var displayName: String
public var fullName: String
public var shortDescr: String?
public var image: String?
public var contactLink: String?
public var preferences: Preferences?
@@ -180,11 +187,27 @@ public struct LocalProfile: Codable, NamedChat, Hashable {
}
public func toLocalProfile (_ profileId: Int64, _ profile: Profile, _ localAlias: String) -> LocalProfile {
LocalProfile(profileId: profileId, displayName: profile.displayName, fullName: profile.fullName, image: profile.image, contactLink: profile.contactLink, preferences: profile.preferences, localAlias: localAlias)
LocalProfile(
profileId: profileId,
displayName: profile.displayName,
fullName: profile.fullName,
shortDescr: profile.shortDescr,
image: profile.image,
contactLink: profile.contactLink,
preferences: profile.preferences,
localAlias: localAlias
)
}
public func fromLocalProfile (_ profile: LocalProfile) -> Profile {
Profile(displayName: profile.displayName, fullName: profile.fullName, image: profile.image, contactLink: profile.contactLink, preferences: profile.preferences)
Profile(
displayName: profile.displayName,
fullName: profile.fullName,
shortDescr: profile.shortDescr,
image: profile.image,
contactLink: profile.contactLink,
preferences: profile.preferences
)
}
public struct UserProfileUpdateSummary: Decodable, Hashable {
@@ -204,6 +227,7 @@ public enum ChatType: String, Hashable {
public protocol NamedChat {
var displayName: String { get }
var fullName: String { get }
var shortDescr: String? { get }
var image: String? { get }
var localAlias: String { get }
}
@@ -1249,6 +1273,17 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
}
}
public var shortDescr: String? {
switch self {
case let .direct(contact): contact.profile.shortDescr
case let .group(groupInfo, _): groupInfo.groupProfile.shortDescr
case .local: nil
case let .contactRequest(contactRequest): contactRequest.profile.shortDescr
case let .contactConnection(contactConnection): nil
case .invalidJSON: nil
}
}
public var image: String? {
get {
switch self {
@@ -1739,6 +1774,7 @@ public struct Contact: Identifiable, Decodable, NamedChat, Hashable {
public var sendMsgToConnect: Bool { nextSendGrpInv || nextConnectPrepared }
public var displayName: String { localAlias == "" ? profile.displayName : localAlias }
public var fullName: String { get { profile.fullName } }
public var shortDescr: String? { profile.shortDescr }
public var image: String? { get { profile.image } }
public var contactLink: String? { get { profile.contactLink } }
public var localAlias: String { profile.localAlias }
@@ -1925,6 +1961,7 @@ public struct UserContactRequest: Decodable, NamedChat, Hashable {
public var apiId: Int64 { get { contactRequestId } }
var ready: Bool { get { true } }
public var displayName: String { get { profile.displayName } }
public var shortDescr: String? { profile.shortDescr }
public var fullName: String { get { profile.fullName } }
public var image: String? { get { profile.image } }
public var localAlias: String { "" }
@@ -1977,6 +2014,7 @@ public struct PendingContactConnection: Decodable, NamedChat, Hashable {
}
}
public var fullName: String { get { "" } }
public var shortDescr: String? { nil }
public var image: String? { get { nil } }
public var initiated: Bool { get { (pccConnStatus.initiated ?? false) && !viaContactUri } }
@@ -2093,6 +2131,7 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat, Hashable {
public var profileChangeProhibited: Bool { preparedGroup?.connLinkPreparedConnection ?? false }
public var displayName: String { localAlias == "" ? groupProfile.displayName : localAlias }
public var fullName: String { get { groupProfile.fullName } }
public var shortDescr: String? { groupProfile.shortDescr }
public var image: String? { get { groupProfile.image } }
public var chatTags: [Int64]
public var chatItemTTL: Int64?
@@ -2152,6 +2191,7 @@ public struct GroupProfile: Codable, NamedChat, Hashable {
public init(
displayName: String,
fullName: String,
shortDescr: String? = nil,
description: String? = nil,
image: String? = nil,
groupPreferences: GroupPreferences? = nil,
@@ -2159,6 +2199,7 @@ public struct GroupProfile: Codable, NamedChat, Hashable {
) {
self.displayName = displayName
self.fullName = fullName
self.shortDescr = shortDescr
self.description = description
self.image = image
self.groupPreferences = groupPreferences
@@ -2167,6 +2208,7 @@ public struct GroupProfile: Codable, NamedChat, Hashable {
public var displayName: String
public var fullName: String
public var shortDescr: String?
public var description: String?
public var image: String?
public var groupPreferences: GroupPreferences?
@@ -2555,6 +2597,7 @@ public struct NoteFolder: Identifiable, Decodable, NamedChat, Hashable {
public var ready: Bool { get { true } }
public var displayName: String { get { ChatInfo.privateNotesChatName } }
public var fullName: String { get { "" } }
public var shortDescr: String? { nil }
public var image: String? { get { nil } }
public var localAlias: String { get { "" } }
@@ -1233,6 +1233,7 @@ data class User(
): NamedChat, UserLike {
override val displayName: String get() = profile.displayName
override val fullName: String get() = profile.fullName
override val shortDescr: String? get() = profile.shortDescr
override val image: String? get() = profile.image
override val localAlias: String = ""
@@ -1302,6 +1303,7 @@ typealias ChatId = String
interface NamedChat {
val displayName: String
val fullName: String
val shortDescr: String?
val image: String?
val localAlias: String
val chatViewName: String
@@ -1412,6 +1414,7 @@ sealed class ChatInfo: SomeChat, NamedChat {
override val updatedAt get() = contact.updatedAt
override val displayName get() = contact.displayName
override val fullName get() = contact.fullName
override val shortDescr get() = contact.profile.shortDescr
override val image get() = contact.image
override val localAlias: String get() = contact.localAlias
override fun anyNameContains(searchAnyCase: String): Boolean = contact.anyNameContains(searchAnyCase)
@@ -1439,6 +1442,7 @@ sealed class ChatInfo: SomeChat, NamedChat {
override val updatedAt get() = groupInfo.updatedAt
override val displayName get() = groupInfo.displayName
override val fullName get() = groupInfo.fullName
override val shortDescr get() = groupInfo.groupProfile.shortDescr
override val image get() = groupInfo.image
override val localAlias get() = groupInfo.localAlias
@@ -1465,6 +1469,7 @@ sealed class ChatInfo: SomeChat, NamedChat {
override val updatedAt get() = noteFolder.updatedAt
override val displayName get() = noteFolder.displayName
override val fullName get() = noteFolder.fullName
override val shortDescr get() = null
override val image get() = noteFolder.image
override val localAlias get() = noteFolder.localAlias
@@ -1491,6 +1496,7 @@ sealed class ChatInfo: SomeChat, NamedChat {
override val updatedAt get() = contactRequest.updatedAt
override val displayName get() = contactRequest.displayName
override val fullName get() = contactRequest.fullName
override val shortDescr get() = contactRequest.profile.shortDescr
override val image get() = contactRequest.image
override val localAlias get() = contactRequest.localAlias
@@ -1517,6 +1523,7 @@ sealed class ChatInfo: SomeChat, NamedChat {
override val updatedAt get() = contactConnection.updatedAt
override val displayName get() = contactConnection.displayName
override val fullName get() = contactConnection.fullName
override val shortDescr get() = null
override val image get() = contactConnection.image
override val localAlias get() = contactConnection.localAlias
@@ -1546,6 +1553,7 @@ sealed class ChatInfo: SomeChat, NamedChat {
override val timedMessagesTTL: Int? get() = null
override val displayName get() = invalidChatName
override val fullName get() = invalidChatName
override val shortDescr get() = null
override val image get() = null
override val localAlias get() = ""
@@ -1750,6 +1758,7 @@ data class Contact(
override val timedMessagesTTL: Int? get() = with(mergedPreferences.timedMessages) { if (enabled.forUser) userPreference.pref.ttl else null }
override val displayName get() = localAlias.ifEmpty { profile.displayName }
override val fullName get() = profile.fullName
override val shortDescr get() = profile.shortDescr
override val image get() = profile.image
val contactLink: String? = profile.contactLink
override val localAlias get() = profile.localAlias
@@ -1900,6 +1909,7 @@ data class SecurityCode(val securityCode: String, val verifiedAt: Instant)
data class Profile(
override val displayName: String,
override val fullName: String,
override val shortDescr: String?,
override val image: String? = null,
override val localAlias : String = "",
val contactLink: String? = null,
@@ -1910,12 +1920,13 @@ data class Profile(
return if (fullName == "" || displayName == fullName) displayName else "$displayName ($fullName)"
}
fun toLocalProfile(profileId: Long): LocalProfile = LocalProfile(profileId, displayName, fullName, image, localAlias, contactLink, preferences)
fun toLocalProfile(profileId: Long): LocalProfile = LocalProfile(profileId, displayName, fullName, shortDescr, image, localAlias, contactLink, preferences)
companion object {
val sampleData = Profile(
displayName = "alice",
fullName = "Alice"
fullName = "Alice",
shortDescr = null,
)
}
}
@@ -1925,6 +1936,7 @@ data class LocalProfile(
val profileId: Long,
override val displayName: String,
override val fullName: String,
override val shortDescr: String?,
override val image: String? = null,
override val localAlias: String,
val contactLink: String? = null,
@@ -1932,13 +1944,14 @@ data class LocalProfile(
): NamedChat {
val profileViewName: String = localAlias.ifEmpty { if (fullName == "" || displayName == fullName) displayName else "$displayName ($fullName)" }
fun toProfile(): Profile = Profile(displayName, fullName, image, localAlias, contactLink, preferences)
fun toProfile(): Profile = Profile(displayName, fullName, shortDescr, image, localAlias, contactLink, preferences)
companion object {
val sampleData = LocalProfile(
profileId = 1L,
displayName = "alice",
fullName = "Alice",
shortDescr = null,
preferences = ChatPreferences.sampleData,
localAlias = ""
)
@@ -2004,6 +2017,7 @@ data class GroupInfo (
override val timedMessagesTTL: Int? get() = with(fullGroupPreferences.timedMessages) { if (on) ttl else null }
override val displayName get() = localAlias.ifEmpty { groupProfile.displayName }
override val fullName get() = groupProfile.fullName
override val shortDescr get() = groupProfile.shortDescr
override val image get() = groupProfile.image
val isOwner: Boolean
@@ -2075,6 +2089,7 @@ data class GroupRef(val groupId: Long, val localDisplayName: String)
data class GroupProfile (
override val displayName: String,
override val fullName: String,
override val shortDescr: String?,
val description: String? = null,
override val image: String? = null,
override val localAlias: String = "",
@@ -2084,7 +2099,8 @@ data class GroupProfile (
companion object {
val sampleData = GroupProfile(
displayName = "team",
fullName = "My Team"
fullName = "My Team",
shortDescr = null,
)
}
}
@@ -2169,6 +2185,7 @@ data class GroupMember (
return pastMember(name)
}
override val fullName: String get() = memberProfile.fullName
override val shortDescr: String? get() = memberProfile.shortDescr
override val image: String? get() = memberProfile.image
val contactLink: String? = memberProfile.contactLink
val verified get() = activeConn?.connectionCode != null
@@ -2453,6 +2470,7 @@ class NoteFolder(
override val timedMessagesTTL: Int? get() = null
override val displayName get() = generalGetString(MR.strings.note_folder_local_display_name)
override val fullName get() = ""
override val shortDescr get() = null
override val image get() = null
override val localAlias get() = ""
override val localDisplayName: String get() = ""
@@ -2491,6 +2509,7 @@ class UserContactRequest (
override val timedMessagesTTL: Int? get() = null
override val displayName get() = profile.displayName
override val fullName get() = profile.fullName
override val shortDescr get() = profile.shortDescr
override val image get() = profile.image
override val localAlias get() = ""
@@ -2548,6 +2567,7 @@ class PendingContactConnection(
}
}
override val fullName get() = ""
override val shortDescr get() = null
override val image get() = null
val initiated get() = (pccConnStatus.initiated ?: false) && !viaContactUri
@@ -573,7 +573,7 @@ object ChatController {
suspend fun startChatWithTemporaryDatabase(ctrl: ChatCtrl, netCfg: NetCfg): User? {
Log.d(TAG, "startChatWithTemporaryDatabase")
val migrationActiveUser = apiGetActiveUser(null, ctrl) ?: apiCreateActiveUser(null, Profile(displayName = "Temp", fullName = ""), ctrl = ctrl)
val migrationActiveUser = apiGetActiveUser(null, ctrl) ?: apiCreateActiveUser(null, Profile(displayName = "Temp", fullName = "", shortDescr = null), ctrl = ctrl)
if (!apiSetNetworkConfig(netCfg, ctrl = ctrl)) {
Log.e(TAG, "Error setting network config, stopping migration")
return null
@@ -46,6 +46,7 @@ fun CreateProfile(chatModel: ChatModel, close: () -> Unit) {
.padding(top = 20.dp)
) {
val displayName = rememberSaveable { mutableStateOf("") }
val shortDescr = rememberSaveable { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
ColumnWithScrollBar {
@@ -66,6 +67,14 @@ fun CreateProfile(chatModel: ChatModel, close: () -> Unit) {
}
}
ProfileNameField(displayName, "", { it.trim() == mkValidName(it) }, focusRequester)
// TODO enable in v6.4.1, limit to 160 characters
// Text(
// stringResource(MR.strings.short_descr),
// fontSize = 16.sp
// )
// ProfileNameField(shortDescr, "")
}
SettingsActionItem(
painterResource(MR.images.ic_check),
@@ -75,7 +84,7 @@ fun CreateProfile(chatModel: ChatModel, close: () -> Unit) {
iconColor = MaterialTheme.colors.primary,
click = {
if (chatModel.localUserCreated.value == true) {
createProfileInProfiles(chatModel, displayName.value, close)
createProfileInProfiles(chatModel, displayName.value, shortDescr.value, close)
} else {
createProfileInNoProfileSetup(displayName.value, close)
}
@@ -162,7 +171,7 @@ fun CreateFirstProfile(chatModel: ChatModel, close: () -> Unit) {
fun createProfileInNoProfileSetup(displayName: String, close: () -> Unit) {
withBGApi {
val user = controller.apiCreateActiveUser(null, Profile(displayName.trim(), "", null)) ?: return@withBGApi
val user = controller.apiCreateActiveUser(null, Profile(displayName.trim(), "", null, null)) ?: return@withBGApi
if (!chatModel.connectedToRemote()) {
chatModel.localUserCreated.value = true
}
@@ -173,11 +182,11 @@ fun createProfileInNoProfileSetup(displayName: String, close: () -> Unit) {
}
}
fun createProfileInProfiles(chatModel: ChatModel, displayName: String, close: () -> Unit) {
fun createProfileInProfiles(chatModel: ChatModel, displayName: String, shortDescr: String, close: () -> Unit) {
withBGApi {
val rhId = chatModel.remoteHostId()
val user = chatModel.controller.apiCreateActiveUser(
rhId, Profile(displayName.trim(), "", null)
rhId, Profile(displayName.trim(), "", shortDescr.trim().ifEmpty { null }, null)
) ?: return@withBGApi
chatModel.currentUser.value = user
if (chatModel.users.isEmpty()) {
@@ -196,7 +205,7 @@ fun createProfileInProfiles(chatModel: ChatModel, displayName: String, close: ()
fun createProfileOnboarding(chatModel: ChatModel, displayName: String, close: () -> Unit) {
withBGApi {
chatModel.currentUser.value = chatModel.controller.apiCreateActiveUser(
null, Profile(displayName.trim(), "", null)
null, Profile(displayName.trim(), "", null, null)
) ?: return@withBGApi
chatModel.localUserCreated.value = true
val onboardingStage = chatModel.controller.appPrefs.onboardingStage
@@ -706,15 +706,16 @@ fun ChatInfoLayout(
@Composable
fun ChatInfoHeader(cInfo: ChatInfo, contact: Contact) {
Column(
Modifier.padding(horizontal = 8.dp),
Modifier.padding(horizontal = DEFAULT_PADDING),
horizontalAlignment = Alignment.CenterHorizontally
) {
ChatInfoImage(cInfo, size = 192.dp, iconColor = if (isInDarkTheme()) GroupDark else SettingsSecondaryLight)
val displayName = contact.profile.displayName.trim()
val text = buildAnnotatedString {
if (contact.verified) {
appendInlineContent(id = "shieldIcon")
}
append(contact.profile.displayName)
append(displayName)
}
val inlineContent: Map<String, InlineTextContent> = mapOf(
"shieldIcon" to InlineTextContent(
@@ -724,10 +725,11 @@ fun ChatInfoHeader(cInfo: ChatInfo, contact: Contact) {
}
)
val clipboard = LocalClipboardManager.current
val copyNameToClipboard = {
clipboard.setText(AnnotatedString(contact.profile.displayName))
val copyNameToClipboard = fun (name: String) {
clipboard.setText(AnnotatedString(name))
showToast(generalGetString(MR.strings.copied))
}
val copyDisplayName = { copyNameToClipboard(displayName) }
Text(
text,
inlineContent = inlineContent,
@@ -735,18 +737,39 @@ fun ChatInfoHeader(cInfo: ChatInfo, contact: Contact) {
textAlign = TextAlign.Center,
maxLines = 3,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.combinedClickable(onClick = copyNameToClipboard, onLongClick = copyNameToClipboard).onRightClick(copyNameToClipboard)
modifier = Modifier.combinedClickable(onClick = copyDisplayName, onLongClick = copyDisplayName).onRightClick(copyDisplayName)
)
ChatInfoDescription(cInfo, displayName, copyNameToClipboard)
}
}
@Composable
fun ChatInfoDescription(c: NamedChat, displayName: String, copyNameToClipboard: (String) -> Unit) {
val fullName = c.fullName.trim()
if (fullName != "" && fullName != displayName && fullName != c.displayName.trim()) {
val copyFullName = { copyNameToClipboard(fullName) }
Text(
fullName, style = MaterialTheme.typography.h2,
color = MaterialTheme.colors.onBackground,
textAlign = TextAlign.Center,
maxLines = 3,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(top = DEFAULT_PADDING_HALF).combinedClickable(onClick = copyFullName, onLongClick = copyFullName).onRightClick(copyFullName)
)
}
val descr = c.shortDescr?.trim()
if (descr != null && descr != "") {
val copyDescr = { copyNameToClipboard(descr) }
Text(
descr,
style = MaterialTheme.typography.body2,
color = MaterialTheme.colors.onBackground,
textAlign = TextAlign.Center,
maxLines = 4,
overflow = TextOverflow.Ellipsis,
lineHeight = 21.sp,
modifier = Modifier.padding(top = DEFAULT_PADDING_HALF).combinedClickable(onClick = copyDescr, onLongClick = copyDescr).onRightClick(copyDescr)
)
if (cInfo.fullName != "" && cInfo.fullName != cInfo.displayName && cInfo.fullName != contact.profile.displayName) {
Text(
cInfo.fullName, style = MaterialTheme.typography.h2,
color = MaterialTheme.colors.onBackground,
textAlign = TextAlign.Center,
maxLines = 4,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.combinedClickable(onClick = copyNameToClipboard, onLongClick = copyNameToClipboard).onRightClick(copyNameToClipboard)
)
}
}
}
@@ -724,33 +724,27 @@ fun ChatTTLOption(chatItemTTL: State<ChatItemTTL?>, setChatItemTTL: (ChatItemTTL
@Composable
private fun GroupChatInfoHeader(cInfo: ChatInfo, groupInfo: GroupInfo) {
Column(
Modifier.padding(horizontal = 8.dp),
Modifier.padding(horizontal = DEFAULT_PADDING),
horizontalAlignment = Alignment.CenterHorizontally
) {
ChatInfoImage(cInfo, size = 192.dp, iconColor = if (isInDarkTheme()) GroupDark else SettingsSecondaryLight)
val clipboard = LocalClipboardManager.current
val copyNameToClipboard = {
clipboard.setText(AnnotatedString(groupInfo.groupProfile.displayName))
val copyNameToClipboard = fun(name: String) {
clipboard.setText(AnnotatedString(name))
showToast(generalGetString(MR.strings.copied))
}
val displayName = groupInfo.groupProfile.displayName.trim()
val copyDisplayName = { copyNameToClipboard(displayName) }
Text(
groupInfo.groupProfile.displayName, style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Normal),
displayName,
style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Normal),
color = MaterialTheme.colors.onBackground,
textAlign = TextAlign.Center,
maxLines = 4,
maxLines = 3,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.combinedClickable(onClick = copyNameToClipboard, onLongClick = copyNameToClipboard).onRightClick(copyNameToClipboard)
modifier = Modifier.combinedClickable(onClick = copyDisplayName, onLongClick = copyDisplayName).onRightClick(copyDisplayName)
)
if (cInfo.fullName != "" && cInfo.fullName != cInfo.displayName && cInfo.fullName != groupInfo.groupProfile.displayName) {
Text(
cInfo.fullName, style = MaterialTheme.typography.h2,
color = MaterialTheme.colors.onBackground,
textAlign = TextAlign.Center,
maxLines = 8,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.combinedClickable(onClick = copyNameToClipboard, onLongClick = copyNameToClipboard).onRightClick(copyNameToClipboard)
)
}
ChatInfoDescription(cInfo, displayName, copyNameToClipboard)
}
}
@@ -568,11 +568,12 @@ fun GroupMemberInfoHeader(member: GroupMember) {
horizontalAlignment = Alignment.CenterHorizontally
) {
MemberProfileImage(size = 192.dp, member, color = if (isInDarkTheme()) GroupDark else SettingsSecondaryLight)
val displayName = member.displayName.trim() // alias if set
val text = buildAnnotatedString {
if (member.verified) {
appendInlineContent(id = "shieldIcon")
}
append(member.displayName)
append(displayName)
}
val inlineContent: Map<String, InlineTextContent> = mapOf(
"shieldIcon" to InlineTextContent(
@@ -582,10 +583,11 @@ fun GroupMemberInfoHeader(member: GroupMember) {
}
)
val clipboard = LocalClipboardManager.current
val copyNameToClipboard = {
clipboard.setText(AnnotatedString(member.displayName))
val copyNameToClipboard = fun(name: String) {
clipboard.setText(AnnotatedString(name))
showToast(generalGetString(MR.strings.copied))
}
val copyDisplayName = { copyNameToClipboard(displayName) }
Text(
text,
inlineContent = inlineContent,
@@ -593,18 +595,10 @@ fun GroupMemberInfoHeader(member: GroupMember) {
textAlign = TextAlign.Center,
maxLines = 3,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.combinedClickable(onClick = copyNameToClipboard, onLongClick = copyNameToClipboard).onRightClick(copyNameToClipboard)
modifier = Modifier.combinedClickable(onClick = copyDisplayName, onLongClick = copyDisplayName).onRightClick(copyDisplayName)
)
if (member.fullName != "" && member.fullName != member.displayName) {
Text(
member.fullName, style = MaterialTheme.typography.h2,
color = MaterialTheme.colors.onBackground,
textAlign = TextAlign.Center,
maxLines = 4,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.combinedClickable(onClick = copyNameToClipboard, onLongClick = copyNameToClipboard).onRightClick(copyNameToClipboard)
)
}
// passing actual display name here, as alias is used above
ChatInfoDescription(member, member.memberProfile.displayName.trim(), copyNameToClipboard)
}
}
@@ -56,14 +56,16 @@ fun GroupProfileLayout(
val bottomSheetModalState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden)
val displayName = rememberSaveable { mutableStateOf(groupProfile.displayName) }
val fullName = rememberSaveable { mutableStateOf(groupProfile.fullName) }
val shortDescr = rememberSaveable { mutableStateOf(groupProfile.shortDescr ?: "") }
val chosenImage = rememberSaveable { mutableStateOf<URI?>(null) }
val profileImage = rememberSaveable { mutableStateOf(groupProfile.image) }
val scope = rememberCoroutineScope()
val scrollState = rememberScrollState()
val focusRequester = remember { FocusRequester() }
val dataUnchanged =
displayName.value == groupProfile.displayName &&
fullName.value == groupProfile.fullName &&
displayName.value.trim() == groupProfile.displayName &&
fullName.value.trim() == groupProfile.fullName &&
shortDescr.value.trim() == (groupProfile.shortDescr ?: "") &&
groupProfile.image == profileImage.value
val closeWithAlert = {
if (dataUnchanged || !canUpdateProfile(displayName.value, groupProfile)) {
@@ -73,7 +75,8 @@ fun GroupProfileLayout(
saveProfile(
groupProfile.copy(
displayName = displayName.value.trim(),
fullName = fullName.value,
fullName = fullName.value.trim(),
shortDescr = shortDescr.value.trim().ifEmpty { null },
image = profileImage.value
)
)
@@ -130,7 +133,7 @@ fun GroupProfileLayout(
}
}
ProfileNameField(displayName, "", { isValidNewProfileName(it, groupProfile) }, focusRequester)
if (groupProfile.fullName.isNotEmpty() && groupProfile.fullName != groupProfile.displayName) {
if (groupProfile.fullName.trim().isNotEmpty() && groupProfile.fullName.trim() != groupProfile.displayName.trim()) {
Spacer(Modifier.height(DEFAULT_PADDING))
Text(
stringResource(MR.strings.group_full_name_field),
@@ -139,6 +142,17 @@ fun GroupProfileLayout(
)
ProfileNameField(fullName)
}
// TODO enable in v6.4.1, limit to 160 characters
// Spacer(Modifier.height(DEFAULT_PADDING))
// Text(
// stringResource(MR.strings.group_short_descr_field),
// fontSize = 16.sp,
// modifier = Modifier.padding(bottom = DEFAULT_PADDING_HALF)
// )
// ProfileNameField(shortDescr)
Spacer(Modifier.height(DEFAULT_PADDING))
val enabled = !dataUnchanged && canUpdateProfile(displayName.value, groupProfile)
if (enabled) {
@@ -148,7 +162,8 @@ fun GroupProfileLayout(
saveProfile(
groupProfile.copy(
displayName = displayName.value.trim(),
fullName = fullName.value,
fullName = fullName.value.trim(),
shortDescr = shortDescr.value.trim().ifEmpty { null },
image = profileImage.value
)
)
@@ -194,7 +194,7 @@ fun CIGroupInvitationViewLongNamePreview() {
CIGroupInvitationView(
ci = ChatItem.getGroupInvitationSample(),
groupInvitation = CIGroupInvitation.getSample(
groupProfile = GroupProfile("group_with_a_really_really_really_long_name", "Group With A Really Really Really Long Name"),
groupProfile = GroupProfile("group_with_a_really_really_really_long_name", "Group With A Really Really Really Long Name", shortDescr = null),
status = CIGroupInvitationStatus.Accepted
),
memberRole = GroupMemberRole.Admin,
@@ -82,7 +82,7 @@ private fun deleteStorageAndRestart(m: ChatModel, password: String, completed: (
}
var profile: Profile? = null
if (!displayName.isNullOrEmpty()) {
profile = Profile(displayName = displayName, fullName = "")
profile = Profile(displayName = displayName, fullName = "", shortDescr = null)
}
val createdUser = m.controller.apiCreateActiveUser(null, profile, pastTimestamp = true)
m.currentUser.value = createdUser
@@ -140,6 +140,7 @@ fun AddGroupLayout(
createGroup(incognito.value, GroupProfile(
displayName = displayName.value.trim(),
fullName = "",
shortDescr = null,
image = profileImage.value,
groupPreferences = GroupPreferences(history = GroupPreference(GroupFeatureEnabled.ON))
))
@@ -40,9 +40,10 @@ fun UserProfileView(chatModel: ChatModel, close: () -> Unit) {
UserProfileLayout(
profile = profile,
close,
saveProfile = { displayName, fullName, image ->
saveProfile = { displayName, fullName, shortDescr, image ->
withBGApi {
val updated = chatModel.controller.apiUpdateProfile(user.remoteHostId, profile.copy(displayName = displayName.trim(), fullName = fullName, image = image))
val updatedProfile = profile.copy(displayName = displayName.trim(), fullName = fullName.trim(), shortDescr = shortDescr.trim().ifEmpty { null }, image = image)
val updated = chatModel.controller.apiUpdateProfile(user.remoteHostId, updatedProfile)
if (updated != null) {
val (newProfile, _) = updated
chatModel.updateCurrentUser(user.remoteHostId, newProfile)
@@ -59,11 +60,12 @@ fun UserProfileView(chatModel: ChatModel, close: () -> Unit) {
fun UserProfileLayout(
profile: Profile,
close: () -> Unit,
saveProfile: (String, String, String?) -> Unit,
saveProfile: (String, String, String, String?) -> Unit,
) {
val bottomSheetModalState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden)
val displayName = remember { mutableStateOf(profile.displayName) }
val fullName = remember { mutableStateOf(profile.fullName) }
val shortDescr = remember { mutableStateOf(profile.shortDescr ?: "") }
val chosenImage = rememberSaveable { mutableStateOf<URI?>(null) }
val profileImage = rememberSaveable { mutableStateOf(profile.image) }
val scope = rememberCoroutineScope()
@@ -85,14 +87,15 @@ fun UserProfileLayout(
sheetShape = RoundedCornerShape(topStart = 18.dp, topEnd = 18.dp)
) {
val dataUnchanged =
displayName.value == profile.displayName &&
fullName.value == profile.fullName &&
displayName.value.trim() == profile.displayName &&
fullName.value.trim() == profile.fullName &&
shortDescr.value.trim() == (profile.shortDescr ?: "") &&
profile.image == profileImage.value
val closeWithAlert = {
if (dataUnchanged || !canSaveProfile(displayName.value, profile)) {
close()
} else {
showUnsavedChangesAlert({ saveProfile(displayName.value, fullName.value, profileImage.value) }, close)
showUnsavedChangesAlert({ saveProfile(displayName.value, fullName.value, shortDescr.value, profileImage.value) }, close)
}
}
ModalView(close = closeWithAlert) {
@@ -144,9 +147,20 @@ fun UserProfileLayout(
)
ProfileNameField(fullName)
}
// TODO enable in v6.4.1, limit to 160 characters
// Spacer(Modifier.height(DEFAULT_PADDING))
// Text(
// stringResource(MR.strings.short_descr__field),
// fontSize = 16.sp,
// modifier = Modifier.padding(bottom = DEFAULT_PADDING_HALF)
// )
// ProfileNameField(shortDescr)
Spacer(Modifier.height(DEFAULT_PADDING))
val enabled = !dataUnchanged && canSaveProfile(displayName.value, profile)
val saveModifier: Modifier = Modifier.clickable(enabled) { saveProfile(displayName.value, fullName.value, profileImage.value) }
val saveModifier: Modifier = Modifier.clickable(enabled) { saveProfile(displayName.value, fullName.value, shortDescr.value, profileImage.value) }
val saveColor: Color = if (enabled) MaterialTheme.colors.primary else MaterialTheme.colors.secondary
Text(
stringResource(MR.strings.save_and_notify_contacts),
@@ -209,7 +223,7 @@ private fun isValidNewProfileName(displayName: String, profile: Profile): Boolea
displayName == profile.displayName || isValidDisplayName(displayName.trim())
private fun showFullName(profile: Profile): Boolean =
profile.fullName.isNotEmpty() && profile.fullName != profile.displayName
profile.fullName.trim().isNotEmpty() && profile.fullName.trim() != profile.displayName.trim()
private fun canSaveProfile(displayName: String, profile: Profile): Boolean =
displayName.trim().isNotEmpty() && isValidNewProfileName(displayName, profile)
@@ -225,7 +239,7 @@ fun PreviewUserProfileLayoutEditOff() {
UserProfileLayout(
profile = Profile.sampleData,
close = {},
saveProfile = { _, _, _ -> }
saveProfile = { _, _, _, _ -> }
)
}
}
@@ -241,7 +255,7 @@ fun PreviewUserProfileLayoutEditOn() {
UserProfileLayout(
profile = Profile.sampleData,
close = {},
saveProfile = { _, _, _ -> }
saveProfile = { _, _, _, _ -> }
)
}
}
@@ -1108,6 +1108,7 @@
<!-- User profile details - UserProfileView.kt -->
<string name="display_name__field">Profile name:</string>
<string name="full_name__field">Full name:</string>
<string name="short_descr__field">Bio:</string>
<string name="your_current_profile">Your current profile</string>
<string name="your_profile_is_stored_on_device_and_shared_only_with_contacts_simplex_cannot_see_it">Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile.</string>
<string name="edit_image">Edit image</string>
@@ -1137,6 +1138,7 @@
<string name="profile_is_only_shared_with_your_contacts">The profile is only shared with your contacts.</string>
<string name="display_name_cannot_contain_whitespace">Display name cannot contain whitespace.</string>
<string name="display_name">Enter your name:</string>
<string name="short_descr">Your bio:</string>
<string name="create_profile_button">Create</string>
<string name="create_another_profile_button">Create profile</string>
<string name="create_address_button">Create</string>
@@ -1914,6 +1916,7 @@
<string name="group_is_decentralized">Fully decentralized visible only to members.</string>
<string name="group_display_name_field">Enter group name:</string>
<string name="group_full_name_field">Group full name:</string>
<string name="group_short_descr_field">Short description:</string>
<string name="group_main_profile_sent">Your chat profile will be sent to group members</string>
<string name="chat_main_profile_sent">Your chat profile will be sent to chat members</string>
<string name="create_group_button">Create group</string>
@@ -234,14 +234,19 @@ directoryServiceEvent st opts@DirectoryOpts {adminUsers, superUsers, serviceName
getGroupReg st groupId >>= \case
Just gr -> action gr
Nothing -> logError $ "Error: " <> err <> ", group: " <> localDisplayName <> ", can't find group registration ID " <> tshow groupId
groupInfoText GroupProfile {displayName = n, fullName = fn, description = d} =
n <> (if n == fn || T.null fn then "" else " (" <> fn <> ")") <> maybe "" ("\nWelcome message:\n" <>) d
groupInfoText p@GroupProfile {description = d} = groupNameDescr p <> maybe "" ("\nWelcome message:\n" <>) d
groupNameDescr GroupProfile {displayName = n, fullName = fn, shortDescr = sd_} =
n <> maybe "" (\d' -> " (" <> d' <> ")") descr
where
descr
| n == fn || T.null fn = if sd_ == Just "" then Nothing else sd_
| otherwise = Just fn
userGroupReference gr GroupInfo {groupProfile = GroupProfile {displayName}} = userGroupReference' gr displayName
userGroupReference' GroupReg {userGroupRegId} displayName = groupReference' userGroupRegId displayName
groupReference GroupInfo {groupId, groupProfile = GroupProfile {displayName}} = groupReference' groupId displayName
groupReference' groupId displayName = "ID " <> tshow groupId <> " (" <> displayName <> ")"
groupAlreadyListed GroupInfo {groupProfile = GroupProfile {displayName, fullName}} =
"The group " <> displayName <> " (" <> fullName <> ") is already listed in the directory, please choose another name."
groupAlreadyListed GroupInfo {groupProfile = p} =
"The group " <> groupNameDescr p <> " is already listed in the directory, please choose another name."
getGroups :: Text -> IO (Maybe [(GroupInfo, GroupSummary)])
getGroups = getGroups_ . Just
@@ -295,7 +300,7 @@ directoryServiceEvent st opts@DirectoryOpts {adminUsers, superUsers, serviceName
\Content and privacy policy: https://simplex.chat/docs/directory.html"
deGroupInvitation :: Contact -> GroupInfo -> GroupMemberRole -> GroupMemberRole -> IO ()
deGroupInvitation ct g@GroupInfo {groupProfile = GroupProfile {displayName, fullName}} fromMemberRole memberRole = do
deGroupInvitation ct g@GroupInfo {groupProfile = p@GroupProfile {displayName}} fromMemberRole memberRole = do
logInfo $ "invited to group " <> viewGroupName g <> " by " <> viewContactName ct
case badRolesMsg $ groupRolesStatus fromMemberRole memberRole of
Just msg -> sendMessage cc ct msg
@@ -308,7 +313,7 @@ directoryServiceEvent st opts@DirectoryOpts {adminUsers, superUsers, serviceName
where
askConfirmation = do
ugrId <- addGroupReg st ct g GRSPendingConfirmation
sendMessage cc ct $ "The group " <> displayName <> " (" <> fullName <> ") is already submitted to the directory.\nTo confirm the registration, please send:"
sendMessage cc ct $ "The group " <> groupNameDescr p <> " is already submitted to the directory.\nTo confirm the registration, please send:"
sendMessage cc ct $ "/confirm " <> tshow ugrId <> ":" <> viewName displayName
badRolesMsg :: GroupRolesStatus -> Maybe Text
@@ -400,9 +405,9 @@ directoryServiceEvent st opts@DirectoryOpts {adminUsers, superUsers, serviceName
GroupInfo {groupId, groupProfile = p} = fromGroup
GroupInfo {groupProfile = p'} = toGroup
sameProfile
GroupProfile {displayName = n, fullName = fn, image = i, description = d}
GroupProfile {displayName = n', fullName = fn', image = i', description = d'} =
n == n' && fn == fn' && i == i' && d == d'
GroupProfile {displayName = n, fullName = fn, shortDescr = sd, image = i, description = d}
GroupProfile {displayName = n', fullName = fn', shortDescr = sd', image = i', description = d'} =
n == n' && fn == fn' && i == i' && sd == sd' && d == d'
groupLinkAdded gr byMember = do
getDuplicateGroup toGroup >>= \case
Nothing -> notifyOwner gr "Error: getDuplicateGroup. Please notify the developers."
+2
View File
@@ -110,6 +110,7 @@ library
Simplex.Chat.Store.Postgres.Migrations.M20250526_short_links
Simplex.Chat.Store.Postgres.Migrations.M20250702_contact_requests_remove_cascade_delete
Simplex.Chat.Store.Postgres.Migrations.M20250704_groups_conn_link_prepared_connection
Simplex.Chat.Store.Postgres.Migrations.M20250709_profile_short_descr
else
exposed-modules:
Simplex.Chat.Archive
@@ -245,6 +246,7 @@ library
Simplex.Chat.Store.SQLite.Migrations.M20250526_short_links
Simplex.Chat.Store.SQLite.Migrations.M20250702_contact_requests_remove_cascade_delete
Simplex.Chat.Store.SQLite.Migrations.M20250704_groups_conn_link_prepared_connection
Simplex.Chat.Store.SQLite.Migrations.M20250709_profile_short_descr
other-modules:
Paths_simplex_chat
hs-source-dirs:
+1 -1
View File
@@ -531,7 +531,7 @@ data ChatCommand
| CancelFile FileTransferId
| FileStatus FileTransferId
| ShowProfile -- UserId (not used in UI)
| UpdateProfile ContactName Text -- UserId (not used in UI)
| UpdateProfile ContactName (Maybe Text) -- UserId (not used in UI)
| UpdateProfileImage (Maybe ImageData) -- UserId (not used in UI)
| ShowProfileImage
| SetUserFeature AChatFeature FeatureAllowed -- UserId (not used in UI)
+1 -1
View File
@@ -107,7 +107,7 @@ createActiveUser cc = do
where
loop = do
displayName <- T.pack <$> getWithPrompt "display name"
let profile = Just Profile {displayName, fullName = "", image = Nothing, contactLink = Nothing, preferences = Nothing}
let profile = Just Profile {displayName, fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, preferences = Nothing}
execChatCommand' (CreateActiveUser NewUser {profile, pastTimestamp = False}) 0 `runReaderT` cc >>= \case
Right (CRActiveUser user) -> pure user
r -> printResponseEvent (Nothing, Nothing) (config cc) r >> loop
+5 -3
View File
@@ -122,7 +122,7 @@ groupsHelpInfo =
map
styleMarkdown
[ green "Group commands:",
indent <> highlight "/group <group> [<full_name>] " <> " - create group",
indent <> highlight "/group <group> [<description>] " <> " - create group",
indent <> highlight "/add <group> <contact> [<role>] " <> " - add contact to group, roles: " <> highlight "owner" <> ", " <> highlight "admin" <> " (default), " <> highlight "member",
indent <> highlight "/join <group> " <> " - accept group invitation",
indent <> highlight "/members <group> " <> " - list group members",
@@ -131,8 +131,10 @@ groupsHelpInfo =
indent <> highlight "/clear #<group> " <> " - clear all messages in the group locally",
indent <> highlight "/delete #<group> " <> " - delete group and all messages",
indent <> highlight "/gp <group> " <> " - view group profile",
indent <> highlight "/gp <group> <name> [<full_name>] " <> " - update group profile names",
indent <> highlight "/group_descr <group> [<descr>] " <> " - update/remove group description",
indent <> highlight "/gp <group> <name> [<description>] " <> " - update group profile names",
indent <> highlight "/set welcome <group> <welcome> " <> " - set group welcome message",
indent <> highlight "/delete welcome <group> " <> " - delete group welcome message",
indent <> highlight "/show welcome <group> " <> " - show group welcome message",
indent <> highlight "/groups " <> " - list groups",
indent <> highlight "#<group> <message> " <> " - send message to group",
"",
+18 -15
View File
@@ -2507,8 +2507,8 @@ processChatCommand vr nm = \case
APIUpdateGroupProfile groupId p' -> withUser $ \user -> do
g <- withFastStore $ \db -> getGroup db vr user groupId
runUpdateGroupProfile user g p'
UpdateGroupNames gName GroupProfile {displayName, fullName} ->
updateGroupProfileByName gName $ \p -> p {displayName, fullName}
UpdateGroupNames gName p'@GroupProfile {displayName, fullName, shortDescr} ->
updateGroupProfileByName gName $ \p -> p {displayName, fullName, shortDescr}
ShowGroupProfile gName -> withUser $ \user ->
CRGroupProfile user <$> withFastStore (\db -> getGroupInfoByName db vr user gName)
UpdateGroupDescription gName description ->
@@ -2729,8 +2729,8 @@ processChatCommand vr nm = \case
fileStatus <- withFastStore $ \db -> getFileTransferProgress db user fileId
pure $ CRFileTransferStatus user fileStatus
ShowProfile -> withUser $ \user@User {profile} -> pure $ CRUserProfile user (fromLocalProfile profile)
UpdateProfile displayName fullName -> withUser $ \user@User {profile} -> do
let p = (fromLocalProfile profile :: Profile) {displayName = displayName, fullName = fullName}
UpdateProfile displayName shortDescr -> withUser $ \user@User {profile} -> do
let p = (fromLocalProfile profile :: Profile) {displayName, shortDescr, fullName = ""}
updateProfile user p
UpdateProfileImage image -> withUser $ \user@User {profile} -> do
let p = (fromLocalProfile profile :: Profile) {image}
@@ -3151,9 +3151,9 @@ processChatCommand vr nm = \case
let (newMs, oldMs) = partition (\m -> maxVersion (memberChatVRange m) >= businessChatPrefsVersion) ms
-- this is a fallback to send the members with the old version correct profile of the business when preferences change
unless (null oldMs) $ do
GroupMember {memberProfile = LocalProfile {displayName, fullName, image}} <-
GroupMember {memberProfile = LocalProfile {displayName, fullName, shortDescr, image}} <-
withStore $ \db -> getGroupMemberByMemberId db vr user g businessId
let p'' = p' {displayName, fullName, image} :: GroupProfile
let p'' = p' {displayName, fullName, shortDescr, image} :: GroupProfile
recipients = filter memberCurrentOrPending oldMs
void $ sendGroupMessage user g' Nothing recipients (XGrpInfo p'')
let ps' = fromMaybe defaultBusinessGroupPrefs $ groupPreferences p'
@@ -3162,6 +3162,7 @@ processChatCommand vr nm = \case
Nothing -> do
setGroupLinkData'
let recipients = filter memberCurrentOrPending ms
liftIO $ putStrLn $ "about to sendGroupMessage to " <> show (length recipients)
sendGroupMessage user g' Nothing recipients (XGrpInfo p')
where
setGroupLinkData' :: CM ()
@@ -3553,14 +3554,14 @@ processChatCommand vr nm = \case
business = maybe False businessAddress settings
contactData
| large = ContactShortLinkData p msg business
| otherwise = ContactShortLinkData p {fullName = "", image = Nothing, contactLink = Nothing} Nothing business
| otherwise = ContactShortLinkData p {fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing} Nothing business
pure $ encodeShortLinkData large contactData
groupShortLinkData :: GroupProfile -> CM UserLinkData
groupShortLinkData gp = do
large <- chatReadVar useLargeLinkData
let gp'
| large = gp
| otherwise = gp {fullName = "", description = Nothing, image = Nothing, memberAdmission = Nothing}
| otherwise = gp {fullName = "", shortDescr = Nothing, description = Nothing, image = Nothing, memberAdmission = Nothing}
pure $ encodeShortLinkData large $ GroupShortLinkData gp'
encodeShortLinkData :: J.ToJSON a => Bool -> a -> UserLinkData
encodeShortLinkData large d =
@@ -4609,7 +4610,7 @@ chatCommandP =
"/set profile image " *> (UpdateProfileImage . Just . ImageData <$> imageP),
"/delete profile image" $> UpdateProfileImage Nothing,
"/show profile image" $> ShowProfileImage,
("/profile " <|> "/p ") *> (uncurry UpdateProfile <$> profileNames),
("/profile " <|> "/p ") *> (uncurry UpdateProfile <$> profileNameDescr),
("/profile" <|> "/p") $> ShowProfile,
"/set voice #" *> (SetGroupFeatureRole (AGFR SGFVoice) <$> displayNameP <*> _strP <*> optional memberRole),
"/set voice @" *> (SetContactFeature (ACF SCFVoice) <$> displayNameP <*> optional (A.space *> strP)),
@@ -4721,24 +4722,26 @@ chatCommandP =
clearOverrides <- (" clear_overrides=" *> onOffP) <|> pure False
pure UserMsgReceiptSettings {enable, clearOverrides}
onOffP = ("on" $> True) <|> ("off" $> False)
profileNames = (,) <$> displayNameP <*> fullNameP
profileNameDescr = (,) <$> displayNameP <*> shortDescrP
newUserP = do
(cName, fullName) <- profileNames
let profile = Just Profile {displayName = cName, fullName, image = Nothing, contactLink = Nothing, preferences = Nothing}
(cName, shortDescr) <- profileNameDescr
let profile = Just Profile {displayName = cName, fullName = "", shortDescr, image = Nothing, contactLink = Nothing, preferences = Nothing}
pure NewUser {profile, pastTimestamp = False}
jsonP :: J.FromJSON a => Parser a
jsonP = J.eitherDecodeStrict' <$?> A.takeByteString
groupProfile = do
(gName, fullName) <- profileNames
(gName, shortDescr) <- profileNameDescr
let groupPreferences =
Just
(emptyGroupPrefs :: GroupPreferences)
{ directMessages = Just DirectMessagesGroupPreference {enable = FEOn, role = Nothing},
history = Just HistoryGroupPreference {enable = FEOn}
}
pure GroupProfile {displayName = gName, fullName, description = Nothing, image = Nothing, groupPreferences, memberAdmission = Nothing}
pure GroupProfile {displayName = gName, fullName = "", shortDescr, description = Nothing, image = Nothing, groupPreferences, memberAdmission = Nothing}
memberCriteriaP = ("all" $> Just MCAll) <|> ("off" $> Nothing)
fullNameP = A.space *> textP <|> pure ""
shortDescrP = do
descr <- A.takeWhile1 isSpace *> (T.dropWhileEnd isSpace <$> textP) <|> pure ""
pure $ if T.null descr then Nothing else Just $ T.take 160 descr
textP = safeDecodeUtf8 <$> A.takeByteString
pwdP = jsonP <|> (UserPwd . safeDecodeUtf8 <$> A.takeTill (== ' '))
verifyCodeP = safeDecodeUtf8 <$> A.takeWhile (\c -> isDigit c || c == ' ')
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -2232,9 +2232,9 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
createInternalChatItem user (CDDirectRcv c') ciContent Nothing
where
visibleProfileUpdated =
n' /= n || fn' /= fn || i' /= i || cl' /= cl
Profile {displayName = n, fullName = fn, image = i, contactLink = cl} = p
Profile {displayName = n', fullName = fn', image = i', contactLink = cl'} = p'
n' /= n || fn' /= fn || sd /= sd' || i' /= i || cl' /= cl
Profile {displayName = n, fullName = fn, shortDescr = sd, image = i, contactLink = cl} = p
Profile {displayName = n', fullName = fn', shortDescr = sd', image = i', contactLink = cl'} = p'
xInfoMember :: GroupInfo -> GroupMember -> Profile -> UTCTime -> CM ()
xInfoMember gInfo m p' brokerTs = void $ processMemberProfileUpdate gInfo m p' True (Just brokerTs)
+2 -2
View File
@@ -318,7 +318,7 @@ e2eInfoPQText =
ciGroupInvitationToText :: CIGroupInvitation -> GroupMemberRole -> Text
ciGroupInvitationToText CIGroupInvitation {groupProfile = GroupProfile {displayName, fullName}} role =
"invitation to join group " <> displayName <> optionalFullName displayName fullName <> " as " <> (decodeLatin1 . strEncode $ role)
"invitation to join group " <> displayName <> optionalFullName displayName fullName Nothing <> " as " <> (decodeLatin1 . strEncode $ role)
rcvDirectEventToText :: RcvDirectEvent -> Text
rcvDirectEventToText = \case
@@ -402,7 +402,7 @@ sndConnEventToText = \case
maybe "" (\GroupMemberRef {profile = Profile {displayName}} -> " for " <> displayName) member_
profileToText :: Profile -> Text
profileToText Profile {displayName, fullName} = displayName <> optionalFullName displayName fullName
profileToText Profile {displayName, fullName} = displayName <> optionalFullName displayName fullName Nothing
msgIntegrityError :: MsgErrorType -> Text
msgIntegrityError = \case
+1 -1
View File
@@ -10,7 +10,7 @@ generateRandomProfile :: IO Profile
generateRandomProfile = do
adjective <- pick adjectives
noun <- pickNoun adjective 2
pure $ Profile {displayName = adjective <> noun, fullName = "", image = Nothing, contactLink = Nothing, preferences = Nothing}
pure $ Profile {displayName = adjective <> noun, fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, preferences = Nothing}
where
pick :: [a] -> IO a
pick xs = (xs !!) <$> randomRIO (0, length xs - 1)
+6 -6
View File
@@ -111,7 +111,7 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
db
[sql|
SELECT
c.contact_profile_id, c.local_display_name, c.via_group, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, c.contact_used, c.contact_status, c.enable_ntfs, c.send_rcpts, c.favorite,
c.contact_profile_id, c.local_display_name, c.via_group, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, c.contact_used, c.contact_status, c.enable_ntfs, c.send_rcpts, c.favorite,
p.preferences, c.user_preferences, c.created_at, c.updated_at, c.chat_ts, c.conn_full_link_to_connect, c.conn_short_link_to_connect, c.welcome_shared_msg_id, c.request_shared_msg_id, c.contact_request_id,
c.contact_group_member_id, c.contact_grp_inv_sent, c.ui_themes, c.chat_deleted, c.custom_data, c.chat_item_ttl
FROM contacts c
@@ -120,8 +120,8 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
|]
(userId, contactId)
toContact' :: Int64 -> Connection -> [ChatTagId] -> ContactRow' -> Contact
toContact' contactId conn chatTags ((profileId, localDisplayName, viaGroup, displayName, fullName, image, contactLink, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, contactGroupMemberId, BI contactGrpInvSent, uiThemes, BI chatDeleted, customData, chatItemTTL)) =
let profile = LocalProfile {profileId, displayName, fullName, image, contactLink, preferences, localAlias}
toContact' contactId conn chatTags ((profileId, localDisplayName, viaGroup, displayName, fullName, shortDescr, image, contactLink, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, contactGroupMemberId, BI contactGrpInvSent, uiThemes, BI chatDeleted, customData, chatItemTTL)) =
let profile = LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, preferences, localAlias}
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito conn
activeConn = Just conn
@@ -137,7 +137,7 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
[sql|
SELECT
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
@@ -147,12 +147,12 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
-- GroupInfo {membership = GroupMember {memberProfile}}
pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts,
-- from GroupMember
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts
FROM group_members m
+7 -6
View File
@@ -72,7 +72,7 @@ createOrUpdateContactRequest
isSimplexTeam
invId
cReqChatVRange@(VersionRange minV maxV)
profile@Profile {displayName, fullName, image, contactLink, preferences}
profile@Profile {displayName, fullName, shortDescr, image, contactLink, preferences}
xContactId_
welcomeMsgId_
requestMsg_
@@ -109,7 +109,7 @@ createOrUpdateContactRequest
[sql|
SELECT
-- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
-- Connection
@@ -143,7 +143,7 @@ createOrUpdateContactRequest
SELECT
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version
@@ -161,8 +161,8 @@ createOrUpdateContactRequest
liftIO $
DB.execute
db
"INSERT INTO contact_profiles (display_name, full_name, image, contact_link, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)"
(displayName, fullName, image, contactLink, userId, preferences, currentTs, currentTs)
"INSERT INTO contact_profiles (display_name, full_name, short_descr, image, contact_link, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)"
(displayName, fullName, shortDescr, image, contactLink, userId, preferences, currentTs, currentTs)
profileId <- liftIO $ insertedRowId db
liftIO $
DB.execute
@@ -229,6 +229,7 @@ createOrUpdateContactRequest
UPDATE contact_profiles
SET display_name = ?,
full_name = ?,
short_descr = ?,
image = ?,
contact_link = ?,
updated_at = ?
@@ -239,7 +240,7 @@ createOrUpdateContactRequest
AND contact_request_id = ?
)
|]
(displayName, fullName, image, contactLink, currentTs, userId, contactRequestId)
(displayName, fullName, shortDescr, image, contactLink, currentTs, userId, contactRequestId)
updateRequest currentTs =
if displayName == oldDisplayName
then
+12 -12
View File
@@ -262,7 +262,7 @@ getContactByConnReqHash db vr user@User {userId} cReqHash1 cReqHash2 = do
[sql|
SELECT
-- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
-- Connection
@@ -692,15 +692,15 @@ updateContactProfile_ db userId profileId profile = do
updateContactProfile_' db userId profileId profile currentTs
updateContactProfile_' :: DB.Connection -> UserId -> ProfileId -> Profile -> UTCTime -> IO ()
updateContactProfile_' db userId profileId Profile {displayName, fullName, image, contactLink, preferences} updatedAt = do
updateContactProfile_' db userId profileId Profile {displayName, fullName, shortDescr, image, contactLink, preferences} updatedAt = do
DB.execute
db
[sql|
UPDATE contact_profiles
SET display_name = ?, full_name = ?, image = ?, contact_link = ?, preferences = ?, updated_at = ?
SET display_name = ?, full_name = ?, short_descr = ?, image = ?, contact_link = ?, preferences = ?, updated_at = ?
WHERE user_id = ? AND contact_profile_id = ?
|]
(displayName, fullName, image, contactLink, preferences, updatedAt, userId, profileId)
(displayName, fullName, shortDescr, image, contactLink, preferences, updatedAt, userId, profileId)
-- update only member profile fields (when member doesn't have associated contact - we can reset contactLink and prefs)
updateMemberContactProfileReset_ :: DB.Connection -> UserId -> ProfileId -> Profile -> IO ()
@@ -709,15 +709,15 @@ updateMemberContactProfileReset_ db userId profileId profile = do
updateMemberContactProfileReset_' db userId profileId profile currentTs
updateMemberContactProfileReset_' :: DB.Connection -> UserId -> ProfileId -> Profile -> UTCTime -> IO ()
updateMemberContactProfileReset_' db userId profileId Profile {displayName, fullName, image} updatedAt = do
updateMemberContactProfileReset_' db userId profileId Profile {displayName, fullName, shortDescr, image} updatedAt = do
DB.execute
db
[sql|
UPDATE contact_profiles
SET display_name = ?, full_name = ?, image = ?, contact_link = NULL, preferences = NULL, updated_at = ?
SET display_name = ?, full_name = ?, short_descr = ?, image = ?, contact_link = NULL, preferences = NULL, updated_at = ?
WHERE user_id = ? AND contact_profile_id = ?
|]
(displayName, fullName, image, updatedAt, userId, profileId)
(displayName, fullName, shortDescr, image, updatedAt, userId, profileId)
-- update only member profile fields (when member has associated contact - we keep contactLink and prefs)
updateMemberContactProfile_ :: DB.Connection -> UserId -> ProfileId -> Profile -> IO ()
@@ -726,15 +726,15 @@ updateMemberContactProfile_ db userId profileId profile = do
updateMemberContactProfile_' db userId profileId profile currentTs
updateMemberContactProfile_' :: DB.Connection -> UserId -> ProfileId -> Profile -> UTCTime -> IO ()
updateMemberContactProfile_' db userId profileId Profile {displayName, fullName, image} updatedAt = do
updateMemberContactProfile_' db userId profileId Profile {displayName, fullName, shortDescr, image} updatedAt = do
DB.execute
db
[sql|
UPDATE contact_profiles
SET display_name = ?, full_name = ?, image = ?, updated_at = ?
SET display_name = ?, full_name = ?, short_descr = ?, image = ?, updated_at = ?
WHERE user_id = ? AND contact_profile_id = ?
|]
(displayName, fullName, image, updatedAt, userId, profileId)
(displayName, fullName, shortDescr, image, updatedAt, userId, profileId)
updateContactLDN_ :: DB.Connection -> User -> Int64 -> ContactName -> ContactName -> UTCTime -> IO ()
updateContactLDN_ db user@User {userId} contactId displayName newName updatedAt = do
@@ -785,7 +785,7 @@ contactRequestQuery =
SELECT
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version
@@ -903,7 +903,7 @@ getContact_ db vr user@User {userId} contactId deleted = do
[sql|
SELECT
-- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
-- Connection
+51 -55
View File
@@ -157,6 +157,7 @@ import Crypto.Random (ChaChaDRG)
import qualified Data.Aeson.TH as J
import Data.Bifunctor (second)
import Data.Bitraversable (bitraverse)
import Data.Char (toLower)
import Data.Either (rights)
import Data.Int (Int64)
import Data.List (partition, sortOn)
@@ -192,11 +193,11 @@ import Database.SQLite.Simple (Only (..), Query, (:.) (..))
import Database.SQLite.Simple.QQ (sql)
#endif
type MaybeGroupMemberRow = (Maybe Int64, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId, Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe LocalAlias, Maybe Preferences) :. (Maybe UTCTime, Maybe UTCTime) :. (Maybe UTCTime, Maybe Int64, Maybe Int64, Maybe Int64, Maybe UTCTime)
type MaybeGroupMemberRow = (Maybe Int64, Maybe Int64, Maybe MemberId, Maybe VersionChat, Maybe VersionChat, Maybe GroupMemberRole, Maybe GroupMemberCategory, Maybe GroupMemberStatus, Maybe BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, Maybe ContactName, Maybe ContactId, Maybe ProfileId) :. (Maybe ProfileId, Maybe ContactName, Maybe Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe LocalAlias, Maybe Preferences) :. (Maybe UTCTime, Maybe UTCTime) :. (Maybe UTCTime, Maybe Int64, Maybe Int64, Maybe Int64, Maybe UTCTime)
toMaybeGroupMember :: Int64 -> MaybeGroupMemberRow -> Maybe GroupMember
toMaybeGroupMember userContactId ((Just groupMemberId, Just groupId, Just memberId, Just minVer, Just maxVer, Just memberRole, Just memberCategory, Just memberStatus, Just showMessages, memberBlocked) :. (invitedById, invitedByGroupMemberId, Just localDisplayName, memberContactId, Just memberContactProfileId, Just profileId, Just displayName, Just fullName, image, contactLink, Just localAlias, contactPreferences) :. (Just createdAt, Just updatedAt) :. (supportChatTs, Just supportChatUnread, Just supportChatUnanswered, Just supportChatMentions, supportChatLastMsgFromMemberTs)) =
Just $ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId, profileId, displayName, fullName, image, contactLink, localAlias, contactPreferences) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs))
toMaybeGroupMember userContactId ((Just groupMemberId, Just groupId, Just memberId, Just minVer, Just maxVer, Just memberRole, Just memberCategory, Just memberStatus, Just showMessages, memberBlocked) :. (invitedById, invitedByGroupMemberId, Just localDisplayName, memberContactId, Just memberContactProfileId) :. (Just profileId, Just displayName, Just fullName, shortDescr, image, contactLink, Just localAlias, contactPreferences) :. (Just createdAt, Just updatedAt) :. (supportChatTs, Just supportChatUnread, Just supportChatUnanswered, Just supportChatMentions, supportChatLastMsgFromMemberTs)) =
Just $ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, showMessages, memberBlocked) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. (profileId, displayName, fullName, shortDescr, image, contactLink, localAlias, contactPreferences) :. (createdAt, updatedAt) :. (supportChatTs, supportChatUnread, supportChatUnanswered, supportChatMentions, supportChatLastMsgFromMemberTs))
toMaybeGroupMember _ _ = Nothing
createGroupLink :: DB.Connection -> User -> GroupInfo -> ConnId -> CreatedLinkContact -> GroupLinkId -> GroupMemberRole -> SubscriptionMode -> ExceptT StoreError IO GroupLink
@@ -317,7 +318,7 @@ setGroupLinkShortLink db gLnk@GroupLink {userContactLinkId, connLinkContact = CC
-- | creates completely new group with a single member - the current user
createNewGroup :: DB.Connection -> VersionRangeChat -> TVar ChaChaDRG -> User -> GroupProfile -> Maybe Profile -> ExceptT StoreError IO GroupInfo
createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = ExceptT $ do
let GroupProfile {displayName, fullName, description, image, groupPreferences, memberAdmission} = groupProfile
let GroupProfile {displayName, fullName, shortDescr, description, image, groupPreferences, memberAdmission} = groupProfile
fullGroupPreferences = mergeGroupPreferences groupPreferences
currentTs <- getCurrentTime
customUserProfileId <- mapM (createIncognitoProfile_ db userId currentTs) incognitoProfile
@@ -325,8 +326,8 @@ createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = Exc
groupId <- liftIO $ do
DB.execute
db
"INSERT INTO group_profiles (display_name, full_name, description, image, user_id, preferences, member_admission, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)"
(displayName, fullName, description, image, userId, groupPreferences, memberAdmission, currentTs, currentTs)
"INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, user_id, preferences, member_admission, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?)"
(displayName, fullName, shortDescr, description, image, userId, groupPreferences, memberAdmission, currentTs, currentTs)
profileId <- insertedRowId db
DB.execute
db
@@ -388,7 +389,7 @@ createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activ
DB.query db "SELECT group_id FROM groups WHERE inv_queue_info = ? AND user_id = ? LIMIT 1" (connRequest, userId)
createGroupInvitation_ :: ExceptT StoreError IO (GroupInfo, GroupMemberId)
createGroupInvitation_ = do
let GroupProfile {displayName, fullName, description, image, groupPreferences, memberAdmission} = groupProfile
let GroupProfile {displayName, fullName, shortDescr, description, image, groupPreferences, memberAdmission} = groupProfile
fullGroupPreferences = mergeGroupPreferences groupPreferences
ExceptT $
withLocalDisplayName db userId displayName $ \localDisplayName -> runExceptT $ do
@@ -396,8 +397,8 @@ createGroupInvitation db vr user@User {userId} contact@Contact {contactId, activ
groupId <- liftIO $ do
DB.execute
db
"INSERT INTO group_profiles (display_name, full_name, description, image, user_id, preferences, member_admission, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)"
(displayName, fullName, description, image, userId, groupPreferences, memberAdmission, currentTs, currentTs)
"INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, user_id, preferences, member_admission, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?)"
(displayName, fullName, shortDescr, description, image, userId, groupPreferences, memberAdmission, currentTs, currentTs)
profileId <- insertedRowId db
DB.execute
db
@@ -753,13 +754,13 @@ createGroupViaLink'
createGroup_ :: DB.Connection -> UserId -> GroupProfile -> Maybe (CreatedLinkContact, Maybe SharedMsgId) -> Maybe BusinessChatInfo -> UTCTime -> ExceptT StoreError IO (GroupId, Text)
createGroup_ db userId groupProfile prepared business currentTs = ExceptT $ do
let GroupProfile {displayName, fullName, description, image, groupPreferences, memberAdmission} = groupProfile
let GroupProfile {displayName, fullName, shortDescr, description, image, groupPreferences, memberAdmission} = groupProfile
withLocalDisplayName db userId displayName $ \localDisplayName -> runExceptT $ do
liftIO $ do
DB.execute
db
"INSERT INTO group_profiles (display_name, full_name, description, image, user_id, preferences, member_admission, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)"
(displayName, fullName, description, image, userId, groupPreferences, memberAdmission, currentTs, currentTs)
"INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, user_id, preferences, member_admission, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?)"
(displayName, fullName, shortDescr, description, image, userId, groupPreferences, memberAdmission, currentTs, currentTs)
profileId <- insertedRowId db
DB.execute
db
@@ -936,14 +937,14 @@ getUserGroupDetails db vr User {userId, userContactId} _contactId_ search_ = do
db
[sql|
SELECT
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.business_chat, g.business_member_id, g.customer_member_id,
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
mu.group_member_id, g.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category, mu.member_status, mu.show_messages, mu.member_restriction,
mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id, pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id, pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts
FROM groups g
@@ -951,15 +952,16 @@ getUserGroupDetails db vr User {userId, userContactId} _contactId_ search_ = do
JOIN group_members mu USING (group_id)
JOIN contact_profiles pu ON pu.contact_profile_id = COALESCE(mu.member_profile_id, mu.contact_profile_id)
WHERE g.user_id = ? AND mu.contact_id = ?
AND (LOWER(gp.display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(gp.full_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(gp.description) LIKE '%' || LOWER(?) || '%'
AND (LOWER(gp.display_name) LIKE '%' || ? || '%'
OR LOWER(gp.full_name) LIKE '%' || ? || '%'
OR LOWER(gp.short_descr) LIKE '%' || ? || '%'
OR LOWER(gp.description) LIKE '%' || ? || '%'
)
|]
(userId, userContactId, search, search, search)
(userId, userContactId, search, search, search, search)
mapM (addGroupChatTags db) g_
where
search = fromMaybe "" search_
search = maybe "" (map toLower) search_
getUserGroupsWithSummary :: DB.Connection -> VersionRangeChat -> User -> Maybe ContactId -> Maybe String -> IO [(GroupInfo, GroupSummary)]
getUserGroupsWithSummary db vr user _contactId_ search_ =
@@ -1008,7 +1010,7 @@ groupMemberQuery =
[sql|
SELECT
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
@@ -1239,7 +1241,7 @@ createJoiningMember
User {userId, userContactId}
GroupInfo {groupId, membership}
cReqChatVRange
Profile {displayName, fullName, image, contactLink, preferences}
Profile {displayName, fullName, shortDescr, image, contactLink, preferences}
cReqXContactId_
welcomeMsgId_
memberRole
@@ -1249,8 +1251,8 @@ createJoiningMember
liftIO $
DB.execute
db
"INSERT INTO contact_profiles (display_name, full_name, image, contact_link, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)"
(displayName, fullName, image, contactLink, userId, preferences, currentTs, currentTs)
"INSERT INTO contact_profiles (display_name, full_name, short_descr, image, contact_link, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)"
(displayName, fullName, shortDescr, image, contactLink, userId, preferences, currentTs, currentTs)
profileId <- liftIO $ insertedRowId db
createWithRandomId gVar $ \memId -> do
insertMember_ ldn profileId (MemberId memId) currentTs
@@ -1299,7 +1301,7 @@ createBusinessRequestGroup
gVar
user@User {userId, userContactId}
cReqChatVRange
Profile {displayName, fullName, image}
Profile {displayName, fullName, shortDescr, image}
profileId -- contact request profile id, to be used for member profile
ldn -- contact request local display name, to be used for group local display name
groupPreferences = do
@@ -1315,8 +1317,8 @@ createBusinessRequestGroup
liftIO $
DB.execute
db
"INSERT INTO group_profiles (display_name, full_name, image, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?)"
(displayName, fullName, image, userId, groupPreferences, currentTs, currentTs)
"INSERT INTO group_profiles (display_name, full_name, short_descr, image, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)"
(displayName, fullName, shortDescr, image, userId, groupPreferences, currentTs, currentTs)
groupProfileId <- liftIO $ insertedRowId db
liftIO $
DB.execute
@@ -1503,12 +1505,12 @@ createNewGroupMember db user gInfo invitingMember memInfo@MemberInfo {profile} m
liftIO $ createNewMember_ db user gInfo newMember currentTs
createNewMemberProfile_ :: DB.Connection -> User -> Profile -> UTCTime -> ExceptT StoreError IO (Text, ProfileId)
createNewMemberProfile_ db User {userId} Profile {displayName, fullName, image, contactLink, preferences} createdAt =
createNewMemberProfile_ db User {userId} Profile {displayName, fullName, shortDescr, image, contactLink, preferences} createdAt =
ExceptT . withLocalDisplayName db userId displayName $ \ldn -> do
DB.execute
db
"INSERT INTO contact_profiles (display_name, full_name, image, contact_link, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)"
(displayName, fullName, image, contactLink, userId, preferences, createdAt, createdAt)
"INSERT INTO contact_profiles (display_name, full_name, short_descr, image, contact_link, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)"
(displayName, fullName, shortDescr, image, contactLink, userId, preferences, createdAt, createdAt)
profileId <- insertedRowId db
pure $ Right (ldn, profileId)
@@ -1818,7 +1820,7 @@ getViaGroupMember db vr User {userId, userContactId} Contact {contactId} = do
[sql|
SELECT
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
@@ -1828,12 +1830,12 @@ getViaGroupMember db vr User {userId, userContactId} Contact {contactId} = do
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
-- GroupInfo {membership = GroupMember {memberProfile}}
pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts,
-- via GroupMember
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
@@ -1881,7 +1883,7 @@ getViaGroupContact db vr user@User {userId} GroupMember {groupMemberId} = do
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getContact db vr user) contactId_
updateGroupProfile :: DB.Connection -> User -> GroupInfo -> GroupProfile -> ExceptT StoreError IO GroupInfo
updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName, groupProfile = GroupProfile {displayName}} p'@GroupProfile {displayName = newName, fullName, description, image, groupPreferences, memberAdmission}
updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName, groupProfile = GroupProfile {displayName}} p'@GroupProfile {displayName = newName, fullName, shortDescr, description, image, groupPreferences, memberAdmission}
| displayName == newName = liftIO $ do
currentTs <- getCurrentTime
updateGroupProfile_ currentTs
@@ -1899,14 +1901,14 @@ updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName,
db
[sql|
UPDATE group_profiles
SET display_name = ?, full_name = ?, description = ?, image = ?, preferences = ?, member_admission = ?, updated_at = ?
SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, preferences = ?, member_admission = ?, updated_at = ?
WHERE group_profile_id IN (
SELECT group_profile_id
FROM groups
WHERE user_id = ? AND group_id = ?
)
|]
(newName, fullName, description, image, groupPreferences, memberAdmission, currentTs, userId, groupId)
(newName, fullName, shortDescr, description, image, groupPreferences, memberAdmission, currentTs, userId, groupId)
updateGroup_ ldn currentTs = do
DB.execute
db
@@ -1932,10 +1934,10 @@ updateGroupPreferences db User {userId} g@GroupInfo {groupId, groupProfile = p}
pure (g :: GroupInfo) {groupProfile = p {groupPreferences = Just ps}, fullGroupPreferences = mergeGroupPreferences $ Just ps}
updateGroupProfileFromMember :: DB.Connection -> User -> GroupInfo -> Profile -> ExceptT StoreError IO GroupInfo
updateGroupProfileFromMember db user g@GroupInfo {groupId} Profile {displayName = n, fullName = fn, image = img} = do
updateGroupProfileFromMember db user g@GroupInfo {groupId} Profile {displayName = n, fullName = fn, shortDescr = sd, image = img} = do
p <- getGroupProfile -- to avoid any race conditions with UI
let g' = g {groupProfile = p} :: GroupInfo
p' = p {displayName = n, fullName = fn, image = img} :: GroupProfile
p' = p {displayName = n, fullName = fn, shortDescr = sd, image = img} :: GroupProfile
updateGroupProfile db user g' p'
where
getGroupProfile =
@@ -1944,14 +1946,14 @@ updateGroupProfileFromMember db user g@GroupInfo {groupId} Profile {displayName
DB.query
db
[sql|
SELECT gp.display_name, gp.full_name, gp.description, gp.image, gp.preferences, gp.member_admission
SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image, gp.preferences, gp.member_admission
FROM group_profiles gp
JOIN groups g ON gp.group_profile_id = g.group_profile_id
WHERE g.group_id = ?
|]
(Only groupId)
toGroupProfile (displayName, fullName, description, image, groupPreferences, memberAdmission) =
GroupProfile {displayName, fullName, description, image, groupPreferences, memberAdmission}
toGroupProfile (displayName, fullName, shortDescr, description, image, groupPreferences, memberAdmission) =
GroupProfile {displayName, fullName, shortDescr, description, image, groupPreferences, memberAdmission}
getGroupInfo :: DB.Connection -> VersionRangeChat -> User -> Int64 -> ExceptT StoreError IO GroupInfo
getGroupInfo db vr User {userId, userContactId} groupId = ExceptT $ do
@@ -2052,11 +2054,8 @@ getActiveMembersByName db vr user@User {userId} groupMemberName = do
ts GroupInfo {chatTs, updatedAt} = fromMaybe updatedAt chatTs
getMatchingContacts :: DB.Connection -> VersionRangeChat -> User -> Contact -> IO [Contact]
getMatchingContacts db vr user@User {userId} Contact {contactId, profile = LocalProfile {displayName, fullName, image}} = do
contactIds <-
map fromOnly <$> case image of
Just img -> DB.query db (q <> " AND p.image = ?") (userId, contactId, CSActive, displayName, fullName, img)
Nothing -> DB.query db (q <> " AND p.image is NULL") (userId, contactId, CSActive, displayName, fullName)
getMatchingContacts db vr user@User {userId} Contact {contactId, profile = LocalProfile {displayName, fullName, shortDescr, image}} = do
contactIds <- map fromOnly <$> DB.query db q (userId, contactId, CSActive, displayName, fullName, shortDescr, image)
rights <$> mapM (runExceptT . getContact db vr user) contactIds
where
-- this query is different from one in getMatchingMemberContacts
@@ -2069,14 +2068,12 @@ getMatchingContacts db vr user@User {userId} Contact {contactId, profile = Local
WHERE ct.user_id = ? AND ct.contact_id != ?
AND ct.contact_status = ? AND ct.deleted = 0 AND ct.is_user = 0
AND p.display_name = ? AND p.full_name = ?
AND p.short_descr IS ? AND p.image IS ?
|]
getMatchingMembers :: DB.Connection -> VersionRangeChat -> User -> Contact -> IO [GroupMember]
getMatchingMembers db vr user@User {userId} Contact {profile = LocalProfile {displayName, fullName, image}} = do
memberIds <-
map fromOnly <$> case image of
Just img -> DB.query db (q <> " AND p.image = ?") (userId, GCUserMember, displayName, fullName, img)
Nothing -> DB.query db (q <> " AND p.image is NULL") (userId, GCUserMember, displayName, fullName)
getMatchingMembers db vr user@User {userId} Contact {profile = LocalProfile {displayName, fullName, shortDescr, image}} = do
memberIds <- map fromOnly <$> DB.query db q (userId, GCUserMember, displayName, fullName, shortDescr, image)
filter memberCurrent . rights <$> mapM (runExceptT . getGroupMemberById db vr user) memberIds
where
-- only match with members without associated contact
@@ -2088,15 +2085,13 @@ getMatchingMembers db vr user@User {userId} Contact {profile = LocalProfile {dis
WHERE m.user_id = ? AND m.contact_id IS NULL
AND m.member_category != ?
AND p.display_name = ? AND p.full_name = ?
AND p.short_descr IS ? AND p.image IS ?
|]
getMatchingMemberContacts :: DB.Connection -> VersionRangeChat -> User -> GroupMember -> IO [Contact]
getMatchingMemberContacts _ _ _ GroupMember {memberContactId = Just _} = pure []
getMatchingMemberContacts db vr user@User {userId} GroupMember {memberProfile = LocalProfile {displayName, fullName, image}} = do
contactIds <-
map fromOnly <$> case image of
Just img -> DB.query db (q <> " AND p.image = ?") (userId, CSActive, displayName, fullName, img)
Nothing -> DB.query db (q <> " AND p.image is NULL") (userId, CSActive, displayName, fullName)
getMatchingMemberContacts db vr user@User {userId} GroupMember {memberProfile = LocalProfile {displayName, fullName, shortDescr, image}} = do
contactIds <- map fromOnly <$> DB.query db q (userId, CSActive, displayName, fullName, shortDescr, image)
rights <$> mapM (runExceptT . getContact db vr user) contactIds
where
q =
@@ -2107,6 +2102,7 @@ getMatchingMemberContacts db vr user@User {userId} GroupMember {memberProfile =
WHERE ct.user_id = ?
AND ct.contact_status = ? AND ct.deleted = 0 AND ct.is_user = 0
AND p.display_name = ? AND p.full_name = ?
AND p.short_descr IS ? AND p.image IS ?
|]
createSentProbe :: DB.Connection -> TVar ChaChaDRG -> UserId -> ContactOrMember -> ExceptT StoreError IO (Probe, Int64)
+25 -19
View File
@@ -144,6 +144,7 @@ import Control.Monad.IO.Class
import Crypto.Random (ChaChaDRG)
import Data.Bifunctor (first)
import Data.ByteString.Char8 (ByteString)
import Data.Char (toLower)
import Data.Either (fromRight, rights)
import Data.Int (Int64)
import Data.List (foldl', sortBy)
@@ -646,7 +647,7 @@ getChatItemQuote_ db User {userId, userContactId} chatDirection QuotedMsg {msgRe
-- GroupMember
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category,
m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id,
p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts
FROM group_members m
@@ -789,13 +790,15 @@ findDirectChatPreviews_ db User {userId} pagination clq =
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
WHERE ct.user_id = ? AND ct.is_user = 0 AND ct.deleted = 0 AND ct.contact_used = 1
AND (
LOWER(ct.local_display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(cp.display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(cp.full_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(cp.local_alias) LIKE '%' || LOWER(?) || '%'
LOWER(ct.local_display_name) LIKE '%' || ? || '%'
OR LOWER(cp.display_name) LIKE '%' || ? || '%'
OR LOWER(cp.full_name) LIKE '%' || ? || '%'
OR LOWER(cp.short_descr) LIKE '%' || ? || '%'
OR LOWER(cp.local_alias) LIKE '%' || ? || '%'
)
|]
p = baseParams :. (userId, search, search, search, search)
s = map toLower search
p = baseParams :. (userId, s, s, s, s, s)
queryWithPagination q p
queryWithPagination :: ToRow p => Query -> p -> IO [(ContactId, UTCTime, Maybe ChatItemId) :. ChatStatsRow]
queryWithPagination query params = case pagination of
@@ -895,13 +898,15 @@ findGroupChatPreviews_ db User {userId} pagination clq =
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
WHERE g.user_id = ?
AND (
LOWER(g.local_display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(gp.display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(gp.full_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(gp.description) LIKE '%' || LOWER(?) || '%'
LOWER(g.local_display_name) LIKE '%' || ? || '%'
OR LOWER(gp.display_name) LIKE '%' || ? || '%'
OR LOWER(gp.full_name) LIKE '%' || ? || '%'
OR LOWER(gp.short_descr) LIKE '%' || ? || '%'
OR LOWER(gp.description) LIKE '%' || ? || '%'
)
|]
p = baseParams :. (userId, search, search, search, search)
s = map toLower search
p = baseParams :. (userId, s, s, s, s, s)
queryWithPagination q p
queryWithPagination :: ToRow p => Query -> p -> IO [(GroupId, UTCTime, Maybe ChatItemId) :. GroupStatsRow]
queryWithPagination query params = case pagination of
@@ -1051,7 +1056,7 @@ getContactRequestChatPreviews_ db User {userId} pagination clq = case clq of
SELECT
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version
@@ -1065,12 +1070,13 @@ getContactRequestChatPreviews_ db User {userId} pagination clq = case clq of
AND cr.contact_id IS NULL
AND cr.business_group_id IS NULL
AND (
LOWER(cr.local_display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(p.display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(p.full_name) LIKE '%' || LOWER(?) || '%'
LOWER(cr.local_display_name) LIKE '%' || ? || '%'
OR LOWER(p.display_name) LIKE '%' || ? || '%'
OR LOWER(p.full_name) LIKE '%' || ? || '%'
OR LOWER(p.short_descr) LIKE '%' || ? || '%'
)
|]
params search = (userId, userId, search, search, search)
params search = let s = map toLower search in (userId, userId, s, s, s, s)
getPreviews search = case pagination of
PTLast count -> DB.query db (query <> " ORDER BY cr.updated_at DESC LIMIT ?") (params search :. Only count)
PTAfter ts count -> DB.query db (query <> " AND cr.updated_at > ? ORDER BY cr.updated_at ASC LIMIT ?") (params search :. (ts, count))
@@ -2896,7 +2902,7 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
-- GroupMember
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category,
m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id,
p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
-- quoted ChatItem
@@ -2904,13 +2910,13 @@ getGroupChatItem db User {userId, userContactId} groupId itemId = ExceptT $ do
-- quoted GroupMember
rm.group_member_id, rm.group_id, rm.member_id, rm.peer_chat_min_version, rm.peer_chat_max_version, rm.member_role, rm.member_category,
rm.member_status, rm.show_messages, rm.member_restriction, rm.invited_by, rm.invited_by_group_member_id, rm.local_display_name, rm.contact_id, rm.contact_profile_id, rp.contact_profile_id,
rp.display_name, rp.full_name, rp.image, rp.contact_link, rp.local_alias, rp.preferences,
rp.display_name, rp.full_name, rp.short_descr, rp.image, rp.contact_link, rp.local_alias, rp.preferences,
rm.created_at, rm.updated_at,
rm.support_chat_ts, rm.support_chat_items_unread, rm.support_chat_items_member_attention, rm.support_chat_items_mentions, rm.support_chat_last_msg_from_member_ts,
-- deleted by GroupMember
dbm.group_member_id, dbm.group_id, dbm.member_id, dbm.peer_chat_min_version, dbm.peer_chat_max_version, dbm.member_role, dbm.member_category,
dbm.member_status, dbm.show_messages, dbm.member_restriction, dbm.invited_by, dbm.invited_by_group_member_id, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id,
dbp.display_name, dbp.full_name, dbp.image, dbp.contact_link, dbp.local_alias, dbp.preferences,
dbp.display_name, dbp.full_name, dbp.short_descr, dbp.image, dbp.contact_link, dbp.local_alias, dbp.preferences,
dbm.created_at, dbm.updated_at,
dbm.support_chat_ts, dbm.support_chat_items_unread, dbm.support_chat_items_member_attention, dbm.support_chat_items_mentions, dbm.support_chat_last_msg_from_member_ts
FROM chat_items i
@@ -11,6 +11,7 @@ import Simplex.Chat.Store.Postgres.Migrations.M20250513_group_scope
import Simplex.Chat.Store.Postgres.Migrations.M20250526_short_links
import Simplex.Chat.Store.Postgres.Migrations.M20250702_contact_requests_remove_cascade_delete
import Simplex.Chat.Store.Postgres.Migrations.M20250704_groups_conn_link_prepared_connection
import Simplex.Chat.Store.Postgres.Migrations.M20250709_profile_short_descr
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
schemaMigrations :: [(String, Text, Maybe Text)]
@@ -21,7 +22,8 @@ schemaMigrations =
("20250513_group_scope", m20250513_group_scope, Just down_m20250513_group_scope),
("20250526_short_links", m20250526_short_links, Just down_m20250526_short_links),
("20250702_contact_requests_remove_cascade_delete", m20250702_contact_requests_remove_cascade_delete, Just down_m20250702_contact_requests_remove_cascade_delete),
("20250704_groups_conn_link_prepared_connection", m20250704_groups_conn_link_prepared_connection, Just down_m20250704_groups_conn_link_prepared_connection)
("20250704_groups_conn_link_prepared_connection", m20250704_groups_conn_link_prepared_connection, Just down_m20250704_groups_conn_link_prepared_connection),
("20250709_profile_short_descr", m20250709_profile_short_descr, Just down_m20250709_profile_short_descr)
]
-- | The list of migrations in ascending order by date
@@ -0,0 +1,23 @@
{-# LANGUAGE QuasiQuotes #-}
module Simplex.Chat.Store.Postgres.Migrations.M20250709_profile_short_descr where
import Data.Text (Text)
import qualified Data.Text as T
import Text.RawString.QQ (r)
m20250709_profile_short_descr :: Text
m20250709_profile_short_descr =
T.pack
[r|
ALTER TABLE contact_profiles ADD COLUMN short_descr TEXT;
ALTER TABLE group_profiles ADD COLUMN short_descr TEXT;
|]
down_m20250709_profile_short_descr :: Text
down_m20250709_profile_short_descr =
T.pack
[r|
ALTER TABLE contact_profiles DROP COLUMN short_descr;
ALTER TABLE group_profiles DROP COLUMN short_descr;
|]
@@ -331,7 +331,8 @@ CREATE TABLE test_chat_schema.contact_profiles (
incognito smallint,
local_alias text DEFAULT ''::text NOT NULL,
preferences text,
contact_link bytea
contact_link bytea,
short_descr text
);
@@ -577,7 +578,8 @@ CREATE TABLE test_chat_schema.group_profiles (
user_id bigint,
preferences text,
description text,
member_admission text
member_admission text,
short_descr text
);
+7 -7
View File
@@ -129,7 +129,7 @@ createUserRecord :: DB.Connection -> AgentUserId -> Profile -> Bool -> ExceptT S
createUserRecord db auId p activeUser = createUserRecordAt db auId p activeUser =<< liftIO getCurrentTime
createUserRecordAt :: DB.Connection -> AgentUserId -> Profile -> Bool -> UTCTime -> ExceptT StoreError IO User
createUserRecordAt db (AgentUserId auId) Profile {displayName, fullName, image, preferences = userPreferences} activeUser currentTs =
createUserRecordAt db (AgentUserId auId) Profile {displayName, fullName, shortDescr, image, preferences = userPreferences} activeUser currentTs =
checkConstraint SEDuplicateName . liftIO $ do
when activeUser $ DB.execute_ db "UPDATE users SET active_user = 0"
let showNtfs = True
@@ -147,8 +147,8 @@ createUserRecordAt db (AgentUserId auId) Profile {displayName, fullName, image,
(displayName, displayName, userId, currentTs, currentTs)
DB.execute
db
"INSERT INTO contact_profiles (display_name, full_name, image, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?)"
(displayName, fullName, image, userId, userPreferences, currentTs, currentTs)
"INSERT INTO contact_profiles (display_name, full_name, short_descr, image, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)"
(displayName, fullName, shortDescr, image, userId, userPreferences, currentTs, currentTs)
profileId <- insertedRowId db
DB.execute
db
@@ -156,7 +156,7 @@ createUserRecordAt db (AgentUserId auId) Profile {displayName, fullName, image,
(profileId, displayName, userId, BI True, currentTs, currentTs, currentTs)
contactId <- insertedRowId db
DB.execute db "UPDATE users SET contact_id = ? WHERE user_id = ?" (contactId, userId)
pure $ toUser $ (userId, auId, contactId, profileId, BI activeUser, order, displayName, fullName, image, Nothing, userPreferences) :. (BI showNtfs, BI sendRcptsContacts, BI sendRcptsSmallGroups, Nothing, Nothing, Nothing, Nothing)
pure $ toUser $ (userId, auId, contactId, profileId, BI activeUser, order, displayName, fullName, shortDescr, image, Nothing, userPreferences) :. (BI showNtfs, BI sendRcptsContacts, BI sendRcptsSmallGroups, Nothing, Nothing, Nothing, Nothing)
-- TODO [mentions]
getUsersInfo :: DB.Connection -> IO [UserInfo]
@@ -344,14 +344,14 @@ getUserContactProfiles db User {userId} =
<$> DB.query
db
[sql|
SELECT display_name, full_name, image, contact_link, preferences
SELECT display_name, full_name, short_descr, image, contact_link, preferences
FROM contact_profiles
WHERE user_id = ?
|]
(Only userId)
where
toContactProfile :: (ContactName, Text, Maybe ImageData, Maybe ConnLinkContact, Maybe Preferences) -> Profile
toContactProfile (displayName, fullName, image, contactLink, preferences) = Profile {displayName, fullName, image, contactLink, preferences}
toContactProfile :: (ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe Preferences) -> Profile
toContactProfile (displayName, fullName, shortDescr, image, contactLink, preferences) = Profile {displayName, fullName, shortDescr, image, contactLink, preferences}
createUserContactLink :: DB.Connection -> User -> ConnId -> CreatedLinkContact -> SubscriptionMode -> ExceptT StoreError IO ()
createUserContactLink db User {userId} agentConnId (CCLink cReq shortLink) subMode =
+3 -1
View File
@@ -134,6 +134,7 @@ import Simplex.Chat.Store.SQLite.Migrations.M20250513_group_scope
import Simplex.Chat.Store.SQLite.Migrations.M20250526_short_links
import Simplex.Chat.Store.SQLite.Migrations.M20250702_contact_requests_remove_cascade_delete
import Simplex.Chat.Store.SQLite.Migrations.M20250704_groups_conn_link_prepared_connection
import Simplex.Chat.Store.SQLite.Migrations.M20250709_profile_short_descr
import Simplex.Messaging.Agent.Store.Shared (Migration (..))
schemaMigrations :: [(String, Query, Maybe Query)]
@@ -267,7 +268,8 @@ schemaMigrations =
("20250513_group_scope", m20250513_group_scope, Just down_m20250513_group_scope),
("20250526_short_links", m20250526_short_links, Just down_m20250526_short_links),
("20250702_contact_requests_remove_cascade_delete", m20250702_contact_requests_remove_cascade_delete, Just down_m20250702_contact_requests_remove_cascade_delete),
("20250704_groups_conn_link_prepared_connection", m20250704_groups_conn_link_prepared_connection, Just down_m20250704_groups_conn_link_prepared_connection)
("20250704_groups_conn_link_prepared_connection", m20250704_groups_conn_link_prepared_connection, Just down_m20250704_groups_conn_link_prepared_connection),
("20250709_profile_short_descr", m20250709_profile_short_descr, Just down_m20250709_profile_short_descr)
]
-- | The list of migrations in ascending order by date
@@ -0,0 +1,20 @@
{-# LANGUAGE QuasiQuotes #-}
module Simplex.Chat.Store.SQLite.Migrations.M20250709_profile_short_descr where
import Database.SQLite.Simple (Query)
import Database.SQLite.Simple.QQ (sql)
m20250709_profile_short_descr :: Query
m20250709_profile_short_descr =
[sql|
ALTER TABLE contact_profiles ADD COLUMN short_descr TEXT;
ALTER TABLE group_profiles ADD COLUMN short_descr TEXT;
|]
down_m20250709_profile_short_descr :: Query
down_m20250709_profile_short_descr =
[sql|
ALTER TABLE contact_profiles DROP COLUMN short_descr;
ALTER TABLE group_profiles DROP COLUMN short_descr;
|]
@@ -60,7 +60,7 @@ Plan:
Query:
SELECT
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
@@ -70,12 +70,12 @@ Query:
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
-- GroupInfo {membership = GroupMember {memberProfile}}
pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts,
-- from GroupMember
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts
FROM group_members m
@@ -196,7 +196,7 @@ Plan:
Query:
SELECT
-- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
-- Connection
@@ -287,6 +287,7 @@ Query:
UPDATE contact_profiles
SET display_name = ?,
full_name = ?,
short_descr = ?,
image = ?,
contact_link = ?,
updated_at = ?
@@ -383,7 +384,7 @@ Plan:
Query:
SELECT
c.contact_profile_id, c.local_display_name, c.via_group, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, c.contact_used, c.contact_status, c.enable_ntfs, c.send_rcpts, c.favorite,
c.contact_profile_id, c.local_display_name, c.via_group, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, c.contact_used, c.contact_status, c.enable_ntfs, c.send_rcpts, c.favorite,
p.preferences, c.user_preferences, c.created_at, c.updated_at, c.chat_ts, c.conn_full_link_to_connect, c.conn_short_link_to_connect, c.welcome_shared_msg_id, c.request_shared_msg_id, c.contact_request_id,
c.contact_group_member_id, c.contact_grp_inv_sent, c.ui_themes, c.chat_deleted, c.custom_data, c.chat_item_ttl
FROM contacts c
@@ -398,7 +399,7 @@ Query:
SELECT
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version
@@ -644,7 +645,7 @@ SEARCH m USING INTEGER PRIMARY KEY (rowid=?)
SEARCH ct USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT gp.display_name, gp.full_name, gp.description, gp.image, gp.preferences, gp.member_admission
SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image, gp.preferences, gp.member_admission
FROM group_profiles gp
JOIN groups g ON gp.group_profile_id = g.group_profile_id
WHERE g.group_id = ?
@@ -673,7 +674,7 @@ Query:
-- GroupMember
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category,
m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id,
p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts
FROM group_members m
@@ -867,7 +868,7 @@ Query:
-- GroupMember
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category,
m.member_status, m.show_messages, m.member_restriction, m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id,
p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
-- quoted ChatItem
@@ -875,13 +876,13 @@ Query:
-- quoted GroupMember
rm.group_member_id, rm.group_id, rm.member_id, rm.peer_chat_min_version, rm.peer_chat_max_version, rm.member_role, rm.member_category,
rm.member_status, rm.show_messages, rm.member_restriction, rm.invited_by, rm.invited_by_group_member_id, rm.local_display_name, rm.contact_id, rm.contact_profile_id, rp.contact_profile_id,
rp.display_name, rp.full_name, rp.image, rp.contact_link, rp.local_alias, rp.preferences,
rp.display_name, rp.full_name, rp.short_descr, rp.image, rp.contact_link, rp.local_alias, rp.preferences,
rm.created_at, rm.updated_at,
rm.support_chat_ts, rm.support_chat_items_unread, rm.support_chat_items_member_attention, rm.support_chat_items_mentions, rm.support_chat_last_msg_from_member_ts,
-- deleted by GroupMember
dbm.group_member_id, dbm.group_id, dbm.member_id, dbm.peer_chat_min_version, dbm.peer_chat_max_version, dbm.member_role, dbm.member_category,
dbm.member_status, dbm.show_messages, dbm.member_restriction, dbm.invited_by, dbm.invited_by_group_member_id, dbm.local_display_name, dbm.contact_id, dbm.contact_profile_id, dbp.contact_profile_id,
dbp.display_name, dbp.full_name, dbp.image, dbp.contact_link, dbp.local_alias, dbp.preferences,
dbp.display_name, dbp.full_name, dbp.short_descr, dbp.image, dbp.contact_link, dbp.local_alias, dbp.preferences,
dbm.created_at, dbm.updated_at,
dbm.support_chat_ts, dbm.support_chat_items_unread, dbm.support_chat_items_member_attention, dbm.support_chat_items_mentions, dbm.support_chat_last_msg_from_member_ts
FROM chat_items i
@@ -933,7 +934,7 @@ SEARCH ri USING COVERING INDEX idx_chat_items_direct_shared_msg_id (user_id=? AN
Query:
SELECT
-- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
-- Connection
@@ -963,7 +964,7 @@ USE TEMP B-TREE FOR ORDER BY
Query:
SELECT
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
@@ -973,12 +974,12 @@ Query:
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
-- GroupInfo {membership = GroupMember {memberProfile}}
pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts,
-- via GroupMember
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
@@ -1013,14 +1014,14 @@ SEARCH cc USING COVERING INDEX idx_connections_group_member (user_id=? AND group
Query:
SELECT
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
g.business_chat, g.business_member_id, g.customer_member_id,
g.ui_themes, g.custom_data, g.chat_item_ttl, g.members_require_attention,
mu.group_member_id, g.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category, mu.member_status, mu.show_messages, mu.member_restriction,
mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id, pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id, pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts
FROM groups g
@@ -1028,9 +1029,10 @@ Query:
JOIN group_members mu USING (group_id)
JOIN contact_profiles pu ON pu.contact_profile_id = COALESCE(mu.member_profile_id, mu.contact_profile_id)
WHERE g.user_id = ? AND mu.contact_id = ?
AND (LOWER(gp.display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(gp.full_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(gp.description) LIKE '%' || LOWER(?) || '%'
AND (LOWER(gp.display_name) LIKE '%' || ? || '%'
OR LOWER(gp.full_name) LIKE '%' || ? || '%'
OR LOWER(gp.short_descr) LIKE '%' || ? || '%'
OR LOWER(gp.description) LIKE '%' || ? || '%'
)
Plan:
@@ -1444,7 +1446,7 @@ SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_profiles
SET display_name = ?, full_name = ?, description = ?, image = ?, preferences = ?, member_admission = ?, updated_at = ?
SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, preferences = ?, member_admission = ?, updated_at = ?
WHERE group_profile_id IN (
SELECT group_profile_id
FROM groups
@@ -1524,7 +1526,7 @@ Plan:
Query:
SELECT
-- Contact
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
ct.contact_id, ct.contact_profile_id, ct.local_display_name, ct.via_group, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.local_alias, ct.contact_used, ct.contact_status, ct.enable_ntfs, ct.send_rcpts, ct.favorite,
cp.preferences, ct.user_preferences, ct.created_at, ct.updated_at, ct.chat_ts, ct.conn_full_link_to_connect, ct.conn_short_link_to_connect, ct.welcome_shared_msg_id, ct.request_shared_msg_id, ct.contact_request_id,
ct.contact_group_member_id, ct.contact_grp_inv_sent, ct.ui_themes, ct.chat_deleted, ct.custom_data, ct.chat_item_ttl,
-- Connection
@@ -1627,7 +1629,7 @@ Query:
SELECT
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version
@@ -1641,9 +1643,10 @@ Query:
AND cr.contact_id IS NULL
AND cr.business_group_id IS NULL
AND (
LOWER(cr.local_display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(p.display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(p.full_name) LIKE '%' || LOWER(?) || '%'
LOWER(cr.local_display_name) LIKE '%' || ? || '%'
OR LOWER(p.display_name) LIKE '%' || ? || '%'
OR LOWER(p.full_name) LIKE '%' || ? || '%'
OR LOWER(p.short_descr) LIKE '%' || ? || '%'
)
AND cr.updated_at < ? ORDER BY cr.updated_at DESC LIMIT ?
Plan:
@@ -1655,7 +1658,7 @@ Query:
SELECT
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version
@@ -1669,9 +1672,10 @@ Query:
AND cr.contact_id IS NULL
AND cr.business_group_id IS NULL
AND (
LOWER(cr.local_display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(p.display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(p.full_name) LIKE '%' || LOWER(?) || '%'
LOWER(cr.local_display_name) LIKE '%' || ? || '%'
OR LOWER(p.display_name) LIKE '%' || ? || '%'
OR LOWER(p.full_name) LIKE '%' || ? || '%'
OR LOWER(p.short_descr) LIKE '%' || ? || '%'
)
AND cr.updated_at > ? ORDER BY cr.updated_at ASC LIMIT ?
Plan:
@@ -1683,7 +1687,7 @@ Query:
SELECT
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version
@@ -1697,9 +1701,10 @@ Query:
AND cr.contact_id IS NULL
AND cr.business_group_id IS NULL
AND (
LOWER(cr.local_display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(p.display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(p.full_name) LIKE '%' || LOWER(?) || '%'
LOWER(cr.local_display_name) LIKE '%' || ? || '%'
OR LOWER(p.display_name) LIKE '%' || ? || '%'
OR LOWER(p.full_name) LIKE '%' || ? || '%'
OR LOWER(p.short_descr) LIKE '%' || ? || '%'
)
ORDER BY cr.updated_at DESC LIMIT ?
Plan:
@@ -1742,10 +1747,11 @@ Query:
JOIN contact_profiles cp ON ct.contact_profile_id = cp.contact_profile_id
WHERE ct.user_id = ? AND ct.is_user = 0 AND ct.deleted = 0 AND ct.contact_used = 1
AND (
LOWER(ct.local_display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(cp.display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(cp.full_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(cp.local_alias) LIKE '%' || LOWER(?) || '%'
LOWER(ct.local_display_name) LIKE '%' || ? || '%'
OR LOWER(cp.display_name) LIKE '%' || ? || '%'
OR LOWER(cp.full_name) LIKE '%' || ? || '%'
OR LOWER(cp.short_descr) LIKE '%' || ? || '%'
OR LOWER(cp.local_alias) LIKE '%' || ? || '%'
)
ORDER BY ct.chat_ts DESC LIMIT ?
Plan:
@@ -2113,10 +2119,11 @@ Query:
JOIN group_profiles gp ON gp.group_profile_id = g.group_profile_id
WHERE g.user_id = ?
AND (
LOWER(g.local_display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(gp.display_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(gp.full_name) LIKE '%' || LOWER(?) || '%'
OR LOWER(gp.description) LIKE '%' || LOWER(?) || '%'
LOWER(g.local_display_name) LIKE '%' || ? || '%'
OR LOWER(gp.display_name) LIKE '%' || ? || '%'
OR LOWER(gp.full_name) LIKE '%' || ? || '%'
OR LOWER(gp.short_descr) LIKE '%' || ? || '%'
OR LOWER(gp.description) LIKE '%' || ? || '%'
)
ORDER BY g.chat_ts DESC LIMIT ?
Plan:
@@ -3169,7 +3176,7 @@ Plan:
SEARCH connections USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, cp.preferences -- , ct.user_preferences
SELECT cp.contact_profile_id, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.local_alias, cp.preferences -- , ct.user_preferences
FROM contact_profiles cp
WHERE cp.user_id = ? AND cp.contact_profile_id = ?
@@ -3183,19 +3190,8 @@ Query:
WHERE ct.user_id = ?
AND ct.contact_status = ? AND ct.deleted = 0 AND ct.is_user = 0
AND p.display_name = ? AND p.full_name = ?
AND p.image = ?
Plan:
SEARCH ct USING INDEX idx_contacts_chat_ts (user_id=?)
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT ct.contact_id
FROM contacts ct
JOIN contact_profiles p ON ct.contact_profile_id = p.contact_profile_id
WHERE ct.user_id = ?
AND ct.contact_status = ? AND ct.deleted = 0 AND ct.is_user = 0
AND p.display_name = ? AND p.full_name = ?
AND p.image is NULL
AND p.short_descr IS ? AND p.image IS ?
Plan:
SEARCH ct USING INDEX idx_contacts_chat_ts (user_id=?)
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
@@ -3223,7 +3219,7 @@ SEARCH f USING PRIMARY KEY (file_id=?)
SEARCH d USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT display_name, full_name, image, contact_link, preferences
SELECT display_name, full_name, short_descr, image, contact_link, preferences
FROM contact_profiles
WHERE user_id = ?
@@ -3324,19 +3320,8 @@ Query:
WHERE m.user_id = ? AND m.contact_id IS NULL
AND m.member_category != ?
AND p.display_name = ? AND p.full_name = ?
AND p.image = ?
Plan:
SEARCH m USING INDEX idx_group_members_user_id (user_id=?)
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT m.group_member_id
FROM group_members m
JOIN contact_profiles p ON p.contact_profile_id = COALESCE(m.member_profile_id, m.contact_profile_id)
WHERE m.user_id = ? AND m.contact_id IS NULL
AND m.member_category != ?
AND p.display_name = ? AND p.full_name = ?
AND p.image is NULL
AND p.short_descr IS ? AND p.image IS ?
Plan:
SEARCH m USING INDEX idx_group_members_user_id (user_id=?)
SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
@@ -4133,8 +4118,8 @@ Query:
Plan:
Query:
INSERT INTO contact_profiles (display_name, full_name, image, user_id, incognito, created_at, updated_at)
VALUES (?,?,?,?,?,?,?)
INSERT INTO contact_profiles (display_name, full_name, short_descr, image, user_id, incognito, created_at, updated_at)
VALUES (?,?,?,?,?,?,?,?)
Plan:
SEARCH contact_requests USING COVERING INDEX idx_contact_requests_contact_profile_id (contact_profile_id=?)
@@ -4389,7 +4374,7 @@ SEARCH contact_profiles USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE contact_profiles
SET display_name = ?, full_name = ?, image = ?, contact_link = ?, preferences = ?, updated_at = ?
SET display_name = ?, full_name = ?, short_descr = ?, image = ?, contact_link = ?, preferences = ?, updated_at = ?
WHERE user_id = ? AND contact_profile_id = ?
Plan:
@@ -4397,7 +4382,7 @@ SEARCH contact_profiles USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE contact_profiles
SET display_name = ?, full_name = ?, image = ?, contact_link = NULL, preferences = NULL, updated_at = ?
SET display_name = ?, full_name = ?, short_descr = ?, image = ?, contact_link = NULL, preferences = NULL, updated_at = ?
WHERE user_id = ? AND contact_profile_id = ?
Plan:
@@ -4405,7 +4390,7 @@ SEARCH contact_profiles USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE contact_profiles
SET display_name = ?, full_name = ?, image = ?, updated_at = ?
SET display_name = ?, full_name = ?, short_descr = ?, image = ?, updated_at = ?
WHERE user_id = ? AND contact_profile_id = ?
Plan:
@@ -4679,7 +4664,7 @@ SEARCH pending_group_messages USING COVERING INDEX idx_pending_group_messages_me
Query:
SELECT
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
@@ -4688,7 +4673,7 @@ Query:
-- GroupMember - membership
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts
FROM groups g
@@ -4705,7 +4690,7 @@ SEARCH pu USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
@@ -4714,7 +4699,7 @@ Query:
-- GroupMember - membership
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts
FROM groups g
@@ -4732,7 +4717,7 @@ Query:
SELECT
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version
@@ -4747,7 +4732,7 @@ Query:
SELECT
cr.contact_request_id, cr.local_display_name, cr.agent_invitation_id,
cr.contact_id, cr.business_group_id, cr.user_contact_link_id,
cr.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, cr.xcontact_id,
cr.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, cr.xcontact_id,
cr.pq_support, cr.welcome_shared_msg_id, cr.request_shared_msg_id, p.preferences,
cr.created_at, cr.updated_at,
cr.peer_chat_min_version, cr.peer_chat_max_version
@@ -4761,7 +4746,7 @@ SEARCH p USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
@@ -4794,7 +4779,7 @@ SEARCH cc USING COVERING INDEX idx_connections_group_member (user_id=? AND group
Query:
SELECT
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
@@ -4819,7 +4804,7 @@ SEARCH cc USING COVERING INDEX idx_connections_group_member (user_id=? AND group
Query:
SELECT
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
@@ -4844,7 +4829,7 @@ SEARCH cc USING COVERING INDEX idx_connections_group_member (user_id=? AND group
Query:
SELECT
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
@@ -4869,7 +4854,7 @@ SEARCH cc USING COVERING INDEX idx_connections_group_member (user_id=? AND group
Query:
SELECT
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
@@ -4894,7 +4879,7 @@ SEARCH cc USING COVERING INDEX idx_connections_group_member (user_id=? AND group
Query:
SELECT
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
@@ -4919,7 +4904,7 @@ SEARCH cc USING COVERING INDEX idx_connections_group_member (user_id=? AND group
Query:
SELECT
m.group_member_id, m.group_id, m.member_id, m.peer_chat_min_version, m.peer_chat_max_version, m.member_role, m.member_category, m.member_status, m.show_messages, m.member_restriction,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.image, p.contact_link, p.local_alias, p.preferences,
m.invited_by, m.invited_by_group_member_id, m.local_display_name, m.contact_id, m.contact_profile_id, p.contact_profile_id, p.display_name, p.full_name, p.short_descr, p.image, p.contact_link, p.local_alias, p.preferences,
m.created_at, m.updated_at,
m.support_chat_ts, m.support_chat_items_unread, m.support_chat_items_member_attention, m.support_chat_items_mentions, m.support_chat_last_msg_from_member_ts,
c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.xcontact_id, c.custom_user_profile_id,
@@ -5065,7 +5050,7 @@ Plan:
SEARCH server_operators USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.image, ucp.contact_link, ucp.preferences,
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.preferences,
u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.ui_themes
FROM users u
JOIN contacts uct ON uct.contact_id = u.contact_id
@@ -5077,7 +5062,7 @@ SEARCH uct USING INTEGER PRIMARY KEY (rowid=?)
SEARCH ucp USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.image, ucp.contact_link, ucp.preferences,
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.preferences,
u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.ui_themes
FROM users u
JOIN contacts uct ON uct.contact_id = u.contact_id
@@ -5090,7 +5075,7 @@ SEARCH uct USING INTEGER PRIMARY KEY (rowid=?)
SEARCH ucp USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.image, ucp.contact_link, ucp.preferences,
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.preferences,
u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.ui_themes
FROM users u
JOIN contacts uct ON uct.contact_id = u.contact_id
@@ -5103,7 +5088,7 @@ SEARCH uct USING INTEGER PRIMARY KEY (rowid=?)
SEARCH ucp USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.image, ucp.contact_link, ucp.preferences,
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.preferences,
u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.ui_themes
FROM users u
JOIN contacts uct ON uct.contact_id = u.contact_id
@@ -5117,7 +5102,7 @@ SEARCH uct USING INTEGER PRIMARY KEY (rowid=?)
SEARCH ucp USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.image, ucp.contact_link, ucp.preferences,
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.preferences,
u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.ui_themes
FROM users u
JOIN contacts uct ON uct.contact_id = u.contact_id
@@ -5130,7 +5115,7 @@ SEARCH uct USING INTEGER PRIMARY KEY (rowid=?)
SEARCH ucp USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.image, ucp.contact_link, ucp.preferences,
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.preferences,
u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.ui_themes
FROM users u
JOIN contacts uct ON uct.contact_id = u.contact_id
@@ -5143,7 +5128,7 @@ SEARCH uct USING INTEGER PRIMARY KEY (rowid=?)
SEARCH ucp USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.image, ucp.contact_link, ucp.preferences,
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.preferences,
u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.ui_themes
FROM users u
JOIN contacts uct ON uct.contact_id = u.contact_id
@@ -5156,7 +5141,7 @@ SEARCH uct USING INTEGER PRIMARY KEY (rowid=?)
SEARCH ucp USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.image, ucp.contact_link, ucp.preferences,
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.preferences,
u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.ui_themes
FROM users u
JOIN contacts uct ON uct.contact_id = u.contact_id
@@ -5169,7 +5154,7 @@ SEARCH uct USING INTEGER PRIMARY KEY (rowid=?)
SEARCH ucp USING INTEGER PRIMARY KEY (rowid=?)
Query:
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.image, ucp.contact_link, ucp.preferences,
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.preferences,
u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.ui_themes
FROM users u
JOIN contacts uct ON uct.contact_id = u.contact_id
@@ -5633,15 +5618,15 @@ Plan:
Query: INSERT INTO chat_item_messages (chat_item_id, message_id, created_at, updated_at) VALUES (?,?,?,?)
Plan:
Query: INSERT INTO contact_profiles (display_name, full_name, image, contact_link, user_id, local_alias, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)
Query: INSERT INTO contact_profiles (display_name, full_name, short_descr, image, contact_link, user_id, local_alias, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?)
Plan:
SEARCH contact_requests USING COVERING INDEX idx_contact_requests_contact_profile_id (contact_profile_id=?)
Query: INSERT INTO contact_profiles (display_name, full_name, image, contact_link, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)
Query: INSERT INTO contact_profiles (display_name, full_name, short_descr, image, contact_link, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)
Plan:
SEARCH contact_requests USING COVERING INDEX idx_contact_requests_contact_profile_id (contact_profile_id=?)
Query: INSERT INTO contact_profiles (display_name, full_name, image, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?)
Query: INSERT INTO contact_profiles (display_name, full_name, short_descr, image, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)
Plan:
SEARCH contact_requests USING COVERING INDEX idx_contact_requests_contact_profile_id (contact_profile_id=?)
@@ -5677,10 +5662,10 @@ Plan:
Query: INSERT INTO files (user_id, group_id, file_name, file_size, chunk_size, file_inline, ci_file_status, protocol, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?)
Plan:
Query: INSERT INTO group_profiles (display_name, full_name, description, image, user_id, preferences, member_admission, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)
Query: INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, user_id, preferences, member_admission, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?)
Plan:
Query: INSERT INTO group_profiles (display_name, full_name, image, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?)
Query: INSERT INTO group_profiles (display_name, full_name, short_descr, image, user_id, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?)
Plan:
Query: INSERT INTO group_snd_item_statuses (chat_item_id, group_member_id, group_snd_item_status) VALUES (?,?,?)
@@ -18,7 +18,8 @@ CREATE TABLE contact_profiles(
incognito INTEGER,
local_alias TEXT DEFAULT '' CHECK(local_alias NOT NULL),
preferences TEXT,
contact_link BLOB
contact_link BLOB,
short_descr TEXT
);
CREATE TABLE users(
user_id INTEGER PRIMARY KEY,
@@ -114,7 +115,8 @@ CREATE TABLE group_profiles(
user_id INTEGER DEFAULT NULL REFERENCES users ON DELETE CASCADE,
preferences TEXT,
description TEXT NULL,
member_admission TEXT
member_admission TEXT,
short_descr TEXT
);
CREATE TABLE groups(
group_id INTEGER PRIMARY KEY, -- local group ID
+33 -30
View File
@@ -293,14 +293,14 @@ createConnection_ db userId connType entityId acId connStatus connChatVersion pe
ent ct = if connType == ct then entityId else Nothing
createIncognitoProfile_ :: DB.Connection -> UserId -> UTCTime -> Profile -> IO Int64
createIncognitoProfile_ db userId createdAt Profile {displayName, fullName, image} = do
createIncognitoProfile_ db userId createdAt Profile {displayName, fullName, shortDescr, image} = do
DB.execute
db
[sql|
INSERT INTO contact_profiles (display_name, full_name, image, user_id, incognito, created_at, updated_at)
VALUES (?,?,?,?,?,?,?)
INSERT INTO contact_profiles (display_name, full_name, short_descr, image, user_id, incognito, created_at, updated_at)
VALUES (?,?,?,?,?,?,?,?)
|]
(displayName, fullName, image, userId, Just (BI True), createdAt, createdAt)
(displayName, fullName, shortDescr, image, userId, Just (BI True), createdAt, createdAt)
insertedRowId db
updateConnSupportPQ :: DB.Connection -> Int64 -> PQSupport -> PQEncryption -> IO ()
@@ -387,12 +387,12 @@ createContact db User {userId} profile = do
void $ createContact_ db userId profile Nothing "" Nothing currentTs
createContact_ :: DB.Connection -> UserId -> Profile -> Maybe (ACreatedConnLink, Maybe SharedMsgId) -> LocalAlias -> Maybe Int64 -> UTCTime -> ExceptT StoreError IO (Text, ContactId, ProfileId)
createContact_ db userId Profile {displayName, fullName, image, contactLink, preferences} prepared localAlias viaGroup currentTs =
createContact_ db userId Profile {displayName, fullName, shortDescr, image, contactLink, preferences} prepared localAlias viaGroup currentTs =
ExceptT . withLocalDisplayName db userId displayName $ \ldn -> do
DB.execute
db
"INSERT INTO contact_profiles (display_name, full_name, image, contact_link, user_id, local_alias, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?)"
(displayName, fullName, image, contactLink, userId, localAlias, preferences, currentTs, currentTs)
"INSERT INTO contact_profiles (display_name, full_name, short_descr, image, contact_link, user_id, local_alias, preferences, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?)"
(displayName, fullName, shortDescr, image, contactLink, userId, localAlias, preferences, currentTs, currentTs)
profileId <- insertedRowId db
DB.execute
db
@@ -436,13 +436,13 @@ deleteUnusedIncognitoProfileById_ db User {userId} profileId =
type PreparedContactRow = (Maybe AConnectionRequestUri, Maybe AConnShortLink, Maybe SharedMsgId, Maybe SharedMsgId)
type ContactRow' = (ProfileId, ContactName, Maybe Int64, ContactName, Text, Maybe ImageData, Maybe ConnLinkContact, LocalAlias, BoolInt, ContactStatus) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Preferences, Preferences, UTCTime, UTCTime, Maybe UTCTime) :. PreparedContactRow :. (Maybe Int64, Maybe GroupMemberId, BoolInt, Maybe UIThemeEntityOverrides, BoolInt, Maybe CustomData, Maybe Int64)
type ContactRow' = (ProfileId, ContactName, Maybe Int64, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, LocalAlias, BoolInt, ContactStatus) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe Preferences, Preferences, UTCTime, UTCTime, Maybe UTCTime) :. PreparedContactRow :. (Maybe Int64, Maybe GroupMemberId, BoolInt, Maybe UIThemeEntityOverrides, BoolInt, Maybe CustomData, Maybe Int64)
type ContactRow = Only ContactId :. ContactRow'
toContact :: VersionRangeChat -> User -> [ChatTagId] -> ContactRow :. MaybeConnectionRow -> Contact
toContact vr user chatTags ((Only contactId :. (profileId, localDisplayName, viaGroup, displayName, fullName, image, contactLink, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, contactGroupMemberId, BI contactGrpInvSent, uiThemes, BI chatDeleted, customData, chatItemTTL)) :. connRow) =
let profile = LocalProfile {profileId, displayName, fullName, image, contactLink, preferences, localAlias}
toContact vr user chatTags ((Only contactId :. (profileId, localDisplayName, viaGroup, displayName, fullName, shortDescr, image, contactLink, localAlias, BI contactUsed, contactStatus) :. (enableNtfs_, sendRcpts, BI favorite, preferences, userPreferences, createdAt, updatedAt, chatTs) :. preparedContactRow :. (contactRequestId, contactGroupMemberId, BI contactGrpInvSent, uiThemes, BI chatDeleted, customData, chatItemTTL)) :. connRow) =
let profile = LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, preferences, localAlias}
activeConn = toMaybeConnection vr connRow
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
incognito = maybe False connIncognito activeConn
@@ -463,42 +463,39 @@ toACreatedConnLink_ (Just (ACR m cr)) csl = case csl of
getProfileById :: DB.Connection -> UserId -> Int64 -> ExceptT StoreError IO LocalProfile
getProfileById db userId profileId =
ExceptT . firstRow toProfile (SEProfileNotFound profileId) $
ExceptT . firstRow rowToLocalProfile (SEProfileNotFound profileId) $
DB.query
db
[sql|
SELECT cp.display_name, cp.full_name, cp.image, cp.contact_link, cp.local_alias, cp.preferences -- , ct.user_preferences
SELECT cp.contact_profile_id, cp.display_name, cp.full_name, cp.short_descr, cp.image, cp.contact_link, cp.local_alias, cp.preferences -- , ct.user_preferences
FROM contact_profiles cp
WHERE cp.user_id = ? AND cp.contact_profile_id = ?
|]
(userId, profileId)
where
toProfile :: (ContactName, Text, Maybe ImageData, Maybe ConnLinkContact, LocalAlias, Maybe Preferences) -> LocalProfile
toProfile (displayName, fullName, image, contactLink, localAlias, preferences) = LocalProfile {profileId, displayName, fullName, image, contactLink, preferences, localAlias}
type ContactRequestRow = (Int64, ContactName, AgentInvId, Maybe ContactId, Maybe GroupId, Maybe Int64) :. (Int64, ContactName, Text, Maybe ImageData, Maybe ConnLinkContact) :. (Maybe XContactId, PQSupport, Maybe SharedMsgId, Maybe SharedMsgId, Maybe Preferences, UTCTime, UTCTime, VersionChat, VersionChat)
type ContactRequestRow = (Int64, ContactName, AgentInvId, Maybe ContactId, Maybe GroupId, Maybe Int64) :. (Int64, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact) :. (Maybe XContactId, PQSupport, Maybe SharedMsgId, Maybe SharedMsgId, Maybe Preferences, UTCTime, UTCTime, VersionChat, VersionChat)
toContactRequest :: ContactRequestRow -> UserContactRequest
toContactRequest ((contactRequestId, localDisplayName, agentInvitationId, contactId_, businessGroupId_, userContactLinkId_) :. (profileId, displayName, fullName, image, contactLink) :. (xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, preferences, createdAt, updatedAt, minVer, maxVer)) = do
let profile = Profile {displayName, fullName, image, contactLink, preferences}
toContactRequest ((contactRequestId, localDisplayName, agentInvitationId, contactId_, businessGroupId_, userContactLinkId_) :. (profileId, displayName, fullName, shortDescr, image, contactLink) :. (xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, preferences, createdAt, updatedAt, minVer, maxVer)) = do
let profile = Profile {displayName, fullName, shortDescr, image, contactLink, preferences}
cReqChatVRange = fromMaybe (versionToRange maxVer) $ safeVersionRange minVer maxVer
in UserContactRequest {contactRequestId, agentInvitationId, contactId_, businessGroupId_, userContactLinkId_, cReqChatVRange, localDisplayName, profileId, profile, xContactId, pqSupport, welcomeSharedMsgId, requestSharedMsgId, createdAt, updatedAt}
userQuery :: Query
userQuery =
[sql|
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.image, ucp.contact_link, ucp.preferences,
SELECT u.user_id, u.agent_user_id, u.contact_id, ucp.contact_profile_id, u.active_user, u.active_order, u.local_display_name, ucp.full_name, ucp.short_descr, ucp.image, ucp.contact_link, ucp.preferences,
u.show_ntfs, u.send_rcpts_contacts, u.send_rcpts_small_groups, u.view_pwd_hash, u.view_pwd_salt, u.user_member_profile_updated_at, u.ui_themes
FROM users u
JOIN contacts uct ON uct.contact_id = u.contact_id
JOIN contact_profiles ucp ON ucp.contact_profile_id = uct.contact_profile_id
|]
toUser :: (UserId, UserId, ContactId, ProfileId, BoolInt, Int64, ContactName, Text, Maybe ImageData, Maybe ConnLinkContact, Maybe Preferences) :. (BoolInt, BoolInt, BoolInt, Maybe B64UrlByteString, Maybe B64UrlByteString, Maybe UTCTime, Maybe UIThemeEntityOverrides) -> User
toUser ((userId, auId, userContactId, profileId, BI activeUser, activeOrder, displayName, fullName, image, contactLink, userPreferences) :. (BI showNtfs, BI sendRcptsContacts, BI sendRcptsSmallGroups, viewPwdHash_, viewPwdSalt_, userMemberProfileUpdatedAt, uiThemes)) =
toUser :: (UserId, UserId, ContactId, ProfileId, BoolInt, Int64, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe Preferences) :. (BoolInt, BoolInt, BoolInt, Maybe B64UrlByteString, Maybe B64UrlByteString, Maybe UTCTime, Maybe UIThemeEntityOverrides) -> User
toUser ((userId, auId, userContactId, profileId, BI activeUser, activeOrder, displayName, fullName, shortDescr, image, contactLink, userPreferences) :. (BI showNtfs, BI sendRcptsContacts, BI sendRcptsSmallGroups, viewPwdHash_, viewPwdSalt_, userMemberProfileUpdatedAt, uiThemes)) =
User {userId, agentUserId = AgentUserId auId, userContactId, localDisplayName = displayName, profile, activeUser, activeOrder, fullPreferences, showNtfs, sendRcptsContacts, sendRcptsSmallGroups, viewPwdHash, userMemberProfileUpdatedAt, uiThemes}
where
profile = LocalProfile {profileId, displayName, fullName, image, contactLink, preferences = userPreferences, localAlias = ""}
profile = LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, preferences = userPreferences, localAlias = ""}
fullPreferences = mergePreferences Nothing userPreferences
viewPwdHash = UserPwdHash <$> viewPwdHash_ <*> viewPwdSalt_
@@ -612,16 +609,18 @@ type PreparedGroupRow = (Maybe ConnReqContact, Maybe ShortLinkContact, BoolInt,
type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe MemberId)
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Text, Maybe Text, Maybe ImageData, Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (Maybe UIThemeEntityOverrides, Maybe CustomData, Maybe Int64, Int) :. GroupMemberRow
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (Maybe UIThemeEntityOverrides, Maybe CustomData, Maybe Int64, Int) :. GroupMemberRow
type GroupMemberRow = (Int64, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId, ProfileId, ContactName, Text, Maybe ImageData, Maybe ConnLinkContact, LocalAlias, Maybe Preferences) :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64, Maybe UTCTime)
type GroupMemberRow = (Int64, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId) :. ProfileRow :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64, Maybe UTCTime)
type ProfileRow = (ProfileId, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, LocalAlias, Maybe Preferences)
toGroupInfo :: VersionRangeChat -> Int64 -> [ChatTagId] -> GroupInfoRow -> GroupInfo
toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, localAlias, description, image, enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. preparedGroupRow :. businessRow :. (uiThemes, customData, chatItemTTL, membersRequireAttention) :. userMemberRow) =
toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, shortDescr, localAlias, description, image) :. (enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. preparedGroupRow :. businessRow :. (uiThemes, customData, chatItemTTL, membersRequireAttention) :. userMemberRow) =
let membership = (toGroupMember userContactId userMemberRow) {memberChatVRange = vr}
chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite}
fullGroupPreferences = mergeGroupPreferences groupPreferences
groupProfile = GroupProfile {displayName, fullName, description, image, groupPreferences, memberAdmission}
groupProfile = GroupProfile {displayName, fullName, shortDescr, description, image, groupPreferences, memberAdmission}
businessChat = toBusinessChatInfo businessRow
preparedGroup = toPreparedGroup preparedGroupRow
in GroupInfo {groupId, localDisplayName, groupProfile, localAlias, businessChat, fullGroupPreferences, membership, chatSettings, createdAt, updatedAt, chatTs, userMemberProfileSentAt, preparedGroup, chatTags, chatItemTTL, uiThemes, customData, membersRequireAttention}
@@ -633,8 +632,8 @@ toPreparedGroup = \case
_ -> Nothing
toGroupMember :: Int64 -> GroupMemberRow -> GroupMember
toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, BI showMessages, memberRestriction_) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId, profileId, displayName, fullName, image, contactLink, localAlias, preferences) :. (createdAt, updatedAt) :. (supportChatTs_, supportChatUnread, supportChatMemberAttention, supportChatMentions, supportChatLastMsgFromMemberTs)) =
let memberProfile = LocalProfile {profileId, displayName, fullName, image, contactLink, preferences, localAlias}
toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer, memberRole, memberCategory, memberStatus, BI showMessages, memberRestriction_) :. (invitedById, invitedByGroupMemberId, localDisplayName, memberContactId, memberContactProfileId) :. profileRow :. (createdAt, updatedAt) :. (supportChatTs_, supportChatUnread, supportChatMemberAttention, supportChatMentions, supportChatLastMsgFromMemberTs)) =
let memberProfile = rowToLocalProfile profileRow
memberSettings = GroupMemberSettings {showMessages}
blockedByAdmin = maybe False mrsBlocked memberRestriction_
invitedBy = toInvitedBy userContactId invitedById
@@ -652,6 +651,10 @@ toGroupMember userContactId ((groupMemberId, groupId, memberId, minVer, maxVer,
_ -> Nothing
in GroupMember {..}
rowToLocalProfile :: ProfileRow -> LocalProfile
rowToLocalProfile (profileId, displayName, fullName, shortDescr, image, contactLink, localAlias, preferences) =
LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, localAlias, preferences}
toBusinessChatInfo :: BusinessChatInfoRow -> Maybe BusinessChatInfo
toBusinessChatInfo (Just chatType, Just businessId, Just customerId) = Just BusinessChatInfo {chatType, businessId, customerId}
toBusinessChatInfo _ = Nothing
@@ -661,7 +664,7 @@ groupInfoQuery =
[sql|
SELECT
-- GroupInfo
g.group_id, g.local_display_name, gp.display_name, gp.full_name, g.local_alias, gp.description, gp.image,
g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image,
g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission,
g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at,
g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id,
@@ -670,7 +673,7 @@ groupInfoQuery =
-- GroupMember - membership
mu.group_member_id, mu.group_id, mu.member_id, mu.peer_chat_min_version, mu.peer_chat_max_version, mu.member_role, mu.member_category,
mu.member_status, mu.show_messages, mu.member_restriction, mu.invited_by, mu.invited_by_group_member_id, mu.local_display_name, mu.contact_id, mu.contact_profile_id, pu.contact_profile_id,
pu.display_name, pu.full_name, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
pu.display_name, pu.full_name, pu.short_descr, pu.image, pu.contact_link, pu.local_alias, pu.preferences,
mu.created_at, mu.updated_at,
mu.support_chat_ts, mu.support_chat_items_unread, mu.support_chat_items_member_attention, mu.support_chat_items_mentions, mu.support_chat_last_msg_from_member_ts
FROM groups g
+14 -11
View File
@@ -422,9 +422,9 @@ type MemberName = Text
type GroupName = Text
optionalFullName :: ContactName -> Text -> Text
optionalFullName displayName fullName
| T.null fullName || displayName == fullName = ""
optionalFullName :: ContactName -> Text -> Maybe Text -> Text
optionalFullName displayName fullName shortDescr
| T.null fullName || displayName == fullName = maybe "" (\sd -> " (" <> sd <> ")") shortDescr
| otherwise = " (" <> fullName <> ")"
data ShortGroup = ShortGroup
@@ -620,6 +620,7 @@ contactUserPreferences user userPreferences contactPreferences connectedIncognit
data Profile = Profile
{ displayName :: ContactName,
fullName :: Text,
shortDescr :: Maybe Text, -- short description limited to 160 characters
image :: Maybe ImageData,
contactLink :: Maybe ConnLinkContact,
preferences :: Maybe Preferences
@@ -632,7 +633,7 @@ data Profile = Profile
profileFromName :: ContactName -> Profile
profileFromName displayName =
Profile {displayName, fullName = "", image = Nothing, contactLink = Nothing, preferences = Nothing}
Profile {displayName, fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, preferences = Nothing}
-- check if profiles match ignoring preferences
profilesMatch :: LocalProfile -> LocalProfile -> Bool
@@ -642,8 +643,8 @@ profilesMatch
n1 == n2 && fn1 == fn2 && i1 == i2
redactedMemberProfile :: Profile -> Profile
redactedMemberProfile Profile {displayName, fullName, image} =
Profile {displayName, fullName, image, contactLink = Nothing, preferences = Nothing}
redactedMemberProfile Profile {displayName, fullName, shortDescr, image} =
Profile {displayName, fullName, shortDescr, image, contactLink = Nothing, preferences = Nothing}
data IncognitoProfile = NewIncognito Profile | ExistingIncognito LocalProfile
@@ -669,6 +670,7 @@ data LocalProfile = LocalProfile
{ profileId :: ProfileId,
displayName :: ContactName,
fullName :: Text,
shortDescr :: Maybe Text,
image :: Maybe ImageData,
contactLink :: Maybe ConnLinkContact,
preferences :: Maybe Preferences,
@@ -680,17 +682,18 @@ localProfileId :: LocalProfile -> ProfileId
localProfileId LocalProfile {profileId} = profileId
toLocalProfile :: ProfileId -> Profile -> LocalAlias -> LocalProfile
toLocalProfile profileId Profile {displayName, fullName, image, contactLink, preferences} localAlias =
LocalProfile {profileId, displayName, fullName, image, contactLink, preferences, localAlias}
toLocalProfile profileId Profile {displayName, fullName, shortDescr, image, contactLink, preferences} localAlias =
LocalProfile {profileId, displayName, fullName, shortDescr, image, contactLink, preferences, localAlias}
fromLocalProfile :: LocalProfile -> Profile
fromLocalProfile LocalProfile {displayName, fullName, image, contactLink, preferences} =
Profile {displayName, fullName, image, contactLink, preferences}
fromLocalProfile LocalProfile {displayName, fullName, shortDescr, image, contactLink, preferences} =
Profile {displayName, fullName, shortDescr, image, contactLink, preferences}
data GroupProfile = GroupProfile
{ displayName :: GroupName,
fullName :: Text,
description :: Maybe Text,
shortDescr :: Maybe Text, -- short description limited to 160 characters
description :: Maybe Text, -- this has been repurposed as welcome message
image :: Maybe ImageData,
groupPreferences :: Maybe GroupPreferences,
memberAdmission :: Maybe GroupMemberAdmission
+41 -34
View File
@@ -606,8 +606,8 @@ viewUsersList us =
in if null ss then ["no users"] else ss
where
ldn (UserInfo User {localDisplayName = n} _) = T.toLower n
userInfo (UserInfo User {localDisplayName = n, profile = LocalProfile {fullName}, activeUser, showNtfs, viewPwdHash} count)
| activeUser || isNothing viewPwdHash = Just $ ttyFullName n fullName <> infoStr
userInfo (UserInfo User {localDisplayName = n, profile = LocalProfile {fullName, shortDescr}, activeUser, showNtfs, viewPwdHash} count)
| activeUser || isNothing viewPwdHash = Just $ ttyFullName n fullName shortDescr <> infoStr
| otherwise = Nothing
where
infoStr = if null info then "" else " (" <> mconcat (intersperse ", " info) <> ")"
@@ -1158,8 +1158,8 @@ viewAcceptingBusinessRequest :: GroupInfo -> [StyledString]
viewAcceptingBusinessRequest g = [ttyFullGroup g <> ": accepting business address request..."]
viewReceivedContactRequest :: ContactName -> Profile -> [StyledString]
viewReceivedContactRequest c Profile {fullName} =
[ ttyFullName c fullName <> " wants to connect to you!",
viewReceivedContactRequest c Profile {fullName, shortDescr} =
[ ttyFullName c fullName shortDescr <> " wants to connect to you!",
"to accept: " <> highlight ("/ac " <> viewName c),
"to reject: " <> highlight ("/rc " <> viewName c) <> " (the sender will NOT be notified)"
]
@@ -1380,9 +1380,9 @@ viewSentGroupInvitation g c = case contactConn c of
Nothing -> []
groupInvitation' :: GroupInfo -> StyledString
groupInvitation' g@GroupInfo {localDisplayName = ldn, groupProfile = GroupProfile {fullName}} =
groupInvitation' g@GroupInfo {localDisplayName = ldn, groupProfile = GroupProfile {fullName, shortDescr}} =
highlight ("#" <> viewName ldn)
<> optFullName ldn fullName
<> optFullName ldn fullName shortDescr
<> " - you are invited ("
<> highlight ("/j " <> viewName ldn)
<> joinText
@@ -1415,9 +1415,9 @@ viewContactAndMemberAssociated ct g m ct' =
]
viewUserProfile :: Profile -> [StyledString]
viewUserProfile Profile {displayName, fullName} =
[ "user profile: " <> ttyFullName displayName fullName,
"use " <> highlight' "/p <display name>" <> " to change it",
viewUserProfile Profile {displayName, fullName, shortDescr} =
[ "user profile: " <> ttyFullName displayName fullName shortDescr,
"use " <> highlight' "/p <name> [<bio>]" <> " to change it",
"(the updated profile will be sent to all your contacts)"
]
@@ -1703,16 +1703,17 @@ viewSwitchPhase = \case
SPCompleted -> "changed address"
viewUserProfileUpdated :: Profile -> Profile -> UserProfileUpdateSummary -> [StyledString]
viewUserProfileUpdated Profile {displayName = n, fullName, image, contactLink, preferences} Profile {displayName = n', fullName = fullName', image = image', contactLink = contactLink', preferences = prefs'} summary =
viewUserProfileUpdated Profile {displayName = n, fullName, shortDescr, image, contactLink, preferences} Profile {displayName = n', fullName = fullName', shortDescr = shortDescr', image = image', contactLink = contactLink', preferences = prefs'} summary =
profileUpdated <> viewPrefsUpdated preferences prefs'
where
UserProfileUpdateSummary {updateSuccesses = s, updateFailures = f} = summary
profileUpdated
| n == n' && fullName == fullName' && image == image' && contactLink == contactLink' = []
| n == n' && fullName == fullName' && image == image' = [if isNothing contactLink' then "contact address removed" else "new contact address set"]
| n == n' && fullName == fullName' = [if isNothing image' then "profile image removed" else "profile image updated"]
| n == n' && fullName == fullName' && shortDescr == shortDescr' && image == image' && contactLink == contactLink' = []
| n == n' && fullName == fullName' && shortDescr == shortDescr' && image == image' = [if isNothing contactLink' then "contact address removed" else "new contact address set"]
| n == n' && fullName == fullName' && shortDescr == shortDescr' = [if isNothing image' then "profile image removed" else "profile image updated"]
| n == n' && fullName == fullName' = ["user bio " <> (if maybe True T.null shortDescr' then "removed" else "changed to " <> maybe "" plain shortDescr') <> notified]
| n == n' = ["user full name " <> (if T.null fullName' || fullName' == n' then "removed" else "changed to " <> plain fullName') <> notified]
| otherwise = ["user profile is changed to " <> ttyFullName n' fullName' <> notified]
| otherwise = ["user profile is changed to " <> ttyFullName n' fullName' shortDescr' <> notified]
notified = " (your " <> sShow s <> " contacts are notified" <> failures <> ")"
failures
| f > 0 = ", " <> sShow f <> " failures"
@@ -1770,8 +1771,8 @@ countactUserPrefText cup = case cup of
viewGroupUpdated :: GroupInfo -> GroupInfo -> Maybe GroupMember -> [StyledString]
viewGroupUpdated
GroupInfo {localDisplayName = n, groupProfile = GroupProfile {fullName, description, image, groupPreferences = gps, memberAdmission = ma}}
g'@GroupInfo {localDisplayName = n', groupProfile = GroupProfile {fullName = fullName', description = description', image = image', groupPreferences = gps', memberAdmission = ma'}}
GroupInfo {localDisplayName = n, groupProfile = GroupProfile {fullName, shortDescr, description, image, groupPreferences = gps, memberAdmission = ma}}
g'@GroupInfo {localDisplayName = n', groupProfile = GroupProfile {fullName = fullName', shortDescr = shortDescr', description = description', image = image', groupPreferences = gps', memberAdmission = ma'}}
m = do
let update = groupProfileUpdated <> groupPrefsUpdated <> memberAdmissionUpdated
if null update
@@ -1782,8 +1783,9 @@ viewGroupUpdated
groupProfileUpdated =
["changed to " <> ttyFullGroup g' | n /= n']
<> ["full name " <> if T.null fullName' || fullName' == n' then "removed" else "changed to: " <> plain fullName' | n == n' && fullName /= fullName']
<> ["description " <> if maybe True T.null shortDescr' then "removed" else "changed to: " <> maybe "" plain shortDescr' | n == n' && fullName == fullName' && shortDescr /= shortDescr']
<> ["profile image " <> maybe "removed" (const "updated") image' | image /= image']
<> (if description == description' then [] else maybe ["description removed"] ((bold' "description changed to:" :) . map plain . T.lines) description')
<> (if description == description' then [] else maybe ["welcome message removed"] ((bold' "welcome message changed to:" :) . map plain . T.lines) description')
groupPrefsUpdated
| null prefs = []
| otherwise = bold' "updated group preferences:" : prefs
@@ -1799,10 +1801,11 @@ viewGroupUpdated
| otherwise = ["changed member admission rules"]
viewGroupProfile :: GroupInfo -> [StyledString]
viewGroupProfile g@GroupInfo {groupProfile = GroupProfile {description, image, groupPreferences = gps}} =
viewGroupProfile g@GroupInfo {groupProfile = GroupProfile {shortDescr, description, image, groupPreferences = gps}} =
[ttyFullGroup g]
<> maybe [] (\sd -> ["description: " <> plain sd]) shortDescr
<> maybe [] (const ["has profile image"]) image
<> maybe [] ((bold' "description:" :) . map plain . T.lines) description
<> maybe [] ((bold' "welcome message:" :) . map plain . T.lines) description
<> (bold' "group preferences:" : map viewPref allGroupFeatures)
where
viewPref (AGF f) = plain $ groupPreferenceText (pref gps)
@@ -1963,16 +1966,20 @@ viewConnectionPlan ChatConfig {logLevel, testView} _connLink = \case
viewContactUpdated :: Contact -> Contact -> [StyledString]
viewContactUpdated
Contact {localDisplayName = n, profile = LocalProfile {fullName, contactLink}}
Contact {localDisplayName = n', profile = LocalProfile {fullName = fullName', contactLink = contactLink'}}
| n == n' && fullName == fullName' && contactLink == contactLink' = []
| n == n' && fullName == fullName' =
Contact {localDisplayName = n, profile = LocalProfile {fullName, shortDescr, contactLink}}
Contact {localDisplayName = n', profile = LocalProfile {fullName = fullName', shortDescr = shortDescr', contactLink = contactLink'}}
| n == n' && fullName == fullName' && shortDescr == shortDescr' && contactLink == contactLink' = []
| n == n' && fullName == fullName' && shortDescr == shortDescr' =
if isNothing contactLink'
then [ttyContact n <> " removed contact address"]
else [ttyContact n <> " set new contact address, use " <> highlight ("/info " <> n) <> " to view"]
| n == n' && fullName == fullName' =
if maybe True T.null shortDescr'
then ["contact " <> ttyContact n <> " removed bio"]
else ["contact " <> ttyContact n <> " updated bio: " <> maybe "" plain shortDescr']
| n == n' = ["contact " <> ttyContact n <> fullNameUpdate]
| otherwise =
[ "contact " <> ttyContact n <> " changed to " <> ttyFullName n' fullName',
[ "contact " <> ttyContact n <> " changed to " <> ttyFullName n' fullName' shortDescr',
"use " <> ttyToContact n' <> highlight' "<message>" <> " to send messages"
]
where
@@ -2566,18 +2573,18 @@ ttyContact' :: Contact -> StyledString
ttyContact' Contact {localDisplayName = c} = ttyContact c
ttyFullContact :: Contact -> StyledString
ttyFullContact Contact {localDisplayName, profile = LocalProfile {fullName}} =
ttyFullName localDisplayName fullName
ttyFullContact Contact {localDisplayName, profile = LocalProfile {fullName, shortDescr}} =
ttyFullName localDisplayName fullName shortDescr
ttyMember :: GroupMember -> StyledString
ttyMember GroupMember {localDisplayName} = ttyContact localDisplayName
ttyFullMember :: GroupMember -> StyledString
ttyFullMember GroupMember {localDisplayName, memberProfile = LocalProfile {fullName}} =
ttyFullName localDisplayName fullName
ttyFullMember GroupMember {localDisplayName, memberProfile = LocalProfile {fullName, shortDescr}} =
ttyFullName localDisplayName fullName shortDescr
ttyFullName :: ContactName -> Text -> StyledString
ttyFullName c fullName = ttyContact c <> optFullName c fullName
ttyFullName :: ContactName -> Text -> Maybe Text -> StyledString
ttyFullName c fullName shortDescr = ttyContact c <> optFullName c fullName shortDescr
ttyToContact :: ContactName -> StyledString
ttyToContact c = ttyTo $ "@" <> viewName c <> " "
@@ -2626,8 +2633,8 @@ ttyGroups [g] = ttyGroup g
ttyGroups (g : gs) = ttyGroup g <> ", " <> ttyGroups gs
ttyFullGroup :: GroupInfo -> StyledString
ttyFullGroup GroupInfo {localDisplayName = g, groupProfile = GroupProfile {fullName}} =
ttyGroup g <> optFullName g fullName
ttyFullGroup GroupInfo {localDisplayName = g, groupProfile = GroupProfile {fullName, shortDescr}} =
ttyGroup g <> optFullName g fullName shortDescr
ttyFromGroup :: GroupInfo -> Maybe GroupChatScopeInfo -> GroupMember -> StyledString
ttyFromGroup g scopeInfo m = ttyFromGroupAttention g scopeInfo m False
@@ -2672,8 +2679,8 @@ groupScopeInfoStr = \case
ttyFilePath :: FilePath -> StyledString
ttyFilePath = plain
optFullName :: ContactName -> Text -> StyledString
optFullName localDisplayName fullName = plain $ optionalFullName localDisplayName fullName
optFullName :: ContactName -> Text -> Maybe Text -> StyledString
optFullName localDisplayName fullName shortDescr = plain $ optionalFullName localDisplayName fullName shortDescr
ctIncognito :: Contact -> StyledString
ctIncognito ct = if contactConnIncognito ct then incognitoPrefix else ""
+1 -1
View File
@@ -33,7 +33,7 @@ withBroadcastBot opts test =
bot = simplexChatCore testCfg (mkChatOpts opts) $ broadcastBot opts
broadcastBotProfile :: Profile
broadcastBotProfile = Profile {displayName = "broadcast_bot", fullName = "Broadcast Bot", image = Nothing, contactLink = Nothing, preferences = Nothing}
broadcastBotProfile = Profile {displayName = "broadcast_bot", fullName = "Broadcast Bot", shortDescr = Nothing, image = Nothing, contactLink = Nothing, preferences = Nothing}
mkBotOpts :: TestParams -> [KnownContact] -> BroadcastBotOpts
mkBotOpts ps publishers =
+24 -24
View File
@@ -75,7 +75,7 @@ directoryServiceTests = do
it "should accept some incorrect spellings" testCaptcha
directoryProfile :: Profile
directoryProfile = Profile {displayName = "SimpleX-Directory", fullName = "", image = Nothing, contactLink = Nothing, preferences = Nothing}
directoryProfile = Profile {displayName = "SimpleX-Directory", fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, preferences = Nothing}
mkDirectoryOpts :: TestParams -> [KnownContact] -> Maybe KnownGroup -> DirectoryOpts
mkDirectoryOpts TestParams {tmpPath = ps} superUsers ownersGroup =
@@ -219,7 +219,7 @@ testDirectoryService ps =
u <## "2 members"
updateGroupProfile u welcome = do
u ##> ("/set welcome #PSA " <> welcome)
u <## "description changed to:"
u <## "welcome message changed to:"
u <## welcome
approvalRequested su welcome grId = do
su <# "SimpleX-Directory> bob submitted the group ID 1:"
@@ -738,11 +738,11 @@ testRegOwnerChangedProfile ps =
registerGroup superUser bob "privacy" "Privacy"
addCathAsOwner bob cath
bob ##> "/gp privacy privacy Privacy and Security"
bob <## "full name changed to: Privacy and Security"
bob <## "description changed to: Privacy and Security"
bob <# "SimpleX-Directory> The group ID 1 (privacy) is updated!"
bob <## "It is hidden from the directory until approved."
cath <## "bob updated group #privacy:"
cath <## "full name changed to: Privacy and Security"
cath <## "description changed to: Privacy and Security"
cath `connectVia` dsLink
cath <## "contact and member are merged: SimpleX-Directory_1, #privacy SimpleX-Directory"
cath <## "use @SimpleX-Directory <message> to send messages"
@@ -763,9 +763,9 @@ testAnotherOwnerChangedProfile ps =
cath <## "contact and member are merged: SimpleX-Directory_1, #privacy SimpleX-Directory"
cath <## "use @SimpleX-Directory <message> to send messages"
cath ##> "/gp privacy privacy Privacy and Security"
cath <## "full name changed to: Privacy and Security"
cath <## "description changed to: Privacy and Security"
bob <## "cath updated group #privacy:"
bob <## "full name changed to: Privacy and Security"
bob <## "description changed to: Privacy and Security"
bob <# "SimpleX-Directory> The group ID 1 (privacy) is updated by cath!"
bob <## "It is hidden from the directory until approved."
groupNotFound cath "privacy"
@@ -784,9 +784,9 @@ testNotConnectedOwnerChangedProfile ps =
registerGroup superUser bob "privacy" "Privacy"
addCathAsOwner bob cath
cath ##> "/gp privacy privacy Privacy and Security"
cath <## "full name changed to: Privacy and Security"
cath <## "description changed to: Privacy and Security"
bob <## "cath updated group #privacy:"
bob <## "full name changed to: Privacy and Security"
bob <## "description changed to: Privacy and Security"
bob <# "SimpleX-Directory> The group ID 1 (privacy) is updated by cath!"
bob <## "It is hidden from the directory until approved."
groupNotFound dan "privacy"
@@ -806,13 +806,13 @@ testRegOwnerRemovedLink ps =
bob <## "Welcome message:"
welcomeWithLink <- getTermLine bob
bob ##> "/set welcome #privacy Welcome!"
bob <## "description changed to:"
bob <## "welcome message changed to:"
bob <## "Welcome!"
bob <# "SimpleX-Directory> The group link for ID 1 (privacy) is removed from the welcome message."
bob <## ""
bob <## "The group is hidden from the directory until the group link is added and the group is re-approved."
cath <## "bob updated group #privacy:"
cath <## "description changed to:"
cath <## "welcome message changed to:"
cath <## "Welcome!"
superUser <# "SimpleX-Directory> The group link is removed from ID 1 (privacy), de-listed."
cath `connectVia` dsLink
@@ -821,12 +821,12 @@ testRegOwnerRemovedLink ps =
groupNotFound cath "privacy"
let withChangedLink = T.unpack $ T.replace "contact#/?v=2-7&" "contact#/?v=3-7&" $ T.pack welcomeWithLink
bob ##> ("/set welcome #privacy " <> withChangedLink)
bob <## "description changed to:"
bob <## "welcome message changed to:"
bob <## withChangedLink
bob <# "SimpleX-Directory> Thank you! The group link for ID 1 (privacy) is added to the welcome message."
bob <## "You will be notified once the group is added to the directory - it may take up to 48 hours."
cath <## "bob updated group #privacy:"
cath <## "description changed to:"
cath <## "welcome message changed to:"
cath <## withChangedLink
reapproveGroup 3 superUser bob
groupFoundN 3 cath "privacy"
@@ -846,10 +846,10 @@ testAnotherOwnerRemovedLink ps =
bob <## "Welcome message:"
welcomeWithLink <- getTermLine bob
cath ##> "/set welcome #privacy Welcome!"
cath <## "description changed to:"
cath <## "welcome message changed to:"
cath <## "Welcome!"
bob <## "cath updated group #privacy:"
bob <## "description changed to:"
bob <## "welcome message changed to:"
bob <## "Welcome!"
bob <# "SimpleX-Directory> The group link for ID 1 (privacy) is removed from the welcome message by cath."
bob <## ""
@@ -857,10 +857,10 @@ testAnotherOwnerRemovedLink ps =
superUser <# "SimpleX-Directory> The group link is removed from ID 1 (privacy), de-listed."
groupNotFound cath "privacy"
cath ##> ("/set welcome #privacy " <> welcomeWithLink)
cath <## "description changed to:"
cath <## "welcome message changed to:"
cath <## welcomeWithLink
bob <## "cath updated group #privacy:"
bob <## "description changed to:"
bob <## "welcome message changed to:"
bob <## welcomeWithLink
bob <# "SimpleX-Directory> Thank you! The group link for ID 1 (privacy) is added to the welcome message by cath."
bob <## "You will be notified once the group is added to the directory - it may take up to 48 hours."
@@ -881,10 +881,10 @@ testNotConnectedOwnerRemovedLink ps =
bob <## "Welcome message:"
welcomeWithLink <- getTermLine bob
cath ##> "/set welcome #privacy Welcome!"
cath <## "description changed to:"
cath <## "welcome message changed to:"
cath <## "Welcome!"
bob <## "cath updated group #privacy:"
bob <## "description changed to:"
bob <## "welcome message changed to:"
bob <## "Welcome!"
bob <# "SimpleX-Directory> The group link for ID 1 (privacy) is removed from the welcome message by cath."
bob <## ""
@@ -892,21 +892,21 @@ testNotConnectedOwnerRemovedLink ps =
superUser <# "SimpleX-Directory> The group link is removed from ID 1 (privacy), de-listed."
groupNotFound dan "privacy"
cath ##> ("/set welcome #privacy " <> welcomeWithLink)
cath <## "description changed to:"
cath <## "welcome message changed to:"
cath <## welcomeWithLink
bob <## "cath updated group #privacy:"
bob <## "description changed to:"
bob <## "welcome message changed to:"
bob <## welcomeWithLink
-- bob <# "SimpleX-Directory> The group link is added by another group member, your registration will not be processed."
-- bob <## ""
-- bob <## "Please update the group profile yourself."
-- bob ##> ("/set welcome #privacy " <> welcomeWithLink <> " - welcome!")
-- bob <## "description changed to:"
-- bob <## "welcome message changed to:"
-- bob <## (welcomeWithLink <> " - welcome!")
bob <# "SimpleX-Directory> Thank you! The group link for ID 1 (privacy) is added to the welcome message by cath."
bob <## "You will be notified once the group is added to the directory - it may take up to 48 hours."
-- cath <## "bob updated group #privacy:"
-- cath <## "description changed to:"
-- cath <## "welcome message changed to:"
-- cath <## (welcomeWithLink <> " - welcome!")
reapproveGroup 3 superUser bob
groupFoundN 3 dan "privacy"
@@ -980,7 +980,7 @@ testDuplicateProhibitWhenUpdated ps =
completeRegistration superUser bob "privacy" "Privacy" welcomeWithLink 1
groupFound cath "privacy"
cath ##> ("/set welcome privacy " <> welcomeWithLink')
cath <## "description changed to:"
cath <## "welcome message changed to:"
cath <## welcomeWithLink'
cath <# "SimpleX-Directory> The group privacy (Privacy) is already listed in the directory, please choose another name."
cath ##> "/gp privacy security Security"
@@ -1352,7 +1352,7 @@ completeRegistrationId su u n fn welcomeWithLink gId ugId = do
updateProfileWithLink :: TestCC -> String -> String -> Int -> IO ()
updateProfileWithLink u n welcomeWithLink ugId = do
u ##> ("/set welcome " <> viewName n <> " " <> welcomeWithLink)
u <## "description changed to:"
u <## "welcome message changed to:"
u <## welcomeWithLink
u <# ("SimpleX-Directory> Thank you! The group link for ID " <> show ugId <> " (" <> n <> ") is added to the welcome message.")
u <## "You will be notified once the group is added to the directory - it may take up to 48 hours."
+41 -21
View File
@@ -353,7 +353,7 @@ testGroupShared alice bob cath checkMessages = do
alice #$> ("/_get chat #1 around=" <> msgItem1 <> " count=2", chat, [(0, "connected"), (0, "connected"), (1, "hello"), (0, "hi there"), (0, "hey team")])
alice #$> ("/_get chat #1 count=100 search=team", chat, [(0, "hey team")])
bob @@@ [("#team", "hey team"), ("@alice", "received invitation to join group team as admin")]
bob #$> ("/_get chat #1 count=100", chat, groupFeatures <> [(0, "connected"), (0, "added cath (Catherine)"), (0, "connected"), (0, "hello"), (1, "hi there"), (0, "hey team")])
bob #$> ("/_get chat #1 count=100", chat, groupFeatures <> [(0, "connected"), (0, "added cath"), (0, "connected"), (0, "hello"), (1, "hi there"), (0, "hey team")])
cath @@@ [("#team", "hey team"), ("@alice", "received invitation to join group team as admin")]
cath #$> ("/_get chat #1 count=100", chat, groupFeatures <> [(0, "connected"), (0, "connected"), (0, "hello"), (0, "hi there"), (1, "hey team")])
alice #$> ("/_read chat #1", id, "ok")
@@ -586,7 +586,7 @@ testGroup2 =
<>
[ "#club bob> connected",
"#club cath> connected",
"#club bob> added dan (Daniel)",
"#club bob> added dan",
"#club dan> connected",
"#club hello",
"#club bob> hi there",
@@ -1466,6 +1466,26 @@ testUpdateGroupProfile =
concurrently_
(alice <# "#my_team bob> hi")
(cath <# "#my_team bob> hi")
alice ##> "/gp my_team my_team My team"
alice <## "description changed to: My team"
concurrentlyN_
[ do
bob <## "alice updated group #my_team:"
bob <## "description changed to: My team",
do
cath <## "alice updated group #my_team:"
cath <## "description changed to: My team"
]
alice ##> "/gp my_team my_team My team updated"
alice <## "description changed to: My team updated"
concurrentlyN_
[ do
bob <## "alice updated group #my_team:"
bob <## "description changed to: My team updated",
do
cath <## "alice updated group #my_team:"
cath <## "description changed to: My team updated"
]
testUpdateMemberRole :: HasCallStack => TestParams -> IO ()
testUpdateMemberRole =
@@ -1525,15 +1545,15 @@ testGroupDescription = testChat4 aliceProfile bobProfile cathProfile danProfile
alice ##> "/group_profile team"
alice <## "#team"
groupInfo' alice
alice ##> "/group_descr team Welcome to the team!"
alice <## "description changed to:"
alice ##> "/set welcome team Welcome to the team!"
alice <## "welcome message changed to:"
alice <## "Welcome to the team!"
bob <## "alice updated group #team:"
bob <## "description changed to:"
bob <## "welcome message changed to:"
bob <## "Welcome to the team!"
alice ##> "/group_profile team"
alice <## "#team"
alice <## "description:"
alice <## "welcome message:"
alice <## "Welcome to the team!"
groupInfo' alice
connectUsers alice cath
@@ -1845,9 +1865,9 @@ testDeleteMemberWithMessages =
bob <## "#team: alice removed you from the group with all messages"
bob <## "use /d #team to delete the group"
cath <## "#team: alice removed bob from the group with all messages"
alice #$> ("/_get chat #1 count=2", chat, [(0, "moderated [deleted by you]"), (1, "removed bob (Bob)")])
alice #$> ("/_get chat #1 count=2", chat, [(0, "moderated [deleted by you]"), (1, "removed bob")])
bob #$> ("/_get chat #1 count=2", chat, [(1, "moderated [deleted by alice]"), (0, "removed you")])
cath #$> ("/_get chat #1 count=2", chat, [(0, "moderated [deleted by alice]"), (0, "removed bob (Bob)")])
cath #$> ("/_get chat #1 count=2", chat, [(0, "moderated [deleted by alice]"), (0, "removed bob")])
testDeleteMemberMarkMessagesDeleted :: HasCallStack => TestParams -> IO ()
testDeleteMemberMarkMessagesDeleted =
@@ -1868,9 +1888,9 @@ testDeleteMemberMarkMessagesDeleted =
bob <## "#team: alice removed you from the group with all messages"
bob <## "use /d #team to delete the group"
cath <## "#team: alice removed bob from the group with all messages"
alice #$> ("/_get chat #1 count=2", chat, [(0, "hello [marked deleted by you]"), (1, "removed bob (Bob)")])
alice #$> ("/_get chat #1 count=2", chat, [(0, "hello [marked deleted by you]"), (1, "removed bob")])
bob #$> ("/_get chat #1 count=2", chat, [(1, "hello [marked deleted by alice]"), (0, "removed you")])
cath #$> ("/_get chat #1 count=2", chat, [(0, "hello [marked deleted by alice]"), (0, "removed bob (Bob)")])
cath #$> ("/_get chat #1 count=2", chat, [(0, "hello [marked deleted by alice]"), (0, "removed bob")])
testSendMulti :: HasCallStack => TestParams -> IO ()
testSendMulti =
@@ -5564,11 +5584,11 @@ testGroupHistoryWelcomeMessage =
createGroup2 "team" alice bob
alice ##> "/set welcome #team welcome to team"
alice <## "description changed to:"
alice <## "welcome message changed to:"
alice <## "welcome to team"
bob <## "alice updated group #team:"
bob <## "description changed to:"
bob <## "welcome message changed to:"
bob <## "welcome to team"
threadDelay 1000000
@@ -6192,10 +6212,10 @@ testBlockForAllMarkedBlocked =
#$> ( "/_get chat #1 count=6",
chat,
[ (0, "1"),
(1, "blocked bob (Bob)"),
(1, "blocked bob"),
(0, "2 [blocked by admin]"),
(0, "3 [blocked by admin]"),
(1, "unblocked bob (Bob)"),
(1, "unblocked bob"),
(0, "4")
]
)
@@ -6203,10 +6223,10 @@ testBlockForAllMarkedBlocked =
#$> ( "/_get chat #1 count=6",
chat,
[ (0, "1"),
(0, "blocked bob (Bob)"),
(0, "blocked bob"),
(0, "2 [blocked by admin]"),
(0, "3 [blocked by admin]"),
(0, "unblocked bob (Bob)"),
(0, "unblocked bob"),
(0, "4")
]
)
@@ -6273,10 +6293,10 @@ testBlockForAllFullDelete =
#$> ( "/_get chat #1 count=6",
chat,
[ (0, "1"),
(1, "blocked bob (Bob)"),
(1, "blocked bob"),
(0, "blocked [blocked by admin]"),
(0, "blocked [blocked by admin]"),
(1, "unblocked bob (Bob)"),
(1, "unblocked bob"),
(0, "4")
]
)
@@ -6284,10 +6304,10 @@ testBlockForAllFullDelete =
#$> ( "/_get chat #1 count=6",
chat,
[ (0, "1"),
(0, "blocked bob (Bob)"),
(0, "blocked bob"),
(0, "blocked [blocked by admin]"),
(0, "blocked [blocked by admin]"),
(0, "unblocked bob (Bob)"),
(0, "unblocked bob"),
(0, "4")
]
)
@@ -6380,7 +6400,7 @@ testBlockForAllBeforeJoining =
dan ##> "/_get chat #1 count=100"
r <- chat <$> getTermLine dan
r `shouldContain` [(0, "3 [blocked by admin]"), (0, "4 [blocked by admin]"), (0, "unblocked bob (Bob)"), (0, "5")]
r `shouldContain` [(0, "3 [blocked by admin]"), (0, "4 [blocked by admin]"), (0, "unblocked bob"), (0, "5")]
r `shouldNotContain` [(0, "1")]
r `shouldNotContain` [(0, "1 [blocked by admin]")]
r `shouldNotContain` [(0, "2")]
+1 -1
View File
@@ -66,7 +66,7 @@ testUserNotes ps = withNewTestChat ps "alice" aliceProfile $ \alice -> do
alice ##> "/create user secret"
alice <## "user profile: secret"
alice <## "use /p <display name> to change it"
alice <## "use /p <name> [<bio>] to change it"
alice <## "(the updated profile will be sent to all your contacts)"
alice ##> "/tail"
+37 -34
View File
@@ -156,19 +156,19 @@ testUpdateProfile =
connectUsers bob cath
alice ##> "/p"
alice <## "user profile: alice (Alice)"
alice <## "use /p <display name> to change it"
alice <## "use /p <name> [<bio>] to change it"
alice <## "(the updated profile will be sent to all your contacts)"
alice ##> "/p alice"
concurrentlyN_
[ alice <## "user full name removed (your 2 contacts are notified)",
bob <## "contact alice removed full name",
cath <## "contact alice removed full name"
[ alice <## "user bio removed (your 2 contacts are notified)",
bob <## "contact alice removed bio",
cath <## "contact alice removed bio"
]
alice ##> "/p alice Alice Jones"
concurrentlyN_
[ alice <## "user full name changed to Alice Jones (your 2 contacts are notified)",
bob <## "contact alice updated full name: Alice Jones",
cath <## "contact alice updated full name: Alice Jones"
[ alice <## "user bio changed to Alice Jones (your 2 contacts are notified)",
bob <## "contact alice updated bio: Alice Jones",
cath <## "contact alice updated bio: Alice Jones"
]
cath ##> "/p cate"
concurrentlyN_
@@ -282,7 +282,7 @@ testMultiWordProfileNames =
aliceProfile' = baseProfile {displayName = "Alice Jones"}
bobProfile' = baseProfile {displayName = "Bob James"}
cathProfile' = baseProfile {displayName = "Cath Johnson"}
baseProfile = Profile {displayName = "", fullName = "", image = Nothing, contactLink = Nothing, preferences = defaultPrefs}
baseProfile = Profile {displayName = "", fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, preferences = defaultPrefs}
testUserContactLink :: HasCallStack => TestParams -> IO ()
testUserContactLink =
@@ -577,7 +577,7 @@ testDeduplicateContactRequestsProfileChange = testChat3 aliceProfile bobProfile
alice @@@ [("@bob", "Audio/video calls: enabled")]
bob ##> "/p bob"
bob <## "user full name removed (your 0 contacts are notified)"
bob <## "user bio removed (your 0 contacts are notified)"
bob ##> ("/c " <> cLink)
bob <## "connection request sent!"
alice <## "bob wants to connect to you!"
@@ -586,7 +586,7 @@ testDeduplicateContactRequestsProfileChange = testChat3 aliceProfile bobProfile
alice @@@ [("@bob", "Audio/video calls: enabled")]
bob ##> "/p bob Bob Ross"
bob <## "user full name changed to Bob Ross (your 0 contacts are notified)"
bob <## "user bio changed to Bob Ross (your 0 contacts are notified)"
bob ##> ("/c " <> cLink)
alice <#? bob
alice @@@ [("@bob", "Audio/video calls: enabled")]
@@ -1362,8 +1362,8 @@ testConnectIncognitoInvitationLink = testChat3 aliceProfile bobProfile cathProfi
-- bob is not notified on profile change
alice ##> "/p alice"
concurrentlyN_
[ alice <## "user full name removed (your 1 contacts are notified)",
cath <## "contact alice removed full name"
[ alice <## "user bio removed (your 1 contacts are notified)",
cath <## "contact alice removed bio"
]
alice ?#> ("@" <> bobIncognito <> " do you see that I've changed profile?")
bob ?<# (aliceIncognito <> "> do you see that I've changed profile?")
@@ -1852,13 +1852,13 @@ testCantSeeGlobalPrefsUpdateIncognito = testChat3 aliceProfile bobProfile cathPr
]
alice <## "cath (Catherine): contact is connected"
alice ##> "/_profile 1 {\"displayName\": \"alice\", \"fullName\": \"\", \"preferences\": {\"fullDelete\": {\"allow\": \"always\"}, \"receipts\": {\"allow\": \"yes\", \"activated\": true}}}"
alice <## "user full name removed (your 1 contacts are notified)"
alice <## "user bio removed (your 1 contacts are notified)"
alice <## "updated preferences:"
alice <## "Full deletion allowed: always"
(alice </)
-- bob doesn't receive profile update
(bob </)
cath <## "contact alice removed full name"
cath <## "contact alice removed bio"
cath <## "alice updated preferences for you:"
cath <## "Full deletion: enabled for you (you allow: default (no), contact allows: always)"
(cath </)
@@ -2210,7 +2210,7 @@ testSetContactPrefs = testChat2 aliceProfile bobProfile $
createDirectoryIfMissing True "./tests/tmp/bob"
copyFile "./tests/fixtures/test.txt" "./tests/tmp/alice/test.txt"
copyFile "./tests/fixtures/test.txt" "./tests/tmp/bob/test.txt"
bob ##> "/_profile 1 {\"displayName\": \"bob\", \"fullName\": \"Bob\", \"preferences\": {\"voice\": {\"allow\": \"no\"}, \"receipts\": {\"allow\": \"yes\", \"activated\": true}}}"
bob ##> "/_profile 1 {\"displayName\": \"bob\", \"fullName\": \"\", \"shortDescr\": \"Bob\", \"preferences\": {\"voice\": {\"allow\": \"no\"}, \"receipts\": {\"allow\": \"yes\", \"activated\": true}}}"
bob <## "profile image removed"
bob <## "updated preferences:"
bob <## "Voice messages allowed: no"
@@ -2270,12 +2270,12 @@ testSetContactPrefs = testChat2 aliceProfile bobProfile $
bob #$> ("/_get chat @2 count=100", chat, startFeatures <> [(0, "Voice messages: enabled for you"), (1, "voice message (00:10)"), (0, "Voice messages: off")])
(bob </)
bob ##> "/_profile 1 {\"displayName\": \"bob\", \"fullName\": \"\", \"preferences\": {\"voice\": {\"allow\": \"yes\"}, \"receipts\": {\"allow\": \"yes\", \"activated\": true}}}"
bob <## "user full name removed (your 1 contacts are notified)"
bob <## "user bio removed (your 1 contacts are notified)"
bob <## "updated preferences:"
bob <## "Voice messages allowed: yes"
bob #$> ("/_get chat @2 count=100", chat, startFeatures <> [(0, "Voice messages: enabled for you"), (1, "voice message (00:10)"), (0, "Voice messages: off"), (1, "Voice messages: enabled")])
(bob </)
alice <## "contact bob removed full name"
alice <## "contact bob removed bio"
alice <## "bob updated preferences for you:"
alice <## "Voice messages: enabled (you allow: yes, contact allows: yes)"
alice #$> ("/_get chat @2 count=100", chat, startFeatures <> [(1, "Voice messages: enabled for contact"), (0, "voice message (00:10)"), (1, "Voice messages: off"), (0, "updated profile"), (0, "Voice messages: enabled")])
@@ -2596,7 +2596,7 @@ testUpdateMultipleUserPrefs = testChat3 aliceProfile bobProfile cathProfile $
alice #> "@cath hi cath"
cath <# "alice> hi cath"
alice ##> "/_profile 1 {\"displayName\": \"alice\", \"fullName\": \"Alice\", \"preferences\": {\"fullDelete\": {\"allow\": \"always\"}, \"reactions\": {\"allow\": \"no\"}, \"receipts\": {\"allow\": \"yes\", \"activated\": true}}}"
alice ##> "/_profile 1 {\"displayName\": \"alice\", \"fullName\": \"\", \"shortDescr\": \"Alice\", \"preferences\": {\"fullDelete\": {\"allow\": \"always\"}, \"reactions\": {\"allow\": \"no\"}, \"receipts\": {\"allow\": \"yes\", \"activated\": true}}}"
alice <## "updated preferences:"
alice <## "Full deletion allowed: always"
alice <## "Message reactions allowed: no"
@@ -2786,7 +2786,7 @@ testSetUITheme =
theme cm = T.unpack $ encodeJSON UIThemeEntityOverrides {light = Nothing, dark = Just $ UIThemeEntityOverride cm Nothing defaultUIColors}
userInfo a name = do
a <## ("user profile: " <> name)
a <## "use /p <display name> to change it"
a <## "use /p <name> [<bio>] to change it"
a <## "(the updated profile will be sent to all your contacts)"
contactInfo a = do
a <## "contact ID: 2"
@@ -2984,7 +2984,7 @@ testShortLinkInvitationPrepareContact ps@TestParams {largeLinkData} = testChatCf
]
alice <# "bob> hello"
unless largeLinkData $
bob <## "contact alice updated full name: Alice"
bob <## "contact alice updated bio: Alice"
concurrently_
(bob <## "alice (Alice): contact is connected")
(alice <## "bob (Bob): contact is connected")
@@ -3035,9 +3035,12 @@ testShortLinkInvitationLongName = testChatCfg2 testCfg {largeLinkData = False} a
WithTime ("@" <> longName <> " hello")
]
bob <# "alice> hello"
concurrently_
(alice <## (longName <> ": contact is connected"))
(bob <## "alice (Alice): contact is connected")
concurrentlyN_
[ do
alice <## ("contact " <> longName <> " updated bio: Bob")
alice <## (longName <> " (Bob): contact is connected"),
(bob <## "alice (Alice): contact is connected")
]
bob <##> alice
where
longName = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
@@ -3070,7 +3073,7 @@ testShortLinkInvitationConnectRetry ps@TestParams {largeLinkData} = testChatCfgO
]
alice <# "bob> hello"
unless largeLinkData $
bob <## "contact alice updated full name: Alice"
bob <## "contact alice updated bio: Alice"
concurrently_
(bob <## "alice (Alice): contact is connected")
(alice <## "bob (Bob): contact is connected")
@@ -3120,7 +3123,7 @@ testShortLinkAddressPrepareContact ps@TestParams {largeLinkData} = testChatCfg2
alice ##> "/ac bob"
alice <## "bob (Bob): accepting contact request, you can send messages to contact"
unless largeLinkData $
bob <## "contact alice updated full name: Alice"
bob <## "contact alice updated bio: Alice"
concurrently_
(bob <## "alice (Alice): contact is connected")
(alice <## "bob (Bob): contact is connected")
@@ -3208,7 +3211,7 @@ testShortLinkAddressConnectRetry ps@TestParams {largeLinkData} =
alice ##> "/ac bob"
alice <## "bob (Bob): accepting contact request, you can send messages to contact"
unless largeLinkData $
bob <## "contact alice updated full name: Alice"
bob <## "contact alice updated bio: Alice"
concurrently_
(bob <## "alice (Alice): contact is connected")
(alice <## "bob (Bob): contact is connected")
@@ -3266,7 +3269,7 @@ testShortLinkAddressConnectRetryIncognito ps@TestParams {largeLinkData} =
alice ##> ("/ac " <> bobIncognito)
alice <## (bobIncognito <> ": accepting contact request, you can send messages to contact")
unless largeLinkData $
bob <## "contact alice updated full name: Alice"
bob <## "contact alice updated bio: Alice"
concurrentlyN_
[ do
_ <- getTermLine bob
@@ -3492,7 +3495,7 @@ testGroupShortLinkWelcome ps@TestParams {largeLinkData} = testChatCfg2 testCfg {
alice <## "group #team is created"
alice <## "to add members use /a team <name> or /create link #team"
alice ##> "/set welcome #team Welcome!"
alice <## "description changed to:"
alice <## "welcome message changed to:"
alice <## "Welcome!"
alice ##> "/create link #team"
(shortLink, fullLink) <- getGroupLinks alice "team" GRMember True
@@ -3593,7 +3596,7 @@ testShortLinkInvitationConnectPreparedContactIncognito ps@TestParams {largeLinkD
bobIncognito <- getTermLine bob
bob <## "alice: connection started incognito"
unless largeLinkData $
bob <## "contact alice updated full name: Alice"
bob <## "contact alice updated bio: Alice"
_ <- getTermLine bob
concurrentlyN_
[ alice <## (bobIncognito <> ": contact is connected"),
@@ -3626,7 +3629,7 @@ testShortLinkAddressConnectPreparedContactIncognito ps@TestParams {largeLinkData
alice ##> ("/ac " <> bobIncognito)
alice <## (bobIncognito <> ": accepting contact request, you can send messages to contact")
unless largeLinkData $
bob <## "contact alice updated full name: Alice"
bob <## "contact alice updated bio: Alice"
_ <- getTermLine bob
concurrentlyN_
[ alice <## (bobIncognito <> ": contact is connected"),
@@ -3670,7 +3673,7 @@ testShortLinkChangePreparedContactUser ps@TestParams {largeLinkData} = testChatC
]
alice <# "robert> hello"
unless largeLinkData $
bob <## "contact alice updated full name: Alice"
bob <## "contact alice updated bio: Alice"
concurrently_
(bob <## "alice (Alice): contact is connected")
(alice <## "robert: contact is connected")
@@ -3724,7 +3727,7 @@ testShortLinkChangePreparedContactUserDuplicate ps@TestParams {largeLinkData} =
]
alice <# "robert_1> hello"
unless largeLinkData $
bob <## "contact alice_1 updated full name: Alice"
bob <## "contact alice_1 updated bio: Alice"
concurrently_
(bob <## "alice_1 (Alice): contact is connected")
(alice <## "robert_1: contact is connected")
@@ -4087,7 +4090,7 @@ testShortLinkAddressChangeAutoReply ps@TestParams {largeLinkData} = testChatCfg3
alice <## "bob (Bob): accepting contact request..."
alice <## "bob (Bob): you can send messages to contact"
unless largeLinkData $
bob <## "contact alice updated full name: Alice"
bob <## "contact alice updated bio: Alice"
concurrently_
(bob <### (["alice (Alice): contact is connected"] <> [WithTime "alice> welcome!" | not largeLinkData]))
(alice <### (["bob (Bob): contact is connected"] <> [WithTime "@bob welcome!" | not largeLinkData]))
@@ -4109,7 +4112,7 @@ testShortLinkAddressChangeAutoReply ps@TestParams {largeLinkData} = testChatCfg3
alice <## "cath (Catherine): accepting contact request..."
alice <## "cath (Catherine): you can send messages to contact"
unless largeLinkData $
cath <## "contact alice updated full name: Alice"
cath <## "contact alice updated bio: Alice"
concurrently_
(cath <## "alice (Alice): contact is connected")
(alice <## "cath (Catherine): contact is connected")
+13 -10
View File
@@ -57,25 +57,28 @@ defaultPrefs :: Maybe Preferences
defaultPrefs = Just $ toChatPrefs defaultChatPrefs
aliceDesktopProfile :: Profile
aliceDesktopProfile = Profile {displayName = "alice_desktop", fullName = "Alice Desktop", image = Nothing, contactLink = Nothing, preferences = defaultPrefs}
aliceDesktopProfile = mkProfile "alice_desktop" "Alice Desktop" Nothing
aliceProfile :: Profile
aliceProfile = Profile {displayName = "alice", fullName = "Alice", image = Nothing, contactLink = Nothing, preferences = defaultPrefs}
aliceProfile = mkProfile "alice" "Alice" Nothing
bobProfile :: Profile
bobProfile = Profile {displayName = "bob", fullName = "Bob", image = Just (ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAKHGlDQ1BJQ0MgUHJvZmlsZQAASImFVgdUVNcWve9Nb7QZeu9NehtAem/Sq6gMQ28OQxWxgAQjEFFEREARNFQFg1KjiIhiIQgoYA9IEFBisCAq6OQNJNH4//r/zDpz9ttzz7n73ffWmg0A6QCDxYqD+QCIT0hmezlYywQEBsngngEYCAIy0AC6DGYSy8rDwxUg8Xf9d7wbAxC33tHgzvrP3/9nCISFJzEBgIIRTGey2MkILkawT1oyi4tnEUxjI6IQvMLFkauYqxjQQtewwuoaHy8bBNMBwJMZDHYkAERbhJdJZUYic4hhCNZOCItOQDB3vjkzioFwxLsIXhcRl5IOAImrRzs+fivCk7QRrIL0shAcwNUW+tX8yH/tFfrPXgxG5D84Pi6F+dc9ck+HHJ7g641UMSQlQATQBHEgBaQDGcACbLAVYaIRJhx5Dv+9j77aZ4OsZIFtSEc0iARRIBnpt/9qlvfqpGSQBhjImnCEcUU+NtxnujZy4fbqVEiU/wuXdQyA9S0cDqfzC+e2F4DzyLkSB79wyi0A8KoBcL2GmcJOXePQ3C8MIAJeQAOiQArIAxXuWwMMgSmwBHbAGbgDHxAINgMmojceUZUGMkEWyAX54AA4DMpAJTgJ6sAZ0ALawQVwGVwDt8AQGAUPwQSYBi/AAngHliEIwkEUiAqJQtKQIqQO6UJ0yByyg1whLygQCoEioQQoBcqE9kD5UBFUBlVB9dBPUCd0GboBDUP3oUloDnoNfYRRMBmmwZKwEqwF02Er2AX2gTfBkXAinAHnwPvhUrgaPg23wZfhW/AoPAG/gBdRAEVCCaFkURooOsoG5Y4KQkWg2KidqDxUCaoa1YTqQvWj7qAmUPOoD2gsmoqWQWugTdGOaF80E52I3okuQJeh69Bt6D70HfQkegH9GUPBSGDUMSYYJ0wAJhKThsnFlGBqMK2Yq5hRzDTmHRaLFcIqY42wjthAbAx2O7YAewzbjO3BDmOnsIs4HE4Up44zw7njGLhkXC7uKO407hJuBDeNe48n4aXxunh7fBA+AZ+NL8E34LvxI/gZ/DKBj6BIMCG4E8II2wiFhFOELsJtwjRhmchPVCaaEX2IMcQsYimxiXiV+Ij4hkQiyZGMSZ6kaNJuUinpLOk6aZL0gSxAViPbkIPJKeT95FpyD/k++Q2FQlGiWFKCKMmU/ZR6yhXKE8p7HiqPJo8TTxjPLp5ynjaeEZ6XvAReRV4r3s28GbwlvOd4b/PO8xH4lPhs+Bh8O/nK+Tr5xvkW+an8Ovzu/PH8BfwN/Df4ZwVwAkoCdgJhAjkCJwWuCExRUVR5qg2VSd1DPUW9Sp2mYWnKNCdaDC2fdoY2SFsQFBDUF/QTTBcsF7woOCGEElISchKKEyoUahEaE/ooLClsJRwuvE+4SXhEeElEXMRSJFwkT6RZZFTko6iMqJ1orOhB0XbRx2JoMTUxT7E0seNiV8XmxWnipuJM8TzxFvEHErCEmoSXxHaJkxIDEouSUpIOkizJo5JXJOelhKQspWKkiqW6peakqdLm0tHSxdKXpJ/LCMpYycTJlMr0ySzISsg6yqbIVskOyi7LKcv5ymXLNcs9lifK0+Uj5Ivle+UXFKQV3BQyFRoVHigSFOmKUYpHFPsVl5SUlfyV9iq1K80qiyg7KWcoNyo/UqGoWKgkqlSr3FXFqtJVY1WPqQ6pwWoGalFq5Wq31WF1Q/Vo9WPqw+sw64zXJayrXjeuQdaw0kjVaNSY1BTSdNXM1mzXfKmloBWkdVCrX+uztoF2nPYp7Yc6AjrOOtk6XTqvddV0mbrlunf1KHr2erv0OvRe6avrh+sf179nQDVwM9hr0GvwydDIkG3YZDhnpGAUYlRhNE6n0T3oBfTrxhhja+NdxheMP5gYmiSbtJj8YaphGmvaYDq7Xnl9+PpT66fM5MwYZlVmE+Yy5iHmJ8wnLGQtGBbVFk8t5S3DLGssZ6xUrWKsTlu9tNa2Zlu3Wi/ZmNjssOmxRdk62ObZDtoJ2Pnaldk9sZezj7RvtF9wMHDY7tDjiHF0cTzoOO4k6cR0qndacDZy3uHc50J28XYpc3nqqubKdu1yg92c3Q65PdqguCFhQ7s7cHdyP+T+2EPZI9HjZ0+sp4dnueczLx2vTK9+b6r3Fu8G73c+1j6FPg99VXxTfHv9eP2C/er9lvxt/Yv8JwK0AnYE3AoUC4wO7AjCBfkF1QQtbrTbeHjjdLBBcG7w2CblTembbmwW2xy3+eIW3i2MLedCMCH+IQ0hKwx3RjVjMdQptCJ0gWnDPMJ8EWYZVhw2F24WXhQ+E2EWURQxG2kWeShyLsoiqiRqPtomuiz6VYxjTGXMUqx7bG0sJ84/rjkeHx8S35kgkBCb0LdVamv61mGWOiuXNZFokng4cYHtwq5JgpI2JXUk05A/0oEUlZTvUiZTzVPLU9+n+aWdS+dPT0gf2Ka2bd+2mQz7jB+3o7czt/dmymZmZU7usNpRtRPaGbqzd5f8rpxd07sddtdlEbNis37J1s4uyn67x39PV45kzu6cqe8cvmvM5cll547vNd1b+T36++jvB/fp7Tu673NeWN7NfO38kvyVAmbBzR90fij9gbM/Yv9goWHh8QPYAwkHxg5aHKwr4i/KKJo65HaorVimOK/47eEth2+U6JdUHiEeSTkyUepa2nFU4eiBoytlUWWj5dblzRUSFfsqlo6FHRs5bnm8qVKyMr/y44noE/eqHKraqpWqS05iT6aefHbK71T/j/Qf62vEavJrPtUm1E7UedX11RvV1zdINBQ2wo0pjXOng08PnbE909Gk0VTVLNScfxacTTn7/KeQn8ZaXFp6z9HPNZ1XPF/RSm3Na4PatrUttEe1T3QEdgx3Onf2dpl2tf6s+XPtBdkL5RcFLxZ2E7tzujmXMi4t9rB65i9HXp7q3dL78ErAlbt9nn2DV12uXr9mf+1Kv1X/petm1y/cMLnReZN+s/2W4a22AYOB1l8MfmkdNBxsu210u2PIeKhreP1w94jFyOU7tneu3XW6e2t0w+jwmO/YvfHg8Yl7Yfdm78fdf/Ug9cHyw92PMI/yHvM9Lnki8aT6V9VfmycMJy5O2k4OPPV++nCKOfXit6TfVqZznlGelcxIz9TP6s5emLOfG3q+8fn0C9aL5fnc3/l/r3ip8vL8H5Z/DCwELEy/Yr/ivC54I/qm9q3+295Fj8Un7+LfLS/lvRd9X/eB/qH/o//HmeW0FdxK6SfVT12fXT4/4sRzOCwGm7FqBVBIwhERALyuBYASCAB1CPEPG9f8119+BvrK2fyNwVndL5jhvubRVsMQgCakeCFp04OsQ1LJEgAe5NodqT6WANbT+yf/iqQIPd21PXgaAcDJcjivtwJAQHLFgcNZ9uBwPlUgYhHf1z37f7V9g9e8ITewiP88wfWIYET6HPg21nzjV2fybQVcxfrg2/onng/F50lD/ccAAAA4ZVhJZk1NACoAAAAIAAGHaQAEAAAAAQAAABoAAAAAAAKgAgAEAAAAAQAAABigAwAEAAAAAQAAABgAAAAAwf1XlwAAAaNJREFUSA3FlT1LA0EQQBN/gYUYRTksJZVgEbCR/D+7QMr8ABtttBBCsLGzsLG2sxaxED/ie4d77u0dyaE5HHjczn7MzO7M7nU6/yXz+bwLhzCCjTQO+rZhDH3opuNLdRYN4RHe4RIKJ7R34Ro+4AEGSw2mE1iUwT18gpI74WvkGlccu4XNdH0jnYU7cAUacidn37qR23cOxc4aGU0nYUAn7iSWEHkz46w0ocdQu1X6B/AMQZ5o7KfBqNOfwRH8JB7FajGhnmcpKvQe3MEbvILiDm5gPXaCHnZr4vvFGMoEKudKn8YvQIOOe+YzCPop7dwJ3zRfJ7GDuso4YJGRa0yZgg4tUaNXdGrbuZWKKxzYYEJc2xp9AUUjGt8KC2jvgYadF8+10vJyDnNLXwbdiWUZi0fUK01Eoc+AZhCLZVzK4Vq6sDUdz+0dEcbbTTIOJmAyTVhx/WmvrExbv2jtPhWLKodjCtefZiEeZeVZWWSndgwj6fVf3XON8Qwq15++uoqrfYVrow6dGBpCq79ME291jaB0/Q2CPncyht/99MNO/vr9AqW/CGi8sJqbAAAAAElFTkSuQmCC"), contactLink = Nothing, preferences = defaultPrefs}
bobProfile = mkProfile "bob" "Bob" $ Just $ ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAKHGlDQ1BJQ0MgUHJvZmlsZQAASImFVgdUVNcWve9Nb7QZeu9NehtAem/Sq6gMQ28OQxWxgAQjEFFEREARNFQFg1KjiIhiIQgoYA9IEFBisCAq6OQNJNH4//r/zDpz9ttzz7n73ffWmg0A6QCDxYqD+QCIT0hmezlYywQEBsngngEYCAIy0AC6DGYSy8rDwxUg8Xf9d7wbAxC33tHgzvrP3/9nCISFJzEBgIIRTGey2MkILkawT1oyi4tnEUxjI6IQvMLFkauYqxjQQtewwuoaHy8bBNMBwJMZDHYkAERbhJdJZUYic4hhCNZOCItOQDB3vjkzioFwxLsIXhcRl5IOAImrRzs+fivCk7QRrIL0shAcwNUW+tX8yH/tFfrPXgxG5D84Pi6F+dc9ck+HHJ7g641UMSQlQATQBHEgBaQDGcACbLAVYaIRJhx5Dv+9j77aZ4OsZIFtSEc0iARRIBnpt/9qlvfqpGSQBhjImnCEcUU+NtxnujZy4fbqVEiU/wuXdQyA9S0cDqfzC+e2F4DzyLkSB79wyi0A8KoBcL2GmcJOXePQ3C8MIAJeQAOiQArIAxXuWwMMgSmwBHbAGbgDHxAINgMmojceUZUGMkEWyAX54AA4DMpAJTgJ6sAZ0ALawQVwGVwDt8AQGAUPwQSYBi/AAngHliEIwkEUiAqJQtKQIqQO6UJ0yByyg1whLygQCoEioQQoBcqE9kD5UBFUBlVB9dBPUCd0GboBDUP3oUloDnoNfYRRMBmmwZKwEqwF02Er2AX2gTfBkXAinAHnwPvhUrgaPg23wZfhW/AoPAG/gBdRAEVCCaFkURooOsoG5Y4KQkWg2KidqDxUCaoa1YTqQvWj7qAmUPOoD2gsmoqWQWugTdGOaF80E52I3okuQJeh69Bt6D70HfQkegH9GUPBSGDUMSYYJ0wAJhKThsnFlGBqMK2Yq5hRzDTmHRaLFcIqY42wjthAbAx2O7YAewzbjO3BDmOnsIs4HE4Up44zw7njGLhkXC7uKO407hJuBDeNe48n4aXxunh7fBA+AZ+NL8E34LvxI/gZ/DKBj6BIMCG4E8II2wiFhFOELsJtwjRhmchPVCaaEX2IMcQsYimxiXiV+Ij4hkQiyZGMSZ6kaNJuUinpLOk6aZL0gSxAViPbkIPJKeT95FpyD/k++Q2FQlGiWFKCKMmU/ZR6yhXKE8p7HiqPJo8TTxjPLp5ynjaeEZ6XvAReRV4r3s28GbwlvOd4b/PO8xH4lPhs+Bh8O/nK+Tr5xvkW+an8Ovzu/PH8BfwN/Df4ZwVwAkoCdgJhAjkCJwWuCExRUVR5qg2VSd1DPUW9Sp2mYWnKNCdaDC2fdoY2SFsQFBDUF/QTTBcsF7woOCGEElISchKKEyoUahEaE/ooLClsJRwuvE+4SXhEeElEXMRSJFwkT6RZZFTko6iMqJ1orOhB0XbRx2JoMTUxT7E0seNiV8XmxWnipuJM8TzxFvEHErCEmoSXxHaJkxIDEouSUpIOkizJo5JXJOelhKQspWKkiqW6peakqdLm0tHSxdKXpJ/LCMpYycTJlMr0ySzISsg6yqbIVskOyi7LKcv5ymXLNcs9lifK0+Uj5Ivle+UXFKQV3BQyFRoVHigSFOmKUYpHFPsVl5SUlfyV9iq1K80qiyg7KWcoNyo/UqGoWKgkqlSr3FXFqtJVY1WPqQ6pwWoGalFq5Wq31WF1Q/Vo9WPqw+sw64zXJayrXjeuQdaw0kjVaNSY1BTSdNXM1mzXfKmloBWkdVCrX+uztoF2nPYp7Yc6AjrOOtk6XTqvddV0mbrlunf1KHr2erv0OvRe6avrh+sf179nQDVwM9hr0GvwydDIkG3YZDhnpGAUYlRhNE6n0T3oBfTrxhhja+NdxheMP5gYmiSbtJj8YaphGmvaYDq7Xnl9+PpT66fM5MwYZlVmE+Yy5iHmJ8wnLGQtGBbVFk8t5S3DLGssZ6xUrWKsTlu9tNa2Zlu3Wi/ZmNjssOmxRdk62ObZDtoJ2Pnaldk9sZezj7RvtF9wMHDY7tDjiHF0cTzoOO4k6cR0qndacDZy3uHc50J28XYpc3nqqubKdu1yg92c3Q65PdqguCFhQ7s7cHdyP+T+2EPZI9HjZ0+sp4dnueczLx2vTK9+b6r3Fu8G73c+1j6FPg99VXxTfHv9eP2C/er9lvxt/Yv8JwK0AnYE3AoUC4wO7AjCBfkF1QQtbrTbeHjjdLBBcG7w2CblTembbmwW2xy3+eIW3i2MLedCMCH+IQ0hKwx3RjVjMdQptCJ0gWnDPMJ8EWYZVhw2F24WXhQ+E2EWURQxG2kWeShyLsoiqiRqPtomuiz6VYxjTGXMUqx7bG0sJ84/rjkeHx8S35kgkBCb0LdVamv61mGWOiuXNZFokng4cYHtwq5JgpI2JXUk05A/0oEUlZTvUiZTzVPLU9+n+aWdS+dPT0gf2Ka2bd+2mQz7jB+3o7czt/dmymZmZU7usNpRtRPaGbqzd5f8rpxd07sddtdlEbNis37J1s4uyn67x39PV45kzu6cqe8cvmvM5cll547vNd1b+T36++jvB/fp7Tu673NeWN7NfO38kvyVAmbBzR90fij9gbM/Yv9goWHh8QPYAwkHxg5aHKwr4i/KKJo65HaorVimOK/47eEth2+U6JdUHiEeSTkyUepa2nFU4eiBoytlUWWj5dblzRUSFfsqlo6FHRs5bnm8qVKyMr/y44noE/eqHKraqpWqS05iT6aefHbK71T/j/Qf62vEavJrPtUm1E7UedX11RvV1zdINBQ2wo0pjXOng08PnbE909Gk0VTVLNScfxacTTn7/KeQn8ZaXFp6z9HPNZ1XPF/RSm3Na4PatrUttEe1T3QEdgx3Onf2dpl2tf6s+XPtBdkL5RcFLxZ2E7tzujmXMi4t9rB65i9HXp7q3dL78ErAlbt9nn2DV12uXr9mf+1Kv1X/petm1y/cMLnReZN+s/2W4a22AYOB1l8MfmkdNBxsu210u2PIeKhreP1w94jFyOU7tneu3XW6e2t0w+jwmO/YvfHg8Yl7Yfdm78fdf/Ug9cHyw92PMI/yHvM9Lnki8aT6V9VfmycMJy5O2k4OPPV++nCKOfXit6TfVqZznlGelcxIz9TP6s5emLOfG3q+8fn0C9aL5fnc3/l/r3ip8vL8H5Z/DCwELEy/Yr/ivC54I/qm9q3+295Fj8Un7+LfLS/lvRd9X/eB/qH/o//HmeW0FdxK6SfVT12fXT4/4sRzOCwGm7FqBVBIwhERALyuBYASCAB1CPEPG9f8119+BvrK2fyNwVndL5jhvubRVsMQgCakeCFp04OsQ1LJEgAe5NodqT6WANbT+yf/iqQIPd21PXgaAcDJcjivtwJAQHLFgcNZ9uBwPlUgYhHf1z37f7V9g9e8ITewiP88wfWIYET6HPg21nzjV2fybQVcxfrg2/onng/F50lD/ccAAAA4ZVhJZk1NACoAAAAIAAGHaQAEAAAAAQAAABoAAAAAAAKgAgAEAAAAAQAAABigAwAEAAAAAQAAABgAAAAAwf1XlwAAAaNJREFUSA3FlT1LA0EQQBN/gYUYRTksJZVgEbCR/D+7QMr8ABtttBBCsLGzsLG2sxaxED/ie4d77u0dyaE5HHjczn7MzO7M7nU6/yXz+bwLhzCCjTQO+rZhDH3opuNLdRYN4RHe4RIKJ7R34Ro+4AEGSw2mE1iUwT18gpI74WvkGlccu4XNdH0jnYU7cAUacidn37qR23cOxc4aGU0nYUAn7iSWEHkz46w0ocdQu1X6B/AMQZ5o7KfBqNOfwRH8JB7FajGhnmcpKvQe3MEbvILiDm5gPXaCHnZr4vvFGMoEKudKn8YvQIOOe+YzCPop7dwJ3zRfJ7GDuso4YJGRa0yZgg4tUaNXdGrbuZWKKxzYYEJc2xp9AUUjGt8KC2jvgYadF8+10vJyDnNLXwbdiWUZi0fUK01Eoc+AZhCLZVzK4Vq6sDUdz+0dEcbbTTIOJmAyTVhx/WmvrExbv2jtPhWLKodjCtefZiEeZeVZWWSndgwj6fVf3XON8Qwq15++uoqrfYVrow6dGBpCq79ME291jaB0/Q2CPncyht/99MNO/vr9AqW/CGi8sJqbAAAAAElFTkSuQmCC"
cathProfile :: Profile
cathProfile = Profile {displayName = "cath", fullName = "Catherine", image = Nothing, contactLink = Nothing, preferences = defaultPrefs}
cathProfile = mkProfile "cath" "Catherine" Nothing
danProfile :: Profile
danProfile = Profile {displayName = "dan", fullName = "Daniel", image = Nothing, contactLink = Nothing, preferences = defaultPrefs}
danProfile = mkProfile "dan" "Daniel" Nothing
eveProfile :: Profile
eveProfile = Profile {displayName = "eve", fullName = "Eve", image = Nothing, contactLink = Nothing, preferences = defaultPrefs}
eveProfile = mkProfile "eve" "Eve" Nothing
businessProfile :: Profile
businessProfile = Profile {displayName = "biz", fullName = "Biz Inc", image = Nothing, contactLink = Nothing, preferences = defaultPrefs}
businessProfile = mkProfile "biz" "Biz Inc" Nothing
mkProfile :: T.Text -> T.Text -> Maybe ImageData -> Profile
mkProfile displayName descr image = Profile {displayName, fullName = "", shortDescr = Just descr, image, contactLink = Nothing, preferences = defaultPrefs}
it :: HasCallStack => String -> (TestParams -> Expectation) -> SpecWith (Arg (TestParams -> Expectation))
it name test =
@@ -685,7 +688,7 @@ lastItemId cc = do
showActiveUser :: HasCallStack => TestCC -> String -> Expectation
showActiveUser cc name = do
cc <## ("user profile: " <> name)
cc <## "use /p <display name> to change it"
cc <## "use /p <name> [<bio>] to change it"
cc <## "(the updated profile will be sent to all your contacts)"
connectUsersNoShortLink :: HasCallStack => TestCC -> TestCC -> IO ()
@@ -710,8 +713,8 @@ connectUsers_ cc1 cc2 noShortLink = do
showName :: TestCC -> IO String
showName (TestCC ChatController {currentUser} _ _ _ _ _) = do
Just User {localDisplayName, profile = LocalProfile {fullName}} <- readTVarIO currentUser
pure . T.unpack $ localDisplayName <> optionalFullName localDisplayName fullName
Just User {localDisplayName, profile = LocalProfile {fullName, shortDescr}} <- readTVarIO currentUser
pure . T.unpack $ localDisplayName <> optionalFullName localDisplayName fullName shortDescr
createGroup2 :: HasCallStack => String -> TestCC -> TestCC -> IO ()
createGroup2 gName cc1 cc2 = createGroup2' gName cc1 (cc2, GRAdmin) True
+3 -3
View File
@@ -17,10 +17,10 @@ activeUserExistsTagged :: LB.ByteString
activeUserExistsTagged = "{\"error\":{\"type\":\"error\",\"errorType\":{\"type\":\"userExists\",\"contactName\":\"alice\"}}}"
activeUserSwift :: LB.ByteString
activeUserSwift = "{\"result\":{\"_owsf\":true,\"activeUser\":{\"user\":{\"userId\":1,\"agentUserId\":\"1\",\"userContactId\":1,\"localDisplayName\":\"alice\",\"profile\":{\"profileId\":1,\"displayName\":\"alice\",\"fullName\":\"Alice\",\"localAlias\":\"\"},\"fullPreferences\":{\"timedMessages\":{\"allow\":\"yes\"},\"fullDelete\":{\"allow\":\"no\"},\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"},\"calls\":{\"allow\":\"yes\"}},\"activeUser\":true,\"activeOrder\":1,\"showNtfs\":true,\"sendRcptsContacts\":true,\"sendRcptsSmallGroups\":true}}}}"
activeUserSwift = "{\"result\":{\"_owsf\":true,\"activeUser\":{\"user\":{\"userId\":1,\"agentUserId\":\"1\",\"userContactId\":1,\"localDisplayName\":\"alice\",\"profile\":{\"profileId\":1,\"displayName\":\"alice\",\"fullName\":\"\",\"shortDescr\":\"Alice\",\"localAlias\":\"\"},\"fullPreferences\":{\"timedMessages\":{\"allow\":\"yes\"},\"fullDelete\":{\"allow\":\"no\"},\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"},\"calls\":{\"allow\":\"yes\"}},\"activeUser\":true,\"activeOrder\":1,\"showNtfs\":true,\"sendRcptsContacts\":true,\"sendRcptsSmallGroups\":true}}}}"
activeUserTagged :: LB.ByteString
activeUserTagged = "{\"result\":{\"type\":\"activeUser\",\"user\":{\"userId\":1,\"agentUserId\":\"1\",\"userContactId\":1,\"localDisplayName\":\"alice\",\"profile\":{\"profileId\":1,\"displayName\":\"alice\",\"fullName\":\"Alice\",\"localAlias\":\"\"},\"fullPreferences\":{\"timedMessages\":{\"allow\":\"yes\"},\"fullDelete\":{\"allow\":\"no\"},\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"},\"calls\":{\"allow\":\"yes\"}},\"activeUser\":true,\"activeOrder\":1,\"showNtfs\":true,\"sendRcptsContacts\":true,\"sendRcptsSmallGroups\":true}}}"
activeUserTagged = "{\"result\":{\"type\":\"activeUser\",\"user\":{\"userId\":1,\"agentUserId\":\"1\",\"userContactId\":1,\"localDisplayName\":\"alice\",\"profile\":{\"profileId\":1,\"displayName\":\"alice\",\"fullName\":\"\",\"shortDescr\":\"Alice\",\"localAlias\":\"\"},\"fullPreferences\":{\"timedMessages\":{\"allow\":\"yes\"},\"fullDelete\":{\"allow\":\"no\"},\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"},\"calls\":{\"allow\":\"yes\"}},\"activeUser\":true,\"activeOrder\":1,\"showNtfs\":true,\"sendRcptsContacts\":true,\"sendRcptsSmallGroups\":true}}}"
chatStartedSwift :: LB.ByteString
chatStartedSwift = "{\"result\":{\"_owsf\":true,\"chatStarted\":{}}}"
@@ -35,7 +35,7 @@ networkStatusesTagged :: LB.ByteString
networkStatusesTagged = "{\"result\":{\"type\":\"networkStatuses\",\"user_\":" <> userJSON <> ",\"networkStatuses\":[]}}"
userJSON :: LB.ByteString
userJSON = "{\"userId\":1,\"agentUserId\":\"1\",\"userContactId\":1,\"localDisplayName\":\"alice\",\"profile\":{\"profileId\":1,\"displayName\":\"alice\",\"fullName\":\"Alice\",\"localAlias\":\"\"},\"fullPreferences\":{\"timedMessages\":{\"allow\":\"yes\"},\"fullDelete\":{\"allow\":\"no\"},\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"},\"calls\":{\"allow\":\"yes\"}},\"activeUser\":true,\"activeOrder\":1,\"showNtfs\":true,\"sendRcptsContacts\":true,\"sendRcptsSmallGroups\":true}"
userJSON = "{\"userId\":1,\"agentUserId\":\"1\",\"userContactId\":1,\"localDisplayName\":\"alice\",\"profile\":{\"profileId\":1,\"displayName\":\"alice\",\"fullName\":\"\",\"shortDescr\":\"Alice\",\"localAlias\":\"\"},\"fullPreferences\":{\"timedMessages\":{\"allow\":\"yes\"},\"fullDelete\":{\"allow\":\"no\"},\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"},\"calls\":{\"allow\":\"yes\"}},\"activeUser\":true,\"activeOrder\":1,\"showNtfs\":true,\"sendRcptsContacts\":true,\"sendRcptsSmallGroups\":true}"
memberSubSummarySwift :: LB.ByteString
memberSubSummarySwift = "{\"result\":{\"_owsf\":true,\"memberSubSummary\":{\"user\":" <> userJSON <> ",\"memberSubscriptions\":[]}}}"
+3 -3
View File
@@ -104,10 +104,10 @@ testGroupPreferences :: Maybe GroupPreferences
testGroupPreferences = Just GroupPreferences {timedMessages = Nothing, directMessages = Nothing, reactions = Just ReactionsGroupPreference {enable = FEOn}, voice = Just VoiceGroupPreference {enable = FEOn, role = Nothing}, files = Nothing, fullDelete = Nothing, simplexLinks = Nothing, history = Nothing, reports = Nothing}
testProfile :: Profile
testProfile = Profile {displayName = "alice", fullName = "Alice", image = Just (ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII="), contactLink = Nothing, preferences = testChatPreferences}
testProfile = Profile {displayName = "alice", fullName = "Alice", shortDescr = Nothing, image = Just (ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII="), contactLink = Nothing, preferences = testChatPreferences}
testGroupProfile :: GroupProfile
testGroupProfile = GroupProfile {displayName = "team", fullName = "Team", description = Nothing, image = Nothing, groupPreferences = testGroupPreferences, memberAdmission = Nothing}
testGroupProfile = GroupProfile {displayName = "team", fullName = "Team", description = Nothing, shortDescr = Nothing, image = Nothing, groupPreferences = testGroupPreferences, memberAdmission = Nothing}
decodeChatMessageTest :: Spec
decodeChatMessageTest = describe "Chat message encoding/decoding" $ do
@@ -223,7 +223,7 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do
#==# XInfo testProfile
it "x.info with empty full name" $
"{\"v\":\"1\",\"event\":\"x.info\",\"params\":{\"profile\":{\"fullName\":\"\",\"displayName\":\"alice\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}"
#==# XInfo Profile {displayName = "alice", fullName = "", image = Nothing, contactLink = Nothing, preferences = testChatPreferences}
#==# XInfo Profile {displayName = "alice", fullName = "", shortDescr = Nothing, image = Nothing, contactLink = Nothing, preferences = testChatPreferences}
it "x.contact with xContactId" $
"{\"v\":\"1\",\"event\":\"x.contact\",\"params\":{\"contactReqId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}"
#==# XContact testProfile (Just $ XContactId "\1\2\3\4") Nothing Nothing
+4 -4
View File
@@ -448,7 +448,7 @@ multipleProfilesTest = testChat4 aliceProfile aliceDesktopProfile bobProfile cat
desktop ##> "/create user desk_bottom"
desktop <## "user profile: desk_bottom"
desktop <## "use /p <display name> to change it"
desktop <## "use /p <name> [<bio>] to change it"
desktop <## "(the updated profile will be sent to all your contacts)"
desktop ##> "/users"
desktop <## "alice_desktop (Alice Desktop)"
@@ -461,7 +461,7 @@ multipleProfilesTest = testChat4 aliceProfile aliceDesktopProfile bobProfile cat
desktop ##> "/create user alt_alice"
desktop <## "user profile: alt_alice"
desktop <## "use /p <display name> to change it"
desktop <## "use /p <name> [<bio>] to change it"
desktop <## "(the updated profile will be sent to all your contacts)"
desktop ##> "/users"
@@ -470,7 +470,7 @@ multipleProfilesTest = testChat4 aliceProfile aliceDesktopProfile bobProfile cat
desktop ##> "/user"
desktop <## "user profile: alt_alice"
desktop <## "use /p <display name> to change it"
desktop <## "use /p <name> [<bio>] to change it"
desktop <## "(the updated profile will be sent to all your contacts)"
bob #> "@alice hi"
@@ -483,7 +483,7 @@ multipleProfilesTest = testChat4 aliceProfile aliceDesktopProfile bobProfile cat
desktop <## "Using local profile"
desktop ##> "/user"
desktop <## "user profile: desk_bottom"
desktop <## "use /p <display name> to change it"
desktop <## "use /p <name> [<bio>] to change it"
desktop <## "(the updated profile will be sent to all your contacts)"
bob #> "@alice hey"