ios: load and save preferences (#1373)

This commit is contained in:
JRoberts
2022-11-16 20:26:43 +04:00
committed by GitHub
parent 491fe4a9bf
commit 828b502431
17 changed files with 1185 additions and 56 deletions
+6
View File
@@ -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 }
@@ -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")
}
@@ -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
)
}
}
@@ -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()
@@ -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<GroupFeatureEnabled>) -> 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
)
}
}
@@ -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
)
}
}
@@ -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") }
@@ -278,6 +278,36 @@
<target>Alle Ihre Kontakte bleiben verbunden.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow irreversible message deletion only if your contact allows it to you." xml:space="preserve">
<source>Allow irreversible message deletion only if your contact allows it to you.</source>
<target>***Allow irreversible message deletion only if your contact allows it to you.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to irreversibly delete sent messages." xml:space="preserve">
<source>Allow to irreversibly delete sent messages.</source>
<target>***Allow to irreversibly delete sent messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send voice messages." xml:space="preserve">
<source>Allow to send voice messages.</source>
<target>***Allow to send voice messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow voice messages only if your contact allows them." xml:space="preserve">
<source>Allow voice messages only if your contact allows them.</source>
<target>***Allow voice messages only if your contact allows them.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow your contacts to irreversibly delete sent messages." xml:space="preserve">
<source>Allow your contacts to irreversibly delete sent messages.</source>
<target>***Allow your contacts to irreversibly delete sent messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow your contacts to send voice messages." xml:space="preserve">
<source>Allow your contacts to send voice messages.</source>
<target>***Allow your contacts to send voice messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Already connected?" xml:space="preserve">
<source>Already connected?</source>
<target>Sind Sie bereits verbunden?</target>
@@ -328,6 +358,16 @@
<target>Automatisch</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Both you and your contact can irreversibly delete sent messages." xml:space="preserve">
<source>Both you and your contact can irreversibly delete sent messages.</source>
<target>***Both you and your contact can irreversibly delete sent messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Both you and your contact can send voice messages." xml:space="preserve">
<source>Both you and your contact can send voice messages.</source>
<target>***Both you and your contact can send voice messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Call already ended!" xml:space="preserve">
<source>Call already ended!</source>
<target>Anruf ist bereits beendet!</target>
@@ -428,6 +468,11 @@
<target>Der Chat ist beendet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat preferences" xml:space="preserve">
<source>Chat preferences</source>
<target>***Chat preferences</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Chats</target>
@@ -558,6 +603,11 @@
<target>Verbindungszeitüberschreitung</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
<source>Contact allows</source>
<target>***Contact allows</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact already exists" xml:space="preserve">
<source>Contact already exists</source>
<target>Der Kontakt ist bereits vorhanden</target>
@@ -588,11 +638,21 @@
<target>Kontaktname</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact preferences" xml:space="preserve">
<source>Contact preferences</source>
<target>***Contact preferences</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact requests" xml:space="preserve">
<source>Contact requests</source>
<target>Kontaktanfragen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contacts can mark messages for deletion; you will be able to view them." xml:space="preserve">
<source>Contacts can mark messages for deletion; you will be able to view them.</source>
<target>***Contacts can mark messages for deletion; you will be able to view them.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy" xml:space="preserve">
<source>Copy</source>
<target>Kopieren</target>
@@ -1221,6 +1281,11 @@
<target>Für Konsole</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Full deletion" xml:space="preserve">
<source>Full deletion</source>
<target>***Full deletion</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Full name (optional)" xml:space="preserve">
<source>Full name (optional)</source>
<target>Vollständiger Name (optional)</target>
@@ -1271,11 +1336,26 @@
<target>Gruppen-Link</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can irreversibly delete sent messages." xml:space="preserve">
<source>Group members can irreversibly delete sent messages.</source>
<target>***Group members can irreversibly delete sent messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send voice messages." xml:space="preserve">
<source>Group members can send voice messages.</source>
<target>***Group members can send voice messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group message:" xml:space="preserve">
<source>Group message:</source>
<target>Grppennachricht:</target>
<note>notification</note>
</trans-unit>
<trans-unit id="Group preferences" xml:space="preserve">
<source>Group preferences</source>
<target>***Group preferences</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group profile is stored on members' devices, not on the servers." xml:space="preserve">
<source>Group profile is stored on members' devices, not on the servers.</source>
<target>Das Gruppenprofil wird nur auf den Mitglieds-Systemen gespeichtert und nicht auf den Servern.</target>
@@ -1453,6 +1533,11 @@
<target>In Gruppe einladen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Irreversible message deletion is prohibited in this chat." xml:space="preserve">
<source>Irreversible message deletion is prohibited in this chat.</source>
<target>***Irreversible message deletion is prohibited in this chat.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It allows having many anonymous connections without any shared data between them in a single chat profile." xml:space="preserve">
<source>It allows having many anonymous connections without any shared data between them in a single chat profile.</source>
<target>Er ermöglicht mehrere anonyme Verbindungen in einem einzigen Chat-Profil ohne Daten zwischen diesen zu teilen.</target>
@@ -1768,6 +1853,31 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v
<target>Nur die Endgeräte speichern Benutzerprofile, Kontakte, Gruppen und Nachrichten, die über eine **2-Schichten Ende-zu-Ende-Verschlüsselung** gesendet werden.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
<source>Only group owners can change group preferences.</source>
<target>***Only group owners can change group preferences.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only you can irreversibly delete messages (your contact can mark them for deletion)." xml:space="preserve">
<source>Only you can irreversibly delete messages (your contact can mark them for deletion).</source>
<target>***Only you can irreversibly delete messages (your contact can mark them for deletion).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only you can send voice messages." xml:space="preserve">
<source>Only you can send voice messages.</source>
<target>***Only you can send voice messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only your contact can irreversibly delete messages (you can mark them for deletion)." xml:space="preserve">
<source>Only your contact can irreversibly delete messages (you can mark them for deletion).</source>
<target>***Only your contact can irreversibly delete messages (you can mark them for deletion).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only your contact can send voice messages." xml:space="preserve">
<source>Only your contact can send voice messages.</source>
<target>***Only your contact can send voice messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Open Settings" xml:space="preserve">
<source>Open Settings</source>
<target>Geräte-Einstellungen öffnen</target>
@@ -1858,6 +1968,11 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v
<target>Bitte bewahren Sie das Passwort sicher auf, Sie können es NICHT mehr ändern, wenn Sie es vergessen haben oder verlieren.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Preferences" xml:space="preserve">
<source>Preferences</source>
<target>***Preferences</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Privacy &amp; security" xml:space="preserve">
<source>Privacy &amp; security</source>
<target>Datenschutz &amp; Sicherheit</target>
@@ -1873,6 +1988,16 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v
<target>Profilbild</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit irreversible message deletion." xml:space="preserve">
<source>Prohibit irreversible message deletion.</source>
<target>***Prohibit irreversible message deletion.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending voice messages." xml:space="preserve">
<source>Prohibit sending voice messages.</source>
<target>***Prohibit sending voice messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protocol timeout" xml:space="preserve">
<source>Protocol timeout</source>
<target>Protokollzeitüberschreitung</target>
@@ -1968,6 +2093,11 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v
<target>Erforderlich</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset" xml:space="preserve">
<source>Reset</source>
<target>***Reset</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset colors" xml:space="preserve">
<source>Reset colors</source>
<target>Farben zurücksetzen</target>
@@ -2038,11 +2168,21 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v
<target>Speichern</target>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Save (and notify contact)" xml:space="preserve">
<source>Save (and notify contact)</source>
<target>***Save (and notify contact)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save (and notify contacts)" xml:space="preserve">
<source>Save (and notify contacts)</source>
<target>Speichern (und Kontakte benachrichtigen)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save (and notify group members)" xml:space="preserve">
<source>Save (and notify group members)</source>
<target>***Save (and notify group members)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save archive" xml:space="preserve">
<source>Save archive</source>
<target>Archiv speichern</target>
@@ -2572,6 +2712,16 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
<target>Videoanruf</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages" xml:space="preserve">
<source>Voice messages</source>
<target>***Voice messages</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages are prohibited in this chat." xml:space="preserve">
<source>Voice messages are prohibited in this chat.</source>
<target>***Voice messages are prohibited in this chat.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source>
<target>Warte auf Datei</target>
@@ -2627,6 +2777,11 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
<target>Sie haben die Verbindung akzeptiert</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You allow" xml:space="preserve">
<source>You allow</source>
<target>***You allow</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You are already connected to %@." xml:space="preserve">
<source>You are already connected to %@.</source>
<target>Sie sind bereits mit %@ verbunden.</target>
@@ -2844,6 +2999,11 @@ Sie können diese Verbindung abbrechen und den Kontakt entfernen (und es später
<target>Ihre aktuelle Chat-Datenbank wird GELÖSCHT und durch die Importierte ERSETZT.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your preferences" xml:space="preserve">
<source>Your preferences</source>
<target>***Your preferences</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your privacy" xml:space="preserve">
<source>Your privacy</source>
<target>Meine Privatsphäre</target>
@@ -2916,6 +3076,11 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
<target>Admin</target>
<note>member role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve">
<source>always</source>
<target>***always</target>
<note>pref value</note>
</trans-unit>
<trans-unit id="audio call (not e2e encrypted)" xml:space="preserve">
<source>audio call (not e2e encrypted)</source>
<target>Audioanruf (nicht E2E verschlüsselt)</target>
@@ -3056,6 +3221,11 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
<target>Ersteller</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="default (%@)" xml:space="preserve">
<source>default (%@)</source>
<target>***default (%@)</target>
<note>pref value</note>
</trans-unit>
<trans-unit id="deleted" xml:space="preserve">
<source>deleted</source>
<target>Gelöscht</target>
@@ -3206,11 +3376,26 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
<target>Neue Nachricht</target>
<note>notification</note>
</trans-unit>
<trans-unit id="no" xml:space="preserve">
<source>no</source>
<target>***no</target>
<note>pref value</note>
</trans-unit>
<trans-unit id="no e2e encryption" xml:space="preserve">
<source>no e2e encryption</source>
<target>Keine E2E-Verschlüsselung</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="off" xml:space="preserve">
<source>off</source>
<target>***off</target>
<note>group pref value</note>
</trans-unit>
<trans-unit id="on" xml:space="preserve">
<source>on</source>
<target>***on</target>
<note>group pref value</note>
</trans-unit>
<trans-unit id="or chat with the developers" xml:space="preserve">
<source>or chat with the developers</source>
<target>oder chatten Sie mit den Entwicklern</target>
@@ -3336,6 +3521,11 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
<target>möchte sich mit Ihnen verbinden!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="yes" xml:space="preserve">
<source>yes</source>
<target>***yes</target>
<note>pref value</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>Sie sind zur Gruppe eingeladen</target>
@@ -278,6 +278,36 @@
<target>All your contacts will remain connected</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow irreversible message deletion only if your contact allows it to you." xml:space="preserve">
<source>Allow irreversible message deletion only if your contact allows it to you.</source>
<target>Allow irreversible message deletion only if your contact allows it to you.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to irreversibly delete sent messages." xml:space="preserve">
<source>Allow to irreversibly delete sent messages.</source>
<target>Allow to irreversibly delete sent messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send voice messages." xml:space="preserve">
<source>Allow to send voice messages.</source>
<target>Allow to send voice messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow voice messages only if your contact allows them." xml:space="preserve">
<source>Allow voice messages only if your contact allows them.</source>
<target>Allow voice messages only if your contact allows them.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow your contacts to irreversibly delete sent messages." xml:space="preserve">
<source>Allow your contacts to irreversibly delete sent messages.</source>
<target>Allow your contacts to irreversibly delete sent messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow your contacts to send voice messages." xml:space="preserve">
<source>Allow your contacts to send voice messages.</source>
<target>Allow your contacts to send voice messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Already connected?" xml:space="preserve">
<source>Already connected?</source>
<target>Already connected?</target>
@@ -328,6 +358,16 @@
<target>Automatically</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Both you and your contact can irreversibly delete sent messages." xml:space="preserve">
<source>Both you and your contact can irreversibly delete sent messages.</source>
<target>Both you and your contact can irreversibly delete sent messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Both you and your contact can send voice messages." xml:space="preserve">
<source>Both you and your contact can send voice messages.</source>
<target>Both you and your contact can send voice messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Call already ended!" xml:space="preserve">
<source>Call already ended!</source>
<target>Call already ended!</target>
@@ -428,6 +468,11 @@
<target>Chat is stopped</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat preferences" xml:space="preserve">
<source>Chat preferences</source>
<target>Chat preferences</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Chats</target>
@@ -558,6 +603,11 @@
<target>Connection timeout</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
<source>Contact allows</source>
<target>Contact allows</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact already exists" xml:space="preserve">
<source>Contact already exists</source>
<target>Contact already exists</target>
@@ -588,11 +638,21 @@
<target>Contact name</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact preferences" xml:space="preserve">
<source>Contact preferences</source>
<target>Contact preferences</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact requests" xml:space="preserve">
<source>Contact requests</source>
<target>Contact requests</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contacts can mark messages for deletion; you will be able to view them." xml:space="preserve">
<source>Contacts can mark messages for deletion; you will be able to view them.</source>
<target>Contacts can mark messages for deletion; you will be able to view them.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy" xml:space="preserve">
<source>Copy</source>
<target>Copy</target>
@@ -1221,6 +1281,11 @@
<target>For console</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Full deletion" xml:space="preserve">
<source>Full deletion</source>
<target>Full deletion</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Full name (optional)" xml:space="preserve">
<source>Full name (optional)</source>
<target>Full name (optional)</target>
@@ -1271,11 +1336,26 @@
<target>Group link</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can irreversibly delete sent messages." xml:space="preserve">
<source>Group members can irreversibly delete sent messages.</source>
<target>Group members can irreversibly delete sent messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send voice messages." xml:space="preserve">
<source>Group members can send voice messages.</source>
<target>Group members can send voice messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group message:" xml:space="preserve">
<source>Group message:</source>
<target>Group message:</target>
<note>notification</note>
</trans-unit>
<trans-unit id="Group preferences" xml:space="preserve">
<source>Group preferences</source>
<target>Group preferences</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group profile is stored on members' devices, not on the servers." xml:space="preserve">
<source>Group profile is stored on members' devices, not on the servers.</source>
<target>Group profile is stored on members' devices, not on the servers.</target>
@@ -1453,6 +1533,11 @@
<target>Invite to group</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Irreversible message deletion is prohibited in this chat." xml:space="preserve">
<source>Irreversible message deletion is prohibited in this chat.</source>
<target>Irreversible message deletion is prohibited in this chat.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It allows having many anonymous connections without any shared data between them in a single chat profile." xml:space="preserve">
<source>It allows having many anonymous connections without any shared data between them in a single chat profile.</source>
<target>It allows having many anonymous connections without any shared data between them in a single chat profile.</target>
@@ -1768,6 +1853,31 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
<source>Only group owners can change group preferences.</source>
<target>Only group owners can change group preferences.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only you can irreversibly delete messages (your contact can mark them for deletion)." xml:space="preserve">
<source>Only you can irreversibly delete messages (your contact can mark them for deletion).</source>
<target>Only you can irreversibly delete messages (your contact can mark them for deletion).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only you can send voice messages." xml:space="preserve">
<source>Only you can send voice messages.</source>
<target>Only you can send voice messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only your contact can irreversibly delete messages (you can mark them for deletion)." xml:space="preserve">
<source>Only your contact can irreversibly delete messages (you can mark them for deletion).</source>
<target>Only your contact can irreversibly delete messages (you can mark them for deletion).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only your contact can send voice messages." xml:space="preserve">
<source>Only your contact can send voice messages.</source>
<target>Only your contact can send voice messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Open Settings" xml:space="preserve">
<source>Open Settings</source>
<target>Open Settings</target>
@@ -1858,6 +1968,11 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>Please store passphrase securely, you will NOT be able to change it if you lose it.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Preferences" xml:space="preserve">
<source>Preferences</source>
<target>Preferences</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Privacy &amp; security" xml:space="preserve">
<source>Privacy &amp; security</source>
<target>Privacy &amp; security</target>
@@ -1873,6 +1988,16 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>Profile image</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit irreversible message deletion." xml:space="preserve">
<source>Prohibit irreversible message deletion.</source>
<target>Prohibit irreversible message deletion.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending voice messages." xml:space="preserve">
<source>Prohibit sending voice messages.</source>
<target>Prohibit sending voice messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protocol timeout" xml:space="preserve">
<source>Protocol timeout</source>
<target>Protocol timeout</target>
@@ -1968,6 +2093,11 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>Required</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset" xml:space="preserve">
<source>Reset</source>
<target>Reset</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset colors" xml:space="preserve">
<source>Reset colors</source>
<target>Reset colors</target>
@@ -2038,11 +2168,21 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>Save</target>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Save (and notify contact)" xml:space="preserve">
<source>Save (and notify contact)</source>
<target>Save (and notify contact)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save (and notify contacts)" xml:space="preserve">
<source>Save (and notify contacts)</source>
<target>Save (and notify contacts)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save (and notify group members)" xml:space="preserve">
<source>Save (and notify group members)</source>
<target>Save (and notify group members)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save archive" xml:space="preserve">
<source>Save archive</source>
<target>Save archive</target>
@@ -2572,6 +2712,16 @@ To connect, please ask your contact to create another connection link and check
<target>Video call</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages" xml:space="preserve">
<source>Voice messages</source>
<target>Voice messages</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages are prohibited in this chat." xml:space="preserve">
<source>Voice messages are prohibited in this chat.</source>
<target>Voice messages are prohibited in this chat.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source>
<target>Waiting for file</target>
@@ -2627,6 +2777,11 @@ To connect, please ask your contact to create another connection link and check
<target>You accepted connection</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You allow" xml:space="preserve">
<source>You allow</source>
<target>You allow</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You are already connected to %@." xml:space="preserve">
<source>You are already connected to %@.</source>
<target>You are already connected to %@.</target>
@@ -2844,6 +2999,11 @@ You can cancel this connection and remove the contact (and try later with a new
<target>Your current chat database will be DELETED and REPLACED with the imported one.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your preferences" xml:space="preserve">
<source>Your preferences</source>
<target>Your preferences</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your privacy" xml:space="preserve">
<source>Your privacy</source>
<target>Your privacy</target>
@@ -2916,6 +3076,11 @@ SimpleX servers cannot see your profile.</target>
<target>admin</target>
<note>member role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve">
<source>always</source>
<target>always</target>
<note>pref value</note>
</trans-unit>
<trans-unit id="audio call (not e2e encrypted)" xml:space="preserve">
<source>audio call (not e2e encrypted)</source>
<target>audio call (not e2e encrypted)</target>
@@ -3056,6 +3221,11 @@ SimpleX servers cannot see your profile.</target>
<target>creator</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="default (%@)" xml:space="preserve">
<source>default (%@)</source>
<target>default (%@)</target>
<note>pref value</note>
</trans-unit>
<trans-unit id="deleted" xml:space="preserve">
<source>deleted</source>
<target>deleted</target>
@@ -3206,11 +3376,26 @@ SimpleX servers cannot see your profile.</target>
<target>new message</target>
<note>notification</note>
</trans-unit>
<trans-unit id="no" xml:space="preserve">
<source>no</source>
<target>no</target>
<note>pref value</note>
</trans-unit>
<trans-unit id="no e2e encryption" xml:space="preserve">
<source>no e2e encryption</source>
<target>no e2e encryption</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="off" xml:space="preserve">
<source>off</source>
<target>off</target>
<note>group pref value</note>
</trans-unit>
<trans-unit id="on" xml:space="preserve">
<source>on</source>
<target>on</target>
<note>group pref value</note>
</trans-unit>
<trans-unit id="or chat with the developers" xml:space="preserve">
<source>or chat with the developers</source>
<target>or chat with the developers</target>
@@ -3336,6 +3521,11 @@ SimpleX servers cannot see your profile.</target>
<target>wants to connect to you!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="yes" xml:space="preserve">
<source>yes</source>
<target>yes</target>
<note>pref value</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>you are invited to group</target>
@@ -278,6 +278,36 @@
<target>Все контакты, которые соединились через этот адрес, сохранятся.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow irreversible message deletion only if your contact allows it to you." xml:space="preserve">
<source>Allow irreversible message deletion only if your contact allows it to you.</source>
<target>Разрешить необратимое удаление сообщений, только если ваш контакт разрешает это вам.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to irreversibly delete sent messages." xml:space="preserve">
<source>Allow to irreversibly delete sent messages.</source>
<target>Разрешить необратимо удалять отправленные сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send voice messages." xml:space="preserve">
<source>Allow to send voice messages.</source>
<target>Разрешить отправлять голосовые сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow voice messages only if your contact allows them." xml:space="preserve">
<source>Allow voice messages only if your contact allows them.</source>
<target>Разрешить голосовые сообщения, только если их разрешает ваш контакт.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow your contacts to irreversibly delete sent messages." xml:space="preserve">
<source>Allow your contacts to irreversibly delete sent messages.</source>
<target>Разрешить вашим контактам необратимо удалять отправленные сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow your contacts to send voice messages." xml:space="preserve">
<source>Allow your contacts to send voice messages.</source>
<target>Разрешить вашим контактам отправлять голосовые сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Already connected?" xml:space="preserve">
<source>Already connected?</source>
<target>Соединение уже установлено?</target>
@@ -328,6 +358,16 @@
<target>Автоматически</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Both you and your contact can irreversibly delete sent messages." xml:space="preserve">
<source>Both you and your contact can irreversibly delete sent messages.</source>
<target>Вы и ваш контакт можете необратимо удалять отправленные сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Both you and your contact can send voice messages." xml:space="preserve">
<source>Both you and your contact can send voice messages.</source>
<target>Вы и ваш контакт можете отправлять голосовые сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Call already ended!" xml:space="preserve">
<source>Call already ended!</source>
<target>Звонок уже завершен!</target>
@@ -428,6 +468,11 @@
<target>Чат остановлен</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat preferences" xml:space="preserve">
<source>Chat preferences</source>
<target>Предпочтения</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Чаты</target>
@@ -558,6 +603,11 @@
<target>Превышено время соединения</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
<source>Contact allows</source>
<target>Контакт разрешает</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact already exists" xml:space="preserve">
<source>Contact already exists</source>
<target>Существующий контакт</target>
@@ -588,11 +638,21 @@
<target>Имена контактов</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact preferences" xml:space="preserve">
<source>Contact preferences</source>
<target>Предпочтения контакта</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact requests" xml:space="preserve">
<source>Contact requests</source>
<target>Запросы контактов</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contacts can mark messages for deletion; you will be able to view them." xml:space="preserve">
<source>Contacts can mark messages for deletion; you will be able to view them.</source>
<target>Контакты могут помечать сообщения для удаления; вы сможете просмотреть их.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy" xml:space="preserve">
<source>Copy</source>
<target>Скопировать</target>
@@ -1221,6 +1281,11 @@
<target>Для консоли</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Full deletion" xml:space="preserve">
<source>Full deletion</source>
<target>Полное удаление</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Full name (optional)" xml:space="preserve">
<source>Full name (optional)</source>
<target>Полное имя (не обязательно)</target>
@@ -1271,11 +1336,26 @@
<target>Ссылка группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can irreversibly delete sent messages." xml:space="preserve">
<source>Group members can irreversibly delete sent messages.</source>
<target>Члены группы могут необратимо удалять отправленные сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send voice messages." xml:space="preserve">
<source>Group members can send voice messages.</source>
<target>Члены группы могут отправлять голосовые сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group message:" xml:space="preserve">
<source>Group message:</source>
<target>Групповое сообщение:</target>
<note>notification</note>
</trans-unit>
<trans-unit id="Group preferences" xml:space="preserve">
<source>Group preferences</source>
<target>Предпочтения группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group profile is stored on members' devices, not on the servers." xml:space="preserve">
<source>Group profile is stored on members' devices, not on the servers.</source>
<target>Профиль группы хранится на устройствах членов, а не на серверах.</target>
@@ -1453,6 +1533,11 @@
<target>Пригласить в группу</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Irreversible message deletion is prohibited in this chat." xml:space="preserve">
<source>Irreversible message deletion is prohibited in this chat.</source>
<target>Необратимое удаление сообщений запрещено в этом чате.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It allows having many anonymous connections without any shared data between them in a single chat profile." xml:space="preserve">
<source>It allows having many anonymous connections without any shared data between them in a single chat profile.</source>
<target>Это позволяет иметь много анонимных соединений без общих данных между ними в одном профиле пользователя.</target>
@@ -1768,6 +1853,31 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Только пользовательские устройства хранят контакты, группы и сообщения, которые отправляются **с двухуровневым end-to-end шифрованием**</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
<source>Only group owners can change group preferences.</source>
<target>Только владельцы группы могут изменять предпочтения группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only you can irreversibly delete messages (your contact can mark them for deletion)." xml:space="preserve">
<source>Only you can irreversibly delete messages (your contact can mark them for deletion).</source>
<target>Только вы можете необратимо удалять сообщения (ваш контакт может помечать их на удаление).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only you can send voice messages." xml:space="preserve">
<source>Only you can send voice messages.</source>
<target>Только вы можете отправлять голосовые сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only your contact can irreversibly delete messages (you can mark them for deletion)." xml:space="preserve">
<source>Only your contact can irreversibly delete messages (you can mark them for deletion).</source>
<target>Только ваш контакт может необратимо удалять сообщения (вы можете помечать их на удаление).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only your contact can send voice messages." xml:space="preserve">
<source>Only your contact can send voice messages.</source>
<target>Только ваш контакт может отправлять голосовые сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Open Settings" xml:space="preserve">
<source>Open Settings</source>
<target>Открыть Настройки</target>
@@ -1858,6 +1968,11 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Пожалуйста, надежно сохраните пароль, вы НЕ сможете его поменять, если потеряете.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Preferences" xml:space="preserve">
<source>Preferences</source>
<target>Предпочтения</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Privacy &amp; security" xml:space="preserve">
<source>Privacy &amp; security</source>
<target>Конфиденциальность</target>
@@ -1873,6 +1988,16 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Аватар</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit irreversible message deletion." xml:space="preserve">
<source>Prohibit irreversible message deletion.</source>
<target>Запретить необратимое удаление сообщений.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending voice messages." xml:space="preserve">
<source>Prohibit sending voice messages.</source>
<target>Запретить отправлять голосовые сообщений.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protocol timeout" xml:space="preserve">
<source>Protocol timeout</source>
<target>Таймаут протокола</target>
@@ -1968,6 +2093,11 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Обязательно</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset" xml:space="preserve">
<source>Reset</source>
<target>Сбросить</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset colors" xml:space="preserve">
<source>Reset colors</source>
<target>Сбросить цвета</target>
@@ -2038,11 +2168,21 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Сохранить</target>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Save (and notify contact)" xml:space="preserve">
<source>Save (and notify contact)</source>
<target>Сохранить (и уведомить контакт)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save (and notify contacts)" xml:space="preserve">
<source>Save (and notify contacts)</source>
<target>Сохранить (и уведомить контакты)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save (and notify group members)" xml:space="preserve">
<source>Save (and notify group members)</source>
<target>Сохранить (и уведомить членов группы)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save archive" xml:space="preserve">
<source>Save archive</source>
<target>Сохранить архив</target>
@@ -2572,6 +2712,16 @@ To connect, please ask your contact to create another connection link and check
<target>Видеозвонок</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages" xml:space="preserve">
<source>Voice messages</source>
<target>Голосовые сообщения</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages are prohibited in this chat." xml:space="preserve">
<source>Voice messages are prohibited in this chat.</source>
<target>Голосовые сообщения запрещены в этом чате.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source>
<target>Ожидается прием файла</target>
@@ -2627,6 +2777,11 @@ To connect, please ask your contact to create another connection link and check
<target>Вы приняли приглашение соединиться</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You allow" xml:space="preserve">
<source>You allow</source>
<target>Вы разрешаете</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You are already connected to %@." xml:space="preserve">
<source>You are already connected to %@.</source>
<target>Вы уже соединены с контактом %@.</target>
@@ -2844,6 +2999,11 @@ You can cancel this connection and remove the contact (and try later with a new
<target>Текущие данные вашего чата будет УДАЛЕНЫ и ЗАМЕНЕНЫ импортированными.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your preferences" xml:space="preserve">
<source>Your preferences</source>
<target>Ваши предпочтения</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your privacy" xml:space="preserve">
<source>Your privacy</source>
<target>Конфиденциальность</target>
@@ -2916,6 +3076,11 @@ SimpleX серверы не могут получить доступ к ваше
<target>админ</target>
<note>member role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve">
<source>always</source>
<target>всегда</target>
<note>pref value</note>
</trans-unit>
<trans-unit id="audio call (not e2e encrypted)" xml:space="preserve">
<source>audio call (not e2e encrypted)</source>
<target>аудиозвонок (не e2e зашифрованный)</target>
@@ -3056,6 +3221,11 @@ SimpleX серверы не могут получить доступ к ваше
<target>создатель</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="default (%@)" xml:space="preserve">
<source>default (%@)</source>
<target>по умолчанию (%@)</target>
<note>pref value</note>
</trans-unit>
<trans-unit id="deleted" xml:space="preserve">
<source>deleted</source>
<target>удалено</target>
@@ -3206,11 +3376,26 @@ SimpleX серверы не могут получить доступ к ваше
<target>новое сообщение</target>
<note>notification</note>
</trans-unit>
<trans-unit id="no" xml:space="preserve">
<source>no</source>
<target>нет</target>
<note>pref value</note>
</trans-unit>
<trans-unit id="no e2e encryption" xml:space="preserve">
<source>no e2e encryption</source>
<target>нет e2e шифрования</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="off" xml:space="preserve">
<source>off</source>
<target>нет</target>
<note>group pref value</note>
</trans-unit>
<trans-unit id="on" xml:space="preserve">
<source>on</source>
<target>да</target>
<note>group pref value</note>
</trans-unit>
<trans-unit id="or chat with the developers" xml:space="preserve">
<source>or chat with the developers</source>
<target>или соединитесь с разработчиками</target>
@@ -3336,6 +3521,11 @@ SimpleX серверы не могут получить доступ к ваше
<target>хочет соединиться с вами!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="yes" xml:space="preserve">
<source>yes</source>
<target>да</target>
<note>pref value</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>вы приглашены в группу</target>
@@ -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 = "<group>"; };
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 = "<group>"; };
6432857B2925443C00FBE5C8 /* GroupPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupPreferencesView.swift; sourceTree = "<group>"; };
6440C9FF288857A10062C672 /* CIEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIEventView.swift; sourceTree = "<group>"; };
6440CA02288AECA70062C672 /* AddGroupMembersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGroupMembersView.swift; sourceTree = "<group>"; };
6442E0B9287F169300CEC0F9 /* AddGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGroupView.swift; sourceTree = "<group>"; };
@@ -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 */,
+6
View File
@@ -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
+183 -5
View File
@@ -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(
+114
View File
@@ -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";
+114
View File
@@ -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" = "Конфиденциальность";
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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'}