From 68b7f09c8f1a313a6ad87f3a110994f3edd03b2d Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Mon, 21 Jul 2025 18:07:21 +0000 Subject: [PATCH] core, ui: chat banner (#6089) * core: create banner item * filter deletions * fix query * ios * fixes * remove comment * revert diff * refactor * fix most tests * fix tests * spacer * plans * create banner for 1-time link initiator * style in progress * change background * ui * remove bio length limit * ui * create banner for client chat * rename * more contexts * fix tests * move * fixed image size * plans * remove diff * kotlin * copy * paddings * paddings * comment * layout, messages * fonts * texts, icons * kotlin refactor * kotlin texts * fix date * Revert "fix date" This reverts commit abbd48b3344308c2bf56bc996d9cde21b24279c4. * date * fix texts * kotlin date * color and corners * kotlin * color * update banner, context menu in ios * update texts, do not show epoch timestamp for banner * fix texts --------- Co-authored-by: Evgeny Poberezkin --- apps/ios/Shared/Model/SimpleXAPI.swift | 2 +- apps/ios/Shared/Views/Chat/ChatItemView.swift | 1 + apps/ios/Shared/Views/Chat/ChatView.swift | 203 ++++++++++++++--- .../Views/ChatList/ChatPreviewView.swift | 6 +- .../Views/Helpers/ChatItemClipShape.swift | 2 +- .../Shared/Views/Helpers/ProfileImage.swift | 6 +- .../bg.xcloc/Localized Contents/bg.xliff | 48 +++- .../cs.xcloc/Localized Contents/cs.xliff | 48 +++- .../de.xcloc/Localized Contents/de.xliff | 49 +++- .../en.xcloc/Localized Contents/en.xliff | 60 ++++- .../es.xcloc/Localized Contents/es.xliff | 49 +++- .../fi.xcloc/Localized Contents/fi.xliff | 48 +++- .../fr.xcloc/Localized Contents/fr.xliff | 48 +++- .../hu.xcloc/Localized Contents/hu.xliff | 49 +++- .../it.xcloc/Localized Contents/it.xliff | 49 +++- .../ja.xcloc/Localized Contents/ja.xliff | 48 +++- .../nl.xcloc/Localized Contents/nl.xliff | 48 +++- .../pl.xcloc/Localized Contents/pl.xliff | 48 +++- .../ru.xcloc/Localized Contents/ru.xliff | 49 +++- .../th.xcloc/Localized Contents/th.xliff | 48 +++- .../tr.xcloc/Localized Contents/tr.xliff | 49 +++- .../uk.xcloc/Localized Contents/uk.xliff | 48 +++- .../Localized Contents/zh-Hans.xliff | 48 +++- apps/ios/SimpleXChat/ChatTypes.swift | 4 + .../chat/simplex/common/model/ChatModel.kt | 4 + .../chat/simplex/common/model/SimpleXAPI.kt | 2 +- .../simplex/common/views/chat/ChatView.kt | 214 +++++++++++++++--- .../common/views/chat/item/ChatItemView.kt | 1 + .../common/views/chatlist/ChatPreviewView.kt | 5 +- .../commonMain/resources/MR/base/strings.xml | 11 + .../MR/images/ic_arrow_circle_right.svg | 1 + src/Simplex/Chat/Library/Commands.hs | 8 +- src/Simplex/Chat/Library/Internal.hs | 6 +- src/Simplex/Chat/Library/Subscriber.hs | 5 + src/Simplex/Chat/Messages/CIContent.hs | 9 + src/Simplex/Chat/Store/Messages.hs | 8 +- .../SQLite/Migrations/agent_query_plans.txt | 9 +- .../SQLite/Migrations/chat_query_plans.txt | 28 ++- tests/ChatTests/Direct.hs | 85 ++++--- tests/ChatTests/Groups.hs | 8 +- tests/ChatTests/Profiles.hs | 2 +- tests/ChatTests/Utils.hs | 22 +- 42 files changed, 1280 insertions(+), 206 deletions(-) create mode 100644 apps/multiplatform/common/src/commonMain/resources/MR/images/ic_arrow_circle_right.svg diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index adda443a3b..cac7c65232 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -316,7 +316,7 @@ func apiDeleteUser(_ userId: Int64, _ delSMPQueues: Bool, viewPwd: String?) asyn } func apiStartChat(ctrl: chat_ctrl? = nil) throws -> Bool { - let r: ChatResponse0 = try chatSendCmdSync(.startChat(mainApp: true, enableSndFiles: true, largeLinkData: false), ctrl: ctrl) + let r: ChatResponse0 = try chatSendCmdSync(.startChat(mainApp: true, enableSndFiles: true, largeLinkData: true), ctrl: ctrl) switch r { case .chatStarted: return true case .chatRunning: return false diff --git a/apps/ios/Shared/Views/Chat/ChatItemView.swift b/apps/ios/Shared/Views/Chat/ChatItemView.swift index 5f0ab5e329..5f48c18881 100644 --- a/apps/ios/Shared/Views/Chat/ChatItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItemView.swift @@ -172,6 +172,7 @@ struct ChatItemContentView: View { case let .rcvDirectE2EEInfo(e2eeInfo): CIEventView(eventText: directE2EEInfoText(e2eeInfo)) case .sndGroupE2EEInfo: CIEventView(eventText: e2eeInfoNoPQText()) case .rcvGroupE2EEInfo: CIEventView(eventText: e2eeInfoNoPQText()) + case .chatBanner: EmptyView() case let .invalidJSON(json): CIInvalidJSONView(json: json) } } diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index 1aca0ab5fc..4a4efb9adc 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -730,33 +730,48 @@ struct ChatView: View { case let .single(item, _, _): item.item case let .grouped(items, _, _, _, _, _, _, _): items.boxedValue.last!.item } - let voiceNoFrame = voiceWithoutFrame(ci) - let maxWidth = cInfo.chatType == .group - ? voiceNoFrame - ? (g.size.width - 28) - 42 - : (g.size.width - 28) * 0.84 - 42 - : voiceNoFrame - ? (g.size.width - 32) - : (g.size.width - 32) * 0.84 - return ChatItemWithMenu( - im: im, - chat: $chat, - index: index, - isLastItem: index == mergedItems.boxedValue.items.count - 1, - chatItem: ci, - scrollToItem: scrollToItem, - scrollToItemId: $scrollToItemId, - merged: mergedItem, - maxWidth: maxWidth, - composeState: $composeState, - selectedMember: $selectedMember, - showChatInfoSheet: $showChatInfoSheet, - revealedItems: $revealedItems, - selectedChatItems: $selectedChatItems, - forwardedChatItems: $forwardedChatItems, - searchText: $searchText, - closeKeyboardAndRun: closeKeyboardAndRun - ) + return Group { + if case .chatBanner = ci.content { + VStack { + ChatBannerView(chat: chat) + .padding(.bottom, 90) + .padding(.top, 8) + + let listItem = mergedItem.newest() + if let prevItem = listItem.prevItem { + DateSeparator(date: prevItem.meta.itemTs).padding(8) + } + } + } else { + let voiceNoFrame = voiceWithoutFrame(ci) + let maxWidth = cInfo.chatType == .group + ? voiceNoFrame + ? (g.size.width - 28) - 42 + : (g.size.width - 28) * 0.84 - 42 + : voiceNoFrame + ? (g.size.width - 32) + : (g.size.width - 32) * 0.84 + ChatItemWithMenu( + im: im, + chat: $chat, + index: index, + isLastItem: index == mergedItems.boxedValue.items.count - 1, + chatItem: ci, + scrollToItem: scrollToItem, + scrollToItemId: $scrollToItemId, + merged: mergedItem, + maxWidth: maxWidth, + composeState: $composeState, + selectedMember: $selectedMember, + showChatInfoSheet: $showChatInfoSheet, + revealedItems: $revealedItems, + selectedChatItems: $selectedChatItems, + forwardedChatItems: $forwardedChatItems, + searchText: $searchText, + closeKeyboardAndRun: closeKeyboardAndRun + ) + } + } // crashes on Cell size calculation without this line .environmentObject(ChatModel.shared) .environmentObject(theme) // crashes without this line when scrolling to the first unread in EndlessScrollVIew @@ -804,6 +819,138 @@ struct ChatView: View { } } + struct ChatBannerView: View { + @EnvironmentObject var theme: AppTheme + @AppStorage(DEFAULT_CHAT_ITEM_ROUNDNESS) private var roundness = defaultChatItemRoundness + @ObservedObject var chat: Chat + + var body: some View { + let v = VStack(spacing: 8) { + ChatInfoImage(chat: chat, size: alertProfileImageSize) + + Text(chat.chatInfo.displayName) + .font(.title3) + .multilineTextAlignment(.center) + .lineLimit(2) + .fixedSize(horizontal: false, vertical: true) + .frame(maxWidth: 240) + + let fullName = chat.chatInfo.fullName.trimmingCharacters(in: .whitespacesAndNewlines) + if fullName != "" && fullName != chat.chatInfo.displayName && fullName != chat.chatInfo.displayName.trimmingCharacters(in: .whitespacesAndNewlines) { + Text(chat.chatInfo.fullName) + .font(.subheadline) + .multilineTextAlignment(.center) + .lineLimit(3) + .fixedSize(horizontal: false, vertical: true) + .frame(maxWidth: 260) + } + + if let shortDescr = chat.chatInfo.shortDescr { + Text(shortDescr) + .font(.subheadline) + .multilineTextAlignment(.center) + .lineLimit(4) + .fixedSize(horizontal: false, vertical: true) + .padding(.horizontal) + } + + if let chatContext { + Text(chatContext) + .font(.callout) + .foregroundColor(theme.colors.secondary) + .padding(.top, 8) + } + } + .frame(maxWidth: .infinity) + .padding() + .background(theme.appColors.receivedMessage) + .clipShape(RoundedRectangle(cornerRadius: msgRectMaxRadius * roundness)) + if let (label, connLink) = chatAddress() { + v.contextMenu { + Button { + let shareItems: [Any] = [connLink] + showShareSheet(items: shareItems) + } label: { + Label(label, systemImage: "square.and.arrow.up") + } + } + .padding(.horizontal) + } else { + v.padding(.horizontal) + } + + } + + func chatAddress() -> (label: LocalizedStringKey, connLink: String)? { + switch chat.chatInfo { + case let .direct(contact): + if !contact.nextConnectPrepared && !contact.nextAcceptContactRequest { + let connLink: String? = if let pct = contact.preparedContact, case .con = pct.uiConnLinkType { + pct.connLinkToConnect.simplexChatUri() + } else { + contact.profile.contactLink + } + if let connLink { + return ("SimpleX address", connLink) + } + } + case let .group(groupInfo, _): + if !groupInfo.nextConnectPrepared { + if let pg = groupInfo.preparedGroup { + let connLink = pg.connLinkToConnect.simplexChatUri() + switch groupInfo.businessChat?.chatType { + case .none: return ("Group link", connLink) + case .business: return ("Business address", connLink) + default: () + } + } + } + default: () + } + return nil + } + + var chatContext: LocalizedStringKey? { + switch chat.chatInfo { + case let .direct(contact): + if contact.nextConnectPrepared, let linkType = contact.preparedContact?.uiConnLinkType { + switch linkType { + case .inv: + "Tap Connect to chat" + case .con: + "Tap Connect to send request" + } + } else if contact.nextAcceptContactRequest { + "Accept contact request" + } else { + "Your contact" + } + case let .group(groupInfo, _): + switch groupInfo.businessChat?.chatType { + case .none: + if groupInfo.nextConnectPrepared { + "Tap Join group" + } else { + switch (groupInfo.membership.memberStatus) { + case .memInvited: "Join group" + case .memCreator: "Your group" + default: "Group" + } + } + case .business: + if groupInfo.nextConnectPrepared { + "Tap Connect to chat" + } else { + "Business connection" + } + case .customer: + "Your business contact" + } + default: nil + } + } + } + private var connectingText: LocalizedStringKey? { switch (chat.chatInfo) { case let .direct(contact): @@ -879,7 +1026,7 @@ struct ChatView: View { var body: some View { ZStack(alignment: .top) { - if let date = model.date { + if let date = model.date, date.timeIntervalSince1970 > 0 { DateSeparator(date: date) .padding(.vertical, 4).padding(.horizontal, 8) .background(.thinMaterial) diff --git a/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift b/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift index c5d9ceda70..01c62ca34f 100644 --- a/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift @@ -364,7 +364,11 @@ struct ChatPreviewView: View { } case let .group(groupInfo, _): if groupInfo.nextConnectPrepared { - Text("Open to join") + if groupInfo.businessChat?.chatType == .business { + Text("Open to connect") + } else { + Text("Open to join") + } } else { switch (groupInfo.membership.memberStatus) { case .memRejected: Text("rejected") diff --git a/apps/ios/Shared/Views/Helpers/ChatItemClipShape.swift b/apps/ios/Shared/Views/Helpers/ChatItemClipShape.swift index 9aa6ac86cf..980308f13c 100644 --- a/apps/ios/Shared/Views/Helpers/ChatItemClipShape.swift +++ b/apps/ios/Shared/Views/Helpers/ChatItemClipShape.swift @@ -76,7 +76,7 @@ struct ChatTailPadding: ViewModifier { } } -private let msgRectMaxRadius: Double = 18 +let msgRectMaxRadius: Double = 18 private let msgBubbleMaxRadius: Double = msgRectMaxRadius * 1.2 private let msgTailWidth: Double = 9 private let msgTailMinHeight: Double = msgTailWidth * 1.254 // ~56deg diff --git a/apps/ios/Shared/Views/Helpers/ProfileImage.swift b/apps/ios/Shared/Views/Helpers/ProfileImage.swift index 3eedd56441..4cc244cb24 100644 --- a/apps/ios/Shared/Views/Helpers/ProfileImage.swift +++ b/apps/ios/Shared/Views/Helpers/ProfileImage.swift @@ -28,7 +28,11 @@ struct ProfileImage: View { .resizable() .foregroundColor(c) .frame(width: size, height: size) - .background(Circle().fill(backgroundColor != nil ? backgroundColor! : .clear)) + .background( + Circle() + .fill(backgroundColor != nil ? backgroundColor! : .clear) + .frame(width: size - 2, height: size - 2) // less than size of Image to avoid slightly visible border + ) } } } diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff index cc6ac9cb18..ffbe0fe134 100644 --- a/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff @@ -1221,6 +1221,14 @@ swipe action Подобрен интерфейс No comment provided by engineer. + + Bio + No comment provided by engineer. + + + Bio too large + alert title + Black Черна @@ -1311,6 +1319,10 @@ swipe action Бизнес чатове No comment provided by engineer. + + Business connection + No comment provided by engineer. + Businesses No comment provided by engineer. @@ -2524,6 +2536,10 @@ swipe action Описание No comment provided by engineer. + + Description too large + alert title + Desktop address Адрес на настолно устройство @@ -6758,10 +6774,6 @@ chat item action Sent reply No comment provided by engineer. - - Sent to your contact after connection. - No comment provided by engineer. - Sent total No comment provided by engineer. @@ -6990,6 +7002,10 @@ chat item action Сподели с контактите No comment provided by engineer. + + Short description + No comment provided by engineer. + Short link No comment provided by engineer. @@ -7367,10 +7383,22 @@ report reason Направи снимка No comment provided by engineer. + + Tap Connect to chat + No comment provided by engineer. + + + Tap Connect to send request + No comment provided by engineer. + Tap Create SimpleX address in the menu to create it later. No comment provided by engineer. + + Tap Join group + No comment provided by engineer. + Tap button Докосни бутона @@ -8685,6 +8713,10 @@ Repeat connection request? Вашият адрес в SimpleX No comment provided by engineer. + + Your business contact + No comment provided by engineer. + Your calls Вашите обаждания @@ -8717,6 +8749,10 @@ Repeat connection request? Your connection was moved to %@ but an error happened when switching profile. No comment provided by engineer. + + Your contact + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Вашият контакт изпрати файл, който е по-голям от поддържания в момента максимален размер (%@). @@ -8746,6 +8782,10 @@ Repeat connection request? Вашият текущ профил No comment provided by engineer. + + Your group + No comment provided by engineer. + Your preferences Вашите настройки diff --git a/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff b/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff index b9319d1318..550d952965 100644 --- a/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff @@ -1167,6 +1167,14 @@ swipe action Better user experience No comment provided by engineer. + + Bio + No comment provided by engineer. + + + Bio too large + alert title + Black No comment provided by engineer. @@ -1245,6 +1253,10 @@ swipe action Business chats No comment provided by engineer. + + Business connection + No comment provided by engineer. + Businesses No comment provided by engineer. @@ -2420,6 +2432,10 @@ swipe action Popis No comment provided by engineer. + + Description too large + alert title + Desktop address No comment provided by engineer. @@ -6533,10 +6549,6 @@ chat item action Sent reply No comment provided by engineer. - - Sent to your contact after connection. - No comment provided by engineer. - Sent total No comment provided by engineer. @@ -6761,6 +6773,10 @@ chat item action Sdílet s kontakty No comment provided by engineer. + + Short description + No comment provided by engineer. + Short link No comment provided by engineer. @@ -7130,10 +7146,22 @@ report reason Vyfotit No comment provided by engineer. + + Tap Connect to chat + No comment provided by engineer. + + + Tap Connect to send request + No comment provided by engineer. + Tap Create SimpleX address in the menu to create it later. No comment provided by engineer. + + Tap Join group + No comment provided by engineer. + Tap button Klepněte na tlačítko @@ -8393,6 +8421,10 @@ Repeat connection request? Vaše SimpleX adresa No comment provided by engineer. + + Your business contact + No comment provided by engineer. + Your calls Vaše hovory @@ -8425,6 +8457,10 @@ Repeat connection request? Your connection was moved to %@ but an error happened when switching profile. No comment provided by engineer. + + Your contact + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Kontakt odeslal soubor, který je větší než aktuálně podporovaná maximální velikost (%@). @@ -8454,6 +8490,10 @@ Repeat connection request? Váš současný profil No comment provided by engineer. + + Your group + No comment provided by engineer. + Your preferences Vaše preference diff --git a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff index 3d7e17a4fd..2186828c7c 100644 --- a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff +++ b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff @@ -1257,6 +1257,14 @@ swipe action Verbesserte Nutzer-Erfahrung No comment provided by engineer. + + Bio + No comment provided by engineer. + + + Bio too large + alert title + Black Schwarz @@ -1347,6 +1355,10 @@ swipe action Geschäftliche Chats No comment provided by engineer. + + Business connection + No comment provided by engineer. + Businesses Unternehmen @@ -2649,6 +2661,10 @@ swipe action Beschreibung No comment provided by engineer. + + Description too large + alert title + Desktop address Desktop-Adresse @@ -7183,11 +7199,6 @@ chat item action Gesendete Antwort No comment provided by engineer. - - Sent to your contact after connection. - Wird nach der Verbindung an Ihren Kontakt gesendet. - No comment provided by engineer. - Sent total Summe aller gesendeten Nachrichten @@ -7444,6 +7455,10 @@ chat item action Mit Kontakten teilen No comment provided by engineer. + + Short description + No comment provided by engineer. + Short link Verkürzter Link @@ -7852,11 +7867,23 @@ report reason Machen Sie ein Foto No comment provided by engineer. + + Tap Connect to chat + No comment provided by engineer. + + + Tap Connect to send request + No comment provided by engineer. + Tap Create SimpleX address in the menu to create it later. Tippen Sie im Menü auf SimpleX-Adresse erstellen, um sie später zu erstellen. No comment provided by engineer. + + Tap Join group + No comment provided by engineer. + Tap button Schaltfläche antippen @@ -9247,6 +9274,10 @@ Verbindungsanfrage wiederholen? Ihre SimpleX-Adresse No comment provided by engineer. + + Your business contact + No comment provided by engineer. + Your calls Anrufe @@ -9282,6 +9313,10 @@ Verbindungsanfrage wiederholen? Ihre Verbindung wurde auf %@ verschoben. Während Sie auf das Profil weitergeleitet wurden trat aber ein unerwarteter Fehler auf. No comment provided by engineer. + + Your contact + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Ihr Kontakt hat eine Datei gesendet, die größer ist als die derzeit unterstützte maximale Größe (%@). @@ -9312,6 +9347,10 @@ Verbindungsanfrage wiederholen? Mein aktuelles Chat-Profil No comment provided by engineer. + + Your group + No comment provided by engineer. + Your preferences Ihre Präferenzen diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff index 8b88f7b37c..22066e767b 100644 --- a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff +++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff @@ -1257,6 +1257,16 @@ swipe action Better user experience No comment provided by engineer. + + Bio + Bio + No comment provided by engineer. + + + Bio too large + Bio too large + alert title + Black Black @@ -1347,6 +1357,11 @@ swipe action Business chats No comment provided by engineer. + + Business connection + Business connection + No comment provided by engineer. + Businesses Businesses @@ -2649,6 +2664,11 @@ swipe action Description No comment provided by engineer. + + Description too large + Description too large + alert title + Desktop address Desktop address @@ -7183,11 +7203,6 @@ chat item action Sent reply No comment provided by engineer. - - Sent to your contact after connection. - Sent to your contact after connection. - No comment provided by engineer. - Sent total Sent total @@ -7444,6 +7459,11 @@ chat item action Share with contacts No comment provided by engineer. + + Short description + Short description + No comment provided by engineer. + Short link Short link @@ -7852,11 +7872,26 @@ report reason Take picture No comment provided by engineer. + + Tap Connect to chat + Tap Connect to chat + No comment provided by engineer. + + + Tap Connect to send request + Tap Connect to send request + No comment provided by engineer. + Tap Create SimpleX address in the menu to create it later. Tap Create SimpleX address in the menu to create it later. No comment provided by engineer. + + Tap Join group + Tap Join group + No comment provided by engineer. + Tap button Tap button @@ -9247,6 +9282,11 @@ Repeat connection request? Your SimpleX address No comment provided by engineer. + + Your business contact + Your business contact + No comment provided by engineer. + Your calls Your calls @@ -9282,6 +9322,11 @@ Repeat connection request? Your connection was moved to %@ but an error happened when switching profile. No comment provided by engineer. + + Your contact + Your contact + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Your contact sent a file that is larger than currently supported maximum size (%@). @@ -9312,6 +9357,11 @@ Repeat connection request? Your current profile No comment provided by engineer. + + Your group + Your group + No comment provided by engineer. + Your preferences Your preferences diff --git a/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff b/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff index 3003d116d0..96aeb50d17 100644 --- a/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff +++ b/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff @@ -1257,6 +1257,14 @@ swipe action Experiencia de usuario mejorada No comment provided by engineer. + + Bio + No comment provided by engineer. + + + Bio too large + alert title + Black Negro @@ -1347,6 +1355,10 @@ swipe action Chats empresariales No comment provided by engineer. + + Business connection + No comment provided by engineer. + Businesses Empresas @@ -2649,6 +2661,10 @@ swipe action Descripción No comment provided by engineer. + + Description too large + alert title + Desktop address Dirección ordenador @@ -7183,11 +7199,6 @@ chat item action Respuesta enviada No comment provided by engineer. - - Sent to your contact after connection. - Enviado a tu contacto tras la conexión. - No comment provided by engineer. - Sent total Total enviados @@ -7444,6 +7455,10 @@ chat item action Compartir con contactos No comment provided by engineer. + + Short description + No comment provided by engineer. + Short link Enlace corto @@ -7852,11 +7867,23 @@ report reason Tomar foto No comment provided by engineer. + + Tap Connect to chat + No comment provided by engineer. + + + Tap Connect to send request + No comment provided by engineer. + Tap Create SimpleX address in the menu to create it later. Pulsa Crear dirección SimpleX en el menú para crearla más tarde. No comment provided by engineer. + + Tap Join group + No comment provided by engineer. + Tap button Pulsa el botón @@ -9247,6 +9274,10 @@ Repeat connection request? Mi dirección SimpleX No comment provided by engineer. + + Your business contact + No comment provided by engineer. + Your calls Llamadas @@ -9282,6 +9313,10 @@ Repeat connection request? Tu conexión ha sido trasladada a %@ pero ha ocurrido un error inesperado al redirigirte al perfil. No comment provided by engineer. + + Your contact + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). El contacto ha enviado un archivo mayor al máximo admitido (%@). @@ -9312,6 +9347,10 @@ Repeat connection request? Tu perfil actual No comment provided by engineer. + + Your group + No comment provided by engineer. + Your preferences Mis preferencias diff --git a/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff b/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff index 0a35d00e70..e9b98da98d 100644 --- a/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff @@ -1147,6 +1147,14 @@ swipe action Better user experience No comment provided by engineer. + + Bio + No comment provided by engineer. + + + Bio too large + alert title + Black No comment provided by engineer. @@ -1224,6 +1232,10 @@ swipe action Business chats No comment provided by engineer. + + Business connection + No comment provided by engineer. + Businesses No comment provided by engineer. @@ -2399,6 +2411,10 @@ swipe action Kuvaus No comment provided by engineer. + + Description too large + alert title + Desktop address No comment provided by engineer. @@ -6506,10 +6522,6 @@ chat item action Sent reply No comment provided by engineer. - - Sent to your contact after connection. - No comment provided by engineer. - Sent total No comment provided by engineer. @@ -6734,6 +6746,10 @@ chat item action Jaa kontaktien kanssa No comment provided by engineer. + + Short description + No comment provided by engineer. + Short link No comment provided by engineer. @@ -7102,10 +7118,22 @@ report reason Ota kuva No comment provided by engineer. + + Tap Connect to chat + No comment provided by engineer. + + + Tap Connect to send request + No comment provided by engineer. + Tap Create SimpleX address in the menu to create it later. No comment provided by engineer. + + Tap Join group + No comment provided by engineer. + Tap button Napauta painiketta @@ -8364,6 +8392,10 @@ Repeat connection request? SimpleX-osoitteesi No comment provided by engineer. + + Your business contact + No comment provided by engineer. + Your calls Puhelusi @@ -8396,6 +8428,10 @@ Repeat connection request? Your connection was moved to %@ but an error happened when switching profile. No comment provided by engineer. + + Your contact + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Yhteyshenkilösi lähetti tiedoston, joka on suurempi kuin tällä hetkellä tuettu enimmäiskoko (%@). @@ -8425,6 +8461,10 @@ Repeat connection request? Nykyinen profiilisi No comment provided by engineer. + + Your group + No comment provided by engineer. + Your preferences Asetuksesi diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff index 10df7eaaa4..7b2dcadd10 100644 --- a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff @@ -1249,6 +1249,14 @@ swipe action Une meilleure expérience pour l'utilisateur No comment provided by engineer. + + Bio + No comment provided by engineer. + + + Bio too large + alert title + Black Noir @@ -1339,6 +1347,10 @@ swipe action Discussions professionnelles No comment provided by engineer. + + Business connection + No comment provided by engineer. + Businesses Entreprises @@ -2634,6 +2646,10 @@ swipe action Description No comment provided by engineer. + + Description too large + alert title + Desktop address Adresse de bureau @@ -7070,10 +7086,6 @@ chat item action Réponse envoyée No comment provided by engineer. - - Sent to your contact after connection. - No comment provided by engineer. - Sent total Total envoyé @@ -7325,6 +7337,10 @@ chat item action Partager avec vos contacts No comment provided by engineer. + + Short description + No comment provided by engineer. + Short link No comment provided by engineer. @@ -7727,11 +7743,23 @@ report reason Prendre une photo No comment provided by engineer. + + Tap Connect to chat + No comment provided by engineer. + + + Tap Connect to send request + No comment provided by engineer. + Tap Create SimpleX address in the menu to create it later. Appuyez sur Créer une adresse SimpleX dans le menu pour la créer ultérieurement. No comment provided by engineer. + + Tap Join group + No comment provided by engineer. + Tap button Appuyez sur le bouton @@ -9108,6 +9136,10 @@ Répéter la demande de connexion ? Votre adresse SimpleX No comment provided by engineer. + + Your business contact + No comment provided by engineer. + Your calls Vos appels @@ -9142,6 +9174,10 @@ Répéter la demande de connexion ? Votre connexion a été déplacée vers %@ mais une erreur inattendue s'est produite lors de la redirection vers le profil. No comment provided by engineer. + + Your contact + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Votre contact a envoyé un fichier plus grand que la taille maximale supportée actuellement(%@). @@ -9172,6 +9208,10 @@ Répéter la demande de connexion ? Votre profil actuel No comment provided by engineer. + + Your group + No comment provided by engineer. + Your preferences Vos préférences diff --git a/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff b/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff index b9bef25df5..d81628314b 100644 --- a/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff +++ b/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff @@ -1257,6 +1257,14 @@ swipe action Továbbfejlesztett felhasználói élmény No comment provided by engineer. + + Bio + No comment provided by engineer. + + + Bio too large + alert title + Black Fekete @@ -1347,6 +1355,10 @@ swipe action Üzleti csevegések No comment provided by engineer. + + Business connection + No comment provided by engineer. + Businesses Üzleti @@ -2649,6 +2661,10 @@ swipe action Leírás No comment provided by engineer. + + Description too large + alert title + Desktop address Számítógép címe @@ -7183,11 +7199,6 @@ chat item action Válaszüzenet-buborék színe No comment provided by engineer. - - Sent to your contact after connection. - Elküldés a partnernek a kapcsolódást követően. - No comment provided by engineer. - Sent total Összes elküldött üzenet @@ -7444,6 +7455,10 @@ chat item action Megosztás a partnerekkel No comment provided by engineer. + + Short description + No comment provided by engineer. + Short link Rövid hivatkozás @@ -7852,11 +7867,23 @@ report reason Kép készítése No comment provided by engineer. + + Tap Connect to chat + No comment provided by engineer. + + + Tap Connect to send request + No comment provided by engineer. + Tap Create SimpleX address in the menu to create it later. Koppintson a SimpleX-cím létrehozása menüpontra a későbbi létrehozáshoz. No comment provided by engineer. + + Tap Join group + No comment provided by engineer. + Tap button Koppintson a @@ -9247,6 +9274,10 @@ Repeat connection request? Profil SimpleX-címe No comment provided by engineer. + + Your business contact + No comment provided by engineer. + Your calls Hívások @@ -9282,6 +9313,10 @@ Repeat connection request? A kapcsolata át lett helyezve ide: %@, de egy váratlan hiba történt a profilra való átirányításkor. No comment provided by engineer. + + Your contact + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). A partnere a jelenleg megengedett maximális méretű (%@) fájlnál nagyobbat küldött. @@ -9312,6 +9347,10 @@ Repeat connection request? Jelenlegi profil No comment provided by engineer. + + Your group + No comment provided by engineer. + Your preferences Beállítások diff --git a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff index 471a9bf585..2b65368348 100644 --- a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff +++ b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff @@ -1257,6 +1257,14 @@ swipe action Esperienza utente migliorata No comment provided by engineer. + + Bio + No comment provided by engineer. + + + Bio too large + alert title + Black Nero @@ -1347,6 +1355,10 @@ swipe action Chat di lavoro No comment provided by engineer. + + Business connection + No comment provided by engineer. + Businesses Lavorative @@ -2649,6 +2661,10 @@ swipe action Descrizione No comment provided by engineer. + + Description too large + alert title + Desktop address Indirizzo desktop @@ -7183,11 +7199,6 @@ chat item action Risposta inviata No comment provided by engineer. - - Sent to your contact after connection. - Inviato al tuo contatto dopo la connessione. - No comment provided by engineer. - Sent total Totale inviato @@ -7444,6 +7455,10 @@ chat item action Condividi con i contatti No comment provided by engineer. + + Short description + No comment provided by engineer. + Short link Link breve @@ -7852,11 +7867,23 @@ report reason Scatta foto No comment provided by engineer. + + Tap Connect to chat + No comment provided by engineer. + + + Tap Connect to send request + No comment provided by engineer. + Tap Create SimpleX address in the menu to create it later. Tocca Crea indirizzo SimpleX nel menu per crearlo più tardi. No comment provided by engineer. + + Tap Join group + No comment provided by engineer. + Tap button Tocca il pulsante @@ -9247,6 +9274,10 @@ Ripetere la richiesta di connessione? Il tuo indirizzo SimpleX No comment provided by engineer. + + Your business contact + No comment provided by engineer. + Your calls Le tue chiamate @@ -9282,6 +9313,10 @@ Ripetere la richiesta di connessione? La tua connessione è stata spostata a %@, ma si è verificato un errore imprevisto durante il reindirizzamento al profilo. No comment provided by engineer. + + Your contact + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Il tuo contatto ha inviato un file più grande della dimensione massima attualmente supportata (%@). @@ -9312,6 +9347,10 @@ Ripetere la richiesta di connessione? Il tuo profilo attuale No comment provided by engineer. + + Your group + No comment provided by engineer. + Your preferences Le tue preferenze diff --git a/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff b/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff index 742375725c..b0413693f1 100644 --- a/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff @@ -1196,6 +1196,14 @@ swipe action Better user experience No comment provided by engineer. + + Bio + No comment provided by engineer. + + + Bio too large + alert title + Black No comment provided by engineer. @@ -1274,6 +1282,10 @@ swipe action Business chats No comment provided by engineer. + + Business connection + No comment provided by engineer. + Businesses No comment provided by engineer. @@ -2469,6 +2481,10 @@ swipe action 説明 No comment provided by engineer. + + Description too large + alert title + Desktop address No comment provided by engineer. @@ -6576,10 +6592,6 @@ chat item action Sent reply No comment provided by engineer. - - Sent to your contact after connection. - No comment provided by engineer. - Sent total No comment provided by engineer. @@ -6804,6 +6816,10 @@ chat item action 連絡先と共有する No comment provided by engineer. + + Short description + No comment provided by engineer. + Short link No comment provided by engineer. @@ -7173,10 +7189,22 @@ report reason 写真を撮影 No comment provided by engineer. + + Tap Connect to chat + No comment provided by engineer. + + + Tap Connect to send request + No comment provided by engineer. + Tap Create SimpleX address in the menu to create it later. No comment provided by engineer. + + Tap Join group + No comment provided by engineer. + Tap button ボタンをタップ @@ -8435,6 +8463,10 @@ Repeat connection request? あなたのSimpleXアドレス No comment provided by engineer. + + Your business contact + No comment provided by engineer. + Your calls あなたの通話 @@ -8467,6 +8499,10 @@ Repeat connection request? Your connection was moved to %@ but an error happened when switching profile. No comment provided by engineer. + + Your contact + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). 連絡先が現在サポートされている最大サイズ (%@) より大きいファイルを送信しました。 @@ -8496,6 +8532,10 @@ Repeat connection request? 現在のプロフィール No comment provided by engineer. + + Your group + No comment provided by engineer. + Your preferences あなたの設定 diff --git a/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff b/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff index f965b2fc50..c03f4727ae 100644 --- a/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff @@ -1254,6 +1254,14 @@ swipe action Betere gebruikerservaring No comment provided by engineer. + + Bio + No comment provided by engineer. + + + Bio too large + alert title + Black Zwart @@ -1344,6 +1352,10 @@ swipe action Zakelijke chats No comment provided by engineer. + + Business connection + No comment provided by engineer. + Businesses bedrijven @@ -2643,6 +2655,10 @@ swipe action Beschrijving No comment provided by engineer. + + Description too large + alert title + Desktop address Desktop adres @@ -7149,10 +7165,6 @@ chat item action Antwoord verzonden No comment provided by engineer. - - Sent to your contact after connection. - No comment provided by engineer. - Sent total Totaal verzonden @@ -7407,6 +7419,10 @@ chat item action Delen met contacten No comment provided by engineer. + + Short description + No comment provided by engineer. + Short link Korte link @@ -7814,11 +7830,23 @@ report reason Foto nemen No comment provided by engineer. + + Tap Connect to chat + No comment provided by engineer. + + + Tap Connect to send request + No comment provided by engineer. + Tap Create SimpleX address in the menu to create it later. Tik op SimpleX-adres maken in het menu om het later te maken. No comment provided by engineer. + + Tap Join group + No comment provided by engineer. + Tap button Tik op de knop @@ -9207,6 +9235,10 @@ Verbindingsverzoek herhalen? Uw SimpleX adres No comment provided by engineer. + + Your business contact + No comment provided by engineer. + Your calls Uw oproepen @@ -9241,6 +9273,10 @@ Verbindingsverzoek herhalen? Uw verbinding is verplaatst naar %@, maar er is een onverwachte fout opgetreden tijdens het omleiden naar het profiel. No comment provided by engineer. + + Your contact + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Uw contact heeft een bestand verzonden dat groter is dan de momenteel ondersteunde maximale grootte (%@). @@ -9271,6 +9307,10 @@ Verbindingsverzoek herhalen? Je huidige profiel No comment provided by engineer. + + Your group + No comment provided by engineer. + Your preferences Jouw voorkeuren diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff index 4b2f6e8f6b..25db19f709 100644 --- a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff +++ b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff @@ -1248,6 +1248,14 @@ swipe action Lepszy interfejs użytkownika No comment provided by engineer. + + Bio + No comment provided by engineer. + + + Bio too large + alert title + Black Czarny @@ -1338,6 +1346,10 @@ swipe action Czaty biznesowe No comment provided by engineer. + + Business connection + No comment provided by engineer. + Businesses Firmy @@ -2603,6 +2615,10 @@ swipe action Opis No comment provided by engineer. + + Description too large + alert title + Desktop address Adres komputera @@ -6975,10 +6991,6 @@ chat item action Wyślij odpowiedź No comment provided by engineer. - - Sent to your contact after connection. - No comment provided by engineer. - Sent total Wysłano łącznie @@ -7223,6 +7235,10 @@ chat item action Udostępnij kontaktom No comment provided by engineer. + + Short description + No comment provided by engineer. + Short link No comment provided by engineer. @@ -7617,10 +7633,22 @@ report reason Zrób zdjęcie No comment provided by engineer. + + Tap Connect to chat + No comment provided by engineer. + + + Tap Connect to send request + No comment provided by engineer. + Tap Create SimpleX address in the menu to create it later. No comment provided by engineer. + + Tap Join group + No comment provided by engineer. + Tap button Naciśnij przycisk @@ -8975,6 +9003,10 @@ Powtórzyć prośbę połączenia? Twój adres SimpleX No comment provided by engineer. + + Your business contact + No comment provided by engineer. + Your calls Twoje połączenia @@ -9009,6 +9041,10 @@ Powtórzyć prośbę połączenia? Twoje połączenie zostało przeniesione do %@, ale podczas przekierowywania do profilu wystąpił nieoczekiwany błąd. No comment provided by engineer. + + Your contact + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Twój kontakt wysłał plik, który jest większy niż obecnie obsługiwany maksymalny rozmiar (%@). @@ -9039,6 +9075,10 @@ Powtórzyć prośbę połączenia? Twój obecny profil No comment provided by engineer. + + Your group + No comment provided by engineer. + Your preferences Twoje preferencje diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff index 29e1b81936..0893462b45 100644 --- a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff @@ -1257,6 +1257,14 @@ swipe action Улучшенный интерфейс No comment provided by engineer. + + Bio + No comment provided by engineer. + + + Bio too large + alert title + Black Черная @@ -1347,6 +1355,10 @@ swipe action Бизнес разговоры No comment provided by engineer. + + Business connection + No comment provided by engineer. + Businesses Бизнесы @@ -2649,6 +2661,10 @@ swipe action Описание No comment provided by engineer. + + Description too large + alert title + Desktop address Адрес компьютера @@ -7181,11 +7197,6 @@ chat item action Отправленный ответ No comment provided by engineer. - - Sent to your contact after connection. - Отправляется Вашему контакту после соединения. - No comment provided by engineer. - Sent total Всего отправлено @@ -7442,6 +7453,10 @@ chat item action Поделиться с контактами No comment provided by engineer. + + Short description + No comment provided by engineer. + Short link Короткая ссылка @@ -7850,11 +7865,23 @@ report reason Сделать фото No comment provided by engineer. + + Tap Connect to chat + No comment provided by engineer. + + + Tap Connect to send request + No comment provided by engineer. + Tap Create SimpleX address in the menu to create it later. Нажмите Создать адрес SimpleX в меню, чтобы создать его позже. No comment provided by engineer. + + Tap Join group + No comment provided by engineer. + Tap button Нажмите кнопку @@ -9245,6 +9272,10 @@ Repeat connection request? Ваш адрес SimpleX No comment provided by engineer. + + Your business contact + No comment provided by engineer. + Your calls Ваши звонки @@ -9279,6 +9310,10 @@ Repeat connection request? Соединение было перемещено на %@, но при смене профиля произошла неожиданная ошибка. No comment provided by engineer. + + Your contact + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Ваш контакт отправил файл, размер которого превышает максимальный размер (%@). @@ -9309,6 +9344,10 @@ Repeat connection request? Ваш активный профиль No comment provided by engineer. + + Your group + No comment provided by engineer. + Your preferences Ваши предпочтения diff --git a/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff b/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff index 01f1f121f0..6f4d9a1b11 100644 --- a/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff +++ b/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff @@ -1139,6 +1139,14 @@ swipe action Better user experience No comment provided by engineer. + + Bio + No comment provided by engineer. + + + Bio too large + alert title + Black No comment provided by engineer. @@ -1216,6 +1224,10 @@ swipe action Business chats No comment provided by engineer. + + Business connection + No comment provided by engineer. + Businesses No comment provided by engineer. @@ -2387,6 +2399,10 @@ swipe action คำอธิบาย No comment provided by engineer. + + Description too large + alert title + Desktop address No comment provided by engineer. @@ -6481,10 +6497,6 @@ chat item action Sent reply No comment provided by engineer. - - Sent to your contact after connection. - No comment provided by engineer. - Sent total No comment provided by engineer. @@ -6709,6 +6721,10 @@ chat item action แชร์กับผู้ติดต่อ No comment provided by engineer. + + Short description + No comment provided by engineer. + Short link No comment provided by engineer. @@ -7075,10 +7091,22 @@ report reason ถ่ายภาพ No comment provided by engineer. + + Tap Connect to chat + No comment provided by engineer. + + + Tap Connect to send request + No comment provided by engineer. + Tap Create SimpleX address in the menu to create it later. No comment provided by engineer. + + Tap Join group + No comment provided by engineer. + Tap button แตะปุ่ม @@ -8333,6 +8361,10 @@ Repeat connection request? ที่อยู่ SimpleX ของคุณ No comment provided by engineer. + + Your business contact + No comment provided by engineer. + Your calls การโทรของคุณ @@ -8365,6 +8397,10 @@ Repeat connection request? Your connection was moved to %@ but an error happened when switching profile. No comment provided by engineer. + + Your contact + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). ผู้ติดต่อของคุณส่งไฟล์ที่ใหญ่กว่าขนาดสูงสุดที่รองรับในปัจจุบัน (%@) @@ -8394,6 +8430,10 @@ Repeat connection request? โปรไฟล์ปัจจุบันของคุณ No comment provided by engineer. + + Your group + No comment provided by engineer. + Your preferences การตั้งค่าของคุณ diff --git a/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff b/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff index b47d2ba290..0ab4fcd75c 100644 --- a/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff +++ b/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff @@ -1257,6 +1257,14 @@ swipe action Daha iyi kullanıcı deneyimi No comment provided by engineer. + + Bio + No comment provided by engineer. + + + Bio too large + alert title + Black Siyah @@ -1347,6 +1355,10 @@ swipe action İş konuşmaları No comment provided by engineer. + + Business connection + No comment provided by engineer. + Businesses İşletmeler @@ -2649,6 +2661,10 @@ swipe action Açıklama No comment provided by engineer. + + Description too large + alert title + Desktop address Bilgisayar adresi @@ -7183,11 +7199,6 @@ chat item action Gönderilen cevap No comment provided by engineer. - - Sent to your contact after connection. - Bağlantıdan sonra kişinize gönderildi. - No comment provided by engineer. - Sent total Gönderilen tüm mesajların toplamı @@ -7444,6 +7455,10 @@ chat item action Kişilerle paylaş No comment provided by engineer. + + Short description + No comment provided by engineer. + Short link Kısa bağlantı @@ -7852,11 +7867,23 @@ report reason Fotoğraf çek No comment provided by engineer. + + Tap Connect to chat + No comment provided by engineer. + + + Tap Connect to send request + No comment provided by engineer. + Tap Create SimpleX address in the menu to create it later. Daha sonra oluşturmak için menüden BasitX adresi oluştur'a dokunun. No comment provided by engineer. + + Tap Join group + No comment provided by engineer. + Tap button Tuşa bas @@ -9247,6 +9274,10 @@ Bağlantı isteği tekrarlansın mı? SimpleX adresin No comment provided by engineer. + + Your business contact + No comment provided by engineer. + Your calls Aramaların @@ -9282,6 +9313,10 @@ Bağlantı isteği tekrarlansın mı? Bağlantınız %@ adresine taşındı ancak sizi profile yönlendirirken beklenmedik bir hata oluştu. No comment provided by engineer. + + Your contact + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Kişiniz şu anda desteklenen maksimum boyuttan (%@) daha büyük bir dosya gönderdi. @@ -9312,6 +9347,10 @@ Bağlantı isteği tekrarlansın mı? Mevcut profiliniz No comment provided by engineer. + + Your group + No comment provided by engineer. + Your preferences Tercihleriniz diff --git a/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff b/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff index f7e5fd4aff..5ddd6d2d9b 100644 --- a/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff +++ b/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff @@ -1254,6 +1254,14 @@ swipe action Покращений користувацький досвід No comment provided by engineer. + + Bio + No comment provided by engineer. + + + Bio too large + alert title + Black Чорний @@ -1344,6 +1352,10 @@ swipe action Ділові чати No comment provided by engineer. + + Business connection + No comment provided by engineer. + Businesses Бізнеси @@ -2643,6 +2655,10 @@ swipe action Опис No comment provided by engineer. + + Description too large + alert title + Desktop address Адреса робочого столу @@ -7094,10 +7110,6 @@ chat item action Надіслано відповідь No comment provided by engineer. - - Sent to your contact after connection. - No comment provided by engineer. - Sent total Відправлено всього @@ -7349,6 +7361,10 @@ chat item action Поділіться з контактами No comment provided by engineer. + + Short description + No comment provided by engineer. + Short link No comment provided by engineer. @@ -7751,11 +7767,23 @@ report reason Сфотографуйте No comment provided by engineer. + + Tap Connect to chat + No comment provided by engineer. + + + Tap Connect to send request + No comment provided by engineer. + Tap Create SimpleX address in the menu to create it later. Натисніть «Створити адресу SimpleX» у меню, щоб створити її пізніше. No comment provided by engineer. + + Tap Join group + No comment provided by engineer. + Tap button Натисніть кнопку @@ -9132,6 +9160,10 @@ Repeat connection request? Ваша адреса SimpleX No comment provided by engineer. + + Your business contact + No comment provided by engineer. + Your calls Твої дзвінки @@ -9166,6 +9198,10 @@ Repeat connection request? Ваше з'єднання було переміщено на %@, але під час перенаправлення на профіль сталася несподівана помилка. No comment provided by engineer. + + Your contact + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Ваш контакт надіслав файл, розмір якого перевищує підтримуваний на цей момент максимальний розмір (%@). @@ -9196,6 +9232,10 @@ Repeat connection request? Ваш поточний профіль No comment provided by engineer. + + Your group + No comment provided by engineer. + Your preferences Ваші уподобання diff --git a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff index 6d0bb1c74a..e9a1904aa8 100644 --- a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff +++ b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff @@ -1251,6 +1251,14 @@ swipe action 更佳的使用体验 No comment provided by engineer. + + Bio + No comment provided by engineer. + + + Bio too large + alert title + Black 黑色 @@ -1341,6 +1349,10 @@ swipe action 企业聊天 No comment provided by engineer. + + Business connection + No comment provided by engineer. + Businesses 企业 @@ -2635,6 +2647,10 @@ swipe action 描述 No comment provided by engineer. + + Description too large + alert title + Desktop address 桌面地址 @@ -7099,10 +7115,6 @@ chat item action 已发送回复 No comment provided by engineer. - - Sent to your contact after connection. - No comment provided by engineer. - Sent total 发送总数 @@ -7344,6 +7356,10 @@ chat item action 与联系人分享 No comment provided by engineer. + + Short description + No comment provided by engineer. + Short link No comment provided by engineer. @@ -7742,10 +7758,22 @@ report reason 拍照 No comment provided by engineer. + + Tap Connect to chat + No comment provided by engineer. + + + Tap Connect to send request + No comment provided by engineer. + Tap Create SimpleX address in the menu to create it later. No comment provided by engineer. + + Tap Join group + No comment provided by engineer. + Tap button 点击按钮 @@ -9095,6 +9123,10 @@ Repeat connection request? 您的 SimpleX 地址 No comment provided by engineer. + + Your business contact + No comment provided by engineer. + Your calls 您的通话 @@ -9127,6 +9159,10 @@ Repeat connection request? Your connection was moved to %@ but an error happened when switching profile. No comment provided by engineer. + + Your contact + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). 您的联系人发送的文件大于当前支持的最大大小 (%@)。 @@ -9156,6 +9192,10 @@ Repeat connection request? 您当前的资料 No comment provided by engineer. + + Your group + No comment provided by engineer. + Your preferences 您的偏好设置 diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index b7a2aa2455..5107ddf45d 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -2929,6 +2929,7 @@ public struct ChatItem: Identifiable, Decodable, Hashable { case .rcvDirectE2EEInfo: return false case .sndGroupE2EEInfo: return false case .rcvGroupE2EEInfo: return false + case .chatBanner: return false case .invalidJSON: return false } } @@ -2996,6 +2997,7 @@ public struct ChatItem: Identifiable, Decodable, Hashable { case .rcvDirectE2EEInfo: return false case .sndGroupE2EEInfo: return false case .rcvGroupE2EEInfo: return false + case .chatBanner: return false default: return true } } @@ -3656,6 +3658,7 @@ public enum CIContent: Decodable, ItemContent, Hashable { case rcvDirectE2EEInfo(e2eeInfo: E2EEInfo) case sndGroupE2EEInfo(e2eeInfo: E2EEInfo) case rcvGroupE2EEInfo(e2eeInfo: E2EEInfo) + case chatBanner case invalidJSON(json: Data?) public var text: String { @@ -3691,6 +3694,7 @@ public enum CIContent: Decodable, ItemContent, Hashable { case let .rcvDirectE2EEInfo(e2eeInfo): return directE2EEInfoStr(e2eeInfo) case .sndGroupE2EEInfo: return e2eeInfoNoPQStr case .rcvGroupE2EEInfo: return e2eeInfoNoPQStr + case .chatBanner: return "" case .invalidJSON: return NSLocalizedString("invalid data", comment: "invalid chat item") } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 588ab17cd2..779e6ec88b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -2813,6 +2813,7 @@ data class ChatItem ( is CIContent.RcvDirectE2EEInfo -> false is CIContent.SndGroupE2EEInfo -> false is CIContent.RcvGroupE2EEInfo -> false + is CIContent.ChatBanner -> false else -> true } @@ -2879,6 +2880,7 @@ data class ChatItem ( is CIContent.RcvDirectE2EEInfo -> false is CIContent.SndGroupE2EEInfo -> false is CIContent.RcvGroupE2EEInfo -> false + is CIContent.ChatBanner -> false is CIContent.InvalidJSON -> false } @@ -3549,6 +3551,7 @@ sealed class CIContent: ItemContent { @Serializable @SerialName("rcvDirectE2EEInfo") class RcvDirectE2EEInfo(val e2eeInfo: E2EEInfo): CIContent() { override val msgContent: MsgContent? get() = null } @Serializable @SerialName("sndGroupE2EEInfo") class SndGroupE2EEInfo(val e2eeInfo: E2EEInfo): CIContent() { override val msgContent: MsgContent? get() = null } @Serializable @SerialName("rcvGroupE2EEInfo") class RcvGroupE2EEInfo(val e2eeInfo: E2EEInfo): CIContent() { override val msgContent: MsgContent? get() = null } + @Serializable @SerialName("chatBanner") object ChatBanner: CIContent() { override val msgContent: MsgContent? get() = null } @Serializable @SerialName("invalidJSON") data class InvalidJSON(val json: String): CIContent() { override val msgContent: MsgContent? get() = null } override val text: String get() = when (this) { @@ -3582,6 +3585,7 @@ sealed class CIContent: ItemContent { is RcvDirectE2EEInfo -> directE2EEInfoStr(e2eeInfo) is SndGroupE2EEInfo -> e2eeInfoNoPQStr is RcvGroupE2EEInfo -> e2eeInfoNoPQStr + is ChatBanner -> "" is InvalidJSON -> "invalid data" } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index ee41e9cbda..070416fbb1 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -883,7 +883,7 @@ object ChatController { } suspend fun apiStartChat(ctrl: ChatCtrl? = null): Boolean { - val r = sendCmd(null, CC.StartChat(mainApp = true, largeLinkData = false), ctrl) + val r = sendCmd(null, CC.StartChat(mainApp = true, largeLinkData = true), ctrl) when (r.result) { is CR.ChatStarted -> return true is CR.ChatRunning -> return false diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt index 5f00a592b8..074609c8f7 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt @@ -42,7 +42,9 @@ import chat.simplex.common.model.GroupInfo import chat.simplex.common.platform.* import chat.simplex.common.platform.AudioPlayer import chat.simplex.common.views.newchat.ContactConnectionInfoView +import chat.simplex.common.views.newchat.alertProfileImageSize import chat.simplex.res.MR +import dev.icerock.moko.resources.ImageResource import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import kotlinx.datetime.* @@ -1754,6 +1756,127 @@ fun BoxScope.ChatItemsList( ChatItemView(cItem, range, itemSeparation, previousItemSeparationLargeGap) } } + + @Composable + fun ChatBannerView() { + fun chatContext(): String? { + return when (chatInfo) { + is ChatInfo.Direct -> { + val contact = chatInfo.contact + val preparedLinkType = contact.preparedContact?.uiConnLinkType + if (contact.nextConnectPrepared && preparedLinkType != null) { + when (preparedLinkType) { + ConnectionMode.Inv -> generalGetString(MR.strings.chat_banner_connect_to_chat) + ConnectionMode.Con -> generalGetString(MR.strings.chat_banner_send_request_to_connect) + } + } else if (contact.nextAcceptContactRequest) { + generalGetString(MR.strings.chat_banner_accept_contact_request) + } else { + generalGetString(MR.strings.chat_banner_your_contact) + } + } + + is ChatInfo.Group -> { + val groupInfo = chatInfo.groupInfo + when (groupInfo.businessChat?.chatType) { + null -> { + if (groupInfo.nextConnectPrepared) { + generalGetString(MR.strings.chat_banner_join_group) + } else { + when (groupInfo.membership.memberStatus) { + GroupMemberStatus.MemInvited -> generalGetString(MR.strings.chat_banner_join_group) + GroupMemberStatus.MemCreator -> generalGetString(MR.strings.chat_banner_your_group) + else -> generalGetString(MR.strings.chat_banner_group) + } + } + } + + BusinessChatType.Business -> + if (groupInfo.nextConnectPrepared) { + generalGetString(MR.strings.chat_banner_connect_to_chat) + } else { + generalGetString(MR.strings.chat_banner_business_connection) + } + BusinessChatType.Customer -> + generalGetString(MR.strings.chat_banner_your_business_contact) + } + } + + else -> null + } + } + + Box( + Modifier + .clipChatItem() + .background(MaterialTheme.appColors.receivedMessage) + ) { + val bannerModifier = if (appPlatform.isDesktop) Modifier.width(400.dp) else Modifier.fillMaxWidth() + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = bannerModifier + .padding(horizontal = DEFAULT_PADDING) + .padding(bottom = DEFAULT_PADDING) + // ChatInfoImage has its own padding somewhere, + // also not doing verticalArrangement = Arrangement.spacedBy(DEFAULT_PADDING_HALF) because of it + .padding(top = DEFAULT_PADDING_HALF) + .background(MaterialTheme.appColors.receivedMessage) + ) { + ChatInfoImage(chatInfo, size = alertProfileImageSize, iconColor = MaterialTheme.colors.secondaryVariant.mixWith(MaterialTheme.colors.onBackground, 0.97f)) + Text( + chatInfo.displayName, + style = MaterialTheme.typography.h3, + color = MaterialTheme.colors.onBackground, + textAlign = TextAlign.Center, + maxLines = 2, + overflow = TextOverflow.Ellipsis, + modifier = Modifier + .widthIn(max = 240.dp) + ) + + val fullName = chatInfo.fullName.trim() + if (fullName.isNotEmpty() && fullName != chatInfo.displayName && fullName != chatInfo.displayName.trim()) { + Text( + fullName, + style = MaterialTheme.typography.h4, + color = MaterialTheme.colors.onBackground, + textAlign = TextAlign.Center, + maxLines = 3, + overflow = TextOverflow.Ellipsis, + modifier = Modifier + .widthIn(max = 260.dp) + .padding(top = DEFAULT_PADDING_HALF) + ) + } + + val descr = chatInfo.shortDescr?.trim() + if (descr != null && 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) + ) + } + + val contextStr = chatContext() + if (contextStr != null) { + Text( + contextStr, + style = MaterialTheme.typography.body2, + color = MaterialTheme.colors.secondary, + modifier = Modifier.padding(top = DEFAULT_PADDING) + ) + } + } + } + } + LazyColumnWithScrollBar( Modifier.align(Alignment.BottomCenter), state = listState.value, @@ -1768,42 +1891,62 @@ fun BoxScope.ChatItemsList( ) { val mergedItemsValue = mergedItems.value itemsIndexed(mergedItemsValue.items, key = { _, merged -> keyForItem(merged.newest().item) }) { index, merged -> - val isLastItem = index == mergedItemsValue.items.lastIndex - val last = if (isLastItem) reversedChatItems.value.lastOrNull() else null val listItem = merged.newest() val item = listItem.item - val range = if (merged is MergedItem.Grouped) { - merged.rangeInReversed.value - } else { - null - } - val showAvatar = shouldShowAvatar(item, merged.oldest().nextItem) - val isRevealed = remember { derivedStateOf { revealedItems.value.contains(item.id) } } - val itemSeparation: ItemSeparation - val prevItemSeparationLargeGap: Boolean - if (merged is MergedItem.Single || isRevealed.value) { - val prev = listItem.prevItem - itemSeparation = getItemSeparation(item, prev) - val nextForGap = if ((item.mergeCategory != null && item.mergeCategory == prev?.mergeCategory) || isLastItem) null else listItem.nextItem - prevItemSeparationLargeGap = if (nextForGap == null) false else getItemSeparationLargeGap(nextForGap, item) - } else { - itemSeparation = getItemSeparation(item, null) - prevItemSeparationLargeGap = false - } - ChatViewListItem(index == 0, rememberUpdatedState(range), showAvatar, item, itemSeparation, prevItemSeparationLargeGap, isRevealed) { - if (merged is MergedItem.Grouped) merged.reveal(it, revealedItems) - } - if (last != null) { - // no using separate item(){} block in order to have total number of items in LazyColumn match number of merged items - DateSeparator(last.meta.itemTs) - } - if (item.isRcvNew) { - val itemIds = when (merged) { - is MergedItem.Single -> listOf(merged.item.item.id) - is MergedItem.Grouped -> merged.items.map { it.item.id } + if (item.content is CIContent.ChatBanner) { + Column { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .fillMaxSize() + .padding(horizontal = DEFAULT_PADDING) + .padding(bottom = 90.dp, top = DEFAULT_PADDING) + ) { + ChatBannerView() + } + + val prevItem = listItem.prevItem + if (prevItem != null) { + DateSeparator(prevItem.meta.itemTs) + } + } + } else { + val isLastItem = index == mergedItemsValue.items.lastIndex + val last = if (isLastItem) reversedChatItems.value.lastOrNull() else null + val range = if (merged is MergedItem.Grouped) { + merged.rangeInReversed.value + } else { + null + } + val showAvatar = shouldShowAvatar(item, merged.oldest().nextItem) + val isRevealed = remember { derivedStateOf { revealedItems.value.contains(item.id) } } + val itemSeparation: ItemSeparation + val prevItemSeparationLargeGap: Boolean + if (merged is MergedItem.Single || isRevealed.value) { + val prev = listItem.prevItem + itemSeparation = getItemSeparation(item, prev) + val nextForGap = if ((item.mergeCategory != null && item.mergeCategory == prev?.mergeCategory) || isLastItem) null else listItem.nextItem + prevItemSeparationLargeGap = if (nextForGap == null) false else getItemSeparationLargeGap(nextForGap, item) + } else { + itemSeparation = getItemSeparation(item, null) + prevItemSeparationLargeGap = false + } + ChatViewListItem(index == 0, rememberUpdatedState(range), showAvatar, item, itemSeparation, prevItemSeparationLargeGap, isRevealed) { + if (merged is MergedItem.Grouped) merged.reveal(it, revealedItems) + } + + if (last != null) { + // no using separate item(){} block in order to have total number of items in LazyColumn match number of merged items + DateSeparator(last.meta.itemTs) + } + if (item.isRcvNew) { + val itemIds = when (merged) { + is MergedItem.Single -> listOf(merged.item.item.id) + is MergedItem.Grouped -> merged.items.map { it.item.id } + } + MarkItemsReadAfterDelay(keyForItem(item), itemIds, finishedInitialComposition, chatInfo.id, listState, markItemsRead) } - MarkItemsReadAfterDelay(keyForItem(item), itemIds, finishedInitialComposition, chatInfo.id, listState, markItemsRead) } } } @@ -2256,7 +2399,12 @@ private fun FloatingDate( if (listState.value.layoutInfo.visibleItemsInfo.lastIndex >= 0) { val lastVisibleChatItem = lastFullyVisibleIemInListState(topPaddingToContentPx, density, fontSizeSqrtMultiplier, mergedItems, listState) val timeZone = TimeZone.currentSystemDefault() - lastVisibleChatItem?.meta?.itemTs?.toLocalDateTime(timeZone)?.date?.atStartOfDayIn(timeZone) + val itemTs = lastVisibleChatItem?.meta?.itemTs + if (itemTs != null && itemTs.epochSeconds > 0) { + itemTs.toLocalDateTime(timeZone).date.atStartOfDayIn(timeZone) + } else { + null + } } else { null } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt index 740fc95871..0f9b3151fe 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ChatItemView.kt @@ -787,6 +787,7 @@ fun ChatItemView( is CIContent.RcvDirectE2EEInfo -> DirectE2EEInfoText(c.e2eeInfo) is CIContent.SndGroupE2EEInfo -> E2EEInfoNoPQText() is CIContent.RcvGroupE2EEInfo -> E2EEInfoNoPQText() + is CIContent.ChatBanner -> Spacer(modifier = Modifier.size(0.dp)) is CIContent.InvalidJSON -> { CIInvalidJSONView(c.json) DeleteItemMenu() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatPreviewView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatPreviewView.kt index fdf149e065..385a24cd50 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatPreviewView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatPreviewView.kt @@ -192,7 +192,10 @@ fun ChatPreviewView( is ChatInfo.Group -> if (cInfo.groupInfo.nextConnectPrepared) { - stringResource(MR.strings.group_preview_open_to_join) to Color.Unspecified + stringResource( + if (cInfo.groupInfo.businessChat?.chatType == BusinessChatType.Business) MR.strings.open_to_connect + else MR.strings.group_preview_open_to_join + ) to Color.Unspecified } else { when (cInfo.groupInfo.membership.memberStatus) { GroupMemberStatus.MemRejected -> stringResource(MR.strings.group_preview_rejected) to Color.Unspecified diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml index 0a6b2b539d..389f48b5d1 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -485,6 +485,17 @@ 1 chat with a member %d chat(s) + + Tap Connect to chat + Tap Connect to send request + Accept contact request + Your contact + Tap Join group + Your group + Group + Business connection + Your business contact + Share message… Share media… diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_arrow_circle_right.svg b/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_arrow_circle_right.svg new file mode 100644 index 0000000000..f5c27f5c9e --- /dev/null +++ b/apps/multiplatform/common/src/commonMain/resources/MR/images/ic_arrow_circle_right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Simplex/Chat/Library/Commands.hs b/src/Simplex/Chat/Library/Commands.hs index 328b59769f..d52820f8fa 100644 --- a/src/Simplex/Chat/Library/Commands.hs +++ b/src/Simplex/Chat/Library/Commands.hs @@ -1766,6 +1766,7 @@ processChatCommand vr nm = \case groupPreferences = maybe defaultBusinessGroupPrefs businessGroupPrefs preferences groupProfile = businessGroupProfile profile groupPreferences (gInfo, hostMember) <- withStore $ \db -> createPreparedGroup db vr user groupProfile True ccLink welcomeSharedMsgId + void $ createChatItem user (CDGroupSnd gInfo Nothing) False CIChatBanner Nothing (Just epochStart) let cd = CDGroupRcv gInfo Nothing hostMember createItem sharedMsgId content = createChatItem user cd True content sharedMsgId Nothing cInfo = GroupChat gInfo Nothing @@ -1777,7 +1778,9 @@ processChatCommand vr nm = \case pure $ CRNewPreparedChat user $ AChat SCTGroup chat ACCL _ (CCLink cReq _) -> do ct <- withStore $ \db -> createPreparedContact db user profile accLink welcomeSharedMsgId - let createItem sharedMsgId content = createChatItem user (CDDirectRcv ct) False content sharedMsgId Nothing + void $ createChatItem user (CDDirectSnd ct) False CIChatBanner Nothing (Just epochStart) + let cd = CDDirectRcv ct + createItem sharedMsgId content = createChatItem user cd False content sharedMsgId Nothing cInfo = DirectChat ct void $ createItem Nothing $ CIRcvDirectE2EEInfo $ E2EInfo $ connRequestPQEncryption cReq void $ createFeatureEnabledItems_ user ct @@ -1790,6 +1793,7 @@ processChatCommand vr nm = \case let GroupShortLinkData {groupProfile = gp@GroupProfile {description}} = groupSLinkData welcomeSharedMsgId <- forM description $ \_ -> getSharedMsgId (gInfo, hostMember) <- withStore $ \db -> createPreparedGroup db vr user gp False ccLink welcomeSharedMsgId + void $ createChatItem user (CDGroupSnd gInfo Nothing) False CIChatBanner Nothing (Just epochStart) let cd = CDGroupRcv gInfo Nothing hostMember cInfo = GroupChat gInfo Nothing void $ createGroupFeatureItems_ user cd True CIRcvGroupFeature gInfo @@ -2111,6 +2115,7 @@ processChatCommand vr nm = \case incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing gInfo <- withFastStore $ \db -> createNewGroup db vr gVar user gProfile incognitoProfile let cd = CDGroupSnd gInfo Nothing + createInternalChatItem user cd CIChatBanner (Just epochStart) createInternalChatItem user cd (CISndGroupE2EEInfo E2EInfo {pqEnabled = Just PQEncOff}) Nothing createGroupFeatureItems user cd CISndGroupFeature gInfo pure $ CRGroupCreated user gInfo @@ -2565,6 +2570,7 @@ processChatCommand vr nm = \case (connId, (CCLink cReq _, _serviceId)) <- withAgent $ \a -> createConnection a nm (aUserId user) True SCMInvitation Nothing Nothing IKPQOff subMode -- [incognito] reuse membership incognito profile ct <- withFastStore' $ \db -> createMemberContact db user connId cReq g m mConn subMode + void $ createChatItem user (CDDirectSnd ct) False CIChatBanner Nothing (Just epochStart) -- TODO not sure it is correct to set connections status here? lift $ setContactNetworkStatus ct NSConnected pure $ CRNewMemberContact user ct g m diff --git a/src/Simplex/Chat/Library/Internal.hs b/src/Simplex/Chat/Library/Internal.hs index 6d6cf33b96..b38c1ebbf3 100644 --- a/src/Simplex/Chat/Library/Internal.hs +++ b/src/Simplex/Chat/Library/Internal.hs @@ -48,7 +48,8 @@ import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) import Data.Time (addUTCTime) -import Data.Time.Clock (UTCTime, diffUTCTime, getCurrentTime, nominalDiffTimeToSeconds) +import Data.Time.Calendar (fromGregorian) +import Data.Time.Clock (UTCTime (..), diffUTCTime, getCurrentTime, nominalDiffTimeToSeconds, secondsToDiffTime) import Simplex.Chat.Call import Simplex.Chat.Controller import Simplex.Chat.Files @@ -2529,3 +2530,6 @@ timeItToView s action = do let diff = diffToMilliseconds $ diffUTCTime t2 t1 toView' $ CEvtTimedAction s diff pure a + +epochStart :: UTCTime +epochStart = UTCTime (fromGregorian 1970 1 1) (secondsToDiffTime 0) diff --git a/src/Simplex/Chat/Library/Subscriber.hs b/src/Simplex/Chat/Library/Subscriber.hs index 6a6f406a75..68791c5cae 100644 --- a/src/Simplex/Chat/Library/Subscriber.hs +++ b/src/Simplex/Chat/Library/Subscriber.hs @@ -577,6 +577,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = -- TODO [short links] get contact request by contactRequestId, check encryption (UserContactRequest.pqSupport)? when (directOrUsed ct') $ case (preparedContact ct', contactRequestId' ct') of (Nothing, Nothing) -> do + createInternalChatItem user (CDDirectSnd ct') CIChatBanner (Just epochStart) createE2EItem createFeatureEnabledItems user ct' (Just PreparedContact {connLinkToConnect = ACCL _ (CCLink cReq _)}, _) -> @@ -1337,6 +1338,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = -- they will be updated after connection is accepted. upsertDirectRequestItem cd (requestMsg_, prevSharedMsgId_) Nothing -> do + void $ createChatItem user (CDDirectSnd ct) False CIChatBanner Nothing (Just epochStart) let e2eContent = CIRcvDirectE2EEInfo $ E2EInfo $ Just $ CR.pqSupportToEnc $ reqPQSup void $ createChatItem user cd False e2eContent Nothing Nothing void $ createFeatureEnabledItems_ user ct @@ -1366,6 +1368,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = -- they will be updated after connection is accepted. upsertBusinessRequestItem cd (requestMsg_, prevSharedMsgId_) Nothing -> do + void $ createChatItem user (CDGroupSnd gInfo Nothing) False CIChatBanner Nothing (Just epochStart) -- TODO [short links] possibly, we can just keep them created where they are created on the business side due to auto-accept -- let e2eContent = CIRcvGroupE2EEInfo $ E2EInfo $ Just False -- no PQ encryption in groups -- void $ createChatItem user cd False e2eContent Nothing Nothing @@ -2249,6 +2252,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = when (fromMemId == memId) $ throwChatError CEGroupDuplicateMemberId -- [incognito] if direct connection with host is incognito, create membership using the same incognito profile (gInfo@GroupInfo {groupId, localDisplayName, groupProfile, membership}, hostId) <- withStore $ \db -> createGroupInvitation db vr user ct inv customUserProfileId + void $ createChatItem user (CDGroupSnd gInfo Nothing) False CIChatBanner Nothing (Just epochStart) let GroupMember {groupMemberId, memberId = membershipMemId} = membership if sameGroupLinkId groupLinkId groupLinkId' then do @@ -3089,6 +3093,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = dm <- encodeConnInfo $ XInfo p joinAgentConnectionAsync user True connReq dm subMode createItems mCt' m' = do + createInternalChatItem user (CDDirectSnd mCt') CIChatBanner (Just epochStart) (g', m'', scopeInfo) <- mkGroupChatScope g m' createInternalChatItem user (CDGroupRcv g' scopeInfo m'') (CIRcvGroupEvent RGEMemberCreatedContact) Nothing toView $ CEvtNewMemberContactReceivedInv user mCt' g' m'' diff --git a/src/Simplex/Chat/Messages/CIContent.hs b/src/Simplex/Chat/Messages/CIContent.hs index e0bff73e0c..a52133110a 100644 --- a/src/Simplex/Chat/Messages/CIContent.hs +++ b/src/Simplex/Chat/Messages/CIContent.hs @@ -165,6 +165,7 @@ data CIContent (d :: MsgDirection) where CIRcvDirectE2EEInfo :: E2EInfo -> CIContent 'MDRcv CISndGroupE2EEInfo :: E2EInfo -> CIContent 'MDSnd -- when new group is created CIRcvGroupE2EEInfo :: E2EInfo -> CIContent 'MDRcv -- when enabled with some member + CIChatBanner :: CIContent 'MDSnd CIInvalidJSON :: Text -> CIContent d -- this is also used for logical database errors, e.g. SEBadChatItem -- ^ This type is used both in API and in DB, so we use different JSON encodings for the database and for the API @@ -292,6 +293,7 @@ ciContentToText = \case CIRcvDirectE2EEInfo e2eeInfo -> directE2EInfoToText e2eeInfo CISndGroupE2EEInfo e2eeInfo -> groupE2EInfoToText e2eeInfo CIRcvGroupE2EEInfo e2eeInfo -> groupE2EInfoToText e2eeInfo + CIChatBanner -> "chat banner" CIInvalidJSON _ -> "invalid content JSON" directE2EInfoToText :: E2EInfo -> Text @@ -471,6 +473,7 @@ data JSONCIContent | JCIRcvDirectE2EEInfo {e2eeInfo :: E2EInfo} | JCISndGroupE2EEInfo {e2eeInfo :: E2EInfo} | JCIRcvGroupE2EEInfo {e2eeInfo :: E2EInfo} + | JCIChatBanner | JCIInvalidJSON {direction :: MsgDirection, json :: Text} jsonCIContent :: forall d. MsgDirectionI d => CIContent d -> JSONCIContent @@ -505,6 +508,7 @@ jsonCIContent = \case CIRcvDirectE2EEInfo e2eeInfo -> JCIRcvDirectE2EEInfo e2eeInfo CISndGroupE2EEInfo e2eeInfo -> JCISndGroupE2EEInfo e2eeInfo CIRcvGroupE2EEInfo e2eeInfo -> JCIRcvGroupE2EEInfo e2eeInfo + CIChatBanner -> JCIChatBanner CIInvalidJSON json -> JCIInvalidJSON (toMsgDirection $ msgDirection @d) json aciContentJSON :: JSONCIContent -> ACIContent @@ -539,6 +543,7 @@ aciContentJSON = \case JCIRcvDirectE2EEInfo {e2eeInfo} -> ACIContent SMDRcv $ CIRcvDirectE2EEInfo e2eeInfo JCISndGroupE2EEInfo {e2eeInfo} -> ACIContent SMDSnd $ CISndGroupE2EEInfo e2eeInfo JCIRcvGroupE2EEInfo {e2eeInfo} -> ACIContent SMDRcv $ CIRcvGroupE2EEInfo e2eeInfo + JCIChatBanner -> ACIContent SMDSnd CIChatBanner JCIInvalidJSON dir json -> case fromMsgDirection dir of AMsgDirection d -> ACIContent d $ CIInvalidJSON json @@ -574,6 +579,7 @@ data DBJSONCIContent | DBJCIRcvDirectE2EEInfo {e2eeInfo :: E2EInfo} | DBJCISndGroupE2EEInfo {e2eeInfo :: E2EInfo} | DBJCIRcvGroupE2EEInfo {e2eeInfo :: E2EInfo} + | DBJCIChatBanner | DBJCIInvalidJSON {direction :: MsgDirection, json :: Text} dbJsonCIContent :: forall d. MsgDirectionI d => CIContent d -> DBJSONCIContent @@ -608,6 +614,7 @@ dbJsonCIContent = \case CIRcvDirectE2EEInfo e2eeInfo -> DBJCIRcvDirectE2EEInfo e2eeInfo CISndGroupE2EEInfo e2eeInfo -> DBJCISndGroupE2EEInfo e2eeInfo CIRcvGroupE2EEInfo e2eeInfo -> DBJCIRcvGroupE2EEInfo e2eeInfo + CIChatBanner -> DBJCIChatBanner CIInvalidJSON json -> DBJCIInvalidJSON (toMsgDirection $ msgDirection @d) json aciContentDBJSON :: DBJSONCIContent -> ACIContent @@ -642,6 +649,7 @@ aciContentDBJSON = \case DBJCIRcvDirectE2EEInfo e2eeInfo -> ACIContent SMDRcv $ CIRcvDirectE2EEInfo e2eeInfo DBJCISndGroupE2EEInfo e2eeInfo -> ACIContent SMDSnd $ CISndGroupE2EEInfo e2eeInfo DBJCIRcvGroupE2EEInfo e2eeInfo -> ACIContent SMDRcv $ CIRcvGroupE2EEInfo e2eeInfo + DBJCIChatBanner -> ACIContent SMDSnd CIChatBanner DBJCIInvalidJSON dir json -> case fromMsgDirection dir of AMsgDirection d -> ACIContent d $ CIInvalidJSON json @@ -749,4 +757,5 @@ toCIContentTag ciContent = case ciContent of CIRcvDirectE2EEInfo _ -> "rcvDirectE2EEInfo" CISndGroupE2EEInfo _ -> "sndGroupE2EEInfo" CIRcvGroupE2EEInfo _ -> "rcvGroupE2EEInfo" + CIChatBanner -> "chatBanner" CIInvalidJSON _ -> "invalidJSON" diff --git a/src/Simplex/Chat/Store/Messages.hs b/src/Simplex/Chat/Store/Messages.hs index 1ba53ec6d6..7f82a8ee5f 100644 --- a/src/Simplex/Chat/Store/Messages.hs +++ b/src/Simplex/Chat/Store/Messages.hs @@ -191,7 +191,7 @@ deleteContactCIs db user@User {userId} ct@Contact {contactId} = do forM_ connIds $ \connId -> DB.execute db "DELETE FROM messages WHERE connection_id = ?" (Only connId) DB.execute db "DELETE FROM chat_item_reactions WHERE contact_id = ?" (Only contactId) - DB.execute db "DELETE FROM chat_items WHERE user_id = ? AND contact_id = ?" (userId, contactId) + DB.execute db "DELETE FROM chat_items WHERE user_id = ? AND contact_id = ? AND item_content_tag != 'chatBanner'" (userId, contactId) getContactConnIds_ :: DB.Connection -> User -> Contact -> IO [Int64] getContactConnIds_ db User {userId} Contact {contactId} = @@ -212,7 +212,7 @@ deleteGroupChatItemsMessages :: DB.Connection -> User -> GroupInfo -> IO () deleteGroupChatItemsMessages db User {userId} GroupInfo {groupId} = do DB.execute db "DELETE FROM messages WHERE group_id = ?" (Only groupId) DB.execute db "DELETE FROM chat_item_reactions WHERE group_id = ?" (Only groupId) - DB.execute db "DELETE FROM chat_items WHERE user_id = ? AND group_id = ?" (userId, groupId) + DB.execute db "DELETE FROM chat_items WHERE user_id = ? AND group_id = ? AND item_content_tag != 'chatBanner'" (userId, groupId) createNewSndMessage :: MsgEncodingI e => DB.Connection -> TVar ChaChaDRG -> ConnOrGroupId -> ChatMsgEvent e -> (SharedMsgId -> EncodedChatMessage) -> ExceptT StoreError IO SndMessage createNewSndMessage db gVar connOrGroupId chatMsgEvent encodeMessage = @@ -3416,7 +3416,7 @@ deleteContactExpiredCIs db user@User {userId} ct@Contact {contactId} expirationD forM_ connIds $ \connId -> DB.execute db "DELETE FROM messages WHERE connection_id = ? AND created_at <= ?" (connId, expirationDate) DB.execute db "DELETE FROM chat_item_reactions WHERE contact_id = ? AND created_at <= ?" (contactId, expirationDate) - DB.execute db "DELETE FROM chat_items WHERE user_id = ? AND contact_id = ? AND created_at <= ?" (userId, contactId, expirationDate) + DB.execute db "DELETE FROM chat_items WHERE user_id = ? AND contact_id = ? AND created_at <= ? AND item_content_tag != 'chatBanner'" (userId, contactId, expirationDate) getGroupExpiredFileInfo :: DB.Connection -> User -> GroupInfo -> UTCTime -> UTCTime -> IO [CIFileInfo] getGroupExpiredFileInfo db User {userId} GroupInfo {groupId} expirationDate createdAtCutoff = @@ -3430,7 +3430,7 @@ deleteGroupExpiredCIs :: DB.Connection -> User -> GroupInfo -> UTCTime -> UTCTim deleteGroupExpiredCIs db User {userId} GroupInfo {groupId} expirationDate createdAtCutoff = do DB.execute db "DELETE FROM messages WHERE group_id = ? AND created_at <= ?" (groupId, min expirationDate createdAtCutoff) DB.execute db "DELETE FROM chat_item_reactions WHERE group_id = ? AND reaction_ts <= ? AND created_at <= ?" (groupId, expirationDate, createdAtCutoff) - DB.execute db "DELETE FROM chat_items WHERE user_id = ? AND group_id = ? AND item_ts <= ? AND created_at <= ?" (userId, groupId, expirationDate, createdAtCutoff) + DB.execute db "DELETE FROM chat_items WHERE user_id = ? AND group_id = ? AND item_ts <= ? AND created_at <= ? AND item_content_tag != 'chatBanner'" (userId, groupId, expirationDate, createdAtCutoff) createCIModeration :: DB.Connection -> GroupInfo -> GroupMember -> MemberId -> SharedMsgId -> MessageId -> UTCTime -> IO () createCIModeration db GroupInfo {groupId} moderatorMember itemMemberId itemSharedMId msgId moderatedAtTs = diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/agent_query_plans.txt b/src/Simplex/Chat/Store/SQLite/Migrations/agent_query_plans.txt index ee717e71f5..501d69bb1d 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/agent_query_plans.txt +++ b/src/Simplex/Chat/Store/SQLite/Migrations/agent_query_plans.txt @@ -555,8 +555,9 @@ Query: INSERT INTO rcv_queues ( host, port, rcv_id, conn_id, rcv_private_key, rcv_dh_secret, e2e_priv_key, e2e_dh_secret, snd_id, queue_mode, status, rcv_queue_id, rcv_primary, replace_rcv_queue_id, smp_client_version, server_key_hash, - link_id, link_key, link_priv_sig_key, link_enc_fixed_data - ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?); + link_id, link_key, link_priv_sig_key, link_enc_fixed_data, + ntf_public_key, ntf_private_key, ntf_id, rcv_ntf_dh_secret + ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?); Plan: @@ -951,6 +952,10 @@ Plan: Query: INSERT INTO xftp_servers (xftp_host, xftp_port, xftp_key_hash) VALUES (?,?,?) Plan: +Query: SELECT 1 FROM connections WHERE conn_id = ? AND deleted_at_wait_delivery < ? LIMIT 1 +Plan: +SEARCH connections USING PRIMARY KEY (conn_id=?) + Query: SELECT 1 FROM encrypted_rcv_message_hashes WHERE conn_id = ? AND hash = ? LIMIT 1 Plan: SEARCH encrypted_rcv_message_hashes USING COVERING INDEX idx_encrypted_rcv_message_hashes_hash (conn_id=? AND hash=?) diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt b/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt index 7ff8ce0ece..339d05b325 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt +++ b/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt @@ -5358,9 +5358,21 @@ SEARCH chat_items USING COVERING INDEX idx_chat_items_fwd_from_chat_item_id (fwd SEARCH files USING COVERING INDEX idx_files_chat_item_id (chat_item_id=?) SEARCH groups USING COVERING INDEX idx_groups_chat_item_id (chat_item_id=?) -Query: DELETE FROM chat_items WHERE user_id = ? AND contact_id = ? AND created_at <= ? +Query: DELETE FROM chat_items WHERE user_id = ? AND contact_id = ? AND created_at <= ? AND item_content_tag != 'chatBanner' Plan: -SEARCH chat_items USING COVERING INDEX idx_chat_items_contacts_created_at (user_id=? AND contact_id=? AND created_at ("/_read chat @2", id, "ok") alice #$> ("/read user", id, "ok") alice #$> ("/_read user 1", id, "ok") - features = - if pqExpected - then chatFeatures - else (0, e2eeInfoNoPQStr) : tail chatFeatures + features = if pqExpected then chatFeatures else chatFeaturesNoPQ testRetryConnecting :: HasCallStack => TestParams -> IO () testRetryConnecting ps = testChatCfgOpts2 cfg' opts' aliceProfile bobProfile test ps @@ -557,9 +554,9 @@ testContactClear = alice <##> bob threadDelay 500000 alice #$> ("/clear bob", id, "bob: all messages are removed locally ONLY") - alice #$> ("/_get chat @2 count=100", chat, []) + alice #$> ("/_get chat @2 count=100", chat, [(1,"chat banner")]) bob #$> ("/clear alice", id, "alice: all messages are removed locally ONLY") - bob #$> ("/_get chat @2 count=100", chat, []) + bob #$> ("/_get chat @2 count=100", chat, [(1,"chat banner")]) testDeleteContactDeletesProfile :: HasCallStack => TestParams -> IO () testDeleteContactDeletesProfile = @@ -611,7 +608,7 @@ testDeleteConversationKeepContact = alice ##> "/_delete @2 messages" alice <## "bob: contact is deleted" - alice @@@ [("@bob", "")] -- UI would filter + alice @@@ [("@bob", "chat banner")] -- UI would filter bob @@@ [("@alice", "hey")] bob #> "@alice hi" alice <# "bob> hi" @@ -916,13 +913,13 @@ testDirectLiveMessage = connectUsers alice bob -- non-empty live message is sent instantly alice `send` "/live @bob hello" - bob <# "alice> [LIVE started] use /show [on/off/7] hello" + bob <# "alice> [LIVE started] use /show [on/off/8] hello" alice ##> ("/_update item @2 " <> itemId 1 <> " text hello there") alice <# "@bob [LIVE] hello there" bob <# "alice> [LIVE ended] hello there" -- empty live message is also sent instantly alice `send` "/live @bob" - bob <# "alice> [LIVE started] use /show [on/off/8]" + bob <# "alice> [LIVE started] use /show [on/off/9]" alice ##> ("/_update item @2 " <> itemId 2 <> " text hello 2") alice <# "@bob [LIVE] hello 2" bob <# "alice> [LIVE ended] hello 2" @@ -2002,7 +1999,7 @@ testUsersDifferentCIExpirationTTL ps = do -- first user messages alice ##> "/user alice" showActiveUser alice "alice (Alice)" - alice #$> ("/_get chat @2 count=100", chat, []) + alice #$> ("/_get chat @2 count=100", chat, [(1,"chat banner")]) -- second user messages alice ##> "/user alisa" @@ -2011,7 +2008,7 @@ testUsersDifferentCIExpirationTTL ps = do threadDelay 2000000 - alice #$> ("/_get chat @6 count=100", chat, []) + alice #$> ("/_get chat @6 count=100", chat, [(1,"chat banner")]) where cfg = testCfg {initialCleanupManagerDelay = 0, cleanupManagerStepDelay = 0, ciExpirationInterval = 500000} @@ -2085,7 +2082,7 @@ testUsersRestartCIExpiration ps = do -- first user messages alice ##> "/user alice" showActiveUser alice "alice (Alice)" - alice #$> ("/_get chat @2 count=100", chat, []) + alice #$> ("/_get chat @2 count=100", chat, [(1,"chat banner")]) -- second user messages alice ##> "/user alisa" @@ -2094,7 +2091,7 @@ testUsersRestartCIExpiration ps = do threadDelay 4000000 - alice #$> ("/_get chat @6 count=100", chat, []) + alice #$> ("/_get chat @6 count=100", chat, [(1,"chat banner")]) where cfg = testCfg {initialCleanupManagerDelay = 0, cleanupManagerStepDelay = 0, ciExpirationInterval = 500000} @@ -2143,7 +2140,7 @@ testEnableCIExpirationOnlyForOneUser ps = do -- messages are deleted for first user alice ##> "/user alice" showActiveUser alice "alice (Alice)" - alice #$> ("/_get chat @2 count=100", chat, []) + alice #$> ("/_get chat @2 count=100", chat, [(1,"chat banner")]) -- messages are not deleted for second user alice ##> "/user alisa" @@ -2204,7 +2201,7 @@ testDisableCIExpirationOnlyForOneUser ps = do threadDelay 2000000 -- second user messages are deleted - alice #$> ("/_get chat @6 count=100", chat, []) + alice #$> ("/_get chat @6 count=100", chat, [(1,"chat banner")]) withTestChatCfg ps cfg "alice" $ \alice -> do alice <## "1 contacts connected (use /cs for the list)" @@ -2218,12 +2215,12 @@ testDisableCIExpirationOnlyForOneUser ps = do bob #> "@alisa alisa 4" alice <# "bob> alisa 4" - alice #$> ("/_get chat @6 count=100", chat, [(1, "alisa 3"), (0, "alisa 4")]) + alice #$> ("/_get chat @6 count=100", chat, [(1,"chat banner"), (1, "alisa 3"), (0, "alisa 4")]) threadDelay 2000000 -- second user messages are deleted - alice #$> ("/_get chat @6 count=100", chat, []) + alice #$> ("/_get chat @6 count=100", chat, [(1,"chat banner")]) where cfg = testCfg {initialCleanupManagerDelay = 0, cleanupManagerStepDelay = 0, ciExpirationInterval = 500000} @@ -2263,11 +2260,11 @@ testUsersTimedMessages ps = do alice ##> "/user alice" showActiveUser alice "alice (Alice)" - alice #$> ("/_get chat @2 count=100", chat, [(1, "alice 1"), (0, "alice 2")]) + alice #$> ("/_get chat @2 count=100", chat, [(1,"chat banner"), (1, "alice 1"), (0, "alice 2")]) alice ##> "/user alisa" showActiveUser alice "alisa" - alice #$> ("/_get chat @6 count=100", chat, [(1, "alisa 1"), (0, "alisa 2")]) + alice #$> ("/_get chat @6 count=100", chat, [(1,"chat banner"), (1, "alisa 1"), (0, "alisa 2")]) threadDelay 1000000 @@ -2278,11 +2275,11 @@ testUsersTimedMessages ps = do alice ##> "/user alice" showActiveUser alice "alice (Alice)" - alice #$> ("/_get chat @2 count=100", chat, []) + alice #$> ("/_get chat @2 count=100", chat, [(1,"chat banner")]) alice ##> "/user alisa" showActiveUser alice "alisa" - alice #$> ("/_get chat @6 count=100", chat, [(1, "alisa 1"), (0, "alisa 2")]) + alice #$> ("/_get chat @6 count=100", chat, [(1,"chat banner"), (1, "alisa 1"), (0, "alisa 2")]) threadDelay 1000000 @@ -2293,7 +2290,7 @@ testUsersTimedMessages ps = do alice ##> "/user" showActiveUser alice "alisa" - alice #$> ("/_get chat @6 count=100", chat, []) + alice #$> ("/_get chat @6 count=100", chat, [(1,"chat banner")]) -- first user messages alice ##> "/user alice" @@ -2319,11 +2316,11 @@ testUsersTimedMessages ps = do alice ##> "/user alice" showActiveUser alice "alice (Alice)" - alice #$> ("/_get chat @2 count=100", chat, [(1, "alice 3"), (0, "alice 4")]) + alice #$> ("/_get chat @2 count=100", chat, [(1,"chat banner"), (1, "alice 3"), (0, "alice 4")]) alice ##> "/user alisa" showActiveUser alice "alisa" - alice #$> ("/_get chat @6 count=100", chat, [(1, "alisa 3"), (0, "alisa 4")]) + alice #$> ("/_get chat @6 count=100", chat, [(1,"chat banner"), (1, "alisa 3"), (0, "alisa 4")]) -- messages are deleted after restart threadDelay 1000000 @@ -2335,11 +2332,11 @@ testUsersTimedMessages ps = do alice ##> "/user alice" showActiveUser alice "alice (Alice)" - alice #$> ("/_get chat @2 count=100", chat, []) + alice #$> ("/_get chat @2 count=100", chat, [(1,"chat banner")]) alice ##> "/user alisa" showActiveUser alice "alisa" - alice #$> ("/_get chat @6 count=100", chat, [(1, "alisa 3"), (0, "alisa 4")]) + alice #$> ("/_get chat @6 count=100", chat, [(1,"chat banner"), (1, "alisa 3"), (0, "alisa 4")]) threadDelay 1000000 @@ -2350,7 +2347,7 @@ testUsersTimedMessages ps = do alice ##> "/user" showActiveUser alice "alisa" - alice #$> ("/_get chat @6 count=100", chat, []) + alice #$> ("/_get chat @6 count=100", chat, [(1,"chat banner")]) where configureTimedMessages :: HasCallStack => TestCC -> TestCC -> String -> String -> IO () configureTimedMessages alice bob bobId ttl = do @@ -2406,20 +2403,21 @@ testUserPrivacy = alice <## "alice (Alice)" alice <## "alisa (active, hidden, muted, unread: 1)" -- hidden message is saved - alice ##> "/tail" + alice ##> "/tail 11" alice <##? chatHistory - alice ##> "/_get items count=10" + alice ##> "/_get items count=11" alice <##? chatHistory - alice ##> "/_get items before=13 count=10" + alice ##> "/_get items before=15 count=10" alice - <##? [ ConsoleString ("bob> " <> e2eeInfoPQStr), + <##? [ "@bob chat banner", + ConsoleString ("bob> " <> e2eeInfoPQStr), "bob> Disappearing messages: allowed", "bob> Full deletion: off", "bob> Message reactions: enabled", "bob> Voice messages: enabled", "bob> Audio/video calls: enabled" ] - alice ##> "/_get items around=11 count=2" + alice ##> "/_get items around=13 count=2" alice <##? [ "bob> Full deletion: off", "bob> Message reactions: enabled", @@ -2427,7 +2425,7 @@ testUserPrivacy = "bob> Audio/video calls: enabled", "@bob hello" ] - alice ##> "/_get items after=12 count=10" + alice ##> "/_get items after=14 count=10" alice <##? [ "@bob hello", "bob> hey", @@ -2491,7 +2489,8 @@ testUserPrivacy = alice <## "messages are shown" alice <## "profile is visible" chatHistory = - [ ConsoleString ("bob> " <> e2eeInfoPQStr), + [ "@bob chat banner", + ConsoleString ("bob> " <> e2eeInfoPQStr), "bob> Disappearing messages: allowed", "bob> Full deletion: off", "bob> Message reactions: enabled", @@ -2529,7 +2528,7 @@ testSetChatItemTTL = alice #$> ("/_get chat @2 count=100", chatF, chatFeaturesF <> [((1, "1"), Nothing), ((0, "2"), Nothing), ((1, ""), Just "test.jpg"), ((1, "3"), Nothing), ((0, "4"), Nothing)]) checkActionDeletesFile "./tests/tmp/app_files/test.jpg" $ alice #$> ("/_ttl 1 2", id, "ok") - alice #$> ("/_get chat @2 count=100", chat, [(1, "3"), (0, "4")]) -- when expiration is turned on, first cycle is synchronous + alice #$> ("/_get chat @2 count=100", chat, [(1, "chat banner"), (1, "3"), (0, "4")]) -- when expiration is turned on, first cycle is synchronous bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "1"), (1, "2"), (0, ""), (0, "3"), (1, "4")]) alice #$> ("/_ttl 1", id, "old messages are set to be deleted after: 2 second(s)") alice #$> ("/ttl week", id, "ok") @@ -2563,7 +2562,7 @@ testSetDirectChatTTL = alice #$> ("/_get chat @2 count=100", chatF, chatFeaturesF <> [((1, "1"), Nothing), ((0, "2"), Nothing), ((1, "3"), Nothing), ((0, "4"), Nothing)]) alice #$> ("/_ttl 1 2", id, "ok") -- when expiration is turned on, first cycle is synchronous - alice #$> ("/_get chat @2 count=100", chat, [(1, "3"), (0, "4")]) + alice #$> ("/_get chat @2 count=100", chat, [(1, "chat banner"), (1, "3"), (0, "4")]) -- chat @3 doesn't expire since it was set to not expire alice #$> ("/_get chat @3 count=100", chat, chatFeatures <> [(1, "10"), (0, "11")]) @@ -2576,14 +2575,14 @@ testSetDirectChatTTL = bob #> "@alice 6" alice <# "bob> 6" alice #$> ("/_get chat @3 count=100", chat, chatFeatures <> [(1, "10"), (0, "11")]) - alice #$> ("/_get chat @2 count=100", chat, [(1, "3"), (0, "4"), (1, "5"), (0, "6")]) + alice #$> ("/_get chat @2 count=100", chat, [(1, "chat banner"), (1, "3"), (0, "4"), (1, "5"), (0, "6")]) -- set ttl for chat @3, only chat @3 is affected since global ttl is disabled alice #$> ("/_ttl 1 @3 1", id, "ok") alice #$> ("/ttl @cath", id, "old messages are set to be deleted after: 1 second(s)") threadDelay 3000000 - alice #$> ("/_get chat @3 count=100", chat, []) - alice #$> ("/_get chat @2 count=100", chat, [(1, "3"), (0, "4"), (1, "5"), (0, "6")]) + alice #$> ("/_get chat @3 count=100", chat, [(1, "chat banner")]) + alice #$> ("/_get chat @2 count=100", chat, [(1, "chat banner"), (1, "3"), (0, "4"), (1, "5"), (0, "6")]) bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "1"), (1, "2"), (0, "3"), (1, "4"), (0, "5"), (1, "6")]) -- set ttl to never expire again @@ -2593,16 +2592,16 @@ testSetDirectChatTTL = cath #> "@alice 13" alice <# "cath> 13" threadDelay 3000000 - alice #$> ("/_get chat @3 count=100", chat, [(1, "12"), (0, "13")]) - alice #$> ("/_get chat @2 count=100", chat, [(1, "3"), (0, "4"), (1, "5"), (0, "6")]) + alice #$> ("/_get chat @3 count=100", chat, [(1, "chat banner"), (1, "12"), (0, "13")]) + alice #$> ("/_get chat @2 count=100", chat, [(1, "chat banner"), (1, "3"), (0, "4"), (1, "5"), (0, "6")]) bob #$> ("/_get chat @2 count=100", chat, chatFeatures <> [(0, "1"), (1, "2"), (0, "3"), (1, "4"), (0, "5"), (1, "6")]) -- set ttl back to default alice #$> ("/ttl @cath default", id, "ok") alice #$> ("/ttl @cath", id, "old messages are set to delete according to default user config") alice #$> ("/_ttl 1 2", id, "ok") - alice #$> ("/_get chat @3 count=100", chat, []) - alice #$> ("/_get chat @2 count=100", chat, []) + alice #$> ("/_get chat @3 count=100", chat, [(1, "chat banner")]) + alice #$> ("/_get chat @2 count=100", chat, [(1, "chat banner")]) alice #$> ("/ttl @cath day", id, "ok") alice #$> ("/ttl @cath", id, "old messages are set to be deleted after: one day") diff --git a/tests/ChatTests/Groups.hs b/tests/ChatTests/Groups.hs index a3e298219d..1aba3a77c6 100644 --- a/tests/ChatTests/Groups.hs +++ b/tests/ChatTests/Groups.hs @@ -345,11 +345,11 @@ testGroupShared alice bob cath checkMessages = do -- test clearing chat threadDelay 1000000 alice #$> ("/clear #team", id, "#team: all messages are removed locally ONLY") - alice #$> ("/_get chat #1 count=100", chat, []) + alice #$> ("/_get chat #1 count=100", chat, [(1,"chat banner")]) bob #$> ("/clear #team", id, "#team: all messages are removed locally ONLY") - bob #$> ("/_get chat #1 count=100", chat, []) + bob #$> ("/_get chat #1 count=100", chat, [(1,"chat banner")]) cath #$> ("/clear #team", id, "#team: all messages are removed locally ONLY") - cath #$> ("/_get chat #1 count=100", chat, []) + cath #$> ("/_get chat #1 count=100", chat, [(1,"chat banner")]) where getReadChats :: HasCallStack => String -> String -> IO () getReadChats msgItem1 msgItem2 = do @@ -588,7 +588,7 @@ testGroup2 = ] dan <##> alice -- show last messages - alice ##> "/t #club 19" + alice ##> "/t #club 20" alice -- these strings are expected in any order because of sorting by time and rounding of time for sent <##? ( map (ConsoleString . ("#club " <> )) groupFeatureStrs diff --git a/tests/ChatTests/Profiles.hs b/tests/ChatTests/Profiles.hs index c59f06c473..028992d176 100644 --- a/tests/ChatTests/Profiles.hs +++ b/tests/ChatTests/Profiles.hs @@ -2220,7 +2220,7 @@ testSetContactPrefs = testChat2 aliceProfile bobProfile $ alice ##> "/_set prefs @2 {}" alice <## "your preferences for bob did not change" (bob ("/_get chat @2 count=100", chat, startFeatures) bob #$> ("/_get chat @2 count=100", chat, startFeatures) let sendVoice = "/_send @2 json [{\"filePath\": \"test.txt\", \"msgContent\": {\"type\": \"voice\", \"text\": \"\", \"duration\": 10}}]" diff --git a/tests/ChatTests/Utils.hs b/tests/ChatTests/Utils.hs index 72c0205883..179aad65bb 100644 --- a/tests/ChatTests/Utils.hs +++ b/tests/ChatTests/Utils.hs @@ -271,6 +271,11 @@ chat'' = read chatFeatures :: [(Int, String)] chatFeatures = map (\(a, _, _) -> a) chatFeatures'' +chatFeaturesNoPQ :: [(Int, String)] +chatFeaturesNoPQ = + map (\(a, _, _) -> a) $ + ((1, "chat banner"), Nothing, Nothing) : ((0, e2eeInfoNoPQStr), Nothing, Nothing) : chatFeatures_ + chatFeatures' :: [((Int, String), Maybe (Int, String))] chatFeatures' = map (\(a, b, _) -> (a, b)) chatFeatures'' @@ -278,9 +283,11 @@ chatFeaturesF :: [((Int, String), Maybe String)] chatFeaturesF = map (\(a, _, c) -> (a, c)) chatFeatures'' chatFeatures'' :: [((Int, String), Maybe (Int, String), Maybe String)] -chatFeatures'' = - [ ((0, e2eeInfoPQStr), Nothing, Nothing), - ((0, "Disappearing messages: allowed"), Nothing, Nothing), +chatFeatures'' = ((1, "chat banner"), Nothing, Nothing) : ((0, e2eeInfoPQStr), Nothing, Nothing) : chatFeatures_ + +chatFeatures_ :: [((Int, String), Maybe (Int, String), Maybe String)] +chatFeatures_ = + [ ((0, "Disappearing messages: allowed"), Nothing, Nothing), ((0, "Full deletion: off"), Nothing, Nothing), ((0, "Message reactions: enabled"), Nothing, Nothing), ((0, "Voice messages: enabled"), Nothing, Nothing), @@ -300,7 +307,7 @@ groupFeatures :: [(Int, String)] groupFeatures = map (\(a, _, _) -> a) $ groupFeatures'' 0 groupFeaturesNoE2E :: [(Int, String)] -groupFeaturesNoE2E = map (\(a, _, _) -> a) $ groupFeatures_ 0 +groupFeaturesNoE2E = map (\(a, _, _) -> a) $ ((1, "chat banner"), Nothing, Nothing) : groupFeatures_ 0 sndGroupFeatures :: [(Int, String)] sndGroupFeatures = map (\(a, _, _) -> a) $ groupFeatures'' 1 @@ -309,7 +316,7 @@ groupFeatureStrs :: [String] groupFeatureStrs = map (\(a, _, _) -> snd a) $ groupFeatures'' 0 groupFeatures'' :: Int -> [((Int, String), Maybe (Int, String), Maybe String)] -groupFeatures'' dir = ((dir, e2eeInfoNoPQStr), Nothing, Nothing) : groupFeatures_ dir +groupFeatures'' dir = ((1, "chat banner"), Nothing, Nothing) : ((dir, e2eeInfoNoPQStr), Nothing, Nothing) : groupFeatures_ dir groupFeatures_ :: Int -> [((Int, String), Maybe (Int, String), Maybe String)] groupFeatures_ dir = @@ -330,7 +337,8 @@ businessGroupFeatures = map (\(a, _, _) -> a) $ businessGroupFeatures'' 0 businessGroupFeatures'' :: Int -> [((Int, String), Maybe (Int, String), Maybe String)] businessGroupFeatures'' dir = -- [ ((dir, e2eeInfoNoPQStr), Nothing, Nothing), - [ ((dir, "Disappearing messages: on"), Nothing, Nothing), + [ ((1, "chat banner"), Nothing, Nothing), + ((dir, "Disappearing messages: on"), Nothing, Nothing), ((dir, "Direct messages: off"), Nothing, Nothing), ((dir, "Full deletion: off"), Nothing, Nothing), ((dir, "Message reactions: on"), Nothing, Nothing), @@ -505,6 +513,8 @@ dropTime_ :: String -> Maybe String dropTime_ msg = case splitAt 6 msg of ([m, m', ':', s, s', ' '], text) -> if all isDigit [m, m', s, s'] then Just text else Nothing + ([month, month', '-', d, d', ' '], text) -> + if all isDigit [month, month', d, d'] then Just text else Nothing _ -> Nothing dropStrPrefix :: HasCallStack => String -> String -> String