Compare commits

...

14 Commits

Author SHA1 Message Date
Diogo 43c356b9c2 rename 2024-12-09 13:07:27 +00:00
Diogo 9b29afef3e save request not needed 2024-12-06 18:23:40 +00:00
Diogo 6eab3aeb62 move saveNetCfg to SaveableSettings 2024-12-06 18:20:48 +00:00
Diogo 2209a467bb ios: ask for confirmation of save on advanced settings dismiss 2024-12-06 18:11:55 +00:00
Diogo df1a471c56 ios: remove all unsafe warnings in group preferences save (#5340) 2024-12-06 19:55:15 +04:00
Diogo 7d43a43e82 ios: ask for confirmation of save on contact preferences sheet dismiss (#5337) 2024-12-06 18:44:56 +04:00
spaced4ndy ae8ad5c639 ios: operators info on onboarding (#5336) 2024-12-06 17:49:57 +04:00
spaced4ndy 1408d75eb3 ios: use async getServerOperators api (#5334) 2024-12-06 17:21:55 +04:00
spaced4ndy f408988035 ios: fix oneHandUI setting becoming enabled on import (#5335) 2024-12-06 13:05:39 +00:00
spaced4ndy 945c5015d8 ui: improve pending connection texts (#5333)
* ui: improve contact request text

* android

* ternary

* shorter

* kotlin

* change

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-12-06 15:35:26 +04:00
Diogo 2e431c5afa ios: fix some real time updates in group members (#5332)
* ios: fix some real time updates in group members

* use chat instead of binding for group info updates
2024-12-06 11:10:52 +00:00
Diogo 924273191e ios: ask for confirmation of save on group preferences sheet dismiss (#5327)
* ios: ask for confirmation of save on group preferences sheet dismiss

* fix exit without saving temporary state and also apply fix on dismiss during group creation
2024-12-06 10:21:58 +00:00
Evgeny Poberezkin 9b82cc3303 core: fix feature items when updating preferences in business chats 2024-12-06 10:18:48 +00:00
Evgeny Poberezkin 19e2cebd68 android, desktop: remove footer in Run chat section when chat is running 2024-12-06 01:16:42 +00:00
43 changed files with 293 additions and 149 deletions
+9 -2
View File
@@ -519,7 +519,14 @@ func testProtoServer(server: String) async throws -> Result<(), ProtocolTestFail
throw r
}
func getServerOperators() throws -> ServerOperatorConditions {
func getServerOperators() async throws -> ServerOperatorConditions {
let r = await chatSendCmd(.apiGetServerOperators)
if case let .serverOperatorConditions(conditions) = r { return conditions }
logger.error("getServerOperators error: \(String(describing: r))")
throw r
}
func getServerOperatorsSync() throws -> ServerOperatorConditions {
let r = chatSendCmdSync(.apiGetServerOperators)
if case let .serverOperatorConditions(conditions) = r { return conditions }
logger.error("getServerOperators error: \(String(describing: r))")
@@ -1599,7 +1606,7 @@ func initializeChat(start: Bool, confirmStart: Bool = false, dbKey: String? = ni
try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get())
m.chatInitialized = true
m.currentUser = try apiGetActiveUser()
m.conditions = try getServerOperators()
m.conditions = try getServerOperatorsSync()
if shouldImportAppSettingsDefault.get() {
do {
let appSettings = try apiGetAppSettings(settings: AppSettings.current.prepareForExport())
+34 -2
View File
@@ -96,6 +96,8 @@ struct ChatInfoView: View {
@ObservedObject var chat: Chat
@State var contact: Contact
@State var localAlias: String
@State var featuresAllowed: ContactFeaturesAllowed
@State var currentFeaturesAllowed: ContactFeaturesAllowed
var onSearch: () -> Void
@State private var connectionStats: ConnectionStats? = nil
@State private var customUserProfile: Profile? = nil
@@ -327,6 +329,16 @@ struct ChatInfoView: View {
$0.content
}
}
.onDisappear {
if currentFeaturesAllowed != featuresAllowed {
showAlert(
title: NSLocalizedString("Save preferences?", comment: "alert title"),
buttonTitle: NSLocalizedString("Save and notify contact", comment: "alert button"),
buttonAction: { savePreferences() },
cancelButton: true
)
}
}
}
private func contactInfoHeader() -> some View {
@@ -447,8 +459,9 @@ struct ChatInfoView: View {
NavigationLink {
ContactPreferencesView(
contact: $contact,
featuresAllowed: contactUserPrefsToFeaturesAllowed(contact.mergedPreferences),
currentFeaturesAllowed: contactUserPrefsToFeaturesAllowed(contact.mergedPreferences)
featuresAllowed: $featuresAllowed,
currentFeaturesAllowed: $currentFeaturesAllowed,
savePreferences: savePreferences
)
.navigationBarTitle("Contact preferences")
.modifier(ThemedBackground(grouped: true))
@@ -617,6 +630,23 @@ struct ChatInfoView: View {
}
}
}
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
}
}
} catch {
logger.error("ContactPreferencesView apiSetContactPrefs error: \(responseError(error))")
}
}
}
}
struct AudioCallButton: View {
@@ -1173,6 +1203,8 @@ struct ChatInfoView_Previews: PreviewProvider {
chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []),
contact: Contact.sampleData,
localAlias: "",
featuresAllowed: contactUserPrefsToFeaturesAllowed(Contact.sampleData.mergedPreferences),
currentFeaturesAllowed: contactUserPrefsToFeaturesAllowed(Contact.sampleData.mergedPreferences),
onSearch: {}
)
}
+10 -1
View File
@@ -133,7 +133,12 @@ struct ChatView: View {
.appSheet(item: $selectedMember) { member in
Group {
if case let .group(groupInfo) = chat.chatInfo {
GroupMemberInfoView(groupInfo: groupInfo, groupMember: member, navigation: true)
GroupMemberInfoView(
groupInfo: groupInfo,
chat: chat,
groupMember: member,
navigation: true
)
}
}
}
@@ -226,6 +231,8 @@ struct ChatView: View {
chat: chat,
contact: contact,
localAlias: chat.chatInfo.localAlias,
featuresAllowed: contactUserPrefsToFeaturesAllowed(contact.mergedPreferences),
currentFeaturesAllowed: contactUserPrefsToFeaturesAllowed(contact.mergedPreferences),
onSearch: { focusSearch() }
)
}
@@ -1122,6 +1129,7 @@ struct ChatView: View {
} else {
let mem = GMember.init(member)
m.groupMembers.append(mem)
m.groupMembersIndexes[member.groupMemberId] = m.groupMembers.count - 1
selectedMember = mem
}
}
@@ -1877,6 +1885,7 @@ struct ReactionContextMenu: View {
} else {
let member = GMember.init(mem)
m.groupMembers.append(member)
m.groupMembersIndexes[member.groupMemberId] = m.groupMembers.count - 1
selectedMember = member
}
} label: {
@@ -14,9 +14,10 @@ struct ContactPreferencesView: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@Binding var contact: Contact
@State var featuresAllowed: ContactFeaturesAllowed
@State var currentFeaturesAllowed: ContactFeaturesAllowed
@Binding var featuresAllowed: ContactFeaturesAllowed
@Binding var currentFeaturesAllowed: ContactFeaturesAllowed
@State private var showSaveDialogue = false
let savePreferences: () -> Void
var body: some View {
let user: User = chatModel.currentUser!
@@ -48,7 +49,10 @@ struct ContactPreferencesView: View {
savePreferences()
dismiss()
}
Button("Exit without saving") { dismiss() }
Button("Exit without saving") {
featuresAllowed = currentFeaturesAllowed
dismiss()
}
}
}
@@ -118,31 +122,15 @@ struct ContactPreferencesView: View {
private func featureFooter(_ feature: ChatFeature, _ enabled: FeatureEnabled) -> some View {
Text(feature.enabledDescription(enabled))
}
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
}
}
} catch {
logger.error("ContactPreferencesView apiSetContactPrefs error: \(responseError(error))")
}
}
}
}
struct ContactPreferencesView_Previews: PreviewProvider {
static var previews: some View {
ContactPreferencesView(
contact: Binding.constant(Contact.sampleData),
featuresAllowed: ContactFeaturesAllowed.sampleData,
currentFeaturesAllowed: ContactFeaturesAllowed.sampleData
featuresAllowed: Binding.constant(ContactFeaturesAllowed.sampleData),
currentFeaturesAllowed: Binding.constant(ContactFeaturesAllowed.sampleData),
savePreferences: {}
)
}
}
@@ -78,7 +78,12 @@ struct AddGroupMembersViewCommon: View {
let count = selectedContacts.count
Section {
if creatingGroup {
groupPreferencesButton($groupInfo, true)
GroupPreferencesButton(
groupInfo: $groupInfo,
preferences: groupInfo.fullGroupPreferences,
currentPreferences: groupInfo.fullGroupPreferences,
creatingGroup: true
)
}
rolePicker()
inviteMembersButton()
@@ -87,7 +87,7 @@ struct GroupChatInfoView: View {
if groupInfo.groupProfile.description != nil || (groupInfo.isOwner && groupInfo.businessChat == nil) {
addOrEditWelcomeMessage()
}
groupPreferencesButton($groupInfo)
GroupPreferencesButton(groupInfo: $groupInfo, preferences: groupInfo.fullGroupPreferences, currentPreferences: groupInfo.fullGroupPreferences)
if members.filter({ $0.wrapped.memberCurrent }).count <= SMALL_GROUPS_RCPS_MEM_LIMIT {
sendReceiptsOption()
} else {
@@ -439,7 +439,7 @@ struct GroupChatInfoView: View {
}
private func memberInfoView(_ groupMember: GMember) -> some View {
GroupMemberInfoView(groupInfo: groupInfo, groupMember: groupMember)
GroupMemberInfoView(groupInfo: groupInfo, chat: chat, groupMember: groupMember)
.navigationBarHidden(false)
}
@@ -654,27 +654,72 @@ func deleteGroupAlertMessage(_ groupInfo: GroupInfo) -> Text {
)
}
func groupPreferencesButton(_ groupInfo: Binding<GroupInfo>, _ creatingGroup: Bool = false) -> some View {
let label: LocalizedStringKey = groupInfo.wrappedValue.businessChat == nil ? "Group preferences" : "Chat preferences"
return NavigationLink {
GroupPreferencesView(
groupInfo: groupInfo,
preferences: groupInfo.wrappedValue.fullGroupPreferences,
currentPreferences: groupInfo.wrappedValue.fullGroupPreferences,
creatingGroup: creatingGroup
)
.navigationBarTitle(label)
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.large)
} label: {
if creatingGroup {
Text("Set group preferences")
} else {
Label(label, systemImage: "switch.2")
struct GroupPreferencesButton: View {
@Binding var groupInfo: GroupInfo
@State var preferences: FullGroupPreferences
@State var currentPreferences: FullGroupPreferences
var creatingGroup: Bool = false
private var label: LocalizedStringKey {
groupInfo.businessChat == nil ? "Group preferences" : "Chat preferences"
}
var body: some View {
NavigationLink {
GroupPreferencesView(
groupInfo: $groupInfo,
preferences: $preferences,
currentPreferences: currentPreferences,
creatingGroup: creatingGroup,
savePreferences: savePreferences
)
.navigationBarTitle(label)
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.large)
.onDisappear {
let saveText = NSLocalizedString(
creatingGroup ? "Save" : "Save and notify group members",
comment: "alert button"
)
if groupInfo.fullGroupPreferences != preferences {
showAlert(
title: NSLocalizedString("Save preferences?", comment: "alert title"),
buttonTitle: saveText,
buttonAction: { savePreferences() },
cancelButton: true
)
}
}
} label: {
if creatingGroup {
Text("Set group preferences")
} else {
Label(label, systemImage: "switch.2")
}
}
}
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.shared.updateGroup(gInfo)
currentPreferences = preferences
}
} catch {
logger.error("GroupPreferencesView apiUpdateGroup error: \(responseError(error))")
}
}
}
}
func cantInviteIncognitoAlert() -> Alert {
Alert(
title: Text("Can't invite contacts!"),
@@ -14,6 +14,7 @@ struct GroupMemberInfoView: View {
@EnvironmentObject var theme: AppTheme
@Environment(\.dismiss) var dismiss: DismissAction
@State var groupInfo: GroupInfo
@ObservedObject var chat: Chat
@ObservedObject var groupMember: GMember
var navigation: Bool = false
@State private var connectionStats: ConnectionStats? = nil
@@ -261,6 +262,11 @@ struct GroupMemberInfoView: View {
ProgressView().scaleEffect(2)
}
}
.onChange(of: chat.chatInfo) { c in
if case let .group(gI) = chat.chatInfo {
groupInfo = gI
}
}
.modifier(ThemedBackground(grouped: true))
}
@@ -758,6 +764,7 @@ struct GroupMemberInfoView_Previews: PreviewProvider {
static var previews: some View {
GroupMemberInfoView(
groupInfo: GroupInfo.sampleData,
chat: Chat.sampleData,
groupMember: GMember.sampleData
)
}
@@ -20,9 +20,10 @@ struct GroupPreferencesView: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@Binding var groupInfo: GroupInfo
@State var preferences: FullGroupPreferences
@State var currentPreferences: FullGroupPreferences
@Binding var preferences: FullGroupPreferences
var currentPreferences: FullGroupPreferences
let creatingGroup: Bool
let savePreferences: () -> Void
@State private var showSaveDialogue = false
var body: some View {
@@ -68,7 +69,10 @@ struct GroupPreferencesView: View {
savePreferences()
dismiss()
}
Button("Exit without saving") { dismiss() }
Button("Exit without saving") {
preferences = currentPreferences
dismiss()
}
}
}
@@ -132,32 +136,16 @@ struct GroupPreferencesView: View {
}
}
}
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
}
} 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,
preferences: Binding.constant(FullGroupPreferences.sampleData),
currentPreferences: FullGroupPreferences.sampleData,
creatingGroup: false
creatingGroup: false,
savePreferences: {}
)
}
}
@@ -33,6 +33,29 @@ enum UserPickerSheet: Identifiable {
class SaveableSettings: ObservableObject {
@Published var servers: ServerSettings = ServerSettings(currUserServers: [], userServers: [], serverErrors: [])
@Published var networkSettings: NetworkSettings = NetworkSettings.defaults
public func saveNetCfg() -> Bool {
do {
let netCfg = networkSettings.netCfg
let netProxy = networkSettings.netProxy
try setNetworkConfig(netCfg)
networkSettings.currentNetCfg = netCfg
setNetCfg(netCfg, networkProxy: netCfg.socksProxy != nil ? netProxy : nil)
networkSettings.currentNetProxy = netProxy
networkProxyDefault.set(netProxy)
return true
} catch let error {
let err = responseError(error)
showAlert(
NSLocalizedString("Error updating settings", comment: "alert title"),
message: responseError(error)
)
logger.error("\(err)")
return false
}
}
}
struct ServerSettings {
@@ -41,6 +64,20 @@ struct ServerSettings {
public var serverErrors: [UserServersError]
}
struct NetworkSettings {
public var currentNetCfg: NetCfg
public var netCfg: NetCfg
public var currentNetProxy: NetworkProxy
public var netProxy: NetworkProxy
static let defaults = NetworkSettings(
currentNetCfg: NetCfg.defaults,
netCfg: NetCfg.defaults,
currentNetProxy: networkProxyDefault.get(),
netProxy: networkProxyDefault.get()
)
}
struct UserPickerSheetView: View {
let sheet: UserPickerSheet
@EnvironmentObject var chatModel: ChatModel
@@ -89,15 +126,30 @@ struct UserPickerSheetView: View {
)
}
.onDisappear {
let advancedNetworkCanBeSaved = advancedNetworkSettingsCanBeSaved(ss.networkSettings)
let advancedNetworkSaveText = NSLocalizedString("Save and reconnect", comment: "alert button")
if serversCanBeSaved(
ss.servers.currUserServers,
ss.servers.userServers,
ss.servers.serverErrors
) {
showAlert(
title: NSLocalizedString("Save servers?", comment: "alert title"),
buttonTitle: NSLocalizedString("Save", comment: "alert button"),
buttonAction: { saveServers($ss.servers.currUserServers, $ss.servers.userServers) },
title: NSLocalizedString(advancedNetworkCanBeSaved ? "Save servers and network settings?" : "Save servers?", comment: "alert title"),
buttonTitle: advancedNetworkCanBeSaved ? NSLocalizedString("Save", comment: "alert button"): advancedNetworkSaveText,
buttonAction: {
saveServers($ss.servers.currUserServers, $ss.servers.userServers)
if advancedNetworkCanBeSaved {
_ = ss.saveNetCfg()
}
},
cancelButton: true
)
} else if (advancedNetworkCanBeSaved) {
showAlert(
title: NSLocalizedString("Update network settings?", comment: "alert title"),
buttonTitle: advancedNetworkSaveText,
buttonAction: { _ = ss.saveNetCfg() },
cancelButton: true
)
}
@@ -409,26 +409,54 @@ struct ChooseServerOperators: View {
let operatorsPostLink = URL(string: "https://simplex.chat/blog/20241125-servers-operated-by-flux-true-privacy-and-decentralization-for-all-users.html")!
struct ChooseServerOperatorsInfoView: View {
@Environment(\.colorScheme) var colorScheme: ColorScheme
@EnvironmentObject var theme: AppTheme
var body: some View {
VStack(alignment: .leading) {
Text("Server operators")
.font(.largeTitle)
.bold()
.padding(.vertical)
ScrollView {
NavigationView {
List {
VStack(alignment: .leading) {
Group {
Text("The app protects your privacy by using different operators in each conversation.")
Text("When more than one operator is enabled, none of them has metadata to learn who communicates with whom.")
Text("For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server.")
Text("The app protects your privacy by using different operators in each conversation.")
.padding(.bottom)
Text("When more than one operator is enabled, none of them has metadata to learn who communicates with whom.")
.padding(.bottom)
Text("For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server.")
}
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.padding(.top)
Section {
ForEach(ChatModel.shared.conditions.serverOperators) { op in
operatorInfoNavLinkView(op)
}
.padding(.bottom)
} header: {
Text("About operators")
.foregroundColor(theme.colors.secondary)
}
}
.navigationTitle("Server operators")
.navigationBarTitleDisplayMode(.large)
.modifier(ThemedBackground(grouped: true))
}
}
private func operatorInfoNavLinkView(_ op: ServerOperator) -> some View {
NavigationLink() {
OperatorInfoView(serverOperator: op)
.navigationBarTitle("Network operator")
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.large)
} label: {
HStack {
Image(op.logo(colorScheme))
.resizable()
.scaledToFit()
.frame(width: 24, height: 24)
Text(op.tradeName)
}
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.modifier(ThemedBackground())
}
}
@@ -28,19 +28,20 @@ struct AdvancedNetworkSettings: View {
@EnvironmentObject var theme: AppTheme
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false
@State private var netCfg = NetCfg.defaults
@State private var currentNetCfg = NetCfg.defaults
@State private var cfgLoaded = false
@State private var enableKeepAlive = true
@State private var keepAliveOpts = KeepAliveOpts.defaults
@State private var showSettingsAlert: NetworkSettingsAlert?
@State private var onionHosts: OnionHosts = .no
@State private var showSaveDialog = false
@State private var netProxy = networkProxyDefault.get()
@State private var currentNetProxy = networkProxyDefault.get()
@State private var useNetProxy = false
@State private var netProxyAuth = false
@Binding public var currentNetCfg: NetCfg
@Binding public var netCfg: NetCfg
@Binding public var currentNetProxy: NetworkProxy
@Binding public var netProxy: NetworkProxy
let saveNetCfg: () -> Bool
var body: some View {
VStack {
List {
@@ -312,22 +313,6 @@ struct AdvancedNetworkSettings: View {
}
}
private func saveNetCfg() -> Bool {
do {
try setNetworkConfig(netCfg)
currentNetCfg = netCfg
setNetCfg(netCfg, networkProxy: useNetProxy ? netProxy : nil)
currentNetProxy = netProxy
networkProxyDefault.set(netProxy)
return true
} catch let error {
let err = responseError(error)
showSettingsAlert = .error(err: err)
logger.error("\(err)")
return false
}
}
private func intSettingPicker(_ title: LocalizedStringKey, selection: Binding<Int>, values: [Int], label: String) -> some View {
Picker(title, selection: selection) {
ForEach(values, id: \.self) { value in
@@ -386,6 +371,13 @@ struct AdvancedNetworkSettings: View {
struct AdvancedNetworkSettings_Previews: PreviewProvider {
static var previews: some View {
AdvancedNetworkSettings()
let defaultSettings = NetworkSettings.defaults
AdvancedNetworkSettings(
currentNetCfg: Binding.constant(defaultSettings.currentNetCfg),
netCfg: Binding.constant(defaultSettings.netCfg),
currentNetProxy: Binding.constant(defaultSettings.currentNetProxy),
netProxy: Binding.constant(defaultSettings.netProxy),
saveNetCfg: { true }
)
}
}
@@ -94,9 +94,15 @@ struct NetworkAndServers: View {
}
NavigationLink {
AdvancedNetworkSettings()
.navigationTitle("Advanced settings")
.modifier(ThemedBackground(grouped: true))
AdvancedNetworkSettings(
currentNetCfg: $ss.networkSettings.currentNetCfg,
netCfg: $ss.networkSettings.netCfg,
currentNetProxy: $ss.networkSettings.currentNetProxy,
netProxy: $ss.networkSettings.netProxy,
saveNetCfg: ss.saveNetCfg
)
.navigationTitle("Advanced settings")
.modifier(ThemedBackground(grouped: true))
} label: {
Text("Advanced network settings")
}
@@ -336,6 +342,13 @@ func serversCanBeSaved(
return userServers != currUserServers && serverErrors.isEmpty
}
func advancedNetworkSettingsCanBeSaved(
_ config: NetworkSettings
) -> Bool {
let useNetProxy = config.netCfg.socksProxy != nil
return (config.currentNetCfg != config.netCfg || config.currentNetProxy != config.netProxy) && (useNetProxy ? config.netProxy.valid : true)
}
struct ServersErrorView: View {
@EnvironmentObject var theme: AppTheme
var errStr: String
+1 -1
View File
@@ -2725,7 +2725,7 @@ public struct AppSettings: Codable, Equatable {
uiDarkColorScheme: DefaultTheme.SIMPLEX.themeName,
uiCurrentThemeIds: nil as [String: String]?,
uiThemes: nil as [ThemeOverrides]?,
oneHandUI: false,
oneHandUI: true,
chatBottomBar: true
)
}
+4 -2
View File
@@ -1782,9 +1782,11 @@ public struct PendingContactConnection: Decodable, NamedChat, Hashable {
public var displayName: String {
get {
if let initiated = pccConnStatus.initiated {
return initiated && !viaContactUri
return viaContactUri
? NSLocalizedString("requested to connect", comment: "chat list item title")
: initiated
? NSLocalizedString("invited to connect", comment: "chat list item title")
: NSLocalizedString("connecting…", comment: "chat list item title")
: NSLocalizedString("accepted invitation", comment: "chat list item title")
} else {
// this should not be in the list
return NSLocalizedString("connection established", comment: "chat list item title (it should not be shown")
@@ -1895,8 +1895,9 @@ class PendingContactConnection(
generalGetString(MR.strings.display_name_connection_established)
} else {
generalGetString(
if (initiated && !viaContactUri) MR.strings.display_name_invited_to_connect
else MR.strings.display_name_connecting
if (viaContactUri) MR.strings.display_name_requested_to_connect
else if (initiated) MR.strings.display_name_invited_to_connect
else MR.strings.display_name_accepted_invitation
)
}
}
@@ -192,13 +192,7 @@ fun DatabaseLayout(
}
RunChatSetting(stopped, toggleEnabled && !progressIndicator, startChat, stopChatAlert)
}
SectionTextFooter(
if (stopped) {
stringResource(MR.strings.you_must_use_the_most_recent_version_of_database)
} else {
stringResource(MR.strings.stop_chat_to_enable_database_actions)
}
)
if (stopped) SectionTextFooter(stringResource(MR.strings.you_must_use_the_most_recent_version_of_database))
SectionDividerSpaced(maxTopPadding = true)
}
@@ -1049,7 +1049,6 @@
<string name="stop_sharing_address">إيقاف مشاركة العنوان؟</string>
<string name="stop_sharing">إيقاف المشاركة</string>
<string name="stop_chat_to_export_import_or_delete_chat_database">أوقف الدردشة لتصدير أو استيراد أو حذف قاعدة بيانات الدردشة. لن تتمكّن من استلام الرسائل وإرسالها أثناء إيقاف الدردشة.</string>
<string name="stop_chat_to_enable_database_actions">أوقف الدردشة لتمكين إجراءات قاعدة البيانات.</string>
<string name="chat_item_ttl_seconds">%s ثانية/ثواني</string>
<string name="callstate_starting">يبدأ…</string>
<string name="auth_simplex_lock_turned_on">تم تشغيل القفل SimpleX</string>
@@ -71,6 +71,8 @@
<string name="connection_local_display_name">connection %1$d</string>
<string name="display_name_connection_established">connection established</string>
<string name="display_name_invited_to_connect">invited to connect</string>
<string name="display_name_requested_to_connect">requested to connect</string>
<string name="display_name_accepted_invitation">accepted invitation</string>
<string name="display_name_connecting">connecting…</string>
<string name="description_you_shared_one_time_link">you shared one-time link</string>
<string name="description_you_shared_one_time_link_incognito">you shared one-time link incognito</string>
@@ -1306,7 +1308,6 @@
<string name="chat_database_deleted">Chat database deleted</string>
<string name="restart_the_app_to_create_a_new_chat_profile">Restart the app to create a new chat profile.</string>
<string name="you_must_use_the_most_recent_version_of_database">You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts.</string>
<string name="stop_chat_to_enable_database_actions">Stop chat to enable database actions.</string>
<string name="files_and_media_section">Files &amp; media</string>
<string name="delete_files_and_media_for_all_users">Delete files for all chat profiles</string>
<string name="delete_files_and_media_all">Delete all files</string>
@@ -1133,7 +1133,6 @@
<string name="save_preferences_question">Запази настройките\?</string>
<string name="icon_descr_speaker_on">Високоговорителят е включен</string>
<string name="icon_descr_speaker_off">Високоговорителят е изключен</string>
<string name="stop_chat_to_enable_database_actions">Спрете чата, за да активирате действията с базата данни.</string>
<string name="role_in_group">Роля</string>
<string name="network_options_save">Запази</string>
<string name="reset_color">Нулирай цветовете</string>
@@ -280,7 +280,6 @@
<string name="delete_chat_profile_action_cannot_be_undone_warning">Tuto akci nelze vzít zpět! Váš profil, kontakty, zprávy a soubory budou nenávratně ztraceny.</string>
<string name="restart_the_app_to_create_a_new_chat_profile">Restartujte aplikaci a vytvořte nový chat profil.</string>
<string name="you_must_use_the_most_recent_version_of_database">Nejnovější verzi databáze chatu musíte používat POUZE v jednom zařízení, jinak se může stát, že přestanete přijímat zprávy od některých kontaktů.</string>
<string name="stop_chat_to_enable_database_actions">Zastavte chat a povolte akce s databází.</string>
<string name="files_and_media_section">Soubory a média</string>
<string name="delete_files_and_media_question">Smazat soubory a média\?</string>
<string name="delete_messages">Odstranit zprávy</string>
@@ -597,7 +597,6 @@
<string name="chat_database_deleted">Chat-Datenbank gelöscht</string>
<string name="restart_the_app_to_create_a_new_chat_profile">Starten Sie die App neu, um ein neues Chat-Profil zu erstellen.</string>
<string name="you_must_use_the_most_recent_version_of_database">Sie dürfen die neueste Version Ihrer Chat-Datenbank NUR auf einem Gerät verwenden, andernfalls erhalten Sie möglicherweise keine Nachrichten mehr von einigen Ihrer Kontakte.</string>
<string name="stop_chat_to_enable_database_actions">Chat beenden, um Datenbankaktionen zu erlauben.</string>
<string name="delete_files_and_media_question">Dateien und Medien löschen?</string>
<string name="delete_files_and_media_desc">Diese Aktion kann nicht rückgängig gemacht werden! Es werden alle empfangenen und gesendeten Dateien und Medien gelöscht. Bilder mit niedriger Auflösung bleiben erhalten.</string>
<string name="no_received_app_files">Keine empfangenen oder gesendeten Dateien</string>
@@ -733,7 +733,6 @@
<string name="share_invitation_link">Compartir enlace de un uso</string>
<string name="update_network_session_mode_question">¿Actualizar el modo de aislamiento de transporte\?</string>
<string name="icon_descr_speaker_on">Altavoz activado</string>
<string name="stop_chat_to_enable_database_actions">Para habilitar las acciones sobre la base de datos, debes parar SimpleX</string>
<string name="connection_you_accepted_will_be_cancelled">¡La conexión que has aceptado se cancelará!</string>
<string name="database_initialization_error_desc">La base de datos no funciona correctamente. Pulsa para conocer más</string>
<string name="moderate_message_will_be_marked_warning">El mensaje será marcado como moderado para todos los miembros.</string>
@@ -942,7 +942,6 @@
<string name="delete_files_and_media_desc">این عمل قابل برگشت نیست - تمام پرونده‌ها و رسانه دریافتی حذف خواهند شد. عکس‌های با کیفیت پایین باقی خواهند ماند.</string>
<string name="you_must_use_the_most_recent_version_of_database">شما باید از تازه‌ترین نسخه پایگاه داده گپ خود روی فقط یک دستگاه استفاده کنید، در غیر این صورت ممکن است از بعضی از مخاطب‌ها ‌دیگر پیامی دریافت نکنید.</string>
<string name="messages_section_title">پیام‌ها</string>
<string name="stop_chat_to_enable_database_actions">به منظور فعال‌سازی اقدامات پایگاه داده، گپ را متوقف کنید.</string>
<string name="enable_automatic_deletion_message">این عمل قابل برگشت نیست - پیام‌های ارسالی و دریافتی قدیمی‌تر از زمان انتخابی حذف خواهند شد. این کار ممکن است چندین دقیقه زمان ببرد.</string>
<string name="error_changing_message_deletion">خطا در تغییر تنظیمات</string>
<string name="save_passphrase_in_settings">ذخیره عبارت عبور در تنظیمات</string>
@@ -666,7 +666,6 @@
<string name="self_destruct_passcode">Itsetuhoutuva pääsykoodi</string>
<string name="restart_the_app_to_use_imported_chat_database">Käynnistä sovellus uudelleen käyttääksesi tuotua keskustelutietokantaa.</string>
<string name="old_database_archive">Vanha tietokanta-arkisto</string>
<string name="stop_chat_to_enable_database_actions">Pysäytä keskustelu, jotta tietokantatoiminnot voidaan ottaa käyttöön.</string>
<string name="restart_the_app_to_create_a_new_chat_profile">Luo uusi keskusteluprofiili käynnistämällä sovellus uudelleen.</string>
<string name="messages_section_title">Viestit</string>
<string name="chat_item_ttl_none">ei koskaan</string>
@@ -617,7 +617,6 @@
<string name="error_importing_database">Erreur lors de l\'importation de la base de données du chat</string>
<string name="chat_database_imported">Base de données du chat importée</string>
<string name="delete_chat_profile_question">Supprimer le profil du chat \?</string>
<string name="stop_chat_to_enable_database_actions">Arrêter le chat pour agir sur la base de données.</string>
<string name="delete_files_and_media_question">Supprimer les fichiers et médias \?</string>
<string name="delete_files_and_media_desc">Cette action ne peut être annulée - tous les fichiers et médias reçus et envoyés seront supprimés. Les photos à faible résolution seront conservées.</string>
<string name="no_received_app_files">Aucun fichier reçu ou envoyé</string>
@@ -922,7 +922,6 @@
<string name="only_your_contact_can_make_calls">Csak az ismerőse tud hívást indítani.</string>
<string name="settings_section_title_themes">TÉMÁK</string>
<string name="videos_limit_title">Túl sok videó!</string>
<string name="stop_chat_to_enable_database_actions">Csevegési szolgáltatás megállítása az adatbázis műveletek elvégzéséhez.</string>
<string name="welcome">Üdvözöljük!</string>
<string name="v5_1_self_destruct_passcode">Önmegsemmisítési jelkód</string>
<string name="connect_via_link_or_qr_from_clipboard_or_in_person">(beolvasás, vagy beillesztés a vágólapról)</string>
@@ -874,7 +874,6 @@
<string name="remove_passphrase_from_keychain">Rimuovere la password dal Keystore\?</string>
<string name="save_passphrase_in_keychain">Salva la password nel Keystore</string>
<string name="chat_item_ttl_seconds">%s secondo/i</string>
<string name="stop_chat_to_enable_database_actions">Ferma la chat per attivare le azioni del database.</string>
<string name="delete_files_and_media_desc">Questa azione non può essere annullata: tutti i file e i media ricevuti e inviati verranno eliminati. Rimarranno le immagini a bassa risoluzione.</string>
<string name="enable_automatic_deletion_message">Questa azione non può essere annullata: i messaggi inviati e ricevuti prima di quanto selezionato verranno eliminati. Potrebbe richiedere diversi minuti.</string>
<string name="update_database">Aggiorna</string>
@@ -981,7 +981,6 @@
<string name="stop_chat_question">לעצור צ׳אט\?</string>
<string name="stop_chat_to_export_import_or_delete_chat_database">עיצרו את הצ׳אט כדי לייצא, לייבא או למחוק את מסד הנתונים. לא תוכלו לקבל ולשלוח הודעות בזמן שהצ׳אט מופסק.</string>
<string name="stop_chat_confirmation">עצור</string>
<string name="stop_chat_to_enable_database_actions">עיצרו את הצ׳אט כדי לאפשר פעולות מסד נתונים.</string>
<string name="skip_inviting_button">דלג על הזמנת חברים</string>
<string name="share_address">שתף כתובת</string>
<string name="theme_simplex">SimpleX</string>
@@ -662,7 +662,6 @@
<string name="icon_descr_speaker_off">スピーカーオフ</string>
<string name="your_chat_database">あなたのチャットデータベース</string>
<string name="stop_chat_confirmation">停止</string>
<string name="stop_chat_to_enable_database_actions">データベース操作をするにはチャットを停止する必要があります。</string>
<string name="simplex_link_contact">SimpleX連絡先アドレス</string>
<string name="simplex_link_invitation">SimpleX使い捨て招待リンク</string>
<string name="description_via_contact_address_link">連絡先アドレスリンク経由</string>
@@ -851,7 +851,6 @@
<string name="submit_passcode">제출하기</string>
<string name="store_passphrase_securely_without_recover">암호를 모르면 채팅에 액세스할 수 없으니 암호를 안전하게 보관해 주세요.</string>
<string name="stop_chat_question">채팅 기능을 중지할까요\?</string>
<string name="stop_chat_to_enable_database_actions">데이터베이스 작업을 할 수 있도록 채팅 기능을 중지하기</string>
<string name="switch_receiving_address">수신 주소 바꾸기</string>
<string name="decryption_error">복호화 오류</string>
<string name="confirm_passcode">패스코드 확인</string>
@@ -1219,7 +1219,6 @@
<string name="set_database_passphrase">Nustatyti duomenų slaptafrazę</string>
<string name="set_passphrase">Nustatyti slaptafrazę</string>
<string name="privacy_show_last_messages">Rodyti paskutines žinutes</string>
<string name="stop_chat_to_enable_database_actions">Sustabdykite pokalbius, kad įgalinti duomenų bazės veiksmus.</string>
<string name="settings_section_title_support">PALAIKYKITE SIMPLEX CHAT</string>
<string name="receipts_section_description_1">Jų galima nepaisyti kontaktų ir grupių nustatymuose.</string>
<string name="enable_automatic_deletion_message">Šis veiksmas negali būti atšauktas - žinutės išsiųstos ir gautos anksčiau nei pasirinkta bus ištrintos. Tai gali užtrukti kelias minutes.</string>
@@ -740,7 +740,6 @@
<string name="restart_the_app_to_create_a_new_chat_profile">Start de app opnieuw om een nieuw chatprofiel aan te maken.</string>
<string name="you_must_use_the_most_recent_version_of_database">U mag ALLEEN de meest recente versie van uw chat-database op één apparaat gebruiken, anders ontvangt u mogelijk geen berichten meer van sommige contacten.</string>
<string name="restart_the_app_to_use_imported_chat_database">Start de app opnieuw om de geïmporteerde chat database te gebruiken.</string>
<string name="stop_chat_to_enable_database_actions">Stop de chat om database acties mogelijk te maken.</string>
<string name="delete_files_and_media_desc">Deze actie kan niet ongedaan worden gemaakt, alle ontvangen en verzonden bestanden en media worden verwijderd. Foto\'s met een lage resolutie blijven behouden.</string>
<string name="remove_passphrase_from_keychain">Wachtwoord verwijderen uit Keychain\?</string>
<string name="remove_passphrase">Verwijderen</string>
@@ -562,7 +562,6 @@
<string name="restart_the_app_to_create_a_new_chat_profile">Uruchom ponownie aplikację, aby utworzyć nowy profil czatu.</string>
<string name="save_passphrase_in_keychain">Zapisz hasło w Keystore</string>
<string name="chat_item_ttl_seconds">%s sekund(y)</string>
<string name="stop_chat_to_enable_database_actions">Zatrzymaj czat, aby umożliwić działania na bazie danych.</string>
<string name="enable_automatic_deletion_message">Tego działania nie można cofnąć - wiadomości wysłane i odebrane wcześniej niż wybrane zostaną usunięte. Może to potrwać kilka minut.</string>
<string name="delete_chat_profile_action_cannot_be_undone_warning">Tego działania nie można cofnąć - Twój profil, kontakty, wiadomości i pliki zostaną nieodwracalnie utracone.</string>
<string name="messages_section_description">To ustawienie dotyczy wiadomości Twojego bieżącego profilu czatu</string>
@@ -853,7 +853,6 @@
<string name="settings_section_title_socks">PROXY SOCKS</string>
<string name="database_backup_can_be_restored">A tentativa de alterar a senha do banco de dados não foi concluída.</string>
<string name="stop_chat_to_export_import_or_delete_chat_database">Pare o bate-papo para exportar, importar ou excluir o banco de dados do chat. Você não poderá receber e enviar mensagens enquanto o chat estiver interrompido.</string>
<string name="stop_chat_to_enable_database_actions">Pare o bate-papo para ativar ações no banco de dados.</string>
<string name="chat_item_ttl_seconds">%s segundo(s)</string>
<string name="unknown_database_error_with_info">Erro de banco de dados desconhecido: %s</string>
<string name="unknown_error">Erro desconhecido</string>
@@ -760,7 +760,6 @@
<string name="group_members_n">%s, %s e %d membros</string>
<string name="add_contact_or_create_group">Iniciar nova conversa</string>
<string name="la_mode_system">Sistema</string>
<string name="stop_chat_to_enable_database_actions">Parar conversa para habilitar ações do banco de dados</string>
<string name="group_invitation_tap_to_join">Toque para participar</string>
<string name="rcv_group_event_3_members_connected">%s, %s e %s conectado</string>
<string name="network_option_tcp_connection_timeout">Tempo esgotado da conexão TCP</string>
@@ -600,7 +600,6 @@
<string name="chat_database_deleted">Данные чата удалены</string>
<string name="restart_the_app_to_create_a_new_chat_profile">Перезапустите приложение, чтобы создать новый профиль.</string>
<string name="you_must_use_the_most_recent_version_of_database">Используйте самую последнюю версию архива чата и ТОЛЬКО на одном устройстве, иначе Вы можете перестать получать сообщения от некоторых контактов.</string>
<string name="stop_chat_to_enable_database_actions">Остановите чат, чтобы разблокировать операции с архивом чата.</string>
<string name="delete_files_and_media_for_all_users">Удалить файлы во всех профилях чата</string>
<string name="delete_files_and_media_all">Удалить все файлы</string>
<string name="delete_files_and_media_question">Удалить файлы и медиа?</string>
@@ -937,7 +937,6 @@
<string name="star_on_github">ติดดาวบน GitHub</string>
<string name="switch_verb">เปลี่ยน</string>
<string name="callstate_starting">กำลังเริ่มต้น…</string>
<string name="stop_chat_to_enable_database_actions">หยุดการแชทเพื่อเปิดใช้งานการดำเนินการกับฐานข้อมูล</string>
<string name="stop_chat_to_export_import_or_delete_chat_database">หยุดแชทเพื่อส่งออก นำเข้า หรือลบฐานข้อมูลแชท คุณจะไม่สามารถรับและส่งข้อความได้ในขณะที่การแชทหยุดลง</string>
<string name="v4_6_audio_video_calls_descr">รองรับบลูทูธและการปรับปรุงอื่นๆ</string>
<string name="stop_sharing_address">หยุดแชร์ที่อยู่ไหม\?</string>
@@ -1034,7 +1034,6 @@
<string name="stop_file__action">Dosyayı durdur</string>
<string name="error_alert_title">Hata</string>
<string name="create_another_profile_button">ProfilProfil oluştur</string>
<string name="stop_chat_to_enable_database_actions">Veri tabanı eylemlerini etkinleştirmek için sohbeti durdur.</string>
<string name="stop_snd_file__title">Dosya göndermeyi durdur?</string>
<string name="auth_stop_chat">Sohbeti durdur</string>
<string name="connect_use_current_profile">Mevcut profili kullan</string>
@@ -899,7 +899,6 @@
<string name="enable_lock">Увімкнути блокування</string>
<string name="passcode_not_changed">Пароль не змінено!</string>
<string name="change_lock_mode">Змінити режим блокування</string>
<string name="stop_chat_to_enable_database_actions">Зупиніть чат, щоб увімкнути дії з базою даних.</string>
<string name="restart_the_app_to_create_a_new_chat_profile">Перезапустіть додаток, щоб створити новий профіль чату.</string>
<string name="delete_files_and_media_for_all_users">Видалити файли для всіх профілів чату</string>
<string name="delete_files_and_media_question">Видалити файли та медіа?</string>
@@ -799,7 +799,6 @@
<string name="prohibit_direct_messages">禁止向成员发送私信。</string>
<string name="protect_app_screen">保护应用程序屏幕</string>
<string name="settings_section_title_themes">主题</string>
<string name="stop_chat_to_enable_database_actions">停止聊天以启用数据库操作。</string>
<string name="chat_item_ttl_seconds">%s 秒</string>
<string name="alert_message_no_group">该群已不存在。</string>
<string name="group_invitation_tap_to_join">点击加入</string>
@@ -119,7 +119,6 @@
<string name="chat_is_stopped">聊天室已停止運作</string>
<string name="stop_chat_confirmation">停止</string>
<string name="chat_database_deleted">已刪除數據庫的對話內容</string>
<string name="stop_chat_to_enable_database_actions">停止聊天室以啟用數據庫功能。</string>
<string name="change_database_passphrase_question">修改數據庫密碼?</string>
<string name="leave_group_question">確定要退出群組?</string>
<string name="leave_group_button">退出</string>
+1 -1
View File
@@ -1472,7 +1472,7 @@ updateGroupPreferences db User {userId} g@GroupInfo {groupId, groupProfile = p}
)
|]
(ps, currentTs, userId, groupId)
pure (g :: GroupInfo) {groupProfile = p {groupPreferences = Just ps}}
pure (g :: GroupInfo) {groupProfile = p {groupPreferences = Just ps}, fullGroupPreferences = mergeGroupPreferences $ Just ps}
updateGroupProfileFromMember :: DB.Connection -> User -> GroupInfo -> Profile -> ExceptT StoreError IO GroupInfo
updateGroupProfileFromMember db user g@GroupInfo {groupId} Profile {displayName = n, fullName = fn, image = img} = do
+4
View File
@@ -857,6 +857,10 @@ testBusinessUpdateProfiles = testChat4 businessProfile aliceProfile bobProfile c
cath <## "updated group preferences:"
cath <## "Voice messages: on"
]
biz #$> ("/_get chat #1 count=1", chat, [(1, "Voice messages: on")])
alice #$> ("/_get chat #1 count=1", chat, [(0, "Voice messages: on")])
bob #$> ("/_get chat #1 count=1", chat, [(0, "Voice messages: on")])
cath #$> ("/_get chat #1 count=1", chat, [(0, "Voice messages: on")])
testPlanAddressOkKnown :: HasCallStack => FilePath -> IO ()
testPlanAddressOkKnown =