From 515a0ddfdd135815415fbb32c1e543da7b3f8697 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Wed, 16 Oct 2024 19:25:47 +0100 Subject: [PATCH 01/11] blog: wired's attack on privacy (#5063) * blog: wired misleading attack on privacy of communications * image * update * title * update * update * preview --- ...ity-review-better-calls-user-experience.md | 2 +- blog/20241016-wired-attack-on-privacy.md | 39 ++++++++++++++++++ blog/images/20241016-wired-privacy.jpg | Bin 0 -> 43148 bytes .../src/_includes/blog_previews/20241016.html | 4 ++ 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 blog/20241016-wired-attack-on-privacy.md create mode 100644 blog/images/20241016-wired-privacy.jpg create mode 100644 website/src/_includes/blog_previews/20241016.html diff --git a/blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.md b/blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.md index feb2a85036..cefe8560f5 100644 --- a/blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.md +++ b/blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.md @@ -23,7 +23,7 @@ permalink: "/blog/20241014-simplex-network-v6-1-security-review-better-calls-use ## SimpleX cryptographic design review by Trail of Bits - + It's been almost two years since Trail of Bits did the first security assessment of SimpleX Chat. diff --git a/blog/20241016-wired-attack-on-privacy.md b/blog/20241016-wired-attack-on-privacy.md new file mode 100644 index 0000000000..3bc69c8176 --- /dev/null +++ b/blog/20241016-wired-attack-on-privacy.md @@ -0,0 +1,39 @@ +--- +layout: layouts/article.html +title: "Wired’s Attack on Privacy" +date: 2024-10-16 +previewBody: blog_previews/20241016.html +image: images/20241016-wired-privacy.jpg +imageWide: true +permalink: "/blog/20241016-wired-attack-on-privacy.html" +--- + +# Wired’s Attack on Privacy + + + +The [Wired article](https://www.wired.com/story/neo-nazis-flee-telegram-encrypted-app-simplex/) by David Gilbert focusing on neo-Nazis moving to SimpleX Chat following the Telegram's changes in privacy policy is biased and misleading. By cherry-picking information from [the report](https://www.isdglobal.org/digital_dispatches/neo-nazi-accelerationists-seek-new-digital-refuge-amid-looming-telegram-crackdown/) by the Institute for Strategic Dialogue (ISD), Wired fails to mention that SimpleX network design prioritizes privacy in order to protect human rights defenders, journalists, and everyday users who value their privacy — many people feel safer using SimpleX than non-private apps, being protected from strangers contacting them. + +Yes, privacy-focused SimpleX network offers encryption and anonymity — that’s the point. To paint this as problematic solely because of who may use such apps misses the broader, critical context. + +SimpleX’s true strength lies in protection of [users' metadata](./20240416-dangers-of-metadata-in-messengers.md), which can reveal sensitive information about who is communicating, when, and how often. SimpleX protocols are designed to minimize metadata collection. For countless people, especially vulnerable groups, these features can be life-saving. Wired article ignores these essential protections, and overlooks the positive aspects of having such a unique design, as noted in the publication which [they link to](https://www.maargentino.com/is-telegrams-privacy-shift-driving-extremists-toward-simplex/): + +> *“SimpleX also has a significant advantage when it comes to protecting metadata — the information that can reveal who you’re talking to, when, and how often. SimpleX is designed with privacy at its core, minimizing the amount of metadata collected and ensuring that any temporary data necessary for functionality is not retained or linked to identifiable users.”* + +Both publications referenced by Wired also explore how SimpleX design actually hinders extremist groups from spreading propaganda or building large networks. SimpleX design restricts message visibility and file retention, making it far from ideal for those looking to coordinate large networks. Yet these important qualities are ignored by Wired in favor of fear-mongering about encryption — an argument we've seen before when apps like Signal [faced similar treatment](https://foreignpolicy.com/2021/03/13/telegram-signal-apps-right-wing-extremism-islamic-state-terrorism-violence-europol-encrypted/). Ironically, Wired just a month earlier encouraged its readers to [adopt encrypted messaging apps](https://www.wired.com/story/gadget-lab-podcast-657/), making its current stance even more contradictory. + +The vilification of apps that offer critically important privacy, anonymity, and encryption must stop. That a small share of users may abuse these tools doesn’t justify broad criticism. Additionally, the lobbying for client-side scanning, which Wired’s article seems to indirectly endorse, is not only dangerous but goes against fundamental principles of free speech and personal security. We strongly oppose the use of private communications for any kind of monitoring, including AI training, which would undermine the very trust encryption is designed to build. + +It’s alarming to see Wired not only criticize SimpleX for its strong privacy protections but also subtly blame the European Court of Human Rights for [upholding basic human rights](https://www.theregister.com/2024/02/15/echr_backdoor_encryption/) by rejecting laws that would force encrypted apps to scan and hand over private messages before encryption. Wired writes: + +> *…European Court of Human Rights decision in February of this year ruled that forcing encrypted messaging apps to provide a backdoor to law enforcement was illegal. This decision undermined the EU’s controversial proposal that would potentially force encrypted messaging apps to scan all user content for identifiers of child sexual abuse material.* + +This commentary is both inappropriate and misguided — it plays into the hands of anti-privacy lobbyists attempting to criminalize access to private communications. Framing privacy and anonymity as tools for criminals ignores the reality that these protections are essential for millions of legitimate users, from activists to journalists, to ordinary citizens. Client-side scanning can't have any meaningful effect on reducing CSAM distribution, instead resulting in increase of crime and abuse when criminals get access to this data. + +We need to correct this narrative. The real danger lies not in protecting communication, but in failing to do so. Privacy apps like SimpleX are crucial, not just for those resisting mass surveillance, but for everyone who values the right to communicate without fear of their conversations being monitored or misused. This is a right we must defend and incorporate into law, [as we wrote before](./20240704-future-of-privacy-enforcing-privacy-standards.md). + +Wired could have stood on the right side of this battle and helped normalize the demand for privacy, genuinely protecting people from criminals and from the exploitation of the increasingly AI-enabled mass surveillance. Instead they chose the path of spreading fear and uncertainty of encrypted messaging and tools that enable privacy and anonymity. + +Spreading misinformation about privacy and security undermines trust in the tools that protect us, making it easier to justify more invasive surveillance measures that chip away at our civil liberties. + +Wired did not respond to our request for comment. diff --git a/blog/images/20241016-wired-privacy.jpg b/blog/images/20241016-wired-privacy.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a97e9ba79cc2ccc6980e40ea2f4d075800c0ea3c GIT binary patch literal 43148 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%0D*g%BKy56d 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 Wired article by David Gilbert focusing on neo-Nazis moving to SimpleX Chat following the Telegram's changes in + privacy policy is biased and misleading. By cherry-picking information from the report by the Institute for + Strategic Dialogue (ISD), Wired fails to mention that SimpleX network design prioritizes privacy in order to protect + human rights defenders, journalists, and everyday users who value their privacy.

\ No newline at end of file From d57abfcc93a787fc52e6e332cc01ee9bb573d4ef Mon Sep 17 00:00:00 2001 From: Arturs Krumins Date: Wed, 16 Oct 2024 21:48:13 +0300 Subject: [PATCH 02/11] ios: fix theme import file picker (#5048) * ios: fix theme import file picker * minor --- .../Views/Helpers/ThemeModeEditor.swift | 64 +++++++------- .../UserSettings/AppearanceSettings.swift | 84 +++++++++++-------- 2 files changed, 83 insertions(+), 65 deletions(-) diff --git a/apps/ios/Shared/Views/Helpers/ThemeModeEditor.swift b/apps/ios/Shared/Views/Helpers/ThemeModeEditor.swift index ae94b4685c..9d5ae2e289 100644 --- a/apps/ios/Shared/Views/Helpers/ThemeModeEditor.swift +++ b/apps/ios/Shared/Views/Helpers/ThemeModeEditor.swift @@ -16,6 +16,7 @@ struct UserWallpaperEditor: View { @State var themeModeOverride: ThemeModeOverride @State var applyToMode: DefaultThemeMode? @State var showMore: Bool = false + @State var showFileImporter: Bool = false @Binding var globalThemeUsed: Bool var save: (DefaultThemeMode?, ThemeModeOverride?) async -> Void @@ -125,24 +126,27 @@ struct UserWallpaperEditor: View { CustomizeThemeColorsSection(editColor: { name in editColor(name, theme) }) - ImportExportThemeSection(perChat: nil, perUser: ChatModel.shared.currentUser?.uiThemes) { imported in - let importedFromString = imported.wallpaper?.importFromString() - let importedType = importedFromString?.toAppWallpaper().type - let currentTheme = ThemeManager.currentColors(nil, nil, nil, themeOverridesDefault.get()) - let type: WallpaperType? = if importedType?.sameType(currentTheme.wallpaper.type) == true { nil } else { importedType } - let colors = ThemeManager.currentThemeOverridesForExport(type, nil, nil).colors - let res = ThemeModeOverride(mode: imported.base.mode, colors: imported.colors, wallpaper: importedFromString).removeSameColors(imported.base, colorsToCompare: colors) - Task { - await MainActor.run { - themeModeOverride = res - } - await save(applyToMode, res) - } - } + ImportExportThemeSection(showFileImporter: $showFileImporter, perChat: nil, perUser: ChatModel.shared.currentUser?.uiThemes) } else { AdvancedSettingsButton(theme.colors.primary) { showMore = true } } } + .modifier( + ThemeImporter(isPresented: $showFileImporter) { imported in + let importedFromString = imported.wallpaper?.importFromString() + let importedType = importedFromString?.toAppWallpaper().type + let currentTheme = ThemeManager.currentColors(nil, nil, nil, themeOverridesDefault.get()) + let type: WallpaperType? = if importedType?.sameType(currentTheme.wallpaper.type) == true { nil } else { importedType } + let colors = ThemeManager.currentThemeOverridesForExport(type, nil, nil).colors + let res = ThemeModeOverride(mode: imported.base.mode, colors: imported.colors, wallpaper: importedFromString).removeSameColors(imported.base, colorsToCompare: colors) + Task { + await MainActor.run { + themeModeOverride = res + } + await save(applyToMode, res) + } + } + ) } private func onTypeCopyFromSameTheme(_ type: WallpaperType?) -> Bool { @@ -216,6 +220,7 @@ struct ChatWallpaperEditor: View { @State var themeModeOverride: ThemeModeOverride @State var applyToMode: DefaultThemeMode? @State var showMore: Bool = false + @State var showFileImporter: Bool = false @Binding var globalThemeUsed: Bool var save: (DefaultThemeMode?, ThemeModeOverride?) async -> Void @@ -328,24 +333,27 @@ struct ChatWallpaperEditor: View { CustomizeThemeColorsSection(editColor: editColor) - ImportExportThemeSection(perChat: themeModeOverride, perUser: ChatModel.shared.currentUser?.uiThemes) { imported in - let importedFromString = imported.wallpaper?.importFromString() - let importedType = importedFromString?.toAppWallpaper().type - let currentTheme = ThemeManager.currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) - let type: WallpaperType? = if importedType?.sameType(currentTheme.wallpaper.type) == true { nil } else { importedType } - let colors = ThemeManager.currentThemeOverridesForExport(type, nil, ChatModel.shared.currentUser?.uiThemes).colors - let res = ThemeModeOverride(mode: imported.base.mode, colors: imported.colors, wallpaper: importedFromString).removeSameColors(imported.base, colorsToCompare: colors) - Task { - await MainActor.run { - themeModeOverride = res - } - await save(applyToMode, res) - } - } + ImportExportThemeSection(showFileImporter: $showFileImporter, perChat: themeModeOverride, perUser: ChatModel.shared.currentUser?.uiThemes) } else { AdvancedSettingsButton(theme.colors.primary) { showMore = true } } } + .modifier( + ThemeImporter(isPresented: $showFileImporter) { imported in + let importedFromString = imported.wallpaper?.importFromString() + let importedType = importedFromString?.toAppWallpaper().type + let currentTheme = ThemeManager.currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + let type: WallpaperType? = if importedType?.sameType(currentTheme.wallpaper.type) == true { nil } else { importedType } + let colors = ThemeManager.currentThemeOverridesForExport(type, nil, ChatModel.shared.currentUser?.uiThemes).colors + let res = ThemeModeOverride(mode: imported.base.mode, colors: imported.colors, wallpaper: importedFromString).removeSameColors(imported.base, colorsToCompare: colors) + Task { + await MainActor.run { + themeModeOverride = res + } + await save(applyToMode, res) + } + } + ) } private func onTypeCopyFromSameTheme(_ type: WallpaperType?) -> Bool { diff --git a/apps/ios/Shared/Views/UserSettings/AppearanceSettings.swift b/apps/ios/Shared/Views/UserSettings/AppearanceSettings.swift index 50f0a3a7b6..4c61d592ac 100644 --- a/apps/ios/Shared/Views/UserSettings/AppearanceSettings.swift +++ b/apps/ios/Shared/Views/UserSettings/AppearanceSettings.swift @@ -583,11 +583,14 @@ struct CustomizeThemeView: View { } } - ImportExportThemeSection(perChat: nil, perUser: nil, save: { theme in + ImportExportThemeSection(showFileImporter: $showFileImporter, perChat: nil, perUser: nil) + } + .modifier( + ThemeImporter(isPresented: $showFileImporter) { theme in ThemeManager.saveAndApplyThemeOverrides(theme) saveThemeToDatabase(nil) - }) - } + } + ) /// When changing app theme, user overrides are hidden. User overrides will be returned back after closing Appearance screen, see ThemeDestinationPicker() .interactiveDismissDisabled(true) } @@ -595,10 +598,9 @@ struct CustomizeThemeView: View { struct ImportExportThemeSection: View { @EnvironmentObject var theme: AppTheme + @Binding var showFileImporter: Bool var perChat: ThemeModeOverride? var perUser: ThemeModeOverrides? - var save: (ThemeOverrides) -> Void - @State private var showFileImporter = false var body: some View { Section { @@ -626,39 +628,47 @@ struct ImportExportThemeSection: View { } label: { Text("Import theme").foregroundColor(theme.colors.primary) } - .fileImporter( - isPresented: $showFileImporter, - allowedContentTypes: [.data/*.plainText*/], - allowsMultipleSelection: false - ) { result in - if case let .success(files) = result, let fileURL = files.first { - do { - var fileSize: Int? = nil - if fileURL.startAccessingSecurityScopedResource() { - let resourceValues = try fileURL.resourceValues(forKeys: [.fileSizeKey]) - fileSize = resourceValues.fileSize - } - if let fileSize = fileSize, - // Same as Android/desktop - fileSize <= 5_500_000 { - if let string = try? String(contentsOf: fileURL, encoding: .utf8), let theme: ThemeOverrides = decodeYAML("themeId: \(UUID().uuidString)\n" + string) { - save(theme) - logger.error("Saved theme from file") - } else { - logger.error("Error decoding theme file") - } - fileURL.stopAccessingSecurityScopedResource() - } else { - fileURL.stopAccessingSecurityScopedResource() - let prettyMaxFileSize = ByteCountFormatter.string(fromByteCount: 5_500_000, countStyle: .binary) - AlertManager.shared.showAlertMsg( - title: "Large file!", - message: "Currently maximum supported file size is \(prettyMaxFileSize)." - ) - } - } catch { - logger.error("Appearance fileImporter error \(error.localizedDescription)") + } + } +} + +struct ThemeImporter: ViewModifier { + @Binding var isPresented: Bool + var save: (ThemeOverrides) -> Void + + func body(content: Content) -> some View { + content.fileImporter( + isPresented: $isPresented, + allowedContentTypes: [.data/*.plainText*/], + allowsMultipleSelection: false + ) { result in + if case let .success(files) = result, let fileURL = files.first { + do { + var fileSize: Int? = nil + if fileURL.startAccessingSecurityScopedResource() { + let resourceValues = try fileURL.resourceValues(forKeys: [.fileSizeKey]) + fileSize = resourceValues.fileSize } + if let fileSize = fileSize, + // Same as Android/desktop + fileSize <= 5_500_000 { + if let string = try? String(contentsOf: fileURL, encoding: .utf8), let theme: ThemeOverrides = decodeYAML("themeId: \(UUID().uuidString)\n" + string) { + save(theme) + logger.error("Saved theme from file") + } else { + logger.error("Error decoding theme file") + } + fileURL.stopAccessingSecurityScopedResource() + } else { + fileURL.stopAccessingSecurityScopedResource() + let prettyMaxFileSize = ByteCountFormatter.string(fromByteCount: 5_500_000, countStyle: .binary) + AlertManager.shared.showAlertMsg( + title: "Large file!", + message: "Currently maximum supported file size is \(prettyMaxFileSize)." + ) + } + } catch { + logger.error("Appearance fileImporter error \(error.localizedDescription)") } } } From c54fae01369a781742d4e69620e3d0a8b88127b7 Mon Sep 17 00:00:00 2001 From: Arturs Krumins Date: Wed, 16 Oct 2024 21:55:59 +0300 Subject: [PATCH 03/11] ios: fix sheets dismissing during biometric authentication (#5062) * ios: fix sheets dismissing during biometric authentication * remove AppSheet * Revert "remove AppSheet" This reverts commit 3aa1688cbd9cb931ff14c522301ee69f843cafb0. * remove local auth request on sheet dismissal * revert biometricAuth --- apps/ios/Shared/ContentView.swift | 4 +++- apps/ios/Shared/Views/ChatList/ChatListView.swift | 8 +++++--- apps/ios/Shared/Views/Helpers/AppSheet.swift | 12 +++++++----- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/apps/ios/Shared/ContentView.swift b/apps/ios/Shared/ContentView.swift index 6d600f33ff..e85f6a664a 100644 --- a/apps/ios/Shared/ContentView.swift +++ b/apps/ios/Shared/ContentView.swift @@ -13,6 +13,7 @@ struct ContentView: View { @EnvironmentObject var chatModel: ChatModel @ObservedObject var alertManager = AlertManager.shared @ObservedObject var callController = CallController.shared + @ObservedObject var appSheetState = AppSheetState.shared @Environment(\.colorScheme) var colorScheme @EnvironmentObject var theme: AppTheme @EnvironmentObject var sceneDelegate: SceneDelegate @@ -250,7 +251,8 @@ struct ContentView: View { private func mainView() -> some View { ZStack(alignment: .top) { - ChatListView(activeUserPickerSheet: $chatListUserPickerSheet).privacySensitive(protectScreen) + ChatListView(activeUserPickerSheet: $chatListUserPickerSheet) + .redacted(reason: appSheetState.redactionReasons(protectScreen)) .onAppear { requestNtfAuthorization() // Local Authentication notice is to be shown on next start after onboarding is complete diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index a1b40aadbe..7b24995f62 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -121,9 +121,11 @@ struct ChatListView: View { UserPicker(userPickerShown: $userPickerShown, activeSheet: $activeUserPickerSheet) } ) - .sheet(item: $activeUserPickerSheet) { - UserPickerSheetView(sheet: $0) - } + .appSheet( + item: $activeUserPickerSheet, + onDismiss: { chatModel.laRequest = nil }, + content: { UserPickerSheetView(sheet: $0) } + ) .onChange(of: activeUserPickerSheet) { if $0 != nil { DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { diff --git a/apps/ios/Shared/Views/Helpers/AppSheet.swift b/apps/ios/Shared/Views/Helpers/AppSheet.swift index 0ade1c0d8e..1e334367e8 100644 --- a/apps/ios/Shared/Views/Helpers/AppSheet.swift +++ b/apps/ios/Shared/Views/Helpers/AppSheet.swift @@ -11,6 +11,12 @@ import SwiftUI class AppSheetState: ObservableObject { static let shared = AppSheetState() @Published var scenePhaseActive: Bool = false + + func redactionReasons(_ protectScreen: Bool) -> RedactionReasons { + !protectScreen || scenePhaseActive + ? RedactionReasons() + : RedactionReasons.placeholder + } } private struct PrivacySensitive: ViewModifier { @@ -19,11 +25,7 @@ private struct PrivacySensitive: ViewModifier { @ObservedObject var appSheetState: AppSheetState = AppSheetState.shared func body(content: Content) -> some View { - if !protectScreen { - content - } else { - content.privacySensitive(!appSheetState.scenePhaseActive).redacted(reason: .privacy) - } + content.redacted(reason: appSheetState.redactionReasons(protectScreen)) } } From a160acef12c67b7d93a140361903dfef064e6807 Mon Sep 17 00:00:00 2001 From: Arturs Krumins Date: Fri, 18 Oct 2024 12:04:53 +0300 Subject: [PATCH 04/11] ios: fix navigation title redaction after biometric authentication (#5065) --- apps/ios/Shared/Views/TerminalView.swift | 7 ++++++- apps/ios/Shared/Views/UserSettings/SettingsView.swift | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/ios/Shared/Views/TerminalView.swift b/apps/ios/Shared/Views/TerminalView.swift index 36c05ed43d..23e1f783f7 100644 --- a/apps/ios/Shared/Views/TerminalView.swift +++ b/apps/ios/Shared/Views/TerminalView.swift @@ -105,7 +105,12 @@ struct TerminalView: View { } } .navigationViewStyle(.stack) - .navigationTitle("Chat console") + .toolbar { + // Redaction broken for `.navigationTitle` - using a toolbar item instead. + ToolbarItem(placement: .principal) { + Text("Chat console").font(.headline) + } + } .modifier(ThemedBackground()) } diff --git a/apps/ios/Shared/Views/UserSettings/SettingsView.swift b/apps/ios/Shared/Views/UserSettings/SettingsView.swift index a7938b86c3..12a982e76b 100644 --- a/apps/ios/Shared/Views/UserSettings/SettingsView.swift +++ b/apps/ios/Shared/Views/UserSettings/SettingsView.swift @@ -331,7 +331,12 @@ struct SettingsView: View { chatDatabaseRow() NavigationLink { MigrateFromDevice(showProgressOnSettings: $showProgress) - .navigationTitle("Migrate device") + .toolbar { + // Redaction broken for `.navigationTitle` - using a toolbar item instead. + ToolbarItem(placement: .principal) { + Text("Migrate device").font(.headline) + } + } .modifier(ThemedBackground(grouped: true)) .navigationBarTitleDisplayMode(.large) } label: { From 391304370597894aa4b675df098dc82d07175159 Mon Sep 17 00:00:00 2001 From: Arturs Krumins Date: Fri, 18 Oct 2024 12:07:18 +0300 Subject: [PATCH 05/11] ios: fix chat not loading if initial page has too many merged items (#5066) Co-authored-by: Evgeny Poberezkin --- apps/ios/Shared/Views/Chat/ReverseList.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ios/Shared/Views/Chat/ReverseList.swift b/apps/ios/Shared/Views/Chat/ReverseList.swift index 2e09909c5e..e33adcef58 100644 --- a/apps/ios/Shared/Views/Chat/ReverseList.swift +++ b/apps/ios/Shared/Views/Chat/ReverseList.swift @@ -78,7 +78,7 @@ struct ReverseList: UIViewControllerRepresentable { self.dataSource = UITableViewDiffableDataSource( tableView: tableView ) { (tableView, indexPath, item) -> UITableViewCell? in - if indexPath.item > self.itemCount - 8, self.itemCount > 8 { + if indexPath.item > self.itemCount - 8 { self.representer.loadPage() } let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseId, for: indexPath) From 7cde2cf6c2798dd63e2e7b2228533c7d64b3a1b5 Mon Sep 17 00:00:00 2001 From: Arturs Krumins Date: Fri, 18 Oct 2024 14:35:27 +0300 Subject: [PATCH 06/11] ios: optimise ComposeView rendering (#5042) * ios: replace revealed bindings with constant value * ios: optimise ComposeView rendering * rename --------- Co-authored-by: Evgeny Poberezkin --- .../Chat/ComposeMessage/ComposeView.swift | 4 +- .../Chat/ComposeMessage/SendMessageView.swift | 2 +- .../Shared/Views/Helpers/UserDefault.swift | 62 +++++++++++++++++++ apps/ios/SimpleX.xcodeproj/project.pbxproj | 4 ++ 4 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 apps/ios/Shared/Views/Helpers/UserDefault.swift diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift index 971d088fe2..19e2b528f1 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift @@ -287,8 +287,8 @@ struct ComposeView: View { // this is a workaround to fire an explicit event in certain cases @State private var stopPlayback: Bool = false - @AppStorage(DEFAULT_PRIVACY_SAVE_LAST_DRAFT) private var saveLastDraft = true - @AppStorage(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial + @UserDefault(DEFAULT_PRIVACY_SAVE_LAST_DRAFT) private var saveLastDraft = true + @UserDefault(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial var body: some View { VStack(spacing: 0) { diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift index 50ec8f28c1..8880023e02 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift @@ -40,7 +40,7 @@ struct SendMessageView: View { @State private var showCustomTimePicker = false @State private var selectedDisappearingMessageTime: Int? = customDisappearingMessageTimeDefault.get() @State private var progressByTimeout = false - @AppStorage(DEFAULT_LIVE_MESSAGE_ALERT_SHOWN) private var liveMessageAlertShown = false + @UserDefault(DEFAULT_LIVE_MESSAGE_ALERT_SHOWN) private var liveMessageAlertShown = false var body: some View { ZStack { diff --git a/apps/ios/Shared/Views/Helpers/UserDefault.swift b/apps/ios/Shared/Views/Helpers/UserDefault.swift new file mode 100644 index 0000000000..5f18465d20 --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/UserDefault.swift @@ -0,0 +1,62 @@ +// +// UserDefault.swift +// SimpleX (iOS) +// +// Created by user on 14/10/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import Combine + +@propertyWrapper +public struct UserDefault: DynamicProperty { + @StateObject private var observer = UserDefaultObserver() + let initialValue: Value + let key: String + let store: UserDefaults + + public init( + wrappedValue: Value, + _ key: String, + store: UserDefaults = .standard + ) { + self.initialValue = wrappedValue + self.key = key + self.store = store + } + + public var wrappedValue: Value { + get { + // Observer can only be accessed after the property wrapper is installed in view (runtime exception) + observer.subscribe(to: key) + return store.object(forKey: key) as? Value ?? initialValue + } + nonmutating set { + store.set(newValue, forKey: key) + } + } +} + +private class UserDefaultObserver: ObservableObject { + private var subscribed = false + + func subscribe(to key: String) { + if !subscribed { + NotificationCenter.default.addObserver( + self, + selector: #selector(userDefaultsDidChange), + name: UserDefaults.didChangeNotification, + object: nil + ) + subscribed = true + } + } + + @objc + private func userDefaultsDidChange(_ notification: Notification) { + Task { @MainActor in objectWillChange.send() } + } + + deinit { NotificationCenter.default.removeObserver(self) } +} diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 2903388fb9..6307bb1d16 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -204,6 +204,7 @@ CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = CE38A29B2C3FCD72005ED185 /* SwiftyGif */; }; CE75480A2C622630009579B7 /* SwipeLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7548092C622630009579B7 /* SwipeLabel.swift */; }; CE984D4B2C36C5D500E3AEFF /* ChatItemClipShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE984D4A2C36C5D500E3AEFF /* ChatItemClipShape.swift */; }; + CEA6E91C2CBD21B0002B5DB4 /* UserDefault.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6E91B2CBD21B0002B5DB4 /* UserDefault.swift */; }; CEDB245B2C9CD71800FBC5F6 /* StickyScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEDB245A2C9CD71800FBC5F6 /* StickyScrollView.swift */; }; CEDE70222C48FD9500233B1F /* SEChatState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEDE70212C48FD9500233B1F /* SEChatState.swift */; }; CEE723AA2C3BD3D70009AE93 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE723A92C3BD3D70009AE93 /* ShareViewController.swift */; }; @@ -545,6 +546,7 @@ CE3097FA2C4C0C9F00180898 /* ErrorAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorAlert.swift; sourceTree = ""; }; CE7548092C622630009579B7 /* SwipeLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeLabel.swift; sourceTree = ""; }; CE984D4A2C36C5D500E3AEFF /* ChatItemClipShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemClipShape.swift; sourceTree = ""; }; + CEA6E91B2CBD21B0002B5DB4 /* UserDefault.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefault.swift; sourceTree = ""; }; CEDB245A2C9CD71800FBC5F6 /* StickyScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickyScrollView.swift; sourceTree = ""; }; CEDE70212C48FD9500233B1F /* SEChatState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SEChatState.swift; sourceTree = ""; }; CEE723A72C3BD3D70009AE93 /* SimpleX SE.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "SimpleX SE.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -802,6 +804,7 @@ CE176F1F2C87014C00145DBC /* InvertedForegroundStyle.swift */, CEDB245A2C9CD71800FBC5F6 /* StickyScrollView.swift */, CEFB2EDE2CA1BCC7004B1ECE /* SheetRepresentable.swift */, + CEA6E91B2CBD21B0002B5DB4 /* UserDefault.swift */, ); path = Helpers; sourceTree = ""; @@ -1482,6 +1485,7 @@ 5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */, 649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */, 5CADE79C292131E900072E13 /* ContactPreferencesView.swift in Sources */, + CEA6E91C2CBD21B0002B5DB4 /* UserDefault.swift in Sources */, 5CB346E52868AA7F001FD2EF /* SuspendChat.swift in Sources */, 5C9C2DA52894777E00CC63B1 /* GroupProfileView.swift in Sources */, 5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */, From f3cd167502af8391a0a83959b3db1db5fd229443 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 18 Oct 2024 13:06:47 +0100 Subject: [PATCH 07/11] core: ntf server --- src/Simplex/Chat.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 5f586e8dad..c875f9afa3 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -203,8 +203,9 @@ _defaultSMPServers = _defaultNtfServers :: [NtfServer] _defaultNtfServers = - [ "ntf://KmpZNNXiVZJx_G2T7jRUmDFxWXM3OAnunz3uLT0tqAA=@ntf3.simplex.im,pxculznuryunjdvtvh6s6szmanyadumpbmvevgdpe4wk5c65unyt4yid.onion", - "ntf://CJ5o7X6fCxj2FFYRU2KuCo70y4jSqz7td2HYhLnXWbU=@ntf4.simplex.im,wtvuhdj26jwprmomnyfu5wfuq2hjkzfcc72u44vi6gdhrwxldt6xauad.onion" + [ "ntf://FB-Uop7RTaZZEG0ZLD2CIaTjsPh-Fw0zFAnb7QyA8Ks=@ntf2.simplex.im,5ex3mupcazy3zlky64ab27phjhijpemsiby33qzq3pliejipbtx5xgad.onion" + -- "ntf://KmpZNNXiVZJx_G2T7jRUmDFxWXM3OAnunz3uLT0tqAA=@ntf3.simplex.im,pxculznuryunjdvtvh6s6szmanyadumpbmvevgdpe4wk5c65unyt4yid.onion", + -- "ntf://CJ5o7X6fCxj2FFYRU2KuCo70y4jSqz7td2HYhLnXWbU=@ntf4.simplex.im,wtvuhdj26jwprmomnyfu5wfuq2hjkzfcc72u44vi6gdhrwxldt6xauad.onion" ] maxImageSize :: Integer From 9175897acff478a5111019c6dc474d534c201298 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Fri, 18 Oct 2024 14:17:04 +0100 Subject: [PATCH 08/11] core, ui: add SMP STORE error (#5071) * core, ui: add SMP STORE error * update library --- apps/ios/SimpleX.xcodeproj/project.pbxproj | 16 ++++++++-------- apps/ios/SimpleXChat/APITypes.swift | 1 + .../chat/simplex/common/model/SimpleXAPI.kt | 2 ++ cabal.project | 2 +- scripts/nix/sha256map.nix | 2 +- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 6307bb1d16..602c337560 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -220,15 +220,15 @@ D77B92DC2952372200A5A1CC /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = D77B92DB2952372200A5A1CC /* SwiftyGif */; }; D7F0E33929964E7E0068AF69 /* LZString in Frameworks */ = {isa = PBXBuildFile; productRef = D7F0E33829964E7E0068AF69 /* LZString */; }; E51CC1E62C62085600DB91FE /* OneHandUICard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E51CC1E52C62085600DB91FE /* OneHandUICard.swift */; }; + E56F461C2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E56F461A2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs-ghc9.6.3.a */; }; + E56F461D2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E56F461B2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs.a */; }; E5DCF8DB2C56FAC1007928CC /* SimpleXChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; }; E5DCF9712C590272007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF96F2C590272007928CC /* Localizable.strings */; }; E5DCF9842C5902CE007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF9822C5902CE007928CC /* Localizable.strings */; }; E5DCF9982C5906FF007928CC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF9962C5906FF007928CC /* InfoPlist.strings */; }; E5E997C92CBA891A00D7A2FA /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E997C42CBA891A00D7A2FA /* libgmpxx.a */; }; E5E997CA2CBA891A00D7A2FA /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E997C52CBA891A00D7A2FA /* libgmp.a */; }; - E5E997CB2CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E997C62CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv-ghc9.6.3.a */; }; E5E997CC2CBA891A00D7A2FA /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E997C72CBA891A00D7A2FA /* libffi.a */; }; - E5E997CD2CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E997C82CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -562,6 +562,8 @@ D741547929AF90B00022400A /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/PushKit.framework; sourceTree = DEVELOPER_DIR; }; D7AA2C3429A936B400737B40 /* MediaEncryption.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = MediaEncryption.playground; path = Shared/MediaEncryption.playground; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; E51CC1E52C62085600DB91FE /* OneHandUICard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OneHandUICard.swift; sourceTree = ""; }; + E56F461A2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs-ghc9.6.3.a"; sourceTree = ""; }; + E56F461B2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs.a"; sourceTree = ""; }; E5DCF9702C590272007928CC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; E5DCF9722C590274007928CC /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = ""; }; E5DCF9732C590275007928CC /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; @@ -616,9 +618,7 @@ E5DCF9A82C590732007928CC /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = ""; }; E5E997C42CBA891A00D7A2FA /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; E5E997C52CBA891A00D7A2FA /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; - E5E997C62CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv-ghc9.6.3.a"; sourceTree = ""; }; E5E997C72CBA891A00D7A2FA /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; - E5E997C82CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv.a"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -657,14 +657,14 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + E56F461C2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs-ghc9.6.3.a in Frameworks */, E5E997C92CBA891A00D7A2FA /* libgmpxx.a in Frameworks */, - E5E997CB2CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv-ghc9.6.3.a in Frameworks */, E5E997CA2CBA891A00D7A2FA /* libgmp.a in Frameworks */, 5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */, E5E997CC2CBA891A00D7A2FA /* libffi.a in Frameworks */, 5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */, + E56F461D2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs.a in Frameworks */, CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */, - E5E997CD2CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -744,8 +744,8 @@ E5E997C72CBA891A00D7A2FA /* libffi.a */, E5E997C52CBA891A00D7A2FA /* libgmp.a */, E5E997C42CBA891A00D7A2FA /* libgmpxx.a */, - E5E997C62CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv-ghc9.6.3.a */, - E5E997C82CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv.a */, + E56F461A2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs-ghc9.6.3.a */, + E56F461B2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs.a */, ); path = Libraries; sourceTree = ""; diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index fae6d2293f..ea567427fe 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -2088,6 +2088,7 @@ public enum ProtocolErrorType: Decodable, Hashable { case AUTH case CRYPTO case QUOTA + case STORE(storeErr: String) case NO_MSG case LARGE_MSG case EXPIRED diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index 9f0ed746a4..29d45a3b9b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -6054,6 +6054,7 @@ sealed class SMPErrorType { is AUTH -> "AUTH" is CRYPTO -> "CRYPTO" is QUOTA -> "QUOTA" + is STORE -> "STORE ${storeErr}" is NO_MSG -> "NO_MSG" is LARGE_MSG -> "LARGE_MSG" is EXPIRED -> "EXPIRED" @@ -6066,6 +6067,7 @@ sealed class SMPErrorType { @Serializable @SerialName("AUTH") class AUTH: SMPErrorType() @Serializable @SerialName("CRYPTO") class CRYPTO: SMPErrorType() @Serializable @SerialName("QUOTA") class QUOTA: SMPErrorType() + @Serializable @SerialName("STORE") class STORE(val storeErr: String): SMPErrorType() @Serializable @SerialName("NO_MSG") class NO_MSG: SMPErrorType() @Serializable @SerialName("LARGE_MSG") class LARGE_MSG: SMPErrorType() @Serializable @SerialName("EXPIRED") class EXPIRED: SMPErrorType() diff --git a/cabal.project b/cabal.project index 2df43da784..4fc8a72d45 100644 --- a/cabal.project +++ b/cabal.project @@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: c41bfe831d6d3fdf068f7419cbfed6afa46cb5b5 + tag: 967afaf802d7ea98480eaf280bfc6f35d4d43f05 source-repository-package type: git diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index 487cbfa9dd..d68731083f 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."c41bfe831d6d3fdf068f7419cbfed6afa46cb5b5" = "1awqy4srdgcwmjf7q3s9w75w6wp38qk65fza2k3q1a1s2lj6h8w1"; + "https://github.com/simplex-chat/simplexmq.git"."967afaf802d7ea98480eaf280bfc6f35d4d43f05" = "0k8m07hxfgn8h8pqrfchqd8490fvv1jf8slw8qjp0vxdpxa84n3i"; "https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d"; "https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl"; From 28383edb83d61c9e5ad62f0352dee39aeba48bff Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 18 Oct 2024 14:21:17 +0100 Subject: [PATCH 09/11] core: 6.1.1.0 (simplexmq: 6.1.1.0) --- package.yaml | 2 +- simplex-chat.cabal | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.yaml b/package.yaml index d98b470be9..94dc13ad2e 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: simplex-chat -version: 6.1.0.9 +version: 6.1.1.0 #synopsis: #description: homepage: https://github.com/simplex-chat/simplex-chat#readme diff --git a/simplex-chat.cabal b/simplex-chat.cabal index 6913ddc14d..96d16f5004 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: simplex-chat -version: 6.1.0.9 +version: 6.1.1.0 category: Web, System, Services, Cryptography homepage: https://github.com/simplex-chat/simplex-chat#readme author: simplex.chat From 2ffabd1ef8a6eea257a727e40f1ab025098905c2 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Fri, 18 Oct 2024 18:07:38 +0400 Subject: [PATCH 10/11] ios: fix changing user via notification (#5069) --- apps/ios/Shared/Model/ChatModel.swift | 1 + apps/ios/Shared/Model/NtfManager.swift | 25 +++++++++++++++++++------ apps/ios/Shared/SimpleXApp.swift | 16 +++++++++++----- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index 8525a141e3..e6970d4728 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -147,6 +147,7 @@ final class ChatModel: ObservableObject { @Published var chatDbEncrypted: Bool? @Published var chatDbStatus: DBMigrationResult? @Published var ctrlInitInProgress: Bool = false + @Published var notificationResponse: UNNotificationResponse? // local authentication @Published var contentViewAccessAuthenticated: Bool = false @Published var laRequest: LocalAuthRequest? diff --git a/apps/ios/Shared/Model/NtfManager.swift b/apps/ios/Shared/Model/NtfManager.swift index 95063845f1..18fabc5a32 100644 --- a/apps/ios/Shared/Model/NtfManager.swift +++ b/apps/ios/Shared/Model/NtfManager.swift @@ -29,17 +29,33 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject { private var granted = false private var prevNtfTime: Dictionary = [:] + override init() { + super.init() + UNUserNotificationCenter.current().delegate = self + } + // Handle notification when app is in background func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler handler: () -> Void) { logger.debug("NtfManager.userNotificationCenter: didReceive") - let content = response.notification.request.content + if appStateGroupDefault.get() == .active { + processNotificationResponse(response) + } else { + logger.debug("NtfManager.userNotificationCenter: remember response in model") + ChatModel.shared.notificationResponse = response + } + handler() + } + + func processNotificationResponse(_ ntfResponse: UNNotificationResponse) { let chatModel = ChatModel.shared - let action = response.actionIdentifier - logger.debug("NtfManager.userNotificationCenter: didReceive: action \(action), categoryIdentifier \(content.categoryIdentifier)") + let content = ntfResponse.notification.request.content + let action = ntfResponse.actionIdentifier + logger.debug("NtfManager.processNotificationResponse: didReceive: action \(action), categoryIdentifier \(content.categoryIdentifier)") if let userId = content.userInfo["userId"] as? Int64, userId != chatModel.currentUser?.userId { + logger.debug("NtfManager.processNotificationResponse changeActiveUser") changeActiveUser(userId, viewPwd: nil) } if content.categoryIdentifier == ntfCategoryContactRequest && (action == ntfActionAcceptContact || action == ntfActionAcceptContactIncognito), @@ -61,7 +77,6 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject { ItemsModel.shared.loadOpenChat(chatId) } } - handler() } private func ntfCallAction(_ content: UNNotificationContent, _ action: String) -> (ChatId, NtfCallAction)? { @@ -76,7 +91,6 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject { return nil } - // Handle notification when the app is in foreground func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, @@ -210,7 +224,6 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject { } } } - center.delegate = self } func notifyContactRequest(_ user: any UserLike, _ contactRequest: UserContactRequest) { diff --git a/apps/ios/Shared/SimpleXApp.swift b/apps/ios/Shared/SimpleXApp.swift index 7f2c3b5866..0dd54782b4 100644 --- a/apps/ios/Shared/SimpleXApp.swift +++ b/apps/ios/Shared/SimpleXApp.swift @@ -82,11 +82,17 @@ struct SimpleXApp: App { if appState != .stopped { startChatAndActivate { - if appState.inactive && chatModel.chatRunning == true { - Task { - await updateChats() - if !chatModel.showCallView && !CallController.shared.hasActiveCalls() { - await updateCallInvitations() + if chatModel.chatRunning == true { + if let ntfResponse = chatModel.notificationResponse { + chatModel.notificationResponse = nil + NtfManager.shared.processNotificationResponse(ntfResponse) + } + if appState.inactive { + Task { + await updateChats() + if !chatModel.showCallView && !CallController.shared.hasActiveCalls() { + await updateCallInvitations() + } } } } From edf99fcd1d7d38d2501d19608b94c084cf00f2ac Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 18 Oct 2024 18:37:14 +0100 Subject: [PATCH 11/11] 6.1.1: ios 245, android 249, desktop 74 --- apps/ios/SimpleX.xcodeproj/project.pbxproj | 56 +++++++++++----------- apps/multiplatform/gradle.properties | 8 ++-- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 602c337560..a21d47c16c 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -220,8 +220,8 @@ D77B92DC2952372200A5A1CC /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = D77B92DB2952372200A5A1CC /* SwiftyGif */; }; D7F0E33929964E7E0068AF69 /* LZString in Frameworks */ = {isa = PBXBuildFile; productRef = D7F0E33829964E7E0068AF69 /* LZString */; }; E51CC1E62C62085600DB91FE /* OneHandUICard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E51CC1E52C62085600DB91FE /* OneHandUICard.swift */; }; - E56F461C2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E56F461A2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs-ghc9.6.3.a */; }; - E56F461D2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E56F461B2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs.a */; }; + E56F46202CC2B2E300F1559D /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E56F461E2CC2B2E300F1559D /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9.a */; }; + E56F46212CC2B2E300F1559D /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E56F461F2CC2B2E300F1559D /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9-ghc9.6.3.a */; }; E5DCF8DB2C56FAC1007928CC /* SimpleXChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; }; E5DCF9712C590272007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF96F2C590272007928CC /* Localizable.strings */; }; E5DCF9842C5902CE007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF9822C5902CE007928CC /* Localizable.strings */; }; @@ -562,8 +562,8 @@ D741547929AF90B00022400A /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/PushKit.framework; sourceTree = DEVELOPER_DIR; }; D7AA2C3429A936B400737B40 /* MediaEncryption.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = MediaEncryption.playground; path = Shared/MediaEncryption.playground; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; E51CC1E52C62085600DB91FE /* OneHandUICard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OneHandUICard.swift; sourceTree = ""; }; - E56F461A2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs-ghc9.6.3.a"; sourceTree = ""; }; - E56F461B2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs.a"; sourceTree = ""; }; + E56F461E2CC2B2E300F1559D /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9.a"; sourceTree = ""; }; + E56F461F2CC2B2E300F1559D /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9-ghc9.6.3.a"; sourceTree = ""; }; E5DCF9702C590272007928CC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; E5DCF9722C590274007928CC /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = ""; }; E5DCF9732C590275007928CC /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; @@ -657,14 +657,14 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - E56F461C2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs-ghc9.6.3.a in Frameworks */, E5E997C92CBA891A00D7A2FA /* libgmpxx.a in Frameworks */, E5E997CA2CBA891A00D7A2FA /* libgmp.a in Frameworks */, 5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */, E5E997CC2CBA891A00D7A2FA /* libffi.a in Frameworks */, 5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */, - E56F461D2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs.a in Frameworks */, + E56F46202CC2B2E300F1559D /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9.a in Frameworks */, CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */, + E56F46212CC2B2E300F1559D /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9-ghc9.6.3.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -744,8 +744,8 @@ E5E997C72CBA891A00D7A2FA /* libffi.a */, E5E997C52CBA891A00D7A2FA /* libgmp.a */, E5E997C42CBA891A00D7A2FA /* libgmpxx.a */, - E56F461A2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs-ghc9.6.3.a */, - E56F461B2CC290A700F1559D /* libHSsimplex-chat-6.1.0.9-7TvTSM1A8tfDn1HsJMdPKs.a */, + E56F461F2CC2B2E300F1559D /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9-ghc9.6.3.a */, + E56F461E2CC2B2E300F1559D /* libHSsimplex-chat-6.1.1.0-KwHIA7FZqPI5ZTCAoi00n9.a */, ); path = Libraries; sourceTree = ""; @@ -1903,7 +1903,7 @@ CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 244; + CURRENT_PROJECT_VERSION = 245; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; @@ -1928,7 +1928,7 @@ "@executable_path/Frameworks", ); LLVM_LTO = YES_THIN; - MARKETING_VERSION = 6.1; + MARKETING_VERSION = 6.1.1; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_NAME = SimpleX; SDKROOT = iphoneos; @@ -1952,7 +1952,7 @@ CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 244; + CURRENT_PROJECT_VERSION = 245; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; @@ -1977,7 +1977,7 @@ "@executable_path/Frameworks", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.1; + MARKETING_VERSION = 6.1.1; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_NAME = SimpleX; SDKROOT = iphoneos; @@ -1993,11 +1993,11 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 244; + CURRENT_PROJECT_VERSION = 245; DEVELOPMENT_TEAM = 5NN7GUYB6T; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 6.1; + MARKETING_VERSION = 6.1.1; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2013,11 +2013,11 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 244; + CURRENT_PROJECT_VERSION = 245; DEVELOPMENT_TEAM = 5NN7GUYB6T; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 6.1; + MARKETING_VERSION = 6.1.1; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2038,7 +2038,7 @@ CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 244; + CURRENT_PROJECT_VERSION = 245; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; GCC_OPTIMIZATION_LEVEL = s; @@ -2053,7 +2053,7 @@ "@executable_path/../../Frameworks", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.1; + MARKETING_VERSION = 6.1.1; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2075,7 +2075,7 @@ CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 244; + CURRENT_PROJECT_VERSION = 245; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; ENABLE_CODE_COVERAGE = NO; @@ -2090,7 +2090,7 @@ "@executable_path/../../Frameworks", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.1; + MARKETING_VERSION = 6.1.1; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2112,7 +2112,7 @@ CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 244; + CURRENT_PROJECT_VERSION = 245; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; DYLIB_COMPATIBILITY_VERSION = 1; @@ -2138,7 +2138,7 @@ "$(PROJECT_DIR)/Libraries/sim", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.1; + MARKETING_VERSION = 6.1.1; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; @@ -2163,7 +2163,7 @@ CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 244; + CURRENT_PROJECT_VERSION = 245; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; DYLIB_COMPATIBILITY_VERSION = 1; @@ -2189,7 +2189,7 @@ "$(PROJECT_DIR)/Libraries/sim", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.1; + MARKETING_VERSION = 6.1.1; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; @@ -2214,7 +2214,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 244; + CURRENT_PROJECT_VERSION = 245; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -2229,7 +2229,7 @@ "@executable_path/../../Frameworks", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 6.1; + MARKETING_VERSION = 6.1.1; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2248,7 +2248,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 244; + CURRENT_PROJECT_VERSION = 245; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -2263,7 +2263,7 @@ "@executable_path/../../Frameworks", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 6.1; + MARKETING_VERSION = 6.1.1; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; diff --git a/apps/multiplatform/gradle.properties b/apps/multiplatform/gradle.properties index 907b0a94ae..32bd7579c8 100644 --- a/apps/multiplatform/gradle.properties +++ b/apps/multiplatform/gradle.properties @@ -26,11 +26,11 @@ android.enableJetifier=true kotlin.mpp.androidSourceSetLayoutVersion=2 kotlin.jvm.target=11 -android.version_name=6.1 -android.version_code=247 +android.version_name=6.1.1 +android.version_code=249 -desktop.version_name=6.1 -desktop.version_code=73 +desktop.version_name=6.1.1 +desktop.version_code=74 kotlin.version=1.9.23 gradle.plugin.version=8.2.0