Авторизация, нерабочий чат
This commit is contained in:
@@ -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 &&
|
||||
|
||||
Reference in New Issue
Block a user