diff --git a/Package.swift b/Package.swift index bf419b1d337afe4e8dc8bd9459758d0a1a658a23..90df7d8a602de9afebb47aaba0d0539de780f83b 100644 --- a/Package.swift +++ b/Package.swift @@ -53,7 +53,7 @@ let package = Package( dependencies: [ .package( url: "https://git.xx.network/elixxir/client-ios-db.git", - revision: "2e51a960216340cf12a4c6cbc7ac18a9f07d5ee6" + revision: "452392e032d52b217a20cd0f9de3b289f12fe444" ), .package(url: "https://github.com/Quick/Quick", from: "3.0.0"), .package(url: "https://github.com/Quick/Nimble", from: "9.0.0"), diff --git a/Sources/ChatFeature/Controllers/GroupChatController.swift b/Sources/ChatFeature/Controllers/GroupChatController.swift index e248c053bc68431171a9817454d68fffb6f6a4a2..6299ecb676646e6078e4e4a8bf282caa174a0362 100644 --- a/Sources/ChatFeature/Controllers/GroupChatController.swift +++ b/Sources/ChatFeature/Controllers/GroupChatController.swift @@ -3,6 +3,7 @@ import Theme import Models import Shared import Combine +import XXModels import Voxophone import ChatLayout import DrawerFeature @@ -32,7 +33,7 @@ public final class GroupChatController: UIViewController { private let layoutDelegate = LayoutDelegate() private var cancellables = Set<AnyCancellable>() private var drawerCancellables = Set<AnyCancellable>() - private var sections = [ArraySection<ChatSection, GroupChatItem>]() + private var sections = [ArraySection<ChatSection, ChatItem>]() private var currentInterfaceActions = SetActor<Set<InterfaceActions>, ReactionTypes>() public override var canBecomeFirstResponder: Bool { true } @@ -41,7 +42,7 @@ public final class GroupChatController: UIViewController { public init(_ info: GroupChatInfo) { let viewModel = GroupChatViewModel(info) self.viewModel = viewModel - self.members = .init(with: info.members) + self.members = .init(with: []) self.inputComponent = ChatInputView(store: .init( initialState: .init(canAddAttachments: false), @@ -59,7 +60,7 @@ public final class GroupChatController: UIViewController { super.init(nibName: nil, bundle: nil) - header.setup(title: info.group.name, memberList: info.members.map { ($0.username, $0.photo) }) +// header.setup(title: info.group.name, memberList: info.members.map { ($0.username, $0.photo) }) } public required init?(coder: NSCoder) { nil } @@ -316,14 +317,10 @@ extension GroupChatController: UICollectionViewDataSource { ) -> UICollectionViewCell { let item = sections[indexPath.section].elements[indexPath.item] - let canReply: () -> Bool = { - item.status == .sent || - item.status == .received || - item.status == .read - } + let canReply: () -> Bool = { item.status == .sent || item.status == .received } let performReply: () -> Void = { [weak self] in - self?.viewModel.didRequestReply(item) + //self?.viewModel.didRequestReply(item) } let name: (Data) -> String = viewModel.getName(from:) @@ -334,15 +331,15 @@ extension GroupChatController: UICollectionViewDataSource { if item.payload.reply != nil { let cell: IncomingGroupReplyCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - Bubbler.buildReplyGroup( - bubble: cell.leftView, - with: item, - reply: .init( - text: text(item.payload.reply!.messageId), - sender: name(item.payload.reply!.senderId) - ), - sender: name(item.sender) - ) +// Bubbler.buildReplyGroup( +// bubble: cell.leftView, +// with: item, +// reply: .init( +// text: text(item.payload.reply!.messageId), +// sender: name(item.payload.reply!.senderId) +// ), +// sender: name(item.sender) +// ) cell.canReply = canReply() cell.performReply = performReply @@ -351,26 +348,26 @@ extension GroupChatController: UICollectionViewDataSource { return cell } else { let cell: IncomingGroupTextCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - Bubbler.buildGroup(bubble: cell.leftView, with: item, with: name(item.sender)) +// Bubbler.buildGroup(bubble: cell.leftView, with: item, with: name(item.sender)) cell.canReply = canReply() cell.performReply = performReply cell.leftView.didTapShowRound = { showRound(item.roundURL) } return cell } - } else if item.status == .failed { + } else if item.status == .sendingFailed { if item.payload.reply != nil { let cell: OutgoingFailedGroupReplyCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - Bubbler.buildReplyGroup( - bubble: cell.rightView, - with: item, - reply: .init( - text: text(item.payload.reply!.messageId), - sender: name(item.payload.reply!.senderId) - ), - sender: name(item.sender) - ) +// Bubbler.buildReplyGroup( +// bubble: cell.rightView, +// with: item, +// reply: .init( +// text: text(item.payload.reply!.messageId), +// sender: name(item.payload.reply!.senderId) +// ), +// sender: name(item.sender) +// ) cell.canReply = canReply() cell.performReply = performReply @@ -379,7 +376,7 @@ extension GroupChatController: UICollectionViewDataSource { } else { let cell: OutgoingFailedGroupTextCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - Bubbler.buildGroup(bubble: cell.rightView, with: item, with: name(item.sender)) +// Bubbler.buildGroup(bubble: cell.rightView, with: item, with: name(item.sender)) cell.canReply = canReply() cell.performReply = performReply @@ -389,15 +386,15 @@ extension GroupChatController: UICollectionViewDataSource { if item.payload.reply != nil { let cell: OutgoingGroupReplyCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - Bubbler.buildReplyGroup( - bubble: cell.rightView, - with: item, - reply: .init( - text: text(item.payload.reply!.messageId), - sender: name(item.payload.reply!.senderId) - ), - sender: name(item.sender) - ) +// Bubbler.buildReplyGroup( +// bubble: cell.rightView, +// with: item, +// reply: .init( +// text: text(item.payload.reply!.messageId), +// sender: name(item.payload.reply!.senderId) +// ), +// sender: name(item.sender) +// ) cell.canReply = canReply() cell.performReply = performReply @@ -407,7 +404,7 @@ extension GroupChatController: UICollectionViewDataSource { } else { let cell: OutgoingGroupTextCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - Bubbler.buildGroup(bubble: cell.rightView, with: item, with: name(item.sender)) +// Bubbler.buildGroup(bubble: cell.rightView, with: item, with: name(item.sender)) cell.canReply = canReply() cell.performReply = performReply cell.rightView.didTapShowRound = { showRound(item.roundURL) } @@ -531,7 +528,7 @@ extension GroupChatController: UICollectionViewDelegate { } let reply = UIAction(title: Localized.Chat.BubbleMenu.reply, state: .off) { [weak self] _ in - self?.viewModel.didRequestReply(item) + //self?.viewModel.didRequestReply(item) } let delete = UIAction(title: Localized.Chat.BubbleMenu.delete, state: .off) { [weak self] _ in @@ -544,7 +541,7 @@ extension GroupChatController: UICollectionViewDelegate { let menu: UIMenu - if item.status == .failed { + if item.status == .sendingFailed { menu = UIMenu(title: "", children: [copy, retry, delete]) } else if item.status == .sending { menu = UIMenu(title: "", children: [copy]) diff --git a/Sources/ChatFeature/Controllers/SingleChatController.swift b/Sources/ChatFeature/Controllers/SingleChatController.swift index b0f9324c2d84dc2d5aec7d589bd917c1e38f8275..9e4b4a5a8d858f72c0573881217425eb1e2f9e1b 100644 --- a/Sources/ChatFeature/Controllers/SingleChatController.swift +++ b/Sources/ChatFeature/Controllers/SingleChatController.swift @@ -154,9 +154,9 @@ public final class SingleChatController: UIViewController { } private func setupNavigationBar(contact: Contact) { - screenView.set(name: contact.nickname ?? contact.username) + screenView.set(name: contact.nickname ?? contact.username!) avatarView.snp.makeConstraints { $0.width.height.equalTo(35) } - avatarView.setupProfile(title: contact.nickname ?? contact.username, image: contact.photo, size: .small) + avatarView.setupProfile(title: contact.nickname ?? contact.username!, image: contact.photo, size: .small) nameLabel.text = contact.nickname ?? contact.username nameLabel.textColor = Asset.neutralActive.color @@ -435,10 +435,10 @@ public final class SingleChatController: UIViewController { } private func previewItemAt(_ indexPath: IndexPath) { - let item = sections[indexPath.section].elements[indexPath.item] - guard let attachment = item.payload.attachment, item.status != .receivingAttachment else { return } - fileURL = FileManager.url(for: "\(attachment.name).\(attachment._extension.written)") - coordinator.toPreview(from: self) +// let item = sections[indexPath.section].elements[indexPath.item] +// guard let attachment = item.payload.attachment, item.status != .receiving else { return } +// fileURL = FileManager.url(for: "\(attachment.name).\(attachment._extension.written)") +// coordinator.toPreview(from: self) } // MARK: Selectors @@ -562,7 +562,7 @@ extension SingleChatController: UICollectionViewDelegate { let status = sections[section].elements[item].status - if status == .received || status == .read || status == .receivingAttachment { + if status == .received || status == .receiving { var leftView: UIView! if let cell = cell as? IncomingReplyCell { diff --git a/Sources/ChatFeature/Helpers/BubbleBuilder.swift b/Sources/ChatFeature/Helpers/BubbleBuilder.swift index 9deb4382350d104a3a7c6393aa32fb1599d8d217..dadbaef0496edcb6a98442a0b07e99419d2249fe 100644 --- a/Sources/ChatFeature/Helpers/BubbleBuilder.swift +++ b/Sources/ChatFeature/Helpers/BubbleBuilder.swift @@ -43,9 +43,9 @@ final class Bubbler { imageBubble: ImageMessageView, with item: ChatItem ) { - let progress = item.payload.attachment!.progress - imageBubble.progressLabel.text = String(format: "%.1f%%", progress * 100) - imageBubble.dateLabel.text = item.date.asHoursAndMinutes() +// let progress = item.payload.attachment!.progress +// imageBubble.progressLabel.text = String(format: "%.1f%%", progress * 100) +// imageBubble.dateLabel.text = item.date.asHoursAndMinutes() switch item.status { case .received: @@ -133,52 +133,6 @@ final class Bubbler { bubble.roundButton.setAttributedTitle(attrString, for: .normal) } - static func buildGroup( - bubble: StackMessageView, - with item: GroupChatItem, - with senderName: String - ) { - bubble.textView.text = item.payload.text - bubble.dateLabel.text = item.date.asHoursAndMinutes() - - let roundButtonColor: UIColor - - switch item.status { - case .received, .read: - bubble.senderLabel.text = senderName - bubble.backgroundColor = Asset.neutralWhite.color - bubble.textView.textColor = Asset.neutralActive.color - bubble.dateLabel.textColor = Asset.neutralDisabled.color - roundButtonColor = Asset.neutralDisabled.color - bubble.lockerImageView.removeFromSuperview() - bubble.revertBottomStackOrder() - case .failed: - bubble.senderLabel.removeFromSuperview() - bubble.backgroundColor = Asset.accentDanger.color - bubble.textView.textColor = Asset.neutralWhite.color - bubble.dateLabel.textColor = Asset.neutralWhite.color - roundButtonColor = Asset.neutralWhite.color - case .sent, .sending: - bubble.senderLabel.removeFromSuperview() - bubble.backgroundColor = Asset.brandBubble.color - bubble.textView.textColor = Asset.neutralWhite.color - bubble.dateLabel.textColor = Asset.neutralWhite.color - roundButtonColor = Asset.neutralWhite.color - } - - let attrString = NSAttributedString( - string: "show mix", - attributes: [ - .underlineStyle: NSUnderlineStyle.single.rawValue, - .underlineColor: roundButtonColor, - .foregroundColor: roundButtonColor, - .font: Fonts.Mulish.regular.font(size: 12.0) as Any - ] - ) - - bubble.roundButton.setAttributedTitle(attrString, for: .normal) - } - static func buildReply( bubble: ReplyStackMessageView, with item: ChatItem, @@ -241,60 +195,4 @@ final class Bubbler { ) bubble.roundButton.setAttributedTitle(attrString, for: .normal) } - - static func buildReplyGroup( - bubble: ReplyStackMessageView, - with item: GroupChatItem, - reply: ReplyModel, - sender: String - ) { - bubble.dateLabel.text = item.date.asHoursAndMinutes() - bubble.textView.text = item.payload.text - - bubble.replyView.message.text = reply.text - bubble.replyView.title.text = reply.sender - - let roundButtonColor: UIColor - - switch item.status { - case .received, .read: - bubble.senderLabel.text = sender - bubble.backgroundColor = Asset.neutralWhite.color - bubble.textView.textColor = Asset.neutralActive.color - bubble.dateLabel.textColor = Asset.neutralDisabled.color - roundButtonColor = Asset.neutralDisabled.color - bubble.replyView.container.backgroundColor = Asset.brandDefault.color - bubble.replyView.space.backgroundColor = Asset.brandPrimary.color - bubble.lockerImageView.removeFromSuperview() - bubble.revertBottomStackOrder() - case .failed: - bubble.senderLabel.removeFromSuperview() - bubble.backgroundColor = Asset.accentDanger.color - bubble.textView.textColor = Asset.neutralWhite.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 - case .sent, .sending: - bubble.senderLabel.removeFromSuperview() - bubble.textView.textColor = Asset.neutralWhite.color - bubble.backgroundColor = Asset.brandBubble.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( - string: "show mix", - attributes: [ - .underlineStyle: NSUnderlineStyle.single.rawValue, - .underlineColor: roundButtonColor, - .foregroundColor: roundButtonColor, - .font: Fonts.Mulish.regular.font(size: 12.0) as Any - ] - ) - - bubble.roundButton.setAttributedTitle(attrString, for: .normal) - } } diff --git a/Sources/ChatFeature/Helpers/CellConfigurator.swift b/Sources/ChatFeature/Helpers/CellConfigurator.swift index bebf5f357eb047827aae2e428ad63a3a4a5f2410..01c5f6c026cf156fc81f298ec43e6b551882517d 100644 --- a/Sources/ChatFeature/Helpers/CellConfigurator.swift +++ b/Sources/ChatFeature/Helpers/CellConfigurator.swift @@ -41,193 +41,195 @@ extension CellFactory { static func incomingAudio( voxophone: Voxophone ) -> Self { - .init( - canBuild: { item in - (item.status == .received || item.status == .receiving) - && item.payload.reply == nil - && item.payload.attachment != nil - && item.payload.attachment?._extension == .audio - - }, build: { item, collectionView, indexPath in - guard let attachment = item.payload.attachment else { fatalError() } - - let cell: IncomingAudioCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - let url = FileManager.url(for: "\(attachment.name).\(attachment._extension.written)")! - - var model = AudioMessageCellState( - date: item.date, - audioURL: url, - isPlaying: false, - transferProgress: attachment.progress, - isLoudspeaker: false, - duration: (try? AVAudioPlayer(contentsOf: url).duration) ?? 0.0, - playbackTime: 0.0 - ) - - cell.leftView.setup(with: model) - cell.canReply = false - cell.performReply = {} - - Bubbler.build(audioBubble: cell.leftView, with: item) - - voxophone.$state - .sink { - switch $0 { - case .playing(url, _, time: let time, _): - model.isPlaying = true - model.playbackTime = time - default: - model.isPlaying = false - model.playbackTime = 0.0 - } - - model.isLoudspeaker = $0.isLoudspeaker - - cell.leftView.setup(with: model) - }.store(in: &cell.leftView.cancellables) - - cell.leftView.didTapRight = { - guard item.status != .receiving else { return } - - voxophone.toggleLoudspeaker() - } - - cell.leftView.didTapLeft = { - guard item.status != .receiving else { return } - - if case .playing(url, _, _, _) = voxophone.state { - voxophone.reset() - } else { - voxophone.load(url) - voxophone.play() - } - } - - return cell - } - ) + fatalError() +// .init( +// canBuild: { item in +// (item.status == .received || item.status == .receiving) +// && item.payload.reply == nil +// +// }, build: { item, collectionView, indexPath in +// guard let attachment = item.payload.attachment else { fatalError() } +// +// let cell: IncomingAudioCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) +// let url = FileManager.url(for: "\(attachment.name).\(attachment._extension.written)")! +// +// var model = AudioMessageCellState( +// date: item.date, +// audioURL: url, +// isPlaying: false, +// transferProgress: attachment.progress, +// isLoudspeaker: false, +// duration: (try? AVAudioPlayer(contentsOf: url).duration) ?? 0.0, +// playbackTime: 0.0 +// ) +// +// cell.leftView.setup(with: model) +// cell.canReply = false +// cell.performReply = {} +// +// Bubbler.build(audioBubble: cell.leftView, with: item) +// +// voxophone.$state +// .sink { +// switch $0 { +// case .playing(url, _, time: let time, _): +// model.isPlaying = true +// model.playbackTime = time +// default: +// model.isPlaying = false +// model.playbackTime = 0.0 +// } +// +// model.isLoudspeaker = $0.isLoudspeaker +// +// cell.leftView.setup(with: model) +// }.store(in: &cell.leftView.cancellables) +// +// cell.leftView.didTapRight = { +// guard item.status != .receiving else { return } +// +// voxophone.toggleLoudspeaker() +// } +// +// cell.leftView.didTapLeft = { +// guard item.status != .receiving else { return } +// +// if case .playing(url, _, _, _) = voxophone.state { +// voxophone.reset() +// } else { +// voxophone.load(url) +// voxophone.play() +// } +// } +// +// return cell +// } +// ) } static func outgoingAudio( voxophone: Voxophone ) -> Self { - .init( - canBuild: { item in - (item.status == .sent || - item.status == .sending || - item.status == .sendingFailed || - item.status == .sendingTimedOut) - && item.payload.reply == nil - && item.payload.attachment != nil - && item.payload.attachment?._extension == .audio - - }, build: { item, collectionView, indexPath in - guard let attachment = item.payload.attachment else { fatalError() } - - let cell: OutgoingAudioCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - let url = FileManager.url(for: "\(attachment.name).\(attachment._extension.written)")! - var model = AudioMessageCellState( - date: item.date, - audioURL: url, - isPlaying: false, - transferProgress: attachment.progress, - isLoudspeaker: false, - duration: (try? AVAudioPlayer(contentsOf: url).duration) ?? 0.0, - playbackTime: 0.0 - ) - - cell.rightView.setup(with: model) - cell.canReply = false - cell.performReply = {} - - Bubbler.build(audioBubble: cell.rightView, with: item) - - voxophone.$state - .sink { - switch $0 { - case .playing(url, _, time: let time, _): - model.isPlaying = true - model.playbackTime = time - default: - model.isPlaying = false - model.playbackTime = 0.0 - } - - model.isLoudspeaker = $0.isLoudspeaker - - cell.rightView.setup(with: model) - }.store(in: &cell.rightView.cancellables) - - cell.rightView.didTapRight = { - voxophone.toggleLoudspeaker() - } - - cell.rightView.didTapLeft = { - if case .playing(url, _, _, _) = voxophone.state { - voxophone.reset() - } else { - voxophone.load(url) - voxophone.play() - } - } - - return cell - } - ) + fatalError() +// .init( +// canBuild: { item in +// (item.status == .sent || +// item.status == .sending || +// item.status == .sendingFailed || +// item.status == .sendingTimedOut) +// && item.payload.reply == nil +// && item.payload.attachment != nil +// && item.payload.attachment?._extension == .audio +// +// }, build: { item, collectionView, indexPath in +// guard let attachment = item.payload.attachment else { fatalError() } +// +// let cell: OutgoingAudioCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) +// let url = FileManager.url(for: "\(attachment.name).\(attachment._extension.written)")! +// var model = AudioMessageCellState( +// date: item.date, +// audioURL: url, +// isPlaying: false, +// transferProgress: attachment.progress, +// isLoudspeaker: false, +// duration: (try? AVAudioPlayer(contentsOf: url).duration) ?? 0.0, +// playbackTime: 0.0 +// ) +// +// cell.rightView.setup(with: model) +// cell.canReply = false +// cell.performReply = {} +// +// Bubbler.build(audioBubble: cell.rightView, with: item) +// +// voxophone.$state +// .sink { +// switch $0 { +// case .playing(url, _, time: let time, _): +// model.isPlaying = true +// model.playbackTime = time +// default: +// model.isPlaying = false +// model.playbackTime = 0.0 +// } +// +// model.isLoudspeaker = $0.isLoudspeaker +// +// cell.rightView.setup(with: model) +// }.store(in: &cell.rightView.cancellables) +// +// cell.rightView.didTapRight = { +// voxophone.toggleLoudspeaker() +// } +// +// cell.rightView.didTapLeft = { +// if case .playing(url, _, _, _) = voxophone.state { +// voxophone.reset() +// } else { +// voxophone.load(url) +// voxophone.play() +// } +// } +// +// return cell +// } +// ) } } extension CellFactory { static func outgoingImage() -> Self { - .init( - canBuild: { item in - (item.status == .sent || - item.status == .sending || - item.status == .sendingFailed || - item.status == .sendingTimedOut) - && item.payload.reply == nil - && item.payload.attachment != nil - && item.payload.attachment?._extension == .image - - }, build: { item, collectionView, indexPath in - guard let attachment = item.payload.attachment else { fatalError() } - - let cell: OutgoingImageCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - - Bubbler.build(imageBubble: cell.rightView, with: item) - - cell.canReply = false - cell.performReply = {} - - if let image = UIImage(data: attachment.data!) { - cell.rightView.imageView.image = UIImage(cgImage: image.cgImage!, scale: image.scale, orientation: .up) - } - - return cell - } - ) + fatalError() +// .init( +// canBuild: { item in +// (item.status == .sent || +// item.status == .sending || +// item.status == .sendingFailed || +// item.status == .sendingTimedOut) +// && item.payload.reply == nil +// && item.payload.attachment != nil +// && item.payload.attachment?._extension == .image +// +// }, build: { item, collectionView, indexPath in +// guard let attachment = item.payload.attachment else { fatalError() } +// +// let cell: OutgoingImageCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) +// +// Bubbler.build(imageBubble: cell.rightView, with: item) +// +// cell.canReply = false +// cell.performReply = {} +// +// if let image = UIImage(data: attachment.data!) { +// cell.rightView.imageView.image = UIImage(cgImage: image.cgImage!, scale: image.scale, orientation: .up) +// } +// +// return cell +// } +// ) } static func incomingImage() -> Self { - .init( - canBuild: { item in - (item.status == .received || item.status == .receiving) - && item.payload.reply == nil - && item.payload.attachment != nil - && item.payload.attachment?._extension == .image - - }, build: { item, collectionView, indexPath in - guard let attachment = item.payload.attachment else { fatalError() } - - let cell: IncomingImageCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) - - Bubbler.build(imageBubble: cell.leftView, with: item) - cell.canReply = false - cell.performReply = {} - cell.leftView.imageView.image = UIImage(data: attachment.data!) - return cell - } - ) + fatalError() +// .init( +// canBuild: { item in +// (item.status == .received || item.status == .receiving) +// && item.payload.reply == nil +// && item.payload.attachment != nil +// && item.payload.attachment?._extension == .image +// +// }, build: { item, collectionView, indexPath in +// guard let attachment = item.payload.attachment else { fatalError() } +// +// let cell: IncomingImageCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) +// +// Bubbler.build(imageBubble: cell.leftView, with: item) +// cell.canReply = false +// cell.performReply = {} +// cell.leftView.imageView.image = UIImage(data: attachment.data!) +// return cell +// } +// ) } } @@ -242,7 +244,6 @@ extension CellFactory { canBuild: { item in (item.status == .sent || item.status == .sending) && item.payload.reply != nil - && item.payload.attachment == nil }, build: { item, collectionView, indexPath in let cell: OutgoingReplyCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) @@ -274,7 +275,6 @@ extension CellFactory { canBuild: { item in item.status == .received && item.payload.reply != nil - && item.payload.attachment == nil }, build: { item, collectionView, indexPath in let cell: IncomingReplyCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) @@ -305,7 +305,6 @@ extension CellFactory { canBuild: { item in (item.status == .sendingFailed || item.status == .sendingTimedOut) && item.payload.reply != nil - && item.payload.attachment == nil }, build: { item, collectionView, indexPath in let cell: OutgoingFailedReplyCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) @@ -336,7 +335,6 @@ extension CellFactory { canBuild: { item in item.status == .received && item.payload.reply == nil - && item.payload.attachment == nil }, build: { item, collectionView, indexPath in let cell: IncomingTextCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) @@ -359,7 +357,6 @@ extension CellFactory { canBuild: { item in (item.status == .sending || item.status == .sent) && item.payload.reply == nil - && item.payload.attachment == nil }, build: { item, collectionView, indexPath in let cell: OutgoingTextCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) @@ -379,7 +376,6 @@ extension CellFactory { canBuild: { item in (item.status == .sendingFailed || item.status == .sendingTimedOut) && item.payload.reply == nil - && item.payload.attachment == nil }, build: { item, collectionView, indexPath in let cell: OutgoingFailedTextCell = collectionView.dequeueReusableCell(forIndexPath: indexPath) @@ -421,8 +417,6 @@ struct ActionFactory { closure: @escaping (ChatItem) -> Void ) -> UIAction? { - guard item.payload.attachment == nil else { return nil } - switch action { case .reply: guard item.status == .received || item.status == .sent else { return nil } diff --git a/Sources/ChatFeature/Models/ChatItem.swift b/Sources/ChatFeature/Models/ChatItem.swift index 22d728d5272ae2eb066a4dc6d8a829d276d113bc..8694d9e54ec9727a18a24a2e9185a40ca262b6f1 100644 --- a/Sources/ChatFeature/Models/ChatItem.swift +++ b/Sources/ChatFeature/Models/ChatItem.swift @@ -15,30 +15,9 @@ struct ChatItem: Equatable, Differentiable { init(_ message: Message) { self.identity = message.id! self.status = message.status - self.payload = message.payload + self.payload = Payload(text: message.text, reply: nil) self.roundURL = message.roundURL - self.uniqueId = message.uniqueId - self.date = Date.fromTimestamp(message.timestamp) - } -} - -struct GroupChatItem: Equatable, Differentiable { - let date: Date - let sender: Data - let identity: Int64 - var uniqueId: Data? - var roundURL: String? - let payload: Payload - var status: GroupMessage.Status - var differenceIdentifier: Int64 { identity } - - init(_ groupMessage: GroupMessage) { - self.identity = groupMessage.id! - self.status = groupMessage.status - self.roundURL = groupMessage.roundURL - self.sender = groupMessage.sender - self.payload = groupMessage.payload - self.uniqueId = groupMessage.uniqueId - self.date = Date.fromTimestamp(groupMessage.timestamp) + self.uniqueId = message.networkId + self.date = message.date } } diff --git a/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift b/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift index ad29c602f94972faaec2aec031838e81b45c2ca6..663d08f777ba945c7ec563975e6aa832c90008c4 100644 --- a/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift +++ b/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift @@ -1,6 +1,7 @@ import UIKit import Models import Combine +import XXModels import Foundation import Integration import DifferenceKit @@ -28,10 +29,11 @@ final class GroupChatViewModel { private let replySubject = PassthroughSubject<ReplyModel, Never>() private let routesSubject = PassthroughSubject<GroupChatNavigationRoutes, Never>() - var messages: AnyPublisher<[ArraySection<ChatSection, GroupChatItem>], Never> { - session.groupMessages(info.group) - .map { messages -> [ArraySection<ChatSection, GroupChatItem>] in - let domainModels = messages.map { GroupChatItem($0) } + var messages: AnyPublisher<[ArraySection<ChatSection, ChatItem>], Never> { + session.dbManager.fetchMessagesPublisher(.init(chat: .group(info.group.id))) + .assertNoFailure() + .map { messages -> [ArraySection<ChatSection, ChatItem>] in + let domainModels = messages.map { ChatItem($0) } let groupedByDate = Dictionary(grouping: domainModels) { domainModel -> Date in let components = Calendar.current.dateComponents([.day, .month, .year], from: domainModel.date) return Calendar.current.date(from: components)! @@ -41,8 +43,8 @@ final class GroupChatViewModel { .map { .init(model: ChatSection(date: $0.key), elements: $0.value) } .sorted(by: { $0.model.date < $1.model.date }) } - .map { sections -> [ArraySection<ChatSection, GroupChatItem>] in - var snapshot = [ArraySection<ChatSection, GroupChatItem>]() + .map { sections -> [ArraySection<ChatSection, ChatItem>] in + var snapshot = [ArraySection<ChatSection, ChatItem>]() sections.forEach { snapshot.append(.init(model: $0.model, elements: $0.elements)) } return snapshot }.eraseToAnyPublisher() @@ -53,24 +55,25 @@ final class GroupChatViewModel { } func readAll() { - session.readAll(from: info.group) + let assignment = Message.Assignments(isUnread: false) + let query = Message.Query(chat: .group(info.group.id)) + _ = try? session.dbManager.bulkUpdateMessages(query, assignment) } - func didRequestDelete(_ items: [GroupChatItem]) { - session.delete(groupMessages: items.map { $0.identity }) + func didRequestDelete(_ items: [ChatItem]) { +// try? session.dbManager.deleteMessages(.init(id: items.map(\.identity))) } func send(_ text: String) { session.send(.init( text: text.trimmingCharacters(in: .whitespacesAndNewlines), - reply: stagedReply, - attachment: nil + reply: stagedReply ), toGroup: info.group) stagedReply = nil } - func retry(_ model: GroupChatItem) { - session.retryGroupMessage(model.identity) + func retry(_ model: ChatItem) { +// session.retryGroupMessage(model.identity) } func showRoundFrom(_ roundURL: String?) { @@ -86,18 +89,20 @@ final class GroupChatViewModel { } func getName(from senderId: Data) -> String { - guard let member = info.members.first(where: { $0.userId == senderId }) else { return "You" } - return member.username + fatalError() +// guard let member = info.members.first(where: { $0.userId == senderId }) else { return "You" } +// return member.username } func getText(from messageId: Data) -> String { - session.getTextFromGroupMessage(messageId: messageId) ?? "[DELETED]" + fatalError() +// session.getTextFromGroupMessage(messageId: messageId) ?? "[DELETED]" } - func didRequestReply(_ model: GroupChatItem) { - guard let messageId = model.uniqueId else { return } - - stagedReply = Reply(messageId: messageId, senderId: model.sender) - replySubject.send(.init(text: model.payload.text, sender: getName(from: model.sender))) - } +// func didRequestReply(_ model: GroupChatItem) { +// guard let messageId = model.uniqueId else { return } +// +// stagedReply = Reply(messageId: messageId, senderId: model.sender) +// replySubject.send(.init(text: model.payload.text, sender: getName(from: model.sender))) +// } } diff --git a/Sources/ChatFeature/ViewModels/SingleChatViewModel.swift b/Sources/ChatFeature/ViewModels/SingleChatViewModel.swift index 152472cd4723f240e87b95076aa15ea7c346f0b6..86e7e3f9ec5083f27fbecd8aa6996da99977c5d1 100644 --- a/Sources/ChatFeature/ViewModels/SingleChatViewModel.swift +++ b/Sources/ChatFeature/ViewModels/SingleChatViewModel.swift @@ -61,7 +61,7 @@ final class SingleChatViewModel { if contact.isRecent == true { var contact = contact contact.isRecent = false - session.update(contact) + _ = try? session.dbManager.saveContact(contact) } } @@ -74,13 +74,14 @@ final class SingleChatViewModel { updateRecentState(contact) - session.contacts(.withUserId(contact.userId)) + session.dbManager.fetchContactsPublisher(Contact.Query(id: [contact.id])) + .assertNoFailure() .compactMap { $0.first } .sink { [unowned self] in contactSubject.send($0) } .store(in: &cancellables) - session.singleMessages(contact) - .map { $0.sorted(by: { $0.timestamp < $1.timestamp }) } + session.dbManager.fetchMessagesPublisher(.init(chat: .direct(session.myId, contact.id))) + .assertNoFailure() .map { messages in let domainModels = messages.map { ChatItem($0) } @@ -100,12 +101,12 @@ final class SingleChatViewModel { // MARK: Public func didSendAudio(url: URL) { - let name = url.deletingPathExtension().lastPathComponent - guard let file = FileManager.retrieve(name: name, type: Attachment.Extension.audio.written) else { return } - - let attachment = Attachment(name: name, data: file, _extension: .audio) - let payload = Payload(text: "You sent a voice message", reply: nil, attachment: attachment) - session.send(payload, toContact: contact) +// let name = url.deletingPathExtension().lastPathComponent +// guard let file = FileManager.retrieve(name: name, type: Attachment.Extension.audio.written) else { return } +// +// let attachment = Attachment(name: name, data: file, _extension: .audio) +// let payload = Payload(text: "You sent a voice message", reply: nil, attachment: attachment) +// session.send(payload, toContact: contact) } func didSend(image: UIImage) { @@ -123,11 +124,13 @@ final class SingleChatViewModel { } func readAll() { - session.readAll(from: contact) + let assignment = Message.Assignments(isUnread: false) + let query = Message.Query(chat: .direct(session.myId, contact.id)) + _ = try? session.dbManager.bulkUpdateMessages(query, assignment) } func didRequestDeleteAll() { - session.deleteAll(from: contact) + _ = try? session.dbManager.deleteMessages(.init(chat: .direct(session.myId, contact.id))) } func didRequestRetry(_ model: ChatItem) { @@ -178,21 +181,22 @@ final class SingleChatViewModel { func send(_ string: String) { let text = string.trimmingCharacters(in: .whitespacesAndNewlines) - let payload = Payload(text: text, reply: stagedReply, attachment: nil) + let payload = Payload(text: text, reply: stagedReply) session.send(payload, toContact: contact) stagedReply = nil } func didRequestReply(_ model: ChatItem) { - guard let messageId = model.uniqueId else { return } - - let isIncoming = model.status == .received || model.status == .read - stagedReply = Reply(messageId: messageId, senderId: isIncoming ? contact.userId : session.myId) - replySubject.send(.init(text: model.payload.text, sender: isIncoming ? contact.nickname ?? contact.username : "You")) +// guard let messageId = model.uniqueId else { return } +// +// let isIncoming = model.status == .received +// stagedReply = Reply(messageId: messageId, senderId: isIncoming ? contact.userId : session.myId) +// replySubject.send(.init(text: model.payload.text, sender: isIncoming ? contact.nickname ?? contact.username : "You")) } func getText(from messageId: Data) -> String { - session.getTextFromMessage(messageId: messageId) ?? "[DELETED]" + fatalError() +// session.getTextFromMessage(messageId: messageId) ?? "[DELETED]" } func showRoundFrom(_ roundURL: String?) { @@ -204,7 +208,7 @@ final class SingleChatViewModel { } func didRequestDelete(_ items: [ChatItem]) { - session.delete(messages: items.map { $0.identity }) + ///session.delete(messages: items.map { $0.identity }) } func itemWith(id: Int64) -> ChatItem? { @@ -212,7 +216,7 @@ final class SingleChatViewModel { } func getName(from senderId: Data) -> String { - senderId == session.myId ? "You" : contact.nickname ?? contact.username + senderId == session.myId ? "You" : contact.nickname ?? contact.username! } func itemAt(indexPath: IndexPath) -> ChatItem? { diff --git a/Sources/ContactFeature/Controllers/ContactController.swift b/Sources/ContactFeature/Controllers/ContactController.swift index f7c33bb8ee34b21cd3582f019cd8ac54b90979b1..5dd2b37eb68790365da473f6a5c9e9acbb535a34 100644 --- a/Sources/ContactFeature/Controllers/ContactController.swift +++ b/Sources/ContactFeature/Controllers/ContactController.swift @@ -1,10 +1,11 @@ import HUD -import DrawerFeature import UIKit import Theme import Shared import Models import Combine +import XXModels +import DrawerFeature import DependencyInjection import ScrollViewController @@ -58,7 +59,7 @@ public final class ContactController: UIViewController { ) } - screenView.set(status: viewModel.contact.status) + screenView.set(status: viewModel.contact.authStatus) } private func setupNavigationBar() { @@ -168,7 +169,7 @@ public final class ContactController: UIViewController { .sink { [unowned self] in coordinator.toNickname( from: self, - prefilled: viewModel.contact.nickname ?? viewModel.contact.username, + prefilled: viewModel.contact.nickname ?? viewModel.contact.username!, viewModel.didTapRequest(with:) ) }.store(in: &cancellables) @@ -180,7 +181,7 @@ public final class ContactController: UIViewController { .sink { [unowned self] in coordinator.toNickname( from: self, - prefilled: viewModel.contact.nickname ?? viewModel.contact.username, + prefilled: viewModel.contact.nickname ?? viewModel.contact.username!, viewModel.didTapAccept(_:) ) }.store(in: &cancellables) @@ -242,7 +243,7 @@ public final class ContactController: UIViewController { .sink { [unowned self] in coordinator.toNickname( from: self, - prefilled: viewModel.contact.nickname ?? viewModel.contact.username, + prefilled: viewModel.contact.nickname ?? viewModel.contact.username!, viewModel.didUpdateNickname(_:) ) } diff --git a/Sources/ContactFeature/Coordinator/ContactCoordinator.swift b/Sources/ContactFeature/Coordinator/ContactCoordinator.swift index 2578bf8cb09a4a58f866dabb4135db243e9c43d4..eea7f1ca94bbcace7a637abc5a1d094d53fff74b 100644 --- a/Sources/ContactFeature/Coordinator/ContactCoordinator.swift +++ b/Sources/ContactFeature/Coordinator/ContactCoordinator.swift @@ -1,6 +1,7 @@ import UIKit import Models import Shared +import XXModels import ChatFeature import Presentation diff --git a/Sources/ContactFeature/ViewModels/ContactViewModel.swift b/Sources/ContactFeature/ViewModels/ContactViewModel.swift index fd456e3f46d14f1f352a364ab35b1c4295e88635..67005d4b9d01cbc493ead72d8f6b89e42e64535e 100644 --- a/Sources/ContactFeature/ViewModels/ContactViewModel.swift +++ b/Sources/ContactFeature/ViewModels/ContactViewModel.swift @@ -2,6 +2,7 @@ import HUD import UIKit import Models import Combine +import XXModels import Integration import CombineSchedulers import DependencyInjection @@ -38,8 +39,8 @@ final class ContactViewModel { self.contact = contact do { - let email = try session.extract(fact: .email, from: contact.marshaled) - let phone = try session.extract(fact: .phone, from: contact.marshaled) + let email = try session.extract(fact: .email, from: contact.marshaled!) + let phone = try session.extract(fact: .phone, from: contact.marshaled!) stateRelay.value = .init( title: contact.nickname ?? contact.username, @@ -57,7 +58,7 @@ final class ContactViewModel { func didChoosePhoto(_ photo: UIImage) { stateRelay.value.photo = photo contact.photo = photo.jpegData(compressionQuality: 0.0) - session.update(contact) + _ = try? session.dbManager.saveContact(contact) } func didTapDelete() { @@ -73,18 +74,18 @@ final class ContactViewModel { } func didTapReject() { - session.delete(contact, isRequest: true) + try? session.deleteContact(contact) popRelay.send() } func didTapClear() { - session.deleteAll(from: contact) + _ = try? session.dbManager.deleteMessages(.init(chat: .direct(session.myId, contact.id))) } func didUpdateNickname(_ string: String) { contact.nickname = string.isEmpty ? nil : string stateRelay.value.title = string.isEmpty ? contact.username : string - session.update(contact) + _ = try? session.dbManager.saveContact(contact) stateRelay.value.nickname = contact.nickname } diff --git a/Sources/ContactFeature/Views/ContactInProgressView.swift b/Sources/ContactFeature/Views/ContactInProgressView.swift index b4cbfa038f6abfa9a10a43598566d7dcc65170fc..165600f9deb908c47de78f7e4b71e9213d3d847e 100644 --- a/Sources/ContactFeature/Views/ContactInProgressView.swift +++ b/Sources/ContactFeature/Views/ContactInProgressView.swift @@ -1,6 +1,7 @@ import UIKit import Shared import Models +import XXModels final class ContactAlmostView: UIView { // MARK: UI @@ -19,7 +20,7 @@ final class ContactAlmostView: UIView { // MARK: Public - func set(status: Contact.Status) { + func set(status: Contact.AuthStatus) { switch status { case .requestFailed, .confirmationFailed: feedback.set( diff --git a/Sources/ContactFeature/Views/ContactView.swift b/Sources/ContactFeature/Views/ContactView.swift index 8111a103c021d7c78bb374e54a10bab2d7cbd83c..e5fb03927caf3de9018a382a004b7c89c8a69e99 100644 --- a/Sources/ContactFeature/Views/ContactView.swift +++ b/Sources/ContactFeature/Views/ContactView.swift @@ -1,6 +1,7 @@ import UIKit import Shared import Models +import XXModels final class ContactView: UIView { let container = UIView() @@ -38,7 +39,7 @@ final class ContactView: UIView { required init?(coder: NSCoder) { nil } - func set(status: Contact.Status) { + func set(status: Contact.AuthStatus) { let contentView: UIView switch status { diff --git a/Sources/ContactListFeature/Controllers/ContactListTableController.swift b/Sources/ContactListFeature/Controllers/ContactListTableController.swift index 5ae07f2fa985d5a35ec2d296a63096ab4d3f50e9..8acfc91190671f875f840b82073935505c0f1d8b 100644 --- a/Sources/ContactListFeature/Controllers/ContactListTableController.swift +++ b/Sources/ContactListFeature/Controllers/ContactListTableController.swift @@ -1,7 +1,8 @@ import UIKit import Shared -import Combine import Models +import Combine +import XXModels final class ContactListTableController: UITableViewController { private var collation = UILocalizedIndexedCollation.current() diff --git a/Sources/ContactListFeature/Controllers/CreateGroupController.swift b/Sources/ContactListFeature/Controllers/CreateGroupController.swift index f307813c7f8e3dd959294979f9dfd26183dbccf2..f5a0ebd656b8bd41e216342ccceddd98f3338c00 100644 --- a/Sources/ContactListFeature/Controllers/CreateGroupController.swift +++ b/Sources/ContactListFeature/Controllers/CreateGroupController.swift @@ -3,6 +3,7 @@ import UIKit import Models import Shared import Combine +import XXModels import DependencyInjection public final class CreateGroupController: UIViewController { diff --git a/Sources/ContactListFeature/Coordinator/ContactListCoordinator.swift b/Sources/ContactListFeature/Coordinator/ContactListCoordinator.swift index 6afe76e1aae847c8080d1b5c80795b5884a2c40d..8cf906fa621df4ae1dca668b40df77cd2ae14149 100644 --- a/Sources/ContactListFeature/Coordinator/ContactListCoordinator.swift +++ b/Sources/ContactListFeature/Coordinator/ContactListCoordinator.swift @@ -1,6 +1,7 @@ import UIKit import Shared import Models +import XXModels import MenuFeature import ChatFeature import Presentation diff --git a/Sources/ContactListFeature/ViewModels/ContactListViewModel.swift b/Sources/ContactListFeature/ViewModels/ContactListViewModel.swift index c1d5d1f83114feaef55a898c9bce85623a20f156..72e4582e977fae4f0377923baf099fb754cbcc79 100644 --- a/Sources/ContactListFeature/ViewModels/ContactListViewModel.swift +++ b/Sources/ContactListFeature/ViewModels/ContactListViewModel.swift @@ -1,5 +1,6 @@ import Models import Combine +import XXModels import Integration import DependencyInjection diff --git a/Sources/ContactListFeature/ViewModels/CreateGroupViewModel.swift b/Sources/ContactListFeature/ViewModels/CreateGroupViewModel.swift index 403e8361b163faa018d16e2702f349428df38838..3c421bfe59b5640d00f6f371a3b7a87dd4ba71e1 100644 --- a/Sources/ContactListFeature/ViewModels/CreateGroupViewModel.swift +++ b/Sources/ContactListFeature/ViewModels/CreateGroupViewModel.swift @@ -1,10 +1,11 @@ import HUD -import Combine +import UIKit import Models +import Combine +import XXModels +import Defaults import Integration import DependencyInjection -import Defaults -import UIKit final class CreateGroupViewModel { @KeyObject(.username, defaultValue: "") var username: String diff --git a/Sources/Integration/Implementations/TransferManager.swift b/Sources/Integration/Implementations/TransferManager.swift index 30fe013e648ee1b51d9da740b716553d1bb3972d..cdf6e874a3c9a93864a9cd0b2b98930e6acdf66e 100644 --- a/Sources/Integration/Implementations/TransferManager.swift +++ b/Sources/Integration/Implementations/TransferManager.swift @@ -38,24 +38,24 @@ extension BindingsFileTransfer: TransferManagerInterface { try receive(id) } - public func uploadFile( - _ file: Attachment, - to recipient: Data, - _ callback: @escaping (Bool, Int, Int, Int, Error?) -> Void - ) throws -> Data { - let cb = OutgoingTransferProgressCallback { completed, sent, arrived, total, error in - callback(completed, sent, arrived, total, error) - } - - return try send( - file.name, - fileType: file._extension.written, - fileData: file.data!, - recipientID: recipient, - retry: 1, - preview: nil, - progressFunc: cb, - periodMS: 1000 - ) - } +// public func uploadFile( +// _ file: Attachment, +// to recipient: Data, +// _ callback: @escaping (Bool, Int, Int, Int, Error?) -> Void +// ) throws -> Data { +// let cb = OutgoingTransferProgressCallback { completed, sent, arrived, total, error in +// callback(completed, sent, arrived, total, error) +// } +// +// return try send( +// file.name, +// fileType: file._extension.written, +// fileData: file.data!, +// recipientID: recipient, +// retry: 1, +// preview: nil, +// progressFunc: cb, +// periodMS: 1000 +// ) +// } } diff --git a/Sources/Integration/Interfaces/TransferManagerInterface.swift b/Sources/Integration/Interfaces/TransferManagerInterface.swift index 294dd0ab32ce27d4fed3367002fdce45dadbedd9..6c0fa09d5dc83d1343c111cc9f305a26a4be56a0 100644 --- a/Sources/Integration/Interfaces/TransferManagerInterface.swift +++ b/Sources/Integration/Interfaces/TransferManagerInterface.swift @@ -20,10 +20,9 @@ public protocol TransferManagerInterface { with: Data ) throws -> Data - - func uploadFile( - _: Attachment, - to: Data, - _: @escaping (Bool, Int, Int, Int, Error?) -> Void - ) throws -> Data +// func uploadFile( +// _: Attachment, +// to: Data, +// _: @escaping (Bool, Int, Int, Int, Error?) -> Void +// ) throws -> Data } diff --git a/Sources/Integration/Mocks/TransferManagerMock.swift b/Sources/Integration/Mocks/TransferManagerMock.swift index cd4b812e4cbfeed4f197585b3921e764f665e7c9..ccf16c1bc7d92fe480d673f50728f9070f1a6525 100644 --- a/Sources/Integration/Mocks/TransferManagerMock.swift +++ b/Sources/Integration/Mocks/TransferManagerMock.swift @@ -24,11 +24,11 @@ final class TransferManagerMock: TransferManagerInterface { fatalError() } - func uploadFile( - _: Attachment, - to: Data, - _: @escaping (Bool, Int, Int, Int, Error?) -> Void - ) throws -> Data { - Data() - } +// func uploadFile( +// _: Attachment, +// to: Data, +// _: @escaping (Bool, Int, Int, Int, Error?) -> Void +// ) throws -> Data { +// Data() +// } } diff --git a/Sources/Integration/Session/Session+Chat.swift b/Sources/Integration/Session/Session+Chat.swift index a76c9b3fcba6045728d6948a7909c5c176d27c52..1d60e9f228bb97174645854d81a9f3057de9af14 100644 --- a/Sources/Integration/Session/Session+Chat.swift +++ b/Sources/Integration/Session/Session+Chat.swift @@ -6,24 +6,24 @@ import Foundation extension Session { public func send(imageData: Data, to contact: Contact, completion: @escaping (Result<Void, Error>) -> Void) { - client.bindings.compress(image: imageData) { [weak self] in - guard let self = self else { - completion(.success(())) - return - } - - switch $0 { - case .success(let compressed): - let name = "image_\(Date.asTimestamp)" - try! FileManager.store(data: compressed, name: name, type: Attachment.Extension.image.written) - let attachment = Attachment(name: name, data: compressed, _extension: .image) - self.send(Payload(text: "You sent an image", reply: nil, attachment: attachment), toContact: contact) - completion(.success(())) - case .failure(let error): - completion(.failure(error)) - log(string: "Error when compressing image: \(error.localizedDescription)", type: .error) - } - } +// client.bindings.compress(image: imageData) { [weak self] in +// guard let self = self else { +// completion(.success(())) +// return +// } +// +// switch $0 { +// case .success(let compressed): +// let name = "image_\(Date.asTimestamp)" +// try! FileManager.store(data: compressed, name: name, type: Attachment.Extension.image.written) +// let attachment = Attachment(name: name, data: compressed, _extension: .image) +// self.send(Payload(text: "You sent an image", reply: nil, attachment: attachment), toContact: contact) +// completion(.success(())) +// case .failure(let error): +// completion(.failure(error)) +// log(string: "Error when compressing image: \(error.localizedDescription)", type: .error) +// } +// } } public func send(_ payload: Payload, toContact contact: Contact) { diff --git a/Sources/Models/Attachment.swift b/Sources/Models/Attachment.swift deleted file mode 100644 index 92eb3c09dcf4a7846bf5e265ec5a5080342a69b1..0000000000000000000000000000000000000000 --- a/Sources/Models/Attachment.swift +++ /dev/null @@ -1,49 +0,0 @@ -import Foundation - -public struct Attachment: Codable, Equatable, Hashable { - - public enum Extension: Int64, Codable, CaseIterable { - case image - case audio - - public static func from(_ string: String) -> Extension? { - self.allCases.first{ $0.written == string } - } - - public var written: String { - switch self { - case .image: - return "jpeg" - case .audio: - return "m4a" - } - } - - public var writtenExtended: String { - switch self { - case .image: - return "image" - case .audio: - return "voice message" - } - } - } - - public let data: Data? - public let name: String - public var transferId: Data? - public let _extension: Extension - public var progress: Float = 0.0 - - public init( - name: String, - data: Data? = nil, - transferId: Data? = nil, - _extension: Extension - ) { - self.data = data - self.name = name - self._extension = _extension - self.transferId = transferId - } -} diff --git a/Sources/Models/Contact.swift b/Sources/Models/Contact.swift deleted file mode 100644 index c0ca99ed09353cfc8afc8d9e4fa0ee0c0b62a088..0000000000000000000000000000000000000000 --- a/Sources/Models/Contact.swift +++ /dev/null @@ -1,123 +0,0 @@ -import UIKit -import DifferenceKit - -public protocol IndexableItem { - var indexedOn: NSString { get } -} - -public class IndexedListCollator<Item: IndexableItem> { - private final class CollationWrapper: NSObject { - let value: Any - @objc let indexedOn: NSString - - init(value: Any, indexedOn: NSString) { - self.value = value - self.indexedOn = indexedOn - } - - func unwrappedValue<UnwrappedType>() -> UnwrappedType { - return value as! UnwrappedType - } - } - - public init() {} - - public func sectioned(items: [Item]) -> (sections: [[Item]], collation: UILocalizedIndexedCollation) { - let collation = UILocalizedIndexedCollation.current() - let selector = #selector(getter: CollationWrapper.indexedOn) - - let wrappedItems = items.map { item in - CollationWrapper(value: item, indexedOn: item.indexedOn) - } - - let sortedObjects = collation.sortedArray(from: wrappedItems, collationStringSelector: selector) as! [CollationWrapper] - - var sections = collation.sectionIndexTitles.map { _ in [Item]() } - sortedObjects.forEach { item in - let sectionNumber = collation.section(for: item, collationStringSelector: selector) - sections[sectionNumber].append(item.unwrappedValue()) - } - - return (sections: sections.filter { !$0.isEmpty }, collation: collation) - } -} -// -//public struct Contact: Codable, Hashable, Equatable { -// public enum Request { -// case all -// case failed -// case friends -// case received -// case requested -// case isRecent -// case verificationInProgress -// case withUserId(Data) -// case withUserIds([Data]) -// case withUsername(String) -// } -// -// public enum Status: Int64, Codable { -// case friend -// case stranger -// case verified -// case verificationFailed -// case verificationInProgress -// case requested -// case requesting -// case requestFailed -// case confirming -// case confirmationFailed -// case hidden -// } -// -// public var id: Int64? -// public var photo: Data? -// public let userId: Data -// public var email: String? -// public var phone: String? -// public var status: Status -// public var marshaled: Data -// public var createdAt: Date -// public let username: String -// public var nickname: String? -// public var isRecent: Bool -// -// public init( -// photo: Data?, -// userId: Data, -// email: String?, -// phone: String?, -// status: Status, -// marshaled: Data, -// username: String, -// nickname: String?, -// createdAt: Date, -// isRecent: Bool -// ) { -// self.email = email -// self.phone = phone -// self.photo = photo -// self.status = status -// self.userId = userId -// self.username = username -// self.nickname = nickname -// self.marshaled = marshaled -// self.createdAt = createdAt -// self.isRecent = isRecent -// } -// -// public var differenceIdentifier: Data { userId } -// -// public static var databaseTableName: String { "contacts" } -//} -// -//extension Contact: Differentiable {} -//extension Contact: IndexableItem { -// public var indexedOn: NSString { -// guard let nickname = nickname else { -// return "\(username.first!)" as NSString -// } -// -// return "\(nickname.first!)" as NSString -// } -//} diff --git a/Sources/Models/FileTransfer.swift b/Sources/Models/FileTransfer.swift deleted file mode 100644 index 79fac5d1b0db78480c766a546b695ad426c8ae68..0000000000000000000000000000000000000000 --- a/Sources/Models/FileTransfer.swift +++ /dev/null @@ -1,37 +0,0 @@ -import Foundation - -public struct FileTransfer { - public enum Request { - case withTID(Data) - case withContactId(Data) - } - - public var tid: Data - public var id: Int64? - public var contact: Data - public var fileName: String - public var fileType: String - public var isIncoming: Bool - - public static var databaseTableName: String { "transfers" } - - public init( - id: Int64? = nil, - tid: Data, - contact: Data, - fileName: String, - fileType: String, - isIncoming: Bool - ) { - self.id = id - self.tid = tid - self.contact = contact - self.fileName = fileName - self.fileType = fileType - self.isIncoming = isIncoming - } -} - -extension FileTransfer: Codable {} -extension FileTransfer: Hashable {} -extension FileTransfer: Equatable {} diff --git a/Sources/Models/Group.swift b/Sources/Models/Group.swift deleted file mode 100644 index 20634688bc4312cfae2f1daaf708889f799803ce..0000000000000000000000000000000000000000 --- a/Sources/Models/Group.swift +++ /dev/null @@ -1,42 +0,0 @@ -//import Foundation -//import KeychainAccess -// -//public struct Group: Codable, Equatable, Hashable { -// public enum Status: Int64, Codable { -// case hidden -// case pending -// case deleting -// case participating -// } -// -// public enum Request { -// case pending -// case accepted -// case withGroupId(Data) -// } -// -// public var id: Int64? -// public var name: String -// public var leader: Data -// public var groupId: Data -// public var status: Status -// public var serialize: Data -// public var createdAt: Date -// public static var databaseTableName: String { "groups" } -// -// public init( -// leader: Data, -// name: String, -// groupId: Data, -// status: Status, -// createdAt: Date, -// serialize: Data -// ) { -// self.name = name -// self.leader = leader -// self.status = status -// self.groupId = groupId -// self.createdAt = createdAt -// self.serialize = serialize -// } -//} diff --git a/Sources/Models/GroupChatInfo.swift b/Sources/Models/GroupChatInfo.swift deleted file mode 100644 index e2d56675a3916b956b1249a3935ebccf9acbddee..0000000000000000000000000000000000000000 --- a/Sources/Models/GroupChatInfo.swift +++ /dev/null @@ -1,22 +0,0 @@ -//import Foundation -// -//public struct GroupChatInfo: Codable, Equatable, Hashable { -// public enum Request { -// case accepted -// case fromGroup(Data) -// } -// -// public var group: Group -// public var members: [GroupMember] -// public var lastMessage: GroupMessage? -// -// public init( -// group: Group, -// members: [GroupMember], -// lastMessage: GroupMessage? = nil -// ) { -// self.group = group -// self.members = members -// self.lastMessage = lastMessage -// } -//} diff --git a/Sources/Models/GroupMember.swift b/Sources/Models/GroupMember.swift deleted file mode 100644 index c0f7123f1eb0a0772a82bce3501ea6d25e4ed44f..0000000000000000000000000000000000000000 --- a/Sources/Models/GroupMember.swift +++ /dev/null @@ -1,42 +0,0 @@ -//import Foundation -// -//public struct GroupMember { -// public enum Request { -// case all -// case strangers -// case fromGroup(Data) -// case withUserId(Data) -// } -// -// public enum Status: Int64, Codable { -// case usernameSet -// case pendingUsername -// } -// -// public var id: Int64? -// public var userId: Data -// public var groupId: Data -// public var status: Status -// public var username: String -// public var photo: Data? -// -// public init( -// id: Int64? = nil, -// userId: Data, -// groupId: Data, -// status: Status, -// username: String, -// photo: Data? = nil -// ) { -// self.id = id -// self.userId = userId -// self.groupId = groupId -// self.username = username -// self.status = status -// self.photo = photo -// } -//} -// -//extension GroupMember: Codable {} -//extension GroupMember: Hashable {} -//extension GroupMember: Equatable {} diff --git a/Sources/Models/GroupMessage.swift b/Sources/Models/GroupMessage.swift deleted file mode 100644 index 771d4ceb8d60fa9bb68ef1dac1e0f7c8a1143d43..0000000000000000000000000000000000000000 --- a/Sources/Models/GroupMessage.swift +++ /dev/null @@ -1,56 +0,0 @@ -//import Foundation -// -//public struct GroupMessage: Codable, Equatable, Hashable { -// public enum Request { -// case withUniqueId(Data) -// case id(Int64) -// case sending -// case fromGroup(Data) -// case unreadsFromGroup(Data) -// } -// -// public static var databaseTableName: String { "groupMessages" } -// -// public enum Status: Int64, Codable { -// case sent -// case read -// case failed -// case sending -// case received -// } -// -// public var id: Int64? -// public var uniqueId: Data? -// public var groupId: Data -// public var sender: Data -// public var roundId: Int64? -// public var payload: Payload -// public var status: Status -// public var roundURL: String? -// public var unread: Bool -// public var timestamp: Int -// -// public init( -// id: Int64? = nil, -// sender: Data, -// groupId: Data, -// payload: Payload, -// unread: Bool, -// timestamp: Int = 0, -// uniqueId: Data?, -// status: Status, -// roundId: Int64? = nil, -// roundURL: String? = nil -// ) { -// self.id = id -// self.sender = sender -// self.groupId = groupId -// self.payload = payload -// self.unread = unread -// self.timestamp = timestamp -// self.uniqueId = uniqueId -// self.status = status -// self.roundId = roundId -// self.roundURL = roundURL -// } -//} diff --git a/Sources/Models/Message.swift b/Sources/Models/Message.swift deleted file mode 100644 index 77ae89db3d0db660d62398ea67934339dacc7487..0000000000000000000000000000000000000000 --- a/Sources/Models/Message.swift +++ /dev/null @@ -1,70 +0,0 @@ -//import Foundation -//import DifferenceKit -// -//public struct Message: Codable, Equatable, Hashable { -// public enum Request { -// case sending -// case withUniqueId(Data) -// case withId(Int64) -// case sendingAttachment -// case withContact(Data) -// case unreadsFromContactId(Data) -// case latestOnesFromContactIds([Data]) -// } -// -// public enum Status: Int64, Codable { -// case read -// case sent -// case sending -// case sendingAttachment -// case receivingAttachment -// case received -// case failedToSend -// case timedOut -// } -// -// public var id: Int64? -// public var unread: Bool -// public let sender: Data -// public var roundURL: String? -// public var report: Data? -// public var status: Status -// public let receiver: Data -// public var timestamp: Int -// public var uniqueId: Data? -// public var payload: Payload -// public static var databaseTableName: String { "messages" } -// -// public init ( -// sender: Data, -// receiver: Data, -// payload: Payload, -// unread: Bool, -// timestamp: Int, -// uniqueId: Data?, -// status: Status, -// roundURL: String? = nil -// ) { -// self.sender = sender -// self.unread = unread -// self.status = status -// self.payload = payload -// self.receiver = receiver -// self.uniqueId = uniqueId -// self.timestamp = timestamp -// self.roundURL = roundURL -// } -//} -// -//public extension Message.Status { -// var canReply: Bool { -// switch self { -// case .sent, .received, .read: -// return true -// default: -// return false -// } -// } -//} -// -//extension Message: Differentiable {} diff --git a/Sources/Models/Payload.swift b/Sources/Models/Payload.swift index 4fda3b78993e049b6ef7d6986411fac1ba161f34..8e6f519b5ca09531b97961d1616a63477325d002 100644 --- a/Sources/Models/Payload.swift +++ b/Sources/Models/Payload.swift @@ -3,12 +3,10 @@ import Foundation public struct Payload: Codable, Equatable, Hashable { public var text: String public var reply: Reply? - public var attachment: Attachment? - public init(text: String, reply: Reply?, attachment: Attachment?) { + public init(text: String, reply: Reply?) { self.text = text self.reply = reply - self.attachment = attachment } public init(with marshaled: Data) throws { @@ -23,7 +21,7 @@ public struct Payload: Codable, Equatable, Hashable { ) } - self.init(text: proto.text, reply: reply, attachment: nil) + self.init(text: proto.text, reply: reply) } public func asData() -> Data { diff --git a/Sources/Models/SingleChatInfo.swift b/Sources/Models/SingleChatInfo.swift deleted file mode 100644 index 79fc4ba0766c688464b17b1a9f71924ef0423c70..0000000000000000000000000000000000000000 --- a/Sources/Models/SingleChatInfo.swift +++ /dev/null @@ -1,18 +0,0 @@ -//import Foundation -// -//public struct SingleChatInfo: Codable, Equatable, Hashable { -// public enum Request { -// case all -// } -// -// public var contact: Contact -// public var lastMessage: Message? -// -// public init( -// contact: Contact, -// lastMessage: Message? -// ) { -// self.contact = contact -// self.lastMessage = lastMessage -// } -//} diff --git a/Sources/RequestsFeature/Coordinator/RequestsCoordinator.swift b/Sources/RequestsFeature/Coordinator/RequestsCoordinator.swift index 4d28c6bf7fc7c8f482a8b69809b679ec9b447816..9a570bc2172552dbed46a50181c0fac862e940cd 100644 --- a/Sources/RequestsFeature/Coordinator/RequestsCoordinator.swift +++ b/Sources/RequestsFeature/Coordinator/RequestsCoordinator.swift @@ -1,6 +1,7 @@ import UIKit import Shared import Models +import XXModels import MenuFeature import Presentation import ContactFeature diff --git a/Sources/RequestsFeature/Models/Request.swift b/Sources/RequestsFeature/Models/Request.swift index 2bf872c5a02815db8be3eb0adf92790328bf0082..097b55b85aa4e2efe4a106c44e2efda30a5dc9d6 100644 --- a/Sources/RequestsFeature/Models/Request.swift +++ b/Sources/RequestsFeature/Models/Request.swift @@ -1,4 +1,5 @@ import Models +import XXModels import Foundation enum Section: Int { @@ -22,9 +23,9 @@ enum Request: Hashable, Equatable { var id: Data { switch self { case .group(let group): - return group.groupId + return group.id case .contact(let contact): - return contact.userId + return contact.id } } } diff --git a/Sources/RequestsFeature/ViewModels/RequestsReceivedViewModel.swift b/Sources/RequestsFeature/ViewModels/RequestsReceivedViewModel.swift index 35c2589da673e10cbc6dac35eec8771ead6a309b..e231498dbb03f04bb7ae1267fb9884d556f1f8a4 100644 --- a/Sources/RequestsFeature/ViewModels/RequestsReceivedViewModel.swift +++ b/Sources/RequestsFeature/ViewModels/RequestsReceivedViewModel.swift @@ -4,8 +4,9 @@ import Models import Shared import Combine import Defaults -import DrawerFeature +import XXModels import Integration +import DrawerFeature import CombineSchedulers import DependencyInjection diff --git a/Sources/ScanFeature/Coordinator/ScanCoordinator.swift b/Sources/ScanFeature/Coordinator/ScanCoordinator.swift index 6a36db1801d9874f687a3eef13dabf657125f943..98e605775ccdf54b5ffda4b0a6ad0183a5c15fe1 100644 --- a/Sources/ScanFeature/Coordinator/ScanCoordinator.swift +++ b/Sources/ScanFeature/Coordinator/ScanCoordinator.swift @@ -1,5 +1,6 @@ import UIKit import Models +import XXModels import MenuFeature import Presentation import ContactFeature diff --git a/Sources/ScanFeature/ViewModels/ScanViewModel.swift b/Sources/ScanFeature/ViewModels/ScanViewModel.swift index c3f51841e1726407c69f47de05f15570f01c438f..e91c94dad4b19f4cdfd3f408712f41df02b6705d 100644 --- a/Sources/ScanFeature/ViewModels/ScanViewModel.swift +++ b/Sources/ScanFeature/ViewModels/ScanViewModel.swift @@ -1,6 +1,7 @@ import Shared import Models import Combine +import XXModels import Foundation import Integration import CombineSchedulers diff --git a/Sources/SearchFeature/Controllers/SearchController.swift b/Sources/SearchFeature/Controllers/SearchController.swift index e192abf4d0005e266d2e3f609067dd52278cb16f..76d823020b0f4b7e5a595a3529cc6bcd3898745b 100644 --- a/Sources/SearchFeature/Controllers/SearchController.swift +++ b/Sources/SearchFeature/Controllers/SearchController.swift @@ -2,13 +2,14 @@ import HUD import Theme import UIKit import Shared -import Combine -import DependencyInjection -import ScrollViewController -import DrawerFeature import Models +import Combine import Defaults +import XXModels import Countries +import DrawerFeature +import DependencyInjection +import ScrollViewController public final class SearchController: UIViewController { @KeyObject(.email, defaultValue: nil) var email: String? diff --git a/Sources/SearchFeature/Coordinator/SearchCoordinator.swift b/Sources/SearchFeature/Coordinator/SearchCoordinator.swift index c9a831c2c6be45b36e23d7329ad26b282f9b539e..2f66a6228fd96bb156266a80c16a9ac5ba53c5d9 100644 --- a/Sources/SearchFeature/Coordinator/SearchCoordinator.swift +++ b/Sources/SearchFeature/Coordinator/SearchCoordinator.swift @@ -1,5 +1,6 @@ import UIKit import Models +import XXModels import Countries import Presentation import ScrollViewController diff --git a/Sources/SearchFeature/ViewModels/SearchViewModel.swift b/Sources/SearchFeature/ViewModels/SearchViewModel.swift index 7702558f24ae08d75c701ef4203a52fe1607574f..2ff6fccc9c6a30f3ce88935c18cc364433a4a902 100644 --- a/Sources/SearchFeature/ViewModels/SearchViewModel.swift +++ b/Sources/SearchFeature/ViewModels/SearchViewModel.swift @@ -3,6 +3,7 @@ import UIKit import Models import Combine import Defaults +import XXModels import Countries import Foundation import Integration diff --git a/client-ios.xcworkspace/xcshareddata/swiftpm/Package.resolved b/client-ios.xcworkspace/xcshareddata/swiftpm/Package.resolved index 435cb2c1f5f93033e366ee014b39cb6643fdf95b..5bc88ee5e470debf44db9efb1bee13633af7b610 100644 --- a/client-ios.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/client-ios.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -50,7 +50,7 @@ "kind" : "remoteSourceControl", "location" : "https://git.xx.network/elixxir/client-ios-db.git", "state" : { - "revision" : "2e51a960216340cf12a4c6cbc7ac18a9f07d5ee6" + "revision" : "452392e032d52b217a20cd0f9de3b289f12fe444" } }, {