Telegram-like composer: voice/circle toggle and unified attach actions
This commit is contained in:
@@ -82,6 +82,7 @@ import androidx.compose.animation.core.tween
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@@ -590,7 +591,9 @@ private fun ChatScreen(
|
|||||||
var showDeleteDialog by remember { mutableStateOf(false) }
|
var showDeleteDialog by remember { mutableStateOf(false) }
|
||||||
var showInlineSearch by remember { mutableStateOf(false) }
|
var showInlineSearch by remember { mutableStateOf(false) }
|
||||||
var showEmojiPicker by remember { mutableStateOf(false) }
|
var showEmojiPicker by remember { mutableStateOf(false) }
|
||||||
|
var showAttachmentSheet by remember { mutableStateOf(false) }
|
||||||
var emojiPickerTab by remember { mutableStateOf(ComposerPickerTab.Emoji) }
|
var emojiPickerTab by remember { mutableStateOf(ComposerPickerTab.Emoji) }
|
||||||
|
var useCircleRecording by rememberSaveable(state.chatId) { mutableStateOf(false) }
|
||||||
var gifSearchQuery by remember { mutableStateOf("") }
|
var gifSearchQuery by remember { mutableStateOf("") }
|
||||||
var giphySearchItems by remember { mutableStateOf<List<RemotePickerItem>>(emptyList()) }
|
var giphySearchItems by remember { mutableStateOf<List<RemotePickerItem>>(emptyList()) }
|
||||||
var isGiphyLoading by remember { mutableStateOf(false) }
|
var isGiphyLoading by remember { mutableStateOf(false) }
|
||||||
@@ -605,6 +608,7 @@ private fun ChatScreen(
|
|||||||
val actionSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
val actionSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||||
val forwardSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
val forwardSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||||
val chatInfoSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
val chatInfoSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||||
|
val attachmentSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||||
val isTabletLayout = LocalConfiguration.current.screenWidthDp >= 840
|
val isTabletLayout = LocalConfiguration.current.screenWidthDp >= 840
|
||||||
val adaptiveHorizontalPadding = if (isTabletLayout) 72.dp else 0.dp
|
val adaptiveHorizontalPadding = if (isTabletLayout) 72.dp else 0.dp
|
||||||
val chatInfoEntries = remember(state.messages, context) { buildChatInfoEntries(state.messages, context) }
|
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),
|
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.14f),
|
||||||
) {
|
) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onCapturePhoto,
|
onClick = { showAttachmentSheet = true },
|
||||||
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,
|
|
||||||
enabled = state.canSendMessages && !state.isUploadingMedia && !state.isRecordingVoice,
|
enabled = state.canSendMessages && !state.isUploadingMedia && !state.isRecordingVoice,
|
||||||
) {
|
) {
|
||||||
if (state.isUploadingMedia) {
|
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 &&
|
val canSend = state.canSendMessages &&
|
||||||
!state.isSending &&
|
!state.isSending &&
|
||||||
!state.isUploadingMedia &&
|
!state.isUploadingMedia &&
|
||||||
@@ -1828,6 +1821,21 @@ private fun ChatScreen(
|
|||||||
Icon(imageVector = Icons.AutoMirrored.Filled.Send, contentDescription = "Send")
|
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 {
|
} else {
|
||||||
VoiceHoldToRecordButton(
|
VoiceHoldToRecordButton(
|
||||||
enabled = state.canSendMessages && !state.isUploadingMedia,
|
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()) {
|
if (!state.errorMessage.isNullOrBlank()) {
|
||||||
Text(
|
Text(
|
||||||
text = state.errorMessage,
|
text = state.errorMessage,
|
||||||
|
|||||||
@@ -147,6 +147,10 @@
|
|||||||
<string name="chat_member_action_kick">Кикнуть</string>
|
<string name="chat_member_action_kick">Кикнуть</string>
|
||||||
<string name="chat_member_action_unban">Разбанить</string>
|
<string name="chat_member_action_unban">Разбанить</string>
|
||||||
<string name="chat_media_badge_video">Видео</string>
|
<string name="chat_media_badge_video">Видео</string>
|
||||||
|
<string name="chat_attachment_title">Вложение</string>
|
||||||
|
<string name="chat_attachment_gallery_file">Галерея / Файл</string>
|
||||||
|
<string name="chat_attachment_take_photo">Сделать фото</string>
|
||||||
|
<string name="chat_attachment_take_video">Снять видео</string>
|
||||||
<string name="chat_media_badge_gif">GIF</string>
|
<string name="chat_media_badge_gif">GIF</string>
|
||||||
<string name="chat_media_badge_sticker">Стикер</string>
|
<string name="chat_media_badge_sticker">Стикер</string>
|
||||||
<string name="chat_picker_tab_emoji">Эмодзи</string>
|
<string name="chat_picker_tab_emoji">Эмодзи</string>
|
||||||
|
|||||||
@@ -147,6 +147,10 @@
|
|||||||
<string name="chat_member_action_kick">Kick</string>
|
<string name="chat_member_action_kick">Kick</string>
|
||||||
<string name="chat_member_action_unban">Unban</string>
|
<string name="chat_member_action_unban">Unban</string>
|
||||||
<string name="chat_media_badge_video">Video</string>
|
<string name="chat_media_badge_video">Video</string>
|
||||||
|
<string name="chat_attachment_title">Attachment</string>
|
||||||
|
<string name="chat_attachment_gallery_file">Gallery / File</string>
|
||||||
|
<string name="chat_attachment_take_photo">Take photo</string>
|
||||||
|
<string name="chat_attachment_take_video">Record video</string>
|
||||||
<string name="chat_media_badge_gif">GIF</string>
|
<string name="chat_media_badge_gif">GIF</string>
|
||||||
<string name="chat_media_badge_sticker">Sticker</string>
|
<string name="chat_media_badge_sticker">Sticker</string>
|
||||||
<string name="chat_picker_tab_emoji">Emoji</string>
|
<string name="chat_picker_tab_emoji">Emoji</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user