@@ -6,14 +6,11 @@ import android.os.Build
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.MaterialExpressiveTheme
|
||||
import androidx.compose.material3.MotionScheme
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
@@ -27,8 +24,6 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.util.Consumer
|
||||
import androidx.lifecycle.ViewModel
|
||||
@@ -235,14 +230,12 @@ fun App(
|
||||
NewIdentityScreen(accountViewModel)
|
||||
}
|
||||
entry<Screen.Chat> { key ->
|
||||
val initialRoom = remember(key.id) { chatRepository.getChatRoom(key.id) }
|
||||
|
||||
if (initialRoom != null) {
|
||||
val factory = remember(initialRoom) {
|
||||
val factory = remember(key) {
|
||||
object : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return ChatScreenViewModel(
|
||||
initialRoom,
|
||||
key.id,
|
||||
key.screening,
|
||||
accountRepository,
|
||||
chatRepository
|
||||
@@ -257,12 +250,6 @@ fun App(
|
||||
),
|
||||
accountViewModel
|
||||
)
|
||||
} else {
|
||||
// Handle the rare case where the room isn't in DB (e.g., invalid deep link)
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text("Room not found")
|
||||
}
|
||||
}
|
||||
}
|
||||
entry<Screen.NewChat> {
|
||||
NewChatScreen(accountViewModel, chatViewModel)
|
||||
|
||||
@@ -94,11 +94,12 @@ import rust.nostr.sdk.UnsignedEvent
|
||||
import su.reya.coop.LocalNavigator
|
||||
import su.reya.coop.LocalProfileCache
|
||||
import su.reya.coop.LocalSnackbarHostState
|
||||
import su.reya.coop.Room
|
||||
import su.reya.coop.RoomUiState
|
||||
import su.reya.coop.Screen
|
||||
import su.reya.coop.flow
|
||||
import su.reya.coop.formatAsGroup
|
||||
import su.reya.coop.shared.Avatar
|
||||
import su.reya.coop.uiStateFlow
|
||||
import su.reya.coop.viewmodel.AccountViewModel
|
||||
import su.reya.coop.viewmodel.ChatScreenViewModel
|
||||
|
||||
@@ -117,11 +118,25 @@ fun ChatScreen(
|
||||
val scope = rememberCoroutineScope()
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
val id = viewModel.id
|
||||
val currentUser by viewModel.currentUser.collectAsStateWithLifecycle()
|
||||
val pubkey = currentUser?.publicKey
|
||||
val chatRooms by viewModel.chatRooms.collectAsStateWithLifecycle()
|
||||
val room by remember(id) { derivedStateOf { chatRooms.firstOrNull { it.id == id } } }
|
||||
|
||||
val room by viewModel.room.collectAsStateWithLifecycle()
|
||||
val roomState by room.flow(profileCache, pubkey).collectAsStateWithLifecycle(RoomUiState())
|
||||
// Show empty screen
|
||||
if (room == null) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = "Something went wrong.",
|
||||
style = MaterialTheme.typography.titleMediumEmphasized,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val loading = viewModel.loading
|
||||
val newOtherMessages = viewModel.newOtherMessages
|
||||
@@ -131,6 +146,9 @@ fun ChatScreen(
|
||||
val groupedMessages =
|
||||
remember { derivedStateOf { messages.groupBy { it.createdAt().formatAsGroup() } } }
|
||||
|
||||
val roomState by (room as Room).uiStateFlow(profileCache, currentUser?.publicKey)
|
||||
.collectAsStateWithLifecycle(RoomUiState())
|
||||
|
||||
var text by remember { mutableStateOf("") }
|
||||
var selectedMessage by remember { mutableStateOf<Pair<MessageModel, Rect>?>(null) }
|
||||
var replyingTo by remember { mutableStateOf<MessageModel?>(null) }
|
||||
@@ -175,7 +193,9 @@ fun ChatScreen(
|
||||
}
|
||||
}
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = Modifier.blur(blurAmount),
|
||||
contentWindowInsets = ScaffoldDefaults.contentWindowInsets.union(WindowInsets.ime),
|
||||
@@ -187,7 +207,7 @@ fun ChatScreen(
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.clickable {
|
||||
room.members.firstOrNull()?.let { pubkey ->
|
||||
room?.members?.firstOrNull()?.let { pubkey ->
|
||||
navigator.navigate(Screen.Profile(pubkey.toBech32()))
|
||||
}
|
||||
}
|
||||
@@ -245,7 +265,7 @@ fun ChatScreen(
|
||||
.padding(bottom = innerPadding.calculateBottomPadding())
|
||||
) {
|
||||
if (requireScreening) {
|
||||
ScreenerCard(accountViewModel, room)
|
||||
room?.let { ScreenerCard(accountViewModel, it) }
|
||||
}
|
||||
|
||||
when (messages.isNotEmpty()) {
|
||||
@@ -263,7 +283,8 @@ fun ChatScreen(
|
||||
items = messagesInGroup,
|
||||
key = { it.ensureId().id()?.toHex()!! }
|
||||
) { event ->
|
||||
val model = rememberMessageModel(event, pubkey)
|
||||
val model =
|
||||
rememberMessageModel(event, currentUser?.publicKey)
|
||||
|
||||
val replyPreview =
|
||||
remember(model.replyEventIds, messages.size) {
|
||||
@@ -277,9 +298,7 @@ fun ChatScreen(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp)
|
||||
) {
|
||||
replyPreview?.let {
|
||||
ReplyPreview(it, model.isMine)
|
||||
}
|
||||
replyPreview?.let { ReplyPreview(it, model.isMine) }
|
||||
ChatMessage(
|
||||
model = model,
|
||||
modifier = Modifier.graphicsLayer {
|
||||
|
||||
@@ -71,7 +71,7 @@ data class RoomUiState(
|
||||
val isGroup: Boolean = false
|
||||
)
|
||||
|
||||
fun Room.flow(
|
||||
fun Room.uiStateFlow(
|
||||
profileCache: ProfileCache,
|
||||
currentUser: PublicKey? = null
|
||||
): Flow<RoomUiState> {
|
||||
|
||||
@@ -7,40 +7,30 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import rust.nostr.sdk.EventId
|
||||
import rust.nostr.sdk.UnsignedEvent
|
||||
import su.reya.coop.Profile
|
||||
import su.reya.coop.Room
|
||||
import su.reya.coop.repository.AccountRepository
|
||||
import su.reya.coop.repository.ChatRepository
|
||||
import su.reya.coop.roomId
|
||||
|
||||
class ChatScreenViewModel(
|
||||
initialRoom: Room,
|
||||
val id: Long,
|
||||
screening: Boolean,
|
||||
accountRepository: AccountRepository,
|
||||
private val chatRepository: ChatRepository,
|
||||
) : ViewModel(), ErrorHost by chatRepository {
|
||||
val currentUser: StateFlow<Profile?> = accountRepository.currentUserProfile
|
||||
val chatRooms: StateFlow<List<Room>> = chatRepository.chatRooms
|
||||
|
||||
var loading by mutableStateOf(true)
|
||||
var newOtherMessages by mutableIntStateOf(0)
|
||||
var requireScreening by mutableStateOf(screening)
|
||||
val messages = mutableStateListOf<UnsignedEvent>()
|
||||
|
||||
val currentUser = accountRepository.currentUserProfile
|
||||
val id = initialRoom.id
|
||||
|
||||
val room: StateFlow<Room> = chatRepository.chatRooms
|
||||
.map { rooms -> rooms.find { it.id == id } ?: initialRoom }
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(5000),
|
||||
initialValue = initialRoom
|
||||
)
|
||||
|
||||
init {
|
||||
loadMessages()
|
||||
connect()
|
||||
|
||||
Reference in New Issue
Block a user