mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
multiplatform: file errors (#4289)
This commit is contained in:
+25
-2
@@ -2660,6 +2660,7 @@ data class CIFile(
|
||||
is CIFileStatus.SndComplete -> true
|
||||
is CIFileStatus.SndCancelled -> true
|
||||
is CIFileStatus.SndError -> true
|
||||
is CIFileStatus.SndWarning -> true
|
||||
is CIFileStatus.RcvInvitation -> false
|
||||
is CIFileStatus.RcvAccepted -> false
|
||||
is CIFileStatus.RcvTransfer -> false
|
||||
@@ -2667,6 +2668,7 @@ data class CIFile(
|
||||
is CIFileStatus.RcvCancelled -> false
|
||||
is CIFileStatus.RcvComplete -> true
|
||||
is CIFileStatus.RcvError -> false
|
||||
is CIFileStatus.RcvWarning -> false
|
||||
is CIFileStatus.Invalid -> false
|
||||
}
|
||||
|
||||
@@ -2682,6 +2684,7 @@ data class CIFile(
|
||||
}
|
||||
is CIFileStatus.SndCancelled -> null
|
||||
is CIFileStatus.SndError -> null
|
||||
is CIFileStatus.SndWarning -> sndCancelAction
|
||||
is CIFileStatus.RcvInvitation -> null
|
||||
is CIFileStatus.RcvAccepted -> rcvCancelAction
|
||||
is CIFileStatus.RcvTransfer -> rcvCancelAction
|
||||
@@ -2689,6 +2692,7 @@ data class CIFile(
|
||||
is CIFileStatus.RcvCancelled -> null
|
||||
is CIFileStatus.RcvComplete -> null
|
||||
is CIFileStatus.RcvError -> null
|
||||
is CIFileStatus.RcvWarning -> rcvCancelAction
|
||||
is CIFileStatus.Invalid -> null
|
||||
}
|
||||
|
||||
@@ -2862,14 +2866,16 @@ sealed class 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("sndError") object SndError: CIFileStatus()
|
||||
@Serializable @SerialName("sndError") class SndError(val sndFileError: FileError): CIFileStatus()
|
||||
@Serializable @SerialName("sndWarning") class SndWarning(val sndFileError: FileError): CIFileStatus()
|
||||
@Serializable @SerialName("rcvInvitation") object RcvInvitation: CIFileStatus()
|
||||
@Serializable @SerialName("rcvAccepted") object RcvAccepted: CIFileStatus()
|
||||
@Serializable @SerialName("rcvTransfer") class RcvTransfer(val rcvProgress: Long, val rcvTotal: Long): CIFileStatus()
|
||||
@Serializable @SerialName("rcvAborted") object RcvAborted: CIFileStatus()
|
||||
@Serializable @SerialName("rcvComplete") object RcvComplete: CIFileStatus()
|
||||
@Serializable @SerialName("rcvCancelled") object RcvCancelled: CIFileStatus()
|
||||
@Serializable @SerialName("rcvError") object RcvError: CIFileStatus()
|
||||
@Serializable @SerialName("rcvError") class RcvError(val rcvFileError: FileError): CIFileStatus()
|
||||
@Serializable @SerialName("rcvWarning") class RcvWarning(val rcvFileError: FileError): CIFileStatus()
|
||||
@Serializable @SerialName("invalid") class Invalid(val text: String): CIFileStatus()
|
||||
|
||||
val sent: Boolean get() = when (this) {
|
||||
@@ -2878,6 +2884,7 @@ sealed class CIFileStatus {
|
||||
is SndComplete -> true
|
||||
is SndCancelled -> true
|
||||
is SndError -> true
|
||||
is SndWarning -> true
|
||||
is RcvInvitation -> false
|
||||
is RcvAccepted -> false
|
||||
is RcvTransfer -> false
|
||||
@@ -2885,10 +2892,26 @@ sealed class CIFileStatus {
|
||||
is RcvComplete -> false
|
||||
is RcvCancelled -> false
|
||||
is RcvError -> false
|
||||
is RcvWarning -> false
|
||||
is Invalid -> false
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
sealed class FileError {
|
||||
@Serializable @SerialName("auth") class Auth: FileError()
|
||||
@Serializable @SerialName("noFile") class NoFile: FileError()
|
||||
@Serializable @SerialName("relay") class Relay(val srvError: SrvError): FileError()
|
||||
@Serializable @SerialName("other") class Other(val fileError: String): FileError()
|
||||
|
||||
val errorInfo: String get() = when (this) {
|
||||
is FileError.Auth -> generalGetString(MR.strings.file_error_auth)
|
||||
is FileError.NoFile -> generalGetString(MR.strings.file_error_no_file)
|
||||
is FileError.Relay -> generalGetString(MR.strings.file_error_relay).format(srvError.errorInfo)
|
||||
is FileError.Other -> generalGetString(MR.strings.ci_status_other_error).format(fileError)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
|
||||
@Serializable(with = MsgContentSerializer::class)
|
||||
sealed class MsgContent {
|
||||
|
||||
+18
-2
@@ -2026,6 +2026,11 @@ object ChatController {
|
||||
cleanupFile(r.chatItem_)
|
||||
}
|
||||
}
|
||||
is CR.RcvFileWarning -> {
|
||||
if (r.chatItem_ != null) {
|
||||
chatItemSimpleUpdate(rhId, r.user, r.chatItem_)
|
||||
}
|
||||
}
|
||||
is CR.SndFileStart ->
|
||||
chatItemSimpleUpdate(rhId, r.user, r.chatItem)
|
||||
is CR.SndFileComplete -> {
|
||||
@@ -2052,6 +2057,11 @@ object ChatController {
|
||||
cleanupFile(r.chatItem_)
|
||||
}
|
||||
}
|
||||
is CR.SndFileWarning -> {
|
||||
if (r.chatItem_ != null) {
|
||||
chatItemSimpleUpdate(rhId, r.user, r.chatItem_)
|
||||
}
|
||||
}
|
||||
is CR.CallInvitation -> {
|
||||
chatModel.callManager.reportNewIncomingCall(r.callInvitation.copy(remoteHostId = rhId))
|
||||
}
|
||||
@@ -4322,6 +4332,7 @@ sealed class CR {
|
||||
@Serializable @SerialName("rcvFileCancelled") class RcvFileCancelled(val user: UserRef, val chatItem_: AChatItem?, val rcvFileTransfer: RcvFileTransfer): CR()
|
||||
@Serializable @SerialName("rcvFileSndCancelled") class RcvFileSndCancelled(val user: UserRef, val chatItem: AChatItem, val rcvFileTransfer: RcvFileTransfer): CR()
|
||||
@Serializable @SerialName("rcvFileError") class RcvFileError(val user: UserRef, val chatItem_: AChatItem?, val agentError: AgentErrorType, val rcvFileTransfer: RcvFileTransfer): CR()
|
||||
@Serializable @SerialName("rcvFileWarning") class RcvFileWarning(val user: UserRef, val chatItem_: AChatItem?, val agentError: AgentErrorType, val rcvFileTransfer: RcvFileTransfer): CR()
|
||||
// sending file events
|
||||
@Serializable @SerialName("sndFileStart") class SndFileStart(val user: UserRef, val chatItem: AChatItem, val sndFileTransfer: SndFileTransfer): CR()
|
||||
@Serializable @SerialName("sndFileComplete") class SndFileComplete(val user: UserRef, val chatItem: AChatItem, val sndFileTransfer: SndFileTransfer): CR()
|
||||
@@ -4334,7 +4345,8 @@ sealed class CR {
|
||||
@Serializable @SerialName("sndFileCompleteXFTP") class SndFileCompleteXFTP(val user: UserRef, val chatItem: AChatItem, val fileTransferMeta: FileTransferMeta): CR()
|
||||
@Serializable @SerialName("sndStandaloneFileComplete") class SndStandaloneFileComplete(val user: UserRef, val fileTransferMeta: FileTransferMeta, val rcvURIs: List<String>): CR()
|
||||
@Serializable @SerialName("sndFileCancelledXFTP") class SndFileCancelledXFTP(val user: UserRef, val chatItem_: AChatItem?, val fileTransferMeta: FileTransferMeta): CR()
|
||||
@Serializable @SerialName("sndFileError") class SndFileError(val user: UserRef, val chatItem_: AChatItem?, val fileTransferMeta: FileTransferMeta): CR()
|
||||
@Serializable @SerialName("sndFileError") class SndFileError(val user: UserRef, val chatItem_: AChatItem?, val fileTransferMeta: FileTransferMeta, val errorMessage: String): CR()
|
||||
@Serializable @SerialName("sndFileWarning") class SndFileWarning(val user: UserRef, val chatItem_: AChatItem?, val fileTransferMeta: FileTransferMeta, val errorMessage: String): CR()
|
||||
// call events
|
||||
@Serializable @SerialName("callInvitation") class CallInvitation(val callInvitation: RcvCallInvitation): CR()
|
||||
@Serializable @SerialName("callInvitations") class CallInvitations(val callInvitations: List<RcvCallInvitation>): CR()
|
||||
@@ -4493,6 +4505,7 @@ sealed class CR {
|
||||
is RcvFileProgressXFTP -> "rcvFileProgressXFTP"
|
||||
is SndFileRedirectStartXFTP -> "sndFileRedirectStartXFTP"
|
||||
is RcvFileError -> "rcvFileError"
|
||||
is RcvFileWarning -> "rcvFileWarning"
|
||||
is SndFileStart -> "sndFileStart"
|
||||
is SndFileComplete -> "sndFileComplete"
|
||||
is SndFileRcvCancelled -> "sndFileRcvCancelled"
|
||||
@@ -4502,6 +4515,7 @@ sealed class CR {
|
||||
is SndStandaloneFileComplete -> "sndStandaloneFileComplete"
|
||||
is SndFileCancelledXFTP -> "sndFileCancelledXFTP"
|
||||
is SndFileError -> "sndFileError"
|
||||
is SndFileWarning -> "sndFileWarning"
|
||||
is CallInvitations -> "callInvitations"
|
||||
is CallInvitation -> "callInvitation"
|
||||
is CallOffer -> "callOffer"
|
||||
@@ -4652,6 +4666,7 @@ sealed class CR {
|
||||
is RcvFileProgressXFTP -> withUser(user, "chatItem: ${json.encodeToString(chatItem_)}\nreceivedSize: $receivedSize\ntotalSize: $totalSize")
|
||||
is RcvStandaloneFileComplete -> withUser(user, targetPath)
|
||||
is RcvFileError -> withUser(user, "chatItem_: ${json.encodeToString(chatItem_)}\nagentError: ${agentError.string}\nrcvFileTransfer: $rcvFileTransfer")
|
||||
is RcvFileWarning -> withUser(user, "chatItem_: ${json.encodeToString(chatItem_)}\nagentError: ${agentError.string}\nrcvFileTransfer: $rcvFileTransfer")
|
||||
is SndFileCancelled -> json.encodeToString(chatItem_)
|
||||
is SndStandaloneFileCreated -> noDetails()
|
||||
is SndFileStartXFTP -> withUser(user, json.encodeToString(chatItem))
|
||||
@@ -4663,7 +4678,8 @@ sealed class CR {
|
||||
is SndFileCompleteXFTP -> withUser(user, json.encodeToString(chatItem))
|
||||
is SndStandaloneFileComplete -> withUser(user, rcvURIs.size.toString())
|
||||
is SndFileCancelledXFTP -> withUser(user, json.encodeToString(chatItem_))
|
||||
is SndFileError -> withUser(user, json.encodeToString(chatItem_))
|
||||
is SndFileError -> withUser(user, "errorMessage: ${json.encodeToString(errorMessage)}\nchatItem: ${json.encodeToString(chatItem_)}")
|
||||
is SndFileWarning -> withUser(user, "errorMessage: ${json.encodeToString(errorMessage)}\nchatItem: ${json.encodeToString(chatItem_)}")
|
||||
is CallInvitations -> "callInvitations: ${json.encodeToString(callInvitations)}"
|
||||
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)}")
|
||||
|
||||
+30
@@ -31,6 +31,7 @@ import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chatlist.*
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.ImageResource
|
||||
import kotlinx.serialization.encodeToString
|
||||
|
||||
sealed class CIInfoTab {
|
||||
class Delivery(val memberDeliveryStatuses: List<MemberDeliveryStatus>): CIInfoTab()
|
||||
@@ -216,6 +217,27 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ExpandableInfoRow(title: String, value: String) {
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.sizeIn(minHeight = 46.dp)
|
||||
.padding(PaddingValues(horizontal = DEFAULT_PADDING))
|
||||
.clickable { expanded.value = !expanded.value },
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(title, color = MaterialTheme.colors.onBackground)
|
||||
if (expanded.value) {
|
||||
Text(value, color = MaterialTheme.colors.secondary)
|
||||
} else {
|
||||
Text(value, color = MaterialTheme.colors.secondary, maxLines = 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Details() {
|
||||
AppBarTitle(stringResource(if (ci.localNote) MR.strings.saved_message_title else if (sent) MR.strings.sent_message else MR.strings.received_message))
|
||||
@@ -244,6 +266,10 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
|
||||
if (devTools) {
|
||||
InfoRow(stringResource(MR.strings.info_row_database_id), ci.meta.itemId.toString())
|
||||
InfoRow(stringResource(MR.strings.info_row_updated_at), localTimestamp(ci.meta.updatedAt))
|
||||
ExpandableInfoRow(stringResource(MR.strings.info_row_message_status), jsonShort.encodeToString(ci.meta.itemStatus))
|
||||
if (ci.file != null) {
|
||||
ExpandableInfoRow(stringResource(MR.strings.info_row_file_status), jsonShort.encodeToString(ci.file.fileStatus))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -531,6 +557,10 @@ fun itemInfoShareText(chatModel: ChatModel, ci: ChatItem, chatItemInfo: ChatItem
|
||||
if (devTools) {
|
||||
shareText.add(String.format(generalGetString(MR.strings.share_text_database_id), meta.itemId))
|
||||
shareText.add(String.format(generalGetString(MR.strings.share_text_updated_at), meta.updatedAt))
|
||||
shareText.add(String.format(generalGetString(MR.strings.share_text_message_status), jsonShort.encodeToString(ci.meta.itemStatus)))
|
||||
if (ci.file != null) {
|
||||
shareText.add(String.format(generalGetString(MR.strings.share_text_file_status), jsonShort.encodeToString(ci.file.fileStatus)))
|
||||
}
|
||||
}
|
||||
val qi = ci.quotedItem
|
||||
if (qi != null) {
|
||||
|
||||
+22
@@ -87,6 +87,26 @@ fun CIFileView(
|
||||
)
|
||||
FileProtocol.LOCAL -> {}
|
||||
}
|
||||
file.fileStatus is CIFileStatus.RcvError ->
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.file_error),
|
||||
file.fileStatus.rcvFileError.errorInfo
|
||||
)
|
||||
file.fileStatus is CIFileStatus.RcvWarning ->
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.temporary_file_error),
|
||||
file.fileStatus.rcvFileError.errorInfo
|
||||
)
|
||||
file.fileStatus is CIFileStatus.SndError ->
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.file_error),
|
||||
file.fileStatus.sndFileError.errorInfo
|
||||
)
|
||||
file.fileStatus is CIFileStatus.SndWarning ->
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.temporary_file_error),
|
||||
file.fileStatus.sndFileError.errorInfo
|
||||
)
|
||||
file.forwardingAllowed() -> {
|
||||
withLongRunningApi(slow = 600_000) {
|
||||
var filePath = getLoadedFilePath(file)
|
||||
@@ -157,6 +177,7 @@ fun CIFileView(
|
||||
is CIFileStatus.SndComplete -> fileIcon(innerIcon = painterResource(MR.images.ic_check_filled))
|
||||
is CIFileStatus.SndCancelled -> fileIcon(innerIcon = painterResource(MR.images.ic_close))
|
||||
is CIFileStatus.SndError -> fileIcon(innerIcon = painterResource(MR.images.ic_close))
|
||||
is CIFileStatus.SndWarning -> fileIcon(innerIcon = painterResource(MR.images.ic_warning_filled))
|
||||
is CIFileStatus.RcvInvitation ->
|
||||
if (fileSizeValid(file))
|
||||
fileIcon(innerIcon = painterResource(MR.images.ic_arrow_downward), color = MaterialTheme.colors.primary)
|
||||
@@ -174,6 +195,7 @@ fun CIFileView(
|
||||
is CIFileStatus.RcvComplete -> fileIcon()
|
||||
is CIFileStatus.RcvCancelled -> fileIcon(innerIcon = painterResource(MR.images.ic_close))
|
||||
is CIFileStatus.RcvError -> fileIcon(innerIcon = painterResource(MR.images.ic_close))
|
||||
is CIFileStatus.RcvWarning -> fileIcon(innerIcon = painterResource(MR.images.ic_warning_filled))
|
||||
is CIFileStatus.Invalid -> fileIcon(innerIcon = painterResource(MR.images.ic_question_mark))
|
||||
}
|
||||
} else {
|
||||
|
||||
+29
-7
@@ -70,14 +70,16 @@ fun CIImageView(
|
||||
is CIFileStatus.SndComplete -> fileIcon(painterResource(MR.images.ic_check_filled), MR.strings.icon_descr_image_snd_complete)
|
||||
is CIFileStatus.SndCancelled -> fileIcon(painterResource(MR.images.ic_close), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.SndError -> fileIcon(painterResource(MR.images.ic_close), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.SndWarning -> fileIcon(painterResource(MR.images.ic_warning_filled), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.RcvInvitation -> fileIcon(painterResource(MR.images.ic_arrow_downward), MR.strings.icon_descr_asked_to_receive)
|
||||
is CIFileStatus.RcvAccepted -> fileIcon(painterResource(MR.images.ic_more_horiz), MR.strings.icon_descr_waiting_for_image)
|
||||
is CIFileStatus.RcvTransfer -> progressIndicator()
|
||||
is CIFileStatus.RcvComplete -> {}
|
||||
is CIFileStatus.RcvAborted -> fileIcon(painterResource(MR.images.ic_sync_problem), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.RcvCancelled -> fileIcon(painterResource(MR.images.ic_close), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.RcvError -> fileIcon(painterResource(MR.images.ic_close), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.RcvWarning -> fileIcon(painterResource(MR.images.ic_warning_filled), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.Invalid -> fileIcon(painterResource(MR.images.ic_question_mark), MR.strings.icon_descr_file)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -201,8 +203,8 @@ fun CIImageView(
|
||||
} else {
|
||||
imageView(base64ToBitmap(image), onClick = {
|
||||
if (file != null) {
|
||||
when (file.fileStatus) {
|
||||
CIFileStatus.RcvInvitation, CIFileStatus.RcvAborted ->
|
||||
when {
|
||||
file.fileStatus is CIFileStatus.RcvInvitation || file.fileStatus is CIFileStatus.RcvAborted ->
|
||||
if (fileSizeValid()) {
|
||||
receiveFile(file.fileId)
|
||||
} else {
|
||||
@@ -211,7 +213,7 @@ fun CIImageView(
|
||||
String.format(generalGetString(MR.strings.contact_sent_large_file), formatBytes(getMaxFileSize(file.fileProtocol)))
|
||||
)
|
||||
}
|
||||
CIFileStatus.RcvAccepted ->
|
||||
file.fileStatus is CIFileStatus.RcvAccepted ->
|
||||
when (file.fileProtocol) {
|
||||
FileProtocol.XFTP ->
|
||||
AlertManager.shared.showAlertMsg(
|
||||
@@ -225,9 +227,29 @@ fun CIImageView(
|
||||
)
|
||||
FileProtocol.LOCAL -> {}
|
||||
}
|
||||
CIFileStatus.RcvTransfer(rcvProgress = 7, rcvTotal = 10) -> {} // ?
|
||||
CIFileStatus.RcvComplete -> {} // ?
|
||||
CIFileStatus.RcvCancelled -> {} // TODO
|
||||
file.fileStatus is CIFileStatus.RcvError ->
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.file_error),
|
||||
file.fileStatus.rcvFileError.errorInfo
|
||||
)
|
||||
file.fileStatus is CIFileStatus.RcvWarning ->
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.temporary_file_error),
|
||||
file.fileStatus.rcvFileError.errorInfo
|
||||
)
|
||||
file.fileStatus is CIFileStatus.SndError ->
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.file_error),
|
||||
file.fileStatus.sndFileError.errorInfo
|
||||
)
|
||||
file.fileStatus is CIFileStatus.SndWarning ->
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.temporary_file_error),
|
||||
file.fileStatus.sndFileError.errorInfo
|
||||
)
|
||||
file.fileStatus is CIFileStatus.RcvTransfer -> {} // ?
|
||||
file.fileStatus is CIFileStatus.RcvComplete -> {} // ?
|
||||
file.fileStatus is CIFileStatus.RcvCancelled -> {} // TODO
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
+51
-7
@@ -107,7 +107,7 @@ fun CIVideoView(
|
||||
}
|
||||
}
|
||||
}
|
||||
loadingIndicator(file)
|
||||
fileStatusIcon(file)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,11 +339,13 @@ private fun progressIndicator() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun fileIcon(icon: Painter, stringId: StringResource) {
|
||||
private fun fileIcon(icon: Painter, stringId: StringResource, onClick: (() -> Unit)? = null) {
|
||||
var modifier = Modifier.fillMaxSize()
|
||||
modifier = if (onClick != null) { modifier.clickable { onClick() } } else { modifier }
|
||||
Icon(
|
||||
icon,
|
||||
stringResource(stringId),
|
||||
Modifier.fillMaxSize(),
|
||||
modifier,
|
||||
tint = Color.White
|
||||
)
|
||||
}
|
||||
@@ -364,7 +366,7 @@ private fun progressCircle(progress: Long, total: Long) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun loadingIndicator(file: CIFile?) {
|
||||
private fun fileStatusIcon(file: CIFile?) {
|
||||
if (file != null) {
|
||||
Box(
|
||||
Modifier
|
||||
@@ -387,7 +389,28 @@ private fun loadingIndicator(file: CIFile?) {
|
||||
}
|
||||
is CIFileStatus.SndComplete -> fileIcon(painterResource(MR.images.ic_check_filled), MR.strings.icon_descr_video_snd_complete)
|
||||
is CIFileStatus.SndCancelled -> fileIcon(painterResource(MR.images.ic_close), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.SndError -> fileIcon(painterResource(MR.images.ic_close), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.SndError ->
|
||||
fileIcon(
|
||||
painterResource(MR.images.ic_close),
|
||||
MR.strings.icon_descr_file,
|
||||
onClick = {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.file_error),
|
||||
file.fileStatus.sndFileError.errorInfo
|
||||
)
|
||||
}
|
||||
)
|
||||
is CIFileStatus.SndWarning ->
|
||||
fileIcon(
|
||||
painterResource(MR.images.ic_warning_filled),
|
||||
MR.strings.icon_descr_file,
|
||||
onClick = {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.temporary_file_error),
|
||||
file.fileStatus.sndFileError.errorInfo
|
||||
)
|
||||
}
|
||||
)
|
||||
is CIFileStatus.RcvInvitation -> fileIcon(painterResource(MR.images.ic_arrow_downward), MR.strings.icon_descr_video_asked_to_receive)
|
||||
is CIFileStatus.RcvAccepted -> fileIcon(painterResource(MR.images.ic_more_horiz), MR.strings.icon_descr_waiting_for_video)
|
||||
is CIFileStatus.RcvTransfer ->
|
||||
@@ -397,10 +420,31 @@ private fun loadingIndicator(file: CIFile?) {
|
||||
progressIndicator()
|
||||
}
|
||||
is CIFileStatus.RcvAborted -> fileIcon(painterResource(MR.images.ic_sync_problem), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.RcvComplete -> {}
|
||||
is CIFileStatus.RcvCancelled -> fileIcon(painterResource(MR.images.ic_close), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.RcvError -> fileIcon(painterResource(MR.images.ic_close), MR.strings.icon_descr_file)
|
||||
is CIFileStatus.RcvError ->
|
||||
fileIcon(
|
||||
painterResource(MR.images.ic_close),
|
||||
MR.strings.icon_descr_file,
|
||||
onClick = {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.file_error),
|
||||
file.fileStatus.rcvFileError.errorInfo
|
||||
)
|
||||
}
|
||||
)
|
||||
is CIFileStatus.RcvWarning ->
|
||||
fileIcon(
|
||||
painterResource(MR.images.ic_warning_filled),
|
||||
MR.strings.icon_descr_file,
|
||||
onClick = {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.temporary_file_error),
|
||||
file.fileStatus.rcvFileError.errorInfo
|
||||
)
|
||||
}
|
||||
)
|
||||
is CIFileStatus.Invalid -> fileIcon(painterResource(MR.images.ic_question_mark), MR.strings.icon_descr_file)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+138
-29
@@ -252,6 +252,81 @@ private fun PlayPauseButton(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PlayablePlayPauseButton(
|
||||
audioPlaying: Boolean,
|
||||
sent: Boolean,
|
||||
hasText: Boolean,
|
||||
progress: State<Int>,
|
||||
duration: State<Int>,
|
||||
strokeWidth: Float,
|
||||
strokeColor: Color,
|
||||
error: Boolean,
|
||||
play: () -> Unit,
|
||||
pause: () -> Unit,
|
||||
longClick: () -> Unit,
|
||||
) {
|
||||
val angle = 360f * (progress.value.toDouble() / duration.value).toFloat()
|
||||
if (hasText) {
|
||||
IconButton({ if (!audioPlaying) play() else pause() }, Modifier.size(56.dp).drawRingModifier(angle, strokeColor, strokeWidth)) {
|
||||
Icon(
|
||||
if (audioPlaying) painterResource(MR.images.ic_pause_filled) else painterResource(MR.images.ic_play_arrow_filled),
|
||||
contentDescription = null,
|
||||
Modifier.size(36.dp),
|
||||
tint = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
} else {
|
||||
PlayPauseButton(audioPlaying, sent, angle, strokeWidth, strokeColor, true, error, play, pause, longClick = longClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun VoiceMsgLoadingProgressIndicator() {
|
||||
Box(
|
||||
Modifier
|
||||
.size(56.dp)
|
||||
.clip(RoundedCornerShape(4.dp)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
ProgressIndicator()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FileStatusIcon(
|
||||
sent: Boolean,
|
||||
icon: ImageResource,
|
||||
longClick: () -> Unit,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
val sentColor = MaterialTheme.appColors.sentMessage
|
||||
val receivedColor = MaterialTheme.appColors.receivedMessage
|
||||
Surface(
|
||||
color = if (sent) sentColor else receivedColor,
|
||||
shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
|
||||
contentColor = LocalContentColor.current
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.defaultMinSize(minWidth = 56.dp, minHeight = 56.dp)
|
||||
.combinedClickable(
|
||||
onClick = onClick,
|
||||
onLongClick = longClick
|
||||
)
|
||||
.onRightClick { longClick() },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
painterResource(icon),
|
||||
contentDescription = null,
|
||||
Modifier.size(36.dp),
|
||||
tint = MaterialTheme.colors.secondary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun VoiceMsgIndicator(
|
||||
file: CIFile?,
|
||||
@@ -268,39 +343,73 @@ private fun VoiceMsgIndicator(
|
||||
) {
|
||||
val strokeWidth = with(LocalDensity.current) { 3.dp.toPx() }
|
||||
val strokeColor = MaterialTheme.colors.primary
|
||||
if (file != null && file.loaded && progress != null && duration != null) {
|
||||
val angle = 360f * (progress.value.toDouble() / duration.value).toFloat()
|
||||
if (hasText) {
|
||||
IconButton({ if (!audioPlaying) play() else pause() }, Modifier.size(56.dp).drawRingModifier(angle, strokeColor, strokeWidth)) {
|
||||
Icon(
|
||||
if (audioPlaying) painterResource(MR.images.ic_pause_filled) else painterResource(MR.images.ic_play_arrow_filled),
|
||||
contentDescription = null,
|
||||
Modifier.size(36.dp),
|
||||
tint = MaterialTheme.colors.primary
|
||||
)
|
||||
when {
|
||||
file?.fileStatus is CIFileStatus.SndStored ->
|
||||
if (file.fileProtocol == FileProtocol.LOCAL && progress != null && duration != null) {
|
||||
PlayablePlayPauseButton(audioPlaying, sent, hasText, progress, duration, strokeWidth, strokeColor, error, play, pause, longClick = longClick)
|
||||
} else {
|
||||
VoiceMsgLoadingProgressIndicator()
|
||||
}
|
||||
} else {
|
||||
PlayPauseButton(audioPlaying, sent, angle, strokeWidth, strokeColor, true, error, play, pause, longClick = longClick)
|
||||
}
|
||||
} else {
|
||||
if (file?.fileStatus is CIFileStatus.RcvInvitation) {
|
||||
file?.fileStatus is CIFileStatus.SndTransfer ->
|
||||
VoiceMsgLoadingProgressIndicator()
|
||||
file != null && file.fileStatus is CIFileStatus.SndError ->
|
||||
FileStatusIcon(
|
||||
sent,
|
||||
MR.images.ic_close,
|
||||
longClick,
|
||||
onClick = {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.file_error),
|
||||
file.fileStatus.sndFileError.errorInfo
|
||||
)
|
||||
}
|
||||
)
|
||||
file != null && file.fileStatus is CIFileStatus.SndWarning ->
|
||||
FileStatusIcon(
|
||||
sent,
|
||||
MR.images.ic_warning_filled,
|
||||
longClick,
|
||||
onClick = {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.temporary_file_error),
|
||||
file.fileStatus.sndFileError.errorInfo
|
||||
)
|
||||
}
|
||||
)
|
||||
file?.fileStatus is CIFileStatus.RcvInvitation ->
|
||||
PlayPauseButton(audioPlaying, sent, 0f, strokeWidth, strokeColor, true, error, { receiveFile(file.fileId) }, {}, longClick = longClick)
|
||||
} else if (file?.fileStatus is CIFileStatus.RcvTransfer
|
||||
|| file?.fileStatus is CIFileStatus.RcvAccepted
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.size(56.dp)
|
||||
.clip(RoundedCornerShape(4.dp)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
ProgressIndicator()
|
||||
}
|
||||
} else if (file?.fileStatus is CIFileStatus.RcvAborted) {
|
||||
file?.fileStatus is CIFileStatus.RcvTransfer || file?.fileStatus is CIFileStatus.RcvAccepted ->
|
||||
VoiceMsgLoadingProgressIndicator()
|
||||
file?.fileStatus is CIFileStatus.RcvAborted ->
|
||||
PlayPauseButton(audioPlaying, sent, 0f, strokeWidth, strokeColor, true, error, { receiveFile(file.fileId) }, {}, longClick = longClick, icon = MR.images.ic_sync_problem)
|
||||
} else {
|
||||
file != null && file.fileStatus is CIFileStatus.RcvError ->
|
||||
FileStatusIcon(
|
||||
sent,
|
||||
MR.images.ic_close,
|
||||
longClick,
|
||||
onClick = {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.file_error),
|
||||
file.fileStatus.rcvFileError.errorInfo
|
||||
)
|
||||
}
|
||||
)
|
||||
file != null && file.fileStatus is CIFileStatus.RcvWarning ->
|
||||
FileStatusIcon(
|
||||
sent,
|
||||
MR.images.ic_warning_filled,
|
||||
longClick,
|
||||
onClick = {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(MR.strings.temporary_file_error),
|
||||
file.fileStatus.rcvFileError.errorInfo
|
||||
)
|
||||
}
|
||||
)
|
||||
file != null && file.loaded && progress != null && duration != null ->
|
||||
PlayablePlayPauseButton(audioPlaying, sent, hasText, progress, duration, strokeWidth, strokeColor, error, play, pause, longClick = longClick)
|
||||
else ->
|
||||
PlayPauseButton(audioPlaying, sent, 0f, strokeWidth, strokeColor, false, false, {}, {}, longClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -271,6 +271,11 @@
|
||||
<string name="srv_error_host">Server address is incompatible with network settings.</string>
|
||||
<string name="srv_error_version">Server version is incompatible with network settings.</string>
|
||||
|
||||
<!-- CIFileStatus errors -->
|
||||
<string name="file_error_auth">Wrong key or unknown file chunk address - most likely file is deleted.</string>
|
||||
<string name="file_error_no_file">File not found - most likely file was deleted or cancelled.</string>
|
||||
<string name="file_error_relay">File server error: %1$s</string>
|
||||
|
||||
<!-- Chat Actions - ChatItemView.kt (and general) -->
|
||||
<string name="reply_verb">Reply</string>
|
||||
<string name="share_verb">Share</string>
|
||||
@@ -408,6 +413,8 @@
|
||||
<string name="error_saving_file">Error saving file</string>
|
||||
<string name="loading_remote_file_title">Loading the file </string>
|
||||
<string name="loading_remote_file_desc">Please, wait while the file is being loaded from the linked mobile</string>
|
||||
<string name="file_error">File error</string>
|
||||
<string name="temporary_file_error">Temporary file error</string>
|
||||
|
||||
<!-- Voice messages -->
|
||||
<string name="voice_message">Voice message</string>
|
||||
@@ -1403,6 +1410,8 @@
|
||||
<string name="info_row_database_id">Database ID</string>
|
||||
<string name="info_row_debug_delivery">Debug delivery</string>
|
||||
<string name="info_row_updated_at">Record updated at</string>
|
||||
<string name="info_row_message_status">Message status</string>
|
||||
<string name="info_row_file_status">File status</string>
|
||||
<string name="info_row_sent_at">Sent at</string>
|
||||
<string name="info_row_created_at">Created at</string>
|
||||
<string name="info_row_received_at">Received at</string>
|
||||
@@ -1411,6 +1420,8 @@
|
||||
<string name="info_row_disappears_at">Disappears at</string>
|
||||
<string name="share_text_database_id">Database ID: %d</string>
|
||||
<string name="share_text_updated_at">Record updated at: %s</string>
|
||||
<string name="share_text_message_status">Message status: %s</string>
|
||||
<string name="share_text_file_status">File status: %s</string>
|
||||
<string name="share_text_sent_at">Sent at: %s</string>
|
||||
<string name="share_text_created_at">Created at: %s</string>
|
||||
<string name="share_text_received_at">Received at: %s</string>
|
||||
|
||||
Reference in New Issue
Block a user