diff --git a/apps/ios/Shared/Views/Helpers/ShareSheet.swift b/apps/ios/Shared/Views/Helpers/ShareSheet.swift index 670cc7cae0..56e437e5c8 100644 --- a/apps/ios/Shared/Views/Helpers/ShareSheet.swift +++ b/apps/ios/Shared/Views/Helpers/ShareSheet.swift @@ -142,6 +142,7 @@ class OpenChatAlertViewController: UIViewController { private let profileBadge: LocalBadge? private let subtitle: String? private let information: String? + private let secondaryInformation: Bool private let cancelTitle: String private let confirmTitle: String? private let secondTitle: String? @@ -156,6 +157,7 @@ class OpenChatAlertViewController: UIViewController { profileBadge: LocalBadge? = nil, subtitle: String? = nil, information: String? = nil, + secondaryInformation: Bool = false, cancelTitle: String = "Cancel", confirmTitle: String? = "Open", secondTitle: String? = nil, @@ -169,6 +171,7 @@ class OpenChatAlertViewController: UIViewController { self.profileBadge = profileBadge self.subtitle = subtitle self.information = information + self.secondaryInformation = secondaryInformation self.cancelTitle = cancelTitle self.confirmTitle = confirmTitle self.secondTitle = secondTitle @@ -248,7 +251,7 @@ class OpenChatAlertViewController: UIViewController { let infoLabel = UILabel() infoLabel.text = information infoLabel.font = UIFont.preferredFont(forTextStyle: .footnote) - infoLabel.textColor = .label + infoLabel.textColor = secondaryInformation ? .secondaryLabel : .label infoLabel.numberOfLines = 3 infoLabel.textAlignment = .center infoLabel.translatesAutoresizingMaskIntoConstraints = false @@ -426,6 +429,7 @@ func showOpenChatAlert( theme: AppTheme, subtitle: String? = nil, information: String? = nil, + secondaryInformation: Bool = false, cancelTitle: String = "Cancel", confirmTitle: String? = "Open", secondTitle: String? = nil, @@ -446,6 +450,7 @@ func showOpenChatAlert( profileBadge: profileBadge, subtitle: subtitle, information: information, + secondaryInformation: secondaryInformation, cancelTitle: cancelTitle, confirmTitle: confirmTitle, secondTitle: secondTitle, diff --git a/apps/ios/Shared/Views/NewChat/NewChatView.swift b/apps/ios/Shared/Views/NewChat/NewChatView.swift index 51746766bd..f938fb0063 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatView.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatView.swift @@ -1195,8 +1195,8 @@ private func showPrepareGroupAlert( information: ownerVerificationMessage(ownerVerification), cancelTitle: NSLocalizedString("Cancel", comment: "new chat action"), confirmTitle: isChannel - ? NSLocalizedString("Open new channel", comment: "new chat action") - : NSLocalizedString("Open new group", comment: "new chat action"), + ? NSLocalizedString("Open channel", comment: "new chat action") + : NSLocalizedString("Open group", comment: "new chat action"), secondTitle: connectOtherButton, onCancel: { cleanup?() }, onConfirm: { @@ -1259,6 +1259,20 @@ private func showOpenKnownContactAlert( ) } +private func memberRoleInformation(_ role: GroupMemberRole, isChannel: Bool) -> String { + switch role { + case .observer: isChannel + ? NSLocalizedString("You are a subscriber", comment: "new chat alert") + : NSLocalizedString("You are an observer", comment: "new chat alert") + case .moderator: NSLocalizedString("You are a moderator", comment: "new chat alert") + case .admin: NSLocalizedString("You are an admin", comment: "new chat alert") + case .owner: NSLocalizedString("You are an owner", comment: "new chat alert") + default: isChannel + ? NSLocalizedString("You are a contributor", comment: "new chat alert") + : NSLocalizedString("You are a member", comment: "new chat alert") + } +} + private func showOpenKnownGroupAlert( _ groupInfo: GroupInfo, theme: AppTheme, @@ -1278,18 +1292,16 @@ private func showOpenKnownGroupAlert( ), theme: theme, subtitle: groupInfo.useRelays ? subscriberCount : nil, + information: groupInfo.nextConnectPrepared || groupInfo.businessChat != nil + ? nil + : memberRoleInformation(groupInfo.membership.memberRole, isChannel: groupInfo.useRelays), + secondaryInformation: true, cancelTitle: NSLocalizedString("Cancel", comment: "new chat action"), confirmTitle: groupInfo.useRelays - ? ( groupInfo.nextConnectPrepared - ? NSLocalizedString("Open new channel", comment: "new chat action") - : NSLocalizedString("Open channel", comment: "new chat action") - ) + ? NSLocalizedString("Open channel", comment: "new chat action") : groupInfo.businessChat == nil - ? ( groupInfo.nextConnectPrepared - ? NSLocalizedString("Open new group", comment: "new chat action") - : NSLocalizedString("Open group", comment: "new chat action") - ) + ? NSLocalizedString("Open group", comment: "new chat action") : ( groupInfo.nextConnectPrepared ? NSLocalizedString("Open new chat", comment: "new chat action") : NSLocalizedString("Open chat", comment: "new chat action") diff --git a/apps/ios/Shared/Views/UserSettings/UserAddressView.swift b/apps/ios/Shared/Views/UserSettings/UserAddressView.swift index b9048becda..9711f3d6f8 100644 --- a/apps/ios/Shared/Views/UserSettings/UserAddressView.swift +++ b/apps/ios/Shared/Views/UserSettings/UserAddressView.swift @@ -802,6 +802,8 @@ private func saveAddressSettings(_ settings: AddressSettingsState, _ savedSettin } } +private let simplexNameSaleStart = Calendar(identifier: .gregorian).date(from: DateComponents(timeZone: TimeZone(identifier: "UTC"), year: 2026, month: 12, day: 12, hour: 18))! + struct SetSimplexDomainView: View { let title: LocalizedStringKey let footer: LocalizedStringKey @@ -815,6 +817,8 @@ struct SetSimplexDomainView: View { @State private var original = "" @State private var didSave = false @State private var editing = false + @State private var timeToSaleStart = simplexNameSaleStart.timeIntervalSinceNow + @State private var saleTimer: Timer? = nil @FocusState private var nameFocused: Bool init(title: LocalizedStringKey, footer: LocalizedStringKey, prompt: String, simplexName: String, broadcastWarning: String? = nil, save: @escaping (String?) async -> Bool) { @@ -873,7 +877,7 @@ struct SetSimplexDomainView: View { Section { if editing { Button { - openBrowserAlert(uri: "https://github.com/simplex-chat/simplex-chat/blob/master/docs/guide/register-simplex-name.md") + openBrowserAlert(uri: "https://simplex.domains/#testing") } label: { HStack { Text("How to register a test name") @@ -901,15 +905,39 @@ struct SetSimplexDomainView: View { } } } + Section { + VStack(alignment: .leading, spacing: 4) { + Text(verbatim: saleCountdown(timeToSaleStart)) + Text(timeToSaleStart > 0 ? "until you can register a SimpleX domain" : "Update the app to register a SimpleX domain") + .font(.caption) + .foregroundColor(theme.colors.secondary) + } + } header: { + Text("SimpleX name sale starts in") + .foregroundColor(theme.colors.secondary) + } footer: { + Text("Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/)") + .foregroundColor(theme.colors.secondary) + .padding(.bottom) + } } + .modifier(ThemedBackground(grouped: true)) .navigationTitle(title) .navigationBarTitleDisplayMode(.large) .onAppear { if editing { DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) { nameFocused = true } } + if timeToSaleStart > 0 { + saleTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { t in + timeToSaleStart = simplexNameSaleStart.timeIntervalSinceNow + if timeToSaleStart <= 0 { t.invalidate() } + } + } } .onDisappear { + saleTimer?.invalidate() + saleTimer = nil if !didSave, !saving, changed, isValid { let domain = normalized(simplexName) let saveName = save @@ -948,6 +976,21 @@ struct SetSimplexDomainView: View { : addSimplexTLD((t.hasPrefix("@") || t.hasPrefix("#") ? String(t.dropFirst()) : t).lowercased()) } + private func saleCountdown(_ remaining: TimeInterval) -> String { + let total = max(0, Int(remaining)) + let days = total / 86400 + let dayStr = String.localizedStringWithFormat( + days == 1 + ? NSLocalizedString("%d day", comment: "time interval") + : NSLocalizedString("%d days", comment: "time interval"), + days + ) + return dayStr + " " + String.localizedStringWithFormat( + NSLocalizedString("%02d hrs %02d min %02d sec", comment: "countdown"), + total / 3600 % 24, total / 60 % 60, total % 60 + ) + } + private func addSimplexTLD(_ d: String) -> String { if d.contains(".") { d } else { "\(d).simplex" } } 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 d0e27058e8..9b35dc90e6 100644 --- a/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff @@ -39,6 +39,10 @@ %1$@ supported SimpleX Chat. The badge expired on %2$@. badge alert + + %1$02d hrs %2$02d min %3$02d sec + countdown + %@ %@ @@ -152,6 +156,10 @@ %@: copied message info + + %d day + time interval + %d days %d дни @@ -2603,6 +2611,10 @@ This is your own one-time link! Линкът се създава… No comment provided by engineer. + + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + No comment provided by engineer. + Crowdfunding on Wefunder No comment provided by engineer. @@ -6311,18 +6323,10 @@ alert button Отвори миграцията към друго устройство authentication reason - - Open new channel - new chat action - Open new chat new chat action - - Open new group - new chat action - Open to accept No comment provided by engineer. @@ -8287,6 +8291,10 @@ copied message info SimpleX name not verified alert title + + SimpleX name sale starts in + No comment provided by engineer. + SimpleX one-time invitation Еднократна покана за SimpleX @@ -9325,6 +9333,10 @@ You will be prompted to complete authentication before this feature is enabled.< Update settings? No comment provided by engineer. + + Update the app to register a SimpleX domain + No comment provided by engineer. + Updated conditions No comment provided by engineer. @@ -9841,6 +9853,23 @@ alert title Вече имате чат профил със същото име. Моля, изберете друго име. No comment provided by engineer. + + You are a contributor + new chat alert + + + You are a member + Вие сте член + new chat alert + + + You are a moderator + new chat alert + + + You are a subscriber + new chat alert + You are already connected to %@. Вече сте вече свързани с %@. @@ -9882,6 +9911,21 @@ Repeat join request? Изпрати отново заявката за присъединяване? new chat sheet title + + You are an admin + Вие сте админ + new chat alert + + + You are an observer + Вие сте наблюдател + new chat alert + + + You are an owner + Вие сте собственик + new chat alert + You are connected to the server used to receive messages from this connection. subscription status explanation @@ -10815,6 +10859,10 @@ pref value препратено No comment provided by engineer. + + forwarded from + No comment provided by engineer. + group shown on group welcome message @@ -11194,9 +11242,9 @@ time to disappear запазено No comment provided by engineer. - - saved from %@ - запазено от %@ + + saved from + запазено от No comment provided by engineer. @@ -11286,6 +11334,10 @@ last received msg: %2$@ unprotected No comment provided by engineer. + + until you can register a SimpleX domain + No comment provided by engineer. + updated channel profile rcv group event chat item 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 26c97d549d..28fc9e9244 100644 --- a/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff @@ -39,6 +39,10 @@ %1$@ supported SimpleX Chat. The badge expired on %2$@. badge alert + + %1$02d hrs %2$02d min %3$02d sec + countdown + %@ %@ @@ -152,6 +156,10 @@ %@: copied message info + + %d day + time interval + %d days %d dní @@ -2499,6 +2507,10 @@ Toto je váš vlastní jednorázový odkaz! Creating link… No comment provided by engineer. + + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + No comment provided by engineer. + Crowdfunding on Wefunder No comment provided by engineer. @@ -6120,18 +6132,10 @@ alert button Open migration to another device authentication reason - - Open new channel - new chat action - Open new chat new chat action - - Open new group - new chat action - Open to accept No comment provided by engineer. @@ -8056,6 +8060,10 @@ copied message info SimpleX name not verified alert title + + SimpleX name sale starts in + No comment provided by engineer. + SimpleX one-time invitation Jednorázová pozvánka SimpleX @@ -9074,6 +9082,10 @@ Před zapnutím této funkce budete vyzváni k dokončení ověření. Update settings? No comment provided by engineer. + + Update the app to register a SimpleX domain + No comment provided by engineer. + Updated conditions No comment provided by engineer. @@ -9567,6 +9579,23 @@ alert title Již máte profil chatu se stejným zobrazovacím názvem. Zvolte prosím jiné jméno. No comment provided by engineer. + + You are a contributor + new chat alert + + + You are a member + Jste člen + new chat alert + + + You are a moderator + new chat alert + + + You are a subscriber + new chat alert + You are already connected to %@. Již jste připojeni k %@. @@ -9601,6 +9630,21 @@ alert title Repeat join request? new chat sheet title + + You are an admin + Jste správce + new chat alert + + + You are an observer + Jste pozorovatel + new chat alert + + + You are an owner + Jste vlastník + new chat alert + You are connected to the server used to receive messages from this connection. subscription status explanation @@ -10519,6 +10563,10 @@ pref value forwarded No comment provided by engineer. + + forwarded from + No comment provided by engineer. + group shown on group welcome message @@ -10892,8 +10940,8 @@ time to disappear saved No comment provided by engineer. - - saved from %@ + + saved from No comment provided by engineer. @@ -10978,6 +11026,10 @@ last received msg: %2$@ unprotected No comment provided by engineer. + + until you can register a SimpleX domain + No comment provided by engineer. + updated channel profile rcv group event chat item 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 1265c782e0..e5cfc54b2c 100644 --- a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff +++ b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff @@ -40,6 +40,10 @@ %1$@ hat SimpleX Chat unterstützt. Das Abzeichen ist am %2$@ abgelaufen. badge alert + + %1$02d hrs %2$02d min %3$02d sec + countdown + %@ %@ @@ -155,6 +159,10 @@ %@: copied message info + + %d day + time interval + %d days %d Tage @@ -2715,6 +2723,10 @@ Das ist Ihr eigener Einmal-Link! Link wird erstellt… No comment provided by engineer. + + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + No comment provided by engineer. + Crowdfunding on Wefunder No comment provided by engineer. @@ -6744,21 +6756,11 @@ alert button Migration auf ein anderes Gerät öffnen authentication reason - - Open new channel - Neuen Kanal öffnen - new chat action - Open new chat Neuen Chat öffnen new chat action - - Open new group - Neue Gruppe öffnen - new chat action - Open to accept Zum Akzeptieren öffnen @@ -8945,6 +8947,10 @@ copied message info SimpleX-Name ist nicht verifiziert alert title + + SimpleX name sale starts in + No comment provided by engineer. + SimpleX one-time invitation SimpleX-Einmal-Einladung @@ -10098,6 +10104,10 @@ Sie werden aufgefordert, die Authentifizierung abzuschließen, bevor diese Funkt Einstellungen aktualisieren? No comment provided by engineer. + + Update the app to register a SimpleX domain + No comment provided by engineer. + Updated conditions Aktualisierte Nutzungsbedingungen @@ -10664,6 +10674,26 @@ alert title Sie haben schon ein Chat-Profil mit dem gleichen Anzeigenamen. Bitte wählen Sie einen anderen Namen aus. No comment provided by engineer. + + You are a contributor + Sie sind Mitwirkender + new chat alert + + + You are a member + Sie sind Mitglied + new chat alert + + + You are a moderator + Sie sind Moderator + new chat alert + + + You are a subscriber + Sie sind Abonnent + new chat alert + You are already connected to %@. Sie sind bereits mit %@ verbunden. @@ -10706,6 +10736,21 @@ Repeat join request? Verbindungsanfrage wiederholen? new chat sheet title + + You are an admin + Sie sind Admin + new chat alert + + + You are an observer + Sie sind Beobachter + new chat alert + + + You are an owner + Sie sind Eigentümer + new chat alert + You are connected to the server used to receive messages from this connection. Sie sind mit dem Server verbunden, der für den Empfang von Nachrichten dieser Verbindung genutzt wird. @@ -11708,6 +11753,10 @@ pref value weitergeleitet No comment provided by engineer. + + forwarded from + No comment provided by engineer. + group Gruppe @@ -12116,9 +12165,9 @@ time to disappear abgespeichert No comment provided by engineer. - - saved from %@ - abgespeichert von %@ + + saved from + abgespeichert von No comment provided by engineer. @@ -12215,6 +12264,10 @@ Zuletzt empfangene Nachricht: %2$@ Ungeschützt No comment provided by engineer. + + until you can register a SimpleX domain + No comment provided by engineer. + updated channel profile Kanalprofil aktualisiert 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 38e0ecfe85..184c9fd6b4 100644 --- a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff +++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff @@ -40,6 +40,11 @@ %1$@ supported SimpleX Chat. The badge expired on %2$@. badge alert + + %1$02d hrs %2$02d min %3$02d sec + %1$02d hrs %2$02d min %3$02d sec + countdown + %@ %@ @@ -155,6 +160,11 @@ %@: copied message info + + %d day + %d day + time interval + %d days %d days @@ -2715,6 +2725,11 @@ This is your own one-time link! Creating link… No comment provided by engineer. + + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + No comment provided by engineer. + Crowdfunding on Wefunder Crowdfunding on Wefunder @@ -6747,21 +6762,11 @@ alert button Open migration to another device authentication reason - - Open new channel - Open new channel - new chat action - Open new chat Open new chat new chat action - - Open new group - Open new group - new chat action - Open to accept Open to accept @@ -8948,6 +8953,11 @@ copied message info SimpleX name not verified alert title + + SimpleX name sale starts in + SimpleX name sale starts in + No comment provided by engineer. + SimpleX one-time invitation SimpleX one-time invitation @@ -10101,6 +10111,11 @@ You will be prompted to complete authentication before this feature is enabled.< Update settings? No comment provided by engineer. + + Update the app to register a SimpleX domain + Update the app to register a SimpleX domain + No comment provided by engineer. + Updated conditions Updated conditions @@ -10667,6 +10682,26 @@ alert title You already have a chat profile with the same display name. Please choose another name. No comment provided by engineer. + + You are a contributor + You are a contributor + new chat alert + + + You are a member + You are a member + new chat alert + + + You are a moderator + You are a moderator + new chat alert + + + You are a subscriber + You are a subscriber + new chat alert + You are already connected to %@. You are already connected to %@. @@ -10709,6 +10744,21 @@ Repeat join request? Repeat join request? new chat sheet title + + You are an admin + You are an admin + new chat alert + + + You are an observer + You are an observer + new chat alert + + + You are an owner + You are an owner + new chat alert + You are connected to the server used to receive messages from this connection. You are connected to the server used to receive messages from this connection. @@ -11713,6 +11763,11 @@ pref value forwarded No comment provided by engineer. + + forwarded from + forwarded from + No comment provided by engineer. + group group @@ -12121,9 +12176,9 @@ time to disappear saved No comment provided by engineer. - - saved from %@ - saved from %@ + + saved from + saved from No comment provided by engineer. @@ -12220,6 +12275,11 @@ last received msg: %2$@ unprotected No comment provided by engineer. + + until you can register a SimpleX domain + until you can register a SimpleX domain + No comment provided by engineer. + updated channel profile updated channel profile 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 60f0dd3e2d..82ba09d6a9 100644 --- a/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff +++ b/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff @@ -40,6 +40,10 @@ %1$@ ha apoyado a SimpleX Chat. La insignia caducó el %2$@. badge alert + + %1$02d hrs %2$02d min %3$02d sec + countdown + %@ %@ @@ -155,6 +159,10 @@ %@: copied message info + + %d day + time interval + %d days %d día(s) @@ -2715,6 +2723,10 @@ This is your own one-time link! Creando enlace… No comment provided by engineer. + + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + No comment provided by engineer. + Crowdfunding on Wefunder No comment provided by engineer. @@ -6744,21 +6756,11 @@ alert button Abrir menú migración a otro dispositivo authentication reason - - Open new channel - Abrir canal nuevo - new chat action - Open new chat Abrir chat nuevo new chat action - - Open new group - Abrir grupo nuevo - new chat action - Open to accept Abrir para aceptar @@ -8945,6 +8947,10 @@ copied message info Nombre SimpleX no verificado alert title + + SimpleX name sale starts in + No comment provided by engineer. + SimpleX one-time invitation Invitación SimpleX de un uso @@ -10098,6 +10104,10 @@ Se te pedirá que completes la autenticación antes de activar esta función.¿Actualizar configuración? No comment provided by engineer. + + Update the app to register a SimpleX domain + No comment provided by engineer. + Updated conditions Condiciones actualizadas @@ -10664,6 +10674,26 @@ alert title Ya tienes un perfil con este nombre mostrado. Por favor, elige otro nombre. No comment provided by engineer. + + You are a contributor + Eres colaborador + new chat alert + + + You are a member + Eres miembro + new chat alert + + + You are a moderator + Eres moderador + new chat alert + + + You are a subscriber + Eres suscriptor + new chat alert + You are already connected to %@. Ya estás conectado con %@. @@ -10706,6 +10736,21 @@ Repeat join request? ¿Repetir solicitud de admisión? new chat sheet title + + You are an admin + Eres administrador + new chat alert + + + You are an observer + Eres observador + new chat alert + + + You are an owner + Eres propietario + new chat alert + You are connected to the server used to receive messages from this connection. Estás conectado al servidor usado para recibir mensajes de esta conexión. @@ -11708,6 +11753,10 @@ pref value reenviado No comment provided by engineer. + + forwarded from + No comment provided by engineer. + group grupo @@ -12116,9 +12165,9 @@ time to disappear guardado No comment provided by engineer. - - saved from %@ - Guardado desde %@ + + saved from + Guardado desde No comment provided by engineer. @@ -12215,6 +12264,10 @@ last received msg: %2$@ desprotegida No comment provided by engineer. + + until you can register a SimpleX domain + No comment provided by engineer. + updated channel profile perfil del canal actualizado 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 78caebe522..e01c47949d 100644 --- a/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff @@ -39,6 +39,10 @@ %1$@ supported SimpleX Chat. The badge expired on %2$@. badge alert + + %1$02d hrs %2$02d min %3$02d sec + countdown + %@ % @ @@ -144,6 +148,10 @@ %@: copied message info + + %d day + time interval + %d days %d päivää @@ -2386,6 +2394,10 @@ This is your own one-time link! Creating link… No comment provided by engineer. + + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + No comment provided by engineer. + Crowdfunding on Wefunder No comment provided by engineer. @@ -6000,18 +6012,10 @@ alert button Open migration to another device authentication reason - - Open new channel - new chat action - Open new chat new chat action - - Open new group - new chat action - Open to accept No comment provided by engineer. @@ -7935,6 +7939,10 @@ copied message info SimpleX name not verified alert title + + SimpleX name sale starts in + No comment provided by engineer. + SimpleX one-time invitation SimpleX-kertakutsu @@ -8948,6 +8956,10 @@ Sinua kehotetaan suorittamaan todennus loppuun, ennen kuin tämä ominaisuus ote Update settings? No comment provided by engineer. + + Update the app to register a SimpleX domain + No comment provided by engineer. + Updated conditions No comment provided by engineer. @@ -9441,6 +9453,23 @@ alert title Sinulla on jo keskusteluprofiili samalla näyttönimellä. Valitse toinen nimi. No comment provided by engineer. + + You are a contributor + new chat alert + + + You are a member + Olet jäsen + new chat alert + + + You are a moderator + new chat alert + + + You are a subscriber + new chat alert + You are already connected to %@. Olet jo muodostanut yhteyden %@:n kanssa. @@ -9475,6 +9504,21 @@ alert title Repeat join request? new chat sheet title + + You are an admin + Olet ylläpitäjä + new chat alert + + + You are an observer + Olet tarkkailija + new chat alert + + + You are an owner + Olet omistaja + new chat alert + You are connected to the server used to receive messages from this connection. subscription status explanation @@ -10391,6 +10435,10 @@ pref value forwarded No comment provided by engineer. + + forwarded from + No comment provided by engineer. + group shown on group welcome message @@ -10764,8 +10812,8 @@ time to disappear saved No comment provided by engineer. - - saved from %@ + + saved from No comment provided by engineer. @@ -10850,6 +10898,10 @@ last received msg: %2$@ unprotected No comment provided by engineer. + + until you can register a SimpleX domain + No comment provided by engineer. + updated channel profile rcv group event chat item 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 78e8e30096..7305dc4346 100644 --- a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff @@ -40,6 +40,10 @@ %1$@ a soutenu SimpleX Chat. Le badge a expiré le %2$@. badge alert + + %1$02d hrs %2$02d min %3$02d sec + countdown + %@ %@ @@ -155,6 +159,10 @@ %@ : copied message info + + %d day + time interval + %d days %d jours @@ -2715,6 +2723,10 @@ Il s'agit de votre propre lien unique ! Création d'un lien… No comment provided by engineer. + + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + No comment provided by engineer. + Crowdfunding on Wefunder No comment provided by engineer. @@ -6744,21 +6756,11 @@ alert button Ouvrir le transfert vers un autre appareil authentication reason - - Open new channel - Ouvrir un nouveau canal - new chat action - Open new chat Ouvrir une nouvelle conversation new chat action - - Open new group - Ouvrir le nouveau groupe - new chat action - Open to accept Ouvrir pour accepter @@ -8945,6 +8947,10 @@ copied message info Nom SimpleX non vérifié alert title + + SimpleX name sale starts in + No comment provided by engineer. + SimpleX one-time invitation Invitation unique SimpleX @@ -10098,6 +10104,10 @@ Vous serez invité à confirmer l'authentification avant que cette fonction ne s Mettre à jour les paramètres ? No comment provided by engineer. + + Update the app to register a SimpleX domain + No comment provided by engineer. + Updated conditions Conditions mises à jour @@ -10664,6 +10674,26 @@ alert title Vous avez déjà un profil de messagerie avec le même nom d’affichage. Veuillez choisir un autre nom. No comment provided by engineer. + + You are a contributor + Vous êtes contributeur + new chat alert + + + You are a member + Vous êtes membre + new chat alert + + + You are a moderator + Vous êtes modérateur + new chat alert + + + You are a subscriber + Vous êtes abonné·e + new chat alert + You are already connected to %@. Vous êtes déjà connecté·e à %@ via ce lien. @@ -10706,6 +10736,21 @@ Repeat join request? Répéter la demande d'adhésion ? new chat sheet title + + You are an admin + Vous êtes admin + new chat alert + + + You are an observer + Vous êtes observateur + new chat alert + + + You are an owner + Vous êtes propriétaire + new chat alert + You are connected to the server used to receive messages from this connection. Vous êtes connecté au serveur utilisé pour recevoir les messages de cette connexion. @@ -11708,6 +11753,10 @@ pref value transféré No comment provided by engineer. + + forwarded from + No comment provided by engineer. + group groupe @@ -12116,9 +12165,9 @@ time to disappear enregistré No comment provided by engineer. - - saved from %@ - enregistré à partir de %@ + + saved from + enregistré à partir de No comment provided by engineer. @@ -12215,6 +12264,10 @@ dernier message reçu : %2$@ non protégé No comment provided by engineer. + + until you can register a SimpleX domain + No comment provided by engineer. + updated channel profile profil du canal mis à jour 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 4663996480..ee4aae63a2 100644 --- a/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff +++ b/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff @@ -40,6 +40,10 @@ %1$@ támogatta a SimpleX Chatet. A kitűző lejárt ekkor: %2$@. badge alert + + %1$02d hrs %2$02d min %3$02d sec + countdown + %@ %@ @@ -155,6 +159,10 @@ %@: copied message info + + %d day + time interval + %d days %d nap @@ -2715,6 +2723,10 @@ Ez a saját egyszer használható meghívója! Hivatkozás létrehozása… No comment provided by engineer. + + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + No comment provided by engineer. + Crowdfunding on Wefunder No comment provided by engineer. @@ -6744,21 +6756,11 @@ alert button Átköltöztetés indítása egy másik eszközre authentication reason - - Open new channel - Új csatorna megnyitása - new chat action - Open new chat Új csevegés megnyitása new chat action - - Open new group - Új csoport megnyitása - new chat action - Open to accept Megnyitás az elfogadáshoz @@ -8945,6 +8947,10 @@ copied message info Nincs ellenőrizve a SimpleX-név alert title + + SimpleX name sale starts in + No comment provided by engineer. + SimpleX one-time invitation Egyszer használható SimpleX meghívó @@ -10098,6 +10104,10 @@ A funkció bekapcsolása előtt a rendszer felszólítja a képernyőzár beáll Frissíti a beállításokat? No comment provided by engineer. + + Update the app to register a SimpleX domain + No comment provided by engineer. + Updated conditions Frissített feltételek @@ -10664,6 +10674,26 @@ alert title Már van egy csevegési profil ugyanezzel a megjelenítendő névvel. Válasszon egy másik nevet. No comment provided by engineer. + + You are a contributor + Ön közreműködő + new chat alert + + + You are a member + Ön tag + new chat alert + + + You are a moderator + Ön moderátor + new chat alert + + + You are a subscriber + Ön feliratkozó + new chat alert + You are already connected to %@. Ön már kapcsolódott a következőhöz: %@. @@ -10706,6 +10736,21 @@ Repeat join request? Megismétli a csatlakozási kérést? new chat sheet title + + You are an admin + Ön adminisztrátor + new chat alert + + + You are an observer + Ön megfigyelő + new chat alert + + + You are an owner + Ön tulajdonos + new chat alert + You are connected to the server used to receive messages from this connection. Ön kapcsolódott ahhoz a kiszolgálóhoz, amely az adott partnerétől érkező üzenetek fogadására szolgál. @@ -11708,6 +11753,10 @@ pref value továbbított No comment provided by engineer. + + forwarded from + No comment provided by engineer. + group csoport @@ -12116,9 +12165,9 @@ time to disappear mentett No comment provided by engineer. - - saved from %@ - mentve innen: %@ + + saved from + mentve innen: No comment provided by engineer. @@ -12215,6 +12264,10 @@ utoljára fogadott üzenet: %2$@ nem védett No comment provided by engineer. + + until you can register a SimpleX domain + No comment provided by engineer. + updated channel profile frissítette a csatorna profilját 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 80f6c9a224..d1b0e19478 100644 --- a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff +++ b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff @@ -40,6 +40,10 @@ %1$@ ha sostenuto SimpleX Chat. La targhetta è scaduta il %2$@. badge alert + + %1$02d hrs %2$02d min %3$02d sec + countdown + %@ %@ @@ -155,6 +159,10 @@ %@: copied message info + + %d day + time interval + %d days %d giorni @@ -2715,6 +2723,10 @@ Questo è il tuo link una tantum! Creazione link… No comment provided by engineer. + + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + No comment provided by engineer. + Crowdfunding on Wefunder No comment provided by engineer. @@ -6744,21 +6756,11 @@ alert button Apri migrazione ad un altro dispositivo authentication reason - - Open new channel - Apri il nuovo canale - new chat action - Open new chat Apri la nuova chat new chat action - - Open new group - Apri il nuovo gruppo - new chat action - Open to accept Apri per accettare @@ -8945,6 +8947,10 @@ copied message info Nome SimpleX non verificato alert title + + SimpleX name sale starts in + No comment provided by engineer. + SimpleX one-time invitation Invito SimpleX una tantum @@ -10098,6 +10104,10 @@ Ti verrà chiesto di completare l'autenticazione prima di attivare questa funzio Aggiornare le impostazioni? No comment provided by engineer. + + Update the app to register a SimpleX domain + No comment provided by engineer. + Updated conditions Condizioni aggiornate @@ -10664,6 +10674,26 @@ alert title Hai già un profilo chat con lo stesso nome da mostrare. Scegli un altro nome. No comment provided by engineer. + + You are a contributor + Sei un collaboratore + new chat alert + + + You are a member + Sei un membro + new chat alert + + + You are a moderator + Sei un moderatore + new chat alert + + + You are a subscriber + Sei iscritto/a + new chat alert + You are already connected to %@. Sei già connesso/a a %@. @@ -10706,6 +10736,21 @@ Repeat join request? Ripetere la richiesta di ingresso? new chat sheet title + + You are an admin + Sei un amministratore + new chat alert + + + You are an observer + Sei un osservatore + new chat alert + + + You are an owner + Sei un proprietario + new chat alert + You are connected to the server used to receive messages from this connection. Sei connesso/a al server usato per ricevere messaggi da questa connessione. @@ -11708,6 +11753,10 @@ pref value inoltrato No comment provided by engineer. + + forwarded from + No comment provided by engineer. + group gruppo @@ -12116,9 +12165,9 @@ time to disappear salvato No comment provided by engineer. - - saved from %@ - salvato da %@ + + saved from + salvato da No comment provided by engineer. @@ -12215,6 +12264,10 @@ ultimo msg ricevuto: %2$@ non protetto No comment provided by engineer. + + until you can register a SimpleX domain + No comment provided by engineer. + updated channel profile profilo del canale aggiornato 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 345e6836a6..5d9be60493 100644 --- a/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff @@ -39,6 +39,10 @@ %1$@ supported SimpleX Chat. The badge expired on %2$@. badge alert + + %1$02d hrs %2$02d min %3$02d sec + countdown + %@ %@ @@ -152,6 +156,10 @@ %@: copied message info + + %d day + time interval + %d days %d 日 @@ -2491,6 +2499,10 @@ This is your own one-time link! Creating link… No comment provided by engineer. + + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + No comment provided by engineer. + Crowdfunding on Wefunder No comment provided by engineer. @@ -6120,18 +6132,10 @@ alert button Open migration to another device authentication reason - - Open new channel - new chat action - Open new chat new chat action - - Open new group - new chat action - Open to accept No comment provided by engineer. @@ -8048,6 +8052,10 @@ copied message info SimpleX name not verified alert title + + SimpleX name sale starts in + No comment provided by engineer. + SimpleX one-time invitation SimpleX使い捨て招待リンク @@ -9061,6 +9069,10 @@ You will be prompted to complete authentication before this feature is enabled.< Update settings? No comment provided by engineer. + + Update the app to register a SimpleX domain + No comment provided by engineer. + Updated conditions No comment provided by engineer. @@ -9554,6 +9566,23 @@ alert title 同じ表示名前のチャットプロフィールが既にあります。別のを選んでください。 No comment provided by engineer. + + You are a contributor + new chat alert + + + You are a member + あなたはメンバーです + new chat alert + + + You are a moderator + new chat alert + + + You are a subscriber + new chat alert + You are already connected to %@. すでに %@ に接続されています。 @@ -9588,6 +9617,21 @@ alert title Repeat join request? new chat sheet title + + You are an admin + あなたは管理者です + new chat alert + + + You are an observer + あなたはオブザーバーです + new chat alert + + + You are an owner + あなたはオーナーです + new chat alert + You are connected to the server used to receive messages from this connection. subscription status explanation @@ -10506,6 +10550,10 @@ pref value forwarded No comment provided by engineer. + + forwarded from + No comment provided by engineer. + group shown on group welcome message @@ -10879,8 +10927,8 @@ time to disappear saved No comment provided by engineer. - - saved from %@ + + saved from No comment provided by engineer. @@ -10965,6 +11013,10 @@ last received msg: %2$@ unprotected No comment provided by engineer. + + until you can register a SimpleX domain + No comment provided by engineer. + updated channel profile rcv group event chat item 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 5ebd47c99a..d1b092f63b 100644 --- a/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff @@ -39,6 +39,10 @@ %1$@ supported SimpleX Chat. The badge expired on %2$@. badge alert + + %1$02d hrs %2$02d min %3$02d sec + countdown + %@ %@ @@ -152,6 +156,10 @@ %@: copied message info + + %d day + time interval + %d days %d dagen @@ -2603,6 +2611,10 @@ Dit is uw eigen eenmalige link! Link maken… No comment provided by engineer. + + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + No comment provided by engineer. + Crowdfunding on Wefunder No comment provided by engineer. @@ -6508,18 +6520,10 @@ alert button Open de migratie naar een ander apparaat authentication reason - - Open new channel - new chat action - Open new chat new chat action - - Open new group - new chat action - Open to accept No comment provided by engineer. @@ -8613,6 +8617,10 @@ copied message info SimpleX name not verified alert title + + SimpleX name sale starts in + No comment provided by engineer. + SimpleX one-time invitation Eenmalige SimpleX uitnodiging @@ -9705,6 +9713,10 @@ U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingesc Instellingen actualiseren? No comment provided by engineer. + + Update the app to register a SimpleX domain + No comment provided by engineer. + Updated conditions Bijgewerkte voorwaarden @@ -10248,6 +10260,24 @@ alert title Je hebt al een chatprofiel met dezelfde weergave naam. Kies een andere naam. No comment provided by engineer. + + You are a contributor + new chat alert + + + You are a member + Je bent lid + new chat alert + + + You are a moderator + Je bent moderator + new chat alert + + + You are a subscriber + new chat alert + You are already connected to %@. U bent al verbonden met %@. @@ -10290,6 +10320,21 @@ Repeat join request? Deelnameverzoek herhalen? new chat sheet title + + You are an admin + Je bent beheerder + new chat alert + + + You are an observer + Je bent waarnemer + new chat alert + + + You are an owner + Je bent eigenaar + new chat alert + You are connected to the server used to receive messages from this connection. subscription status explanation @@ -11254,6 +11299,10 @@ pref value doorgestuurd No comment provided by engineer. + + forwarded from + No comment provided by engineer. + group shown on group welcome message @@ -11651,9 +11700,9 @@ time to disappear opgeslagen No comment provided by engineer. - - saved from %@ - opgeslagen van %@ + + saved from + opgeslagen van No comment provided by engineer. @@ -11749,6 +11798,10 @@ laatst ontvangen bericht: %2$@ onbeschermd No comment provided by engineer. + + until you can register a SimpleX domain + No comment provided by engineer. + updated channel profile rcv group event chat item 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 68f836184f..a48e4219d4 100644 --- a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff +++ b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff @@ -39,6 +39,10 @@ %1$@ supported SimpleX Chat. The badge expired on %2$@. badge alert + + %1$02d hrs %2$02d min %3$02d sec + countdown + %@ %@ @@ -152,6 +156,10 @@ %@: copied message info + + %d day + time interval + %d days %d dni @@ -2622,6 +2630,10 @@ To jest twój jednorazowy link! Tworzenie linku… No comment provided by engineer. + + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + No comment provided by engineer. + Crowdfunding on Wefunder No comment provided by engineer. @@ -6565,20 +6577,11 @@ alert button Otwórz migrację na innym urządzeniu authentication reason - - Open new channel - new chat action - Open new chat Otwórz nowy czat new chat action - - Open new group - Otwórz nową grupę - new chat action - Open to accept Otwórz by zaakceptować @@ -8699,6 +8702,10 @@ copied message info SimpleX name not verified alert title + + SimpleX name sale starts in + No comment provided by engineer. + SimpleX one-time invitation Zaproszenie jednorazowe SimpleX @@ -9805,6 +9812,10 @@ Przed włączeniem tej funkcji zostanie wyświetlony monit uwierzytelniania.Zaktualizować ustawienia? No comment provided by engineer. + + Update the app to register a SimpleX domain + No comment provided by engineer. + Updated conditions Zaktualizowane warunki @@ -10357,6 +10368,24 @@ alert title Masz już profil czatu o tej samej nazwie wyświetlanej. Proszę wybrać inną nazwę. No comment provided by engineer. + + You are a contributor + new chat alert + + + You are a member + Jesteś członkiem + new chat alert + + + You are a moderator + Jesteś moderatorem + new chat alert + + + You are a subscriber + new chat alert + You are already connected to %@. Jesteś już połączony z %@. @@ -10399,6 +10428,21 @@ Repeat join request? Powtórzyć prośbę dołączenia? new chat sheet title + + You are an admin + Jesteś administratorem + new chat alert + + + You are an observer + Jesteś obserwatorem + new chat alert + + + You are an owner + Jesteś właścicielem + new chat alert + You are connected to the server used to receive messages from this connection. Jesteś połączony z serwerem służącym do odbierania wiadomości z tego połączenia. @@ -11374,6 +11418,10 @@ pref value przekazane dalej No comment provided by engineer. + + forwarded from + No comment provided by engineer. + group grupa @@ -11776,9 +11824,9 @@ time to disappear zapisane No comment provided by engineer. - - saved from %@ - zapisane od %@ + + saved from + zapisane od No comment provided by engineer. @@ -11874,6 +11922,10 @@ ostatnia otrzymana wiadomość: %2$@ niezabezpieczony No comment provided by engineer. + + until you can register a SimpleX domain + No comment provided by engineer. + updated channel profile rcv group event chat item 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 4d08db0a99..67d70d9708 100644 --- a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff @@ -40,6 +40,10 @@ %1$@ поддерживал(а) SimpleX Chat. Срок действия значка истёк %2$@. badge alert + + %1$02d hrs %2$02d min %3$02d sec + countdown + %@ %@ @@ -155,6 +159,10 @@ %@: copied message info + + %d day + time interval + %d days %d дней @@ -2715,6 +2723,10 @@ This is your own one-time link! Создаётся ссылка… No comment provided by engineer. + + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + No comment provided by engineer. + Crowdfunding on Wefunder No comment provided by engineer. @@ -6743,21 +6755,11 @@ alert button Открытие миграции на другое устройство authentication reason - - Open new channel - Открыть новый канал - new chat action - Open new chat Открыть новый чат new chat action - - Open new group - Открыть новую группу - new chat action - Open to accept Откройте чтобы принять @@ -8944,6 +8946,10 @@ copied message info SimpleX имя не проверено alert title + + SimpleX name sale starts in + No comment provided by engineer. + SimpleX one-time invitation SimpleX одноразовая ссылка @@ -10097,6 +10103,10 @@ You will be prompted to complete authentication before this feature is enabled.< Обновить настройки? No comment provided by engineer. + + Update the app to register a SimpleX domain + No comment provided by engineer. + Updated conditions Обновлённые условия @@ -10663,6 +10673,26 @@ alert title У Вас уже есть профиль с таким именем. Пожалуйста, выберите другое имя. No comment provided by engineer. + + You are a contributor + Вы соавтор + new chat alert + + + You are a member + Вы член группы + new chat alert + + + You are a moderator + Вы модератор + new chat alert + + + You are a subscriber + Вы подписчик + new chat alert + You are already connected to %@. Вы уже соединены с контактом %@. @@ -10705,6 +10735,21 @@ Repeat join request? Повторить запрос на вступление? new chat sheet title + + You are an admin + Вы админ + new chat alert + + + You are an observer + Вы читатель + new chat alert + + + You are an owner + Вы владелец + new chat alert + You are connected to the server used to receive messages from this connection. Вы подключены к серверу, используемому для приёма сообщений от этого соединения. @@ -11707,6 +11752,10 @@ pref value переслано No comment provided by engineer. + + forwarded from + No comment provided by engineer. + group группа @@ -12115,9 +12164,9 @@ time to disappear сохранено No comment provided by engineer. - - saved from %@ - сохранено из %@ + + saved from + сохранено из No comment provided by engineer. @@ -12214,6 +12263,10 @@ last received msg: %2$@ незащищённый No comment provided by engineer. + + until you can register a SimpleX domain + No comment provided by engineer. + updated channel profile обновил профиль канала 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 5859a228a3..b77da016c7 100644 --- a/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff +++ b/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff @@ -36,6 +36,10 @@ %1$@ supported SimpleX Chat. The badge expired on %2$@. badge alert + + %1$02d hrs %2$02d min %3$02d sec + countdown + %@ %@ @@ -139,6 +143,10 @@ %@: copied message info + + %d day + time interval + %d days %d วัน @@ -2375,6 +2383,10 @@ This is your own one-time link! Creating link… No comment provided by engineer. + + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + No comment provided by engineer. + Crowdfunding on Wefunder No comment provided by engineer. @@ -5979,18 +5991,10 @@ alert button Open migration to another device authentication reason - - Open new channel - new chat action - Open new chat new chat action - - Open new group - new chat action - Open to accept No comment provided by engineer. @@ -7907,6 +7911,10 @@ copied message info SimpleX name not verified alert title + + SimpleX name sale starts in + No comment provided by engineer. + SimpleX one-time invitation คำเชิญ SimpleX แบบครั้งเดียว @@ -8918,6 +8926,10 @@ You will be prompted to complete authentication before this feature is enabled.< Update settings? No comment provided by engineer. + + Update the app to register a SimpleX domain + No comment provided by engineer. + Updated conditions No comment provided by engineer. @@ -9409,6 +9421,23 @@ alert title คุณมีโปรไฟล์แชทที่ใช้ชื่อแสดงเดียวกันอยู่แล้ว กรุณาเลือกชื่ออื่น No comment provided by engineer. + + You are a contributor + new chat alert + + + You are a member + คุณเป็นสมาชิก + new chat alert + + + You are a moderator + new chat alert + + + You are a subscriber + new chat alert + You are already connected to %@. คุณได้เชื่อมต่อกับ %@ แล้ว @@ -9443,6 +9472,21 @@ alert title Repeat join request? new chat sheet title + + You are an admin + คุณเป็นผู้ดูแลระบบ + new chat alert + + + You are an observer + คุณเป็นผู้สังเกตการณ์ + new chat alert + + + You are an owner + คุณเป็นเจ้าของ + new chat alert + You are connected to the server used to receive messages from this connection. subscription status explanation @@ -10356,6 +10400,10 @@ pref value forwarded No comment provided by engineer. + + forwarded from + No comment provided by engineer. + group shown on group welcome message @@ -10729,8 +10777,8 @@ time to disappear saved No comment provided by engineer. - - saved from %@ + + saved from No comment provided by engineer. @@ -10815,6 +10863,10 @@ last received msg: %2$@ unprotected No comment provided by engineer. + + until you can register a SimpleX domain + No comment provided by engineer. + updated channel profile rcv group event chat item 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 f6c20f8cdd..7b34d788cc 100644 --- a/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff +++ b/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff @@ -40,6 +40,10 @@ %1$@, SimpleX Chat'i destekledi. Rozetin süresi %2$@ tarihinde doldu. badge alert + + %1$02d hrs %2$02d min %3$02d sec + countdown + %@ %@ @@ -155,6 +159,10 @@ %@: copied message info + + %d day + time interval + %d days %d gün @@ -2633,6 +2641,10 @@ Bu senin kendi tek kullanımlık bağlantın! Link oluşturuluyor… No comment provided by engineer. + + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + No comment provided by engineer. + Crowdfunding on Wefunder No comment provided by engineer. @@ -6561,20 +6573,11 @@ alert button Başka bir cihaza açık geçiş authentication reason - - Open new channel - new chat action - Open new chat Yeni sohbet aç new chat action - - Open new group - Yeni grup aç - new chat action - Open to accept Kabul etmek için aç @@ -8689,6 +8692,10 @@ copied message info SimpleX name not verified alert title + + SimpleX name sale starts in + No comment provided by engineer. + SimpleX one-time invitation SimpleX tek kullanımlık davet @@ -9791,6 +9798,10 @@ Bu özellik etkinleştirilmeden önce kimlik doğrulamayı tamamlamanız istenec Ayarları güncelleyelim mi? No comment provided by engineer. + + Update the app to register a SimpleX domain + No comment provided by engineer. + Updated conditions Güncellenmiş koşullar @@ -10342,6 +10353,24 @@ alert title Aynı görünen ada sahip bir konuşma profilin zaten var. Lütfen başka bir ad seç. No comment provided by engineer. + + You are a contributor + new chat alert + + + You are a member + Üyesiniz + new chat alert + + + You are a moderator + Moderatörsünüz + new chat alert + + + You are a subscriber + new chat alert + You are already connected to %@. Zaten %@'a bağlısınız. @@ -10384,6 +10413,21 @@ Repeat join request? Katılma isteği tekrarlansın mı? new chat sheet title + + You are an admin + Yöneticisiniz + new chat alert + + + You are an observer + Gözlemcisiniz + new chat alert + + + You are an owner + Sahipsiniz + new chat alert + You are connected to the server used to receive messages from this connection. subscription status explanation @@ -11354,6 +11398,10 @@ pref value iletildi No comment provided by engineer. + + forwarded from + No comment provided by engineer. + group grup @@ -11755,9 +11803,9 @@ time to disappear kaydedildi No comment provided by engineer. - - saved from %@ - %@ tarafından kaydedildi + + saved from + kaydedildi: No comment provided by engineer. @@ -11853,6 +11901,10 @@ son alınan msj: %2$@ korumasız No comment provided by engineer. + + until you can register a SimpleX domain + No comment provided by engineer. + updated channel profile rcv group event chat item 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 1d6c687a94..e2df9d37af 100644 --- a/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff +++ b/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff @@ -40,6 +40,10 @@ %1$@ підтримував SimpleX Chat. Термін дії значка вичерпався %2$@. badge alert + + %1$02d hrs %2$02d min %3$02d sec + countdown + %@ %@ @@ -155,6 +159,10 @@ %@: copied message info + + %d day + time interval + %d days %d днів @@ -2654,6 +2662,10 @@ This is your own one-time link! Створення посилання… No comment provided by engineer. + + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + No comment provided by engineer. + Crowdfunding on Wefunder No comment provided by engineer. @@ -6573,20 +6585,11 @@ alert button Відкрита міграція на інший пристрій authentication reason - - Open new channel - new chat action - Open new chat Відкрити новий чат new chat action - - Open new group - Відкрити нову групу - new chat action - Open to accept Відкрити для прийняття @@ -8699,6 +8702,10 @@ copied message info SimpleX name not verified alert title + + SimpleX name sale starts in + No comment provided by engineer. + SimpleX one-time invitation Одноразове запрошення SimpleX @@ -9799,6 +9806,10 @@ You will be prompted to complete authentication before this feature is enabled.< Оновити налаштування? No comment provided by engineer. + + Update the app to register a SimpleX domain + No comment provided by engineer. + Updated conditions Оновлені умови @@ -10350,6 +10361,24 @@ alert title Ви вже маєте профіль у чаті з таким самим іменем. Будь ласка, виберіть інше ім'я. No comment provided by engineer. + + You are a contributor + new chat alert + + + You are a member + Ви учасник + new chat alert + + + You are a moderator + Ви модератор + new chat alert + + + You are a subscriber + new chat alert + You are already connected to %@. Ви вже підключені до %@. @@ -10392,6 +10421,21 @@ Repeat join request? Повторити запит на приєднання? new chat sheet title + + You are an admin + Ви адмін + new chat alert + + + You are an observer + Ви спостерігач + new chat alert + + + You are an owner + Ви власник + new chat alert + You are connected to the server used to receive messages from this connection. subscription status explanation @@ -11362,6 +11406,10 @@ pref value переслано No comment provided by engineer. + + forwarded from + No comment provided by engineer. + group група @@ -11762,9 +11810,9 @@ time to disappear збережено No comment provided by engineer. - - saved from %@ - збережено з %@ + + saved from + збережено з No comment provided by engineer. @@ -11860,6 +11908,10 @@ last received msg: %2$@ незахищені No comment provided by engineer. + + until you can register a SimpleX domain + No comment provided by engineer. + updated channel profile rcv group event chat item 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 c977009785..1f7e0c5fa2 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 @@ -40,6 +40,10 @@ %1$@ 曾是 SimpleX Chat 支持者。徽章已于 %2$@ 过期。 badge alert + + %1$02d hrs %2$02d min %3$02d sec + countdown + %@ %@ @@ -155,6 +159,10 @@ %@: copied message info + + %d day + time interval + %d days %d 天 @@ -2710,6 +2718,10 @@ This is your own one-time link! 创建链接中… No comment provided by engineer. + + Crowdfunding investors can reserve names before the sale starts: [simplex.domains](https://simplex.domains/) + No comment provided by engineer. + Crowdfunding on Wefunder No comment provided by engineer. @@ -6725,21 +6737,11 @@ alert button 打开迁移到另一台设备 authentication reason - - Open new channel - 打开新频道 - new chat action - Open new chat 打开新聊天 new chat action - - Open new group - 打开新群 - new chat action - Open to accept 打开以接受 @@ -8913,6 +8915,10 @@ copied message info SimpleX 名称未验证 alert title + + SimpleX name sale starts in + No comment provided by engineer. + SimpleX one-time invitation SimpleX 一次性邀请 @@ -10056,6 +10062,10 @@ You will be prompted to complete authentication before this feature is enabled.< 更新设置? No comment provided by engineer. + + Update the app to register a SimpleX domain + No comment provided by engineer. + Updated conditions 条款已更新 @@ -10620,6 +10630,25 @@ alert title 您已经有一个显示名相同的聊天资料。请选择另一个名字。 No comment provided by engineer. + + You are a contributor + new chat alert + + + You are a member + 你是成员 + new chat alert + + + You are a moderator + 你是协管 + new chat alert + + + You are a subscriber + 你是订阅者 + new chat alert + You are already connected to %@. 您已经连接到 %@。 @@ -10662,6 +10691,21 @@ Repeat join request? 重复加入请求? new chat sheet title + + You are an admin + 你是管理员 + new chat alert + + + You are an observer + 你是观察者 + new chat alert + + + You are an owner + 你是群主 + new chat alert + You are connected to the server used to receive messages from this connection. 你已连接到用于接收该连接消息的服务器。 @@ -11662,6 +11706,10 @@ pref value 已转发 No comment provided by engineer. + + forwarded from + No comment provided by engineer. + group @@ -12070,9 +12118,9 @@ time to disappear 已保存 No comment provided by engineer. - - saved from %@ - 保存自 %@ + + saved from + 保存自 No comment provided by engineer. @@ -12168,6 +12216,10 @@ last received msg: %2$@ 未受保护 No comment provided by engineer. + + until you can register a SimpleX domain + No comment provided by engineer. + updated channel profile 频道更新了频道资料 diff --git a/apps/ios/bg.lproj/Localizable.strings b/apps/ios/bg.lproj/Localizable.strings index e27ef696d4..4eecbe2fdc 100644 --- a/apps/ios/bg.lproj/Localizable.strings +++ b/apps/ios/bg.lproj/Localizable.strings @@ -3516,10 +3516,10 @@ chat item action */ "Saved" = "Запазено"; /* No comment provided by engineer. */ -"Saved from" = "Запазено от"; +"saved from" = "запазено от"; /* No comment provided by engineer. */ -"saved from" = "запазено от"; +"Saved from" = "Запазено от"; /* message info title */ "Saved message" = "Запазено съобщение"; @@ -4393,6 +4393,9 @@ server test failure */ /* No comment provided by engineer. */ "You already have a chat profile with the same display name. Please choose another name." = "Вече имате чат профил със същото име. Моля, изберете друго име."; +/* new chat alert */ +"You are a member" = "Вие сте член"; + /* No comment provided by engineer. */ "You are already connected to %@." = "Вече сте вече свързани с %@."; @@ -4414,6 +4417,15 @@ server test failure */ /* new chat sheet title */ "You are already joining the group!\nRepeat join request?" = "Вече се присъединихте към групата!\nИзпрати отново заявката за присъединяване?"; +/* new chat alert */ +"You are an admin" = "Вие сте админ"; + +/* new chat alert */ +"You are an observer" = "Вие сте наблюдател"; + +/* new chat alert */ +"You are an owner" = "Вие сте собственик"; + /* No comment provided by engineer. */ "You are invited to group" = "Поканени сте в групата"; diff --git a/apps/ios/cs.lproj/Localizable.strings b/apps/ios/cs.lproj/Localizable.strings index 165177876c..d9d723be36 100644 --- a/apps/ios/cs.lproj/Localizable.strings +++ b/apps/ios/cs.lproj/Localizable.strings @@ -3505,9 +3505,21 @@ server test failure */ /* No comment provided by engineer. */ "You already have a chat profile with the same display name. Please choose another name." = "Již máte profil chatu se stejným zobrazovacím názvem. Zvolte prosím jiné jméno."; +/* new chat alert */ +"You are a member" = "Jste člen"; + /* No comment provided by engineer. */ "You are already connected to %@." = "Již jste připojeni k %@."; +/* new chat alert */ +"You are an admin" = "Jste správce"; + +/* new chat alert */ +"You are an observer" = "Jste pozorovatel"; + +/* new chat alert */ +"You are an owner" = "Jste vlastník"; + /* No comment provided by engineer. */ "You are invited to group" = "Jste pozváni do skupiny"; diff --git a/apps/ios/de.lproj/Localizable.strings b/apps/ios/de.lproj/Localizable.strings index dda49b67b8..7d6fda744f 100644 --- a/apps/ios/de.lproj/Localizable.strings +++ b/apps/ios/de.lproj/Localizable.strings @@ -4464,15 +4464,9 @@ alert button */ /* authentication reason */ "Open migration to another device" = "Migration auf ein anderes Gerät öffnen"; -/* new chat action */ -"Open new channel" = "Neuen Kanal öffnen"; - /* new chat action */ "Open new chat" = "Neuen Chat öffnen"; -/* new chat action */ -"Open new group" = "Neue Gruppe öffnen"; - /* No comment provided by engineer. */ "Open Settings" = "Geräte-Einstellungen öffnen"; @@ -5343,10 +5337,10 @@ chat item action */ "Saved" = "Abgespeichert"; /* No comment provided by engineer. */ -"Saved from" = "Abgespeichert von"; +"saved from" = "abgespeichert von"; /* No comment provided by engineer. */ -"saved from" = "abgespeichert von"; +"Saved from" = "Abgespeichert von"; /* message info title */ "Saved message" = "Gespeicherte Nachricht"; @@ -7013,6 +7007,18 @@ alert title */ /* No comment provided by engineer. */ "You already have a chat profile with the same display name. Please choose another name." = "Sie haben schon ein Chat-Profil mit dem gleichen Anzeigenamen. Bitte wählen Sie einen anderen Namen aus."; +/* new chat alert */ +"You are a contributor" = "Sie sind Mitwirkender"; + +/* new chat alert */ +"You are a member" = "Sie sind Mitglied"; + +/* new chat alert */ +"You are a moderator" = "Sie sind Moderator"; + +/* new chat alert */ +"You are a subscriber" = "Sie sind Abonnent"; + /* No comment provided by engineer. */ "You are already connected to %@." = "Sie sind bereits mit %@ verbunden."; @@ -7037,6 +7043,15 @@ alert title */ /* new chat sheet title */ "You are already joining the group!\nRepeat join request?" = "Sie sind bereits Mitglied dieser Gruppe!\nVerbindungsanfrage wiederholen?"; +/* new chat alert */ +"You are an admin" = "Sie sind Admin"; + +/* new chat alert */ +"You are an observer" = "Sie sind Beobachter"; + +/* new chat alert */ +"You are an owner" = "Sie sind Eigentümer"; + /* subscription status explanation */ "You are connected to the server used to receive messages from this connection." = "Sie sind mit dem Server verbunden, der für den Empfang von Nachrichten dieser Verbindung genutzt wird."; diff --git a/apps/ios/es.lproj/Localizable.strings b/apps/ios/es.lproj/Localizable.strings index 5c72f88b2c..cca9ef9ba5 100644 --- a/apps/ios/es.lproj/Localizable.strings +++ b/apps/ios/es.lproj/Localizable.strings @@ -4464,15 +4464,9 @@ alert button */ /* authentication reason */ "Open migration to another device" = "Abrir menú migración a otro dispositivo"; -/* new chat action */ -"Open new channel" = "Abrir canal nuevo"; - /* new chat action */ "Open new chat" = "Abrir chat nuevo"; -/* new chat action */ -"Open new group" = "Abrir grupo nuevo"; - /* No comment provided by engineer. */ "Open Settings" = "Abrir Configuración"; @@ -5343,10 +5337,10 @@ chat item action */ "Saved" = "Guardado"; /* No comment provided by engineer. */ -"Saved from" = "Guardado desde"; +"saved from" = "Guardado desde"; /* No comment provided by engineer. */ -"saved from" = "Guardado desde"; +"Saved from" = "Guardado desde"; /* message info title */ "Saved message" = "Mensaje guardado"; @@ -7013,6 +7007,18 @@ alert title */ /* No comment provided by engineer. */ "You already have a chat profile with the same display name. Please choose another name." = "Ya tienes un perfil con este nombre mostrado. Por favor, elige otro nombre."; +/* new chat alert */ +"You are a contributor" = "Eres colaborador"; + +/* new chat alert */ +"You are a member" = "Eres miembro"; + +/* new chat alert */ +"You are a moderator" = "Eres moderador"; + +/* new chat alert */ +"You are a subscriber" = "Eres suscriptor"; + /* No comment provided by engineer. */ "You are already connected to %@." = "Ya estás conectado con %@."; @@ -7037,6 +7043,15 @@ alert title */ /* new chat sheet title */ "You are already joining the group!\nRepeat join request?" = "¡En proceso de unirte al grupo!\n¿Repetir solicitud de admisión?"; +/* new chat alert */ +"You are an admin" = "Eres administrador"; + +/* new chat alert */ +"You are an observer" = "Eres observador"; + +/* new chat alert */ +"You are an owner" = "Eres propietario"; + /* subscription status explanation */ "You are connected to the server used to receive messages from this connection." = "Estás conectado al servidor usado para recibir mensajes de esta conexión."; diff --git a/apps/ios/fi.lproj/Localizable.strings b/apps/ios/fi.lproj/Localizable.strings index dfe1b6479d..0ec2565331 100644 --- a/apps/ios/fi.lproj/Localizable.strings +++ b/apps/ios/fi.lproj/Localizable.strings @@ -3141,9 +3141,21 @@ server test failure */ /* No comment provided by engineer. */ "You already have a chat profile with the same display name. Please choose another name." = "Sinulla on jo keskusteluprofiili samalla näyttönimellä. Valitse toinen nimi."; +/* new chat alert */ +"You are a member" = "Olet jäsen"; + /* No comment provided by engineer. */ "You are already connected to %@." = "Olet jo muodostanut yhteyden %@:n kanssa."; +/* new chat alert */ +"You are an admin" = "Olet ylläpitäjä"; + +/* new chat alert */ +"You are an observer" = "Olet tarkkailija"; + +/* new chat alert */ +"You are an owner" = "Olet omistaja"; + /* No comment provided by engineer. */ "You are invited to group" = "Sinut on kutsuttu ryhmään"; diff --git a/apps/ios/fr.lproj/Localizable.strings b/apps/ios/fr.lproj/Localizable.strings index c3bea2ff40..65e81432be 100644 --- a/apps/ios/fr.lproj/Localizable.strings +++ b/apps/ios/fr.lproj/Localizable.strings @@ -4464,15 +4464,9 @@ alert button */ /* authentication reason */ "Open migration to another device" = "Ouvrir le transfert vers un autre appareil"; -/* new chat action */ -"Open new channel" = "Ouvrir un nouveau canal"; - /* new chat action */ "Open new chat" = "Ouvrir une nouvelle conversation"; -/* new chat action */ -"Open new group" = "Ouvrir le nouveau groupe"; - /* No comment provided by engineer. */ "Open Settings" = "Ouvrir les Paramètres"; @@ -5343,10 +5337,10 @@ chat item action */ "Saved" = "Enregistré"; /* No comment provided by engineer. */ -"Saved from" = "Enregistré depuis"; +"saved from" = "enregistré à partir de"; /* No comment provided by engineer. */ -"saved from" = "enregistré à partir de"; +"Saved from" = "Enregistré depuis"; /* message info title */ "Saved message" = "Message enregistré"; @@ -7013,6 +7007,18 @@ alert title */ /* No comment provided by engineer. */ "You already have a chat profile with the same display name. Please choose another name." = "Vous avez déjà un profil de messagerie avec le même nom d’affichage. Veuillez choisir un autre nom."; +/* new chat alert */ +"You are a contributor" = "Vous êtes contributeur"; + +/* new chat alert */ +"You are a member" = "Vous êtes membre"; + +/* new chat alert */ +"You are a moderator" = "Vous êtes modérateur"; + +/* new chat alert */ +"You are a subscriber" = "Vous êtes abonné·e"; + /* No comment provided by engineer. */ "You are already connected to %@." = "Vous êtes déjà connecté·e à %@ via ce lien."; @@ -7037,6 +7043,15 @@ alert title */ /* new chat sheet title */ "You are already joining the group!\nRepeat join request?" = "Vous êtes déjà membre de ce groupe !\nRépéter la demande d'adhésion ?"; +/* new chat alert */ +"You are an admin" = "Vous êtes admin"; + +/* new chat alert */ +"You are an observer" = "Vous êtes observateur"; + +/* new chat alert */ +"You are an owner" = "Vous êtes propriétaire"; + /* subscription status explanation */ "You are connected to the server used to receive messages from this connection." = "Vous êtes connecté au serveur utilisé pour recevoir les messages de cette connexion."; diff --git a/apps/ios/hu.lproj/Localizable.strings b/apps/ios/hu.lproj/Localizable.strings index 873fc56584..a468ad982b 100644 --- a/apps/ios/hu.lproj/Localizable.strings +++ b/apps/ios/hu.lproj/Localizable.strings @@ -4464,15 +4464,9 @@ alert button */ /* authentication reason */ "Open migration to another device" = "Átköltöztetés indítása egy másik eszközre"; -/* new chat action */ -"Open new channel" = "Új csatorna megnyitása"; - /* new chat action */ "Open new chat" = "Új csevegés megnyitása"; -/* new chat action */ -"Open new group" = "Új csoport megnyitása"; - /* No comment provided by engineer. */ "Open Settings" = "Beállítások megnyitása"; @@ -5343,10 +5337,10 @@ chat item action */ "Saved" = "Mentett"; /* No comment provided by engineer. */ -"Saved from" = "Mentve innen"; +"saved from" = "mentve innen:"; /* No comment provided by engineer. */ -"saved from" = "mentve innen:"; +"Saved from" = "Mentve innen"; /* message info title */ "Saved message" = "Mentett üzenet"; @@ -7013,6 +7007,18 @@ alert title */ /* No comment provided by engineer. */ "You already have a chat profile with the same display name. Please choose another name." = "Már van egy csevegési profil ugyanezzel a megjelenítendő névvel. Válasszon egy másik nevet."; +/* new chat alert */ +"You are a contributor" = "Ön közreműködő"; + +/* new chat alert */ +"You are a member" = "Ön tag"; + +/* new chat alert */ +"You are a moderator" = "Ön moderátor"; + +/* new chat alert */ +"You are a subscriber" = "Ön feliratkozó"; + /* No comment provided by engineer. */ "You are already connected to %@." = "Ön már kapcsolódott a következőhöz: %@."; @@ -7037,6 +7043,15 @@ alert title */ /* new chat sheet title */ "You are already joining the group!\nRepeat join request?" = "A csatlakozás már folyamatban van a csoporthoz!\nMegismétli a csatlakozási kérést?"; +/* new chat alert */ +"You are an admin" = "Ön adminisztrátor"; + +/* new chat alert */ +"You are an observer" = "Ön megfigyelő"; + +/* new chat alert */ +"You are an owner" = "Ön tulajdonos"; + /* subscription status explanation */ "You are connected to the server used to receive messages from this connection." = "Ön kapcsolódott ahhoz a kiszolgálóhoz, amely az adott partnerétől érkező üzenetek fogadására szolgál."; diff --git a/apps/ios/it.lproj/Localizable.strings b/apps/ios/it.lproj/Localizable.strings index c57daef544..6a36b39e96 100644 --- a/apps/ios/it.lproj/Localizable.strings +++ b/apps/ios/it.lproj/Localizable.strings @@ -4464,15 +4464,9 @@ alert button */ /* authentication reason */ "Open migration to another device" = "Apri migrazione ad un altro dispositivo"; -/* new chat action */ -"Open new channel" = "Apri il nuovo canale"; - /* new chat action */ "Open new chat" = "Apri la nuova chat"; -/* new chat action */ -"Open new group" = "Apri il nuovo gruppo"; - /* No comment provided by engineer. */ "Open Settings" = "Apri le impostazioni"; @@ -5343,10 +5337,10 @@ chat item action */ "Saved" = "Salvato"; /* No comment provided by engineer. */ -"Saved from" = "Salvato da"; +"saved from" = "salvato da"; /* No comment provided by engineer. */ -"saved from" = "salvato da"; +"Saved from" = "Salvato da"; /* message info title */ "Saved message" = "Messaggio salvato"; @@ -7013,6 +7007,18 @@ alert title */ /* No comment provided by engineer. */ "You already have a chat profile with the same display name. Please choose another name." = "Hai già un profilo chat con lo stesso nome da mostrare. Scegli un altro nome."; +/* new chat alert */ +"You are a contributor" = "Sei un collaboratore"; + +/* new chat alert */ +"You are a member" = "Sei un membro"; + +/* new chat alert */ +"You are a moderator" = "Sei un moderatore"; + +/* new chat alert */ +"You are a subscriber" = "Sei iscritto/a"; + /* No comment provided by engineer. */ "You are already connected to %@." = "Sei già connesso/a a %@."; @@ -7037,6 +7043,15 @@ alert title */ /* new chat sheet title */ "You are already joining the group!\nRepeat join request?" = "Stai già entrando nel gruppo!\nRipetere la richiesta di ingresso?"; +/* new chat alert */ +"You are an admin" = "Sei un amministratore"; + +/* new chat alert */ +"You are an observer" = "Sei un osservatore"; + +/* new chat alert */ +"You are an owner" = "Sei un proprietario"; + /* subscription status explanation */ "You are connected to the server used to receive messages from this connection." = "Sei connesso/a al server usato per ricevere messaggi da questa connessione."; diff --git a/apps/ios/ja.lproj/Localizable.strings b/apps/ios/ja.lproj/Localizable.strings index 60ef7e2d36..9d5d160b0a 100644 --- a/apps/ios/ja.lproj/Localizable.strings +++ b/apps/ios/ja.lproj/Localizable.strings @@ -3475,9 +3475,21 @@ server test failure */ /* No comment provided by engineer. */ "You already have a chat profile with the same display name. Please choose another name." = "同じ表示名前のチャットプロフィールが既にあります。別のを選んでください。"; +/* new chat alert */ +"You are a member" = "あなたはメンバーです"; + /* No comment provided by engineer. */ "You are already connected to %@." = "すでに %@ に接続されています。"; +/* new chat alert */ +"You are an admin" = "あなたは管理者です"; + +/* new chat alert */ +"You are an observer" = "あなたはオブザーバーです"; + +/* new chat alert */ +"You are an owner" = "あなたはオーナーです"; + /* No comment provided by engineer. */ "You are invited to group" = "グループ招待が届きました"; diff --git a/apps/ios/nl.lproj/Localizable.strings b/apps/ios/nl.lproj/Localizable.strings index d644779e7d..4161bac952 100644 --- a/apps/ios/nl.lproj/Localizable.strings +++ b/apps/ios/nl.lproj/Localizable.strings @@ -4408,10 +4408,10 @@ chat item action */ "Saved" = "Opgeslagen"; /* No comment provided by engineer. */ -"Saved from" = "Opgeslagen van"; +"saved from" = "opgeslagen van"; /* No comment provided by engineer. */ -"saved from" = "opgeslagen van"; +"Saved from" = "Opgeslagen van"; /* message info title */ "Saved message" = "Opgeslagen bericht"; @@ -5706,6 +5706,12 @@ server test failure */ /* No comment provided by engineer. */ "You already have a chat profile with the same display name. Please choose another name." = "Je hebt al een chatprofiel met dezelfde weergave naam. Kies een andere naam."; +/* new chat alert */ +"You are a member" = "Je bent lid"; + +/* new chat alert */ +"You are a moderator" = "Je bent moderator"; + /* No comment provided by engineer. */ "You are already connected to %@." = "U bent al verbonden met %@."; @@ -5730,6 +5736,15 @@ server test failure */ /* new chat sheet title */ "You are already joining the group!\nRepeat join request?" = "Je sluit je al aan bij de groep!\nDeelnameverzoek herhalen?"; +/* new chat alert */ +"You are an admin" = "Je bent beheerder"; + +/* new chat alert */ +"You are an observer" = "Je bent waarnemer"; + +/* new chat alert */ +"You are an owner" = "Je bent eigenaar"; + /* No comment provided by engineer. */ "You are invited to group" = "Je bent uitgenodigd voor de groep"; diff --git a/apps/ios/pl.lproj/Localizable.strings b/apps/ios/pl.lproj/Localizable.strings index a41a8bc0ea..3288d2545b 100644 --- a/apps/ios/pl.lproj/Localizable.strings +++ b/apps/ios/pl.lproj/Localizable.strings @@ -3900,9 +3900,6 @@ alert button */ /* new chat action */ "Open new chat" = "Otwórz nowy czat"; -/* new chat action */ -"Open new group" = "Otwórz nową grupę"; - /* No comment provided by engineer. */ "Open Settings" = "Otwórz Ustawienia"; @@ -4643,10 +4640,10 @@ chat item action */ "Saved" = "Zapisane"; /* No comment provided by engineer. */ -"Saved from" = "Zapisane od"; +"saved from" = "zapisane od"; /* No comment provided by engineer. */ -"saved from" = "zapisane od"; +"Saved from" = "Zapisane od"; /* message info title */ "Saved message" = "Zachowano wiadomość"; @@ -6056,6 +6053,12 @@ alert title */ /* No comment provided by engineer. */ "You already have a chat profile with the same display name. Please choose another name." = "Masz już profil czatu o tej samej nazwie wyświetlanej. Proszę wybrać inną nazwę."; +/* new chat alert */ +"You are a member" = "Jesteś członkiem"; + +/* new chat alert */ +"You are a moderator" = "Jesteś moderatorem"; + /* No comment provided by engineer. */ "You are already connected to %@." = "Jesteś już połączony z %@."; @@ -6080,6 +6083,15 @@ alert title */ /* new chat sheet title */ "You are already joining the group!\nRepeat join request?" = "Już dołączasz do grupy!\nPowtórzyć prośbę dołączenia?"; +/* new chat alert */ +"You are an admin" = "Jesteś administratorem"; + +/* new chat alert */ +"You are an observer" = "Jesteś obserwatorem"; + +/* new chat alert */ +"You are an owner" = "Jesteś właścicielem"; + /* subscription status explanation */ "You are connected to the server used to receive messages from this connection." = "Jesteś połączony z serwerem służącym do odbierania wiadomości z tego połączenia."; diff --git a/apps/ios/product/flows/connection.md b/apps/ios/product/flows/connection.md index 115e420f7c..7073e07070 100644 --- a/apps/ios/product/flows/connection.md +++ b/apps/ios/product/flows/connection.md @@ -113,7 +113,7 @@ Establishing contact between two SimpleX Chat users. SimpleX uses no user identi 1. When connecting to a channel link (`GroupShortLinkInfo.direct == false`): 2. `apiPrepareGroup(connLink:directLink:groupShortLinkData:)` is called with `directLink: false`, preparing the channel locally. 3. `groupShortLinkInfo.groupRelays` (hostnames) stored in `ChatModel.shared.channelRelayHostnames[groupId]`. -4. Pre-join UI shows channel icon and "Open new channel" (not "Open new group"). +4. Pre-join UI shows channel icon and "Open channel" (not "Open group"). 5. `apiConnectPreparedGroup(groupId:incognito:msg:)` returns `(GroupInfo, [RelayConnectionResult])`. 6. `RelayConnectionResult` contains `relayMember: GroupMember` and optional `relayError: ChatError?` per relay. 7. Relay members are upserted to `chatModel.groupMembers`; `channelRelayHostnames` entry is cleared. diff --git a/apps/ios/product/views/new-chat.md b/apps/ios/product/views/new-chat.md index 1ab84c098a..0d1e384325 100644 --- a/apps/ios/product/views/new-chat.md +++ b/apps/ios/product/views/new-chat.md @@ -118,10 +118,18 @@ When `planAndConnect` encounters a `.simplexLink(_, .relay, _, _)`, it shows a " | Context | Channel behavior | Group behavior | |---|---|---| | Prepare alert icon | `antenna.radiowaves.left.and.right.circle.fill` | `person.2.circle.fill` | -| Prepare alert title | "Open new channel" | "Open new group" | +| Prepare alert title | "Open channel" | "Open group" | | Error text | "Error opening channel" | "Error opening group" | | Own-link confirm | "This is your link for channel" with only "Open channel" + "Cancel" (no incognito/profile options) | Full incognito/profile selection | -| Known group alert | "Open channel" / "Open new channel" | "Open group" / "Open new group" | +| Known group alert | "Open channel", with the membership role line | "Open group", with the membership role line | + +The known group alert shows an information line with the user's role, in the +secondary color (matching the subscriber count): "You are a subscriber" / +"You are a contributor" for channels, "You are an observer" / "You are a +member" for groups, and "You are a moderator" / "You are an admin" / +"You are an owner" for both. The line is omitted for prepared chats +(`nextConnectPrepared`) and business chats, so the prepare and known alerts +differ only by this line. ### Pre-Join Relay Info diff --git a/apps/ios/ru.lproj/Localizable.strings b/apps/ios/ru.lproj/Localizable.strings index 389cce9efe..db62641eb8 100644 --- a/apps/ios/ru.lproj/Localizable.strings +++ b/apps/ios/ru.lproj/Localizable.strings @@ -4464,15 +4464,9 @@ alert button */ /* authentication reason */ "Open migration to another device" = "Открытие миграции на другое устройство"; -/* new chat action */ -"Open new channel" = "Открыть новый канал"; - /* new chat action */ "Open new chat" = "Открыть новый чат"; -/* new chat action */ -"Open new group" = "Открыть новую группу"; - /* No comment provided by engineer. */ "Open Settings" = "Открыть Настройки"; @@ -5343,10 +5337,10 @@ chat item action */ "Saved" = "Сохранено"; /* No comment provided by engineer. */ -"Saved from" = "Сохранено из"; +"saved from" = "сохранено из"; /* No comment provided by engineer. */ -"saved from" = "сохранено из"; +"Saved from" = "Сохранено из"; /* message info title */ "Saved message" = "Сохранённое сообщение"; @@ -7013,6 +7007,18 @@ alert title */ /* No comment provided by engineer. */ "You already have a chat profile with the same display name. Please choose another name." = "У Вас уже есть профиль с таким именем. Пожалуйста, выберите другое имя."; +/* new chat alert */ +"You are a contributor" = "Вы соавтор"; + +/* new chat alert */ +"You are a member" = "Вы член группы"; + +/* new chat alert */ +"You are a moderator" = "Вы модератор"; + +/* new chat alert */ +"You are a subscriber" = "Вы подписчик"; + /* No comment provided by engineer. */ "You are already connected to %@." = "Вы уже соединены с контактом %@."; @@ -7037,6 +7043,15 @@ alert title */ /* new chat sheet title */ "You are already joining the group!\nRepeat join request?" = "Вы уже вступаете в группу!\nПовторить запрос на вступление?"; +/* new chat alert */ +"You are an admin" = "Вы админ"; + +/* new chat alert */ +"You are an observer" = "Вы читатель"; + +/* new chat alert */ +"You are an owner" = "Вы владелец"; + /* subscription status explanation */ "You are connected to the server used to receive messages from this connection." = "Вы подключены к серверу, используемому для приёма сообщений от этого соединения."; diff --git a/apps/ios/spec/client/navigation.md b/apps/ios/spec/client/navigation.md index 920780cc0f..64d0940e39 100644 --- a/apps/ios/spec/client/navigation.md +++ b/apps/ios/spec/client/navigation.md @@ -344,7 +344,7 @@ Similarly, in `planAndConnect()` (`NewChatView.swift`), `.simplexLink(_, .relay, When `groupShortLinkInfo?.direct == false` (channel relay link), the prepare alert uses: - Channel icon: `antenna.radiowaves.left.and.right.circle.fill` -- Title: "Open new channel" +- Title: "Open channel" - Error: "Error opening channel" - `apiPrepareGroup` call passes `directLink: false` - Stores `groupShortLinkInfo.groupRelays` in `ChatModel.shared.channelRelayHostnames` @@ -355,7 +355,7 @@ For channels: shows "This is your link for channel" with only "Open channel" + " ### Known Group Alert (`showOpenKnownGroupAlert`) -For channels (`groupInfo.useRelays`): titles become "Open channel" / "Open new channel". +For channels (`groupInfo.useRelays`): the title is "Open channel"; for groups, "Open group"; business chats keep "Open chat" / "Open new chat". Unless the chat is merely prepared (`nextConnectPrepared`) or a business chat, the alert shows an information line with the user's membership role (`memberRoleInformation`) in the secondary color: subscriber/contributor for channels, observer/member for groups, moderator/admin/owner for both. --- diff --git a/apps/ios/th.lproj/Localizable.strings b/apps/ios/th.lproj/Localizable.strings index 8114685292..087baff8d8 100644 --- a/apps/ios/th.lproj/Localizable.strings +++ b/apps/ios/th.lproj/Localizable.strings @@ -3045,9 +3045,21 @@ server test failure */ /* No comment provided by engineer. */ "You already have a chat profile with the same display name. Please choose another name." = "คุณมีโปรไฟล์แชทที่ใช้ชื่อแสดงเดียวกันอยู่แล้ว กรุณาเลือกชื่ออื่น"; +/* new chat alert */ +"You are a member" = "คุณเป็นสมาชิก"; + /* No comment provided by engineer. */ "You are already connected to %@." = "คุณได้เชื่อมต่อกับ %@ แล้ว"; +/* new chat alert */ +"You are an admin" = "คุณเป็นผู้ดูแลระบบ"; + +/* new chat alert */ +"You are an observer" = "คุณเป็นผู้สังเกตการณ์"; + +/* new chat alert */ +"You are an owner" = "คุณเป็นเจ้าของ"; + /* No comment provided by engineer. */ "You are invited to group" = "คุณได้รับเชิญให้เข้าร่วมกลุ่ม"; diff --git a/apps/ios/tr.lproj/Localizable.strings b/apps/ios/tr.lproj/Localizable.strings index 4ac1840c6e..9444453779 100644 --- a/apps/ios/tr.lproj/Localizable.strings +++ b/apps/ios/tr.lproj/Localizable.strings @@ -3886,9 +3886,6 @@ alert button */ /* new chat action */ "Open new chat" = "Yeni sohbet aç"; -/* new chat action */ -"Open new group" = "Yeni grup aç"; - /* No comment provided by engineer. */ "Open Settings" = "Ayarları aç"; @@ -4626,10 +4623,10 @@ chat item action */ "Saved" = "Kaydedildi"; /* No comment provided by engineer. */ -"Saved from" = "Tarafından kaydedildi"; +"saved from" = "kaydedildi:"; /* No comment provided by engineer. */ -"saved from" = "kaydedildi:"; +"Saved from" = "Tarafından kaydedildi"; /* message info title */ "Saved message" = "Kaydedilmiş mesaj"; @@ -6009,6 +6006,12 @@ alert title */ /* No comment provided by engineer. */ "You already have a chat profile with the same display name. Please choose another name." = "Aynı görünen ada sahip bir konuşma profilin zaten var. Lütfen başka bir ad seç."; +/* new chat alert */ +"You are a member" = "Üyesiniz"; + +/* new chat alert */ +"You are a moderator" = "Moderatörsünüz"; + /* No comment provided by engineer. */ "You are already connected to %@." = "Zaten %@'a bağlısınız."; @@ -6033,6 +6036,15 @@ alert title */ /* new chat sheet title */ "You are already joining the group!\nRepeat join request?" = "Gruba zaten katılıyorsunuz!\nKatılma isteği tekrarlansın mı?"; +/* new chat alert */ +"You are an admin" = "Yöneticisiniz"; + +/* new chat alert */ +"You are an observer" = "Gözlemcisiniz"; + +/* new chat alert */ +"You are an owner" = "Sahipsiniz"; + /* No comment provided by engineer. */ "You are invited to group" = "Gruba davet edildiniz"; diff --git a/apps/ios/uk.lproj/Localizable.strings b/apps/ios/uk.lproj/Localizable.strings index cd46b33067..9046e21f9b 100644 --- a/apps/ios/uk.lproj/Localizable.strings +++ b/apps/ios/uk.lproj/Localizable.strings @@ -3919,9 +3919,6 @@ alert button */ /* new chat action */ "Open new chat" = "Відкрити новий чат"; -/* new chat action */ -"Open new group" = "Відкрити нову групу"; - /* No comment provided by engineer. */ "Open Settings" = "Відкрийте Налаштування"; @@ -4647,10 +4644,10 @@ chat item action */ "Saved" = "Збережено"; /* No comment provided by engineer. */ -"Saved from" = "Збережено з"; +"saved from" = "збережено з"; /* No comment provided by engineer. */ -"saved from" = "збережено з"; +"Saved from" = "Збережено з"; /* message info title */ "Saved message" = "Збережене повідомлення"; @@ -6024,6 +6021,12 @@ alert title */ /* No comment provided by engineer. */ "You already have a chat profile with the same display name. Please choose another name." = "Ви вже маєте профіль у чаті з таким самим іменем. Будь ласка, виберіть інше ім'я."; +/* new chat alert */ +"You are a member" = "Ви учасник"; + +/* new chat alert */ +"You are a moderator" = "Ви модератор"; + /* No comment provided by engineer. */ "You are already connected to %@." = "Ви вже підключені до %@."; @@ -6048,6 +6051,15 @@ alert title */ /* new chat sheet title */ "You are already joining the group!\nRepeat join request?" = "Ви вже приєдналися до групи!\nПовторити запит на приєднання?"; +/* new chat alert */ +"You are an admin" = "Ви адмін"; + +/* new chat alert */ +"You are an observer" = "Ви спостерігач"; + +/* new chat alert */ +"You are an owner" = "Ви власник"; + /* No comment provided by engineer. */ "You are invited to group" = "Запрошуємо вас до групи"; diff --git a/apps/ios/zh-Hans.lproj/Localizable.strings b/apps/ios/zh-Hans.lproj/Localizable.strings index b4ae54a6c5..4ef03cb433 100644 --- a/apps/ios/zh-Hans.lproj/Localizable.strings +++ b/apps/ios/zh-Hans.lproj/Localizable.strings @@ -4404,15 +4404,9 @@ alert button */ /* authentication reason */ "Open migration to another device" = "打开迁移到另一台设备"; -/* new chat action */ -"Open new channel" = "打开新频道"; - /* new chat action */ "Open new chat" = "打开新聊天"; -/* new chat action */ -"Open new group" = "打开新群"; - /* No comment provided by engineer. */ "Open Settings" = "打开设置"; @@ -5268,10 +5262,10 @@ chat item action */ "Saved" = "已保存"; /* No comment provided by engineer. */ -"Saved from" = "保存自"; +"saved from" = "保存自"; /* No comment provided by engineer. */ -"saved from" = "保存自"; +"Saved from" = "保存自"; /* message info title */ "Saved message" = "已保存的消息"; @@ -6874,6 +6868,15 @@ alert title */ /* No comment provided by engineer. */ "You already have a chat profile with the same display name. Please choose another name." = "您已经有一个显示名相同的聊天资料。请选择另一个名字。"; +/* new chat alert */ +"You are a member" = "你是成员"; + +/* new chat alert */ +"You are a moderator" = "你是协管"; + +/* new chat alert */ +"You are a subscriber" = "你是订阅者"; + /* No comment provided by engineer. */ "You are already connected to %@." = "您已经连接到 %@。"; @@ -6898,6 +6901,15 @@ alert title */ /* new chat sheet title */ "You are already joining the group!\nRepeat join request?" = "您已经加入了这个群组!\n重复加入请求?"; +/* new chat alert */ +"You are an admin" = "你是管理员"; + +/* new chat alert */ +"You are an observer" = "你是观察者"; + +/* new chat alert */ +"You are an owner" = "你是群主"; + /* subscription status explanation */ "You are connected to the server used to receive messages from this connection." = "你已连接到用于接收该连接消息的服务器。"; diff --git a/apps/multiplatform/common/build.gradle.kts b/apps/multiplatform/common/build.gradle.kts index ec4235d344..413667c968 100644 --- a/apps/multiplatform/common/build.gradle.kts +++ b/apps/multiplatform/common/build.gradle.kts @@ -211,7 +211,7 @@ afterEvaluate { val fontLtGtRegex = Regex("[^>]*>.*<font[^>]*>.*</font>.*") val unbracketedColorRegex = Regex("color=#[abcdefABCDEF0-9]{3,6}") val correctHtmlRegex = Regex("[^>]*>.*.*.*|[^>]*>.*.*.*|[^>]*>.*.*.*|[^>]*>.*]*>.*.*") - val possibleFormat = listOf("s", "d", "1\$s", "2\$s", "3\$s", "4\$s", "1\$d", "2\$d", "3\$d", "4\$d", "2s", "f") + val possibleFormat = listOf("s", "d", "1\$s", "2\$s", "3\$s", "4\$s", "1\$d", "2\$d", "3\$d", "4\$d", "1\$02d", "2\$02d", "3\$02d", "2s", "f") fun String.id(): String = replace(" Unit)? = null, connectOtherButton: String? = null, @@ -378,6 +379,7 @@ class AlertManager { information, textAlign = TextAlign.Center, style = MaterialTheme.typography.body2, + color = if (secondaryInformation) MaterialTheme.colors.secondary else Color.Unspecified, maxLines = 3, modifier = Modifier.fillMaxWidth() ) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ConnectPlan.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ConnectPlan.kt index 161681c91d..5e5769891a 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ConnectPlan.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ConnectPlan.kt @@ -656,11 +656,24 @@ private fun showOpenKnownGroupAlert(chatModel: ChatModel, rhId: Long?, close: (( }, nameCaption = planSimplexName?.shortStr, subtitle = subscriberCount, + information = if (groupInfo.nextConnectPrepared || groupInfo.businessChat != null) { + null + } else { + val isChannel = groupInfo.useRelays + generalGetString(when (groupInfo.membership.memberRole) { + GroupMemberRole.Observer -> if (isChannel) MR.strings.connect_plan_you_are_subscriber else MR.strings.connect_plan_you_are_observer + GroupMemberRole.Moderator -> MR.strings.connect_plan_you_are_moderator + GroupMemberRole.Admin -> MR.strings.connect_plan_you_are_admin + GroupMemberRole.Owner -> MR.strings.connect_plan_you_are_owner + else -> if (isChannel) MR.strings.connect_plan_you_are_contributor else MR.strings.connect_plan_you_are_member + }) + }, + secondaryInformation = true, confirmText = generalGetString( if (groupInfo.useRelays) { - if (groupInfo.nextConnectPrepared) MR.strings.connect_plan_open_new_channel else MR.strings.connect_plan_open_channel + MR.strings.connect_plan_open_channel } else if (groupInfo.businessChat == null) { - if (groupInfo.nextConnectPrepared) MR.strings.connect_plan_open_new_group else MR.strings.connect_plan_open_group + MR.strings.connect_plan_open_group } else { if (groupInfo.nextConnectPrepared) MR.strings.connect_plan_open_new_chat else MR.strings.connect_plan_open_chat } @@ -761,7 +774,7 @@ fun showPrepareGroupAlert( nameCaption = planSimplexName?.shortStr, subtitle = subscriberCount, information = ownerVerificationMessage(ownerVerification), - confirmText = generalGetString(if (isChannel) MR.strings.connect_plan_open_new_channel else MR.strings.connect_plan_open_new_group), + confirmText = generalGetString(if (isChannel) MR.strings.connect_plan_open_channel else MR.strings.connect_plan_open_group), onConfirm = { AlertManager.privacySensitive.hideAlert() withBGApi { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SetSimplexNameView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SetSimplexNameView.kt index 7ab1ffa33d..f2c7b02b42 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SetSimplexNameView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SetSimplexNameView.kt @@ -14,7 +14,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.* +import androidx.compose.ui.unit.* import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.* import chat.simplex.common.views.* @@ -24,10 +25,14 @@ import chat.simplex.res.MR import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource import kotlinx.coroutines.* +import kotlinx.datetime.* // Each dot-separated label is ASCII letters/digits with single internal hyphens (mirrors simplexmq SimplexName.hs nameLabelP). private val simplexNameLabelRegex = Regex("[A-Za-z0-9]+(-[A-Za-z0-9]+)*") +private val simplexNameSaleStart = LocalDateTime(2026, 12, 12, 18, 0).toInstant(TimeZone.UTC) +private const val SIMPLEX_DOMAINS_URL = "https://simplex.domains/" + // Set the user's own (prefix "@") or a channel's (prefix "#") SimpleX name. // The field is prefilled with the full prefixed name; `save` receives the encoded name (or null to // clear) and returns true on success (it shows its own error alert otherwise). @@ -128,6 +133,13 @@ fun SetSimplexDomainView( } } + fun saleCountdown(msRemaining: Long): String { + val total = (msRemaining / 1000).coerceAtLeast(0) + val days = total / 86400 + val dayStr = String.format(generalGetString(if (days == 1L) MR.strings.ttl_day else MR.strings.ttl_days), days) + return dayStr + " " + String.format(generalGetString(MR.strings.countdown_hrs_min_sec), total / 3600 % 24, total / 60 % 60, total % 60) + } + ModalView(close = { onClose(close) }, cardScreen = true) { ColumnWithScrollBar { AppBarTitle(title) @@ -163,7 +175,7 @@ fun SetSimplexDomainView( SettingsActionItem( painterResource(MR.images.ic_open_in_new), stringResource(MR.strings.register_test_name), - { openBrowserAlert("https://github.com/simplex-chat/simplex-chat/blob/master/docs/guide/register-simplex-name.md", uriHandler) }, + { openBrowserAlert("https://simplex.domains/#testing", uriHandler) }, textColor = MaterialTheme.colors.primary, iconColor = MaterialTheme.colors.primary ) @@ -179,6 +191,35 @@ fun SetSimplexDomainView( } } } + SectionDividerSpaced() + val msToSaleStart = remember { mutableStateOf(simplexNameSaleStart.toEpochMilliseconds() - System.currentTimeMillis()) } + LaunchedEffect(Unit) { + while (msToSaleStart.value > 0) { + delay(1000) + msToSaleStart.value = simplexNameSaleStart.toEpochMilliseconds() - System.currentTimeMillis() + } + } + SectionView(stringResource(MR.strings.simplex_name_sales)) { + SectionItemView { + Column(verticalArrangement = Arrangement.spacedBy(4.dp)) { + Text(saleCountdown(msToSaleStart.value)) + Text( + stringResource(if (msToSaleStart.value > 0) MR.strings.until_register_simplex_domain else MR.strings.update_app_register_simplex_domain), + color = MaterialTheme.colors.secondary, + fontSize = 12.sp + ) + } + } + } + SectionTextFooter(buildAnnotatedString { + append(generalGetString(MR.strings.simplex_name_sales_footer)) + append(" ") + withLink(LinkAnnotation.Url(SIMPLEX_DOMAINS_URL) { uriHandler.openUriCatching(SIMPLEX_DOMAINS_URL) }) { + withStyle(SpanStyle(color = MaterialTheme.colors.primary)) { + append("simplex.domains") + } + } + }) SectionBottomSpacer() } } diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml index f66d9c8f0f..0df6dafacb 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml @@ -1219,6 +1219,13 @@ تحديث إعدادات الشبكة؟ سيؤدي تحديث الإعدادات إلى إعادة توصيل العميل بجميع الخوادم. أنت المراقب + أنت المراقب + أنت عضو + أنت مُشرف + أنت المُدير + أنت المالك + أنت مشترك + أنت مساهم أنت مدعو إلى المجموعة في انتظار التأكيد… خطأ غير معروف في قاعدة البيانات: %s 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 9e1da97c27..b1661d195f 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -20,6 +20,11 @@ Open new chat Open group Open new group + You are an observer + You are a member + You are a moderator + You are an admin + You are an owner Invalid link Please check that SimpleX link is correct. @@ -398,6 +403,11 @@ Get SimpleX name (BETA) Channel SimpleX name How to register a test name + SimpleX name sale starts in + until you can register a SimpleX domain + Update the app to register a SimpleX domain + %1$02d hrs %2$02d min %3$02d sec + Crowdfunding investors can reserve names before the sale starts: Remove name Save Edit @@ -3171,6 +3181,8 @@ This is a chat relay address, it cannot be used to connect. Open channel Open new channel + You are a subscriber + You are a contributor Your channel %1$s!]]> Error opening channel diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/bg/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/bg/strings.xml index 65c19107c7..c79aa07a1e 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/bg/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/bg/strings.xml @@ -1301,6 +1301,11 @@ Отключи Ще трябва да се идентифицирате, когато стартирате или възобновите приложението след 30 секунди във фонов режим. вие сте наблюдател + Вие сте наблюдател + Вие сте член + Вие сте модератор + Вие сте админ + Вие сте собственик Видео се свържете с разработчиците на SimpleX Chat, за да задавате въпроси и да получавате актуализации;.]]> иска да се свърже с вас! diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/bn/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/bn/strings.xml index bb448339bd..dfcfb1685c 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/bn/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/bn/strings.xml @@ -23,6 +23,7 @@ কলটি গৃহীত হয়েছে পূর্বনির্ধারিত সার্ভারগুলি যুক্ত করুন অ্যাডমিন + আপনি একজন অ্যাডমিন স্বাগত বার্তা যুক্ত করুন প্রোফাইল যুক্ত করুন আনুষঙ্গিক রং diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/ca/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/ca/strings.xml index 5467ddf043..3d49c533ff 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/ca/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/ca/strings.xml @@ -1715,6 +1715,11 @@ La imatge no es pot descodificar. Si us plau, proveu amb una imatge diferent o contacteu amb els desenvolupadors. El vídeo no es pot descodificar. Si us plau, prova amb un vídeo diferent o contacta amb els desenvolupadors. ets observador + Ets observador + Ets membre + Ets moderador + Ets administrador + Ets propietari ets observador(a) Poseu-vos en contacte amb l\'administrador del grup. Només els propietaris del grup poden activar fitxers i mitjans. diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/cs/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/cs/strings.xml index 5dfe53fb1c..16241e07e2 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/cs/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/cs/strings.xml @@ -931,6 +931,11 @@ Moderovat Kontaktujte prosím správce skupiny. jste pozorovatel + Jste pozorovatel + Jste člen + Jste moderátor + Jste správce + Jste vlastník pozorovatel Zpráva bude smazána pro všechny členy. Zpráva bude pro všechny členy označena jako moderovaná. diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/da/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/da/strings.xml index 03c9a49278..82f8ae5770 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/da/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/da/strings.xml @@ -635,6 +635,8 @@ du forlod kan ikke sende beskeder du er observatør + Du er observatør + Du er administrator gennemgået af administratorer medlemmet har en gammel version Billede diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml index f62d8256ba..26ddf2107d 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml @@ -1012,6 +1012,13 @@ Moderieren Diese Nachricht wird für alle Mitglieder als moderiert gekennzeichnet. Sie sind Beobachter + Sie sind Beobachter + Sie sind Mitglied + Sie sind Moderator + Sie sind Admin + Sie sind Eigentümer + Sie sind Abonnent + Sie sind Mitwirkender Sie sind Beobachter Beobachter Anfängliche Rolle diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/el/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/el/strings.xml index 5caed9b913..73abd7d876 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/el/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/el/strings.xml @@ -2429,6 +2429,11 @@ Δεν είσαι συνδεδεμένος στον διακομιστή που χρησιμοποιείται για τη λήψη μηνυμάτων από αυτή τη σύνδεση (δεν υπάρχει συνδρομή). Δεν είσαι συνδεδεμένος σε αυτούς τους διακομιστές. Για την παράδοση μηνυμάτων σε αυτούς, χρησιμοποιείται ιδιωτική δρομολόγηση. είσαι παρατηρητής + Είσαι παρατηρητής + Είσαι μέλος + Είσαι διαχειριστής + Είσαι διαχειριστής + Είσαι ιδιοκτήτης είσαι παρατηρητής μπλόκαρες %s Μπορείς να το αλλάξεις στις ρυθμίσεις Εμφάνισης. diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml index 41cb15e0c6..4e63f2c76a 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml @@ -2665,6 +2665,13 @@ Espera respuesta eres suscriptor + Eres observador + Eres miembro + Eres moderador + Eres administrador + Eres propietario + Eres suscriptor + Eres colaborador Puedes compartir el enlace o código QR. Cualquiera podrá unirse al canal. Te conectaste al canal mediante este enlace de servidor. Tu canal diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/fa/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/fa/strings.xml index 71387e9561..b65fc5ab93 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/fa/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/fa/strings.xml @@ -437,6 +437,11 @@ تایید شما ممکن نیست؛ لطفا دوباره امتحان کنید. فایل شما ناظر هستید + شما ناظر هستید + شما عضو هستید + شما مدیر هستید + شما مدیر هستید + شما صاحب هستید چت پاک شود؟ تمام پیام‌ها حذف خواهند شد - این عمل قابل برگشت نیست! حذف diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/fi/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/fi/strings.xml index 7ae3f506a2..e5e7c525a5 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/fi/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/fi/strings.xml @@ -1169,6 +1169,10 @@ SimpleX-taustapalvelu – se kuluttaa muutaman prosentin akusta päivässä.]]> Avaa olet tarkkailija + Olet tarkkailija + Olet jäsen + Olet ylläpitäjä + Olet omistaja Liikaa videoita! Ääniviesti Odottaa videota diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/fr/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/fr/strings.xml index 10b0215d3b..1997a54dca 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/fr/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/fr/strings.xml @@ -929,6 +929,13 @@ Le message sera supprimé pour tous les membres. Le message sera marqué comme modéré pour tous les membres. vous êtes observateur + Vous êtes observateur + Vous êtes membre + Vous êtes modérateur(trice) + Vous êtes admin + Vous êtes propriétaire + Vous êtes abonné + Vous êtes contributeur Erreur lors de la mise à jour du lien de groupe Rôle initial Veuillez contacter l\'administrateur du groupe. diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/hi/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/hi/strings.xml index f3b1df5a40..3f17a6d8a7 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/hi/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/hi/strings.xml @@ -283,6 +283,9 @@ सदस्य को समूह से निकाल दिया जाएगा - इसे पूर्ववत नहीं किया जा सकता! सदस्य सदस्य + आप सदस्य हैं + आप व्यवस्थापक हैं + आप स्वामी हैं खोजें बंद है संपर्क पते के माध्यम से कनेक्ट करें? diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/hr/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/hr/strings.xml index b64dc65119..3493f56711 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/hr/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/hr/strings.xml @@ -1435,6 +1435,11 @@ Za pozive je potreban podrazumevani veb pretraživač. Molimo vas da konfigurišete podrazumevani pretraživač u sistemu i podelite više informacija sa programerima. odblokirali ste %s Vi ste posmatrač. + Vi ste posmatrač + Vi ste član + Vi ste moderator + Vi ste administrator + Vi ste vlasnik Unapređena privatnost i bezbednost Migriraj na drugi uređaj pomoću QR koda. odbijeno diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml index f6551d7572..2fca87fdcd 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml @@ -1127,6 +1127,13 @@ Üzenetek fogadása… %s és %s kapcsolódott Ön megfigyelő + Ön megfigyelő + Ön tag + Ön moderátor + Ön adminisztrátor + Ön tulajdonos + Ön feliratkozó + Ön közreműködő Port Jelkód beállítása Újdonságok diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/in/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/in/strings.xml index 544e6d572e..b5c35bc7c6 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/in/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/in/strings.xml @@ -2010,6 +2010,13 @@ Kunci salah atau alamat potongan berkas tidak dikenal - kemungkinan berkas dihapus. Versi server tidak kompatibel dengan pengaturan jaringan. Anda adalah pengamat + Anda adalah pengamat + Anda adalah anggota + Anda adalah moderator + Anda adalah admin + Anda adalah pemilik + Anda adalah pelanggan + Anda adalah kontributor Untuk memulai obrolan baru Video Koneksi yang Anda terima akan dibatalkan! diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml index d5a4080b61..eba8b408df 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml @@ -934,6 +934,13 @@ Il messaggio verrà eliminato per tutti i membri. Il messaggio sarà segnato come moderato per tutti i membri. sei un osservatore + Sei un osservatore + Sei un membro + Sei un moderatore + Sei un amministratore + Sei un proprietario + Sei iscritto/a + Sei un collaboratore Ruolo iniziale Errore nell\'aggiornamento del link del gruppo osservatore diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/iw/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/iw/strings.xml index efa9f0d1f8..8487614c9a 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/iw/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/iw/strings.xml @@ -1128,6 +1128,10 @@ %1$d הודעות שדולגו שבועות הינך צופה + הינך צופה + הינך חבר קבוצה + הינך מנהל + הינך בעלים אין באפשרותך לשלוח הודעות! סרטון נשלח הודעה קולית diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/ja/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/ja/strings.xml index 61e887f092..1e0686d2de 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/ja/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/ja/strings.xml @@ -990,6 +990,12 @@ ビデオ メッセージのハッシュ値問題 あなたはオブザーバーです + あなたはオブザーバーです + あなたはメンバーです + あなたはモデレーターです + あなたは管理者です + あなたはオーナーです + あなたは購読者です グループの管理者に連絡してください。 動画は相手がアップロードを完了した時点で受信するができます。 .onion hostを使用する、は「いいえ」に設定します。]]> diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/ko/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/ko/strings.xml index ea87347a13..a987a0c92e 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/ko/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/ko/strings.xml @@ -468,6 +468,10 @@ 나감 멤버 소유자 + 당신은 관찰자입니다 + 당신은 멤버입니다 + 당신은 관리자입니다 + 당신은 소유자입니다 그룹 삭제됨 초대됨 강퇴됨 diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/lt/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/lt/strings.xml index eeba2cb6c0..aa1da521bb 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/lt/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/lt/strings.xml @@ -1547,6 +1547,10 @@ Jums reikės autentifikuotis kai paleidžiate programėlę arba pratęsiate jos naudojimą po 30 sekundžių fone. Nėra istorijos esate stebėtojas + Esate stebėtojas + Esate narys + Esate administratorius + Esate savininkas (saugo tik grupės nariai) Jūsų SimpleX adresas Nuskanuoti serverio QR kodą diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/lv/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/lv/strings.xml index 673fe74c77..a6882871c7 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/lv/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/lv/strings.xml @@ -283,6 +283,7 @@ Nevar nosūtīt ziņu, jūs esat izgājis Nevar nosūtīt ziņu Jūs esat vērotājs + Jūs esat vērotājs Pārbaudīts ar administratoriem Nevar nosūtīt ziņu, dalībniekam ir veca versija Nevar Nosūtīt Komandas Brīdinājuma Teksts diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/ml/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/ml/strings.xml index 19aa92a4a0..008ad2bb81 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/ml/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/ml/strings.xml @@ -295,6 +295,9 @@ സ്വാഗതം! ഈ വാചകം ക്രമീകരണങ്ങളിൽ ലഭ്യമാണ് നിങ്ങൾ നിരീക്ഷകനാണ് + നിങ്ങൾ നിരീക്ഷകനാണ് + നിങ്ങൾ അംഗമാണ് + നിങ്ങൾ ഉടമയാണ് തീർപ്പാക്കാത്തത് സന്ദേശം അയയ്ക്കുക തത്സമയ സന്ദേശം അയയ്ക്കുക diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/nb-rNO/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/nb-rNO/strings.xml index 1275c31573..eeddfd1b38 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/nb-rNO/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/nb-rNO/strings.xml @@ -61,6 +61,7 @@ Legg til velkomstmelding Legg til dine teammedlemmer i samtalene. administrator + Du er administrator administratorer Administratorer kan blokkere ett medlem for alle. Administratorer kan lage lenker for å bli med i grupper. diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/nl/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/nl/strings.xml index 4db882608f..2ec65534a4 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/nl/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/nl/strings.xml @@ -936,6 +936,11 @@ Waarnemer jij bent waarnemer je bent waarnemer + Je bent waarnemer + Je bent lid + Je bent moderator + Je bent beheerder + Je bent eigenaar Systeem Audio en video oproepen Bevestig wachtwoord diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/pl/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/pl/strings.xml index 8792d7ffac..bf47cebdb5 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/pl/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/pl/strings.xml @@ -179,6 +179,11 @@ Oczekiwanie na film Oczekiwanie na film jesteś obserwatorem + Jesteś obserwatorem + Jesteś członkiem + Jesteś moderatorem + Jesteś administratorem + Jesteś właścicielem Jesteś obserwatorem Połączony Obecnie maksymalny obsługiwany rozmiar pliku to %1$s. diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/pt-rBR/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/pt-rBR/strings.xml index 256f388cf7..3b28d2fd20 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/pt-rBR/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/pt-rBR/strings.xml @@ -874,6 +874,13 @@ Trocar Totalmente descentralizado — visível apenas para os membros. você é um observador + Você é um observador + Você é um membro + Você é um moderador + Você é um administrador + Você é um proprietário + Você é um inscrito + Você é um colaborador Mensagem de voz (%1$s) Compartilhar link Para proteger sua privacidade, o SimpleX usa IDs separados para cada um dos seus contatos. diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/pt/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/pt/strings.xml index 08285dbe78..6ab12f76f6 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/pt/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/pt/strings.xml @@ -88,6 +88,9 @@ enviada você está convidado para o grupo você é observador + Você é observador + Você é membro + Você é administrador Notificações Desconectado Definir nome do contato… diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/ro/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/ro/strings.xml index dfde10dbbf..f2437af22b 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/ro/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/ro/strings.xml @@ -2195,6 +2195,11 @@ Se opresc conversațiile Total ești observator + Ești observator + Ești membru + Ești moderator + Ești administrator + Ești proprietar Videoclipul nu poate fi decodificat. Vă rugăm să încercați un alt videoclip sau să contactați dezvoltatorii. Puteți copia și micșora dimensiunea mesajului pentru a-l trimite. aștept răspunsul… diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/ru/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/ru/strings.xml index e769453e0a..44f3d3858e 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/ru/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/ru/strings.xml @@ -2771,6 +2771,13 @@ (от владельца) Ошибка при публикации канала Вы подписчик + Вы читатель + Вы член группы + Вы модератор + Вы админ + Вы владелец + Вы подписчик + Вы соавтор Новая одноразовая ссылка Или покажите QR лично или через видеозвонок. Используйте этот адрес в профиле социальных сетей, на сайте или в подписи email. diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/sk/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/sk/strings.xml index ed729b3ce2..dfd1f3d1f5 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/sk/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/sk/strings.xml @@ -595,6 +595,13 @@ Počiatočná rola člen moderátor + Ste pozorovateľ + Ste člen + Ste moderátor + Ste správca + Ste majiteľ + Ste odberateľ + Ste prispievateľ moderátori Nová skupinová rola: Moderátor Nová rola člena diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/th/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/th/strings.xml index ddc258490f..da2aea2868 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/th/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/th/strings.xml @@ -1078,6 +1078,10 @@ คุณไม่มีการแชท แชท คุณเป็นผู้สังเกตการณ์ + คุณเป็นผู้สังเกตการณ์ + คุณเป็นสมาชิก + คุณเป็นผู้ดูแลระบบ + คุณเป็นเจ้าของ ภาพไม่สามารถถอดรหัส ได้ โปรดลองใช้รูปภาพอื่นหรือติดต่อนักพัฒนา คุณไม่สามารถส่งข้อความได้! กําลังรอภาพ diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/tr/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/tr/strings.xml index 42eec6e0bc..bacc84b0f7 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/tr/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/tr/strings.xml @@ -842,6 +842,13 @@ Gruba davetlisiniz Hiç sohbetiniz yok Gözlemcisiniz + Gözlemcisiniz + Üyesiniz + Yöneticisiniz + Yöneticisiniz + Sahipsiniz + Abonesiniz + Katkıda bulunansınız sen gözlemcisin Güvenlik kodunu görüntüle Sesli mesaj gönderebilmeniz için kişinizin de sesli mesaj göndermesine izin vermeniz gerekir. diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/uk/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/uk/strings.xml index 5ef2a424cf..457fd4abe1 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/uk/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/uk/strings.xml @@ -1155,6 +1155,12 @@ Забагато зображень! Забагато відео! ви спостерігач + Ви спостерігач + Ви учасник + Ви модератор + Ви адміністратор + Ви власник + Ви автор кольоровий дзвінок завершено %1$s помилка дзвінка diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/vi/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/vi/strings.xml index ca361a494b..aac2109d11 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/vi/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/vi/strings.xml @@ -2213,6 +2213,12 @@ Bạn có thể tùy chỉnh các máy chủ thông qua cài đặt. Bạn có thể đặt tên kết nối, để nhớ xem đường dẫn đã được chia sẻ với ai. bạn là quan sát viên + Bạn là quan sát viên + Bạn là thành viên + Bạn là kiểm duyệt viên + Bạn là quản trị viên + Bạn là chủ sở hữu + Bạn là người theo dõi Bạn có thể sao chép và giảm kích thước tin nhắn để gửi nó đi. Bạn có thể bật chúng vào lúc sau thông qua cài đặt Quyền riêng tư & Bảo mật của ứng dụng. Bạn có thể ẩn hoặc tắt thông báo một hồ sơ người dùng - giữ nó trong phần menu. diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml index e4849cea39..2d51120ac2 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml @@ -927,6 +927,13 @@ 删除成员消息? 观察员 你是观察者 + 你是观察员 + 你是成员 + 你是协管 + 你是管理员 + 你是群主 + 你是订阅者 + 你是贡献者 更新群链接错误 你是观察员 初始角色 diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/zh-rTW/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/zh-rTW/strings.xml index e0f280ee2e..05e3cd8030 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/zh-rTW/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/zh-rTW/strings.xml @@ -926,6 +926,13 @@ 該訊息將對所有成員標記為已移除。 你是觀察員 你是觀察員 + 你是觀察員 + 你是成員 + 你是審核員 + 你是管理員 + 你是擁有者 + 你是訂閱者 + 你是貢獻者 觀察員 更新群組連接時出錯 請聯絡群組管理員。 diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/other/videoplayer/SkiaBitmapVideoSurface.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/other/videoplayer/SkiaBitmapVideoSurface.kt index f5bba2d344..26748b8425 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/other/videoplayer/SkiaBitmapVideoSurface.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/other/videoplayer/SkiaBitmapVideoSurface.kt @@ -8,6 +8,7 @@ import org.jetbrains.skia.Bitmap import org.jetbrains.skia.ColorAlphaType import org.jetbrains.skia.ColorType import org.jetbrains.skia.ImageInfo +import uk.co.caprica.vlcj.media.VideoOrientation import uk.co.caprica.vlcj.player.base.MediaPlayer import uk.co.caprica.vlcj.player.embedded.videosurface.CallbackVideoSurface import uk.co.caprica.vlcj.player.embedded.videosurface.VideoSurface @@ -22,10 +23,42 @@ import javax.swing.SwingUtilities // https://github.com/JetBrains/compose-multiplatform/pull/3336/files internal class SkiaBitmapVideoSurface : VideoSurface(VideoSurfaceAdapters.getVideoSurfaceAdapter()) { + private companion object { + // A received file declares its own size, and vlc allocates the buffer we ask for here (and we copy + // it into a java array of the same size), so an unbounded request is an out-of-memory from a message. + // Above the budget the picture is scaled down keeping its aspect - 4096x4096 of RV32 is 64 MB + const val MAX_BUFFER_PIXELS = 4096L * 4096L + val transposedOrientations = setOf( + VideoOrientation.LEFT_TOP, + VideoOrientation.LEFT_BOTTOM, + VideoOrientation.RIGHT_TOP, + VideoOrientation.RIGHT_BOTTOM, + ) + + // Keeps the aspect, never returns a side below 1, and keeps width * height * 4 inside an Int + fun boundedSize(width: Int, height: Int): Pair { + val w = width.coerceAtLeast(1) + val h = height.coerceAtLeast(1) + val pixels = w.toLong() * h.toLong() + if (pixels <= MAX_BUFFER_PIXELS) return w to h + val scale = kotlin.math.sqrt(MAX_BUFFER_PIXELS.toDouble() / pixels.toDouble()) + var sw = (w * scale).toInt().coerceAtLeast(1) + var sh = (h * scale).toInt().coerceAtLeast(1) + // Scaling both sides assumes both shrink; a side pinned at 1 only shrinks the area linearly, + // so a 2_000_000_000 x 1 declaration would still get 45 times the budget. Divide the budget + // by the pinned side instead + if (sw.toLong() * sh.toLong() > MAX_BUFFER_PIXELS) { + if (sw >= sh) sw = (MAX_BUFFER_PIXELS / sh).toInt() else sh = (MAX_BUFFER_PIXELS / sw).toInt() + } + return sw to sh + } + } + private val videoSurface = SkiaBitmapVideoSurface() @Volatile private var mediaPlayer: MediaPlayer? = null private lateinit var imageInfo: ImageInfo private lateinit var frameBytes: ByteArray + @Volatile private var allocated = false private val skiaBitmap: Bitmap = Bitmap() private val composeBitmap = mutableStateOf(null) @@ -49,19 +82,37 @@ internal class SkiaBitmapVideoSurface : VideoSurface(VideoSurfaceAdapters.getVid val tracks = player?.media()?.info()?.videoTracks() val playingTrack = player?.video()?.track() val track = tracks?.firstOrNull { it.id() == playingTrack } ?: tracks?.singleOrNull() - this.sourceWidth = track?.width()?.takeIf { it > 0 } ?: sourceWidth - this.sourceHeight = track?.height()?.takeIf { it > 0 } ?: sourceHeight - return RV32BufferFormat(this.sourceWidth, this.sourceHeight) + // Both track sides or neither: one side from the track and the other from the padded size libvlc + // passed never described the same picture, and transposing such a pair compounds the mismatch + val trackW = track?.width() ?: 0 + val trackH = track?.height() ?: 0 + val useTrack = trackW > 0 && trackH > 0 + val width = if (useTrack) trackW else sourceWidth + val height = if (useTrack) trackH else sourceHeight + // The track carries the size before rotation, but vlc rotates the picture before it reaches + // this buffer, so for the transposed orientations the picture arrives with the sides swapped. + // Only when the track's own sides are used: the size libvlc passed is already rotated + val transposed = useTrack && (track?.orientation() in transposedOrientations) + val orientedWidth = if (transposed) height else width + val orientedHeight = if (transposed) width else height + val (w, h) = boundedSize(orientedWidth, orientedHeight) + this.sourceWidth = w + this.sourceHeight = h + return RV32BufferFormat(w, h) } override fun allocatedBuffers(buffers: Array) { - frameBytes = buffers[0].run { ByteArray(remaining()).also(::get) } + // rewind first, as in display: remaining() on an already-read buffer would size this short + frameBytes = buffers[0].run { rewind(); ByteArray(remaining()).also(::get) } imageInfo = ImageInfo( sourceWidth, sourceHeight, ColorType.BGRA_8888, ColorAlphaType.PREMUL, ) + // Last, and volatile: vlc calls this on its own thread while display reads imageInfo and + // frameBytes on the event thread, and this write is what publishes them to it + this@SkiaBitmapVideoSurface.allocated = true } } @@ -71,11 +122,35 @@ internal class SkiaBitmapVideoSurface : VideoSurface(VideoSurfaceAdapters.getVid nativeBuffers: Array, bufferFormat: BufferFormat, ) { + // The native buffer belongs to vlc and is only guaranteed to exist for the duration of this + // callback, so everything that touches it has to happen here, on vlc's thread - deferred code + // would read through a pointer vlc may have freed on a format change. Only the copy is done + // here; skia and compose are event-thread objects and get the private copy + if (!this@SkiaBitmapVideoSurface.allocated) return + val info = imageInfo + // imageInfo comes from the format that was last allocated and this frame from the format it was + // rendered with; they differ across a renegotiation, and the pixels would be read with the + // wrong stride, so display only what matches + if (bufferFormat.width != info.width || bufferFormat.height != info.height) return + val rowBytes = info.width.toLong() * 4 + val needed = rowBytes * info.height + val buffer = nativeBuffers[0] + // rewind first: the same buffer is reused for every frame, so its position is at the end of + // the previous read and remaining() would be 0 + buffer.rewind() + // Capture the array: a renegotiation replaces the field with one of another size before the + // deferred install runs, and info's geometry must be read against the array it was copied into. + // The next frame's copy can overwrite it while the install reads - a torn frame at worst, since + // the geometry checks above hold for both frames of the same format + val bytes = frameBytes + if (needed > bytes.size || buffer.remaining().toLong() < needed) return + buffer.get(bytes, 0, needed.toInt()) SwingUtilities.invokeLater { - nativeBuffers[0].rewind() - nativeBuffers[0].get(frameBytes) - skiaBitmap.installPixels(imageInfo, frameBytes, bufferFormat.width * 4) - composeBitmap.value = skiaBitmap.asComposeImageBitmap() + // installPixels reports whether skia took the pixels; publishing the bitmap when it did not + // would hand compose a bitmap with no pixels behind it + if (skiaBitmap.installPixels(info, bytes, rowBytes.toInt())) { + composeBitmap.value = skiaBitmap.asComposeImageBitmap() + } } } } diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/VideoPlayer.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/VideoPlayer.desktop.kt index 768d2f421d..d9375f5d79 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/VideoPlayer.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/VideoPlayer.desktop.kt @@ -11,7 +11,6 @@ import uk.co.caprica.vlcj.media.Media import uk.co.caprica.vlcj.media.MediaEventAdapter import uk.co.caprica.vlcj.media.MediaParsedStatus import uk.co.caprica.vlcj.media.ParseFlag -import uk.co.caprica.vlcj.media.VideoOrientation import uk.co.caprica.vlcj.player.base.* import uk.co.caprica.vlcj.player.component.CallbackMediaPlayerComponent import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent @@ -232,7 +231,18 @@ actual class VideoPlayer actual constructor( player.media().startPaused(uri.toFile().absolutePath) val snap = withTimeoutOrNull(1500L) { while (surface.bitmap.value == null) delay(50) - surface.bitmap.value!!.toAwtImage() + // The render callback installs pixels into the surface bitmap on the event thread, so read it + // there too - converting off that thread races a resize on format renegotiation and segfaults + // inside skia while reading pixels of the previous, smaller buffer + val holder = java.util.concurrent.atomic.AtomicReference(null) + // invokeAndWait rethrows whatever the conversion threw, wrapped, and the callers of this have + // no handler; a frame that cannot be converted is a missing preview, not a failed send + try { + javax.swing.SwingUtilities.invokeAndWait { holder.set(surface.bitmap.value?.toAwtImage()) } + } catch (e: Exception) { + Log.e(TAG, "getBitmapFromVideo snapshot failed: ${e.stackTraceToString()}") + } + holder.get() } val orientation = player.media().info().videoTracks().firstOrNull()?.orientation() if (orientation == null) { @@ -242,17 +252,9 @@ actual class VideoPlayer actual constructor( return@withContext VideoPlayerInterface.PreviewAndDuration(preview = defaultPreview, timestamp = 0L, duration = 0L) } - val preview: ImageBitmap? = when (orientation) { - VideoOrientation.TOP_LEFT -> snap - VideoOrientation.TOP_RIGHT -> snap?.flip(false, true) - VideoOrientation.BOTTOM_LEFT -> snap?.flip(true, false) - VideoOrientation.BOTTOM_RIGHT -> snap?.rotate(180.0) - VideoOrientation.LEFT_TOP -> snap /* Transposed */ - VideoOrientation.LEFT_BOTTOM -> snap?.rotate(-90.0) - VideoOrientation.RIGHT_TOP -> snap?.rotate(90.0) - VideoOrientation.RIGHT_BOTTOM -> snap /* Anti-transposed */ - else -> snap - }?.toComposeImageBitmap() + // vlc applies the display matrix before the frame reaches the video surface, so the snapshot + // arrives upright; orienting it again here would undo that + val preview: ImageBitmap? = snap?.toComposeImageBitmap() val duration = player.duration.toLong() player.stop() putHelperPlayer(mediaComponent) diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/CIVideoView.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/CIVideoView.desktop.kt index bdfbf6863f..d60790b694 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/CIVideoView.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/CIVideoView.desktop.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalWindowInfo import androidx.compose.ui.unit.Dp @@ -14,6 +15,8 @@ import java.awt.Window @Composable actual fun PlayerView(player: VideoPlayer, width: Dp, onClick: () -> Unit, onLongClick: () -> Unit, stop: () -> Unit) { Box { + // The preview this replaces while playing is drawn with FillWidth, so a video smaller than the + // item width has to grow the same way here - Fit would leave it at its own size in the middle SurfaceFromPlayer(player, Modifier .width(width) @@ -21,7 +24,8 @@ actual fun PlayerView(player: VideoPlayer, width: Dp, onClick: () -> Unit, onLon onLongClick = onLongClick, onClick = { if (player.player.isPlaying) stop() else onClick() } ) - .onRightClick(onLongClick) + .onRightClick(onLongClick), + contentScale = ContentScale.FillWidth ) } } diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/ImageFullScreenView.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/ImageFullScreenView.desktop.kt index 583aa8f52f..9fb4afbf7d 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/ImageFullScreenView.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/ImageFullScreenView.desktop.kt @@ -43,7 +43,7 @@ actual fun FullScreenVideoView(player: VideoPlayer, modifier: Modifier, close: ( } @Composable -fun BoxScope.SurfaceFromPlayer(player: VideoPlayer, modifier: Modifier) { +fun BoxScope.SurfaceFromPlayer(player: VideoPlayer, modifier: Modifier, contentScale: ContentScale = ContentScale.Fit) { val surface = remember { SkiaBitmapVideoSurface().also { player.player.videoSurface().set(it) @@ -54,7 +54,7 @@ fun BoxScope.SurfaceFromPlayer(player: VideoPlayer, modifier: Modifier) { bitmap, modifier = modifier.align(Alignment.Center), contentDescription = null, - contentScale = ContentScale.Fit, + contentScale = contentScale, alignment = Alignment.Center, ) } diff --git a/apps/multiplatform/external/nanohttpd/build.gradle.kts b/apps/multiplatform/external/nanohttpd/build.gradle.kts index fb24922208..7841d8f803 100644 --- a/apps/multiplatform/external/nanohttpd/build.gradle.kts +++ b/apps/multiplatform/external/nanohttpd/build.gradle.kts @@ -26,16 +26,25 @@ java { targetCompatibility = jvmVersion } -// Without this the jar records the build machine's timestamps, file order and file modes, -// which makes the desktop packages unreproducible -tasks.jar { - // Checked here and not during configuration, so that Android builds, which don't use nanohttpd, - // work without the submodule +val upstreamSources = sourceSets.main.get().java.matching { include("org/nanohttpd/**") } + +// compileJava and jar silently succeed without the sources, so the check needs its own task. +// It cannot run during configuration, Android builds must work without the submodule. +val checkUpstreamSources by tasks.registering { doFirst { - if (!upstream.file("core/src/main/java").asFile.isDirectory) { + if (upstreamSources.isEmpty) { throw GradleException("nanohttpd sources are missing, run: git submodule update --init --recursive") } } +} + +tasks.compileJava { + dependsOn(checkUpstreamSources) +} + +// Without this the jar records the build machine's timestamps, file order and file modes, +// which makes the desktop packages unreproducible +tasks.jar { isPreserveFileTimestamps = false isReproducibleFileOrder = true filePermissions { unix("644") } diff --git a/docs/CHAT-RELAY.md b/docs/CHAT-RELAY.md index a06c06026f..38c88d560e 100644 --- a/docs/CHAT-RELAY.md +++ b/docs/CHAT-RELAY.md @@ -1,6 +1,7 @@ --- title: Hosting your own Chat Relay -revision: 16.07.2026 +revision: 28.07.2026 +templateEngineOverride: md --- # Hosting your own Chat Relay @@ -20,6 +21,7 @@ This guide explains how to set up a chat relay on a Linux server, how to run it, - [Relay options](#relay-options) - [Get the relay address](#get-the-relay-address) - [Run relay commands](#run-relay-commands) +- [Run with Docker](#run-with-docker) - [Channel web previews](#channel-web-previews) - [Relay web options](#relay-web-options) - [Serve the previews with Caddy](#serve-the-previews-with-caddy) @@ -126,6 +128,73 @@ simplex-chat-relay -d /home/relay/relay -e "/set profile image file /home/relay/ systemctl start simplex-relay ``` +## Run with Docker + +The relay can also be built and run with Docker Compose, using PostgreSQL for storage. The files are in [`scripts/relay`](https://github.com/simplex-chat/simplex-chat/tree/master/scripts/relay). + +1. Clone the repository and switch to the relay directory: + + ```sh + git clone https://github.com/simplex-chat/simplex-chat + cd simplex-chat/scripts/relay + ``` + +2. Copy the example environment file, then set `RELAY_NAME`, `RELAY_WEB_DOMAIN` and `POSTGRES_PASSWORD` in it: + + ```sh + cp .env.example .env + ``` + +3. Create the directory for the previews and the CORS file, owned by the container's user (UID `1000`): + + ```sh + mkdir -p /var/www/relay-web-channels/channel + chown -R 1000:1000 /var/www/relay-web-channels + chmod 0755 /var/www/relay-web-channels + ``` + +4. Create the output directory for the relay address, then build and start: + + ```sh + mkdir -p out && chown 1000:1000 out + docker compose build + docker compose up -d + ``` + + The first build compiles from source and takes a while. + +5. Read the relay address, written on the first start: + + ```sh + cat out/relay-address.txt + ``` + +To give the relay a picture, put a small `.png`/`.jpg`/`.jpeg` file (large images are rejected) next to the compose file and add a `docker-compose.override.yml`: + +```yaml +services: + relay: + environment: + RELAY_IMAGE_FILE: /avatar.png + volumes: + - ./avatar.png:/avatar.png:ro +``` + +To run a one-off command against the relay's database, override the entrypoint: + +```sh +docker compose run --rm --entrypoint sh relay -c \ + 'simplex-chat-relay -d "$DB_CONN" -e "/set profile image file /avatar.png"' +``` + +Relay metrics from the database are published by [sql_exporter](https://github.com/burningalchemist/sql_exporter) on `127.0.0.1:9399/metrics`, with the queries in `sql_exporter.yml`. + +Relay database live in PostgreSQL docker volume. To print the full path to PostgreSQL database, execute in the host: + +```sh +docker volume inspect simplex-chat-relay_pgdata --format '{{.Mountpoint}}' +``` + ## Channel web previews Chat relays can render recent messages of its public channels as JSON files, which can be served over HTTPS using a web server to create channel web previews. This is optional. @@ -219,11 +288,9 @@ Create `/etc/systemd/system/simplex-cors-sync.service`: ```ini [Unit] Description=Sync SimpleX relay CORS config to Caddy -StartLimitIntervalSec=30 -StartLimitBurst=10 +StartLimitIntervalSec=0 [Service] Type=oneshot -ExecStartPre=/bin/sleep 2 ExecStart=/usr/local/bin/simplex-cors-sync.sh ``` @@ -236,6 +303,7 @@ After=caddy.service [Path] PathChanged=/var/www/relay-web-channels/cors.conf Unit=simplex-cors-sync.service +TriggerLimitIntervalSec=0 [Install] WantedBy=multi-user.target ``` diff --git a/plans/2026-08-24-desktop-rotated-video-playback.md b/plans/2026-08-24-desktop-rotated-video-playback.md new file mode 100644 index 0000000000..0ef27efbaf --- /dev/null +++ b/plans/2026-08-24-desktop-rotated-video-playback.md @@ -0,0 +1,109 @@ +# Desktop: rotated videos squashed on playback, preview rotated twice, snapshot crash + +## Problem + +A video carrying rotation metadata - what a phone records in portrait - is squashed when it +plays in a chat item on desktop, while its preview looks correct. Re-sending such a video from +desktop produces a preview that is wrong for every recipient. Attaching one can take the app +down with a SIGSEGV inside skia. Separately, any video smaller than the width of the message +item plays at its own size in the middle of the item instead of filling it, though its preview +fills the width. + +## Cause + +Three defects, of different ages, on the path a frame takes from libvlc to the screen. + +**The buffer is sized from the wrong dimensions.** `SkiaBitmapVideoSurface` asks libvlc for a +buffer of `track.width() x track.height()`. The track carries the size before rotation, but vlc +applies the display matrix before the frame reaches the vmem callback, so the picture arriving +is transposed with respect to the buffer, and vlc stretches it to fill. Measured with a +1920x1080 HEVC file whose display matrix is -90: + +| source | value | +| -------------------------- | -------------------- | +| size libvlc offers | 1088x1920 (rotated) | +| `track.width()/height()` | 1920x1080 (coded) | +| buffer requested before fix | 1920x1080 | + +Asking for the track size was introduced to drop the decoder's padding (#7391); it is right for +an unrotated video and wrong for a rotated one, because it discards the orientation libvlc had +already applied. + +**The preview is oriented twice.** `previewAndDuration` takes a snapshot from the same surface +and then rotates it by hand. The snapshot arrives at 1080x1920 and already upright - dumping it +to a PNG confirms the content, not just the dimensions - and the manual rotation turns it back +to 1920x1080. Before this change the two errors cancelled: a wrongly shaped buffer plus a +manual rotation produced a preview that looked right, which is why the preview was correct while +playback was not. + +**The snapshot races the render callback.** The render callback installs pixels into the shared +bitmap on the event thread; the snapshot converted it on the preview thread. The format is +renegotiated several times per file, so a resize between `installPixels` and `readPixels` makes +skia read past the end of the buffer: + +``` +SIGSEGV ... C [libskiko-linux-x64.so+0x1e7807] sse2::load_8888(...) + at org.jetbrains.skia.Bitmap.readPixels + at chat.simplex.common.platform.VideoPlayer$Companion$getBitmapFromVideo$2$snap$1 +``` + +**Small videos do not fill the item.** The preview is drawn with `ContentScale.FillWidth` and +the playback surface with `ContentScale.Fit`. `Fit` never exceeds the height of the box, so a +320x240 video stays at its own size, centred, while its preview fills the width. This is only +visible for sources narrower than the item. + +## Fix + +Swap the requested width and height for the four transposed orientations, so the buffer matches +the picture vlc delivers, and keep the track size otherwise so the padding fix still holds. +Drop the manual orientation handling from the preview, since the frame is already upright. Read +the snapshot on the event thread, where the render callback writes it. Draw the inline playback +surface with `FillWidth`, as its preview is drawn. + +## Bounds + +The dimensions come from a received file, so they are attacker-chosen and are treated as such. + +- Both track sides are used or neither. One side from the track beside the other from libvlc's + padded size never described the same picture, and transposing such a pair compounds it. +- The requested area is capped, scaling down and keeping the aspect where both sides can shrink; + a side pinned at 1 takes the whole budget on the other side instead, since scaling cannot keep + the aspect of a 2000000000x1 declaration and hold the area at once. An unbounded request is an + out-of-memory from a message: 16000x16000 is 1 GB of RV32, requested from vlc and copied into + a java array of the same size. The cap also keeps `width * height * 4` inside an `Int`. +- Neither side can be zero, so a 1x4000 or 4000x1 file cannot produce an empty buffer. +- The sides are only swapped when the track's own sides are used. The size libvlc passes is + already rotated, so swapping that pair would recreate the squash for a file that declares a + rotation and a zero-sized track. +- A frame is dropped rather than displayed when it does not fill the bitmap skia is told to + read, when the format it was rendered with is not the one the bitmap was sized by, and before + any buffer has been allocated. The checks and the copy run inside the render callback, on + vlc's thread: the native buffer is only guaranteed to exist for the duration of the callback, + so code deferred to another thread would read through a pointer vlc may have freed on a format + change. Only the copied frame is handed to the event thread. +- The bitmap is published only when skia reports that it took the pixels, and a snapshot that + cannot be converted is logged and left empty rather than thrown into callers that have no + handler for it. + +## Testing + +Fifteen files covering 320x240 to 3840x2160, square, odd, and 1234x567 sizes, h264, vp9, av1 +and hevc, unrotated and 90/180/270, plus 1x4000, 4000x1 and 16000x16000. Checked that rotated +videos play upright and preview upright, that a re-sent video keeps its shape, that attaching +does not crash, that a 320x240 video fills the item, that the AV1 padding fix still holds, and +that the 16000x16000 file is scaled to the cap instead of allocating a gigabyte. + +## Android + +None of these reach android. The buffer format callback is desktop only - android renders +through exoplayer's `StyledPlayerView`, with no buffer for us to size - and its preview comes +from `MediaMetadataRetriever.getFrameAtTime`, which returns an oriented frame and is not +rotated again. The event thread race is skia and swing. Android already fills the item width +with `RESIZE_MODE_FIXED_WIDTH`, which is what the `FillWidth` change gives desktop. + +## Not addressed + +`CIVideoView` bounds the item's aspect ratio above at 2.33 but not below, so a 4000x1 video +still lays out with a height that rounds to zero. The snapshot's `invokeAndWait` is not +cancellable, so its 1.5s timeout cannot interrupt a wedged event thread. Both are outside the +functions this change touches. diff --git a/scripts/desktop/prepare-vlc-linux.sh b/scripts/desktop/prepare-vlc-linux.sh index ef1ee1b308..be30c7a4c9 100755 --- a/scripts/desktop/prepare-vlc-linux.sh +++ b/scripts/desktop/prepare-vlc-linux.sh @@ -12,7 +12,7 @@ vlc_dir=$root_dir/apps/multiplatform/common/src/commonMain/cpp/desktop/libs/linu mkdir $vlc_dir || exit 0 -vlc_tag='v3.0.21-1' +vlc_tag='v3.0.23-2' vlc_url="https://github.com/simplex-chat/vlc/releases/download/${vlc_tag}/vlc-linux-${ARCH}.appimage" cd /tmp diff --git a/scripts/desktop/prepare-vlc-mac.sh b/scripts/desktop/prepare-vlc-mac.sh index 180acf4426..0e83f44dcf 100755 --- a/scripts/desktop/prepare-vlc-mac.sh +++ b/scripts/desktop/prepare-vlc-mac.sh @@ -10,7 +10,7 @@ else vlc_arch=intel64 fi -vlc_tag='v3.0.21-1' +vlc_tag='v3.0.23-2' vlc_url="https://github.com/simplex-chat/vlc/releases/download/${vlc_tag}/vlc-macos-${ARCH}.zip" function readlink() { diff --git a/scripts/desktop/prepare-vlc-windows.sh b/scripts/desktop/prepare-vlc-windows.sh index 4e65528ca0..cf9b553f15 100644 --- a/scripts/desktop/prepare-vlc-windows.sh +++ b/scripts/desktop/prepare-vlc-windows.sh @@ -10,7 +10,7 @@ vlc_dir=$root_dir/apps/multiplatform/common/src/commonMain/cpp/desktop/libs/wind rm -rf $vlc_dir mkdir -p $vlc_dir/vlc || exit 0 -vlc_tag='v3.0.21-1' +vlc_tag='v3.0.23-2' vlc_url="https://github.com/simplex-chat/vlc/releases/download/${vlc_tag}/vlc-win-x86_64.zip" cd /tmp diff --git a/scripts/relay/.dockerignore b/scripts/relay/.dockerignore new file mode 100644 index 0000000000..00bc43ddaf --- /dev/null +++ b/scripts/relay/.dockerignore @@ -0,0 +1,4 @@ +# The Dockerfile clones the source itself and only needs entrypoint.py from the +# build context. Exclude everything else, especially .env (secrets) and out/. +* +!entrypoint.py diff --git a/scripts/relay/.env.example b/scripts/relay/.env.example new file mode 100644 index 0000000000..7bfbd89615 --- /dev/null +++ b/scripts/relay/.env.example @@ -0,0 +1,21 @@ +# Copy to .env and edit. + +RELAY_NAME="My Relay" +RELAY_WEB_DOMAIN=relay1.example.com + +POSTGRES_USER=simplex +POSTGRES_DB=simplex_chat_relay +POSTGRES_PASSWORD=change-me + +# Ref to build: tag, branch or commit. +CHAT_REF=88df79d1e26921c7d1835fc8484fade596356fb6 + +# SMP server for the relay address, used when the address is created. +#RELAY_ADDRESS_SERVER=smp://@smp.example.com + +# GHC runtime options, without the +RTS/-RTS markers. +#RELAY_RTS_OPTS="-N -F1.2 -A16m -I0.01 -Iw15" + +# Database connection pool and internal queue size. +#RELAY_POOL_SIZE=4 +#RELAY_QUEUE_SIZE=65536 diff --git a/scripts/relay/Dockerfile b/scripts/relay/Dockerfile new file mode 100644 index 0000000000..1a731adf41 --- /dev/null +++ b/scripts/relay/Dockerfile @@ -0,0 +1,70 @@ +# syntax=docker/dockerfile:1 + +ARG CHAT_REF=88df79d1e26921c7d1835fc8484fade596356fb6 +ARG GHC=9.6.3 +# 3.10.1.0 predates the Hackage root key rotation and fails `cabal update`. +ARG CABAL=3.10.2.0 + +# ---- build ---------------------------------------------------------------- +FROM ubuntu:22.04 AS build + +ARG GHC +ARG CABAL +ENV DEBIAN_FRONTEND=noninteractive \ + PATH="/root/.cabal/bin:/root/.ghcup/bin:$PATH" + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates curl git build-essential \ + libpq-dev libgmp3-dev zlib1g-dev libnuma-dev libssl-dev \ + llvm-12 llvm-12-dev \ + && rm -rf /var/lib/apt/lists/* + +RUN curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org \ + | BOOTSTRAP_HASKELL_NONINTERACTIVE=1 \ + BOOTSTRAP_HASKELL_GHC_VERSION="${GHC}" \ + BOOTSTRAP_HASKELL_CABAL_VERSION="${CABAL}" sh \ + && ghcup set ghc "${GHC}" \ + && ghcup set cabal "${CABAL}" + +# Kept out of the layer above so that a failure here does not reinstall GHC. +# Retried because a failed fetch falls back to the mirrors in Hackage's +# mirrors.json, which are dead, and the last one aborts the build. +RUN cabal update || { sleep 5; cabal update; } || { sleep 20; cabal update; } + +# Declared after the toolchain layers so that changing the ref does not rebuild +# them. Unlike `clone --branch`, this form also accepts a commit hash. +ARG CHAT_REF +WORKDIR /project +RUN git init -q . \ + && git remote add origin https://github.com/simplex-chat/simplex-chat \ + && git fetch -q --depth 1 origin "${CHAT_REF}" \ + && git checkout -q FETCH_HEAD + +# The cache mounts keep compiled dependencies across ref changes. +RUN --mount=type=cache,target=/root/.cabal/store,sharing=locked \ + --mount=type=cache,target=/project/dist-newstyle,sharing=locked \ + cp scripts/cabal.project.local.linux cabal.project.local \ + && cabal build -fclient_postgres exe:simplex-chat \ + && bin=$(find dist-newstyle -name simplex-chat -type f -executable | head -n1) \ + && install -m 0755 "$bin" /simplex-chat-relay \ + && strip /simplex-chat-relay + +# ---- runtime -------------------------------------------------------------- +FROM debian:stable-slim AS runtime + +# The binary is dynamically linked, so it needs these libraries at runtime. +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates python3 \ + libpq5 libgmp10 libssl3 zlib1g libnuma1 libffi8 \ + && rm -rf /var/lib/apt/lists/* \ + && useradd -m -u 1000 relay + +COPY --from=build /simplex-chat-relay /usr/local/bin/simplex-chat-relay +COPY entrypoint.py /usr/local/bin/entrypoint.py + +# Bind-mounted host directories must be writable by this UID. +USER relay + +# The relay shuts down cleanly on SIGINT, not SIGTERM. +STOPSIGNAL SIGINT +ENTRYPOINT ["python3", "/usr/local/bin/entrypoint.py"] diff --git a/scripts/relay/docker-compose.yaml b/scripts/relay/docker-compose.yaml new file mode 100644 index 0000000000..569d403966 --- /dev/null +++ b/scripts/relay/docker-compose.yaml @@ -0,0 +1,87 @@ +name: simplex-chat-relay + +services: + relay: + build: + context: . + args: + CHAT_REF: ${CHAT_REF:-88df79d1e26921c7d1835fc8484fade596356fb6} + image: chat-relay:latest # set your image/registry name + container_name: chat-relay + depends_on: + db: + condition: service_healthy + environment: + RELAY_NAME: ${RELAY_NAME} + RELAY_WEB_DOMAIN: ${RELAY_WEB_DOMAIN} + RELAY_ADDRESS_SERVER: ${RELAY_ADDRESS_SERVER:-} + # Empty values fall back to the entrypoint defaults. + RELAY_RTS_OPTS: ${RELAY_RTS_OPTS:-} + RELAY_POOL_SIZE: ${RELAY_POOL_SIZE:-} + RELAY_QUEUE_SIZE: ${RELAY_QUEUE_SIZE:-} + # Password is passed as PGPASSWORD to keep it out of argv. + DB_CONN: postgresql://${POSTGRES_USER}@db:5432/${POSTGRES_DB} + PGPASSWORD: ${POSTGRES_PASSWORD} + volumes: + - /var/www/relay-web-channels:/var/www/relay-web-channels + - ./out:/out + restart: unless-stopped + + metrics: + image: burningalchemist/sql_exporter:latest + container_name: chat-relay-metrics + depends_on: + db: + condition: service_healthy + environment: + # Percent-encode the password if it contains URL-reserved characters. + SQLEXPORTER_TARGET_DSN: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?sslmode=disable + volumes: + - ./sql_exporter.yml:/etc/sql_exporter/sql_exporter.yml:ro + command: ["-config.file=/etc/sql_exporter/sql_exporter.yml"] + ports: + - "127.0.0.1:9399:9399" + restart: unless-stopped + + db: + image: postgres:18 + container_name: chat-relay-db + environment: + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + shm_size: 256mb # parallel workers need more than the 64 MB default + volumes: + # PG18+ keeps PGDATA in a version-specific subdirectory of this volume. + - pgdata:/var/lib/postgresql + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] + interval: 5s + timeout: 5s + retries: 10 + # Tuning for ~5 GB RAM / 4 cores. Match to the actual server. + command: + - postgres + - --max_connections=100 + - --shared_buffers=1280MB + - --effective_cache_size=3840MB + - --maintenance_work_mem=320MB + - --checkpoint_completion_target=0.9 + - --wal_buffers=16MB + - --default_statistics_target=100 + - --random_page_cost=1.1 + - --effective_io_concurrency=200 + - --work_mem=6301kB + - --huge_pages=off + - --jit=off + - --wal_compression=lz4 + - --min_wal_size=1GB + - --max_wal_size=4GB + - --max_worker_processes=4 + - --max_parallel_workers_per_gather=2 + - --max_parallel_workers=4 + - --max_parallel_maintenance_workers=2 + restart: unless-stopped + +volumes: + pgdata: diff --git a/scripts/relay/entrypoint.py b/scripts/relay/entrypoint.py new file mode 100755 index 0000000000..522e87eb22 --- /dev/null +++ b/scripts/relay/entrypoint.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +"""Save the relay address on first start, then exec the relay. + +The address is printed only when it is created, and is not stored in the +database in its displayed form, so it is captured from the relay's output. +""" +import os +import shlex +import subprocess +import sys + +BIN = "simplex-chat-relay" +WEB_ROOT = "/var/www/relay-web-channels" +ADDR_FILE = "/out/relay-address.txt" +CAPTURE_TIMEOUT = 180 # seconds +DEFAULT_RTS_OPTS = "-N -F1.2 -A16m -I0.01 -Iw15" +DEFAULT_POOL_SIZE = "4" # the binary defaults to a single connection +DEFAULT_QUEUE_SIZE = "65536" + + +def require(name): + value = os.environ.get(name) + if not value: + sys.exit(f"{name} is required") + return value + + +def rts_args(): + """RELAY_RTS_OPTS holds bare options; the +RTS/-RTS markers are added here.""" + opts = [ + o + for o in shlex.split(os.environ.get("RELAY_RTS_OPTS") or DEFAULT_RTS_OPTS) + if o not in ("+RTS", "-RTS") + ] + return ["+RTS", *opts, "-RTS"] if opts else [] + + +def find_address(text): + for token in text.split(): + if token.startswith("https://") or token.startswith("simplex:"): + return token + return None + + +def capture_address(oneshot): + """Create the address if needed and save it. Runs before the relay starts.""" + cmd = oneshot + ["--create-schema", "-t", "0", "-e", "/sa"] + try: + out = subprocess.run( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + errors="replace", + timeout=CAPTURE_TIMEOUT, + ).stdout + except subprocess.TimeoutExpired as exc: + out = exc.stdout or "" + sys.stdout.write(out) + sys.stdout.flush() + + address = find_address(out) + if address: + with open(ADDR_FILE, "w") as f: + f.write(address + "\n") + else: + sys.stderr.write("entrypoint: relay address not captured; will retry next start\n") + + +def main(): + name = require("RELAY_NAME") + domain = require("RELAY_WEB_DOMAIN") + conn = require("DB_CONN") + image_file = os.environ.get("RELAY_IMAGE_FILE") + + os.makedirs(f"{WEB_ROOT}/channel", exist_ok=True) + os.makedirs("/out", exist_ok=True) + + common = [BIN, "--relay", "--headless", "--user-display-name", name] + + # Only applied when the address is created, which the one-shot below does. + address_server = os.environ.get("RELAY_ADDRESS_SERVER") + if address_server: + common += ["--relay-address-server", address_server] + + # The image is applied only when the profile is created. + if not os.path.exists(ADDR_FILE): + oneshot = common + (["--user-image-file", image_file] if image_file else []) + ["-d", conn] + capture_address(oneshot) + + relay = common + [ + "--relay-web-domain", domain, + "--relay-web-dir", f"{WEB_ROOT}/channel", + "--relay-web-cors-file", f"{WEB_ROOT}/cors.conf", + "--relay-web-interval", "30", + "-d", conn, + "--create-schema", + "--pool-size", os.environ.get("RELAY_POOL_SIZE") or DEFAULT_POOL_SIZE, + "--queue-size", os.environ.get("RELAY_QUEUE_SIZE") or DEFAULT_QUEUE_SIZE, + ] + rts_args() + os.execvp(relay[0], relay) + + +if __name__ == "__main__": + main() diff --git a/scripts/relay/sql_exporter.yml b/scripts/relay/sql_exporter.yml new file mode 100644 index 0000000000..ee8f62218b --- /dev/null +++ b/scripts/relay/sql_exporter.yml @@ -0,0 +1,71 @@ +global: + min_interval: 0s + max_connections: 3 + max_idle_connections: 3 + +target: + name: chat_relay + # Replaced by SQLEXPORTER_TARGET_DSN. A non-empty value is required here + # because the DSN is validated before environment variables are applied. + data_source_name: "postgresql://replaced-by-env" + collectors: [chat_relay] + +collectors: + - collector_name: chat_relay + metrics: + - metric_name: chat_relay_channels + type: gauge + help: "Channels by relay status." + key_labels: [status] + values: [channels] + query: | + SELECT relay_own_status AS status, count(*) AS channels + FROM simplex_v1_chat_schema.groups + WHERE relay_own_status IS NOT NULL + GROUP BY relay_own_status + + - metric_name: chat_relay_published_channels + type: gauge + help: "Served channels that have a public address." + values: [channels] + query: | + SELECT count(*) AS channels + FROM simplex_v1_chat_schema.groups g + JOIN simplex_v1_chat_schema.group_profiles gp + ON gp.group_profile_id = g.group_profile_id + WHERE gp.public_group_id IS NOT NULL + AND g.relay_own_status IN ('active', 'accepted') + + - metric_name: chat_relay_members + type: gauge + help: "Members across all channels." + values: [members] + query: | + SELECT count(*) AS members FROM simplex_v1_chat_schema.group_members + + - metric_name: chat_relay_pending_deliveries + type: gauge + help: "Messages queued for delivery." + values: [deliveries] + query: | + SELECT count(*) AS deliveries + FROM simplex_v1_chat_schema.msg_deliveries + WHERE delivery_status = 'snd_pending' + + - metric_name: chat_relay_oldest_pending_delivery_seconds + type: gauge + help: "Age of the oldest message queued for delivery." + values: [age] + query: | + SELECT COALESCE(EXTRACT(EPOCH FROM (now() - min(created_at))), 0) AS age + FROM simplex_v1_chat_schema.msg_deliveries + WHERE delivery_status = 'snd_pending' + + - metric_name: chat_relay_messages_24h + type: gauge + help: "Chat items created in the last 24 hours." + values: [messages] + query: | + SELECT count(*) AS messages + FROM simplex_v1_chat_schema.chat_items + WHERE created_at > now() - interval '24 hours' diff --git a/src/Simplex/Chat/Markdown.hs b/src/Simplex/Chat/Markdown.hs index cd2e337aff..c877a22c2b 100644 --- a/src/Simplex/Chat/Markdown.hs +++ b/src/Simplex/Chat/Markdown.hs @@ -211,7 +211,7 @@ markdownP = mconcat <$> A.many' fragmentP Just c -> case c of ' ' -> unmarked <$> A.takeWhile (== ' ') '+' -> phoneP <|> wordP - '*' -> formattedP '*' Bold + '*' -> boldP <|> formattedP '*' Bold '_' -> formattedP '_' Italic '~' -> formattedP '~' StrikeThrough '`' -> formattedP '`' Snippet @@ -233,6 +233,12 @@ markdownP = mconcat <$> A.many' fragmentP | T.null s || T.head s == ' ' || T.last s == ' ' = unmarked $ c `T.cons` s `T.snoc` c | otherwise = markdown f s + boldP :: Parser Markdown + boldP = do + s <- A.string "**" *> A.takeTill (== '*') <* A.string "**" + if T.null s || T.head s == ' ' || T.last s == ' ' + then fail "not bold" + else pure $ markdown Bold s secretP :: Parser Markdown secretP = secret <$?> ((,,) <$> A.takeWhile (== '#') <*> A.takeTill (== '#') <*> A.takeWhile1 (== '#')) secret :: (Text, Text, Text) -> Either String Markdown diff --git a/tests/MarkdownTests.hs b/tests/MarkdownTests.hs index e315b59f5e..1f9936044c 100644 --- a/tests/MarkdownTests.hs +++ b/tests/MarkdownTests.hs @@ -74,6 +74,19 @@ textFormat = describe "text format (bold)" do <==> "this is " <> bold "bold" <> " " "this is *bold* " <==> "this is " <> bold "bold" <> " " + it "correct markdown with double asterisk" do + "this is **bold formatted** text" + ==> "this is " <> bold "bold formatted" <> " text" + "**bold formatted** text" + ==> bold "bold formatted" <> " text" + "this is **bold**" + ==> "this is " <> bold "bold" + " **bold** text" + ==> " " <> bold "bold" <> " text" + "this is **bold** " + ==> "this is " <> bold "bold" <> " " + "this is **bold** " + ==> "this is " <> bold "bold" <> " " it "ignored as markdown" do "this is * unformatted * text" <==> "this is * unformatted * text" @@ -81,8 +94,16 @@ textFormat = describe "text format (bold)" do <==> "this is *unformatted * text" "this is * unformatted* text" <==> "this is * unformatted* text" - "this is **unformatted** text" - <==> "this is **unformatted** text" + "this is ** unformatted ** text" + <==> "this is ** unformatted ** text" + "this is **unformatted ** text" + <==> "this is **unformatted ** text" + "this is ** unformatted** text" + <==> "this is ** unformatted** text" + "this is **unformatted text" + <==> "this is **unformatted text" + "this is **unformatted* text" + <==> "this is **unformatted* text" "this is*unformatted* text" <==> "this is*unformatted* text" "this is *unformatted text" diff --git a/website/src/_includes/footer.html b/website/src/_includes/footer.html index 27fb44b178..31c9d9e557 100644 --- a/website/src/_includes/footer.html +++ b/website/src/_includes/footer.html @@ -63,7 +63,7 @@
- + @@ -108,7 +108,7 @@ 19.3216 25.0173 19.3216C23.9563 19.3216 23.0902 20.1878 23.0902 21.2488Z" /> - + diff --git a/website/src/crowdfunding.md b/website/src/crowdfunding.md index 0ccc1d38c6..8d0c1c37b8 100644 --- a/website/src/crowdfunding.md +++ b/website/src/crowdfunding.md @@ -33,7 +33,7 @@ SimpleX Chat — the company that builds the first and the only messaging ne Live event SimpleX Chat: Foundation for the Future -Livestream and Q&A for SimpleX Chat crowdfunding investors. Tuesday, September 15, 2026 at 5:00 PM UTC. +Livestream and Q&A about SimpleX Chat roadmap and crowdfunding. Tuesday, September 15, 2026 at 5:00 PM UTC. Open event page diff --git a/website/src/css/livestream.css b/website/src/css/livestream.css index ef1d2e69c2..cc8021127d 100644 --- a/website/src/css/livestream.css +++ b/website/src/css/livestream.css @@ -5,6 +5,10 @@ -webkit-backface-visibility: hidden; } +main .section-bg { + position: relative; +} + .livestream { --art-edge: 62.5; --split: 56; @@ -60,7 +64,7 @@ } .livestream .text-container .event-utc, -.livestream .text-container .event-local { +.livestream .text-container .event-join { font-family: "Manrope", sans-serif; font-weight: 300; font-size: calc(var(--sec-vwu) * 1.4); @@ -69,16 +73,36 @@ color: #000000; } -.dark .livestream .text-container .event-utc { +.dark .livestream .text-container .event-utc, +.dark .livestream .text-container .event-join { color: #ffffff; } -.livestream .text-container .event-local { +.livestream .text-container .event-join { font-size: calc(var(--sec-vwu) * 1.3); - color: #009df7; } -.dark .livestream .text-container .event-local { +.livestream .text-container .event-join span { + font-weight: inherit; +} + +.livestream .text-container .event-join .join-count { + margin-right: 0.3em; +} + +.livestream .text-container .event-join.counting .join-where { + display: block; +} + +.livestream .text-container .event-join a { + color: #009df7; + font-family: inherit; + font-weight: inherit; + font-size: inherit; + max-width: none; +} + +.dark .livestream .text-container .event-join a { color: #64fdff; } @@ -152,12 +176,12 @@ } .livestream .text-container .event-utc, - .livestream .text-container .event-local { + .livestream .text-container .event-join { font-size: calc(var(--sec-vwu) * 3.9); max-width: calc(var(--sec-vwu) * 80); } - .livestream .text-container .event-local { + .livestream .text-container .event-join { font-size: calc(var(--sec-vwu) * 3.3); } @@ -273,6 +297,34 @@ color: #000000; } +.register-card .channel-link { + display: inline-flex; + align-items: center; + margin-top: 18px; + font-family: "Manrope", sans-serif; + font-weight: 500; + font-size: 16px; + color: #0053d0; + text-decoration: none; +} + +.register-card .channel-link:hover { + text-decoration: underline; + text-underline-offset: 3px; +} + +.dark .register-card .channel-link { + color: #70f0f9; +} + +.register-card .channel-link svg { + width: 0.82em; + height: 0.82em; + margin-left: 0.4em; + fill: currentColor; + flex: none; +} + .register-card .close-register { position: absolute; top: 16px; diff --git a/website/src/img/crowdfunding/why-now.png b/website/src/img/crowdfunding/why-now.png new file mode 100644 index 0000000000..ee03c19686 Binary files /dev/null and b/website/src/img/crowdfunding/why-now.png differ diff --git a/website/src/index.html b/website/src/index.html index 7a923fe264..0093ed718d 100644 --- a/website/src/index.html +++ b/website/src/index.html @@ -66,6 +66,47 @@ active_home: true +

{{ "index-hero-h1" | i18n({}, lang) | safe }}

diff --git a/website/src/js/livestream.js b/website/src/js/livestream.js index 0b170bd9df..cc236ee221 100644 --- a/website/src/js/livestream.js +++ b/website/src/js/livestream.js @@ -1,7 +1,6 @@ function showLocalTime() { const utc = document.querySelector('.event-utc'); - const local = document.querySelector('.event-local'); - if (!utc || !local) return; + if (!utc) return; const start = new Date(utc.dateTime); if (isNaN(start.getTime())) return; @@ -15,8 +14,48 @@ function showLocalTime() { timeZoneName: 'short' }); - local.textContent = 'Your time: ' + format.format(start); - local.removeAttribute('hidden'); + utc.textContent = format.format(start); +} + +function startCountdown() { + const utc = document.querySelector('.event-utc'); + const join = document.querySelector('.event-join'); + const count = join && join.querySelector('.join-count'); + if (!utc || !count) return; + + const start = new Date(utc.dateTime); + if (isNaN(start.getTime())) return; + + const pad = (n) => String(n).padStart(2, '0'); + + function tick() { + const left = Math.floor((start.getTime() - Date.now()) / 1000); + if (left <= 0) { + count.textContent = 'now'; + join.classList.remove('counting'); + } else { + const days = Math.floor(left / 86400); + const dayPart = days ? days + (days === 1 ? ' day ' : ' days ') : ''; + count.textContent = 'in ' + dayPart + + pad(Math.floor((left % 86400) / 3600)) + ' hrs ' + + pad(Math.floor((left % 3600) / 60)) + ' min ' + + pad(left % 60) + ' sec'; + join.classList.add('counting'); + } + count.removeAttribute('hidden'); + } + + tick(); + setInterval(tick, 1000); +} + +function setSignupSource() { + const field = document.querySelector('input[name="SOURCE"]'); + if (!field) return; + + const inHash = new URLSearchParams(location.hash.replace(/^#\??/, '')).get('utm_source'); + const source = inHash ?? new URLSearchParams(location.search).get('utm_source'); + if (source && /^[\w.-]{1,40}$/.test(source)) field.value = source; } function setupRegisterOverlay() { @@ -67,5 +106,7 @@ function trackNavColor() { } showLocalTime(); +startCountdown(); +setSignupSource(); setupRegisterOverlay(); trackNavColor(); diff --git a/website/src/livestream.html b/website/src/livestream.html index dc88553b30..ea2665ac6f 100644 --- a/website/src/livestream.html +++ b/website/src/livestream.html @@ -70,15 +70,15 @@ templateEngineOverride: njk

SimpleX Chat:
Foundation
For the Future

-

Livestream and Q&A for SimpleX Chat Crowdfunding Investors

+

Livestream and Q&A about SimpleX Chat roadmap and crowdfunding

- +

Join us on X, YouTube or our page

@@ -96,12 +96,15 @@ templateEngineOverride: njk
+
+ Or join our SimpleX Crowdfunding News channel +