Commit 5e8d8b81 authored by Kateryna Kostiuk's avatar Kateryna Kostiuk

database: migrate to database for account

New database is created per account. It contains:
- profiles table
- conversations table
- interaction table
- account profile. This table has only one row and store account
avatar and alias

Database file named by account id.

Database content also was changed:
- contact uri stored in canonical format, so it is unique for SIP
contacts
- generated messages stored in a format that allows localizations.

Change-Id: Id7803ab797ac937d63155870fc5c8edf94119bbd
parent 0ea39d4d
...@@ -85,7 +85,7 @@ ...@@ -85,7 +85,7 @@
04399B151D1C341A00E99CD9 /* libyaml-cpp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 04399AE31D1C341A00E99CD9 /* libyaml-cpp.a */; }; 04399B151D1C341A00E99CD9 /* libyaml-cpp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 04399AE31D1C341A00E99CD9 /* libyaml-cpp.a */; };
0586C94B1F684DF600613517 /* UIImage+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0586C94A1F684DF600613517 /* UIImage+Helpers.swift */; }; 0586C94B1F684DF600613517 /* UIImage+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0586C94A1F684DF600613517 /* UIImage+Helpers.swift */; };
0E0FF1A71FC38070003898C2 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E0FF1A61FC38070003898C2 /* SQLite.framework */; }; 0E0FF1A71FC38070003898C2 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E0FF1A61FC38070003898C2 /* SQLite.framework */; };
0E0FF1AA1FC3843E003898C2 /* RingDB.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0FF1A91FC3843E003898C2 /* RingDB.swift */; }; 0E0FF1AA1FC3843E003898C2 /* DBContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0FF1A91FC3843E003898C2 /* DBContainer.swift */; };
0E0FF1AF1FC38CBC003898C2 /* ProfileDataHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0FF1AE1FC38CBC003898C2 /* ProfileDataHelper.swift */; }; 0E0FF1AF1FC38CBC003898C2 /* ProfileDataHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0FF1AE1FC38CBC003898C2 /* ProfileDataHelper.swift */; };
0E0FF1B51FC3947B003898C2 /* DBManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0FF1B41FC3947B003898C2 /* DBManager.swift */; }; 0E0FF1B51FC3947B003898C2 /* DBManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0FF1B41FC3947B003898C2 /* DBManager.swift */; };
0E0FF1B71FC398B3003898C2 /* ConversationDataHepler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0FF1B61FC398B3003898C2 /* ConversationDataHepler.swift */; }; 0E0FF1B71FC398B3003898C2 /* ConversationDataHepler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0FF1B61FC398B3003898C2 /* ConversationDataHepler.swift */; };
...@@ -394,7 +394,7 @@ ...@@ -394,7 +394,7 @@
04399AE31D1C341A00E99CD9 /* libyaml-cpp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libyaml-cpp.a"; path = "../fat/lib/libyaml-cpp.a"; sourceTree = "<group>"; }; 04399AE31D1C341A00E99CD9 /* libyaml-cpp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libyaml-cpp.a"; path = "../fat/lib/libyaml-cpp.a"; sourceTree = "<group>"; };
0586C94A1F684DF600613517 /* UIImage+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Helpers.swift"; sourceTree = "<group>"; }; 0586C94A1F684DF600613517 /* UIImage+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Helpers.swift"; sourceTree = "<group>"; };
0E0FF1A61FC38070003898C2 /* SQLite.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SQLite.framework; path = Carthage/Build/iOS/SQLite.framework; sourceTree = "<group>"; }; 0E0FF1A61FC38070003898C2 /* SQLite.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SQLite.framework; path = Carthage/Build/iOS/SQLite.framework; sourceTree = "<group>"; };
0E0FF1A91FC3843E003898C2 /* RingDB.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RingDB.swift; sourceTree = "<group>"; }; 0E0FF1A91FC3843E003898C2 /* DBContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBContainer.swift; sourceTree = "<group>"; };
0E0FF1AE1FC38CBC003898C2 /* ProfileDataHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileDataHelper.swift; sourceTree = "<group>"; }; 0E0FF1AE1FC38CBC003898C2 /* ProfileDataHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileDataHelper.swift; sourceTree = "<group>"; };
0E0FF1B41FC3947B003898C2 /* DBManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBManager.swift; sourceTree = "<group>"; }; 0E0FF1B41FC3947B003898C2 /* DBManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBManager.swift; sourceTree = "<group>"; };
0E0FF1B61FC398B3003898C2 /* ConversationDataHepler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationDataHepler.swift; sourceTree = "<group>"; }; 0E0FF1B61FC398B3003898C2 /* ConversationDataHepler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationDataHepler.swift; sourceTree = "<group>"; };
...@@ -1049,7 +1049,7 @@ ...@@ -1049,7 +1049,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
0E0FF1AB1FC38BD1003898C2 /* DBHelpers */, 0E0FF1AB1FC38BD1003898C2 /* DBHelpers */,
0E0FF1A91FC3843E003898C2 /* RingDB.swift */, 0E0FF1A91FC3843E003898C2 /* DBContainer.swift */,
0E0FF1B41FC3947B003898C2 /* DBManager.swift */, 0E0FF1B41FC3947B003898C2 /* DBManager.swift */,
); );
path = Database; path = Database;
...@@ -1901,7 +1901,7 @@ ...@@ -1901,7 +1901,7 @@
1A2D18E51F29197100B2C785 /* MessageAccessoryView.swift in Sources */, 1A2D18E51F29197100B2C785 /* MessageAccessoryView.swift in Sources */,
0E0FF1B91FC398C5003898C2 /* InteractionDataHelper.swift in Sources */, 0E0FF1B91FC398C5003898C2 /* InteractionDataHelper.swift in Sources */,
0EE1B54E1F75ACDE00BA98EE /* CNContactVCardSerialization+Helpers.swift in Sources */, 0EE1B54E1F75ACDE00BA98EE /* CNContactVCardSerialization+Helpers.swift in Sources */,
0E0FF1AA1FC3843E003898C2 /* RingDB.swift in Sources */, 0E0FF1AA1FC3843E003898C2 /* DBContainer.swift in Sources */,
56308BA71EA00E5700660275 /* NameRegistrationResponse.m in Sources */, 56308BA71EA00E5700660275 /* NameRegistrationResponse.m in Sources */,
1A3CA32D1F13DA7200283748 /* Chameleon+Ring.swift in Sources */, 1A3CA32D1F13DA7200283748 /* Chameleon+Ring.swift in Sources */,
0ED2B6FC1F96A158001572F0 /* LinkNewDeviceViewController.swift in Sources */, 0ED2B6FC1F96A158001572F0 /* LinkNewDeviceViewController.swift in Sources */,
......
...@@ -33,20 +33,37 @@ import PushKit ...@@ -33,20 +33,37 @@ import PushKit
@UIApplicationMain @UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow? var window: UIWindow?
let dBManager = DBManager(profileHepler: ProfileDataHelper(),
conversationHelper: ConversationDataHelper(),
interactionHepler: InteractionDataHelper(),
dbConnections: DBContainer())
private let daemonService = DaemonService(dRingAdaptor: DRingAdapter()) private let daemonService = DaemonService(dRingAdaptor: DRingAdapter())
private let accountService = AccountsService(withAccountAdapter: AccountAdapter())
private let nameService = NameService(withNameRegistrationAdapter: NameRegistrationAdapter()) private let nameService = NameService(withNameRegistrationAdapter: NameRegistrationAdapter())
private let conversationsService = ConversationsService(withMessageAdapter: MessagesAdapter())
private let contactsService = ContactsService(withContactsAdapter: ContactsAdapter())
private let presenceService = PresenceService(withPresenceAdapter: PresenceAdapter()) private let presenceService = PresenceService(withPresenceAdapter: PresenceAdapter())
private let callService = CallsService(withCallsAdapter: CallsAdapter())
private let videoService = VideoService(withVideoAdapter: VideoAdapter()) private let videoService = VideoService(withVideoAdapter: VideoAdapter())
private let audioService = AudioService(withAudioAdapter: AudioAdapter()) private let audioService = AudioService(withAudioAdapter: AudioAdapter())
private let dataTransferService = DataTransferService(withDataTransferAdapter: DataTransferAdapter())
private let networkService = NetworkService() private let networkService = NetworkService()
private let profileService = ProfilesService()
private var conversationManager: ConversationsManager? private var conversationManager: ConversationsManager?
private var interactionsManager: GeneratedInteractionsManager? private var interactionsManager: GeneratedInteractionsManager?
private lazy var callService: CallsService = {
CallsService(withCallsAdapter: CallsAdapter(), dbManager: self.dBManager)
}()
private lazy var accountService: AccountsService = {
AccountsService(withAccountAdapter: AccountAdapter(), dbManager: self.dBManager)
}()
private lazy var contactsService: ContactsService = {
ContactsService(withContactsAdapter: ContactsAdapter(), dbManager: self.dBManager)
}()
private lazy var profileService: ProfilesService = {
ProfilesService(dbManager: self.dBManager)
}()
private lazy var dataTransferService: DataTransferService = {
DataTransferService(withDataTransferAdapter: DataTransferAdapter(),
dbManager: self.dBManager)
}()
private lazy var conversationsService: ConversationsService = {
ConversationsService(withMessageAdapter: MessagesAdapter(), dbManager: self.dBManager)
}()
private let voipRegistry = PKPushRegistry(queue: DispatchQueue.main) private let voipRegistry = PKPushRegistry(queue: DispatchQueue.main)
...@@ -72,6 +89,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD ...@@ -72,6 +89,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
fileprivate let disposeBag = DisposeBag() fileprivate let disposeBag = DisposeBag()
// swiftlint:disable function_body_length
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// ignore sigpipe // ignore sigpipe
...@@ -131,7 +149,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD ...@@ -131,7 +149,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
nameService: self.nameService, nameService: self.nameService,
dataTransferService: self.dataTransferService, dataTransferService: self.dataTransferService,
callService: self.callService) callService: self.callService)
self.startDB()
self.accountService self.accountService
.sharedResponseStream .sharedResponseStream
...@@ -163,23 +180,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD ...@@ -163,23 +180,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}) })
.disposed(by: disposeBag) .disposed(by: disposeBag)
self.accountService.loadAccounts().subscribe { [unowned self] (_) in self.window?.rootViewController = self.appCoordinator.rootViewController
self.window?.makeKeyAndVisible()
self.accountService.initialAccountsLoading().subscribe(onCompleted: {
self.appCoordinator.start()
guard let currentAccount = self.accountService.currentAccount else { guard let currentAccount = self.accountService.currentAccount else {
self.log.error("Can't get current account!") self.log.error("Can't get current account!")
return return
} }
self.contactsService.loadContacts(withAccount: currentAccount) self.contactsService.loadContacts(withAccount: currentAccount)
self.contactsService.loadContactRequests(withAccount: currentAccount) self.contactsService.loadContactRequests(withAccount: currentAccount)
self.presenceService.subscribeBuddies(withAccount: currentAccount, withContacts: self.contactsService.contacts.value) self.presenceService.subscribeBuddies(withAccount: currentAccount,
withContacts: self.contactsService.contacts.value)
if let ringID = AccountModelHelper(withAccount: currentAccount).ringId { if let ringID = AccountModelHelper(withAccount: currentAccount).ringId {
self.conversationManager? self.conversationManager?
.prepareConversationsForAccount(accountId: currentAccount.id, accountUri: ringID) .prepareConversationsForAccount(accountId: currentAccount.id, accountUri: ringID)
} }
}.disposed(by: self.disposeBag) }, onError: { _ in
self.appCoordinator.start()
self.window?.rootViewController = self.appCoordinator.rootViewController let time = DispatchTime.now() + 1
self.window?.makeKeyAndVisible() DispatchQueue.main.asyncAfter(deadline: time) {
self.appCoordinator.start() self.appCoordinator.showDatabaseError()
}
}).disposed(by: self.disposeBag)
self.voipRegistry.delegate = self self.voipRegistry.delegate = self
NotificationCenter.default.addObserver(self, selector: #selector(registerVoipNotifications), NotificationCenter.default.addObserver(self, selector: #selector(registerVoipNotifications),
name: NSNotification.Name(rawValue: NotificationName.enablePushNotifications.rawValue), name: NSNotification.Name(rawValue: NotificationName.enablePushNotifications.rawValue),
...@@ -236,20 +260,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD ...@@ -236,20 +260,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
} }
} }
private func startDB() {
do {
let dbManager = DBManager(profileHepler: ProfileDataHelper(),
conversationHelper: ConversationDataHelper(),
interactionHepler: InteractionDataHelper())
try dbManager.start()
} catch {
let time = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: time) {
self.appCoordinator.showDatabaseError()
}
}
}
// swiftlint:disable cyclomatic_complexity // swiftlint:disable cyclomatic_complexity
func updateNotificationAvailability() { func updateNotificationAvailability() {
let enabled = LocalNotificationsHelper.isEnabled() let enabled = LocalNotificationsHelper.isEnabled()
......
...@@ -61,11 +61,11 @@ class CallViewModel: Stateable, ViewModel { ...@@ -61,11 +61,11 @@ class CallViewModel: Stateable, ViewModel {
// data for ViewController binding // data for ViewController binding
lazy var contactImageData: Observable<Data?>? = { lazy var contactImageData: Observable<Data?>? = {
guard let call = self.call else { guard let call = self.call, let account = self.accountService.currentAccount else {
return nil return nil
} }
return self.profileService.getProfile(ringId: call.participantRingId, return self.profileService.getProfile(ringId: call.participantRingId,
createIfNotexists: true) createIfNotexists: true, accountId: account.id)
.filter({ profile in .filter({ profile in
guard let photo = profile.photo else { guard let photo = profile.photo else {
return false return false
......
...@@ -246,6 +246,23 @@ internal enum L10n { ...@@ -246,6 +246,23 @@ internal enum L10n {
internal static let readableStatusSuccess = L10n.tr("Localizable", "dataTransfer.readableStatusSuccess") internal static let readableStatusSuccess = L10n.tr("Localizable", "dataTransfer.readableStatusSuccess")
} }
internal enum GeneratedMessage {
/// Contact added
internal static let contactAdded = L10n.tr("Localizable", "generatedMessage.contactAdded")
/// Incoming call
internal static let incomingCall = L10n.tr("Localizable", "generatedMessage.incomingCall")
/// Invitation accepted
internal static let invitationAccepted = L10n.tr("Localizable", "generatedMessage.invitationAccepted")
/// Invitation received
internal static let invitationReceived = L10n.tr("Localizable", "generatedMessage.invitationReceived")
/// Missed incoming call
internal static let missedIncomingCall = L10n.tr("Localizable", "generatedMessage.missedIncomingCall")
/// Missed outgoing call
internal static let missedOutgoingCall = L10n.tr("Localizable", "generatedMessage.missedOutgoingCall")
/// Outgoing call
internal static let outgoingCall = L10n.tr("Localizable", "generatedMessage.outgoingCall")
}
internal enum Global { internal enum Global {
/// Invitations /// Invitations
internal static let contactRequestsTabBarTitle = L10n.tr("Localizable", "global.contactRequestsTabBarTitle") internal static let contactRequestsTabBarTitle = L10n.tr("Localizable", "global.contactRequestsTabBarTitle")
......
...@@ -97,7 +97,8 @@ class ContactViewModel: ViewModel, Stateable { ...@@ -97,7 +97,8 @@ class ContactViewModel: ViewModel, Stateable {
}) })
.disposed(by: self.disposeBag) .disposed(by: self.disposeBag)
self.profileService.getProfile(ringId: conversation.recipientRingId, self.profileService.getProfile(ringId: conversation.recipientRingId,
createIfNotexists: false) createIfNotexists: false,
accountId: conversation.accountId)
.subscribe(onNext: { [unowned self] profile in .subscribe(onNext: { [unowned self] profile in
if let alias = profile.alias, !alias.isEmpty { if let alias = profile.alias, !alias.isEmpty {
self.displayName.value = alias self.displayName.value = alias
......
...@@ -97,21 +97,11 @@ final class AppCoordinator: Coordinator, StateableResponsive { ...@@ -97,21 +97,11 @@ final class AppCoordinator: Coordinator, StateableResponsive {
/// Handles the switch between the three supported screens. /// Handles the switch between the three supported screens.
private func dispatchApplication() { private func dispatchApplication() {
self.injectionBag.accountService if self.injectionBag.accountService.accounts.isEmpty {
.loadAccounts() self.stateSubject.onNext(AppState.needToOnboard)
.map({ (accounts) -> Bool in } else {
return !accounts.isEmpty self.stateSubject.onNext(AppState.allSet)
}) }
.subscribe(onSuccess: { [unowned self] (hasAccounts) in
if hasAccounts {
self.stateSubject.onNext(AppState.allSet)
} else {
self.stateSubject.onNext(AppState.needToOnboard)
}
}, onError: { (error) in
print(error)
})
.disposed(by: self.disposeBag)
} }
// MARK: - Private methods // MARK: - Private methods
......
...@@ -23,30 +23,102 @@ import SwiftyBeaver ...@@ -23,30 +23,102 @@ import SwiftyBeaver
enum DataAccessError: Error { enum DataAccessError: Error {
case datastoreConnectionError case datastoreConnectionError
case databaseMigrationError
case databaseError case databaseError
} }
final class RingDB { final class DBContainer {
static let instance = RingDB() var jamiDB: Connection?
let ringDB: Connection? private var connections = [String: Connection?]()
private let log = SwiftyBeaver.self private let log = SwiftyBeaver.self
private let dbName = "ring.db" private let jamiDBName = "ring.db"
private let path: String
private let dbVersion = 1
//tables init() {
var tableProfiles = Table("profiles") path = NSSearchPathForDirectoriesInDomains(
var tableConversations = Table("conversations")
var tableInteractionss = Table("interactions")
private init() {
let path = NSSearchPathForDirectoriesInDomains(
.documentDirectory, .userDomainMask, true .documentDirectory, .userDomainMask, true
).first! ).first!
}
func getJamiDB() -> Connection? {
if jamiDB != nil {
return jamiDB
}
do {
jamiDB = try Connection("\(path)/" + jamiDBName)
} catch {
jamiDB = nil
log.error("Unable to open database")
}
return jamiDB
}
func removeJamiDB() {
self.removeDBNamed(dbName: jamiDBName)
}
func removeDBForAccount(account: String) {
self.removeDBNamed(dbName: "\(account).db")
}
func forAccount(account: String) -> Connection? {
if connections[account] != nil {
return connections[account] ?? nil
}
do { do {
ringDB = try Connection("\(path)/" + dbName) let accountDb = try Connection("\(path)/" + "\(account).db")
accountDb.userVersion = dbVersion
connections[account] = accountDb
return accountDb
} catch { } catch {
ringDB = nil
log.error("Unable to open database") log.error("Unable to open database")
return nil
}
}
func isDBExistsFor(account: String) -> Bool {
let url = NSURL(fileURLWithPath: path)
if let pathComponent = url.appendingPathComponent("/" + "\(account).db") {
let filePath = pathComponent.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) {
return false
} else {
return true
}
} else {
return true
}
}
private func removeDBNamed(dbName: String) {
let url = NSURL(fileURLWithPath: path)
guard let pathComponent = url
.appendingPathComponent("/" + dbName) else {
return
}
let filePath = pathComponent.path
let filemManager = FileManager.default
do {
let fileURL = NSURL(fileURLWithPath: filePath)
try filemManager.removeItem(at: fileURL as URL)
print("old database deleted")
} catch {
print("Error on delete old database!!!")
}
}
}
extension Connection {
public var userVersion: Int? {
get {
if let version = try? scalar("PRAGMA user_version"),
let intVersion = version as? Int64 {return Int(intVersion)}
return nil
}
set {
if let version = newValue {_ = try? run("PRAGMA user_version = \(version)")}
} }
} }
} }
...@@ -22,37 +22,50 @@ import SQLite ...@@ -22,37 +22,50 @@ import SQLite
typealias Conversation = ( typealias Conversation = (
id: Int64, id: Int64,
participantID: Int64 participant: String
) )
final class ConversationDataHelper { final class ConversationDataHelper {
let table = RingDB.instance.tableConversations let table = Table("conversations")
let id = Expression<Int64>("id") let id = Expression<Int64>("id")
let participantId = Expression<Int64>("participant_id") let participant = Expression<String>("participant")
// reference foreign key
let tableProfiles = Table("profiles")
let uri = Expression<String>("uri")
func createTable() throws { // to migrate from legacy db
guard let dataBase = RingDB.instance.ringDB else { let participantId = Expression<Int64>("participant_id")
throw DataAccessError.datastoreConnectionError func migrateToDBForAccount(from oldDB: Connection,
to newDB: Connection,
accountProfileId: Int64,
contactsMap: [Int64: String]) throws {
let query = table.filter(accountProfileId != participantId)
let items = try oldDB.prepare(query)
for item in items {
if let uri = contactsMap[item[participantId]] {
let query = table.insert(id <- item[id],
participant <- "ring:" + uri)
try newDB.run(query)
}
} }
}
func createTable(accountDb: Connection) {
do { do {
try dataBase.run(table.create(ifNotExists: true) { table in try accountDb.run(table.create(ifNotExists: true) { table in
table.column(id) table.column(id)
table.column(participantId) table.column(participant)
table.foreignKey(participantId, references: RingDB.instance.tableProfiles, id, delete: .noAction) table.foreignKey(participant, references: tableProfiles, uri, delete: .noAction)
}) })
} catch _ { } catch _ {
print("Table already exists") print("Table already exists")
} }
} }
func insert(item: Conversation) -> Bool { func insert(item: Conversation, dataBase: Connection) -> Bool {
guard let dataBase = RingDB.instance.ringDB else {
return false
}
let query = table.insert(id <- item.id, let query = table.insert(id <- item.id,
participantId <- item.participantID) participant <- item.participant)
do { do {
let rowId = try dataBase.run(query) let rowId = try dataBase.run(query)
guard rowId > 0 else { guard rowId > 0 else {
...@@ -64,10 +77,7 @@ final class ConversationDataHelper { ...@@ -64,10 +77,7 @@ final class ConversationDataHelper {
} }
} }
func delete (item: Conversation) -> Bool { func delete (item: Conversation, dataBase: Connection) -> Bool {
guard let dataBase = RingDB.instance.ringDB else {
return false
}
let conversationId = item.id let conversationId = item.id
let query = table.filter(id == conversationId) let query = table.filter(id == conversationId)
do { do {
...@@ -81,48 +91,36 @@ final class ConversationDataHelper { ...@@ -81,48 +91,36 @@ final class ConversationDataHelper {
} }
} }
func selectConversations (conversationId: Int64) throws -> [Conversation]? { func selectConversations (conversationId: Int64, dataBase: Connection) throws -> [Conversation]? {
guard let dataBase = RingDB.instance.ringDB else {
throw DataAccessError.datastoreConnectionError
}
let query = table.filter(id == conversationId) let query = table.filter(id == conversationId)
var conversations = [Conversation]() var conversations = [Conversation]()
let items = try dataBase.prepare(query) let items = try dataBase.prepare(query)
for item in items { for item in items {
conversations.append(Conversation(id: item[id], participantID: item[participantId])) conversations.append(Conversation(id: item[id], participant: item[participant]))
} }
return conversations return conversations
} }
func selectAll() throws -> [Conversation]? { func selectAll(dataBase: Connection) throws -> [Conversation]? {
guard let dataBase = RingDB.instance.ringDB else {
throw DataAccessError.datastoreConnectionError
}
var conversations = [Conversation]() var conversations = [Conversation]()
let items = try dataBase.prepare(table) let items = try dataBase.prepare(table)
for item in items { for item in items {
conversations.append(Conversation(id: item[id], participantID: item[participantId])) conversations.append(Conversation(id: item[id], participant: item[participant]))
} }
return conversations return conversations
} }
func selectConversationsForProfile(profileId: Int64) throws -> [Conversation]? { func selectConversationsForProfile(profileUri: String, dataBase: Connection) throws -> [Conversation]? {
guard let dataBase = RingDB.instance.ringDB else {
throw DataAccessError.datastoreConnectionError
}
var conversations = [Conversation]() var conversations = [Conversation]()
let query = table.filter(participantId == profileId) let query = table.filter(participant == profileUri)
let items = try dataBase.prepare(query) let items = try dataBase.prepare(query)
for item in items { for item in items {
conversations.append(Conversation(id: item[id], participantID: item[participantId])) conversations.append(Conversation(id: item[id], participant: item[participant]))
} }
return conversations return conversations
} }
func deleteConversations(conversationID: Int64) -> Bool { func deleteConversations(conversationID: Int64, dataBase: Connection) -> Bool {
guard let dataBase = RingDB.instance.ringDB else {
return false
}
let query = table.filter(id == conversationID) let query = table.filter(id == conversationID)
do { do {
if try dataBase.run(query.delete()) > 0 { if try dataBase.run(query.delete()) > 0 {
......
...@@ -22,52 +22,122 @@ import SQLite ...@@ -22,52 +22,122 @@ import SQLite
import SwiftyBeaver import SwiftyBeaver
typealias Profile = ( typealias Profile = (
id: Int64,
uri: String, uri: String,
alias: String?, alias: String?,
photo: String?, photo: String?,
type: String, type: String
status: String
) )
final class ProfileDataHelper { final class ProfileDataHelper {
let table = RingDB.instance.tableProfiles let contactsProfileTable = Table("profiles")
let id = Expression<Int64>("id") let accountProfileTable = Table("account_profile")
let uri = Expression<String>("uri") let uri = Expression<String>("uri")
let alias = Expression<String?>("alias") let alias = Expression<String?>("alias")
let photo = Expression<String?>("photo") let photo = Expression<String?>("photo")
let type = Expression<String>("type") let type = Expression<String>("type")
let status = Expression<String>("status")
private let log = SwiftyBeaver.self private let log = SwiftyBeaver.self
func createTable() throws { //migrate from legacy db
guard let dataBase = RingDB.instance.ringDB else { let id = Expression<Int64>("id")
throw DataAccessError.datastoreConnectionError 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]()