diff --git a/android/app/src/main/java/ru/daemonlord/messenger/ui/chat/ChatScreen.kt b/android/app/src/main/java/ru/daemonlord/messenger/ui/chat/ChatScreen.kt index d10f080..e1b020e 100644 --- a/android/app/src/main/java/ru/daemonlord/messenger/ui/chat/ChatScreen.kt +++ b/android/app/src/main/java/ru/daemonlord/messenger/ui/chat/ChatScreen.kt @@ -82,6 +82,7 @@ import androidx.compose.animation.core.tween import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext @@ -590,7 +591,9 @@ private fun ChatScreen( var showDeleteDialog by remember { mutableStateOf(false) } var showInlineSearch by remember { mutableStateOf(false) } var showEmojiPicker by remember { mutableStateOf(false) } + var showAttachmentSheet by remember { mutableStateOf(false) } var emojiPickerTab by remember { mutableStateOf(ComposerPickerTab.Emoji) } + var useCircleRecording by rememberSaveable(state.chatId) { mutableStateOf(false) } var gifSearchQuery by remember { mutableStateOf("") } var giphySearchItems by remember { mutableStateOf>(emptyList()) } var isGiphyLoading by remember { mutableStateOf(false) } @@ -605,6 +608,7 @@ private fun ChatScreen( val actionSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val forwardSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val chatInfoSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + val attachmentSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val isTabletLayout = LocalConfiguration.current.screenWidthDp >= 840 val adaptiveHorizontalPadding = if (isTabletLayout) 72.dp else 0.dp val chatInfoEntries = remember(state.messages, context) { buildChatInfoEntries(state.messages, context) } @@ -1769,40 +1773,7 @@ private fun ChatScreen( color = MaterialTheme.colorScheme.primary.copy(alpha = 0.14f), ) { IconButton( - onClick = onCapturePhoto, - enabled = state.canSendMessages && !state.isUploadingMedia && !state.isRecordingVoice, - ) { - Icon(imageVector = Icons.Filled.AddAPhoto, contentDescription = "Capture photo") - } - } - Surface( - shape = CircleShape, - color = MaterialTheme.colorScheme.primary.copy(alpha = 0.14f), - ) { - IconButton( - onClick = onCaptureVideo, - enabled = state.canSendMessages && !state.isUploadingMedia && !state.isRecordingVoice, - ) { - Icon(imageVector = Icons.Filled.Movie, contentDescription = "Capture video") - } - } - Surface( - shape = CircleShape, - color = MaterialTheme.colorScheme.primary.copy(alpha = 0.14f), - ) { - IconButton( - onClick = onCaptureCircleVideo, - enabled = state.canSendMessages && !state.isUploadingMedia && !state.isRecordingVoice, - ) { - Icon(imageVector = Icons.Filled.RadioButtonChecked, contentDescription = "Record circle video") - } - } - Surface( - shape = CircleShape, - color = MaterialTheme.colorScheme.primary.copy(alpha = 0.14f), - ) { - IconButton( - onClick = onPickMedia, + onClick = { showAttachmentSheet = true }, enabled = state.canSendMessages && !state.isUploadingMedia && !state.isRecordingVoice, ) { if (state.isUploadingMedia) { @@ -1812,6 +1783,28 @@ private fun ChatScreen( } } } + Surface( + shape = CircleShape, + color = MaterialTheme.colorScheme.primary.copy(alpha = 0.14f), + ) { + IconButton( + onClick = { useCircleRecording = !useCircleRecording }, + enabled = state.canSendMessages && !state.isUploadingMedia && !state.isRecordingVoice, + ) { + Icon( + imageVector = if (useCircleRecording) { + Icons.Filled.RadioButtonChecked + } else { + Icons.Filled.Mic + }, + contentDescription = if (useCircleRecording) { + "Circle mode enabled" + } else { + "Voice mode enabled" + }, + ) + } + } val canSend = state.canSendMessages && !state.isSending && !state.isUploadingMedia && @@ -1828,6 +1821,21 @@ private fun ChatScreen( Icon(imageVector = Icons.AutoMirrored.Filled.Send, contentDescription = "Send") } } + } else if (useCircleRecording) { + Surface( + shape = CircleShape, + color = MaterialTheme.colorScheme.primary.copy(alpha = 0.18f), + ) { + IconButton( + onClick = onCaptureCircleVideo, + enabled = state.canSendMessages && !state.isUploadingMedia, + ) { + Icon( + imageVector = Icons.Filled.RadioButtonChecked, + contentDescription = "Record circle video", + ) + } + } } else { VoiceHoldToRecordButton( enabled = state.canSendMessages && !state.isUploadingMedia, @@ -1843,6 +1851,89 @@ private fun ChatScreen( } } + if (showAttachmentSheet) { + ModalBottomSheet( + onDismissRequest = { showAttachmentSheet = false }, + sheetState = attachmentSheetState, + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + verticalArrangement = Arrangement.spacedBy(10.dp), + ) { + Text( + text = stringResource(id = R.string.chat_attachment_title), + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.SemiBold, + ) + Surface( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .clickable { + showAttachmentSheet = false + onPickMedia() + }, + color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.35f), + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp, vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + Icon(imageVector = Icons.Filled.AttachFile, contentDescription = null) + Text(stringResource(id = R.string.chat_attachment_gallery_file)) + } + } + Surface( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .clickable { + showAttachmentSheet = false + onCapturePhoto() + }, + color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.35f), + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp, vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + Icon(imageVector = Icons.Filled.AddAPhoto, contentDescription = null) + Text(stringResource(id = R.string.chat_attachment_take_photo)) + } + } + Surface( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .clickable { + showAttachmentSheet = false + onCaptureVideo() + }, + color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.35f), + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp, vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp), + ) { + Icon(imageVector = Icons.Filled.Movie, contentDescription = null) + Text(stringResource(id = R.string.chat_attachment_take_video)) + } + } + } + } + } + if (!state.errorMessage.isNullOrBlank()) { Text( text = state.errorMessage, diff --git a/android/app/src/main/res/values-ru/strings.xml b/android/app/src/main/res/values-ru/strings.xml index 743bc1c..458502b 100644 --- a/android/app/src/main/res/values-ru/strings.xml +++ b/android/app/src/main/res/values-ru/strings.xml @@ -147,6 +147,10 @@ Кикнуть Разбанить Видео + Вложение + Галерея / Файл + Сделать фото + Снять видео GIF Стикер Эмодзи diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 90ed85a..a0798f7 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -147,6 +147,10 @@ Kick Unban Video + Attachment + Gallery / File + Take photo + Record video GIF Sticker Emoji