Авторизация, нерабочий чат

This commit is contained in:
Халимов Рустам
2026-04-14 01:41:51 +03:00
parent 1fb1be47dd
commit dc051fa9ae
22 changed files with 160 additions and 49 deletions
@@ -1,6 +1,5 @@
package chats.presentation.chat_detail
import android.Manifest
import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
@@ -23,13 +22,13 @@ import chats.presentation.components.EmojiPicker
import chats.presentation.components.MessageBubble
import core.utils.VoiceRecorder
import core.utils.copyUriToFile
import kotlinx.coroutines.launch
import java.io.File
import ru.knot.messager.R
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ChatDetailScreen(
chatId: String,
chatName: String,
viewModel: ChatDetailViewModel,
onBack: () -> Unit
@@ -52,6 +51,11 @@ fun ChatDetailScreen(
}
}
// Загрузка данных чата при входе
LaunchedEffect(chatId) {
viewModel.setChatId(chatId)
}
// Автопрокрутка к последнему сообщению
LaunchedEffect(state.messages.size) {
if (state.messages.isNotEmpty()) {
@@ -112,7 +116,7 @@ fun ChatDetailScreen(
items(state.messages) { message ->
MessageBubble(
message = message,
isCurrentUser = message.senderId == "CURRENT_USER_ID" // TODO: Get from Auth
isCurrentUser = message.senderId == viewModel.getCurrentUserId()
)
}
}
@@ -163,7 +167,6 @@ fun ChatDetailScreen(
} else {
voiceRecorder.stopRecording()
isRecording = false
// TODO: Send voice file
}
}
) {
@@ -8,6 +8,7 @@ import chats.domain.model.Message
import chats.domain.repository.ChatRepository
import chats.data.repository.toDomain
import core.network.ServerConfig
import core.security.TokenManager
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
@@ -30,7 +31,8 @@ data class ChatDetailState(
class ChatDetailViewModel @Inject constructor(
private val repository: ChatRepository,
private val signalrClient: ChatHubClient,
private val serverConfig: ServerConfig
private val serverConfig: ServerConfig,
private val tokenManager: TokenManager
) : ViewModel() {
private val _state = MutableStateFlow(ChatDetailState())
@@ -48,6 +50,10 @@ class ChatDetailViewModel @Inject constructor(
) }
}
fun getCurrentUserId(): String {
return tokenManager.getUserId() ?: ""
}
fun setChatId(chatId: String) {
currentChatId = chatId
loadMessages(chatId)
@@ -58,7 +64,9 @@ class ChatDetailViewModel @Inject constructor(
viewModelScope.launch {
_state.update { it.copy(isLoading = true) }
try {
val messages = repository.getMessages(chatId)
// Бэкенд обычно возвращает сообщения от новых к старым.
// Для чата нам нужно наоборот: старые вверху, новые внизу.
val messages = repository.getMessages(chatId).reversed()
_state.update { it.copy(messages = messages, isLoading = false) }
} catch (e: Exception) {
_state.update { it.copy(isLoading = false, error = e.localizedMessage) }
@@ -146,6 +154,15 @@ class ChatDetailViewModel @Inject constructor(
_state.update { it.copy(error = "File too large") }
return
}
// Upload logic...
viewModelScope.launch {
try {
val url = repository.uploadMedia(file)
// After upload, we might want to send a message with this media
// For now, let's just log it or handle as per app requirements
} catch (e: Exception) {
_state.update { it.copy(error = e.localizedMessage) }
}
}
}
}
@@ -7,6 +7,7 @@ import chats.domain.repository.ChatRepository
import chats.data.remote.signalr.ChatHubClient
import chats.data.remote.signalr.ChatEvent
import core.network.ServerConfig
import core.security.TokenManager
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
@@ -24,7 +25,8 @@ data class ChatListState(
class ChatListViewModel @Inject constructor(
private val repository: ChatRepository,
private val signalrClient: ChatHubClient,
private val serverConfig: ServerConfig
private val serverConfig: ServerConfig,
private val tokenManager: TokenManager
) : ViewModel() {
private val _state = MutableStateFlow(ChatListState())
@@ -41,6 +43,8 @@ class ChatListViewModel @Inject constructor(
observeSignalREvents()
}
private fun getCurrentUserId(): String = tokenManager.getUserId() ?: ""
fun loadChats() {
viewModelScope.launch {
_state.update { it.copy(isLoading = true) }
@@ -61,7 +65,8 @@ class ChatListViewModel @Inject constructor(
updateChatsWithNewMessage(event)
}
is ChatEvent.NewChat -> {
_state.update { it.copy(chats = listOf(event.chat.toDomain()) + it.chats) }
val currentUserId = getCurrentUserId()
_state.update { it.copy(chats = listOf(event.chat.toDomain(currentUserId)) + it.chats) }
}
else -> Unit
}
@@ -69,6 +74,7 @@ class ChatListViewModel @Inject constructor(
.launchIn(viewModelScope)
}
private fun updateChatsWithNewMessage(event: ChatEvent.NewMessage) {
_state.update { currentState ->
val updatedChats = currentState.chats.map { chat ->
@@ -17,6 +17,10 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chats.domain.model.Chat
import androidx.compose.foundation.shape.RoundedCornerShape
import coil.compose.AsyncImage
import androidx.compose.ui.layout.ContentScale
@Composable
fun ChatItem(
chat: Chat,
@@ -29,18 +33,27 @@ fun ChatItem(
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
// Заглушка аватара (в реальном приложении используем Coil для URL)
// Аватар (мягкий квадрат)
Box(
modifier = Modifier
.size(50.dp)
.clip(CircleShape)
.clip(RoundedCornerShape(12.dp))
.background(MaterialTheme.colorScheme.primaryContainer),
contentAlignment = Alignment.Center
) {
Text(
text = chat.name.take(1).uppercase(),
style = MaterialTheme.typography.titleMedium
)
if (chat.avatar != null) {
AsyncImage(
model = chat.avatar,
contentDescription = null,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
} else {
Text(
text = chat.name.take(1).uppercase(),
style = MaterialTheme.typography.titleMedium
)
}
}
Spacer(modifier = Modifier.width(12.dp))
@@ -38,22 +38,18 @@ fun MessageBubble(
var showReactionPicker by remember { mutableStateOf(false) }
val backgroundColor = if (isCurrentUser) {
MaterialTheme.colorScheme.primary
Color(0xFF3390EC) // Telegram Blue
} else {
MaterialTheme.colorScheme.surfaceVariant
Color(0xFF2B2B2B) // Dark Grey
}
val contentColor = if (isCurrentUser) {
MaterialTheme.colorScheme.onPrimary
} else {
MaterialTheme.colorScheme.onSurfaceVariant
}
val contentColor = Color.White
val alignment = if (isCurrentUser) Alignment.CenterEnd else Alignment.CenterStart
val shape = if (isCurrentUser) {
RoundedCornerShape(16.dp, 16.dp, 4.dp, 16.dp)
RoundedCornerShape(12.dp, 12.dp, 4.dp, 12.dp)
} else {
RoundedCornerShape(16.dp, 16.dp, 16.dp, 4.dp)
RoundedCornerShape(12.dp, 12.dp, 12.dp, 4.dp)
}
val isVoiceMessage = message.mediaType == MediaType.AUDIO &&