From ffea61917d0507b3ded5ff61ad71b369eab39337 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Mon, 27 Mar 2023 21:02:54 +0400 Subject: [PATCH] ios: display rcv & snd files progress (#2085) * ios: display rcv & snd files progress * remove animation --- apps/ios/Shared/Model/SimpleXAPI.swift | 4 +++ .../Views/Chat/ChatItem/CIFileView.swift | 27 ++++++++++++++----- apps/ios/SimpleXChat/APITypes.swift | 6 +++++ apps/ios/SimpleXChat/ChatTypes.swift | 4 +-- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 9c23b01cf..7c22e69e1 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -1321,6 +1321,8 @@ func processReceivedMsg(_ res: ChatResponse) async { chatItemSimpleUpdate(user, aChatItem) case let .rcvFileComplete(user, aChatItem): chatItemSimpleUpdate(user, aChatItem) + case let .rcvFileProgressXFTP(user, aChatItem, _, _): + chatItemSimpleUpdate(user, aChatItem) case let .sndFileStart(user, aChatItem, _): chatItemSimpleUpdate(user, aChatItem) case let .sndFileComplete(user, aChatItem, _): @@ -1332,6 +1334,8 @@ func processReceivedMsg(_ res: ChatResponse) async { let fileName = cItem.file?.filePath { removeFile(fileName) } + case let .sndFileProgressXFTP(user, aChatItem, _, _, _): + chatItemSimpleUpdate(user, aChatItem) case let .callInvitation(invitation): m.callInvitations[invitation.contact.id] = invitation activateCall(invitation) diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift index 08170f825..1445ea3df 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift @@ -48,7 +48,7 @@ struct CIFileView: View { .disabled(!itemInteractive) } - var itemInteractive: Bool { + private var itemInteractive: Bool { if let file = file { switch (file.fileStatus) { case .sndStored: return false @@ -65,14 +65,14 @@ struct CIFileView: View { return false } - func fileSizeValid() -> Bool { + private func fileSizeValid() -> Bool { if let file = file { return file.fileSize <= MAX_FILE_SIZE } return false } - func fileAction() { + private func fileAction() { logger.debug("CIFileView fileAction") if let file = file { switch (file.fileStatus) { @@ -107,11 +107,12 @@ struct CIFileView: View { } } - @ViewBuilder func fileIndicator() -> some View { + @ViewBuilder private func fileIndicator() -> some View { if let file = file { switch file.fileStatus { case .sndStored: fileIcon("doc.fill") - case .sndTransfer: ProgressView().frame(width: 30, height: 30) + // case .sndTransfer: ProgressView().frame(width: 30, height: 30) // TODO use for SMP files + case let .sndTransfer(sndProgress, sndTotal): progressCircle(sndProgress, sndTotal) case .sndComplete: fileIcon("doc.fill", innerIcon: "checkmark", innerIconSize: 10) case .sndCancelled: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10) case .rcvInvitation: @@ -121,7 +122,8 @@ struct CIFileView: View { fileIcon("doc.fill", color: .orange, innerIcon: "exclamationmark", innerIconSize: 12) } case .rcvAccepted: fileIcon("doc.fill", innerIcon: "ellipsis", innerIconSize: 12) - case .rcvTransfer: ProgressView().frame(width: 30, height: 30) + // case .rcvTransfer: ProgressView().frame(width: 30, height: 30) // TODO use for SMP files + case let .rcvTransfer(rcvProgress, rcvTotal): progressCircle(rcvProgress, rcvTotal) case .rcvComplete: fileIcon("doc.fill") case .rcvCancelled: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10) } @@ -130,7 +132,7 @@ struct CIFileView: View { } } - func fileIcon(_ icon: String, color: Color = Color(uiColor: .tertiaryLabel), innerIcon: String? = nil, innerIconSize: CGFloat? = nil) -> some View { + private func fileIcon(_ icon: String, color: Color = Color(uiColor: .tertiaryLabel), innerIcon: String? = nil, innerIconSize: CGFloat? = nil) -> some View { ZStack(alignment: .center) { Image(systemName: icon) .resizable() @@ -149,6 +151,17 @@ struct CIFileView: View { } } } + + private func progressCircle(_ progress: Int64, _ total: Int64) -> some View { + Circle() + .trim(from: 0, to: Double(progress) / Double(total)) + .stroke( + Color.accentColor, + style: StrokeStyle(lineWidth: 3) + ) + .rotationEffect(.degrees(-90)) + .frame(width: 30, height: 30) + } } struct CIFileView_Previews: PreviewProvider { diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index defc069a7..0f9dffedb 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -451,6 +451,7 @@ public enum ChatResponse: Decodable, Error { case rcvFileAccepted(user: User, chatItem: AChatItem) case rcvFileAcceptedSndCancelled(user: User, rcvFileTransfer: RcvFileTransfer) case rcvFileStart(user: User, chatItem: AChatItem) + case rcvFileProgressXFTP(user: User, chatItem: AChatItem, receivedSize: Int64, totalSize: Int64) case rcvFileComplete(user: User, chatItem: AChatItem) // sending file events case sndFileStart(user: User, chatItem: AChatItem, sndFileTransfer: SndFileTransfer) @@ -458,6 +459,7 @@ public enum ChatResponse: Decodable, Error { case sndFileCancelled(chatItem: AChatItem, sndFileTransfer: SndFileTransfer) case sndFileRcvCancelled(user: User, chatItem: AChatItem, sndFileTransfer: SndFileTransfer) case sndGroupFileCancelled(user: User, chatItem: AChatItem, fileTransferMeta: FileTransferMeta, sndFileTransfers: [SndFileTransfer]) + case sndFileProgressXFTP(user: User, chatItem: AChatItem, fileTransferMeta: FileTransferMeta, sentSize: Int64, totalSize: Int64) case callInvitation(callInvitation: RcvCallInvitation) case callOffer(user: User, contact: Contact, callType: CallType, offer: WebRTCSession, sharedKey: String?, askConfirmation: Bool) case callAnswer(user: User, contact: Contact, answer: WebRTCSession) @@ -558,12 +560,14 @@ public enum ChatResponse: Decodable, Error { case .rcvFileAccepted: return "rcvFileAccepted" case .rcvFileAcceptedSndCancelled: return "rcvFileAcceptedSndCancelled" case .rcvFileStart: return "rcvFileStart" + case .rcvFileProgressXFTP: return "rcvFileProgressXFTP" case .rcvFileComplete: return "rcvFileComplete" case .sndFileStart: return "sndFileStart" case .sndFileComplete: return "sndFileComplete" case .sndFileCancelled: return "sndFileCancelled" case .sndFileRcvCancelled: return "sndFileRcvCancelled" case .sndGroupFileCancelled: return "sndGroupFileCancelled" + case .sndFileProgressXFTP: return "sndFileProgressXFTP" case .callInvitation: return "callInvitation" case .callOffer: return "callOffer" case .callAnswer: return "callAnswer" @@ -667,12 +671,14 @@ public enum ChatResponse: Decodable, Error { case let .rcvFileAccepted(u, chatItem): return withUser(u, String(describing: chatItem)) case .rcvFileAcceptedSndCancelled: return noDetails case let .rcvFileStart(u, chatItem): return withUser(u, String(describing: chatItem)) + case let .rcvFileProgressXFTP(u, chatItem, receivedSize, totalSize): return withUser(u, "chatItem: \(String(describing: chatItem))\nreceivedSize: \(receivedSize)\ntotalSize: \(totalSize)") case let .rcvFileComplete(u, chatItem): return withUser(u, String(describing: chatItem)) case let .sndFileStart(u, chatItem, _): return withUser(u, String(describing: chatItem)) case let .sndFileComplete(u, chatItem, _): return withUser(u, String(describing: chatItem)) case let .sndFileCancelled(chatItem, _): return String(describing: chatItem) case let .sndFileRcvCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem)) case let .sndGroupFileCancelled(u, chatItem, _, _): return withUser(u, String(describing: chatItem)) + case let .sndFileProgressXFTP(u, chatItem, _, sentSize, totalSize): return withUser(u, "chatItem: \(String(describing: chatItem))\nsentSize: \(sentSize)\ntotalSize: \(totalSize)") case let .callInvitation(inv): return String(describing: inv) case let .callOffer(u, contact, callType, offer, sharedKey, askConfirmation): return withUser(u, "contact: \(contact.id)\ncallType: \(String(describing: callType))\nsharedKey: \(sharedKey ?? "")\naskConfirmation: \(askConfirmation)\noffer: \(String(describing: offer))") case let .callAnswer(u, contact, answer): return withUser(u, "contact: \(contact.id)\nanswer: \(String(describing: answer))") diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index bcd033f67..d58347e31 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -2239,12 +2239,12 @@ public struct CIFile: Decodable { public enum CIFileStatus: Decodable { case sndStored - case sndTransfer(sndProgress: Int, sndTotal: Int) + case sndTransfer(sndProgress: Int64, sndTotal: Int64) case sndComplete case sndCancelled case rcvInvitation case rcvAccepted - case rcvTransfer(rcvProgress: Int, rcvTotal: Int) + case rcvTransfer(rcvProgress: Int64, rcvTotal: Int64) case rcvComplete case rcvCancelled