Нормальный чат
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -71,7 +71,6 @@ fun ChatDetailScreen(
|
|||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val voiceRecorder = remember { VoiceRecorder(context) }
|
val voiceRecorder = remember { VoiceRecorder(context) }
|
||||||
var textInput by remember { mutableStateOf("") }
|
|
||||||
var isEmojiPickerVisible by remember { mutableStateOf(false) }
|
var isEmojiPickerVisible by remember { mutableStateOf(false) }
|
||||||
var isRecording by remember { mutableStateOf(false) }
|
var isRecording by remember { mutableStateOf(false) }
|
||||||
var recordingOffset by remember { mutableFloatStateOf(0f) }
|
var recordingOffset by remember { mutableFloatStateOf(0f) }
|
||||||
@@ -601,8 +600,8 @@ fun ChatDetailScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
BasicTextField(
|
BasicTextField(
|
||||||
value = textInput,
|
value = state.inputText,
|
||||||
onValueChange = { textInput = it },
|
onValueChange = { viewModel.onInputTextChanged(it) },
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.onFocusChanged {
|
.onFocusChanged {
|
||||||
@@ -611,23 +610,14 @@ fun ChatDetailScreen(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
textStyle = MaterialTheme.typography.bodyLarge.copy(color = MaterialTheme.colorScheme.onSurface),
|
textStyle = MaterialTheme.typography.bodyLarge.copy(color = MaterialTheme.colorScheme.onSurface),
|
||||||
|
cursorBrush = androidx.compose.ui.graphics.SolidColor(MaterialTheme.colorScheme.primary),
|
||||||
keyboardOptions = KeyboardOptions(
|
keyboardOptions = KeyboardOptions(
|
||||||
imeAction = ImeAction.Send,
|
imeAction = ImeAction.Default,
|
||||||
capitalization = KeyboardCapitalization.Sentences
|
capitalization = KeyboardCapitalization.Sentences
|
||||||
),
|
),
|
||||||
keyboardActions = KeyboardActions(
|
maxLines = 6,
|
||||||
onSend = {
|
|
||||||
if (textInput.isNotBlank() || state.pendingAttachments.isNotEmpty()) {
|
|
||||||
viewModel.sendMessage(textInput, onFail = { failedText ->
|
|
||||||
textInput = failedText
|
|
||||||
})
|
|
||||||
textInput = ""
|
|
||||||
isEmojiPickerVisible = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
decorationBox = { innerTextField ->
|
decorationBox = { innerTextField ->
|
||||||
if (textInput.isEmpty()) {
|
if (state.inputText.isEmpty()) {
|
||||||
Text(
|
Text(
|
||||||
stringResource(R.string.message_placeholder),
|
stringResource(R.string.message_placeholder),
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
@@ -643,15 +633,12 @@ fun ChatDetailScreen(
|
|||||||
Spacer(modifier = Modifier.width(8.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
|
||||||
// 3. Кнопка действия (в синем квадрате)
|
// 3. Кнопка действия (в синем квадрате)
|
||||||
val showMic = textInput.isEmpty() && state.pendingAttachments.isEmpty()
|
val showMic = state.inputText.isEmpty() && state.pendingAttachments.isEmpty()
|
||||||
|
|
||||||
Surface(
|
Surface(
|
||||||
onClick = {
|
onClick = {
|
||||||
if (!showMic) {
|
if (!showMic) {
|
||||||
viewModel.sendMessage(textInput, onFail = { failedText ->
|
viewModel.sendMessage()
|
||||||
textInput = failedText
|
|
||||||
})
|
|
||||||
textInput = ""
|
|
||||||
isEmojiPickerVisible = false
|
isEmojiPickerVisible = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -747,7 +734,7 @@ fun ChatDetailScreen(
|
|||||||
gifCategories = state.gifCategories,
|
gifCategories = state.gifCategories,
|
||||||
isGifsLoading = state.isGifsLoading,
|
isGifsLoading = state.isGifsLoading,
|
||||||
error = state.error,
|
error = state.error,
|
||||||
onEmojiSelected = { textInput += it },
|
onEmojiSelected = { viewModel.onInputTextChanged(state.inputText + it) },
|
||||||
onGifSelected = { url ->
|
onGifSelected = { url ->
|
||||||
viewModel.sendGif(url)
|
viewModel.sendGif(url)
|
||||||
isEmojiPickerVisible = false
|
isEmojiPickerVisible = false
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ data class ChatDetailState(
|
|||||||
val initialScrollIndex: Int? = null,
|
val initialScrollIndex: Int? = null,
|
||||||
val pendingAttachments: List<File> = emptyList(),
|
val pendingAttachments: List<File> = emptyList(),
|
||||||
val isUploading: Boolean = false,
|
val isUploading: Boolean = false,
|
||||||
val isCompressionEnabled: Boolean = true
|
val isCompressionEnabled: Boolean = true,
|
||||||
|
val inputText: String = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
@@ -277,11 +278,19 @@ class ChatDetailViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendMessage(text: String, onFail: (String) -> Unit = {}) {
|
fun onInputTextChanged(text: String) {
|
||||||
|
_state.update { it.copy(inputText = text) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendMessage(onFail: (String) -> Unit = {}) {
|
||||||
val chatId = currentChatId ?: return
|
val chatId = currentChatId ?: return
|
||||||
|
val text = _state.value.inputText
|
||||||
val pending = _state.value.pendingAttachments
|
val pending = _state.value.pendingAttachments
|
||||||
if (text.isBlank() && pending.isEmpty()) return
|
if (text.isBlank() && pending.isEmpty()) return
|
||||||
|
|
||||||
|
// Clear input immediately to avoid double clicks and ensure UI experience
|
||||||
|
_state.update { it.copy(inputText = "") }
|
||||||
|
|
||||||
val tempId = "temp_${System.currentTimeMillis()}"
|
val tempId = "temp_${System.currentTimeMillis()}"
|
||||||
val userId = getCurrentUserId()
|
val userId = getCurrentUserId()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user