From f36172660b1d0b7b4d994fe9d8ee67c6637c87cc Mon Sep 17 00:00:00 2001 From: lissine Date: Sat, 30 Nov 2024 22:15:22 +0100 Subject: [PATCH 1/6] Rewrite the inline WebKit view in SwiftUI --- Monal/Classes/RegisterAccount.swift | 42 +++---------------------- Monal/Classes/WebView.swift | 45 +++++++++++++++++++++++++++ Monal/Monal.xcodeproj/project.pbxproj | 4 +++ 3 files changed, 53 insertions(+), 38 deletions(-) create mode 100644 Monal/Classes/WebView.swift diff --git a/Monal/Classes/RegisterAccount.swift b/Monal/Classes/RegisterAccount.swift index 140c11030..b962fdc68 100644 --- a/Monal/Classes/RegisterAccount.swift +++ b/Monal/Classes/RegisterAccount.swift @@ -6,26 +6,8 @@ // Copyright © 2022 Monal.im. All rights reserved. // -import SafariServices -import WebKit import FrameUp -struct WebView: UIViewRepresentable { - var url: URL - - func makeUIView(context: Context) -> WKWebView { - return WKWebView() - } - - func updateUIView(_ webView: WKWebView, context: Context) { - var request = URLRequest(url: url) - if HelperTools.defaultsDB().bool(forKey:"useDnssecForAllConnections") { - request.requiresDNSSECValidation = true; - } - webView.load(request) - } -} - struct RegisterAccount: View { static private let xmppFaultyPattern = ".+\\..{2,}$" static private let credFaultyPattern = ".*@.*" @@ -57,7 +39,6 @@ struct RegisterAccount: View { @StateObject private var overlay = LoadingOverlayState() @State private var currentTimeout : DispatchTime? = nil - @State private var showWebView = false @State private var errorObserverEnabled = false var delegate: SheetDismisserProtocol @@ -428,29 +409,14 @@ struct RegisterAccount: View { .padding(.vertical, 8) if(selectedServerIndex != 0) { - Button (action: { - showWebView.toggle() - }){ + NavigationLink(destination: LazyClosureView(WebView(url: termsSiteForCurrentLanguage()))) { Text("Terms of use for \(RegisterAccount.XMPPServer[$selectedServerIndex.wrappedValue]["XMPPServer"]!)") .font(.system(size: 10)) - } - .frame(maxWidth: .infinity) - .sheet(isPresented: $showWebView) { - NavigationStack { - WebView(url: termsSiteForCurrentLanguage()) - .navigationBarTitle(Text("Terms of \(RegisterAccount.XMPPServer[$selectedServerIndex.wrappedValue]["XMPPServer"]!)"), displayMode: .inline) - .toolbar(content: { - ToolbarItem(placement: .bottomBar) { - Button (action: { - showWebView.toggle() - }){ - Text("Close") - } - } - }) - } + .foregroundStyle(Color.accentColor) + .frame(maxWidth: .infinity, alignment: .center) } } + } .textFieldStyle(.roundedBorder) } diff --git a/Monal/Classes/WebView.swift b/Monal/Classes/WebView.swift new file mode 100644 index 000000000..bc27db61f --- /dev/null +++ b/Monal/Classes/WebView.swift @@ -0,0 +1,45 @@ +// +// WebView.swift +// Monal +// +// Created by lissine on 30/11/2024. +// Copyright © 2024 monal-im.org. All rights reserved. +// + +import WebKit + +struct WebKitView: UIViewRepresentable { + var url: URL + + func makeUIView(context: Context) -> WKWebView { + return WKWebView() + } + + func updateUIView(_ webView: WKWebView, context: Context) { + var request = URLRequest(url: url) + if HelperTools.defaultsDB().bool(forKey: "useDnssecForAllConnections") { + request.requiresDNSSECValidation = true + } + webView.load(request) + } +} + +struct WebView: View { + var url: URL + var body: some View { + WebKitView(url: url) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + if !url.isFileURL { + ToolbarItem(placement: .topBarTrailing) { + Button(action: { + UIApplication.shared.open(url) + }, label: { + Image(systemName: "safari") + .accessibilityLabel("Open in default browser") + }) + } + } + } + } +} diff --git a/Monal/Monal.xcodeproj/project.pbxproj b/Monal/Monal.xcodeproj/project.pbxproj index cd832858e..1880da27f 100644 --- a/Monal/Monal.xcodeproj/project.pbxproj +++ b/Monal/Monal.xcodeproj/project.pbxproj @@ -217,6 +217,7 @@ C1F5C7AF2777638B0001F295 /* OrderedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = C1F5C7AE2777638B0001F295 /* OrderedCollections */; }; D02192F32C89BB3800202A59 /* BlockedUsers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02192F22C89BB3800202A59 /* BlockedUsers.swift */; }; D09B51F62C7F30DD008D725B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 26B2A4BA1B73061400272E63 /* Images.xcassets */; }; + D0C7F9432CFB563B00F3562D /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7F9422CFB563B00F3562D /* WebView.swift */; }; D0FA79B12C7E5C7400216D2A /* ServerDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA79B02C7E5C7400216D2A /* ServerDetails.swift */; }; D7E74AF213445E39318BC648 /* Pods_MonalUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29250DA62DD2322383585B2B /* Pods_MonalUITests.framework */; }; E89DD32525C6626400925F62 /* MLFileTransferDataCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E89DD32025C6626300925F62 /* MLFileTransferDataCell.m */; }; @@ -760,6 +761,7 @@ C1F5C7A82775DA000001F295 /* MLContactSoftwareVersionInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MLContactSoftwareVersionInfo.m; sourceTree = ""; }; C1F5C7AB2777621B0001F295 /* ContactResources.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactResources.swift; sourceTree = ""; }; D02192F22C89BB3800202A59 /* BlockedUsers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedUsers.swift; sourceTree = ""; }; + D0C7F9422CFB563B00F3562D /* WebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebView.swift; sourceTree = ""; }; D0FA79B02C7E5C7400216D2A /* ServerDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerDetails.swift; sourceTree = ""; }; D310A8387B2EB10761312F77 /* Pods-NotificaionService.appstore.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificaionService.appstore.xcconfig"; path = "Target Support Files/Pods-NotificaionService/Pods-NotificaionService.appstore.xcconfig"; sourceTree = ""; }; D8D2595B2BE453296E59F1AF /* Pods-MonalUITests.appstore.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MonalUITests.appstore.xcconfig"; path = "Target Support Files/Pods-MonalUITests/Pods-MonalUITests.appstore.xcconfig"; sourceTree = ""; }; @@ -1085,6 +1087,7 @@ C114D13C2B15B903000FB99F /* ContactEntry.swift */, 952EBC7F2BAF72F300183DBF /* DebugView.swift */, 34BC08112C5E9BE30099FB85 /* ContentUnavailableShimView.swift */, + D0C7F9422CFB563B00F3562D /* WebView.swift */, ); name = "View Controllers"; path = Classes; @@ -2084,6 +2087,7 @@ E89DD32525C6626400925F62 /* MLFileTransferDataCell.m in Sources */, E89DD32825C6626400925F62 /* MLFileTransferTextCell.m in Sources */, 84BBAECA2C42D272009492E2 /* Quicksy_RegisterAccount.swift in Sources */, + D0C7F9432CFB563B00F3562D /* WebView.swift in Sources */, 26B0CA8921AE2E3C0080B133 /* MLSoundsTableViewController.m in Sources */, 84D31CE628653B83006D7926 /* WebRTCClient.swift in Sources */, 54F0B81C282316F5003664BD /* RegisterAccount.swift in Sources */, From 5704ee584dc7ac6e6c1e7e3db78ea65fd26ef661 Mon Sep 17 00:00:00 2001 From: lissine Date: Sat, 30 Nov 2024 22:20:25 +0100 Subject: [PATCH 2/6] Rewrite the AccountList view in SwiftUI --- Monal/Classes/AccountList.swift | 111 ++++++++++++++++++++++++++ Monal/Monal.xcodeproj/project.pbxproj | 4 + 2 files changed, 115 insertions(+) create mode 100644 Monal/Classes/AccountList.swift diff --git a/Monal/Classes/AccountList.swift b/Monal/Classes/AccountList.swift new file mode 100644 index 000000000..932c09759 --- /dev/null +++ b/Monal/Classes/AccountList.swift @@ -0,0 +1,111 @@ +// +// AccountList.swift +// Monal +// +// Created by lissine on 30/11/2024. +// Copyright © 2024 monal-im.org. All rights reserved. +// + +private class Account: Identifiable { + let accountID: NSNumber + let username: String + let domain: String + var enabled: Bool + var jid: String { + return username+"@"+domain + } + var connected: Bool { + return MLXMPPManager.sharedInstance().isAccount(forIdConnected: self.accountID) + } + var connectedTime: Date { + return MLXMPPManager.sharedInstance().connectedTime(for: self.accountID) + } + var avatar: UIImage { + return MLImageManager.sharedInstance().getIconFor(MLContact.createContact(fromJid: self.jid, andAccountID: self.accountID)) ?? UIImage(named: "noicon")! + } + // Conformance to the Identifiable protocol + var id: NSNumber { + return accountID + } + + init(account: [String: Any]) { + self.accountID = account["account_id"] as! NSNumber + self.username = account["username"] as! String + self.domain = account["domain"] as! String + self.enabled = account["enabled"] as! Bool + } +} + +private struct AccountEntry: View { + let account: Account + let uptimeFormatter: DateFormatter + + init(account: Account) { + self.account = account + self.uptimeFormatter = DateFormatter() + uptimeFormatter.dateStyle = .short + uptimeFormatter.timeStyle = .short + uptimeFormatter.doesRelativeDateFormatting = true + } + + var connectionStatusString: String { + if account.enabled && account.connected { + return String(format: NSLocalizedString("Connected since: %@", comment: ""), uptimeFormatter.string(from: account.connectedTime)) + } else if account.enabled && !account.connected { + return NSLocalizedString("Connecting...", comment: "") + } else { + return NSLocalizedString("Account disabled", comment: "") + } + } + + var body: some View { + HStack { + Image(uiImage: account.avatar) + .resizable() + .scaledToFit() + .frame(width: 40, height: 40) + .padding(.leading, -3) + .padding(.trailing, 4) + VStack { + Text(account.jid) + .frame(maxWidth: .infinity, alignment: .leading) + + Text(self.connectionStatusString) + .font(.footnote) + .frame(maxWidth: .infinity, alignment: .leading) + } + Spacer() + Image(systemName: account.enabled ? (account.connected ? "checkmark.circle.fill" : "checkmark.circle") : "circle") + .foregroundStyle(Color.accentColor) + } + } +} + +struct AccountList: View { + @State private var accounts: [Account] = getAccountList() + + private static func getAccountList() -> [Account] { + return (DataLayer.sharedInstance().accountList() as! [[String: Any]]).map { Account(account: $0) } + } + private func refreshAccountList() { + self.accounts = AccountList.getAccountList() + } + + var body: some View { + List { + ForEach(accounts) { account in + NavigationLink { + LazyClosureView(EmptyView()) + } label: { + AccountEntry(account: account) + } + } + } + .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name(kMonalAccountStatusChanged)).receive(on: RunLoop.main)) { notification in + DispatchQueue.main.async { + DDLogVerbose("Refreshing the account list in the Settings view") + refreshAccountList() + } + } + } +} diff --git a/Monal/Monal.xcodeproj/project.pbxproj b/Monal/Monal.xcodeproj/project.pbxproj index 1880da27f..c889313fe 100644 --- a/Monal/Monal.xcodeproj/project.pbxproj +++ b/Monal/Monal.xcodeproj/project.pbxproj @@ -216,6 +216,7 @@ C1F5C7AC2777621B0001F295 /* ContactResources.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F5C7AB2777621B0001F295 /* ContactResources.swift */; }; C1F5C7AF2777638B0001F295 /* OrderedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = C1F5C7AE2777638B0001F295 /* OrderedCollections */; }; D02192F32C89BB3800202A59 /* BlockedUsers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02192F22C89BB3800202A59 /* BlockedUsers.swift */; }; + D06DCE862CF0F5CB00EDB010 /* AccountList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06DCE852CF0F5CA00EDB010 /* AccountList.swift */; }; D09B51F62C7F30DD008D725B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 26B2A4BA1B73061400272E63 /* Images.xcassets */; }; D0C7F9432CFB563B00F3562D /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7F9422CFB563B00F3562D /* WebView.swift */; }; D0FA79B12C7E5C7400216D2A /* ServerDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA79B02C7E5C7400216D2A /* ServerDetails.swift */; }; @@ -761,6 +762,7 @@ C1F5C7A82775DA000001F295 /* MLContactSoftwareVersionInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MLContactSoftwareVersionInfo.m; sourceTree = ""; }; C1F5C7AB2777621B0001F295 /* ContactResources.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactResources.swift; sourceTree = ""; }; D02192F22C89BB3800202A59 /* BlockedUsers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedUsers.swift; sourceTree = ""; }; + D06DCE852CF0F5CA00EDB010 /* AccountList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountList.swift; sourceTree = ""; }; D0C7F9422CFB563B00F3562D /* WebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebView.swift; sourceTree = ""; }; D0FA79B02C7E5C7400216D2A /* ServerDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerDetails.swift; sourceTree = ""; }; D310A8387B2EB10761312F77 /* Pods-NotificaionService.appstore.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificaionService.appstore.xcconfig"; path = "Target Support Files/Pods-NotificaionService/Pods-NotificaionService.appstore.xcconfig"; sourceTree = ""; }; @@ -1058,6 +1060,7 @@ 26AAAAD92295742400200433 /* MLPasswordChangeTableViewController.m */, D0FA79B02C7E5C7400216D2A /* ServerDetails.swift */, D02192F22C89BB3800202A59 /* BlockedUsers.swift */, + D06DCE852CF0F5CA00EDB010 /* AccountList.swift */, ); name = Accounts; sourceTree = ""; @@ -2070,6 +2073,7 @@ 26D89F061A890672009B147C /* MLSwitchCell.m in Sources */, C18967C72B81F61B0073C7C5 /* ChannelMemberList.swift in Sources */, C114D13D2B15B903000FB99F /* ContactEntry.swift in Sources */, + D06DCE862CF0F5CB00EDB010 /* AccountList.swift in Sources */, 841B6F1A297B18720074F9B7 /* AccountPicker.swift in Sources */, 3D65B791272350F0005A30F4 /* SwiftuiHelpers.swift in Sources */, C1A80DA424D9552400B99E01 /* MLChatViewHelper.m in Sources */, From fad973ffa56cbab3fd3fed040f094123c11675f7 Mon Sep 17 00:00:00 2001 From: lissine Date: Sat, 30 Nov 2024 22:22:23 +0100 Subject: [PATCH 3/6] Bridge MLSoundsTableViewController to SwiftUI --- Monal/Classes/Monal-Bridging-Header.h | 1 + Monal/Classes/SwiftuiHelpers.swift | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/Monal/Classes/Monal-Bridging-Header.h b/Monal/Classes/Monal-Bridging-Header.h index ef3d166fb..316ccfa22 100644 --- a/Monal/Classes/Monal-Bridging-Header.h +++ b/Monal/Classes/Monal-Bridging-Header.h @@ -2,3 +2,4 @@ #import "ActiveChatsViewController.h" #import "MLMucProcessor.h" #import "SCRAM.h" +#import "MLSoundsTableViewController.h" diff --git a/Monal/Classes/SwiftuiHelpers.swift b/Monal/Classes/SwiftuiHelpers.swift index e21e3a599..59e1dc876 100644 --- a/Monal/Classes/SwiftuiHelpers.swift +++ b/Monal/Classes/SwiftuiHelpers.swift @@ -842,4 +842,14 @@ class SwiftuiInterface : NSObject { delegate.host = host! return host! } + + struct SoundsSettings: UIViewControllerRepresentable { + func makeUIViewController(context: Context) -> MLSoundsTableViewController { + let viewController = MLSoundsTableViewController() + viewController.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "soundCell") + return viewController + } + func updateUIViewController(_ uiViewController: MLSoundsTableViewController, context: Context) { + } + } } From a425c521462f52308b1fa7aa56a8a0d0924df124 Mon Sep 17 00:00:00 2001 From: lissine Date: Sat, 30 Nov 2024 22:25:06 +0100 Subject: [PATCH 4/6] Rewrite the Settings view in SwiftUI --- Monal/Classes/ActiveChatsViewController.m | 18 +++- Monal/Classes/Settings.swift | 108 ++++++++++++++++++++++ Monal/Classes/SwiftuiHelpers.swift | 9 ++ Monal/Monal.xcodeproj/project.pbxproj | 4 + 4 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 Monal/Classes/Settings.swift diff --git a/Monal/Classes/ActiveChatsViewController.m b/Monal/Classes/ActiveChatsViewController.m index a62eab26d..8a6cf5a20 100755 --- a/Monal/Classes/ActiveChatsViewController.m +++ b/Monal/Classes/ActiveChatsViewController.m @@ -262,6 +262,17 @@ -(void) configureComposeButton [self.composeButton setAccessibilityTraits:UIAccessibilityTraitButton]; } +-(void) configureSettingsButton +{ + UIImageView* image = [[UIImageView alloc] initWithImage:[[UIImage systemImageNamed:@"gearshape.fill"] imageWithTintColor:UIColor.tintColor]]; + UITapGestureRecognizer* tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(showSettings)]; + [image addGestureRecognizer:tapRecognizer]; + self.settingsButton.customView = image; + [self.settingsButton setIsAccessibilityElement:YES]; + [self.settingsButton setAccessibilityLabel:NSLocalizedString(@"Open the settings", @"")]; + [self.settingsButton setAccessibilityTraits:UIAccessibilityTraitButton]; +} + -(void) viewDidLoad { DDLogDebug(@"active chats view did load"); @@ -305,8 +316,9 @@ -(void) viewDidLoad #if !TARGET_OS_MACCATALYST self.splitViewController.primaryBackgroundStyle = UISplitViewControllerBackgroundStyleSidebar; #endif - self.settingsButton.image = [UIImage systemImageNamed:@"gearshape.fill"]; + [self configureComposeButton]; + [self configureSettingsButton]; self.spinnerButton.customView = self.spinner; @@ -943,8 +955,8 @@ -(void) showGeneralSettings -(void) showSettings { appendToViewQueue((^(PMKResolver resolve) { - [self performSegueWithIdentifier:@"showSettings" sender:self]; - resolve(nil); + UIViewController* settingsView = [[SwiftuiInterface new] makeSettingsView]; + [self presentViewController:settingsView animated:YES completion:^{resolve(nil);}]; })); } diff --git a/Monal/Classes/Settings.swift b/Monal/Classes/Settings.swift new file mode 100644 index 000000000..729f71d62 --- /dev/null +++ b/Monal/Classes/Settings.swift @@ -0,0 +1,108 @@ +// +// Settings.swift +// Monal +// +// Created by lissine on 30/11/2024. +// Copyright © 2024 monal-im.org. All rights reserved. +// + +struct Settings: View { + @State private var tappedVersionInfo = 0 + @State private var showDebugEntry = HelperTools.defaultsDB().bool(forKey: "showLogInSettings") + var delegate: SheetDismisserProtocol + var body: some View { + Form { + Section(header: Text("")) { + AccountList() +#if !IS_QUICKSY + NavigationLink(destination: LazyClosureView(WelcomeLogIn(delegate: delegate))) { + Text("Add Account") + } + NavigationLink(destination: LazyClosureView(WelcomeLogIn(advancedMode: true, delegate: delegate))) { + Text("Add Account (advanced)") + } +#endif + } + Section(header: Text("App")) { + NavigationLink(destination: LazyClosureView(GeneralSettings())) { + Text("General Settings") + } + NavigationLink(destination: LazyClosureView(SwiftuiInterface.SoundsSettings())) { + Text("Sounds") + } + } + Section(header: Text("Support")) { + Link("Email Support", + destination: URL(string: "mailto:info@monal-im.org")!) + + NavigationLink(destination: LazyClosureView(WebView(url: URL(string: "https://github.com/monal-im/Monal/issues")!))) { + Text("Submit A Bug") + } + + NavigationLink(destination: LazyClosureView(WebView(url: URL(string: "https://github.com/monal-im/Monal/wiki/FAQ---Frequently-Asked-Questions")!))) { + Text("Frequently Asked Questions") + } + } + .tint(Color.primary) + Section(header: Text("About")) { +#if TARGET_OS_MACCATALYST + Link("Rate Monal", + destination: URL(string: "itms-apps://itunes.apple.com/app/1637078500")!) +#elseif IS_QUICKSY + Link("Rate Quicksy", + destination: URL(string: "itms-apps://itunes.apple.com/app/6538727270")!) +#else + Link("Rate Monal", + destination: URL(string: "itms-apps://itunes.apple.com/app/317711500")!) +#endif + + let path = Bundle.main.path(forResource: "opensource", ofType: "html") + NavigationLink(destination: LazyClosureView(WebView(url: URL(fileURLWithPath: path!)))) { + Text("Open Source") + } + + NavigationLink(destination: LazyClosureView(WebView(url: URL(string: "https://monal-im.org/privacy")!))) { + Text("Privacy") + } + + NavigationLink(destination: LazyClosureView(WebView(url: URL(string: "https://monal-im.org/about")!))) { + Text("About") + } +#if DEBUG + NavigationLink(destination: LazyClosureView(DebugView())) { + Text("Debug") + } +#else + if showDebugEntry { + NavigationLink(destination: LazyClosureView(DebugView())) { + Text("Debug") + } + } +#endif + + // Version button + Button(action: { + // Copy the version string to the clipboard + UIPasteboard.general.setValue(HelperTools.appBuildVersionInfo(for: MLVersionType.IQ), forPasteboardType: UTType.utf8PlainText.identifier) +#if !DEBUG + tappedVersionInfo += 1 + if tappedVersionInfo > 16 { + HelperTools.defaultsDB().set(true, forKey: "showLogInSettings") + // Redraw the view + showDebugEntry = true + } +#endif + }, label: { + HStack { + Text("Version") + Spacer() + Text(HelperTools.appBuildVersionInfo(for: MLVersionType.IQ)) + } + }) + } + .tint(Color.primary) + } + .navigationTitle("Settings") + .navigationBarTitleDisplayMode(.inline) + } +} diff --git a/Monal/Classes/SwiftuiHelpers.swift b/Monal/Classes/SwiftuiHelpers.swift index 59e1dc876..1e3f7c267 100644 --- a/Monal/Classes/SwiftuiHelpers.swift +++ b/Monal/Classes/SwiftuiHelpers.swift @@ -811,6 +811,15 @@ class SwiftuiInterface : NSObject { return host } + @objc + func makeSettingsView() -> UIViewController { + let delegate = SheetDismisserProtocol() + let host = UIHostingController(rootView:AnyView(EmptyView())) + delegate.host = host + host.rootView = AnyView(AddTopLevelNavigation(withDelegate: delegate, to: Settings(delegate: delegate))) + return host + } + @objc func makeView(name: String) -> UIViewController { let delegate = SheetDismisserProtocol() diff --git a/Monal/Monal.xcodeproj/project.pbxproj b/Monal/Monal.xcodeproj/project.pbxproj index c889313fe..43ad64545 100644 --- a/Monal/Monal.xcodeproj/project.pbxproj +++ b/Monal/Monal.xcodeproj/project.pbxproj @@ -219,6 +219,7 @@ D06DCE862CF0F5CB00EDB010 /* AccountList.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06DCE852CF0F5CA00EDB010 /* AccountList.swift */; }; D09B51F62C7F30DD008D725B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 26B2A4BA1B73061400272E63 /* Images.xcassets */; }; D0C7F9432CFB563B00F3562D /* WebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7F9422CFB563B00F3562D /* WebView.swift */; }; + D0E9996A2CEDF2CB00D9D63A /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E999692CEDF2CB00D9D63A /* Settings.swift */; }; D0FA79B12C7E5C7400216D2A /* ServerDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FA79B02C7E5C7400216D2A /* ServerDetails.swift */; }; D7E74AF213445E39318BC648 /* Pods_MonalUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29250DA62DD2322383585B2B /* Pods_MonalUITests.framework */; }; E89DD32525C6626400925F62 /* MLFileTransferDataCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E89DD32025C6626300925F62 /* MLFileTransferDataCell.m */; }; @@ -764,6 +765,7 @@ D02192F22C89BB3800202A59 /* BlockedUsers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedUsers.swift; sourceTree = ""; }; D06DCE852CF0F5CA00EDB010 /* AccountList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountList.swift; sourceTree = ""; }; D0C7F9422CFB563B00F3562D /* WebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebView.swift; sourceTree = ""; }; + D0E999692CEDF2CB00D9D63A /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; D0FA79B02C7E5C7400216D2A /* ServerDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerDetails.swift; sourceTree = ""; }; D310A8387B2EB10761312F77 /* Pods-NotificaionService.appstore.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificaionService.appstore.xcconfig"; path = "Target Support Files/Pods-NotificaionService/Pods-NotificaionService.appstore.xcconfig"; sourceTree = ""; }; D8D2595B2BE453296E59F1AF /* Pods-MonalUITests.appstore.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MonalUITests.appstore.xcconfig"; path = "Target Support Files/Pods-MonalUITests/Pods-MonalUITests.appstore.xcconfig"; sourceTree = ""; }; @@ -1030,6 +1032,7 @@ E8CF9CC026249640001A1952 /* MLSettingsAboutViewController.m */, 8441EFF82921B53500E851E9 /* BackgroundSettings.swift */, 20ED55842BADDA5C0005783E /* GeneralSettings.swift */, + D0E999692CEDF2CB00D9D63A /* Settings.swift */, ); name = Settings; sourceTree = ""; @@ -2075,6 +2078,7 @@ C114D13D2B15B903000FB99F /* ContactEntry.swift in Sources */, D06DCE862CF0F5CB00EDB010 /* AccountList.swift in Sources */, 841B6F1A297B18720074F9B7 /* AccountPicker.swift in Sources */, + D0E9996A2CEDF2CB00D9D63A /* Settings.swift in Sources */, 3D65B791272350F0005A30F4 /* SwiftuiHelpers.swift in Sources */, C1A80DA424D9552400B99E01 /* MLChatViewHelper.m in Sources */, C117F7E22B0863B3001F2BC6 /* ContactPicker.swift in Sources */, From 60839b8ca68fdd90acc65cc483ceaf0b616f50b6 Mon Sep 17 00:00:00 2001 From: lissine Date: Sat, 30 Nov 2024 22:29:06 +0100 Subject: [PATCH 5/6] Use the UIKit settings for now Revert this commit after the Account Settings are rewritten in SwiftUI (and are linked to from Settings.swift) --- Monal/Classes/ActiveChatsViewController.m | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Monal/Classes/ActiveChatsViewController.m b/Monal/Classes/ActiveChatsViewController.m index 8a6cf5a20..7bf705ba3 100755 --- a/Monal/Classes/ActiveChatsViewController.m +++ b/Monal/Classes/ActiveChatsViewController.m @@ -316,9 +316,8 @@ -(void) viewDidLoad #if !TARGET_OS_MACCATALYST self.splitViewController.primaryBackgroundStyle = UISplitViewControllerBackgroundStyleSidebar; #endif - + self.settingsButton.image = [UIImage systemImageNamed:@"gearshape.fill"]; [self configureComposeButton]; - [self configureSettingsButton]; self.spinnerButton.customView = self.spinner; @@ -955,8 +954,8 @@ -(void) showGeneralSettings -(void) showSettings { appendToViewQueue((^(PMKResolver resolve) { - UIViewController* settingsView = [[SwiftuiInterface new] makeSettingsView]; - [self presentViewController:settingsView animated:YES completion:^{resolve(nil);}]; + [self performSegueWithIdentifier:@"showSettings" sender:self]; + resolve(nil); })); } From f7a41a4581fbcef195acc15b1cfe85318014b23f Mon Sep 17 00:00:00 2001 From: lissine Date: Sun, 1 Dec 2024 16:41:11 +0100 Subject: [PATCH 6/6] Only hide the navigation back button on iPads This fixes the back button being missing on big-screen iPhones in landscape mode. For more information, see the following commit messages: 8183703 and 009becf --- Monal/Classes/SwiftuiHelpers.swift | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Monal/Classes/SwiftuiHelpers.swift b/Monal/Classes/SwiftuiHelpers.swift index 1e3f7c267..5514fc132 100644 --- a/Monal/Classes/SwiftuiHelpers.swift +++ b/Monal/Classes/SwiftuiHelpers.swift @@ -556,16 +556,12 @@ struct LazyClosureView: View { // use this to wrap a view into NavigationStack, if it should be the outermost swiftui view of a new view stack struct AddTopLevelNavigation: View { @Environment(\.presentationMode) private var presentationMode - @StateObject private var sizeClass: ObservableKVOWrapper let build: () -> Content let delegate: SheetDismisserProtocol? init(withDelegate delegate: SheetDismisserProtocol?, to build: @autoclosure @escaping () -> Content) { self.build = build self.delegate = delegate - - let activeChats = (UIApplication.shared.delegate as! MonalAppDelegate).activeChats! - self._sizeClass = StateObject(wrappedValue: ObservableKVOWrapper(activeChats.sizeClass)) } var body: some View { @@ -574,10 +570,13 @@ struct AddTopLevelNavigation: View { .navigationBarTitleDisplayMode(.automatic) .navigationBarBackButtonHidden(true) // will not be shown because swiftui does not know we navigated here from UIKit .toolbar { +// The macCatalyst build is currently using the iPad UI idiom +// But we want to display the back button on mac #if targetEnvironment(macCatalyst) let shouldDisplayBackButton = true #else - let shouldDisplayBackButton = UIUserInterfaceSizeClass(rawValue: sizeClass.horizontal) == .compact + // Only hide the back button on iPads + let shouldDisplayBackButton = UIDevice.current.userInterfaceIdiom != .pad #endif if shouldDisplayBackButton { ToolbarItem(placement: .topBarLeading) {