Skip to content

Commit 45a8be7

Browse files
committed
better ui ux
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
1 parent 06e148f commit 45a8be7

5 files changed

Lines changed: 109 additions & 37 deletions

File tree

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,8 @@ fun AssistantScreen(
258258
TranslationScreen(
259259
selectedTaskType,
260260
viewModel,
261-
textToTranslate
261+
textToTranslate,
262+
isTaskExists = (task != null)
262263
)
263264
}
264265

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

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import com.nextcloud.client.assistant.repository.remote.AssistantRemoteRepositor
1616
import com.nextcloud.utils.TimeConstants.MILLIS_PER_SECOND
1717
import com.nextcloud.utils.extensions.isHuman
1818
import com.owncloud.android.R
19-
import com.owncloud.android.lib.common.operations.RemoteOperationResult
2019
import com.owncloud.android.lib.common.utils.Log_OC
2120
import com.owncloud.android.lib.resources.assistant.chat.model.ChatMessage
2221
import com.owncloud.android.lib.resources.assistant.chat.model.ChatMessageRequest
@@ -66,8 +65,10 @@ class AssistantViewModel(
6665
private val _isTranslationTask = MutableStateFlow<Boolean>(false)
6766
val isTranslationTask: StateFlow<Boolean> = _isTranslationTask
6867

69-
private val _selectedTask = MutableStateFlow<Task?>(null)
70-
val selectedTask: StateFlow<Task?> = _selectedTask
68+
private val _isTranslationTaskCreated = MutableStateFlow<Boolean>(false)
69+
val isTranslationTaskCreated: StateFlow<Boolean> = _isTranslationTaskCreated
70+
71+
private val selectedTask = MutableStateFlow<Task?>(null)
7172

7273
private val _selectedTaskType = MutableStateFlow<TaskTypeData?>(null)
7374
val selectedTaskType: StateFlow<TaskTypeData?> = _selectedTaskType
@@ -174,7 +175,7 @@ class AssistantViewModel(
174175
private fun observeScreenState() {
175176
viewModelScope.launch {
176177
combine(
177-
_selectedTask,
178+
selectedTask,
178179
_selectedTaskType,
179180
_chatMessages,
180181
_filteredTaskList
@@ -231,7 +232,11 @@ class AssistantViewModel(
231232
)
232233

233234
val result = remoteRepository.translate(input, task)
234-
handleTaskCreation(result)
235+
if (result.isSuccess) {
236+
_isTranslationTaskCreated.update {
237+
true
238+
}
239+
}
235240
}
236241
}
237242

@@ -280,10 +285,6 @@ class AssistantViewModel(
280285
// region task
281286
fun createTask(input: String, taskType: TaskTypeData) = viewModelScope.launch(Dispatchers.IO) {
282287
val result = remoteRepository.createTask(input, taskType)
283-
handleTaskCreation(result)
284-
}
285-
286-
private suspend fun handleTaskCreation(result: RemoteOperationResult<*>) {
287288
val message = if (result.isSuccess) {
288289
R.string.assistant_screen_task_create_success_message
289290
} else {
@@ -379,7 +380,7 @@ class AssistantViewModel(
379380
}
380381

381382
fun selectTask(task: Task?) {
382-
_selectedTask.update {
383+
selectedTask.update {
383384
task
384385
}
385386
}
@@ -414,6 +415,18 @@ class AssistantViewModel(
414415
}
415416
}
416417

418+
fun updateTranslationTaskCreation(value: Boolean) {
419+
_isTranslationTaskCreated.update {
420+
value
421+
}
422+
}
423+
424+
fun onTranslationScreenDismissed() {
425+
updateTranslationTaskCreation(false)
426+
updateTranslationTaskState(false)
427+
selectTask(null)
428+
}
429+
417430
private fun removeTaskFromList(id: Long) {
418431
_filteredTaskList.update { currentList ->
419432
currentList?.filter { it.id != id }

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

Lines changed: 75 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@
88
package com.nextcloud.client.assistant.translate
99

1010
import androidx.activity.compose.BackHandler
11+
import androidx.compose.animation.core.RepeatMode
12+
import androidx.compose.animation.core.animateFloat
13+
import androidx.compose.animation.core.infiniteRepeatable
14+
import androidx.compose.animation.core.rememberInfiniteTransition
15+
import androidx.compose.animation.core.tween
1116
import androidx.compose.foundation.clickable
17+
import androidx.compose.foundation.layout.Column
1218
import androidx.compose.foundation.layout.Row
1319
import androidx.compose.foundation.layout.Spacer
1420
import androidx.compose.foundation.layout.fillMaxSize
@@ -32,6 +38,7 @@ import androidx.compose.material3.TextField
3238
import androidx.compose.material3.TextFieldDefaults
3339
import androidx.compose.runtime.Composable
3440
import androidx.compose.runtime.DisposableEffect
41+
import androidx.compose.runtime.collectAsState
3542
import androidx.compose.runtime.getValue
3643
import androidx.compose.runtime.mutableStateOf
3744
import androidx.compose.runtime.remember
@@ -56,9 +63,14 @@ import com.owncloud.android.utils.ClipboardUtil
5663
@Suppress("LongMethod")
5764
@OptIn(ExperimentalMaterial3Api::class)
5865
@Composable
59-
fun TranslationScreen(selectedTaskType: TaskTypeData?, viewModel: AssistantViewModel, textToTranslate: String) {
66+
fun TranslationScreen(
67+
selectedTaskType: TaskTypeData?,
68+
viewModel: AssistantViewModel,
69+
textToTranslate: String,
70+
isTaskExists: Boolean
71+
) {
6072
val languages = remember(selectedTaskType) { selectedTaskType?.toTranslationLanguages() }
61-
73+
val isTranslationTaskCreated by viewModel.isTranslationTaskCreated.collectAsState()
6274
var sourceState by remember {
6375
mutableStateOf(
6476
TranslationSideState(
@@ -73,16 +85,14 @@ fun TranslationScreen(selectedTaskType: TaskTypeData?, viewModel: AssistantViewM
7385
}
7486

7587
BackHandler {
76-
viewModel.updateTranslationTaskState(false)
77-
viewModel.selectTask(null)
88+
viewModel.onTranslationScreenDismissed()
7889
viewModel.updateScreenState(AssistantScreenState.TaskContent)
7990
}
8091

8192
// task is unselected
8293
DisposableEffect(Unit) {
8394
onDispose {
84-
viewModel.updateTranslationTaskState(false)
85-
viewModel.selectTask(null)
95+
viewModel.onTranslationScreenDismissed()
8696
}
8797
}
8898

@@ -111,6 +121,7 @@ fun TranslationScreen(selectedTaskType: TaskTypeData?, viewModel: AssistantViewM
111121
state = sourceState,
112122
availableLanguages = languages?.originLanguages ?: emptyList(),
113123
maxDp = 120.dp,
124+
shimmer = false,
114125
onStateChange = { sourceState = it }
115126
)
116127
}
@@ -126,25 +137,31 @@ fun TranslationScreen(selectedTaskType: TaskTypeData?, viewModel: AssistantViewM
126137
item {
127138
TranslationSection(
128139
labelId = R.string.translation_screen_label_to,
129-
hintId = R.string.translation_screen_hint_target,
140+
hintId = if (isTaskExists) {
141+
R.string.translation_screen_translating
142+
} else {
143+
R.string.translation_screen_start_to_translate_task
144+
},
130145
state = targetState,
131146
availableLanguages = languages?.targetLanguages ?: emptyList(),
132147
maxDp = Dp.Unspecified,
148+
shimmer = isTaskExists || isTranslationTaskCreated,
133149
onStateChange = { targetState = it }
134150
)
135151
}
136152
}
137153
}
138154
}
139155

140-
@Suppress("LongMethod")
156+
@Suppress("LongMethod", "LongParameterList")
141157
@Composable
142158
private fun TranslationSection(
143159
labelId: Int,
144160
hintId: Int,
145161
state: TranslationSideState,
146162
availableLanguages: List<TranslationLanguage>,
147163
maxDp: Dp,
164+
shimmer: Boolean,
148165
onStateChange: (TranslationSideState) -> Unit
149166
) {
150167
val activity = LocalContext.current.getActivity()
@@ -199,23 +216,56 @@ private fun TranslationSection(
199216
}
200217
}
201218

202-
TextField(
203-
value = state.text,
204-
onValueChange = { onStateChange(state.copy(text = it)) },
205-
readOnly = state.isTarget,
206-
modifier = Modifier
207-
.fillMaxWidth()
208-
.heightIn(min = 120.dp, max = maxDp),
209-
placeholder = {
210-
Text(text = stringResource(hintId), style = MaterialTheme.typography.headlineSmall)
211-
},
212-
textStyle = MaterialTheme.typography.headlineSmall,
213-
colors = TextFieldDefaults.colors(
214-
focusedContainerColor = Color.Transparent,
215-
unfocusedContainerColor = Color.Transparent,
216-
disabledContainerColor = Color.Transparent,
217-
focusedIndicatorColor = Color.Transparent,
218-
unfocusedIndicatorColor = Color.Transparent
219+
if (state.isTarget && shimmer) {
220+
TranslatingShimmer(
221+
modifier = Modifier
222+
.fillMaxWidth()
223+
.heightIn(min = 120.dp)
224+
.padding(horizontal = 16.dp)
225+
)
226+
} else {
227+
TextField(
228+
value = state.text,
229+
onValueChange = { onStateChange(state.copy(text = it)) },
230+
readOnly = state.isTarget,
231+
modifier = Modifier
232+
.fillMaxWidth()
233+
.heightIn(min = 120.dp, max = maxDp),
234+
placeholder = {
235+
Text(text = stringResource(hintId), style = MaterialTheme.typography.headlineSmall)
236+
},
237+
textStyle = MaterialTheme.typography.headlineSmall,
238+
colors = TextFieldDefaults.colors(
239+
focusedContainerColor = Color.Transparent,
240+
unfocusedContainerColor = Color.Transparent,
241+
disabledContainerColor = Color.Transparent,
242+
focusedIndicatorColor = Color.Transparent,
243+
unfocusedIndicatorColor = Color.Transparent
244+
)
219245
)
246+
}
247+
}
248+
249+
@Suppress("MagicNumber")
250+
@Composable
251+
private fun TranslatingShimmer(modifier: Modifier = Modifier) {
252+
val transition = rememberInfiniteTransition(label = "shimmer")
253+
val alpha by transition.animateFloat(
254+
initialValue = 0.3f,
255+
targetValue = 1f,
256+
animationSpec = infiniteRepeatable(
257+
animation = tween(900),
258+
repeatMode = RepeatMode.Reverse
259+
),
260+
label = "alpha"
220261
)
262+
263+
Column(modifier = modifier) {
264+
Text(
265+
text = stringResource(R.string.translation_screen_translating),
266+
style = MaterialTheme.typography.headlineSmall,
267+
color = MaterialTheme.colorScheme.onSurface.copy(alpha = alpha),
268+
modifier = Modifier.padding(vertical = 4.dp)
269+
)
270+
}
221271
}

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
androidCommonLibraryVersion = "4fc0f29981"
66
androidGifDrawableVersion = "1.2.30"
77
androidImageCropperVersion = "4.7.0"
8-
androidLibraryVersion ="26d94e4405"
8+
androidLibraryVersion ="0752f97f74"
99
androidPluginVersion = "9.0.0"
1010
androidsvgVersion = "1.4"
1111
androidxMediaVersion = "1.5.1"

gradle/verification-metadata.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19524,6 +19524,14 @@
1952419524
<sha256 value="fc987eaf53cefe50c60453d32727bb23bb1818eb4898ef89abce4d001c0f87c5" origin="Generated by Gradle" reason="Artifact is not signed"/>
1952519525
</artifact>
1952619526
</component>
19527+
<component group="com.github.nextcloud" name="android-library" version="0752f97f74">
19528+
<artifact name="android-library-0752f97f74.aar">
19529+
<sha256 value="adb3fc467564250decb3cd7b210a9895cb44d5b378035288119742df078f1888" origin="Generated by Gradle" reason="Artifact is not signed"/>
19530+
</artifact>
19531+
<artifact name="android-library-0752f97f74.module">
19532+
<sha256 value="cf46658b54de149dfa2e528aecdec71b08939a30ef8067bf384562a95a406609" origin="Generated by Gradle" reason="Artifact is not signed"/>
19533+
</artifact>
19534+
</component>
1952719535
<component group="com.github.nextcloud" name="android-library" version="0782f95a2a01a80588ce2df666845d10bdef6c32">
1952819536
<artifact name="android-library-0782f95a2a01a80588ce2df666845d10bdef6c32.aar">
1952919537
<sha256 value="22c05e04d2b2667c39da419b2d3d07e7832babd4b2e235aa5d4394a4fecf92ee" origin="Generated by Gradle" reason="Artifact is not signed"/>

0 commit comments

Comments
 (0)