From 828b50243101bf00752d2573496c882e65a44fc1 Mon Sep 17 00:00:00 2001 From: JRoberts <8711996+jr-simplex@users.noreply.github.com> Date: Wed, 16 Nov 2022 20:26:43 +0400 Subject: [PATCH] ios: load and save preferences (#1373) --- apps/ios/Shared/Model/SimpleXAPI.swift | 6 + apps/ios/Shared/Views/Chat/ChatInfoView.swift | 12 +- .../Views/Chat/ContactPreferencesView.swift | 66 +++--- .../Views/Chat/Group/GroupChatInfoView.swift | 20 +- .../Chat/Group/GroupPreferencesView.swift | 86 ++++++++ .../Views/UserSettings/PreferencesView.swift | 49 +++-- .../Views/UserSettings/SettingsView.swift | 2 +- .../de.xcloc/Localized Contents/de.xliff | 190 ++++++++++++++++++ .../en.xcloc/Localized Contents/en.xliff | 190 ++++++++++++++++++ .../ru.xcloc/Localized Contents/ru.xliff | 190 ++++++++++++++++++ apps/ios/SimpleX.xcodeproj/project.pbxproj | 4 + apps/ios/SimpleXChat/APITypes.swift | 6 + apps/ios/SimpleXChat/ChatTypes.swift | 188 ++++++++++++++++- apps/ios/de.lproj/Localizable.strings | 114 +++++++++++ apps/ios/ru.lproj/Localizable.strings | 114 +++++++++++ src/Simplex/Chat/Controller.hs | 2 +- src/Simplex/Chat/Store.hs | 2 +- 17 files changed, 1185 insertions(+), 56 deletions(-) create mode 100644 apps/ios/Shared/Views/Chat/Group/GroupPreferencesView.swift diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 60589cf0c6..8335c7dfc4 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -487,6 +487,12 @@ func apiUpdateProfile(profile: Profile) async throws -> Profile? { } } +func apiSetContactPrefs(contactId: Int64, preferences: Preferences) async throws -> Contact? { + let r = await chatSendCmd(.apiSetContactPrefs(contactId: contactId, preferences: preferences)) + if case let .contactPrefsUpdated(_, toContact) = r { return toContact } + throw r +} + func apiSetContactAlias(contactId: Int64, localAlias: String) async throws -> Contact? { let r = await chatSendCmd(.apiSetContactAlias(contactId: contactId, localAlias: localAlias)) if case let .contactAliasUpdated(toContact) = r { return toContact } diff --git a/apps/ios/Shared/Views/Chat/ChatInfoView.swift b/apps/ios/Shared/Views/Chat/ChatInfoView.swift index ed636d88fa..1945524973 100644 --- a/apps/ios/Shared/Views/Chat/ChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatInfoView.swift @@ -53,7 +53,7 @@ struct ChatInfoView: View { @EnvironmentObject var chatModel: ChatModel @Environment(\.dismiss) var dismiss: DismissAction @ObservedObject var chat: Chat - var contact: Contact + @State var contact: Contact @Binding var connectionStats: ConnectionStats? var customUserProfile: Profile? @State var localAlias: String @@ -101,9 +101,13 @@ struct ChatInfoView: View { Section("Preferences") { NavigationLink { - ContactPreferencesView() - .navigationBarTitle("Contact preferences") - .navigationBarTitleDisplayMode(.large) + ContactPreferencesView( + contact: $contact, + featuresAllowed: contactUserPrefsToFeaturesAllowed(contact.mergedPreferences), + currentFeaturesAllowed: contactUserPrefsToFeaturesAllowed(contact.mergedPreferences) + ) + .navigationBarTitle("Contact preferences") + .navigationBarTitleDisplayMode(.large) } label: { Text("Contact preferences") } diff --git a/apps/ios/Shared/Views/Chat/ContactPreferencesView.swift b/apps/ios/Shared/Views/Chat/ContactPreferencesView.swift index f3febd5dec..32fe15c41c 100644 --- a/apps/ios/Shared/Views/Chat/ContactPreferencesView.swift +++ b/apps/ios/Shared/Views/Chat/ContactPreferencesView.swift @@ -10,37 +10,25 @@ import SwiftUI import SimpleXChat struct ContactPreferencesView: View { - @State var allowFullDeletion = ContactFeatureAllowed.yes - @State var allowVoice = ContactFeatureAllowed.yes - @State var prefs = ContactUserPreferences( - fullDelete: ContactUserPreference( - enabled: FeatureEnabled(forUser: true, forContact: true), - userPreference: .user(preference: Preference(allow: .yes)), - contactPreference: Preference(allow: .no) - ), - voice: ContactUserPreference( - enabled: FeatureEnabled(forUser: true, forContact: true), - userPreference: .user(preference: Preference(allow: .yes)), - contactPreference: Preference(allow: .no) - ) - ) + @EnvironmentObject var chatModel: ChatModel + @Environment(\.dismiss) var dismiss: DismissAction + @Binding var contact: Contact + @State var featuresAllowed: ContactFeaturesAllowed + @State var currentFeaturesAllowed: ContactFeaturesAllowed var body: some View { + let user: User = chatModel.currentUser! + VStack { List { - featureSection(.fullDelete, .yes, prefs.fullDelete, $allowFullDeletion) - featureSection(.voice, .yes, prefs.voice, $allowVoice) + featureSection(.fullDelete, user.fullPreferences.fullDelete.allow, contact.mergedPreferences.fullDelete, $featuresAllowed.fullDelete) + featureSection(.voice, user.fullPreferences.voice.allow, contact.mergedPreferences.voice, $featuresAllowed.voice) Section { - HStack { - Text("Reset") - Spacer() - Text("Save") - } - .foregroundColor(.accentColor) - .disabled(true) + Button("Reset", role: .destructive) { featuresAllowed = currentFeaturesAllowed } + Button("Save (and notify contact)") { savePreferences() } } - .listRowBackground(Color.clear) + .disabled(currentFeaturesAllowed == featuresAllowed) } } } @@ -57,11 +45,7 @@ struct ContactPreferencesView: View { } } .frame(height: 36) - HStack { - Text("Contact allows") - Spacer() - Text(pref.contactPreference.allow.text) - } + infoRow("Contact allows", pref.contactPreference.allow.text) } header: { HStack { Image(systemName: "\(feature.icon).fill") @@ -73,10 +57,32 @@ struct ContactPreferencesView: View { .frame(height: 36, alignment: .topLeading) } } + + private func savePreferences() { + Task { + do { + let prefs = contactFeaturesAllowedToPrefs(featuresAllowed) + if let toContact = try await apiSetContactPrefs(contactId: contact.contactId, preferences: prefs) { + await MainActor.run { + contact = toContact + chatModel.updateContact(toContact) + currentFeaturesAllowed = featuresAllowed + dismiss() + } + } + } catch { + logger.error("ContactPreferencesView apiSetContactPrefs error: \(responseError(error))") + } + } + } } struct ContactPreferencesView_Previews: PreviewProvider { static var previews: some View { - ContactPreferencesView() + ContactPreferencesView( + contact: Binding.constant(Contact.sampleData), + featuresAllowed: ContactFeaturesAllowed.sampleData, + currentFeaturesAllowed: ContactFeaturesAllowed.sampleData + ) } } diff --git a/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift index 57ecb687e1..288a30807f 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift @@ -13,7 +13,7 @@ struct GroupChatInfoView: View { @EnvironmentObject var chatModel: ChatModel @Environment(\.dismiss) var dismiss: DismissAction @ObservedObject var chat: Chat - var groupInfo: GroupInfo + @State var groupInfo: GroupInfo @ObservedObject private var alertManager = AlertManager.shared @State private var alert: GroupChatInfoViewAlert? = nil @State private var groupLink: String? @@ -42,6 +42,24 @@ struct GroupChatInfoView: View { groupInfoHeader() .listRowBackground(Color.clear) + Section { + NavigationLink { + GroupPreferencesView( + groupInfo: $groupInfo, + preferences: groupInfo.fullGroupPreferences, + currentPreferences: groupInfo.fullGroupPreferences + ) + .navigationBarTitle("Group preferences") + .navigationBarTitleDisplayMode(.large) + } label: { + Text("Group preferences") + } + } header: { + Text("Preferences") + } footer: { + Text("Only group owners can change group preferences.") + } + Section("\(members.count + 1) members") { if groupInfo.canAddMembers { groupLinkButton() diff --git a/apps/ios/Shared/Views/Chat/Group/GroupPreferencesView.swift b/apps/ios/Shared/Views/Chat/Group/GroupPreferencesView.swift new file mode 100644 index 0000000000..dd546666de --- /dev/null +++ b/apps/ios/Shared/Views/Chat/Group/GroupPreferencesView.swift @@ -0,0 +1,86 @@ +// +// GroupPreferencesView.swift +// SimpleX (iOS) +// +// Created by JRoberts on 16.11.2022. +// Copyright © 2022 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +struct GroupPreferencesView: View { + @EnvironmentObject var chatModel: ChatModel + @Environment(\.dismiss) var dismiss: DismissAction + @Binding var groupInfo: GroupInfo + @State var preferences: FullGroupPreferences + @State var currentPreferences: FullGroupPreferences + + var body: some View { + VStack { + List { + featureSection(.fullDelete, $preferences.fullDelete.enable) + featureSection(.voice, $preferences.voice.enable) + + if groupInfo.canEdit { + Section { + Button("Reset", role: .destructive) { preferences = currentPreferences } + Button("Save (and notify group members)") { savePreferences() } + } + .disabled(currentPreferences == preferences) + } + } + } + } + + private func featureSection(_ feature: Feature, _ enableFeature: Binding) -> some View { + Section { + if (groupInfo.canEdit) { + settingsRow(feature.icon) { + Picker(feature.text, selection: enableFeature) { + ForEach(GroupFeatureEnabled.values) { enable in + Text(enable.text) + } + } + .frame(height: 36) + } + } + else { + settingsRow(feature.icon) { + infoRow(feature.text, enableFeature.wrappedValue.text) + } + } + } footer: { + Text(feature.enableGroupPrefDescription(enableFeature.wrappedValue, groupInfo.canEdit)) + .frame(height: 36, alignment: .topLeading) + } + } + + private func savePreferences() { + Task { + do { + var gp = groupInfo.groupProfile + gp.groupPreferences = toGroupPreferences(preferences) + let gInfo = try await apiUpdateGroup(groupInfo.groupId, gp) + await MainActor.run { + groupInfo = gInfo + chatModel.updateGroup(gInfo) + currentPreferences = preferences + dismiss() + } + } catch { + logger.error("GroupPreferencesView apiUpdateGroup error: \(responseError(error))") + } + } + } +} + +struct GroupPreferencesView_Previews: PreviewProvider { + static var previews: some View { + GroupPreferencesView( + groupInfo: Binding.constant(GroupInfo.sampleData), + preferences: FullGroupPreferences.sampleData, + currentPreferences: FullGroupPreferences.sampleData + ) + } +} diff --git a/apps/ios/Shared/Views/UserSettings/PreferencesView.swift b/apps/ios/Shared/Views/UserSettings/PreferencesView.swift index 9b7f8c7882..5db7c3724d 100644 --- a/apps/ios/Shared/Views/UserSettings/PreferencesView.swift +++ b/apps/ios/Shared/Views/UserSettings/PreferencesView.swift @@ -10,25 +10,23 @@ import SwiftUI import SimpleXChat struct PreferencesView: View { - @State var allowFullDeletion = FeatureAllowed.yes - @State var allowVoice = FeatureAllowed.yes + @EnvironmentObject var chatModel: ChatModel + @Environment(\.dismiss) var dismiss: DismissAction + @State var profile: LocalProfile + @State var preferences: FullPreferences + @State var currentPreferences: FullPreferences var body: some View { VStack { List { - featureSection(.fullDelete, $allowFullDeletion) - featureSection(.voice, $allowVoice) + featureSection(.fullDelete, $preferences.fullDelete.allow) + featureSection(.voice, $preferences.voice.allow) Section { - HStack { - Text("Reset") - Spacer() - Text("Save") - } - .foregroundColor(.accentColor) - .disabled(true) + Button("Reset", role: .destructive) { preferences = currentPreferences } + Button("Save (and notify contacts)") { savePreferences() } } - .listRowBackground(Color.clear) + .disabled(currentPreferences == preferences) } } } @@ -48,10 +46,35 @@ struct PreferencesView: View { .frame(height: 36, alignment: .topLeading) } } + + private func savePreferences() { + Task { + do { + var p = fromLocalProfile(profile) + p.preferences = toPreferences(preferences) + if let newProfile = try await apiUpdateProfile(profile: p) { + await MainActor.run { + if let profileId = chatModel.currentUser?.profile.profileId { + chatModel.currentUser?.profile = toLocalProfile(profileId, newProfile, "") + chatModel.currentUser?.fullPreferences = preferences + } + currentPreferences = preferences + dismiss() + } + } + } catch { + logger.error("PreferencesView apiUpdateProfile error: \(responseError(error))") + } + } + } } struct PreferencesView_Previews: PreviewProvider { static var previews: some View { - PreferencesView() + PreferencesView( + profile: LocalProfile(profileId: 1, displayName: "alice", fullName: "", localAlias: ""), + preferences: FullPreferences.sampleData, + currentPreferences: FullPreferences.sampleData + ) } } diff --git a/apps/ios/Shared/Views/UserSettings/SettingsView.swift b/apps/ios/Shared/Views/UserSettings/SettingsView.swift index 3859d932dd..7c9fb500e0 100644 --- a/apps/ios/Shared/Views/UserSettings/SettingsView.swift +++ b/apps/ios/Shared/Views/UserSettings/SettingsView.swift @@ -121,7 +121,7 @@ struct SettingsView: View { Section("Settings") { NavigationLink { - PreferencesView() + PreferencesView(profile: user.profile, preferences: user.fullPreferences, currentPreferences: user.fullPreferences) .navigationTitle("Your preferences") } label: { settingsRow("list.bullet") { Text("Chat preferences") } 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 f21eeb8f1b..f01578bd1b 100644 --- a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff +++ b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff @@ -278,6 +278,36 @@ Alle Ihre Kontakte bleiben verbunden. No comment provided by engineer. + + Allow irreversible message deletion only if your contact allows it to you. + ***Allow irreversible message deletion only if your contact allows it to you. + No comment provided by engineer. + + + Allow to irreversibly delete sent messages. + ***Allow to irreversibly delete sent messages. + No comment provided by engineer. + + + Allow to send voice messages. + ***Allow to send voice messages. + No comment provided by engineer. + + + Allow voice messages only if your contact allows them. + ***Allow voice messages only if your contact allows them. + No comment provided by engineer. + + + Allow your contacts to irreversibly delete sent messages. + ***Allow your contacts to irreversibly delete sent messages. + No comment provided by engineer. + + + Allow your contacts to send voice messages. + ***Allow your contacts to send voice messages. + No comment provided by engineer. + Already connected? Sind Sie bereits verbunden? @@ -328,6 +358,16 @@ Automatisch No comment provided by engineer. + + Both you and your contact can irreversibly delete sent messages. + ***Both you and your contact can irreversibly delete sent messages. + No comment provided by engineer. + + + Both you and your contact can send voice messages. + ***Both you and your contact can send voice messages. + No comment provided by engineer. + Call already ended! Anruf ist bereits beendet! @@ -428,6 +468,11 @@ Der Chat ist beendet No comment provided by engineer. + + Chat preferences + ***Chat preferences + No comment provided by engineer. + Chats Chats @@ -558,6 +603,11 @@ Verbindungszeitüberschreitung No comment provided by engineer. + + Contact allows + ***Contact allows + No comment provided by engineer. + Contact already exists Der Kontakt ist bereits vorhanden @@ -588,11 +638,21 @@ Kontaktname No comment provided by engineer. + + Contact preferences + ***Contact preferences + No comment provided by engineer. + Contact requests Kontaktanfragen No comment provided by engineer. + + Contacts can mark messages for deletion; you will be able to view them. + ***Contacts can mark messages for deletion; you will be able to view them. + No comment provided by engineer. + Copy Kopieren @@ -1221,6 +1281,11 @@ Für Konsole No comment provided by engineer. + + Full deletion + ***Full deletion + No comment provided by engineer. + Full name (optional) Vollständiger Name (optional) @@ -1271,11 +1336,26 @@ Gruppen-Link No comment provided by engineer. + + Group members can irreversibly delete sent messages. + ***Group members can irreversibly delete sent messages. + No comment provided by engineer. + + + Group members can send voice messages. + ***Group members can send voice messages. + No comment provided by engineer. + Group message: Grppennachricht: notification + + Group preferences + ***Group preferences + No comment provided by engineer. + Group profile is stored on members' devices, not on the servers. Das Gruppenprofil wird nur auf den Mitglieds-Systemen gespeichtert und nicht auf den Servern. @@ -1453,6 +1533,11 @@ In Gruppe einladen No comment provided by engineer. + + Irreversible message deletion is prohibited in this chat. + ***Irreversible message deletion is prohibited in this chat. + No comment provided by engineer. + It allows having many anonymous connections without any shared data between them in a single chat profile. Er ermöglicht mehrere anonyme Verbindungen in einem einzigen Chat-Profil ohne Daten zwischen diesen zu teilen. @@ -1768,6 +1853,31 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Nur die Endgeräte speichern Benutzerprofile, Kontakte, Gruppen und Nachrichten, die über eine **2-Schichten Ende-zu-Ende-Verschlüsselung** gesendet werden. No comment provided by engineer. + + Only group owners can change group preferences. + ***Only group owners can change group preferences. + No comment provided by engineer. + + + Only you can irreversibly delete messages (your contact can mark them for deletion). + ***Only you can irreversibly delete messages (your contact can mark them for deletion). + No comment provided by engineer. + + + Only you can send voice messages. + ***Only you can send voice messages. + No comment provided by engineer. + + + Only your contact can irreversibly delete messages (you can mark them for deletion). + ***Only your contact can irreversibly delete messages (you can mark them for deletion). + No comment provided by engineer. + + + Only your contact can send voice messages. + ***Only your contact can send voice messages. + No comment provided by engineer. + Open Settings Geräte-Einstellungen öffnen @@ -1858,6 +1968,11 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Bitte bewahren Sie das Passwort sicher auf, Sie können es NICHT mehr ändern, wenn Sie es vergessen haben oder verlieren. No comment provided by engineer. + + Preferences + ***Preferences + No comment provided by engineer. + Privacy & security Datenschutz & Sicherheit @@ -1873,6 +1988,16 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Profilbild No comment provided by engineer. + + Prohibit irreversible message deletion. + ***Prohibit irreversible message deletion. + No comment provided by engineer. + + + Prohibit sending voice messages. + ***Prohibit sending voice messages. + No comment provided by engineer. + Protocol timeout Protokollzeitüberschreitung @@ -1968,6 +2093,11 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Erforderlich No comment provided by engineer. + + Reset + ***Reset + No comment provided by engineer. + Reset colors Farben zurücksetzen @@ -2038,11 +2168,21 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Speichern chat item action + + Save (and notify contact) + ***Save (and notify contact) + No comment provided by engineer. + Save (and notify contacts) Speichern (und Kontakte benachrichtigen) No comment provided by engineer. + + Save (and notify group members) + ***Save (and notify group members) + No comment provided by engineer. + Save archive Archiv speichern @@ -2572,6 +2712,16 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Videoanruf No comment provided by engineer. + + Voice messages + ***Voice messages + No comment provided by engineer. + + + Voice messages are prohibited in this chat. + ***Voice messages are prohibited in this chat. + No comment provided by engineer. + Waiting for file Warte auf Datei @@ -2627,6 +2777,11 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Sie haben die Verbindung akzeptiert No comment provided by engineer. + + You allow + ***You allow + No comment provided by engineer. + You are already connected to %@. Sie sind bereits mit %@ verbunden. @@ -2844,6 +2999,11 @@ Sie können diese Verbindung abbrechen und den Kontakt entfernen (und es später Ihre aktuelle Chat-Datenbank wird GELÖSCHT und durch die Importierte ERSETZT. No comment provided by engineer. + + Your preferences + ***Your preferences + No comment provided by engineer. + Your privacy Meine Privatsphäre @@ -2916,6 +3076,11 @@ SimpleX-Server können Ihr Profil nicht einsehen. Admin member role + + always + ***always + pref value + audio call (not e2e encrypted) Audioanruf (nicht E2E verschlüsselt) @@ -3056,6 +3221,11 @@ SimpleX-Server können Ihr Profil nicht einsehen. Ersteller No comment provided by engineer. + + default (%@) + ***default (%@) + pref value + deleted Gelöscht @@ -3206,11 +3376,26 @@ SimpleX-Server können Ihr Profil nicht einsehen. Neue Nachricht notification + + no + ***no + pref value + no e2e encryption Keine E2E-Verschlüsselung No comment provided by engineer. + + off + ***off + group pref value + + + on + ***on + group pref value + or chat with the developers oder chatten Sie mit den Entwicklern @@ -3336,6 +3521,11 @@ SimpleX-Server können Ihr Profil nicht einsehen. möchte sich mit Ihnen verbinden! No comment provided by engineer. + + yes + ***yes + pref value + you are invited to group Sie sind zur Gruppe eingeladen 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 1c20d8e047..0d3432d87d 100644 --- a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff +++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff @@ -278,6 +278,36 @@ All your contacts will remain connected No comment provided by engineer. + + Allow irreversible message deletion only if your contact allows it to you. + Allow irreversible message deletion only if your contact allows it to you. + No comment provided by engineer. + + + Allow to irreversibly delete sent messages. + Allow to irreversibly delete sent messages. + No comment provided by engineer. + + + Allow to send voice messages. + Allow to send voice messages. + No comment provided by engineer. + + + Allow voice messages only if your contact allows them. + Allow voice messages only if your contact allows them. + No comment provided by engineer. + + + Allow your contacts to irreversibly delete sent messages. + Allow your contacts to irreversibly delete sent messages. + No comment provided by engineer. + + + Allow your contacts to send voice messages. + Allow your contacts to send voice messages. + No comment provided by engineer. + Already connected? Already connected? @@ -328,6 +358,16 @@ Automatically No comment provided by engineer. + + Both you and your contact can irreversibly delete sent messages. + Both you and your contact can irreversibly delete sent messages. + No comment provided by engineer. + + + Both you and your contact can send voice messages. + Both you and your contact can send voice messages. + No comment provided by engineer. + Call already ended! Call already ended! @@ -428,6 +468,11 @@ Chat is stopped No comment provided by engineer. + + Chat preferences + Chat preferences + No comment provided by engineer. + Chats Chats @@ -558,6 +603,11 @@ Connection timeout No comment provided by engineer. + + Contact allows + Contact allows + No comment provided by engineer. + Contact already exists Contact already exists @@ -588,11 +638,21 @@ Contact name No comment provided by engineer. + + Contact preferences + Contact preferences + No comment provided by engineer. + Contact requests Contact requests No comment provided by engineer. + + Contacts can mark messages for deletion; you will be able to view them. + Contacts can mark messages for deletion; you will be able to view them. + No comment provided by engineer. + Copy Copy @@ -1221,6 +1281,11 @@ For console No comment provided by engineer. + + Full deletion + Full deletion + No comment provided by engineer. + Full name (optional) Full name (optional) @@ -1271,11 +1336,26 @@ Group link No comment provided by engineer. + + Group members can irreversibly delete sent messages. + Group members can irreversibly delete sent messages. + No comment provided by engineer. + + + Group members can send voice messages. + Group members can send voice messages. + No comment provided by engineer. + Group message: Group message: notification + + Group preferences + Group preferences + No comment provided by engineer. + Group profile is stored on members' devices, not on the servers. Group profile is stored on members' devices, not on the servers. @@ -1453,6 +1533,11 @@ Invite to group No comment provided by engineer. + + Irreversible message deletion is prohibited in this chat. + Irreversible message deletion is prohibited in this chat. + No comment provided by engineer. + It allows having many anonymous connections without any shared data between them in a single chat profile. It allows having many anonymous connections without any shared data between them in a single chat profile. @@ -1768,6 +1853,31 @@ We will be adding server redundancy to prevent lost messages. Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. No comment provided by engineer. + + Only group owners can change group preferences. + Only group owners can change group preferences. + No comment provided by engineer. + + + Only you can irreversibly delete messages (your contact can mark them for deletion). + Only you can irreversibly delete messages (your contact can mark them for deletion). + No comment provided by engineer. + + + Only you can send voice messages. + Only you can send voice messages. + No comment provided by engineer. + + + Only your contact can irreversibly delete messages (you can mark them for deletion). + Only your contact can irreversibly delete messages (you can mark them for deletion). + No comment provided by engineer. + + + Only your contact can send voice messages. + Only your contact can send voice messages. + No comment provided by engineer. + Open Settings Open Settings @@ -1858,6 +1968,11 @@ We will be adding server redundancy to prevent lost messages. Please store passphrase securely, you will NOT be able to change it if you lose it. No comment provided by engineer. + + Preferences + Preferences + No comment provided by engineer. + Privacy & security Privacy & security @@ -1873,6 +1988,16 @@ We will be adding server redundancy to prevent lost messages. Profile image No comment provided by engineer. + + Prohibit irreversible message deletion. + Prohibit irreversible message deletion. + No comment provided by engineer. + + + Prohibit sending voice messages. + Prohibit sending voice messages. + No comment provided by engineer. + Protocol timeout Protocol timeout @@ -1968,6 +2093,11 @@ We will be adding server redundancy to prevent lost messages. Required No comment provided by engineer. + + Reset + Reset + No comment provided by engineer. + Reset colors Reset colors @@ -2038,11 +2168,21 @@ We will be adding server redundancy to prevent lost messages. Save chat item action + + Save (and notify contact) + Save (and notify contact) + No comment provided by engineer. + Save (and notify contacts) Save (and notify contacts) No comment provided by engineer. + + Save (and notify group members) + Save (and notify group members) + No comment provided by engineer. + Save archive Save archive @@ -2572,6 +2712,16 @@ To connect, please ask your contact to create another connection link and check Video call No comment provided by engineer. + + Voice messages + Voice messages + No comment provided by engineer. + + + Voice messages are prohibited in this chat. + Voice messages are prohibited in this chat. + No comment provided by engineer. + Waiting for file Waiting for file @@ -2627,6 +2777,11 @@ To connect, please ask your contact to create another connection link and check You accepted connection No comment provided by engineer. + + You allow + You allow + No comment provided by engineer. + You are already connected to %@. You are already connected to %@. @@ -2844,6 +2999,11 @@ You can cancel this connection and remove the contact (and try later with a new Your current chat database will be DELETED and REPLACED with the imported one. No comment provided by engineer. + + Your preferences + Your preferences + No comment provided by engineer. + Your privacy Your privacy @@ -2916,6 +3076,11 @@ SimpleX servers cannot see your profile. admin member role + + always + always + pref value + audio call (not e2e encrypted) audio call (not e2e encrypted) @@ -3056,6 +3221,11 @@ SimpleX servers cannot see your profile. creator No comment provided by engineer. + + default (%@) + default (%@) + pref value + deleted deleted @@ -3206,11 +3376,26 @@ SimpleX servers cannot see your profile. new message notification + + no + no + pref value + no e2e encryption no e2e encryption No comment provided by engineer. + + off + off + group pref value + + + on + on + group pref value + or chat with the developers or chat with the developers @@ -3336,6 +3521,11 @@ SimpleX servers cannot see your profile. wants to connect to you! No comment provided by engineer. + + yes + yes + pref value + you are invited to group you are invited to group 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 711c67fc54..a5f1c75edb 100644 --- a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff @@ -278,6 +278,36 @@ Все контакты, которые соединились через этот адрес, сохранятся. No comment provided by engineer. + + Allow irreversible message deletion only if your contact allows it to you. + Разрешить необратимое удаление сообщений, только если ваш контакт разрешает это вам. + No comment provided by engineer. + + + Allow to irreversibly delete sent messages. + Разрешить необратимо удалять отправленные сообщения. + No comment provided by engineer. + + + Allow to send voice messages. + Разрешить отправлять голосовые сообщения. + No comment provided by engineer. + + + Allow voice messages only if your contact allows them. + Разрешить голосовые сообщения, только если их разрешает ваш контакт. + No comment provided by engineer. + + + Allow your contacts to irreversibly delete sent messages. + Разрешить вашим контактам необратимо удалять отправленные сообщения. + No comment provided by engineer. + + + Allow your contacts to send voice messages. + Разрешить вашим контактам отправлять голосовые сообщения. + No comment provided by engineer. + Already connected? Соединение уже установлено? @@ -328,6 +358,16 @@ Автоматически No comment provided by engineer. + + Both you and your contact can irreversibly delete sent messages. + Вы и ваш контакт можете необратимо удалять отправленные сообщения. + No comment provided by engineer. + + + Both you and your contact can send voice messages. + Вы и ваш контакт можете отправлять голосовые сообщения. + No comment provided by engineer. + Call already ended! Звонок уже завершен! @@ -428,6 +468,11 @@ Чат остановлен No comment provided by engineer. + + Chat preferences + Предпочтения + No comment provided by engineer. + Chats Чаты @@ -558,6 +603,11 @@ Превышено время соединения No comment provided by engineer. + + Contact allows + Контакт разрешает + No comment provided by engineer. + Contact already exists Существующий контакт @@ -588,11 +638,21 @@ Имена контактов No comment provided by engineer. + + Contact preferences + Предпочтения контакта + No comment provided by engineer. + Contact requests Запросы контактов No comment provided by engineer. + + Contacts can mark messages for deletion; you will be able to view them. + Контакты могут помечать сообщения для удаления; вы сможете просмотреть их. + No comment provided by engineer. + Copy Скопировать @@ -1221,6 +1281,11 @@ Для консоли No comment provided by engineer. + + Full deletion + Полное удаление + No comment provided by engineer. + Full name (optional) Полное имя (не обязательно) @@ -1271,11 +1336,26 @@ Ссылка группы No comment provided by engineer. + + Group members can irreversibly delete sent messages. + Члены группы могут необратимо удалять отправленные сообщения. + No comment provided by engineer. + + + Group members can send voice messages. + Члены группы могут отправлять голосовые сообщения. + No comment provided by engineer. + Group message: Групповое сообщение: notification + + Group preferences + Предпочтения группы + No comment provided by engineer. + Group profile is stored on members' devices, not on the servers. Профиль группы хранится на устройствах членов, а не на серверах. @@ -1453,6 +1533,11 @@ Пригласить в группу No comment provided by engineer. + + Irreversible message deletion is prohibited in this chat. + Необратимое удаление сообщений запрещено в этом чате. + No comment provided by engineer. + It allows having many anonymous connections without any shared data between them in a single chat profile. Это позволяет иметь много анонимных соединений без общих данных между ними в одном профиле пользователя. @@ -1768,6 +1853,31 @@ We will be adding server redundancy to prevent lost messages. Только пользовательские устройства хранят контакты, группы и сообщения, которые отправляются **с двухуровневым end-to-end шифрованием** No comment provided by engineer. + + Only group owners can change group preferences. + Только владельцы группы могут изменять предпочтения группы. + No comment provided by engineer. + + + Only you can irreversibly delete messages (your contact can mark them for deletion). + Только вы можете необратимо удалять сообщения (ваш контакт может помечать их на удаление). + No comment provided by engineer. + + + Only you can send voice messages. + Только вы можете отправлять голосовые сообщения. + No comment provided by engineer. + + + Only your contact can irreversibly delete messages (you can mark them for deletion). + Только ваш контакт может необратимо удалять сообщения (вы можете помечать их на удаление). + No comment provided by engineer. + + + Only your contact can send voice messages. + Только ваш контакт может отправлять голосовые сообщения. + No comment provided by engineer. + Open Settings Открыть Настройки @@ -1858,6 +1968,11 @@ We will be adding server redundancy to prevent lost messages. Пожалуйста, надежно сохраните пароль, вы НЕ сможете его поменять, если потеряете. No comment provided by engineer. + + Preferences + Предпочтения + No comment provided by engineer. + Privacy & security Конфиденциальность @@ -1873,6 +1988,16 @@ We will be adding server redundancy to prevent lost messages. Аватар No comment provided by engineer. + + Prohibit irreversible message deletion. + Запретить необратимое удаление сообщений. + No comment provided by engineer. + + + Prohibit sending voice messages. + Запретить отправлять голосовые сообщений. + No comment provided by engineer. + Protocol timeout Таймаут протокола @@ -1968,6 +2093,11 @@ We will be adding server redundancy to prevent lost messages. Обязательно No comment provided by engineer. + + Reset + Сбросить + No comment provided by engineer. + Reset colors Сбросить цвета @@ -2038,11 +2168,21 @@ We will be adding server redundancy to prevent lost messages. Сохранить chat item action + + Save (and notify contact) + Сохранить (и уведомить контакт) + No comment provided by engineer. + Save (and notify contacts) Сохранить (и уведомить контакты) No comment provided by engineer. + + Save (and notify group members) + Сохранить (и уведомить членов группы) + No comment provided by engineer. + Save archive Сохранить архив @@ -2572,6 +2712,16 @@ To connect, please ask your contact to create another connection link and check Видеозвонок No comment provided by engineer. + + Voice messages + Голосовые сообщения + No comment provided by engineer. + + + Voice messages are prohibited in this chat. + Голосовые сообщения запрещены в этом чате. + No comment provided by engineer. + Waiting for file Ожидается прием файла @@ -2627,6 +2777,11 @@ To connect, please ask your contact to create another connection link and check Вы приняли приглашение соединиться No comment provided by engineer. + + You allow + Вы разрешаете + No comment provided by engineer. + You are already connected to %@. Вы уже соединены с контактом %@. @@ -2844,6 +2999,11 @@ You can cancel this connection and remove the contact (and try later with a new Текущие данные вашего чата будет УДАЛЕНЫ и ЗАМЕНЕНЫ импортированными. No comment provided by engineer. + + Your preferences + Ваши предпочтения + No comment provided by engineer. + Your privacy Конфиденциальность @@ -2916,6 +3076,11 @@ SimpleX серверы не могут получить доступ к ваше админ member role + + always + всегда + pref value + audio call (not e2e encrypted) аудиозвонок (не e2e зашифрованный) @@ -3056,6 +3221,11 @@ SimpleX серверы не могут получить доступ к ваше создатель No comment provided by engineer. + + default (%@) + по умолчанию (%@) + pref value + deleted удалено @@ -3206,11 +3376,26 @@ SimpleX серверы не могут получить доступ к ваше новое сообщение notification + + no + нет + pref value + no e2e encryption нет e2e шифрования No comment provided by engineer. + + off + нет + group pref value + + + on + да + group pref value + or chat with the developers или соединитесь с разработчиками @@ -3336,6 +3521,11 @@ SimpleX серверы не могут получить доступ к ваше хочет соединиться с вами! No comment provided by engineer. + + yes + да + pref value + you are invited to group вы приглашены в группу diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index a6981fd9f3..3f1119315c 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -129,6 +129,7 @@ 5CFE0921282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; }; 5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; }; 640F50E327CF991C001E05C2 /* SMPServers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640F50E227CF991C001E05C2 /* SMPServers.swift */; }; + 6432857C2925443C00FBE5C8 /* GroupPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6432857B2925443C00FBE5C8 /* GroupPreferencesView.swift */; }; 6440CA00288857A10062C672 /* CIEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440C9FF288857A10062C672 /* CIEventView.swift */; }; 6440CA03288AECA70062C672 /* AddGroupMembersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440CA02288AECA70062C672 /* AddGroupMembersView.swift */; }; 6442E0BA287F169300CEC0F9 /* AddGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6442E0B9287F169300CEC0F9 /* AddGroupView.swift */; }; @@ -332,6 +333,7 @@ 5CFA59CF286477B400863A68 /* ChatArchiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatArchiveView.swift; sourceTree = ""; }; 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ZoomableScrollView.swift; path = Shared/Views/ZoomableScrollView.swift; sourceTree = SOURCE_ROOT; }; 640F50E227CF991C001E05C2 /* SMPServers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SMPServers.swift; sourceTree = ""; }; + 6432857B2925443C00FBE5C8 /* GroupPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupPreferencesView.swift; sourceTree = ""; }; 6440C9FF288857A10062C672 /* CIEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIEventView.swift; sourceTree = ""; }; 6440CA02288AECA70062C672 /* AddGroupMembersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGroupMembersView.swift; sourceTree = ""; }; 6442E0B9287F169300CEC0F9 /* AddGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGroupView.swift; sourceTree = ""; }; @@ -692,6 +694,7 @@ 6440CA01288AEC770062C672 /* Group */ = { isa = PBXGroup; children = ( + 6432857B2925443C00FBE5C8 /* GroupPreferencesView.swift */, 6440CA02288AECA70062C672 /* AddGroupMembersView.swift */, 6442E0BD2880182D00CEC0F9 /* GroupChatInfoView.swift */, 647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */, @@ -904,6 +907,7 @@ 5CB924E127A867BA00ACCCDD /* UserProfile.swift in Sources */, 5CB0BA9A2827FD8800B3292C /* HowItWorks.swift in Sources */, 5C13730B28156D2700F43030 /* ContactConnectionView.swift in Sources */, + 6432857C2925443C00FBE5C8 /* GroupPreferencesView.swift in Sources */, 5C93293129239BED0090FFF9 /* SMPServerView.swift in Sources */, 5C9CC7AD28C55D7800BEF955 /* DatabaseEncryptionView.swift in Sources */, 5CE4407927ADB701007B033A /* EmojiItemView.swift in Sources */, diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index 6569530190..518c7390a6 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -64,6 +64,7 @@ public enum ChatCommand { case apiClearChat(type: ChatType, id: Int64) case listContacts case apiUpdateProfile(profile: Profile) + case apiSetContactPrefs(contactId: Int64, preferences: Preferences) case apiSetContactAlias(contactId: Int64, localAlias: String) case apiSetConnectionAlias(connId: Int64, localAlias: String) case createMyAddress @@ -143,6 +144,7 @@ public enum ChatCommand { case let .apiClearChat(type, id): return "/_clear chat \(ref(type, id))" case .listContacts: return "/contacts" case let .apiUpdateProfile(profile): return "/_profile \(encodeJSON(profile))" + case let .apiSetContactPrefs(contactId, preferences): return "/_set prefs @\(contactId) \(encodeJSON(preferences))" case let .apiSetContactAlias(contactId, localAlias): return "/_set alias @\(contactId) \(localAlias.trimmingCharacters(in: .whitespaces))" case let .apiSetConnectionAlias(connId, localAlias): return "/_set alias :\(connId) \(localAlias.trimmingCharacters(in: .whitespaces))" case .createMyAddress: return "/address" @@ -221,6 +223,7 @@ public enum ChatCommand { case .apiClearChat: return "apiClearChat" case .listContacts: return "listContacts" case .apiUpdateProfile: return "apiUpdateProfile" + case .apiSetContactPrefs: return "apiSetContactPrefs" case .apiSetContactAlias: return "apiSetContactAlias" case .apiSetConnectionAlias: return "apiSetConnectionAlias" case .createMyAddress: return "createMyAddress" @@ -307,6 +310,7 @@ public enum ChatResponse: Decodable, Error { case userProfileUpdated(fromProfile: Profile, toProfile: Profile) case contactAliasUpdated(toContact: Contact) case connectionAliasUpdated(toConnection: PendingContactConnection) + case contactPrefsUpdated(fromContact: Contact, toContact: Contact) case userContactLink(contactLink: UserContactLink) case userContactLinkUpdated(contactLink: UserContactLink) case userContactLinkCreated(connReqContact: String) @@ -409,6 +413,7 @@ public enum ChatResponse: Decodable, Error { case .userProfileUpdated: return "userProfileUpdated" case .contactAliasUpdated: return "contactAliasUpdated" case .connectionAliasUpdated: return "connectionAliasUpdated" + case .contactPrefsUpdated: return "contactPrefsUpdated" case .userContactLink: return "userContactLink" case .userContactLinkUpdated: return "userContactLinkUpdated" case .userContactLinkCreated: return "userContactLinkCreated" @@ -511,6 +516,7 @@ public enum ChatResponse: Decodable, Error { case let .userProfileUpdated(_, toProfile): return String(describing: toProfile) case let .contactAliasUpdated(toContact): return String(describing: toContact) case let .connectionAliasUpdated(toConnection): return String(describing: toConnection) + case let .contactPrefsUpdated(fromContact, toContact): return "fromContact: \(String(describing: fromContact))\ntoContact: \(String(describing: toContact))" case let .userContactLink(contactLink): return contactLink.responseDetails case let .userContactLinkUpdated(contactLink): return contactLink.responseDetails case let .userContactLinkCreated(connReq): return connReq diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index 5f5dc545dd..4ce462a2ca 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -14,6 +14,7 @@ public struct User: Decodable, NamedChat { var userContactId: Int64 var localDisplayName: ContactName public var profile: LocalProfile + public var fullPreferences: FullPreferences var activeUser: Bool public var displayName: String { get { profile.displayName } } @@ -26,6 +27,7 @@ public struct User: Decodable, NamedChat { userContactId: 1, localDisplayName: "alice", profile: LocalProfile.sampleData, + fullPreferences: FullPreferences.sampleData, activeUser: true ) } @@ -35,15 +37,17 @@ public typealias ContactName = String public typealias GroupName = String public struct Profile: Codable, NamedChat { - public init(displayName: String, fullName: String, image: String? = nil) { + public init(displayName: String, fullName: String, image: String? = nil, preferences: Preferences? = nil) { self.displayName = displayName self.fullName = fullName self.image = image + self.preferences = preferences } public var displayName: String public var fullName: String public var image: String? + public var preferences: Preferences? public var localAlias: String { get { "" } } var profileViewName: String { @@ -57,11 +61,12 @@ public struct Profile: Codable, NamedChat { } public struct LocalProfile: Codable, NamedChat { - public init(profileId: Int64, displayName: String, fullName: String, image: String? = nil, localAlias: String) { + public init(profileId: Int64, displayName: String, fullName: String, image: String? = nil, preferences: Preferences? = nil, localAlias: String) { self.profileId = profileId self.displayName = displayName self.fullName = fullName self.image = image + self.preferences = preferences self.localAlias = localAlias } @@ -69,6 +74,7 @@ public struct LocalProfile: Codable, NamedChat { public var displayName: String public var fullName: String public var image: String? + public var preferences: Preferences? public var localAlias: String var profileViewName: String { @@ -81,6 +87,7 @@ public struct LocalProfile: Codable, NamedChat { profileId: 1, displayName: "alice", fullName: "Alice", + preferences: Preferences.sampleData, localAlias: "" ) } @@ -117,7 +124,7 @@ extension NamedChat { public typealias ChatId = String -public struct FullPreferences: Decodable { +public struct FullPreferences: Decodable, Equatable { public var fullDelete: Preference public var voice: Preference @@ -125,9 +132,27 @@ public struct FullPreferences: Decodable { self.fullDelete = fullDelete self.voice = voice } + + public static let sampleData = FullPreferences(fullDelete: Preference(allow: .no), voice: Preference(allow: .yes)) } -public struct Preference: Codable { +public struct Preferences: Codable { + public var fullDelete: Preference? + public var voice: Preference? + + public init(fullDelete: Preference?, voice: Preference?) { + self.fullDelete = fullDelete + self.voice = voice + } + + public static let sampleData = Preferences(fullDelete: Preference(allow: .no), voice: Preference(allow: .yes)) +} + +public func toPreferences(_ fullPreferences: FullPreferences) -> Preferences { + Preferences(fullDelete: fullPreferences.fullDelete, voice: fullPreferences.voice) +} + +public struct Preference: Codable, Equatable { public var allow: FeatureAllowed public init(allow: FeatureAllowed) { @@ -143,6 +168,19 @@ public struct ContactUserPreferences: Decodable { self.fullDelete = fullDelete self.voice = voice } + + public static let sampleData = ContactUserPreferences( + fullDelete: ContactUserPreference( + enabled: FeatureEnabled(forUser: false, forContact: false), + userPreference: .user(preference: Preference(allow: .no)), + contactPreference: Preference(allow: .no) + ), + voice: ContactUserPreference( + enabled: FeatureEnabled(forUser: true, forContact: true), + userPreference: .user(preference: Preference(allow: .yes)), + contactPreference: Preference(allow: .yes) + ) + ) } public struct ContactUserPreference: Decodable { @@ -241,6 +279,36 @@ public enum Feature { : "Voice messages are prohibited in this chat." } } + + public func enableGroupPrefDescription(_ enabled: GroupFeatureEnabled, _ canEdit: Bool) -> LocalizedStringKey { + if canEdit { + switch self { + case .fullDelete: + switch enabled { + case .on: return "Allow to irreversibly delete sent messages." + case .off: return "Prohibit irreversible message deletion." + } + case .voice: + switch enabled { + case .on: return "Allow to send voice messages." + case .off: return "Prohibit sending voice messages." + } + } + } else { + switch self { + case .fullDelete: + switch enabled { + case .on: return "Group members can irreversibly delete sent messages." + case .off: return "Irreversible message deletion is prohibited in this chat." + } + case .voice: + switch enabled { + case .on: return "Group members can send voice messages." + case .off: return "Voice messages are prohibited in this chat." + } + } + } + } } public enum ContactFeatureAllowed: Identifiable, Hashable { @@ -274,6 +342,56 @@ public enum ContactFeatureAllowed: Identifiable, Hashable { } } +public struct ContactFeaturesAllowed: Equatable { + public var fullDelete: ContactFeatureAllowed + public var voice: ContactFeatureAllowed + + public init(fullDelete: ContactFeatureAllowed, voice: ContactFeatureAllowed) { + self.fullDelete = fullDelete + self.voice = voice + } + + public static let sampleData = ContactFeaturesAllowed( + fullDelete: ContactFeatureAllowed.userDefault(.no), + voice: ContactFeatureAllowed.userDefault(.yes) + ) +} + +public func contactUserPrefsToFeaturesAllowed(_ contactUserPreferences: ContactUserPreferences) -> ContactFeaturesAllowed { + ContactFeaturesAllowed( + fullDelete: contactUserPrefToFeatureAllowed(contactUserPreferences.fullDelete), + voice: contactUserPrefToFeatureAllowed(contactUserPreferences.voice) + ) +} + +public func contactUserPrefToFeatureAllowed(_ contactUserPreference: ContactUserPreference) -> ContactFeatureAllowed { + switch contactUserPreference.userPreference { + case let .user(preference): return .userDefault(preference.allow) + case let .contact(preference): + switch preference.allow { + case .always: return .always + case .yes: return .yes + case .no: return .no + } + } +} + +public func contactFeaturesAllowedToPrefs(_ contactFeaturesAllowed: ContactFeaturesAllowed) -> Preferences { + Preferences( + fullDelete: contactFeatureAllowedToPref(contactFeaturesAllowed.fullDelete), + voice: contactFeatureAllowedToPref(contactFeaturesAllowed.voice) + ) +} + +public func contactFeatureAllowedToPref(_ contactFeatureAllowed: ContactFeatureAllowed) -> Preference? { + switch contactFeatureAllowed { + case .userDefault: return nil + case .always: return Preference(allow: .always) + case .yes: return Preference(allow: .yes) + case .no: return Preference(allow: .no) + } +} + public enum FeatureAllowed: String, Codable, Identifiable { case always case yes @@ -292,6 +410,58 @@ public enum FeatureAllowed: String, Codable, Identifiable { } } +public struct FullGroupPreferences: Decodable, Equatable { + public var fullDelete: GroupPreference + public var voice: GroupPreference + + public init(fullDelete: GroupPreference, voice: GroupPreference) { + self.fullDelete = fullDelete + self.voice = voice + } + + public static let sampleData = FullGroupPreferences(fullDelete: GroupPreference(enable: .off), voice: GroupPreference(enable: .on)) +} + +public struct GroupPreferences: Codable { + public var fullDelete: GroupPreference? + public var voice: GroupPreference? + + public init(fullDelete: GroupPreference?, voice: GroupPreference?) { + self.fullDelete = fullDelete + self.voice = voice + } + + public static let sampleData = GroupPreferences(fullDelete: GroupPreference(enable: .off), voice: GroupPreference(enable: .on)) +} + +public func toGroupPreferences(_ fullPreferences: FullGroupPreferences) -> GroupPreferences { + GroupPreferences(fullDelete: fullPreferences.fullDelete, voice: fullPreferences.voice) +} + +public struct GroupPreference: Codable, Equatable { + public var enable: GroupFeatureEnabled + + public init(enable: GroupFeatureEnabled) { + self.enable = enable + } +} + +public enum GroupFeatureEnabled: String, Codable, Identifiable { + case on + case off + + public static var values: [GroupFeatureEnabled] { [.on, .off] } + + public var id: Self { self } + + public var text: String { + switch self { + case .on: return NSLocalizedString("on", comment: "group pref value") + case .off: return NSLocalizedString("off", comment: "group pref value") + } + } +} + public enum ChatInfo: Identifiable, Decodable, NamedChat { case direct(contact: Contact) case group(groupInfo: GroupInfo) @@ -496,6 +666,8 @@ public struct Contact: Identifiable, Decodable, NamedChat { public var activeConn: Connection public var viaGroup: Int64? public var chatSettings: ChatSettings + public var userPreferences: Preferences + public var mergedPreferences: ContactUserPreferences var createdAt: Date var updatedAt: Date @@ -526,6 +698,8 @@ public struct Contact: Identifiable, Decodable, NamedChat { profile: LocalProfile.sampleData, activeConn: Connection.sampleData, chatSettings: ChatSettings.defaults, + userPreferences: Preferences.sampleData, + mergedPreferences: ContactUserPreferences.sampleData, createdAt: .now, updatedAt: .now ) @@ -731,6 +905,7 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat { public var groupId: Int64 var localDisplayName: GroupName public var groupProfile: GroupProfile + public var fullGroupPreferences: FullGroupPreferences public var membership: GroupMember public var hostConnCustomUserProfileId: Int64? public var chatSettings: ChatSettings @@ -762,6 +937,7 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat { groupId: 1, localDisplayName: "team", groupProfile: GroupProfile.sampleData, + fullGroupPreferences: FullGroupPreferences.sampleData, membership: GroupMember.sampleData, hostConnCustomUserProfileId: nil, chatSettings: ChatSettings.defaults, @@ -771,15 +947,17 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat { } public struct GroupProfile: Codable, NamedChat { - public init(displayName: String, fullName: String, image: String? = nil) { + public init(displayName: String, fullName: String, image: String? = nil, groupPreferences: GroupPreferences? = nil) { self.displayName = displayName self.fullName = fullName self.image = image + self.groupPreferences = groupPreferences } public var displayName: String public var fullName: String public var image: String? + public var groupPreferences: GroupPreferences? public var localAlias: String { "" } public static let sampleData = GroupProfile( diff --git a/apps/ios/de.lproj/Localizable.strings b/apps/ios/de.lproj/Localizable.strings index 49d88aeef8..6570a7e7a2 100644 --- a/apps/ios/de.lproj/Localizable.strings +++ b/apps/ios/de.lproj/Localizable.strings @@ -188,9 +188,30 @@ /* No comment provided by engineer. */ "All your contacts will remain connected" = "Alle Ihre Kontakte bleiben verbunden."; +/* No comment provided by engineer. */ +"Allow irreversible message deletion only if your contact allows it to you." = "***Allow irreversible message deletion only if your contact allows it to you."; + +/* No comment provided by engineer. */ +"Allow to irreversibly delete sent messages." = "***Allow to irreversibly delete sent messages."; + +/* No comment provided by engineer. */ +"Allow to send voice messages." = "***Allow to send voice messages."; + +/* No comment provided by engineer. */ +"Allow voice messages only if your contact allows them." = "***Allow voice messages only if your contact allows them."; + +/* No comment provided by engineer. */ +"Allow your contacts to irreversibly delete sent messages." = "***Allow your contacts to irreversibly delete sent messages."; + +/* No comment provided by engineer. */ +"Allow your contacts to send voice messages." = "***Allow your contacts to send voice messages."; + /* No comment provided by engineer. */ "Already connected?" = "Sind Sie bereits verbunden?"; +/* pref value */ +"always" = "***always"; + /* No comment provided by engineer. */ "Answer call" = "Anruf annehmen"; @@ -230,6 +251,12 @@ /* No comment provided by engineer. */ "bold" = "fett"; +/* No comment provided by engineer. */ +"Both you and your contact can irreversibly delete sent messages." = "***Both you and your contact can irreversibly delete sent messages."; + +/* No comment provided by engineer. */ +"Both you and your contact can send voice messages." = "***Both you and your contact can send voice messages."; + /* No comment provided by engineer. */ "Call already ended!" = "Anruf ist bereits beendet!"; @@ -314,6 +341,9 @@ /* No comment provided by engineer. */ "Chat is stopped" = "Der Chat ist beendet"; +/* No comment provided by engineer. */ +"Chat preferences" = "***Chat preferences"; + /* No comment provided by engineer. */ "Chats" = "Chats"; @@ -431,6 +461,9 @@ /* connection information */ "connection:%@" = "Verbindung:%@"; +/* No comment provided by engineer. */ +"Contact allows" = "***Contact allows"; + /* No comment provided by engineer. */ "Contact already exists" = "Der Kontakt ist bereits vorhanden"; @@ -455,9 +488,15 @@ /* No comment provided by engineer. */ "Contact name" = "Kontaktname"; +/* No comment provided by engineer. */ +"Contact preferences" = "***Contact preferences"; + /* No comment provided by engineer. */ "Contact requests" = "Kontaktanfragen"; +/* No comment provided by engineer. */ +"Contacts can mark messages for deletion; you will be able to view them." = "***Contacts can mark messages for deletion; you will be able to view them."; + /* chat item action */ "Copy" = "Kopieren"; @@ -542,6 +581,9 @@ /* No comment provided by engineer. */ "Decentralized" = "Dezentral"; +/* pref value */ +"default (%@)" = "***default (%@)"; + /* chat item action */ "Delete" = "Löschen"; @@ -857,6 +899,9 @@ /* No comment provided by engineer. */ "For console" = "Für Konsole"; +/* No comment provided by engineer. */ +"Full deletion" = "***Full deletion"; + /* No comment provided by engineer. */ "Full name (optional)" = "Vollständiger Name (optional)"; @@ -890,9 +935,18 @@ /* No comment provided by engineer. */ "Group link" = "Gruppen-Link"; +/* No comment provided by engineer. */ +"Group members can irreversibly delete sent messages." = "***Group members can irreversibly delete sent messages."; + +/* No comment provided by engineer. */ +"Group members can send voice messages." = "***Group members can send voice messages."; + /* notification */ "Group message:" = "Grppennachricht:"; +/* No comment provided by engineer. */ +"Group preferences" = "***Group preferences"; + /* No comment provided by engineer. */ "Group profile is stored on members' devices, not on the servers." = "Das Gruppenprofil wird nur auf den Mitglieds-Systemen gespeichtert und nicht auf den Servern."; @@ -1034,6 +1088,9 @@ /* No comment provided by engineer. */ "iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "Für die sichere Speicherung des Passworts nach dem Neustart der App und dem Wechsel des Passworts wird der iOS Schlüsselbund verwendet - dies erlaubt den Empfang von Push-Benachrichtigungen."; +/* No comment provided by engineer. */ +"Irreversible message deletion is prohibited in this chat." = "***Irreversible message deletion is prohibited in this chat."; + /* No comment provided by engineer. */ "It allows having many anonymous connections without any shared data between them in a single chat profile." = "Er ermöglicht mehrere anonyme Verbindungen in einem einzigen Chat-Profil ohne Daten zwischen diesen zu teilen."; @@ -1193,6 +1250,9 @@ /* No comment provided by engineer. */ "New passphrase…" = "Neues Passwort…"; +/* pref value */ +"no" = "***no"; + /* No comment provided by engineer. */ "No" = "Nein"; @@ -1220,6 +1280,9 @@ /* No comment provided by engineer. */ "Notifications are disabled!" = "Benachrichtigungen sind deaktiviert!"; +/* group pref value */ +"off" = "***off"; + /* No comment provided by engineer. */ "Off (Local)" = "Aus (Lokal)"; @@ -1232,6 +1295,9 @@ /* No comment provided by engineer. */ "Old database archive" = "Altes Datenbankarchiv"; +/* group pref value */ +"on" = "***on"; + /* No comment provided by engineer. */ "One-time invitation link" = "Einmal-Einladungslink"; @@ -1247,6 +1313,21 @@ /* No comment provided by engineer. */ "Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Nur die Endgeräte speichern Benutzerprofile, Kontakte, Gruppen und Nachrichten, die über eine **2-Schichten Ende-zu-Ende-Verschlüsselung** gesendet werden."; +/* No comment provided by engineer. */ +"Only group owners can change group preferences." = "***Only group owners can change group preferences."; + +/* No comment provided by engineer. */ +"Only you can irreversibly delete messages (your contact can mark them for deletion)." = "***Only you can irreversibly delete messages (your contact can mark them for deletion)."; + +/* No comment provided by engineer. */ +"Only you can send voice messages." = "***Only you can send voice messages."; + +/* No comment provided by engineer. */ +"Only your contact can irreversibly delete messages (you can mark them for deletion)." = "***Only your contact can irreversibly delete messages (you can mark them for deletion)."; + +/* No comment provided by engineer. */ +"Only your contact can send voice messages." = "***Only your contact can send voice messages."; + /* No comment provided by engineer. */ "Open chat" = "Chat öffnen"; @@ -1310,6 +1391,9 @@ /* No comment provided by engineer. */ "Please store passphrase securely, you will NOT be able to change it if you lose it." = "Bitte bewahren Sie das Passwort sicher auf, Sie können es NICHT mehr ändern, wenn Sie es vergessen haben oder verlieren."; +/* No comment provided by engineer. */ +"Preferences" = "***Preferences"; + /* No comment provided by engineer. */ "Privacy & security" = "Datenschutz & Sicherheit"; @@ -1319,6 +1403,12 @@ /* No comment provided by engineer. */ "Profile image" = "Profilbild"; +/* No comment provided by engineer. */ +"Prohibit irreversible message deletion." = "***Prohibit irreversible message deletion."; + +/* No comment provided by engineer. */ +"Prohibit sending voice messages." = "***Prohibit sending voice messages."; + /* No comment provided by engineer. */ "Protocol timeout" = "Protokollzeitüberschreitung"; @@ -1394,6 +1484,9 @@ /* No comment provided by engineer. */ "Required" = "Erforderlich"; +/* No comment provided by engineer. */ +"Reset" = "***Reset"; + /* No comment provided by engineer. */ "Reset colors" = "Farben zurücksetzen"; @@ -1430,9 +1523,15 @@ /* chat item action */ "Save" = "Speichern"; +/* No comment provided by engineer. */ +"Save (and notify contact)" = "***Save (and notify contact)"; + /* No comment provided by engineer. */ "Save (and notify contacts)" = "Speichern (und Kontakte benachrichtigen)"; +/* No comment provided by engineer. */ +"Save (and notify group members)" = "***Save (and notify group members)"; + /* No comment provided by engineer. */ "Save archive" = "Archiv speichern"; @@ -1793,6 +1892,12 @@ /* No comment provided by engineer. */ "video call (not e2e encrypted)" = "Videoanruf (nicht E2E verschlüsselt)"; +/* No comment provided by engineer. */ +"Voice messages" = "***Voice messages"; + +/* No comment provided by engineer. */ +"Voice messages are prohibited in this chat." = "***Voice messages are prohibited in this chat."; + /* No comment provided by engineer. */ "waiting for answer…" = "Warten auf Antwort…"; @@ -1829,12 +1934,18 @@ /* No comment provided by engineer. */ "Wrong passphrase!" = "Falsches Passwort!"; +/* pref value */ +"yes" = "***yes"; + /* No comment provided by engineer. */ "You" = "Meine Daten"; /* No comment provided by engineer. */ "You accepted connection" = "Sie haben die Verbindung akzeptiert"; +/* No comment provided by engineer. */ +"You allow" = "***You allow"; + /* No comment provided by engineer. */ "You are already connected to %@." = "Sie sind bereits mit %@ verbunden."; @@ -1988,6 +2099,9 @@ /* No comment provided by engineer. */ "Your ICE servers" = "Ihre ICE-Server"; +/* No comment provided by engineer. */ +"Your preferences" = "***Your preferences"; + /* No comment provided by engineer. */ "Your privacy" = "Meine Privatsphäre"; diff --git a/apps/ios/ru.lproj/Localizable.strings b/apps/ios/ru.lproj/Localizable.strings index dba6dd8577..aa42f56efc 100644 --- a/apps/ios/ru.lproj/Localizable.strings +++ b/apps/ios/ru.lproj/Localizable.strings @@ -188,9 +188,30 @@ /* No comment provided by engineer. */ "All your contacts will remain connected" = "Все контакты, которые соединились через этот адрес, сохранятся."; +/* No comment provided by engineer. */ +"Allow irreversible message deletion only if your contact allows it to you." = "Разрешить необратимое удаление сообщений, только если ваш контакт разрешает это вам."; + +/* No comment provided by engineer. */ +"Allow to irreversibly delete sent messages." = "Разрешить необратимо удалять отправленные сообщения."; + +/* No comment provided by engineer. */ +"Allow to send voice messages." = "Разрешить отправлять голосовые сообщения."; + +/* No comment provided by engineer. */ +"Allow voice messages only if your contact allows them." = "Разрешить голосовые сообщения, только если их разрешает ваш контакт."; + +/* No comment provided by engineer. */ +"Allow your contacts to irreversibly delete sent messages." = "Разрешить вашим контактам необратимо удалять отправленные сообщения."; + +/* No comment provided by engineer. */ +"Allow your contacts to send voice messages." = "Разрешить вашим контактам отправлять голосовые сообщения."; + /* No comment provided by engineer. */ "Already connected?" = "Соединение уже установлено?"; +/* pref value */ +"always" = "всегда"; + /* No comment provided by engineer. */ "Answer call" = "Принять звонок"; @@ -230,6 +251,12 @@ /* No comment provided by engineer. */ "bold" = "жирный"; +/* No comment provided by engineer. */ +"Both you and your contact can irreversibly delete sent messages." = "Вы и ваш контакт можете необратимо удалять отправленные сообщения."; + +/* No comment provided by engineer. */ +"Both you and your contact can send voice messages." = "Вы и ваш контакт можете отправлять голосовые сообщения."; + /* No comment provided by engineer. */ "Call already ended!" = "Звонок уже завершен!"; @@ -314,6 +341,9 @@ /* No comment provided by engineer. */ "Chat is stopped" = "Чат остановлен"; +/* No comment provided by engineer. */ +"Chat preferences" = "Предпочтения"; + /* No comment provided by engineer. */ "Chats" = "Чаты"; @@ -431,6 +461,9 @@ /* connection information */ "connection:%@" = "connection:%@"; +/* No comment provided by engineer. */ +"Contact allows" = "Контакт разрешает"; + /* No comment provided by engineer. */ "Contact already exists" = "Существующий контакт"; @@ -455,9 +488,15 @@ /* No comment provided by engineer. */ "Contact name" = "Имена контактов"; +/* No comment provided by engineer. */ +"Contact preferences" = "Предпочтения контакта"; + /* No comment provided by engineer. */ "Contact requests" = "Запросы контактов"; +/* No comment provided by engineer. */ +"Contacts can mark messages for deletion; you will be able to view them." = "Контакты могут помечать сообщения для удаления; вы сможете просмотреть их."; + /* chat item action */ "Copy" = "Скопировать"; @@ -542,6 +581,9 @@ /* No comment provided by engineer. */ "Decentralized" = "Децентрализованный"; +/* pref value */ +"default (%@)" = "по умолчанию (%@)"; + /* chat item action */ "Delete" = "Удалить"; @@ -857,6 +899,9 @@ /* No comment provided by engineer. */ "For console" = "Для консоли"; +/* No comment provided by engineer. */ +"Full deletion" = "Полное удаление"; + /* No comment provided by engineer. */ "Full name (optional)" = "Полное имя (не обязательно)"; @@ -890,9 +935,18 @@ /* No comment provided by engineer. */ "Group link" = "Ссылка группы"; +/* No comment provided by engineer. */ +"Group members can irreversibly delete sent messages." = "Члены группы могут необратимо удалять отправленные сообщения."; + +/* No comment provided by engineer. */ +"Group members can send voice messages." = "Члены группы могут отправлять голосовые сообщения."; + /* notification */ "Group message:" = "Групповое сообщение:"; +/* No comment provided by engineer. */ +"Group preferences" = "Предпочтения группы"; + /* No comment provided by engineer. */ "Group profile is stored on members' devices, not on the servers." = "Профиль группы хранится на устройствах членов, а не на серверах."; @@ -1034,6 +1088,9 @@ /* No comment provided by engineer. */ "iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "Пароль базы данных будет безопасно сохранен в iOS Keychain после запуска чата или изменения пароля - это позволит получать мгновенные уведомления."; +/* No comment provided by engineer. */ +"Irreversible message deletion is prohibited in this chat." = "Необратимое удаление сообщений запрещено в этом чате."; + /* No comment provided by engineer. */ "It allows having many anonymous connections without any shared data between them in a single chat profile." = "Это позволяет иметь много анонимных соединений без общих данных между ними в одном профиле пользователя."; @@ -1193,6 +1250,9 @@ /* No comment provided by engineer. */ "New passphrase…" = "Новый пароль…"; +/* pref value */ +"no" = "нет"; + /* No comment provided by engineer. */ "No" = "Нет"; @@ -1220,6 +1280,9 @@ /* No comment provided by engineer. */ "Notifications are disabled!" = "Уведомления выключены"; +/* group pref value */ +"off" = "нет"; + /* No comment provided by engineer. */ "Off (Local)" = "Выключить (Локальные)"; @@ -1232,6 +1295,9 @@ /* No comment provided by engineer. */ "Old database archive" = "Старый архив чата"; +/* group pref value */ +"on" = "да"; + /* No comment provided by engineer. */ "One-time invitation link" = "Одноразовая ссылка"; @@ -1247,6 +1313,21 @@ /* No comment provided by engineer. */ "Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Только пользовательские устройства хранят контакты, группы и сообщения, которые отправляются **с двухуровневым end-to-end шифрованием**"; +/* No comment provided by engineer. */ +"Only group owners can change group preferences." = "Только владельцы группы могут изменять предпочтения группы."; + +/* No comment provided by engineer. */ +"Only you can irreversibly delete messages (your contact can mark them for deletion)." = "Только вы можете необратимо удалять сообщения (ваш контакт может помечать их на удаление)."; + +/* No comment provided by engineer. */ +"Only you can send voice messages." = "Только вы можете отправлять голосовые сообщения."; + +/* No comment provided by engineer. */ +"Only your contact can irreversibly delete messages (you can mark them for deletion)." = "Только ваш контакт может необратимо удалять сообщения (вы можете помечать их на удаление)."; + +/* No comment provided by engineer. */ +"Only your contact can send voice messages." = "Только ваш контакт может отправлять голосовые сообщения."; + /* No comment provided by engineer. */ "Open chat" = "Открыть чат"; @@ -1310,6 +1391,9 @@ /* No comment provided by engineer. */ "Please store passphrase securely, you will NOT be able to change it if you lose it." = "Пожалуйста, надежно сохраните пароль, вы НЕ сможете его поменять, если потеряете."; +/* No comment provided by engineer. */ +"Preferences" = "Предпочтения"; + /* No comment provided by engineer. */ "Privacy & security" = "Конфиденциальность"; @@ -1319,6 +1403,12 @@ /* No comment provided by engineer. */ "Profile image" = "Аватар"; +/* No comment provided by engineer. */ +"Prohibit irreversible message deletion." = "Запретить необратимое удаление сообщений."; + +/* No comment provided by engineer. */ +"Prohibit sending voice messages." = "Запретить отправлять голосовые сообщений."; + /* No comment provided by engineer. */ "Protocol timeout" = "Таймаут протокола"; @@ -1394,6 +1484,9 @@ /* No comment provided by engineer. */ "Required" = "Обязательно"; +/* No comment provided by engineer. */ +"Reset" = "Сбросить"; + /* No comment provided by engineer. */ "Reset colors" = "Сбросить цвета"; @@ -1430,9 +1523,15 @@ /* chat item action */ "Save" = "Сохранить"; +/* No comment provided by engineer. */ +"Save (and notify contact)" = "Сохранить (и уведомить контакт)"; + /* No comment provided by engineer. */ "Save (and notify contacts)" = "Сохранить (и уведомить контакты)"; +/* No comment provided by engineer. */ +"Save (and notify group members)" = "Сохранить (и уведомить членов группы)"; + /* No comment provided by engineer. */ "Save archive" = "Сохранить архив"; @@ -1793,6 +1892,12 @@ /* No comment provided by engineer. */ "video call (not e2e encrypted)" = "видеозвонок (не e2e зашифрованный)"; +/* No comment provided by engineer. */ +"Voice messages" = "Голосовые сообщения"; + +/* No comment provided by engineer. */ +"Voice messages are prohibited in this chat." = "Голосовые сообщения запрещены в этом чате."; + /* No comment provided by engineer. */ "waiting for answer…" = "ожидается ответ…"; @@ -1829,12 +1934,18 @@ /* No comment provided by engineer. */ "Wrong passphrase!" = "Неправильный пароль!"; +/* pref value */ +"yes" = "да"; + /* No comment provided by engineer. */ "You" = "Вы"; /* No comment provided by engineer. */ "You accepted connection" = "Вы приняли приглашение соединиться"; +/* No comment provided by engineer. */ +"You allow" = "Вы разрешаете"; + /* No comment provided by engineer. */ "You are already connected to %@." = "Вы уже соединены с контактом %@."; @@ -1988,6 +2099,9 @@ /* No comment provided by engineer. */ "Your ICE servers" = "Ваши ICE серверы"; +/* No comment provided by engineer. */ +"Your preferences" = "Ваши предпочтения"; + /* No comment provided by engineer. */ "Your privacy" = "Конфиденциальность"; diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 5b1771b472..fbc0015a5b 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -167,7 +167,7 @@ data ChatCommand | APIGetCallInvitations | APICallStatus ContactId WebRTCCallStatus | APIUpdateProfile Profile - | APISetContactPrefs Int64 Preferences + | APISetContactPrefs ContactId Preferences | APISetContactAlias ContactId LocalAlias | APISetConnectionAlias Int64 LocalAlias | APIParseMarkdown Text diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index d48fe2fd4c..a50886c671 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -3670,7 +3670,7 @@ getGroupInfo db User {userId, userContactId} groupId = (groupId, userId, userContactId) updateGroupProfile :: DB.Connection -> User -> GroupInfo -> GroupProfile -> ExceptT StoreError IO GroupInfo -updateGroupProfile db User {userId} g@GroupInfo {groupId, localDisplayName, groupProfile = GroupProfile {displayName, groupPreferences}} p'@GroupProfile {displayName = newName, fullName, image} +updateGroupProfile db User {userId} g@GroupInfo {groupId, localDisplayName, groupProfile = GroupProfile {displayName}} p'@GroupProfile {displayName = newName, fullName, image, groupPreferences} | displayName == newName = liftIO $ do currentTs <- getCurrentTime updateGroupProfile_ currentTs $> (g :: GroupInfo) {groupProfile = p'}