diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt b/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt index e3351dc12..7612ec830 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt @@ -1711,7 +1711,8 @@ class CIFile( val fileName: String, val fileSize: Long, val filePath: String? = null, - val fileStatus: CIFileStatus + val fileStatus: CIFileStatus, + val fileProtocol: FileProtocol ) { val loaded: Boolean = when (fileStatus) { is CIFileStatus.SndStored -> true @@ -1733,19 +1734,25 @@ class CIFile( filePath: String? = "test.txt", fileStatus: CIFileStatus = CIFileStatus.RcvComplete ): CIFile = - CIFile(fileId = fileId, fileName = fileName, fileSize = fileSize, filePath = filePath, fileStatus = fileStatus) + CIFile(fileId = fileId, fileName = fileName, fileSize = fileSize, filePath = filePath, fileStatus = fileStatus, fileProtocol = FileProtocol.XFTP) } } +@Serializable +enum class FileProtocol { + @SerialName("smp") SMP, + @SerialName("xftp") XFTP; +} + @Serializable sealed class CIFileStatus { @Serializable @SerialName("sndStored") object SndStored: CIFileStatus() - @Serializable @SerialName("sndTransfer") class SndTransfer(val sndProgress: Int, val sndTotal: Int): CIFileStatus() + @Serializable @SerialName("sndTransfer") class SndTransfer(val sndProgress: Long, val sndTotal: Long): CIFileStatus() @Serializable @SerialName("sndComplete") object SndComplete: CIFileStatus() @Serializable @SerialName("sndCancelled") object SndCancelled: CIFileStatus() @Serializable @SerialName("rcvInvitation") object RcvInvitation: CIFileStatus() @Serializable @SerialName("rcvAccepted") object RcvAccepted: CIFileStatus() - @Serializable @SerialName("rcvTransfer") class RcvTransfer(val rcvProgress: Int, val rcvTotal: Int): CIFileStatus() + @Serializable @SerialName("rcvTransfer") class RcvTransfer(val rcvProgress: Long, val rcvTotal: Long): CIFileStatus() @Serializable @SerialName("rcvComplete") object RcvComplete: CIFileStatus() @Serializable @SerialName("rcvCancelled") object RcvCancelled: CIFileStatus() } diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt index 04f721ec3..c8ceacae5 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt @@ -1402,6 +1402,8 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a chatItemSimpleUpdate(r.user, r.chatItem) is CR.RcvFileComplete -> chatItemSimpleUpdate(r.user, r.chatItem) + is CR.RcvFileProgressXFTP -> + chatItemSimpleUpdate(r.user, r.chatItem) is CR.SndFileStart -> chatItemSimpleUpdate(r.user, r.chatItem) is CR.SndFileComplete -> { @@ -1417,6 +1419,8 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a removeFile(appContext, fileName) } } + is CR.SndFileProgressXFTP -> + chatItemSimpleUpdate(r.user, r.chatItem) is CR.CallInvitation -> { chatModel.callManager.reportNewIncomingCall(r.callInvitation) } @@ -3044,12 +3048,14 @@ sealed class CR { @Serializable @SerialName("rcvFileAcceptedSndCancelled") class RcvFileAcceptedSndCancelled(val user: User, val rcvFileTransfer: RcvFileTransfer): CR() @Serializable @SerialName("rcvFileStart") class RcvFileStart(val user: User, val chatItem: AChatItem): CR() @Serializable @SerialName("rcvFileComplete") class RcvFileComplete(val user: User, val chatItem: AChatItem): CR() + @Serializable @SerialName("rcvFileProgressXFTP") class RcvFileProgressXFTP(val user: User, val chatItem: AChatItem, val receivedSize: Long, val totalSize: Long): CR() // sending file events @Serializable @SerialName("sndFileStart") class SndFileStart(val user: User, val chatItem: AChatItem, val sndFileTransfer: SndFileTransfer): CR() @Serializable @SerialName("sndFileComplete") class SndFileComplete(val user: User, val chatItem: AChatItem, val sndFileTransfer: SndFileTransfer): CR() @Serializable @SerialName("sndFileCancelled") class SndFileCancelled(val chatItem: AChatItem, val sndFileTransfer: SndFileTransfer): CR() @Serializable @SerialName("sndFileRcvCancelled") class SndFileRcvCancelled(val user: User, val chatItem: AChatItem, val sndFileTransfer: SndFileTransfer): CR() @Serializable @SerialName("sndGroupFileCancelled") class SndGroupFileCancelled(val user: User, val chatItem: AChatItem, val fileTransferMeta: FileTransferMeta, val sndFileTransfers: List): CR() + @Serializable @SerialName("sndFileProgressXFTP") class SndFileProgressXFTP(val user: User, val chatItem: AChatItem, val fileTransferMeta: FileTransferMeta, val sentSize: Long, val totalSize: Long): CR() @Serializable @SerialName("callInvitation") class CallInvitation(val callInvitation: RcvCallInvitation): CR() @Serializable @SerialName("callOffer") class CallOffer(val user: User, val contact: Contact, val callType: CallType, val offer: WebRTCSession, val sharedKey: String? = null, val askConfirmation: Boolean): CR() @Serializable @SerialName("callAnswer") class CallAnswer(val user: User, val contact: Contact, val answer: WebRTCSession): CR() @@ -3146,11 +3152,13 @@ sealed class CR { is RcvFileAccepted -> "rcvFileAccepted" is RcvFileStart -> "rcvFileStart" is RcvFileComplete -> "rcvFileComplete" + is RcvFileProgressXFTP -> "rcvFileProgressXFTP" is SndFileCancelled -> "sndFileCancelled" is SndFileComplete -> "sndFileComplete" is SndFileRcvCancelled -> "sndFileRcvCancelled" is SndFileStart -> "sndFileStart" is SndGroupFileCancelled -> "sndGroupFileCancelled" + is SndFileProgressXFTP -> "sndFileProgressXFTP" is CallInvitation -> "callInvitation" is CallOffer -> "callOffer" is CallAnswer -> "callAnswer" @@ -3249,11 +3257,13 @@ sealed class CR { is RcvFileAccepted -> withUser(user, json.encodeToString(chatItem)) is RcvFileStart -> withUser(user, json.encodeToString(chatItem)) is RcvFileComplete -> withUser(user, json.encodeToString(chatItem)) + is RcvFileProgressXFTP -> withUser(user, "chatItem: ${json.encodeToString(chatItem)}\nreceivedSize: $receivedSize\ntotalSize: $totalSize") is SndFileCancelled -> json.encodeToString(chatItem) is SndFileComplete -> withUser(user, json.encodeToString(chatItem)) is SndFileRcvCancelled -> withUser(user, json.encodeToString(chatItem)) is SndFileStart -> withUser(user, json.encodeToString(chatItem)) is SndGroupFileCancelled -> withUser(user, json.encodeToString(chatItem)) + is SndFileProgressXFTP -> withUser(user, "chatItem: ${json.encodeToString(chatItem)}\nsentSize: $sentSize\ntotalSize: $totalSize") is CallInvitation -> "contact: ${callInvitation.contact.id}\ncallType: $callInvitation.callType\nsharedKey: ${callInvitation.sharedKey ?: ""}" is CallOffer -> withUser(user, "contact: ${contact.id}\ncallType: $callType\nsharedKey: ${sharedKey ?: ""}\naskConfirmation: $askConfirmation\noffer: ${json.encodeToString(offer)}") is CallAnswer -> withUser(user, "contact: ${contact.id}\nanswer: ${json.encodeToString(answer)}") diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt index d63385ee2..574e56f74 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt @@ -182,6 +182,8 @@ fun ComposeView( val pendingLinkUrl = rememberSaveable { mutableStateOf(null) } val cancelledLinks = rememberSaveable { mutableSetOf() } val useLinkPreviews = chatModel.controller.appPrefs.privacyLinkPreviews.get() + val xftpSendEnabled = chatModel.controller.appPrefs.xftpSendEnabled.get() + val maxFileSize = getMaxFileSize(fileProtocol = if (xftpSendEnabled) FileProtocol.XFTP else FileProtocol.SMP) val smallFont = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onBackground) val textStyle = remember { mutableStateOf(smallFont) } val cameraLauncher = rememberCameraLauncher { uri: Uri? -> @@ -212,13 +214,13 @@ fun ComposeView( if (isAnimNewApi || isAnimOldApi) { // It's a gif or webp val fileSize = getFileSize(context, uri) - if (fileSize != null && fileSize <= MAX_FILE_SIZE) { + if (fileSize != null && fileSize <= maxFileSize) { content.add(UploadContent.AnimatedImage(uri)) } else { bitmap = null AlertManager.shared.showAlertMsg( generalGetString(R.string.large_file), - String.format(generalGetString(R.string.maximum_supported_file_size), formatBytes(MAX_FILE_SIZE)) + String.format(generalGetString(R.string.maximum_supported_file_size), formatBytes(maxFileSize)) ) } } else { @@ -236,7 +238,7 @@ fun ComposeView( val processPickedFile = { uri: Uri?, text: String? -> if (uri != null) { val fileSize = getFileSize(context, uri) - if (fileSize != null && fileSize <= MAX_FILE_SIZE) { + if (fileSize != null && fileSize <= maxFileSize) { val fileName = getFileName(SimplexApp.context, uri) if (fileName != null) { composeState.value = composeState.value.copy(message = text ?: composeState.value.message, preview = ComposePreview.FilePreview(fileName, uri)) @@ -244,7 +246,7 @@ fun ComposeView( } else { AlertManager.shared.showAlertMsg( generalGetString(R.string.large_file), - String.format(generalGetString(R.string.maximum_supported_file_size), formatBytes(MAX_FILE_SIZE)) + String.format(generalGetString(R.string.maximum_supported_file_size), formatBytes(maxFileSize)) ) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt index 0df2c1518..ac759cd2c 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIFileView.kt @@ -3,6 +3,7 @@ package chat.simplex.app.views.chat.item import android.widget.Toast import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CornerSize import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* import androidx.compose.material.icons.Icons @@ -16,6 +17,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.* import androidx.compose.ui.unit.dp @@ -64,7 +66,7 @@ fun CIFileView( fun fileSizeValid(): Boolean { if (file != null) { - return file.fileSize <= MAX_FILE_SIZE + return file.fileSize <= getMaxFileSize(file.fileProtocol) } return false } @@ -78,15 +80,23 @@ fun CIFileView( } else { AlertManager.shared.showAlertMsg( generalGetString(R.string.large_file), - String.format(generalGetString(R.string.contact_sent_large_file), formatBytes(MAX_FILE_SIZE)) + String.format(generalGetString(R.string.contact_sent_large_file), formatBytes(getMaxFileSize(file.fileProtocol))) ) } } is CIFileStatus.RcvAccepted -> - AlertManager.shared.showAlertMsg( - generalGetString(R.string.waiting_for_file), - String.format(generalGetString(R.string.file_will_be_received_when_contact_is_online), MAX_FILE_SIZE) - ) + when (file.fileProtocol) { + FileProtocol.XFTP -> + AlertManager.shared.showAlertMsg( + generalGetString(R.string.waiting_for_file), + generalGetString(R.string.file_will_be_received_when_contact_completes_uploading) + ) + FileProtocol.SMP -> + AlertManager.shared.showAlertMsg( + generalGetString(R.string.waiting_for_file), + generalGetString(R.string.file_will_be_received_when_contact_is_online) + ) + } is CIFileStatus.RcvComplete -> { val filePath = getLoadedFilePath(context, file) if (filePath != null) { @@ -109,6 +119,20 @@ fun CIFileView( ) } + @Composable + fun progressCircle(progress: Long, total: Long) { + val angle = 360f * (progress.toDouble() / total.toDouble()).toFloat() + val strokeWidth = with(LocalDensity.current) { 3.dp.toPx() } + val strokeColor = if (isInDarkTheme()) FileDark else FileLight + Surface( + Modifier.drawRingModifier(angle, strokeColor, strokeWidth), + color = Color.Transparent, + shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)) + ) { + Box(Modifier.size(32.dp)) + } + } + @Composable fun fileIndicator() { Box( @@ -120,8 +144,16 @@ fun CIFileView( ) { if (file != null) { when (file.fileStatus) { - is CIFileStatus.SndStored -> fileIcon() - is CIFileStatus.SndTransfer -> progressIndicator() + is CIFileStatus.SndStored -> + when (file.fileProtocol) { + FileProtocol.XFTP -> progressIndicator() + FileProtocol.SMP -> fileIcon() + } + is CIFileStatus.SndTransfer -> + when (file.fileProtocol) { + FileProtocol.XFTP -> progressCircle(file.fileStatus.sndProgress, file.fileStatus.sndTotal) + FileProtocol.SMP -> progressIndicator() + } is CIFileStatus.SndComplete -> fileIcon(innerIcon = Icons.Filled.Check) is CIFileStatus.SndCancelled -> fileIcon(innerIcon = Icons.Outlined.Close) is CIFileStatus.RcvInvitation -> @@ -130,7 +162,12 @@ fun CIFileView( else fileIcon(innerIcon = Icons.Outlined.PriorityHigh, color = WarningOrange) is CIFileStatus.RcvAccepted -> fileIcon(innerIcon = Icons.Outlined.MoreHoriz) - is CIFileStatus.RcvTransfer -> progressIndicator() + is CIFileStatus.RcvTransfer -> + if (file.fileProtocol == FileProtocol.XFTP && file.fileStatus.rcvProgress < file.fileStatus.rcvTotal) { + progressCircle(file.fileStatus.rcvProgress, file.fileStatus.rcvTotal) + } else { + progressIndicator() + } is CIFileStatus.RcvComplete -> fileIcon() is CIFileStatus.RcvCancelled -> fileIcon(innerIcon = Icons.Outlined.Close) } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt index 7fb2e92f9..8599dc913 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIImageView.kt @@ -27,8 +27,7 @@ import androidx.compose.ui.unit.dp import androidx.core.content.FileProvider import chat.simplex.app.* import chat.simplex.app.R -import chat.simplex.app.model.CIFile -import chat.simplex.app.model.CIFileStatus +import chat.simplex.app.model.* import chat.simplex.app.views.helpers.* import coil.ImageLoader import coil.compose.rememberAsyncImagePainter @@ -45,6 +44,15 @@ fun CIImageView( showMenu: MutableState, receiveFile: (Long) -> Unit ) { + @Composable + fun progressIndicator() { + CircularProgressIndicator( + Modifier.size(16.dp), + color = Color.White, + strokeWidth = 2.dp + ) + } + @Composable fun loadingIndicator() { if (file != null) { @@ -55,12 +63,13 @@ fun CIImageView( contentAlignment = Alignment.Center ) { when (file.fileStatus) { + is CIFileStatus.SndStored -> + when (file.fileProtocol) { + FileProtocol.XFTP -> progressIndicator() + FileProtocol.SMP -> {} + } is CIFileStatus.SndTransfer -> - CircularProgressIndicator( - Modifier.size(16.dp), - color = Color.White, - strokeWidth = 2.dp - ) + progressIndicator() is CIFileStatus.SndComplete -> Icon( Icons.Filled.Check, @@ -76,11 +85,7 @@ fun CIImageView( tint = Color.White ) is CIFileStatus.RcvTransfer -> - CircularProgressIndicator( - Modifier.size(16.dp), - color = Color.White, - strokeWidth = 2.dp - ) + progressIndicator() is CIFileStatus.RcvInvitation -> Icon( Icons.Outlined.ArrowDownward, @@ -136,7 +141,7 @@ fun CIImageView( fun fileSizeValid(): Boolean { if (file != null) { - return file.fileSize <= MAX_FILE_SIZE + return file.fileSize <= getMaxFileSize(file.fileProtocol) } return false } @@ -179,14 +184,22 @@ fun CIImageView( } else { AlertManager.shared.showAlertMsg( generalGetString(R.string.large_file), - String.format(generalGetString(R.string.contact_sent_large_file), formatBytes(MAX_FILE_SIZE)) + String.format(generalGetString(R.string.contact_sent_large_file), formatBytes(getMaxFileSize(file.fileProtocol))) ) } CIFileStatus.RcvAccepted -> - AlertManager.shared.showAlertMsg( - generalGetString(R.string.waiting_for_image), - generalGetString(R.string.image_will_be_received_when_contact_is_online) - ) + when (file.fileProtocol) { + FileProtocol.XFTP -> + AlertManager.shared.showAlertMsg( + generalGetString(R.string.waiting_for_image), + generalGetString(R.string.image_will_be_received_when_contact_completes_uploading) + ) + FileProtocol.SMP -> + AlertManager.shared.showAlertMsg( + generalGetString(R.string.waiting_for_image), + generalGetString(R.string.image_will_be_received_when_contact_is_online) + ) + } CIFileStatus.RcvTransfer(rcvProgress = 7, rcvTotal = 10) -> {} // ? CIFileStatus.RcvComplete -> {} // ? CIFileStatus.RcvCancelled -> {} // TODO diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIVoiceView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIVoiceView.kt index c20df776a..cc873ac6e 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIVoiceView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/CIVoiceView.kt @@ -228,7 +228,7 @@ private fun VoiceMsgIndicator( } } -private fun Modifier.drawRingModifier(angle: Float, color: Color, strokeWidth: Float) = drawWithCache { +fun Modifier.drawRingModifier(angle: Float, color: Color, strokeWidth: Float) = drawWithCache { val brush = Brush.linearGradient( 0f to Color.Transparent, 0f to color, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt index cdb567554..d361daa9a 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt @@ -237,8 +237,9 @@ const val MAX_VOICE_SIZE_AUTO_RCV: Long = MAX_IMAGE_SIZE const val MAX_VOICE_SIZE_FOR_SENDING: Long = 94680 // 6 chunks * 15780 bytes per chunk const val MAX_VOICE_MILLIS_FOR_SENDING: Int = 43_000 -//const val MAX_FILE_SIZE_SMP: Long = 8000000 // TODO distinguish between XFTP and SMP files -const val MAX_FILE_SIZE: Long = 1_073_741_824 +const val MAX_FILE_SIZE_SMP: Long = 8000000 + +const val MAX_FILE_SIZE_XFTP: Long = 1_073_741_824 fun getFilesDirectory(context: Context): String { return context.filesDir.toString() @@ -494,9 +495,9 @@ fun formatBytes(bytes: Long): String { return "0 bytes" } val bytesDouble = bytes.toDouble() - val k = 1000.toDouble() + val k = 1024.toDouble() val units = arrayOf("bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB") - val i = kotlin.math.floor(log2(bytesDouble) / log2(k)) + val i = floor(log2(bytesDouble) / log2(k)) val size = bytesDouble / k.pow(i) val unit = units[i.toInt()] @@ -541,6 +542,13 @@ fun directoryFileCountAndSize(dir: String): Pair { // count, size in return fileCount to bytes } +fun getMaxFileSize(fileProtocol: FileProtocol): Long { + return when (fileProtocol) { + FileProtocol.XFTP -> MAX_FILE_SIZE_XFTP + FileProtocol.SMP -> MAX_FILE_SIZE_SMP + } +} + fun Color.darker(factor: Float = 0.1f): Color = Color(max(red * (1 - factor), 0f), max(green * (1 - factor), 0f), max(blue * (1 - factor), 0f), alpha) diff --git a/apps/android/app/src/main/res/values/strings.xml b/apps/android/app/src/main/res/values/strings.xml index 045da6c4b..aa6d9f453 100644 --- a/apps/android/app/src/main/res/values/strings.xml +++ b/apps/android/app/src/main/res/values/strings.xml @@ -238,6 +238,7 @@ Asked to receive the image Image sent Waiting for image + Image will be received when your contact completes uploading it. Image will be received when your contact is online, please wait or check later! Image saved to Gallery @@ -247,6 +248,7 @@ Your contact sent a file that is larger than currently supported maximum size (%1$s). Currently maximum supported file size is %1$s. Waiting for file + File will be received when your contact completes uploading it. File will be received when your contact is online, please wait or check later! File saved File not found diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift index 170f2d7cd..8e6e4af4f 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift @@ -56,7 +56,7 @@ struct CIFileView: View { case .sndComplete: return false case .sndCancelled: return false case .rcvInvitation: return true - case .rcvAccepted: return file.isSMP + case .rcvAccepted: return true case .rcvTransfer: return false case .rcvComplete: return true case .rcvCancelled: return false @@ -92,7 +92,13 @@ struct CIFileView: View { ) } case .rcvAccepted: - if file.isSMP { + switch file.fileProtocol { + case .xftp: + AlertManager.shared.showAlertMsg( + title: "Waiting for file", + message: "File will be received when your contact completes uploading it." + ) + case .smp: AlertManager.shared.showAlertMsg( title: "Waiting for file", message: "File will be received when your contact is online, please wait or check later!" @@ -112,7 +118,11 @@ struct CIFileView: View { @ViewBuilder private func fileIndicator() -> some View { if let file = file { switch file.fileStatus { - case .sndStored: fileIcon("doc.fill") + case .sndStored: + switch file.fileProtocol { + case .xftp: progressView() + case .smp: fileIcon("doc.fill") + } case let .sndTransfer(sndProgress, sndTotal): switch file.fileProtocol { case .xftp: progressCircle(sndProgress, sndTotal) @@ -169,7 +179,7 @@ struct CIFileView: View { Circle() .trim(from: 0, to: Double(progress) / Double(total)) .stroke( - Color.accentColor, + Color(uiColor: .tertiaryLabel), style: StrokeStyle(lineWidth: 3) ) .rotationEffect(.degrees(-90)) diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift index a52cd72a2..d53639cd2 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift @@ -41,7 +41,13 @@ struct CIImageView: View { // TODO image accepted alert? } case .rcvAccepted: - if file.isSMP { + switch file.fileProtocol { + case .xftp: + AlertManager.shared.showAlertMsg( + title: "Waiting for image", + message: "Image will be received when your contact completes uploading it." + ) + case .smp: AlertManager.shared.showAlertMsg( title: "Waiting for image", message: "Image will be received when your contact is online, please wait or check later!" @@ -79,6 +85,11 @@ struct CIImageView: View { @ViewBuilder private func loadingIndicator() -> some View { if let file = chatItem.file { switch file.fileStatus { + case .sndStored: + switch file.fileProtocol { + case .xftp: progressView() + case .smp: EmptyView() + } case .sndTransfer: progressView() case .sndComplete: diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index f1e9ba0a4..3b3b4acd2 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -2221,13 +2221,6 @@ public struct CIFile: Decodable { CIFile(fileId: fileId, fileName: fileName, fileSize: fileSize, filePath: filePath, fileStatus: fileStatus, fileProtocol: .xftp) } - public var isSMP: Bool { - switch self.fileProtocol { - case .smp: return true - default: return false - } - } - public var loaded: Bool { get { switch self.fileStatus {