diff --git a/Sources/ChatFeature/Helpers/BubbleBuilder.swift b/Sources/ChatFeature/Helpers/BubbleBuilder.swift index 32b1648006dca1e9750d1afa57699f3b37c35615..ae33fd01e60bfaf501583779e11eb59575444b4f 100644 --- a/Sources/ChatFeature/Helpers/BubbleBuilder.swift +++ b/Sources/ChatFeature/Helpers/BubbleBuilder.swift @@ -36,7 +36,9 @@ final class Bubbler { audioBubble.dateLabel.textColor = Asset.neutralWhite.color audioBubble.progressLabel.textColor = Asset.neutralWhite.color case .receivingFailed: - fatalError() + audioBubble.backgroundColor = Asset.accentWarning.color + audioBubble.dateLabel.textColor = Asset.neutralWhite.color + audioBubble.progressLabel.textColor = Asset.neutralWhite.color } } @@ -75,7 +77,9 @@ final class Bubbler { imageBubble.dateLabel.textColor = Asset.neutralWhite.color imageBubble.progressLabel.textColor = Asset.neutralWhite.color case .receivingFailed: - fatalError() + imageBubble.backgroundColor = Asset.accentWarning.color + imageBubble.dateLabel.textColor = Asset.neutralWhite.color + imageBubble.progressLabel.textColor = Asset.neutralWhite.color } } @@ -118,7 +122,10 @@ final class Bubbler { bubble.dateLabel.textColor = Asset.neutralWhite.color roundButtonColor = Asset.neutralWhite.color case .receivingFailed: - fatalError() + bubble.backgroundColor = Asset.accentWarning.color + bubble.textView.textColor = Asset.neutralWhite.color + bubble.dateLabel.textColor = Asset.neutralWhite.color + roundButtonColor = Asset.neutralWhite.color } let attrString = NSAttributedString( @@ -182,7 +189,13 @@ final class Bubbler { bubble.replyView.space.backgroundColor = Asset.neutralWhite.color bubble.replyView.container.backgroundColor = Asset.brandLight.color case .receivingFailed: - fatalError() + bubble.senderLabel.removeFromSuperview() + bubble.textView.textColor = Asset.neutralWhite.color + bubble.backgroundColor = Asset.accentWarning.color + bubble.dateLabel.textColor = Asset.neutralWhite.color + roundButtonColor = Asset.neutralWhite.color + bubble.replyView.space.backgroundColor = Asset.neutralWhite.color + bubble.replyView.container.backgroundColor = Asset.brandLight.color } let attrString = NSAttributedString( @@ -239,7 +252,13 @@ final class Bubbler { bubble.replyView.space.backgroundColor = Asset.neutralWhite.color bubble.replyView.container.backgroundColor = Asset.brandLight.color case .receivingFailed: - fatalError() + bubble.senderLabel.removeFromSuperview() + bubble.textView.textColor = Asset.neutralWhite.color + bubble.backgroundColor = Asset.accentWarning.color + bubble.dateLabel.textColor = Asset.neutralWhite.color + roundButtonColor = Asset.neutralWhite.color + bubble.replyView.space.backgroundColor = Asset.neutralWhite.color + bubble.replyView.container.backgroundColor = Asset.brandLight.color } let attrString = NSAttributedString( @@ -287,7 +306,11 @@ final class Bubbler { bubble.dateLabel.textColor = Asset.neutralWhite.color roundButtonColor = Asset.neutralWhite.color case .receivingFailed: - fatalError() + bubble.senderLabel.removeFromSuperview() + bubble.backgroundColor = Asset.accentWarning.color + bubble.textView.textColor = Asset.neutralWhite.color + bubble.dateLabel.textColor = Asset.neutralWhite.color + roundButtonColor = Asset.neutralWhite.color } let attrString = NSAttributedString( diff --git a/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift b/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift index a257d9f3a5af00e1e9639f4b7e7505fedd5c22c0..aa1dbefc8ed34f582d84df609d70b65631c69013 100644 --- a/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift +++ b/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift @@ -109,6 +109,6 @@ final class GroupChatViewModel { func didRequestReply(_ message: Message) { guard let networkId = message.networkId else { return } stagedReply = Reply(messageId: networkId, senderId: message.senderId) - replySubject.send(getReplyContent(for: message.id)) + replySubject.send(getReplyContent(for: networkId)) } } diff --git a/Sources/ChatListFeature/Controller/ChatListSearchTableController.swift b/Sources/ChatListFeature/Controller/ChatListSearchTableController.swift index e8f07fbff2f78b159de5cd41e1cd3c45e04a7d0d..07efc15ce666f7b2183d915e832773529694eede 100644 --- a/Sources/ChatListFeature/Controller/ChatListSearchTableController.swift +++ b/Sources/ChatListFeature/Controller/ChatListSearchTableController.swift @@ -34,8 +34,13 @@ final class ChatSearchTableController: UITableViewController { switch item { case .chat(let info): switch info { - case .group: - fatalError() + case .group(let group): + cell.setupGroup( + name: group.name, + date: group.createdAt, + preview: nil, + hasUnread: false + ) case .groupChat(let groupChatInfo): cell.setupGroup( @@ -57,7 +62,7 @@ final class ChatSearchTableController: UITableViewController { case .connection(let contact): cell.setupContact( - name: contact.nickname ?? contact.username!, + name: (contact.nickname ?? contact.username) ?? "", image: contact.photo, date: nil, hasUnread: false, @@ -101,13 +106,16 @@ extension ChatSearchTableController { switch item { case .chat(let chatInfo): switch chatInfo { - case .group: - fatalError() + case .group(let group): + if let groupInfo = viewModel.groupInfo(from: group) { + coordinator.toGroupChat(with: groupInfo, from: self) + } - case .groupChat: - fatalError() + case .groupChat(let info): + if let groupInfo = viewModel.groupInfo(from: info.group) { + coordinator.toGroupChat(with: groupInfo, from: self) + } - //coordinator.toGroupChat(with: info, from: self) case .contactChat(let info): guard info.contact.authStatus == .friend else { return } coordinator.toSingleChat(with: info.contact, from: self) diff --git a/Sources/ChatListFeature/Controller/ChatListTableController.swift b/Sources/ChatListFeature/Controller/ChatListTableController.swift index f20902adca15d41bf75a87512054ce25f36b4f01..9c97a2414b9d6d99f9ed304c16ab470674d651ed 100644 --- a/Sources/ChatListFeature/Controller/ChatListTableController.swift +++ b/Sources/ChatListFeature/Controller/ChatListTableController.swift @@ -94,12 +94,15 @@ extension ChatListTableController { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { switch rows[indexPath.row] { - case .group: - fatalError() + case .group(let group): + if let groupInfo = viewModel.groupInfo(from: group) { + coordinator.toGroupChat(with: groupInfo, from: self) + } - case .groupChat: - fatalError() - //coordinator.toGroupChat(with: info, from: self) + case .groupChat(let info): + if let groupInfo = viewModel.groupInfo(from: info.group) { + coordinator.toGroupChat(with: groupInfo, from: self) + } case .contactChat(let info): guard info.contact.authStatus == .friend else { return } @@ -115,8 +118,13 @@ extension ChatListTableController { let cell = tableView.dequeueReusableCell(forIndexPath: indexPath, ofType: ChatListCell.self) switch rows[indexPath.row] { - case .group: - fatalError() + case .group(let group): + cell.setupGroup( + name: group.name, + date: group.createdAt, + preview: nil, + hasUnread: false + ) case .groupChat(let info): cell.setupGroup( @@ -146,8 +154,11 @@ extension ChatListTableController { let actionClosure: () -> Void switch item { - case .group: - fatalError() + case .group(let group): + title = Localized.ChatList.DeleteGroup.title + subtitle = Localized.ChatList.DeleteGroup.subtitle + actionTitle = Localized.ChatList.DeleteGroup.action + actionClosure = { [weak viewModel] in viewModel?.leave(group) } case .contactChat(let info): title = Localized.ChatList.Delete.title diff --git a/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift b/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift index 2f8817aa1634fb8c18c596534750fcc46260286c..b8f1792a5369849b1f8aab7b9b56b7a214570c16 100644 --- a/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift +++ b/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift @@ -66,8 +66,8 @@ final class ChatListViewModel { let chatItems = chats.filter { switch $0 { - case .group: - fatalError() + case .group(let group): + return group.name.lowercased().contains(query.lowercased()) case .groupChat(let info): let name = info.group.name.lowercased().contains(query.lowercased()) @@ -148,4 +148,13 @@ final class ChatListViewModel { func clear(_ contact: Contact) { _ = try? session.dbManager.deleteMessages(.init(chat: .direct(session.myId, contact.id))) } + + func groupInfo(from group: Group) -> GroupInfo? { + let query = GroupInfo.Query(groupId: group.id) + guard let info = try? session.dbManager.fetchGroupInfos(query).first else { + return nil + } + + return info + } } diff --git a/Sources/Integration/Session/Session+Group.swift b/Sources/Integration/Session/Session+Group.swift index e7b96b3bc8e31936d15bfac72b77fe912bdd46a1..9a37e4b8a1c350ece156ca3a147f2fc2a463f4b1 100644 --- a/Sources/Integration/Session/Session+Group.swift +++ b/Sources/Integration/Session/Session+Group.swift @@ -58,9 +58,9 @@ extension Session { _ = try? dbManager.saveMessage(.init( networkId: nil, senderId: group.leaderId, - recipientId: client.bindings.myId, + recipientId: nil, groupId: group.id, - date: Date(), + date: group.createdAt, status: .received, isUnread: true, text: welcome, diff --git a/Sources/Integration/Session/Session.swift b/Sources/Integration/Session/Session.swift index 6763bed2cd3d1d3e273bce476ff48e741ef04e89..7d8d2088fbf4f50a8a6692a05144cfc444c318f5 100644 --- a/Sources/Integration/Session/Session.swift +++ b/Sources/Integration/Session/Session.swift @@ -90,23 +90,39 @@ public final class Session: SessionType { self.client = client - let oldPath = NSSearchPathForDirectoriesInDomains( + let legacyOldPath = NSSearchPathForDirectoriesInDomains( .documentDirectory, .userDomainMask, true )[0].appending("/xxmessenger.sqlite") - let newPath = FileManager.default + let legacyPath = FileManager.default .containerURL(forSecurityApplicationGroupIdentifier: "group.elixxir.messenger")! .appendingPathComponent("database") .appendingPathExtension("sqlite").path - try Migrator.live()( - try .init(path: oldPath), - to: try .onDisk(path: newPath), - myContactId: client.bindings.myId, - meMarshaled: client.bindings.meMarshalled - ) + let dbExistsInLegacyOldPath = FileManager.default.fileExists(atPath: legacyOldPath) + let dbExistsInLegacyPath = FileManager.default.fileExists(atPath: legacyPath) - dbManager = try Database.onDisk(path: newPath) + if dbExistsInLegacyOldPath && !dbExistsInLegacyPath { + try? FileManager.default.moveItem(atPath: legacyOldPath, toPath: legacyPath) + } + + let dbPath = FileManager.default + .containerURL(forSecurityApplicationGroupIdentifier: "group.elixxir.messenger")! + .appendingPathComponent("xxm_database") + .appendingPathExtension("sqlite").path + + dbManager = try Database.onDisk(path: dbPath) + + if dbExistsInLegacyPath { + try Migrator.live()( + try .init(path: legacyPath), + to: dbManager, + myContactId: client.bindings.myId, + meMarshaled: client.bindings.meMarshalled + ) + + try FileManager.default.moveItem(atPath: legacyPath, toPath: legacyPath.appending("-backup")) + } let report = try! JSONDecoder().decode(BackupReport.self, from: backupData!) @@ -129,23 +145,39 @@ public final class Session: SessionType { let network = try! DependencyInjection.Container.shared.resolve() as XXNetworking self.client = try network.newClient(ndf: ndf) - let oldPath = NSSearchPathForDirectoriesInDomains( + let legacyOldPath = NSSearchPathForDirectoriesInDomains( .documentDirectory, .userDomainMask, true )[0].appending("/xxmessenger.sqlite") - let newPath = FileManager.default + let legacyPath = FileManager.default .containerURL(forSecurityApplicationGroupIdentifier: "group.elixxir.messenger")! .appendingPathComponent("database") .appendingPathExtension("sqlite").path - try Migrator.live()( - try .init(path: oldPath), - to: try .onDisk(path: newPath), - myContactId: client.bindings.myId, - meMarshaled: client.bindings.meMarshalled - ) + let dbExistsInLegacyOldPath = FileManager.default.fileExists(atPath: legacyOldPath) + let dbExistsInLegacyPath = FileManager.default.fileExists(atPath: legacyPath) - dbManager = try Database.onDisk(path: newPath) + if dbExistsInLegacyOldPath && !dbExistsInLegacyPath { + try? FileManager.default.moveItem(atPath: legacyOldPath, toPath: legacyPath) + } + + let dbPath = FileManager.default + .containerURL(forSecurityApplicationGroupIdentifier: "group.elixxir.messenger")! + .appendingPathComponent("xxm_database") + .appendingPathExtension("sqlite").path + + dbManager = try Database.onDisk(path: dbPath) + + if dbExistsInLegacyPath { + try Migrator.live()( + try .init(path: legacyPath), + to: dbManager, + myContactId: client.bindings.myId, + meMarshaled: client.bindings.meMarshalled + ) + + try FileManager.default.moveItem(atPath: legacyPath, toPath: legacyPath.appending("-backup")) + } try continueInitialization() } @@ -212,8 +244,6 @@ public final class Session: SessionType { .compactMap(\.fileTransferId)))) else { return } - // What would be a good way to do this? - let pairs = unfinishedSendingMessages.map { message -> (Message, FileTransfer) in let transfer = unfinishedSendingTransfers.first { ft in ft.id == message.fileTransferId