Keep Android media preparation status scoped to its conversation
This commit is contained in:
1 parent
225e11265f
commit
79f0f2a07f
2 files changed
+29
-3
No files matched your search
+6
-1
@@ -41,6 +41,9 @@ fun ConversationComposer(target: ConversationTarget, viewModel: ChatViewModel, o
|
||||
val duration by viewModel.recordingDurationMs.collectAsState()
|
||||
val videoMode by viewModel.isVideoMode.collectAsState()
|
||||
var level by remember { mutableStateOf(0f) }
|
||||
val preparations by viewModel.attachmentPreparations.collectAsState()
|
||||
val preparation = preparations[target.nativeId]
|
||||
DisposableEffect(target) { onDispose { viewModel.cancelVoiceRecording(target) } }
|
||||
LaunchedEffect(recording) {
|
||||
while (recording) { level = NativeLib.voiceGetPeakLevel(); delay(80) }
|
||||
level = 0f
|
||||
@@ -64,7 +67,9 @@ fun ConversationComposer(target: ConversationTarget, viewModel: ChatViewModel, o
|
||||
}
|
||||
Column {
|
||||
if (error.isNotEmpty()) Text(error, color = MaterialTheme.colorScheme.error, modifier = Modifier.padding(8.dp))
|
||||
if (preparing) Text("Подготовка вложения…", modifier = Modifier.padding(8.dp))
|
||||
if (preparing || preparation?.jobs?.isNotEmpty() == true) Text("Подготовка медиа…", modifier = Modifier.padding(8.dp))
|
||||
else if (preparation?.failed == true) Text("Не удалось подготовить вложение", color = MaterialTheme.colorScheme.error,
|
||||
modifier = Modifier.padding(8.dp))
|
||||
InputBar(
|
||||
text = text, onTextChange = { text = it }, enabled = !preparing,
|
||||
onSend = {
|
||||
|
||||
@@ -23,6 +23,18 @@ import java.io.FileOutputStream
|
||||
private val IMAGE_EXTENSIONS = setOf("jpg", "jpeg", "png", "gif", "bmp", "webp")
|
||||
|
||||
class ChatViewModel : ViewModel() {
|
||||
data class AttachmentPreparation(val jobs: Set<String> = emptySet(), val failed: Boolean = false)
|
||||
private val _attachmentPreparations = MutableStateFlow<Map<String, AttachmentPreparation>>(emptyMap())
|
||||
val attachmentPreparations: StateFlow<Map<String, AttachmentPreparation>> = _attachmentPreparations
|
||||
|
||||
private fun preparationState(target: String, job: String, state: Int) {
|
||||
_attachmentPreparations.update { map ->
|
||||
val previous = map[target] ?: AttachmentPreparation()
|
||||
val value = if (state == 0) AttachmentPreparation(previous.jobs + job)
|
||||
else previous.copy(jobs = previous.jobs - job, failed = previous.failed || state == 2)
|
||||
if (value.jobs.isEmpty() && !value.failed) map - target else map + (target to value)
|
||||
}
|
||||
}
|
||||
|
||||
data class PlaybackState(
|
||||
val filePath: String? = null,
|
||||
@@ -525,6 +537,15 @@ class ChatViewModel : ViewModel() {
|
||||
if (data == null) return
|
||||
_dmConversations.value = repo?.parseDmConversations(String(data, Charsets.UTF_8)) ?: emptyList()
|
||||
}
|
||||
46 -> {
|
||||
if (data == null || data.size < 18) return
|
||||
val length = data[0].toInt() and 255
|
||||
val state = data.last().toInt() and 255
|
||||
if (data.size != length + 18 || state > 2) { LogManager.addLog("ERROR", "VM", "invalid preparation event"); return }
|
||||
val target = String(data, 1, length, Charsets.UTF_8)
|
||||
val job = android.util.Base64.encodeToString(data.copyOfRange(1 + length, 17 + length), android.util.Base64.NO_WRAP)
|
||||
preparationState(target, job, state)
|
||||
}
|
||||
43 -> { /* RADIO_TALK: [group_id:8][src:8][stream:2][on:1] */
|
||||
if (data == null || data.size < 19) return
|
||||
val bb = java.nio.ByteBuffer.wrap(data, 0, 19).order(java.nio.ByteOrder.LITTLE_ENDIAN)
|
||||
@@ -985,8 +1006,8 @@ class ChatViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelVoiceRecording() {
|
||||
if (recordingStopping) return
|
||||
fun cancelVoiceRecording(target: ConversationTarget? = null) {
|
||||
if (recordingStopping || !_isRecording.value || (target != null && target != recordingTarget)) return
|
||||
recordingStopping = true
|
||||
_isRecording.value = false
|
||||
_isRecordingLocked.value = false
|
||||
|
||||
Reference in new issue
Block a user