Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,17 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.rounded.KeyboardArrowDown
import androidx.compose.material3.BottomAppBarDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FabPosition
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SmallFloatingActionButton
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
Expand All @@ -40,6 +43,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
Expand All @@ -62,6 +66,8 @@ import dev.chungjungsoo.gptmobile.data.database.entity.Message
import dev.chungjungsoo.gptmobile.data.model.ApiType
import dev.chungjungsoo.gptmobile.util.collectManagedState
import dev.chungjungsoo.gptmobile.util.multiScrollStateSaver
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

@OptIn(ExperimentalMaterial3Api::class)
@Composable
Expand All @@ -78,6 +84,7 @@ fun ChatScreen(
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()

val isIdle by chatViewModel.isIdle.collectManagedState()
val isLoaded by chatViewModel.isLoaded.collectManagedState()
val messages by chatViewModel.messages.collectManagedState()
val question by chatViewModel.question.collectManagedState()
val appEnabledPlatforms by chatViewModel.enabledPlatformsInApp.collectManagedState()
Expand All @@ -99,6 +106,8 @@ fun ChatScreen(
val latestMessageIndex = groupedMessages.keys.maxOrNull() ?: 0
val chatBubbleScrollStates = rememberSaveable(saver = multiScrollStateSaver) { MutableList(latestMessageIndex + 2) { ScrollState(0) } }

val scope = rememberCoroutineScope()

LaunchedEffect(latestMessageIndex) {
val opponentBubbles = ((latestMessageIndex + 1) / 2) + 1
val scrollStatesToAdd = opponentBubbles - chatBubbleScrollStates.size
Expand All @@ -114,6 +123,11 @@ fun ChatScreen(
listState.animateScrollToItem(groupedMessages.keys.size)
}

LaunchedEffect(isLoaded) {
delay(300)
listState.animateScrollToItem(groupedMessages.keys.size)
}

Scaffold(
modifier = Modifier
.fillMaxSize()
Expand All @@ -133,7 +147,17 @@ fun ChatScreen(
chatViewModel.askQuestion()
focusManager.clearFocus()
}
}
},
floatingActionButton = {
if (listState.canScrollForward) {
ScrollToBottomButton {
scope.launch {
listState.animateScrollToItem(groupedMessages.keys.size)
}
}
}
},
floatingActionButtonPosition = FabPosition.Center
) { innerPadding ->
groupedMessages.forEach { (i, k) -> Log.d("grouped", "idx: $i, data: $k") }
LazyColumn(
Expand Down Expand Up @@ -348,3 +372,14 @@ fun ChatInputBox(
)
}
}

@Composable
fun ScrollToBottomButton(onClick: () -> Unit) {
SmallFloatingActionButton(
onClick = onClick,
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
contentColor = MaterialTheme.colorScheme.onTertiaryContainer
) {
Icon(Icons.Rounded.KeyboardArrowDown, stringResource(R.string.scroll_to_bottom_icon))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ class ChatViewModel @Inject constructor(
private val _isIdle = MutableStateFlow(true)
val isIdle = _isIdle.asStateFlow()

private val _isLoaded = MutableStateFlow(false)
val isLoaded = _isLoaded.asStateFlow()

private val _userMessage = MutableStateFlow(Message(chatId = chatRoomId, content = "", platformType = null))
val userMessage = _userMessage.asStateFlow()

Expand All @@ -86,7 +89,9 @@ class ChatViewModel @Inject constructor(
Log.d("ViewModel", "$chatRoomId")
Log.d("ViewModel", "$enabledPlatformsInChat")
fetchChatRoom()
fetchMessages()
viewModelScope.launch {
fetchMessages()
}
fetchEnabledPlatformsInApp()
observeFlow()
}
Expand Down Expand Up @@ -208,19 +213,18 @@ class ChatViewModel @Inject constructor(
}
}

private fun fetchMessages() {
viewModelScope.launch {
// If the room isn't new
if (chatRoomId != 0) {
_messages.update { chatRepository.fetchMessages(chatRoomId) }
return@launch
}
private suspend fun fetchMessages() {
// If the room isn't new
if (chatRoomId != 0) {
_messages.update { chatRepository.fetchMessages(chatRoomId) }
_isLoaded.update { true } // Finish fetching
return
}

// When message id should sync after saving chats
if (chatRoom.id != 0) {
_messages.update { chatRepository.fetchMessages(chatRoom.id) }
return@launch
}
// When message id should sync after saving chats
if (chatRoom.id != 0) {
_messages.update { chatRepository.fetchMessages(chatRoom.id) }
return
}
}

Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,5 @@
<string name="bug_report">Bug Report</string>
<string name="bug_report_description">Something isn\'t working correctly?</string>
<string name="bug_report_icon">Bug Report Icon</string>
<string name="scroll_to_bottom_icon">Scroll to bottom icon</string>
</resources>