Skip to content

Commit d216afd

Browse files
committed
fix ux
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
1 parent 3e3a0d8 commit d216afd

5 files changed

Lines changed: 344 additions & 155 deletions

File tree

app/src/main/java/com/nextcloud/client/assistant/AssistantScreen.kt

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import com.nextcloud.client.assistant.repository.remote.MockAssistantRemoteRepos
6767
import com.nextcloud.client.assistant.task.TaskView
6868
import com.nextcloud.client.assistant.taskTypes.TaskTypesRow
6969
import com.nextcloud.client.assistant.translate.TranslationScreen
70+
import com.nextcloud.client.assistant.translate.TranslationViewModel
7071
import com.nextcloud.ui.composeActivity.ComposeActivity
7172
import com.nextcloud.ui.composeActivity.ComposeViewModel
7273
import com.nextcloud.ui.composeComponents.alertDialog.SimpleAlertDialog
@@ -252,15 +253,20 @@ fun AssistantScreen(
252253
}
253254

254255
is AssistantScreenState.Translation -> {
255-
val task = (screenState as AssistantScreenState.Translation).task
256-
val textToTranslate = task?.input?.input ?: selectedText ?: ""
256+
selectedTaskType?.let {
257+
val task = (screenState as AssistantScreenState.Translation).task
258+
val textToTranslate = task?.input?.input ?: selectedText ?: ""
257259

258-
TranslationScreen(
259-
selectedTaskType,
260-
viewModel,
261-
textToTranslate,
262-
isTaskExists = (task != null)
263-
)
260+
val translationViewModel =
261+
TranslationViewModel(remoteRepository = viewModel.getRemoteRepository())
262+
263+
translationViewModel.init(it, task, textToTranslate)
264+
265+
TranslationScreen(
266+
viewModel = translationViewModel,
267+
assistantViewModel = viewModel
268+
)
269+
}
264270
}
265271

266272
else -> EmptyContent(

app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt

Lines changed: 2 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@ import com.owncloud.android.lib.resources.assistant.chat.model.ChatMessage
2121
import com.owncloud.android.lib.resources.assistant.chat.model.ChatMessageRequest
2222
import com.owncloud.android.lib.resources.assistant.v2.model.Task
2323
import com.owncloud.android.lib.resources.assistant.v2.model.TaskTypeData
24-
import com.owncloud.android.lib.resources.assistant.v2.model.TranslationLanguage
25-
import com.owncloud.android.lib.resources.assistant.v2.model.TranslationRequest
26-
import com.owncloud.android.lib.resources.assistant.v2.model.toTranslationModel
2724
import kotlinx.coroutines.Dispatchers
2825
import kotlinx.coroutines.Job
2926
import kotlinx.coroutines.delay
@@ -65,12 +62,6 @@ class AssistantViewModel(
6562
private val _isTranslationTask = MutableStateFlow(false)
6663
val isTranslationTask: StateFlow<Boolean> = _isTranslationTask
6764

68-
private val _isTranslationTaskCreated = MutableStateFlow(false)
69-
val isTranslationTaskCreated: StateFlow<Boolean> = _isTranslationTaskCreated
70-
71-
private val _translationTaskOutput = MutableStateFlow("")
72-
val translationTaskOutput: StateFlow<String> = _translationTaskOutput
73-
7465
private val selectedTask = MutableStateFlow<Task?>(null)
7566

7667
private val _selectedTaskType = MutableStateFlow<TaskTypeData?>(null)
@@ -207,78 +198,6 @@ class AssistantViewModel(
207198
}
208199
}
209200

210-
// region translation
211-
fun translate(textToTranslate: String, originLanguage: TranslationLanguage, targetLanguage: TranslationLanguage) {
212-
viewModelScope.launch(Dispatchers.IO) {
213-
val taskType = _selectedTaskType.value
214-
if (taskType == null) {
215-
_snackbarMessageId.update {
216-
R.string.assistant_screen_select_task
217-
}
218-
return@launch
219-
}
220-
221-
val model = taskType.toTranslationModel()
222-
223-
if (model == null) {
224-
_snackbarMessageId.update {
225-
R.string.translation_screen_error_message
226-
}
227-
return@launch
228-
}
229-
230-
val input = TranslationRequest(
231-
input = textToTranslate,
232-
originLanguage = originLanguage.code,
233-
targetLanguage = targetLanguage.code,
234-
maxTokens = model.maxTokens,
235-
model = model.model
236-
)
237-
238-
val result = remoteRepository.translate(input, taskType)
239-
if (result.isSuccess) {
240-
_isTranslationTaskCreated.update { true }
241-
242-
val selectedTaskId = selectedTask.value?.id ?: return@launch
243-
244-
pollTranslationResult(
245-
taskType = taskType,
246-
selectedTaskId = selectedTaskId
247-
)
248-
249-
_isTranslationTaskCreated.update { false }
250-
}
251-
}
252-
}
253-
254-
private suspend fun pollTranslationResult(taskType: TaskTypeData, selectedTaskId: Long, maxRetries: Int = 3) {
255-
val taskTypeId = taskType.id ?: return
256-
257-
repeat(maxRetries) { attempt ->
258-
val translationTasks = remoteRepository.getTaskList(taskTypeId)
259-
val translationResult = translationTasks
260-
?.find { it.id == selectedTaskId }
261-
?.output
262-
?.output
263-
264-
if (!translationResult.isNullOrBlank()) {
265-
_translationTaskOutput.update { translationResult }
266-
return
267-
}
268-
269-
Log_OC.d(TAG, "Translation not ready yet (attempt ${attempt + 1}/$maxRetries)")
270-
271-
if (attempt < maxRetries - 1) {
272-
delay(POLLING_INTERVAL_MS)
273-
}
274-
}
275-
276-
Log_OC.w(TAG, "Translation polling finished but result is still empty")
277-
updateSnackbarMessage(R.string.translation_screen_task_processing)
278-
onTranslationScreenDismissed()
279-
}
280-
// endregion
281-
282201
// region chat
283202
fun sendChatMessage(content: String, sessionId: Long) = viewModelScope.launch(Dispatchers.IO) {
284203
val request = ChatMessageRequest(
@@ -419,14 +338,6 @@ class AssistantViewModel(
419338
}
420339

421340
fun selectTask(task: Task?) {
422-
viewModelScope.launch {
423-
if (task?.isTranslate() == true) {
424-
_selectedTaskType.value?.let {
425-
pollTranslationResult(it, task.id)
426-
}
427-
}
428-
}
429-
430341
selectedTask.update {
431342
task
432343
}
@@ -462,18 +373,13 @@ class AssistantViewModel(
462373
}
463374
}
464375

465-
fun updateTranslationTaskCreation(value: Boolean) {
466-
_isTranslationTaskCreated.update {
467-
value
468-
}
469-
}
470-
471376
fun onTranslationScreenDismissed() {
472-
updateTranslationTaskCreation(false)
473377
updateTranslationTaskState(false)
474378
selectTask(null)
475379
}
476380

381+
fun getRemoteRepository(): AssistantRemoteRepository = remoteRepository
382+
477383
private fun removeTaskFromList(id: Long) {
478384
_filteredTaskList.update { currentList ->
479385
currentList?.filter { it.id != id }

app/src/main/java/com/nextcloud/client/assistant/translate/TranslationScreen.kt

Lines changed: 35 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import androidx.compose.material3.Icon
3333
import androidx.compose.material3.IconButton
3434
import androidx.compose.material3.MaterialTheme
3535
import androidx.compose.material3.Scaffold
36+
import androidx.compose.material3.SnackbarHost
37+
import androidx.compose.material3.SnackbarHostState
3638
import androidx.compose.material3.Text
3739
import androidx.compose.material3.TextField
3840
import androidx.compose.material3.TextFieldDefaults
@@ -41,9 +43,7 @@ import androidx.compose.runtime.DisposableEffect
4143
import androidx.compose.runtime.LaunchedEffect
4244
import androidx.compose.runtime.collectAsState
4345
import androidx.compose.runtime.getValue
44-
import androidx.compose.runtime.mutableStateOf
4546
import androidx.compose.runtime.remember
46-
import androidx.compose.runtime.setValue
4747
import androidx.compose.ui.Alignment
4848
import androidx.compose.ui.Modifier
4949
import androidx.compose.ui.graphics.Color
@@ -56,50 +56,35 @@ import com.nextcloud.client.assistant.AssistantViewModel
5656
import com.nextcloud.client.assistant.model.AssistantScreenState
5757
import com.nextcloud.utils.extensions.getActivity
5858
import com.owncloud.android.R
59-
import com.owncloud.android.lib.resources.assistant.v2.model.TaskTypeData
6059
import com.owncloud.android.lib.resources.assistant.v2.model.TranslationLanguage
6160
import com.owncloud.android.lib.resources.assistant.v2.model.toTranslationLanguages
6261
import com.owncloud.android.utils.ClipboardUtil
6362

6463
@Suppress("LongMethod")
6564
@OptIn(ExperimentalMaterial3Api::class)
6665
@Composable
67-
fun TranslationScreen(
68-
selectedTaskType: TaskTypeData?,
69-
viewModel: AssistantViewModel,
70-
textToTranslate: String,
71-
isTaskExists: Boolean
72-
) {
73-
val languages = remember(selectedTaskType) { selectedTaskType?.toTranslationLanguages() }
74-
val isTranslationTaskCreated by viewModel.isTranslationTaskCreated.collectAsState()
75-
val translationTaskOutput by viewModel.translationTaskOutput.collectAsState()
76-
77-
var sourceState by remember {
78-
mutableStateOf(
79-
TranslationSideState(
80-
text = textToTranslate,
81-
language = languages?.originLanguages?.firstOrNull(),
82-
isTarget = false
83-
)
84-
)
85-
}
86-
var targetState by remember {
87-
mutableStateOf(TranslationSideState(language = languages?.targetLanguages?.firstOrNull(), isTarget = true))
88-
}
66+
fun TranslationScreen(viewModel: TranslationViewModel, assistantViewModel: AssistantViewModel) {
67+
val context = LocalContext.current
68+
val state by viewModel.screenState.collectAsState()
69+
val messageId by viewModel.snackbarMessageId.collectAsState()
70+
val snackbarHostState = remember { SnackbarHostState() }
8971

90-
LaunchedEffect(translationTaskOutput) {
91-
targetState = targetState.copy(text = translationTaskOutput)
72+
BackHandler {
73+
assistantViewModel.onTranslationScreenDismissed()
74+
assistantViewModel.updateScreenState(AssistantScreenState.TaskContent)
9275
}
9376

94-
BackHandler {
95-
viewModel.onTranslationScreenDismissed()
96-
viewModel.updateScreenState(AssistantScreenState.TaskContent)
77+
LaunchedEffect(messageId) {
78+
messageId?.let {
79+
snackbarHostState.showSnackbar(context.getString(it))
80+
viewModel.updateSnackbarMessage(null)
81+
}
9782
}
9883

9984
// task is unselected
10085
DisposableEffect(Unit) {
10186
onDispose {
102-
viewModel.onTranslationScreenDismissed()
87+
assistantViewModel.onTranslationScreenDismissed()
10388
}
10489
}
10590

@@ -109,29 +94,30 @@ fun TranslationScreen(
10994
.padding(16.dp)
11095
.padding(top = 32.dp),
11196
floatingActionButton = {
112-
if (!isTaskExists && !isTranslationTaskCreated) {
97+
if (state.fabVisibility) {
11398
FloatingActionButton(onClick = {
114-
val originLang = sourceState.language
115-
val targetLang = targetState.language
116-
if (originLang != null && targetLang != null) {
117-
viewModel.translate(sourceState.text, originLang, targetLang)
118-
}
99+
viewModel.translate()
119100
}, content = {
120101
Icon(painter = painterResource(R.drawable.ic_translate), contentDescription = "translate button")
121102
})
122103
}
104+
},
105+
snackbarHost = {
106+
SnackbarHost(snackbarHostState)
123107
}
124108
) { paddingValues ->
125109
LazyColumn(modifier = Modifier.padding(paddingValues)) {
126110
item {
127111
TranslationSection(
128112
labelId = R.string.translation_screen_label_from,
129113
hintId = R.string.translation_screen_hint_source,
130-
state = sourceState,
131-
availableLanguages = languages?.originLanguages ?: emptyList(),
114+
state = state.source,
115+
availableLanguages = state.taskTypeData.toTranslationLanguages().originLanguages,
132116
maxDp = 120.dp,
133117
shimmer = false,
134-
onStateChange = { sourceState = it }
118+
onStateChange = {
119+
viewModel.updateSourceState(it)
120+
}
135121
)
136122
}
137123

@@ -146,16 +132,14 @@ fun TranslationScreen(
146132
item {
147133
TranslationSection(
148134
labelId = R.string.translation_screen_label_to,
149-
hintId = if (isTaskExists) {
150-
R.string.translation_screen_translating
151-
} else {
152-
R.string.translation_screen_start_to_translate_task
153-
},
154-
state = targetState,
155-
availableLanguages = languages?.targetLanguages ?: emptyList(),
135+
hintId = state.targetHintMessageId,
136+
state = state.target,
137+
availableLanguages = state.taskTypeData.toTranslationLanguages().targetLanguages,
156138
maxDp = Dp.Unspecified,
157-
shimmer = isTaskExists || isTranslationTaskCreated,
158-
onStateChange = { targetState = it }
139+
shimmer = state.shimmer,
140+
onStateChange = {
141+
viewModel.updateTargetState(it)
142+
}
159143
)
160144
}
161145
}
@@ -166,7 +150,7 @@ fun TranslationScreen(
166150
@Composable
167151
private fun TranslationSection(
168152
labelId: Int,
169-
hintId: Int,
153+
hintId: Int?,
170154
state: TranslationSideState,
171155
availableLanguages: List<TranslationLanguage>,
172156
maxDp: Dp,
@@ -241,7 +225,7 @@ private fun TranslationSection(
241225
.fillMaxWidth()
242226
.heightIn(min = 120.dp, max = maxDp),
243227
placeholder = {
244-
Text(text = stringResource(hintId), style = MaterialTheme.typography.headlineSmall)
228+
hintId?.let { Text(text = stringResource(it), style = MaterialTheme.typography.headlineSmall) }
245229
},
246230
textStyle = MaterialTheme.typography.headlineSmall,
247231
colors = TextFieldDefaults.colors(

0 commit comments

Comments
 (0)