Skip to content
Snippets Groups Projects
Commit e7668d03 authored by Kateryna Kostiuk's avatar Kateryna Kostiuk
Browse files

db: migrate to version 2

- save profiles as vCards
- move db inside account folder

Change-Id: I023b312f1589a98d489e8e99dbf4edab4903a98c
Gitlab: #71
parent 4a1c6a51
No related branches found
No related tags found
No related merge requests found
Showing
with 384 additions and 342 deletions
/* /*
* Copyright (C) 2017-2019 Savoir-faire Linux Inc. * Copyright (C) 2017-2020 Savoir-faire Linux Inc.
* *
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com> * Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
* *
...@@ -21,6 +21,25 @@ ...@@ -21,6 +21,25 @@
import SQLite import SQLite
import SwiftyBeaver import SwiftyBeaver
//================================================================================
// jami files structure
//
// Jami Documents folder
// └──{ account_id }
// ├── config.yml
// ├── contacts
// ├── archive.gz
// ├── incomingTrustRequests
// ├── knownDevicesNames
// ├── { account_id }.db < --conversations and interactions database
// ├── profile.vcf < --account vcard
// ├── profiles < --account contact vcards
// │ │──{ contact_uri }.vcf
// │ └── ...
// ├── ring_device.crt
// └── ring_device.key
//================================================================================
enum DataAccessError: Error { enum DataAccessError: Error {
case datastoreConnectionError case datastoreConnectionError
case databaseMigrationError case databaseMigrationError
...@@ -32,32 +51,18 @@ final class DBContainer { ...@@ -32,32 +51,18 @@ final class DBContainer {
private var connections = [String: Connection?]() private var connections = [String: Connection?]()
var connectionsSemaphore = DispatchSemaphore(value: 1) var connectionsSemaphore = DispatchSemaphore(value: 1)
private let log = SwiftyBeaver.self private let log = SwiftyBeaver.self
private let jamiDBName = "ring.db" private let dbVersions = [1, 2]
private let path: String?
private let dbVersion = 1
init() { let documentsPath = {
path = NSSearchPathForDirectoriesInDomains( return NSSearchPathForDirectoriesInDomains(
.documentDirectory, .userDomainMask, true .documentDirectory, .userDomainMask, true
).first ).first
} }()
func getJamiDB() -> Connection? {
if jamiDB != nil {
return jamiDB
}
guard let dbPath = path else { return nil }
do {
jamiDB = try Connection("\(dbPath)/" + jamiDBName)
} catch {
jamiDB = nil
log.error("Unable to open database")
}
return jamiDB
}
func removeJamiDB() { func removeDBForAccount(account: String, removeFolder: Bool) {
self.removeDBNamed(dbName: jamiDBName) connections[account] = nil
if !removeFolder { return }
self.removeAccountFolder(accountId: account)
} }
func removeDBForAccount(account: String) { func removeDBForAccount(account: String) {
...@@ -69,11 +74,11 @@ final class DBContainer { ...@@ -69,11 +74,11 @@ final class DBContainer {
if connections[account] != nil { if connections[account] != nil {
return connections[account] ?? nil return connections[account] ?? nil
} }
guard let dbPath = path else { return nil } guard let dbPath = accountDbPath(accountId: account) else { return nil }
do { do {
self.connectionsSemaphore.wait() self.connectionsSemaphore.wait()
connections[account] = try Connection("\(dbPath)/" + "\(account).db") connections[account] = try Connection(dbPath)
connections[account]??.userVersion = dbVersion connections[account]??.userVersion = dbVersions.last
self.connectionsSemaphore.signal() self.connectionsSemaphore.signal()
return connections[account] ?? nil return connections[account] ?? nil
} catch { } catch {
...@@ -83,24 +88,84 @@ final class DBContainer { ...@@ -83,24 +88,84 @@ final class DBContainer {
} }
} }
func isDBExistsFor(account: String) -> Bool { // MARK: paths
guard let dbPath = path else { return false }
let url = NSURL(fileURLWithPath: dbPath) private func accountFolderPath(accountId: String) -> String? {
if let pathComponent = url.appendingPathComponent("/" + "\(account).db") { guard let documents = documentsPath else { return nil }
let filePath = pathComponent.path return documents + "/" + "\(accountId)" + "/"
}
private func accountDbPath(accountId: String) -> String? {
guard let accountFolder = accountFolderPath(accountId: accountId) else { return nil }
return accountFolder + "\(accountId).db"
}
func contactsPath(accountId: String, createIfNotExists: Bool) -> String? {
guard let accountFolder = accountFolderPath(accountId: accountId) else { return nil }
let profilesFolder = accountFolder + "profiles/"
let fileManager = FileManager.default let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) { if fileManager.fileExists(atPath: profilesFolder) { return profilesFolder }
if !createIfNotExists { return nil }
do {
try fileManager.createDirectory(atPath: profilesFolder,
withIntermediateDirectories: true,
attributes: nil)
} catch {
return nil
}
return fileManager.fileExists(atPath: profilesFolder) ? profilesFolder : nil
}
private func isDbExists(accountId: String) -> Bool {
guard let path = accountDbPath(accountId: accountId) else { return false }
return isFileExists(path: path)
}
private func isFileExists(path: String) -> Bool {
if path.isEmpty {
return false return false
} else { }
let fileManager = FileManager.default
return fileManager.fileExists(atPath: path)
}
func contactProfilePath(accountId: String, profileURI: String, createifNotExists: Bool) -> String? {
guard let profilesFolder = contactsPath(accountId: accountId,
createIfNotExists: createifNotExists) else { return nil }
return profilesFolder + "\(profileURI.toBase64()).vcf"
}
func accountProfilePath(accountId: String) -> String? {
guard let accountFolder = accountFolderPath(accountId: accountId) else { return nil }
return accountFolder + "profile.vcf"
}
func isAccountProfileExists(accountId: String) -> Bool {
guard let path = accountProfilePath(accountId: accountId) else { return false }
return isFileExists(path: path)
}
func isContactProfileExists(accountId: String, profileURI: String) -> Bool {
guard let path = contactProfilePath(accountId: accountId, profileURI: profileURI, createifNotExists: false) else { return false }
return isFileExists(path: path)
}
func isMigrationToDBv2Needed(for accountId: String) -> Bool {
if !isDbExists(accountId: accountId) { return true }
guard let dbase = self.forAccount(account: accountId) else {
return true return true
} }
} else { let table = Table("profiles")
do {
try _ = dbase.scalar(table.exists)
return true return true
} catch {
return false
} }
} }
private func removeDBNamed(dbName: String) { private func removeDBNamed(dbName: String) {
guard let dbPath = path else { return } guard let dbPath = documentsPath else { return }
let url = NSURL(fileURLWithPath: dbPath) let url = NSURL(fileURLWithPath: dbPath)
guard let pathComponent = url guard let pathComponent = url
.appendingPathComponent("/" + dbName) else { .appendingPathComponent("/" + dbName) else {
...@@ -116,6 +181,50 @@ final class DBContainer { ...@@ -116,6 +181,50 @@ final class DBContainer {
print("Error on delete old database!!!") print("Error on delete old database!!!")
} }
} }
func createAccountfolder(for accountId: String) {
guard let accountFolder = accountFolderPath(accountId: accountId) else { return }
let fileManager = FileManager.default
if fileManager.fileExists(atPath: accountFolder) { return }
do {
try fileManager.createDirectory(atPath: accountFolder,
withIntermediateDirectories: true,
attributes: nil)
} catch {
return
}
}
func copyDbToAccountFolder(for accountId: String) -> Bool {
if isDbExists(accountId: accountId) { return true }
guard let dbPath = documentsPath else { return false }
let url = NSURL(fileURLWithPath: dbPath)
guard let oldPath = url.appendingPathComponent("/" + "\(accountId).db") else { return false }
guard let newPath = accountDbPath(accountId: accountId) else { return false }
let fileManager = FileManager.default
do {
try fileManager.copyItem(atPath: oldPath.path, toPath: newPath)
return fileManager.fileExists(atPath: newPath)
} catch _ as NSError {
return false
}
}
func removeContacts(accountId: String) {
guard let contacts = self.contactsPath(accountId: accountId, createIfNotExists: false) else { return }
let fileManager = FileManager.default
do {
try fileManager.removeItem(atPath: contacts)
} catch _ as NSError {}
}
func removeAccountFolder(accountId: String) {
guard let account = self.accountFolderPath(accountId: accountId) else { return }
let fileManager = FileManager.default
do {
try fileManager.removeItem(atPath: account)
} catch _ as NSError {}
}
} }
extension Connection { extension Connection {
......
...@@ -37,132 +37,33 @@ final class ProfileDataHelper { ...@@ -37,132 +37,33 @@ final class ProfileDataHelper {
let type = Expression<String>("type") let type = Expression<String>("type")
private let log = SwiftyBeaver.self private let log = SwiftyBeaver.self
//migrate from legacy db func dropAccountTable(accountDb: Connection) {
let id = Expression<Int64>("id")
func getLegacyProfileID(profileURI: String, dataBase: Connection) throws -> Int64? {
let query = contactsProfileTable.filter(uri == profileURI)
let items = try dataBase.prepare(query)
for item in items {
return item[id]
}
return nil
}
func getLegacyProfiles(accountURI: String,
accountId: String,
database: Connection) throws -> [Int64: String] {
let query = contactsProfileTable.filter(accountId != uri && accountURI != uri)
let items = try database.prepare(query)
var profiles = [Int64: String]()
for item in items {
profiles[item[id]] = item[uri]
}
return profiles
}
func migrateToDBForAccount (from oldDB: Connection,
to newDB: Connection,
jamiId: String,
accountId: String) throws {
// migrate account profile
// get account profile, it should be only one
let accountQuery = contactsProfileTable.filter(uri == jamiId)
let items = try oldDB.prepare(accountQuery)
for item in items {
let query = accountProfileTable.insert(alias <- item[alias],
photo <- item[photo])
try newDB.run(query)
}
//migrate contacts rofiles
let contactQuery = contactsProfileTable.filter((uri != jamiId) && (uri != accountId))
let rows = try oldDB.prepare(contactQuery)
for row in rows {
let query = contactsProfileTable.insert(uri <- "ring:" + row[uri],
alias <- row[alias],
photo <- row[photo],
type <- row[type])
try newDB.run(query)
}
}
func createAccountTable(accountDb: Connection) {
do { do {
try accountDb.run(accountProfileTable.create(ifNotExists: true) { table in try accountDb.run(accountProfileTable.drop(ifExists: true))
table.column(alias) } catch {
table.column(photo) debugPrint(error)
})
} catch _ {
print("Table already exists")
} }
} }
func updateAccountProfile(accountAlias: String?, accountPhoto: String?, dataBase: Connection) -> Bool { func dropProfileTable(accountDb: Connection) {
do { do {
if try dataBase.pluck(accountProfileTable) != nil { try accountDb.run(contactsProfileTable.drop(ifExists: true))
try dataBase.run(accountProfileTable.update(alias <- accountAlias,
photo <- accountPhoto))
} else {
try dataBase.run(accountProfileTable.insert(alias <- accountAlias,
photo <- accountPhoto))
}
return true
} catch { } catch {
return false debugPrint(error)
} }
} }
func getAccountProfile(dataBase: Connection) -> AccountProfile? { func getAccountProfile(dataBase: Connection) -> Profile? {
do { do {
guard let row = try dataBase.pluck(accountProfileTable) else { return nil} guard let row = try dataBase.pluck(accountProfileTable) else { return nil}
return (row[alias], row[photo]) // account profile saved in db does not have uri and type,
// return default values that need to be updated by function caller
return Profile("", row[alias], row[photo], ProfileType.ring.rawValue)
} catch { } catch {
return nil return nil
} }
} }
func createContactsTable(accountDb: Connection) {
do {
try accountDb.run(contactsProfileTable.create(ifNotExists: true) { table in
table.column(uri, unique: true)
table.column(alias)
table.column(photo)
table.column(type)
})
try accountDb.run(contactsProfileTable.createIndex(uri))
} catch _ {
print("Table already exists")
}
}
func insert(item: Profile, dataBase: Connection) -> Bool {
let query = contactsProfileTable.insert(uri <- item.uri,
alias <- item.alias,
photo <- item.photo,
type <- item.type)
do {
let rowId = try dataBase.run(query)
guard rowId > 0 else {
return false
}
return true
} catch _ {
return false
}
}
func delete(item: Profile, dataBase: Connection) -> Bool {
let profileUri = item.uri
let query = contactsProfileTable.filter(uri == profileUri)
do {
let deletedRows = try dataBase.run(query.delete())
guard deletedRows == 1 else {
return false
}
return true
} catch _ {
return false
}
}
func selectAll(dataBase: Connection) throws -> [Profile]? { func selectAll(dataBase: Connection) throws -> [Profile]? {
var profiles = [Profile]() var profiles = [Profile]()
let items = try dataBase.prepare(contactsProfileTable) let items = try dataBase.prepare(contactsProfileTable)
...@@ -172,46 +73,4 @@ final class ProfileDataHelper { ...@@ -172,46 +73,4 @@ final class ProfileDataHelper {
} }
return profiles return profiles
} }
func selectProfile(profileURI: String, dataBase: Connection) throws -> Profile? {
let query = contactsProfileTable.filter(uri == profileURI)
let items = try dataBase.prepare(query)
// for one URI we should have only one profile
for item in items {
return Profile(uri: item[uri], alias: item[alias],
photo: item[photo], type: item[type])
}
return nil
}
func insertOrUpdateProfile(item: Profile, dataBase: Connection) throws {
try dataBase.transaction {
let selectQuery = contactsProfileTable.filter(uri == item.uri)
let rows = try dataBase.run(selectQuery.update(alias <- item.alias,
photo <- item.photo))
if rows > 0 {
return
}
let insertQuery = contactsProfileTable.insert(uri <- item.uri,
alias <- item.alias,
photo <- item.photo,
type <- item.type)
let rowId = try dataBase.run(insertQuery)
guard rowId > 0 else {
throw DataAccessError.databaseError
}
}
}
func deleteAll(dataBase: Connection) -> Bool {
do {
if try dataBase.run(contactsProfileTable.delete()) > 0 {
return true
} else {
return false
}
} catch {
return false
}
}
} }
This diff is collapsed.
...@@ -57,7 +57,7 @@ class EditProfileViewModel { ...@@ -57,7 +57,7 @@ class EditProfileViewModel {
}) })
}() }()
var profileForCurrentAccount = PublishSubject<AccountProfile>() var profileForCurrentAccount = PublishSubject<Profile>()
lazy var profileName: Observable<String?> = { [unowned self] in lazy var profileName: Observable<String?> = { [unowned self] in
return profileForCurrentAccount.share() return profileForCurrentAccount.share()
...@@ -107,9 +107,11 @@ class EditProfileViewModel { ...@@ -107,9 +107,11 @@ class EditProfileViewModel {
details.set(withConfigKeyModel: ConfigKeyModel(withKey: ConfigKey.displayName), withValue: self.name) details.set(withConfigKeyModel: ConfigKeyModel(withKey: ConfigKey.displayName), withValue: self.name)
account.details = details account.details = details
self.accountService.setAccountDetails(forAccountId: account.id, withDetails: details) self.accountService.setAccountDetails(forAccountId: account.id, withDetails: details)
let accountUri = AccountModelHelper.init(withAccount: account).uri ?? ""
self.profileService.updateAccountProfile(accountId: account.id, self.profileService.updateAccountProfile(accountId: account.id,
alias: self.name, alias: self.name,
photo: photo) photo: photo, accountURI: accountUri)
} }
func updateImage(_ image: UIImage) { func updateImage(_ image: UIImage) {
......
...@@ -30,8 +30,8 @@ enum VCardFields: String { ...@@ -30,8 +30,8 @@ enum VCardFields: String {
case photoJPEG = "PHOTO;ENCODING=BASE64;TYPE=JPEG:" case photoJPEG = "PHOTO;ENCODING=BASE64;TYPE=JPEG:"
case photoPNG = "PHOTO;ENCODING=BASE64;TYPE=PNG:" case photoPNG = "PHOTO;ENCODING=BASE64;TYPE=PNG:"
case end = "END:VCARD" case end = "END:VCARD"
case name = "N:"
case fullName = "FN:" case fullName = "FN:"
case telephone = "TEL;other:"
} }
extension CNContactVCardSerialization { extension CNContactVCardSerialization {
...@@ -42,11 +42,12 @@ extension CNContactVCardSerialization { ...@@ -42,11 +42,12 @@ extension CNContactVCardSerialization {
let beginString = VCardFields.begin.rawValue + "\n" let beginString = VCardFields.begin.rawValue + "\n"
let entryUIDString = VCardFields.uid.rawValue + contact.identifier + "\n" let entryUIDString = VCardFields.uid.rawValue + contact.identifier + "\n"
let name = contact.familyName.trimmingCharacters(in: .whitespacesAndNewlines) let name = contact.familyName.trimmingCharacters(in: .whitespacesAndNewlines)
let firstnameString = VCardFields.name.rawValue + name + "\n" let phone = contact.phoneNumbers.isEmpty ? "" : contact.phoneNumbers[0].value.stringValue
let telephoneString = VCardFields.telephone.rawValue + phone + "\n"
let fullNameString = VCardFields.fullName.rawValue + name + "\n" let fullNameString = VCardFields.fullName.rawValue + name + "\n"
let endString = VCardFields.end.rawValue let endString = VCardFields.end.rawValue
var vCardString = beginString + entryUIDString + firstnameString + fullNameString + endString var vCardString = beginString + entryUIDString + fullNameString + telephoneString + endString
// if contact have an image add it to vCard data // if contact have an image add it to vCard data
guard var image = contact.imageData else { guard var image = contact.imageData else {
...@@ -96,6 +97,7 @@ extension CNContactVCardSerialization { ...@@ -96,6 +97,7 @@ extension CNContactVCardSerialization {
let vcard = CNMutableContact() let vcard = CNMutableContact()
let name = String(nameRow.suffix(nameRow.count - 3)) let name = String(nameRow.suffix(nameRow.count - 3))
vcard.familyName = name vcard.familyName = name
vcard.phoneNumbers = vCard.phoneNumbers
vcard.imageData = vCard.imageData vcard.imageData = vCard.imageData
return vcard return vcard
} }
......
...@@ -131,4 +131,8 @@ extension String { ...@@ -131,4 +131,8 @@ extension String {
} }
return fileIsImage return fileIsImage
} }
func toBase64() -> String {
return Data(self.utf8).base64EncodedString()
}
} }
...@@ -55,7 +55,7 @@ class ContactRequestItem { ...@@ -55,7 +55,7 @@ class ContactRequestItem {
createIfNotexists: false, createIfNotexists: false,
accountId: contactRequest.accountId) accountId: contactRequest.accountId)
.subscribe(onNext: { [weak self] profile in .subscribe(onNext: { [weak self] profile in
if let photo = profile.photo, if let photo = profile.photo, !photo.isEmpty,
let data = NSData(base64Encoded: photo, let data = NSData(base64Encoded: photo,
options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) as Data? { options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) as Data? {
self?.profileImageData.value = data self?.profileImageData.value = data
......
...@@ -24,7 +24,7 @@ import RxCocoa ...@@ -24,7 +24,7 @@ import RxCocoa
struct AccountItem { struct AccountItem {
let account: AccountModel let account: AccountModel
let profileObservable: Observable<AccountProfile> let profileObservable: Observable<Profile>
} }
final class AccountPickerAdapter: NSObject, UIPickerViewDataSource, UIPickerViewDelegate, RxPickerViewDataSourceType, SectionedViewDataSourceType { final class AccountPickerAdapter: NSObject, UIPickerViewDataSource, UIPickerViewDelegate, RxPickerViewDataSourceType, SectionedViewDataSourceType {
......
...@@ -90,7 +90,7 @@ class SmartlistViewModel: Stateable, ViewModel, FilterConversationDataSource { ...@@ -90,7 +90,7 @@ class SmartlistViewModel: Stateable, ViewModel, FilterConversationDataSource {
} }
let injectionBag: InjectionBag let injectionBag: InjectionBag
//Values need to be updated when selected account changed //Values need to be updated when selected account changed
var profileImageForCurrentAccount = PublishSubject<AccountProfile>() var profileImageForCurrentAccount = PublishSubject<Profile>()
lazy var profileImage: Observable<UIImage> = { [unowned self] in lazy var profileImage: Observable<UIImage> = { [unowned self] in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01, execute: { DispatchQueue.main.asyncAfter(deadline: .now() + 0.01, execute: {
......
...@@ -202,14 +202,12 @@ class AccountsService: AccountAdapterDelegate { ...@@ -202,14 +202,12 @@ class AccountsService: AccountAdapterDelegate {
fileprivate func loadDatabases() -> Bool { fileprivate func loadDatabases() -> Bool {
for account in accountList { for account in accountList {
if dbManager.isNeedMigrationToAccountDB(accountId: account.id) { if dbManager.isMigrationToDBv2Needed(accountId: account.id) {
do { if let accountURI = AccountModelHelper
if let jamiId = AccountModelHelper .init(withAccount: account).uri {
.init(withAccount: account).ringId { if !dbManager.migrateToDbVersion2(accountId: account.id,
try dbManager.migrateToAccountDB(accountId: account.id, accountURI: accountURI) { return false }
jamiId: jamiId)
} }
} catch { return false}
} else { } else {
do { do {
// return false if could not open database connection // return false if could not open database connection
...@@ -300,7 +298,7 @@ class AccountsService: AccountAdapterDelegate { ...@@ -300,7 +298,7 @@ class AccountsService: AccountAdapterDelegate {
return true return true
} }
func getAccountProfile(accountId: String) -> AccountProfile? { func getAccountProfile(accountId: String) -> Profile? {
return self.dbManager.accountProfile(for: accountId) return self.dbManager.accountProfile(for: accountId)
} }
...@@ -361,7 +359,9 @@ class AccountsService: AccountAdapterDelegate { ...@@ -361,7 +359,9 @@ class AccountsService: AccountAdapterDelegate {
if try !self.dbManager.createDatabaseForAccount(accountId: accountModel.id) { if try !self.dbManager.createDatabaseForAccount(accountId: accountModel.id) {
throw AddAccountError.unknownError throw AddAccountError.unknownError
} }
_ = self.dbManager.saveAccountProfile(alias: nil, photo: nil, accountId: accountModel.id) let uri = JamiURI(schema: URIType.ring, infoHach: accountModel.jamiId)
let uriString = uri.uriString ?? ""
_ = self.dbManager.saveAccountProfile(alias: nil, photo: nil, accountId: accountModel.id, accountURI: uriString)
self.loadAccountsFromDaemon() self.loadAccountsFromDaemon()
return accountModel return accountModel
}.take(1) }.take(1)
...@@ -385,12 +385,13 @@ class AccountsService: AccountAdapterDelegate { ...@@ -385,12 +385,13 @@ class AccountsService: AccountAdapterDelegate {
accountDetails.updateValue(password, forKey: ConfigKey.localPort.rawValue) accountDetails.updateValue(password, forKey: ConfigKey.localPort.rawValue)
} }
guard let account = self.accountAdapter.addAccount(accountDetails) else {return false} guard let account = self.accountAdapter.addAccount(accountDetails) else {return false}
_ = try self.dbManager.createDatabaseForAccount(accountId: account) _ = try self.dbManager.createDatabaseForAccount(accountId: account, createFolder: true)
_ = self.dbManager.saveAccountProfile(alias: nil, photo: nil, accountId: account)
self.loadAccountsFromDaemon() self.loadAccountsFromDaemon()
let newAccount = self.getAccount(fromAccountId: account) guard let newAccount = self.getAccount(fromAccountId: account) else { return false }
self.currentAccount = newAccount self.currentAccount = newAccount
UserDefaults.standard.set(account, forKey: self.selectedAccountID) UserDefaults.standard.set(account, forKey: self.selectedAccountID)
let accountUri = AccountModelHelper.init(withAccount: newAccount).uri ?? ""
_ = self.dbManager.saveAccountProfile(alias: nil, photo: nil, accountId: account, accountURI: accountUri)
return true return true
} catch { } catch {
return false return false
...@@ -440,7 +441,9 @@ class AccountsService: AccountAdapterDelegate { ...@@ -440,7 +441,9 @@ class AccountsService: AccountAdapterDelegate {
if try !self.dbManager.createDatabaseForAccount(accountId: accountModel.id) { if try !self.dbManager.createDatabaseForAccount(accountId: accountModel.id) {
throw AddAccountError.unknownError throw AddAccountError.unknownError
} }
_ = self.dbManager.saveAccountProfile(alias: nil, photo: nil, accountId: accountModel.id) let uri = JamiURI(schema: URIType.ring, infoHach: accountModel.jamiId)
let uriString = uri.uriString ?? ""
_ = self.dbManager.saveAccountProfile(alias: nil, photo: nil, accountId: accountModel.id, accountURI: uriString)
self.loadAccountsFromDaemon() self.loadAccountsFromDaemon()
return accountModel return accountModel
}.take(1) }.take(1)
...@@ -781,11 +784,12 @@ class AccountsService: AccountAdapterDelegate { ...@@ -781,11 +784,12 @@ class AccountsService: AccountAdapterDelegate {
} }
func removeAccount(id: String) { func removeAccount(id: String) {
if self.getAccount(fromAccountId: id) == nil {return} guard let account = self.getAccount(fromAccountId: id) else {return}
let shouldRemoveFolder = AccountModelHelper.init(withAccount: account).isAccountSip()
self.accountAdapter.removeAccount(id) self.accountAdapter.removeAccount(id)
self.loadAccountsFromDaemon() self.loadAccountsFromDaemon()
if self.getAccount(fromAccountId: id) == nil { if self.getAccount(fromAccountId: id) == nil {
self.dbManager.removeDBForAccount(accountId: id) self.dbManager.removeDBForAccount(accountId: id, removeFolder: shouldRemoveFolder)
guard let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { guard let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return return
} }
...@@ -875,7 +879,11 @@ class AccountsService: AccountAdapterDelegate { ...@@ -875,7 +879,11 @@ class AccountsService: AccountAdapterDelegate {
} }
let accountDetails = getAccountDetails(fromAccountId: account) let accountDetails = getAccountDetails(fromAccountId: account)
let displayName: String? = accountDetails.get(withConfigKeyModel: ConfigKeyModel(withKey: ConfigKey.displayName)) let displayName: String? = accountDetails.get(withConfigKeyModel: ConfigKeyModel(withKey: ConfigKey.displayName))
_ = self.dbManager.saveAccountProfile(alias: displayName, photo: photo, accountId: account)
guard let accountToUpdate = self.getAccount(fromAccountId: account),
let accountURI = AccountModelHelper
.init(withAccount: accountToUpdate).uri else {return}
_ = self.dbManager.saveAccountProfile(alias: displayName, photo: photo, accountId: account, accountURI: accountURI)
} }
// MARK: Push Notifications // MARK: Push Notifications
......
...@@ -328,19 +328,7 @@ class CallsService: CallsAdapterDelegate { ...@@ -328,19 +328,7 @@ class CallsService: CallsAdapterDelegate {
if accountID.isEmpty || callID.isEmpty { if accountID.isEmpty || callID.isEmpty {
return return
} }
guard let accountProfile = self.dbManager.accountProfile(for: accountID) else {return} guard let vCard = self.dbManager.accountVCard(for: accountID) else {return}
let vCard = CNMutableContact()
var cardChanged = false
if let name = accountProfile.alias {
vCard.familyName = name
cardChanged = true
}
if let photo = accountProfile.photo {
vCard.imageData = NSData(base64Encoded: photo,
options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) as Data?
cardChanged = true
}
if cardChanged {
DispatchQueue.main.async { [unowned self] in DispatchQueue.main.async { [unowned self] in
VCardUtils.sendVCard(card: vCard, VCardUtils.sendVCard(card: vCard,
callID: callID, callID: callID,
...@@ -348,7 +336,6 @@ class CallsService: CallsAdapterDelegate { ...@@ -348,7 +336,6 @@ class CallsService: CallsAdapterDelegate {
sender: self) sender: self)
} }
} }
}
func sendTextMessage(callID: String, message: String, accountId: AccountModel) { func sendTextMessage(callID: String, message: String, accountId: AccountModel) {
guard let call = self.call(callID: callID) else {return} guard let call = self.call(callID: callID) else {return}
......
...@@ -85,7 +85,7 @@ class ContactsService { ...@@ -85,7 +85,7 @@ class ContactsService {
func loadSipContacts(withAccount account: AccountModel) { func loadSipContacts(withAccount account: AccountModel) {
guard let profiles = self.dbManager guard let profiles = self.dbManager
.getProfilesForAccount(accountID: account.id) else {return} .getProfilesForAccount(accountId: account.id) else {return}
let contacts = profiles.map({ profile in let contacts = profiles.map({ profile in
return ContactModel(withUri: JamiURI.init(schema: URIType.sip, infoHach: profile.uri)) return ContactModel(withUri: JamiURI.init(schema: URIType.sip, infoHach: profile.uri))
}) })
...@@ -151,8 +151,10 @@ class ContactsService { ...@@ -151,8 +151,10 @@ class ContactsService {
stringImage = image.base64EncodedString() stringImage = image.base64EncodedString()
} }
let name = VCardUtils.getName(from: contactRequest.vCard) let name = VCardUtils.getName(from: contactRequest.vCard)
let uri = JamiURI(schema: URIType.ring, infoHach: contactRequest.ringId)
let uriString = uri.uriString ?? contactRequest.ringId
_ = self.dbManager _ = self.dbManager
.createOrUpdateRingProfile(profileUri: contactRequest.ringId, .createOrUpdateRingProfile(profileUri: uriString,
alias: name, alias: name,
image: stringImage, image: stringImage,
accountId: account.id) accountId: account.id)
...@@ -375,7 +377,7 @@ extension ContactsService: ContactsAdapterDelegate { ...@@ -375,7 +377,7 @@ extension ContactsService: ContactsAdapterDelegate {
func getProfile(uri: String, accountId: String) -> Profile? { func getProfile(uri: String, accountId: String) -> Profile? {
do { do {
return try self.dbManager.getProfile(for: uri, createIfNotExists: false, accounId: accountId) return try self.dbManager.getProfile(for: uri, createIfNotExists: false, accountId: accountId)
} catch { } catch {
return nil return nil
} }
......
...@@ -46,7 +46,7 @@ class ProfilesService { ...@@ -46,7 +46,7 @@ class ProfilesService {
fileprivate let log = SwiftyBeaver.self fileprivate let log = SwiftyBeaver.self
var profiles = [String: ReplaySubject<Profile>]() var profiles = [String: ReplaySubject<Profile>]()
var accountProfiles = [String: ReplaySubject<AccountProfile>]() var accountProfiles = [String: ReplaySubject<Profile>]()
let dbManager: DBManager let dbManager: DBManager
...@@ -69,7 +69,10 @@ class ProfilesService { ...@@ -69,7 +69,10 @@ class ProfilesService {
guard let accountId = notification.userInfo?[ProfileNotificationsKeys.accountId.rawValue] as? String else { guard let accountId = notification.userInfo?[ProfileNotificationsKeys.accountId.rawValue] as? String else {
return return
} }
self.triggerProfileSignal(uri: ringId, createIfNotexists: false, accountId: accountId)
let uri = JamiURI(schema: URIType.ring, infoHach: ringId)
let uriString = uri.uriString ?? ringId
self.triggerProfileSignal(uri: uriString, createIfNotexists: false, accountId: accountId)
} }
// swiftlint:disable cyclomatic_complexity // swiftlint:disable cyclomatic_complexity
...@@ -197,13 +200,12 @@ class ProfilesService { ...@@ -197,13 +200,12 @@ class ProfilesService {
} }
// MARK: account profile // MARK: account profile
typealias AccountProfile = (alias: String?, photo: String?)
extension ProfilesService { extension ProfilesService {
func getAccountProfile(accountId: String) -> Observable<AccountProfile> { func getAccountProfile(accountId: String) -> Observable<Profile> {
if let profile = self.accountProfiles[accountId] { if let profile = self.accountProfiles[accountId] {
return profile.asObservable().share() return profile.asObservable().share()
} }
let profileObservable = ReplaySubject<AccountProfile>.create(bufferSize: 1) let profileObservable = ReplaySubject<Profile>.create(bufferSize: 1)
self.accountProfiles[accountId] = profileObservable self.accountProfiles[accountId] = profileObservable
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
self?.triggerAccountProfileSignal(accountId: accountId) self?.triggerAccountProfileSignal(accountId: accountId)
...@@ -222,10 +224,10 @@ extension ProfilesService { ...@@ -222,10 +224,10 @@ extension ProfilesService {
}).disposed(by: self.disposeBag) }).disposed(by: self.disposeBag)
} }
func updateAccountProfile(accountId: String, alias: String?, photo: String?) { func updateAccountProfile(accountId: String, alias: String?, photo: String?, accountURI: String) {
if self.dbManager if self.dbManager
.saveAccountProfile(alias: alias, photo: photo, .saveAccountProfile(alias: alias, photo: photo,
accountId: accountId) { accountId: accountId, accountURI: accountURI) {
self.triggerAccountProfileSignal(accountId: accountId) self.triggerAccountProfileSignal(accountId: accountId)
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment