@@ -9,6 +9,7 @@ package com.nextcloud.client.assistant
99
1010import android.app.Activity
1111import androidx.compose.foundation.Image
12+ import androidx.compose.foundation.background
1213import androidx.compose.foundation.layout.Arrangement
1314import androidx.compose.foundation.layout.Column
1415import androidx.compose.foundation.layout.PaddingValues
@@ -55,6 +56,7 @@ import androidx.compose.ui.tooling.preview.Preview
5556import androidx.compose.ui.unit.dp
5657import androidx.compose.ui.unit.sp
5758import com.nextcloud.client.assistant.chat.ChatContent
59+ import com.nextcloud.client.assistant.chat.ChatViewModel
5860import com.nextcloud.client.assistant.conversation.ConversationScreen
5961import com.nextcloud.client.assistant.conversation.ConversationViewModel
6062import com.nextcloud.client.assistant.conversation.repository.MockConversationRemoteRepository
@@ -92,11 +94,13 @@ private const val PULL_TO_REFRESH_DELAY = 1500L
9294fun AssistantScreen (
9395 composeViewModel : ComposeViewModel ,
9496 viewModel : AssistantViewModel ,
97+ chatViewModel : ChatViewModel ,
9598 conversationViewModel : ConversationViewModel ,
9699 capability : OCCapability ,
97100 activity : Activity
98101) {
99102 val selectedText by composeViewModel.selectedText.collectAsState()
103+ val sessionTitle by chatViewModel.sessionTitle.collectAsState()
100104 val sessionId by viewModel.sessionId.collectAsState()
101105 val messageId by viewModel.snackbarMessageId.collectAsState()
102106 val screenOverlayState by viewModel.screenOverlayState.collectAsState()
@@ -140,12 +144,8 @@ fun AssistantScreen(
140144 }
141145 }
142146
143- LaunchedEffect (sessionId ) {
147+ LaunchedEffect (Unit ) {
144148 viewModel.startPolling(sessionId)
145-
146- sessionId?.let {
147- viewModel.fetchChatMessages(it)
148- }
149149 }
150150
151151 DisposableEffect (Unit ) {
@@ -164,8 +164,10 @@ fun AssistantScreen(
164164 scope.launch {
165165 pagerState.scrollToPage(AssistantPage .Content .id)
166166 }
167- }, openChat = { newSessionId ->
168- viewModel.initSessionId(newSessionId)
167+ }, openChat = { conversation ->
168+ viewModel.updateInputBarText(" " )
169+ chatViewModel.updateSessionTitle(conversation.timestamp)
170+ chatViewModel.selectConversation(conversation.id)
169171 taskTypes.getChat()?.let { chatTaskType ->
170172 viewModel.selectTaskType(chatTaskType)
171173 }
@@ -184,32 +186,52 @@ fun AssistantScreen(
184186 scope.launch {
185187 delay(PULL_TO_REFRESH_DELAY )
186188
187- val newSessionId = sessionId
188- if (newSessionId != null ) {
189- viewModel.fetchChatMessages(newSessionId )
189+ val currentSessionId = sessionId
190+ if (currentSessionId != null ) {
191+ chatViewModel.selectConversation(currentSessionId )
190192 } else {
191193 viewModel.fetchTaskList()
192194 }
193195 }
194196 }
195197 ),
196198 topBar = {
197- taskTypes?.let {
198- TaskTypesRow (selectedTaskType, data = it, selectTaskType = { task ->
199- viewModel.selectTaskType(task)
200- }, navigateToConversationList = {
201- scope.launch {
202- pagerState.scrollToPage(AssistantPage .Conversation .id)
199+ Column (modifier = Modifier .fillMaxWidth(), horizontalAlignment = Alignment .Start ) {
200+ taskTypes?.let {
201+ TaskTypesRow (selectedTaskType, data = it, selectTaskType = { task ->
202+ viewModel.selectTaskType(task)
203+ }, navigateToConversationList = {
204+ scope.launch {
205+ pagerState.scrollToPage(AssistantPage .Conversation .id)
206+ }
207+ })
208+ }
209+
210+ if (selectedTaskType?.isChat() == true && sessionTitle != null ) {
211+ Column (
212+ modifier = Modifier
213+ .fillMaxWidth()
214+ .background(MaterialTheme .colorScheme.surfaceVariant.copy(alpha = 0.3f ))
215+ .padding(horizontal = 16 .dp, vertical = 12 .dp),
216+ verticalArrangement = Arrangement .Center
217+ ) {
218+ Text (
219+ text = sessionTitle!! ,
220+ style = MaterialTheme .typography.titleMedium,
221+ color = MaterialTheme .colorScheme.onSurface,
222+ maxLines = 1
223+ )
203224 }
204- })
225+ }
205226 }
206227 },
207228 bottomBar = {
208229 if (! taskTypes.isNullOrEmpty() && selectedTaskType?.isTranslate() != true ) {
209230 InputBar (
210231 sessionId,
211232 selectedTaskType,
212- viewModel
233+ viewModel,
234+ chatViewModel
213235 )
214236 }
215237 },
@@ -257,7 +279,7 @@ fun AssistantScreen(
257279
258280 AssistantScreenState .ChatContent -> {
259281 ChatContent (
260- viewModel = viewModel ,
282+ chatViewModel = chatViewModel ,
261283 modifier = Modifier .padding(paddingValues)
262284 )
263285 }
@@ -301,9 +323,15 @@ fun AssistantScreen(
301323
302324@Suppress(" LongMethod" )
303325@Composable
304- private fun InputBar (sessionId : Long? , selectedTaskType : TaskTypeData ? , viewModel : AssistantViewModel ) {
326+ private fun InputBar (
327+ sessionId : Long? ,
328+ selectedTaskType : TaskTypeData ? ,
329+ viewModel : AssistantViewModel ,
330+ chatViewModel : ChatViewModel
331+ ) {
305332 val scope = rememberCoroutineScope()
306333 val text by viewModel.inputBarText.collectAsState()
334+ val chatUIState by chatViewModel.uiState.collectAsState()
307335
308336 Surface (
309337 tonalElevation = 3 .dp,
@@ -349,9 +377,9 @@ private fun InputBar(sessionId: Long?, selectedTaskType: TaskTypeData?, viewMode
349377 val taskType = selectedTaskType ? : return @IconButton
350378 if (taskType.isChat()) {
351379 if (sessionId != null ) {
352- viewModel.sendChatMessage (content = text, sessionId)
380+ chatViewModel.sendMessage (content = text, sessionId = sessionId)
353381 } else {
354- viewModel.createConversation( text)
382+ chatViewModel.startNewConversation(content = text)
355383 }
356384 } else {
357385 viewModel.createTask(input = text, taskType = taskType)
@@ -361,12 +389,19 @@ private fun InputBar(sessionId: Long?, selectedTaskType: TaskTypeData?, viewMode
361389 delay(CHAT_INPUT_DELAY )
362390 viewModel.updateInputBarText(" " )
363391 }
364- }
392+ },
393+ enabled = chatUIState.canSend()
365394 ) {
366395 Icon (
367396 painter = painterResource(id = R .drawable.ic_send),
368397 contentDescription = stringResource(R .string.assistant_screen_send_message),
369- tint = MaterialTheme .colorScheme.primary
398+ tint = if (chatUIState.canSend()) {
399+ MaterialTheme .colorScheme.primary
400+ } else {
401+ colorResource(
402+ R .color.disabled_text
403+ )
404+ }
370405 )
371406 }
372407 }
@@ -498,6 +533,7 @@ private fun AssistantScreenPreview() {
498533 composeViewModel = ComposeViewModel (),
499534 conversationViewModel = getMockConversationViewModel(),
500535 viewModel = getMockAssistantViewModel(false ),
536+ chatViewModel = ChatViewModel (MockAssistantRemoteRepository ()),
501537 activity = ComposeActivity (),
502538 capability = OCCapability ().apply {
503539 versionMayor = 30
@@ -517,6 +553,7 @@ private fun AssistantEmptyScreenPreview() {
517553 composeViewModel = ComposeViewModel (),
518554 conversationViewModel = getMockConversationViewModel(),
519555 viewModel = getMockAssistantViewModel(true ),
556+ chatViewModel = ChatViewModel (MockAssistantRemoteRepository ()),
520557 activity = ComposeActivity (),
521558 capability = OCCapability ().apply {
522559 versionMayor = 30
0 commit comments