core, mobile: file error statuses, cancel sent file (#2193)

This commit is contained in:
spaced4ndy
2023-04-18 12:48:36 +04:00
committed by GitHub
parent 6913bf1a46
commit 09481e09b6
22 changed files with 445 additions and 177 deletions
+6
View File
@@ -1333,6 +1333,9 @@ func processReceivedMsg(_ res: ChatResponse) async {
cleanupFile(aChatItem)
case let .rcvFileProgressXFTP(user, aChatItem, _, _):
chatItemSimpleUpdate(user, aChatItem)
case let .rcvFileError(user, aChatItem):
chatItemSimpleUpdate(user, aChatItem)
cleanupFile(aChatItem)
case let .sndFileStart(user, aChatItem, _):
chatItemSimpleUpdate(user, aChatItem)
case let .sndFileComplete(user, aChatItem, _):
@@ -1346,6 +1349,9 @@ func processReceivedMsg(_ res: ChatResponse) async {
case let .sndFileCompleteXFTP(user, aChatItem, _):
chatItemSimpleUpdate(user, aChatItem)
cleanupFile(aChatItem)
case let .sndFileError(user, aChatItem):
chatItemSimpleUpdate(user, aChatItem)
cleanupFile(aChatItem)
case let .callInvitation(invitation):
m.callInvitations[invitation.contact.id] = invitation
activateCall(invitation)
@@ -55,11 +55,13 @@ struct CIFileView: View {
case .sndTransfer: return false
case .sndComplete: return false
case .sndCancelled: return false
case .sndError: return false
case .rcvInvitation: return true
case .rcvAccepted: return true
case .rcvTransfer: return false
case .rcvComplete: return true
case .rcvCancelled: return false
case .rcvError: return false
}
}
return false
@@ -130,6 +132,7 @@ struct CIFileView: View {
}
case .sndComplete: fileIcon("doc.fill", innerIcon: "checkmark", innerIconSize: 10)
case .sndCancelled: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
case .sndError: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
case .rcvInvitation:
if fileSizeValid() {
fileIcon("arrow.down.doc.fill", color: .accentColor)
@@ -145,6 +148,7 @@ struct CIFileView: View {
}
case .rcvComplete: fileIcon("doc.fill")
case .rcvCancelled: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
case .rcvError: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
}
} else {
fileIcon("doc.fill")
@@ -93,10 +93,12 @@ struct CIImageView: View {
case .sndTransfer: progressView()
case .sndComplete: fileIcon("checkmark", 10, 13)
case .sndCancelled: fileIcon("xmark", 10, 13)
case .sndError: fileIcon("xmark", 10, 13)
case .rcvInvitation: fileIcon("arrow.down", 10, 13)
case .rcvAccepted: fileIcon("ellipsis", 14, 11)
case .rcvTransfer: progressView()
case .rcvCancelled: fileIcon("xmark", 10, 13)
case .rcvError: fileIcon("xmark", 10, 13)
default: EmptyView()
}
}
@@ -199,38 +199,33 @@ struct CIVideoView: View {
case .xftp: progressCircle(sndProgress, sndTotal)
case .smp: progressView()
}
case .sndComplete:
Image(systemName: "checkmark")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 10, height: 10)
.foregroundColor(.white)
.padding(13)
case .rcvInvitation:
Image(systemName: "arrow.down")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 14, height: 14)
.foregroundColor(.white)
.padding(11)
case .rcvAccepted:
Image(systemName: "ellipsis")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 14, height: 14)
.foregroundColor(.white)
.padding(11)
case .sndComplete: fileIcon("checkmark", 10, 13)
case .sndCancelled: fileIcon("xmark", 10, 13)
case .sndError: fileIcon("xmark", 10, 13)
case .rcvInvitation: fileIcon("arrow.down", 10, 13)
case .rcvAccepted: fileIcon("ellipsis", 14, 11)
case let .rcvTransfer(rcvProgress, rcvTotal):
if file.fileProtocol == .xftp && rcvProgress < rcvTotal {
progressCircle(rcvProgress, rcvTotal)
} else {
progressView()
}
case .rcvCancelled: fileIcon("xmark", 10, 13)
case .rcvError: fileIcon("xmark", 10, 13)
default: EmptyView()
}
}
}
private func fileIcon(_ icon: String, _ size: CGFloat, _ padding: CGFloat) -> some View {
Image(systemName: icon)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: size, height: size)
.foregroundColor(.white)
.padding(padding)
}
private func progressView() -> some View {
ProgressView()
.progressViewStyle(.circular)
@@ -108,11 +108,13 @@ struct VoiceMessagePlayer: View {
case .sndTransfer: playbackButton()
case .sndComplete: playbackButton()
case .sndCancelled: playbackButton()
case .sndError: playbackButton()
case .rcvInvitation: loadingIcon()
case .rcvAccepted: loadingIcon()
case .rcvTransfer: loadingIcon()
case .rcvComplete: playbackButton()
case .rcvCancelled: playPauseIcon("play.fill", Color(uiColor: .tertiaryLabel))
case .rcvError: playPauseIcon("play.fill", Color(uiColor: .tertiaryLabel))
}
} else {
playPauseIcon("play.fill", Color(uiColor: .tertiaryLabel))
+10 -9
View File
@@ -496,8 +496,8 @@ struct ChatView: View {
}
if ci.meta.itemDeleted == nil,
let file = ci.file,
file.cancellable {
menu.append(cancelFileUIAction(file.fileId, sent: ci.chatDir.sent))
let cancelAction = file.cancelAction {
menu.append(cancelFileUIAction(file.fileId, cancelAction))
}
if !live || !ci.meta.isLive {
menu.append(deleteUIAction())
@@ -589,15 +589,16 @@ struct ChatView: View {
}
}
private func cancelFileUIAction(_ fileId: Int64, sent: Bool) -> UIAction {
UIAction(
title: NSLocalizedString("Stop file", comment: "chat item action"),
image: UIImage(systemName: "xmark")
private func cancelFileUIAction(_ fileId: Int64, _ cancelAction: CancelAction) -> UIAction {
return UIAction(
title: cancelAction.uiAction,
image: UIImage(systemName: "xmark"),
attributes: [.destructive]
) { _ in
AlertManager.shared.showAlert(Alert(
title: Text(sent ? "Stop sending file?" : "Stop receiving file?"),
message: Text(sent ? "Sending file will be stopped." : "Receiving file will be stopped."),
primaryButton: .destructive(Text("Stop")) {
title: Text(cancelAction.alert.title),
message: Text(cancelAction.alert.message),
primaryButton: .destructive(Text(cancelAction.alert.confirm)) {
Task {
if let user = ChatModel.shared.currentUser {
await cancelFile(user: user, fileId: fileId)
+7
View File
@@ -453,6 +453,7 @@ public enum ChatResponse: Decodable, Error {
case rcvFileComplete(user: User, chatItem: AChatItem)
case rcvFileCancelled(user: User, chatItem: AChatItem, rcvFileTransfer: RcvFileTransfer)
case rcvFileSndCancelled(user: User, chatItem: AChatItem, rcvFileTransfer: RcvFileTransfer)
case rcvFileError(user: User, chatItem: AChatItem)
// sending file events
case sndFileStart(user: User, chatItem: AChatItem, sndFileTransfer: SndFileTransfer)
case sndFileComplete(user: User, chatItem: AChatItem, sndFileTransfer: SndFileTransfer)
@@ -460,6 +461,8 @@ public enum ChatResponse: Decodable, Error {
case sndFileRcvCancelled(user: User, chatItem: AChatItem, sndFileTransfer: SndFileTransfer)
case sndFileProgressXFTP(user: User, chatItem: AChatItem, fileTransferMeta: FileTransferMeta, sentSize: Int64, totalSize: Int64)
case sndFileCompleteXFTP(user: User, chatItem: AChatItem, fileTransferMeta: FileTransferMeta)
case sndFileError(user: User, chatItem: AChatItem)
// call events
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)
@@ -564,12 +567,14 @@ public enum ChatResponse: Decodable, Error {
case .rcvFileComplete: return "rcvFileComplete"
case .rcvFileCancelled: return "rcvFileCancelled"
case .rcvFileSndCancelled: return "rcvFileSndCancelled"
case .rcvFileError: return "rcvFileError"
case .sndFileStart: return "sndFileStart"
case .sndFileComplete: return "sndFileComplete"
case .sndFileCancelled: return "sndFileCancelled"
case .sndFileRcvCancelled: return "sndFileRcvCancelled"
case .sndFileProgressXFTP: return "sndFileProgressXFTP"
case .sndFileCompleteXFTP: return "sndFileCompleteXFTP"
case .sndFileError: return "sndFileError"
case .callInvitation: return "callInvitation"
case .callOffer: return "callOffer"
case .callAnswer: return "callAnswer"
@@ -677,12 +682,14 @@ public enum ChatResponse: Decodable, Error {
case let .rcvFileComplete(u, chatItem): return withUser(u, String(describing: chatItem))
case let .rcvFileCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem))
case let .rcvFileSndCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem))
case let .rcvFileError(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(u, chatItem, _, _): return withUser(u, String(describing: chatItem))
case let .sndFileRcvCancelled(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 .sndFileCompleteXFTP(u, chatItem, _): return withUser(u, String(describing: chatItem))
case let .sndFileError(u, chatItem): return withUser(u, String(describing: chatItem))
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))")
+61 -10
View File
@@ -2281,32 +2281,79 @@ public struct CIFile: Decodable {
case .sndTransfer: return true
case .sndComplete: return true
case .sndCancelled: return true
case .sndError: return true
case .rcvInvitation: return false
case .rcvAccepted: return false
case .rcvTransfer: return false
case .rcvCancelled: return false
case .rcvComplete: return true
case .rcvError: return false
}
}
}
public var cancellable: Bool {
public var cancelAction: CancelAction? {
get {
switch self.fileStatus {
case .sndStored: return self.fileProtocol != .xftp // TODO true - enable when XFTP send supports cancel
case .sndTransfer: return self.fileProtocol != .xftp // TODO true
case .sndComplete: return false
case .sndCancelled: return false
case .rcvInvitation: return false
case .rcvAccepted: return true
case .rcvTransfer: return true
case .rcvCancelled: return false
case .rcvComplete: return false
case .sndStored: return sndCancelAction
case .sndTransfer: return sndCancelAction
case .sndComplete:
if self.fileProtocol == .xftp {
return revokeCancelAction
} else {
return nil
}
case .sndCancelled: return nil
case .sndError: return nil
case .rcvInvitation: return nil
case .rcvAccepted: return rcvCancelAction
case .rcvTransfer: return rcvCancelAction
case .rcvCancelled: return nil
case .rcvComplete: return nil
case .rcvError: return nil
}
}
}
}
public struct CancelAction {
public var uiAction: String
public var alert: AlertInfo
}
public struct AlertInfo {
public var title: LocalizedStringKey
public var message: LocalizedStringKey
public var confirm: LocalizedStringKey
}
private var sndCancelAction = CancelAction(
uiAction: NSLocalizedString("Stop file", comment: "cancel file action"),
alert: AlertInfo(
title: "Stop sending file?",
message: "Sending file will be stopped.",
confirm: "Stop"
)
)
private var revokeCancelAction = CancelAction(
uiAction: NSLocalizedString("Revoke file", comment: "cancel file action"),
alert: AlertInfo(
title: "Revoke file?",
message: "File will be deleted from servers.",
confirm: "Revoke"
)
)
private var rcvCancelAction = CancelAction(
uiAction: NSLocalizedString("Stop file", comment: "cancel file action"),
alert: AlertInfo(
title: "Stop receiving file?",
message: "Receiving file will be stopped.",
confirm: "Stop"
)
)
public enum FileProtocol: String, Decodable {
case smp = "smp"
case xftp = "xftp"
@@ -2317,11 +2364,13 @@ public enum CIFileStatus: Decodable {
case sndTransfer(sndProgress: Int64, sndTotal: Int64)
case sndComplete
case sndCancelled
case sndError
case rcvInvitation
case rcvAccepted
case rcvTransfer(rcvProgress: Int64, rcvTotal: Int64)
case rcvComplete
case rcvCancelled
case rcvError
var id: String {
switch self {
@@ -2329,11 +2378,13 @@ public enum CIFileStatus: Decodable {
case let .sndTransfer(sndProgress, sndTotal): return "sndTransfer \(sndProgress) \(sndTotal)"
case .sndComplete: return "sndComplete"
case .sndCancelled: return "sndCancelled"
case .sndError: return "sndError"
case .rcvInvitation: return "rcvInvitation"
case .rcvAccepted: return "rcvAccepted"
case let .rcvTransfer(rcvProgress, rcvTotal): return "rcvTransfer \(rcvProgress) \(rcvTotal)"
case .rcvComplete: return "rcvComplete"
case .rcvCancelled: return "rcvCancelled"
case .rcvError: return "rcvError"
}
}
}