Просмотр исходного кода

add basic account switching ui and helpers

B. Petersen 5 лет назад
Родитель
Сommit
7b452e26e1

+ 4 - 0
DcCore/DcCore.xcodeproj/project.pbxproj

@@ -29,6 +29,7 @@
 		30E8F2482449C98600CE2C90 /* UIView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F2472449C98600CE2C90 /* UIView+Extensions.swift */; };
 		30E8F24B2449CF6500CE2C90 /* InitialsBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F24A2449CF6500CE2C90 /* InitialsBadge.swift */; };
 		30E8F24D2449D30200CE2C90 /* DcColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30E8F24C2449D30200CE2C90 /* DcColors.swift */; };
+		B245B07124BF68990084271C /* AccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B245B07024BF68990084271C /* AccountManager.swift */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -67,6 +68,7 @@
 		30E8F2472449C98600CE2C90 /* UIView+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Extensions.swift"; sourceTree = "<group>"; };
 		30E8F24A2449CF6500CE2C90 /* InitialsBadge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitialsBadge.swift; sourceTree = "<group>"; };
 		30E8F24C2449D30200CE2C90 /* DcColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DcColors.swift; sourceTree = "<group>"; };
+		B245B07024BF68990084271C /* AccountManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountManager.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -168,6 +170,7 @@
 		30421977243F1AF400516852 /* Helper */ = {
 			isa = PBXGroup;
 			children = (
+				B245B07024BF68990084271C /* AccountManager.swift */,
 				30E8F2202447357500CE2C90 /* DatabaseHelper.swift */,
 				30421987243F23E500516852 /* Constants.swift */,
 				3042195C243E23F100516852 /* DcUtils.swift */,
@@ -324,6 +327,7 @@
 				30421951243DE15D00516852 /* Wrapper.swift in Sources */,
 				306C324824460CDE001D89F3 /* DateUtils.swift in Sources */,
 				30421952243DE15D00516852 /* wrapper.c in Sources */,
+				B245B07124BF68990084271C /* AccountManager.swift in Sources */,
 				30E8F24B2449CF6500CE2C90 /* InitialsBadge.swift in Sources */,
 				30E8F2212447357500CE2C90 /* DatabaseHelper.swift in Sources */,
 				3042195D243E23F100516852 /* DcUtils.swift in Sources */,

+ 5 - 1
DcCore/DcCore/DC/Wrapper.swift

@@ -13,7 +13,7 @@ public class DcContext {
     public var lastWarningString: String = "" // temporary thing to get a grip on some weird errors
     public var maxConfigureProgress: Int = 0 // temporary thing to get a grip on some weird errors
 
-    private init() {
+    public init() {
     }
 
     deinit {
@@ -195,6 +195,10 @@ public class DcContext {
         contextPointer = dc_context_new("iOS" + version, dbFile, nil)
     }
 
+    public func isOk() -> Bool {
+        return contextPointer != nil
+    }
+
     public func closeDatabase() {
         dc_context_unref(contextPointer)
         contextPointer = nil

+ 1 - 0
DcCore/DcCore/Extensions/UserDefaults+Extensions.swift

@@ -1,6 +1,7 @@
 import Foundation
 public extension UserDefaults {
     static var hasExtensionAttemptedToSend = "hasExtensionAttemptedToSend"
+    static var currAccountDbName = "currAccountDbName"
     static var shared: UserDefaults? {
         return UserDefaults(suiteName: "group.chat.delta.ios")
     }

+ 86 - 0
DcCore/DcCore/Helper/AccountManager.swift

@@ -0,0 +1,86 @@
+import Foundation
+
+public class Account {
+    private let dbName: String
+    public let displayname: String
+    public let addr: String
+    public let configured: Bool
+    public let current: Bool
+
+    public init(dbName: String, displayname: String, addr: String, configured: Bool) {
+        self.dbName = dbName
+        self.displayname = displayname
+        self.addr = addr
+        self.configured = configured
+        self.current = false
+    }
+}
+
+public class AccountManager {
+
+    private let defaultDbName = "messenger.db"
+
+    public init() {
+    }
+
+    private func maybeGetAccount(dbFile: String) -> Account? {
+        let testContext = DcContext()
+        testContext.openDatabase(dbFile: dbFile)
+        if !testContext.isOk() {
+            return nil
+        }
+
+        return Account(dbName: dbFile,
+                       displayname: testContext.getConfig("displayname") ?? "",
+                       addr: testContext.getConfig("addr") ?? "",
+                       configured: testContext.isConfigured())
+    }
+
+    private func resetDcContext() {
+
+    }
+
+
+    // MARK: - public api
+
+    // get a list of all accounts available or an empty array on errors
+    // (eg. when the shared dir is empty due to out-of-space on updateDatabaseLocation()-migration)
+    public func getAccounts() -> [Account] {
+        var result: [Account] = Array()
+        do {
+            let databaseHelper = DatabaseHelper()
+            if databaseHelper.updateSucceeded(), let sharedDir = databaseHelper.sharedDir {
+                let names = try FileManager.default.contentsOfDirectory(atPath: sharedDir.path)
+                for name in names {
+                    if name.hasPrefix("messenger") && name.hasSuffix(".db") {
+                        let dbFile = sharedDir.appendingPathComponent(name).path
+                        if let account = maybeGetAccount(dbFile: dbFile) {
+                            result.append(account)
+                        }
+                    }
+                }
+            }
+        } catch {
+            DcContext.shared.logger?.error("Could not iterate through sharedDir.")
+        }
+        return result
+    }
+
+    public func getSelectedAccount() -> String {
+        let databaseHelper = DatabaseHelper()
+        if databaseHelper.updateSucceeded(), let sharedDir = databaseHelper.sharedDir {
+            if let userDefaults = UserDefaults.shared {
+                let name = userDefaults.string(forKey: UserDefaults.currAccountDbName) ?? defaultDbName
+                return sharedDir.appendingPathComponent(name).path
+            }
+        }
+        // error, usefallback
+        return databaseHelper.currentDatabaseLocation
+    }
+
+    // pause the current account and let the user create a new one.
+    // this function is not needed on the very first account creation.
+    public func beginAccountCreation() {
+        resetDcContext()
+    }
+}

+ 9 - 2
DcCore/DcCore/Helper/DatabaseHelper.swift

@@ -5,8 +5,12 @@ public class DatabaseHelper {
     /// The ID is created in the apple developer portal and can be changed there.
     static let applicationGroupIdentifier = "group.chat.delta.ios"
 
+    public var sharedDir: URL? {
+        return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: DatabaseHelper.applicationGroupIdentifier)
+    }
+
     public var sharedDbFile: String {
-        guard let fileContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: DatabaseHelper.applicationGroupIdentifier) else {
+        guard let fileContainer = sharedDir else {
             return ""
         }
         let storeURL = fileContainer.appendingPathComponent("messenger.db")
@@ -18,7 +22,7 @@ public class DatabaseHelper {
     }
 
     var sharedDbBlobsDir: String {
-        guard let fileContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: DatabaseHelper.applicationGroupIdentifier) else {
+        guard let fileContainer = sharedDir else {
             return ""
         }
         return fileContainer.appendingPathComponent("messenger.db-blobs").path
@@ -112,4 +116,7 @@ public class DatabaseHelper {
       return sharedDbFile
     }
 
+    public func updateSucceeded() -> Bool {
+        return !FileManager.default.fileExists(atPath: localDbFile)
+    }
 }

+ 45 - 1
deltachat-ios/Controller/SettingsController.swift

@@ -24,6 +24,7 @@ internal final class SettingsViewController: UITableViewController, ProgressAler
         case help = 10
         case autodel = 11
         case mediaQuality = 12
+        case switchAccount = 13
     }
 
     private var dcContext: DcContext
@@ -170,6 +171,14 @@ internal final class SettingsViewController: UITableViewController, ProgressAler
         return cell
     }()
 
+    private lazy var switchAccountCell: ActionCell = {
+        let cell = ActionCell()
+        cell.tag = CellTags.switchAccount.rawValue
+        cell.actionTitle = String.localized("switch_account")
+        cell.selectionStyle = .default
+        return cell
+    }()
+
     private lazy var helpCell: ActionCell = {
         let cell = ActionCell()
         cell.tag = CellTags.help.rawValue
@@ -201,7 +210,7 @@ internal final class SettingsViewController: UITableViewController, ProgressAler
         let backupSection = SectionConfigs(
             headerTitle: nil,
             footerTitle: String.localized("pref_backup_explain"),
-            cells: [advancedCell, exportBackupCell])
+            cells: [switchAccountCell, advancedCell, exportBackupCell])
         let helpSection = SectionConfigs(
             headerTitle: nil,
             footerTitle: appNameAndVersion,
@@ -293,6 +302,7 @@ internal final class SettingsViewController: UITableViewController, ProgressAler
         case .sendAutocryptMessage: sendAutocryptSetupMessage()
         case .exportBackup: createBackup()
         case .advanced: showAdvancedDialog()
+        case .switchAccount: showSwitchAccountMenu()
         case .help: showHelp()
         }
     }
@@ -422,6 +432,40 @@ internal final class SettingsViewController: UITableViewController, ProgressAler
         present(alert, animated: true, completion: nil)
     }
 
+    private func showSwitchAccountMenu() {
+        let accounts = AccountManager().getAccounts()
+        if accounts.isEmpty {
+            let error = UIAlertController(title: String.localized("switch_account"),
+                message: "Cannot switch or add accounts as the 'shared folder' is not set up. " +
+                "To fix this, make sure, there is enough free space available and restart Delta Chat.",
+                preferredStyle: .alert)
+            error.addAction(UIAlertAction(title: String.localized("ok"), style: .cancel))
+            present(error, animated: true)
+            return
+        }
+
+        // switch account
+        let alert = UIAlertController(title: String.localized("switch_account"), message: nil, preferredStyle: .safeActionSheet)
+        for account in accounts {
+            var title = account.displayname.isEmpty ? account.addr : "\(account.displayname) (\(account.addr))"
+            title += account.current ? " ✓" : ""
+            alert.addAction(UIAlertAction(title: title, style: .default, handler: nil))
+        }
+
+        // delete account
+        if accounts.count > 1 {
+            alert.addAction(UIAlertAction(title: String.localized("delete_account"), style: .default, handler: nil))
+        }
+
+        // add account
+        alert.addAction(UIAlertAction(title: String.localized("add_account"), style: .default, handler: { _ in
+            AccountManager().beginAccountCreation()
+        }))
+
+        alert.addAction(UIAlertAction(title: String.localized("cancel"), style: .cancel, handler: nil))
+        present(alert, animated: true, completion: nil)
+    }
+
     private func startImex(what: Int32) {
         let documents = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
         if !documents.isEmpty {