diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/MainActivity.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/MainActivity.kt index f140aba5..03b33f5d 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/MainActivity.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/MainActivity.kt @@ -206,6 +206,7 @@ class MainActivity : AppCompatActivity() { if (it.startsWith("dm:")) ConversationTarget.Pm(it.removePrefix("dm:")) else ConversationTarget.Channel(it) } var photoMessage by remember { mutableStateOf(null) } + var photoTargetId by rememberSaveable { mutableStateOf(null) } var hadCall by rememberSaveable { mutableStateOf(false) } var screenBeforeCall by rememberSaveable { mutableStateOf(null) } val snackbarHostState = remember { SnackbarHostState() } @@ -371,8 +372,14 @@ class MainActivity : AppCompatActivity() { onBack = { screen = Screen.ChannelList }, onPlayVideo = { msg -> videoMessage = msg }, onViewPhoto = { msg -> photoMessage = msg }, - onRecordVideo = { videoTargetId = ConversationTarget.Channel(channel.id).nativeId; screen = Screen.VideoRecord }, - onCapturePhoto = { screen = Screen.PhotoRecord } + onRecordVideo = { + val target = ConversationTarget.Channel(channel.id) + videoTargetId = target.nativeId; vm.captureCameraReply(target); screen = Screen.VideoRecord + }, + onCapturePhoto = { + photoTargetId = channel.id + vm.captureCameraReply(ConversationTarget.Channel(channel.id)); screen = Screen.PhotoRecord + } ) } } @@ -399,7 +406,10 @@ class MainActivity : AppCompatActivity() { messages = dmMessages, viewModel = vm, onSendMessage = { vm.sendDmMessage(it) }, - onRecordVideo = { videoTargetId = ConversationTarget.Pm(dm.convId).nativeId; screen = Screen.VideoRecord }, + onRecordVideo = { + val target = ConversationTarget.Pm(dm.convId) + videoTargetId = target.nativeId; vm.captureCameraReply(target); screen = Screen.VideoRecord + }, onPlayVideo = { videoMessage = it }, onBack = { vm.closeDm(); screen = Screen.ChannelList } ) @@ -417,7 +427,7 @@ class MainActivity : AppCompatActivity() { if (target != null) { VideoRecordScreen( onSend = { path -> - vm.sendVideoMessage(path, target) + vm.sendVideoMessage(path, target, vm.cameraReply) screen = if (target is ConversationTarget.Pm) Screen.Dm else Screen.Chat }, onClose = { screen = if (target is ConversationTarget.Pm) Screen.Dm else Screen.Chat } @@ -429,7 +439,7 @@ class MainActivity : AppCompatActivity() { if (channel != null) { PhotoRecordScreen( onSend = { path, width, height -> - vm.sendPhotoMessage(path, width, height) + vm.sendPhotoMessage(path, width, height, photoTargetId ?: channel.id, vm.cameraReply) screen = Screen.Chat }, onClose = { screen = Screen.Chat } diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/ChatScreen.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/ChatScreen.kt index 75d67a0a..1952df32 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/ChatScreen.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/ui/screens/ChatScreen.kt @@ -117,10 +117,30 @@ fun ChatScreen( } val playbackState by viewModel.playbackState.collectAsState() + val originals by viewModel.replyOriginals.collectAsState() + val anchor by viewModel.historyAnchor.collectAsState() + val jump by viewModel.messageJump.collectAsState() + val originalMissing by viewModel.originalMissing.collectAsState() + var highlighted by remember(channel.id) { mutableStateOf("") } + if (originalMissing) AlertDialog(onDismissRequest = viewModel::dismissOriginalMissing, + text = { Text(stringResource(R.string.reply_original_missing)) }, + confirmButton = { TextButton(onClick = viewModel::dismissOriginalMissing) { Text(stringResource(R.string.close)) } }) + + LaunchedEffect(jump) { + val destination = jump ?: return@LaunchedEffect + val index = if (destination.messageId.isEmpty()) 0 + else messages.asReversed().indexOfFirst { it.messageId == destination.messageId }.let { if (it < 0) -1 else it + 1 } + if (index >= 0) { + listState.scrollToItem(index) + highlighted = destination.messageId + kotlinx.coroutines.delay(1500) + highlighted = "" + } + } /* своё сообщение — подтягиваемся вниз (в reverseLayout индекс 0 — это низ) */ LaunchedEffect(messages.size) { - if (messages.lastOrNull()?.isOutgoing == true) { + if (anchor.isEmpty() && messages.lastOrNull()?.isOutgoing == true) { listState.scrollToItem(0) } } @@ -196,8 +216,11 @@ fun ChatScreen( onDownloadFile = { viewModel.downloadAttachment(it) }, onOpenFile = { if (it.filePath.isNotEmpty()) { pendingSaveFilePath = it.filePath; saveFileLauncher.launch(it.text) } }, onOpenImage = onViewPhoto, onPlayVideo = onPlayVideo, onInviteLink = viewModel::openInviteLink, - reactionCatalog = reactionCatalog, onVote = viewModel::vote, onReact = viewModel::react, authorName = viewModel::radioName + reactionCatalog = reactionCatalog, onVote = viewModel::vote, onReact = viewModel::react, authorName = viewModel::radioName, + onReply = viewModel::selectReply, onOriginal = viewModel::showOriginal, originals = originals, highlightedId = highlighted ) + if (anchor.isNotEmpty()) FilledTonalButton(onClick = viewModel::showLatestMessages, + modifier = Modifier.align(Alignment.BottomEnd).padding(12.dp)) { Text(stringResource(R.string.reply_latest)) } if (radioActive && !overlayShown) { RadioPttButton( modifier = Modifier.align(Alignment.TopCenter).padding(top = 12.dp),