Загрузка данных
}
Text(
text = "Моника – это ИИ. Она может ошибаться. Рекомендую перепроверять информацию.",
fontSize = 9.sp,
color = Color.Gray,
textAlign = TextAlign.Center,
lineHeight = 10.sp,
modifier = Modifier.padding(start = 4.dp)
)
}
}
}
}
}
if (isThinking) {
item { BouncingDots() }
}
}
}
}
// Поле ввода и отправки сообщения
Surface(
Modifier.fillMaxWidth().padding(8.dp),
shape = RoundedCornerShape(25.dp),
color = MaterialTheme.colorScheme.surfaceVariant
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(horizontal = 4.dp, vertical = 4.dp)
) {
IconButton(onClick = { showBottomSheet = true }) {
Icon(Icons.Default.Add, contentDescription = null)
}
TextField(
value = inputText,
onValueChange = { inputText = it },
modifier = Modifier.weight(1f),
placeholder = { Text("Спросить Монику....") },
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
)
)
IconButton(
onClick = {
if (inputText.isBlank()) return@IconButton
val userMsg = inputText
messages.add(ChatMessage(text = userMsg, isUser = true))
inputText = ""
autoSaveCurrentChat()
isThinking = true
scope.launch {
try {
val model = generativeModel
if (model != null) {
val response = model.generateContent(userMsg)
messages.add(
ChatMessage(
text = response.text ?: "...",
isUser = false
)
)
} else {
messages.add(
ChatMessage(text = "", isUser = false, isError = true)
)
}
} catch (e: Exception) {
messages.add(
ChatMessage(text = "", isUser = false, isError = true)
)
} finally {
autoSaveCurrentChat()
isThinking = false
}
}
},
modifier = Modifier.background(Color(0xFF8E24AA), CircleShape)
) {
Icon(Icons.Default.Send, contentDescription = null, tint = Color.White)
}
}
}
}
}
// Диалоговое окно для редактирования сообщения
if (editMessageData != null) {
AlertDialog(
onDismissRequest = { editMessageData = null },
title = { Text("Изменить сообщение") },
text = {
TextField(
value = editedText,
onValueChange = { editedText = it },
modifier = Modifier.fillMaxWidth()
)
},
confirmButton = {
Button(onClick = {
val index = messages.indexOf(editMessageData)
if (index != -1 && editedText.isNotBlank()) {
messages[index] = messages[index].copy(text = editedText)
autoSaveCurrentChat()
}
editMessageData = null
}) {
Text("Сохранить")
}
},
dismissButton = {
TextButton(onClick = { editMessageData = null }) {
Text("Отмена")
}
}
)
}
// Диалоговое окно для создания нового чата
if (showNewChatDialog) {
AlertDialog(
onDismissRequest = { showNewChatDialog = false },
title = { Text("Назовите текущий диалог для сохранения") },
text = {
TextField(
value = newChatName,
onValueChange = { newChatName = it },
modifier = Modifier.fillMaxWidth()
)
},
confirmButton = {
Button(onClick = {
if (newChatName.isNotBlank()) {
val newChat = ChatSession(
id = UUID.randomUUID().toString(),
name = newChatName,
messages = messages.toList()
)
savedChats.add(newChat)
ChatManager.saveChats(context, savedChats)
messages.clear()
currentChatId = null
newChatName = ""
showNewChatDialog = false
scope.launch { leftDrawerState.close() }
}
}) {
Text("Сохранить")
}
}
)
}
// Нижняя шторка для вложений
if (showBottomSheet) {
ModalBottomSheet(
onDismissRequest = { showBottomSheet = false }
) {
Box(
Modifier.fillMaxWidth().padding(bottom = 32.dp),
contentAlignment = Alignment.Center
) {
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
TextButton(onClick = {}) { Text("Сделать фото", fontSize = 18.sp) }
Spacer(Modifier.height(8.dp))
TextButton(onClick = {}) { Text("Прикрепить фото", fontSize = 18.sp) }
Spacer(Modifier.height(8.dp))
TextButton(onClick = {}) { Text("Прикрепить материал/файл", fontSize = 18.sp) }
}
Text(
text = "Скоро....",
color = Color.Red,
fontSize = 42.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.rotate(-45f)
)
}
}
}
}
}
}
}
}
}
}
}
// Вспомогательные классы и функции
@Composable
fun BouncingDots() {
val infiniteTransition = rememberInfiniteTransition()
val dot1 by infiniteTransition.animateFloat(0f, -10f, infiniteRepeatable(tween(300), RepeatMode.Reverse))
val dot2 by infiniteTransition.animateFloat(0f, -10f, infiniteRepeatable(tween(300, 100), RepeatMode.Reverse))
val dot3 by infiniteTransition.animateFloat(0f, -10f, infiniteRepeatable(tween(300, 200), RepeatMode.Reverse))
Box(Modifier.fillMaxWidth().padding(vertical = 4.dp), contentAlignment = Alignment.CenterStart) {
Row(Modifier.padding(12.dp), verticalAlignment = Alignment.Bottom) {
Text("Думаю над ответом", color = MaterialTheme.colorScheme.onBackground)
Spacer(modifier = Modifier.width(2.dp))
Text(".", modifier = Modifier.offset(y = dot1.dp), fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onBackground)
Text(".", modifier = Modifier.offset(y = dot2.dp), fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onBackground)
Text(".", modifier = Modifier.offset(y = dot3.dp), fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onBackground)
}
}
}
data class ChatMessage(
val id: String = UUID.randomUUID().toString(),
val text: String,
val isUser: Boolean,
val isError: Boolean = false
)
data class ChatSession(
val id: String,
val name: String,
val messages: List<ChatMessage>
)
object ChatManager {
private const val PREFS_NAME = "monika_chats_prefs"
private const val KEY_CHATS = "saved_chats_json"
fun saveChats(context: Context, chats: List<ChatSession>) {
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
val jsonArray = JSONArray()
for (chat in chats) {
val chatObj = JSONObject()
chatObj.put("id", chat.id)
chatObj.put("name", chat.name)
val msgsArray = JSONArray()
for (msg in chat.messages) {
val msgObj = JSONObject()
msgObj.put("id", msg.id)
msgObj.put("text", msg.text)
msgObj.put("isUser", msg.isUser)
msgObj.put("isError", msg.isError)
msgsArray.put(msgObj)
}
chatObj.put("messages", msgsArray)
jsonArray.put(chatObj)
}
prefs.edit().putString(KEY_CHATS, jsonArray.toString()).apply()
}
fun loadChats(context: Context): List<ChatSession> {
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
val jsonString = prefs.getString(KEY_CHATS, "[]") ?: "[]"
val chats = mutableListOf<ChatSession>()
try {
val jsonArray = JSONArray(jsonString)
for (i in 0 until jsonArray.length()) {
val chatObj = jsonArray.getJSONObject(i)
val id = chatObj.getString("id")
val name = chatObj.getString("name")
val msgsArray = chatObj.getJSONArray("messages")
val msgs = mutableListOf<ChatMessage>()
for (j in 0 until msgsArray.length()) {
val msgObj = msgsArray.getJSONObject(j)
msgs.add(ChatMessage(
id = msgObj.getString("id"),
text = msgObj.getString("text"),
isUser = msgObj.getBoolean("isUser"),
isError = msgObj.optBoolean("isError", false)
))
}
chats.add(ChatSession(id, name, msgs))
}
} catch (e: Exception) {
e.printStackTrace()
}
return chats
}
}