Эх сурвалжийг харах

Add Task delete feature

Signed-off-by: alperozturk <alper_ozturk@proton.me>
alperozturk 1 жил өмнө
parent
commit
9afdbd89c9

+ 25 - 9
app/src/main/java/com/nextcloud/client/assistant/AssistantViewModel.kt

@@ -39,8 +39,8 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() {
 
 
     private val repository: AssistantRepository = AssistantRepository(client)
     private val repository: AssistantRepository = AssistantRepository(client)
 
 
-    private val _selectedTask = MutableStateFlow<TaskType?>(null)
-    val selectedTask: StateFlow<TaskType?> = _selectedTask
+    private val _selectedTaskType = MutableStateFlow<TaskType?>(null)
+    val selectedTaskType: StateFlow<TaskType?> = _selectedTaskType
 
 
     private val _taskTypes = MutableStateFlow<RemoteOperationResult<TaskTypes>?>(null)
     private val _taskTypes = MutableStateFlow<RemoteOperationResult<TaskTypes>?>(null)
     val taskTypes: StateFlow<RemoteOperationResult<TaskTypes>?> = _taskTypes
     val taskTypes: StateFlow<RemoteOperationResult<TaskTypes>?> = _taskTypes
@@ -54,6 +54,9 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() {
     private val _isTaskCreated = MutableStateFlow(false)
     private val _isTaskCreated = MutableStateFlow(false)
     val isTaskCreated: StateFlow<Boolean> = _isTaskCreated
     val isTaskCreated: StateFlow<Boolean> = _isTaskCreated
 
 
+    private val _isTaskDeleted = MutableStateFlow(false)
+    val isTaskDeleted: StateFlow<Boolean> = _isTaskDeleted
+
     init {
     init {
         getTaskTypes()
         getTaskTypes()
         getTaskList()
         getTaskList()
@@ -73,7 +76,7 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() {
     }
     }
 
 
     fun selectTask(task: TaskType) {
     fun selectTask(task: TaskType) {
-        _selectedTask.update {
+        _selectedTaskType.update {
             task
             task
         }
         }
     }
     }
@@ -86,7 +89,7 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() {
                 result
                 result
             }
             }
 
 
-            _selectedTask.update {
+            _selectedTaskType.update {
                 result.resultData.types.first()
                 result.resultData.types.first()
             }
             }
         }
         }
@@ -106,13 +109,26 @@ class AssistantViewModel(client: NextcloudClient) : ViewModel() {
         }
         }
     }
     }
 
 
-
-    /*
-    fun deleteTask(id: String) {
+    fun deleteTask(id: Long) {
         viewModelScope.launch(Dispatchers.IO) {
         viewModelScope.launch(Dispatchers.IO) {
-            repository?.deleteTask(id)
+            val result = repository.deleteTask(id)
+
+            _isTaskDeleted.update {
+                if (result.isSuccess) {
+                    removeTaskFromList(id)
+                }
+
+                result.isSuccess
+            }
         }
         }
     }
     }
-     */
 
 
+    private fun removeTaskFromList(id: Long) {
+        _taskList.update { currentList ->
+            currentList?.resultData?.tasks?.let { tasks ->
+                currentList.resultData.tasks = tasks.filter { it.id != id }
+            }
+            currentList
+        }
+    }
 }
 }

+ 16 - 6
app/src/main/java/com/nextcloud/client/assistant/AsssistantScreen.kt

@@ -54,10 +54,13 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin
     // TODO hide sort group, search bar
     // TODO hide sort group, search bar
     // TODO top bar, back button causes crash
     // TODO top bar, back button causes crash
     // TODO generate list according to selection (selectedTask).
     // TODO generate list according to selection (selectedTask).
+    // TODO add swipe to refresh
+    val activity = LocalContext.current as Activity
     val loading by viewModel.loading.collectAsState()
     val loading by viewModel.loading.collectAsState()
-    val selectedTask by viewModel.selectedTask.collectAsState()
+    val selectedTaskType by viewModel.selectedTaskType.collectAsState()
     val taskList by viewModel.taskList.collectAsState()
     val taskList by viewModel.taskList.collectAsState()
     val isTaskCreated by viewModel.isTaskCreated.collectAsState()
     val isTaskCreated by viewModel.isTaskCreated.collectAsState()
+    val isTaskDeleted by viewModel.isTaskDeleted.collectAsState()
     val taskTypes by viewModel.taskTypes.collectAsState()
     val taskTypes by viewModel.taskTypes.collectAsState()
     var showAddTaskAlertDialog by remember {
     var showAddTaskAlertDialog by remember {
         mutableStateOf(false)
         mutableStateOf(false)
@@ -72,18 +75,25 @@ fun AssistantScreen(viewModel: AssistantViewModel, floatingActionButton: Floatin
     } else {
     } else {
         val tasks = taskList?.resultData?.tasks ?: return
         val tasks = taskList?.resultData?.tasks ?: return
         val types = taskTypes?.resultData?.types ?: return
         val types = taskTypes?.resultData?.types ?: return
-        AssistantContent(tasks, types, selectedTask, viewModel)
+        AssistantContent(tasks, types, selectedTaskType, viewModel)
     }
     }
 
 
     if (isTaskCreated) {
     if (isTaskCreated) {
         DisplayUtils.showSnackMessage(
         DisplayUtils.showSnackMessage(
-            LocalContext.current as Activity,
+            activity,
             stringResource(id = R.string.assistant_screen_task_create_success_message)
             stringResource(id = R.string.assistant_screen_task_create_success_message)
         )
         )
     }
     }
 
 
+    if (isTaskDeleted) {
+        DisplayUtils.showSnackMessage(
+            activity,
+            stringResource(id = R.string.assistant_screen_task_delete_success_message)
+        )
+    }
+
     if (showAddTaskAlertDialog) {
     if (showAddTaskAlertDialog) {
-        selectedTask?.let {
+        selectedTaskType?.let {
             AddTaskAlertDialog(viewModel, it) {
             AddTaskAlertDialog(viewModel, it) {
                 showAddTaskAlertDialog = false
                 showAddTaskAlertDialog = false
             }
             }
@@ -97,7 +107,7 @@ private fun AssistantContent(
     taskList: List<Task>,
     taskList: List<Task>,
     taskTypes: List<TaskType>,
     taskTypes: List<TaskType>,
     selectedTask: TaskType?,
     selectedTask: TaskType?,
-    viewModel: AssistantViewModel
+    viewModel: AssistantViewModel,
 ) {
 ) {
     LazyColumn(
     LazyColumn(
         modifier = Modifier
         modifier = Modifier
@@ -116,7 +126,7 @@ private fun AssistantContent(
             if (taskList.isEmpty()) {
             if (taskList.isEmpty()) {
                 CenterText(text = stringResource(id = R.string.assistant_screen_no_task_available_text))
                 CenterText(text = stringResource(id = R.string.assistant_screen_no_task_available_text))
             } else {
             } else {
-                TaskView(task = it)
+                TaskView(task = it, deleteTask = { viewModel.deleteTask(it.id) } )
                 Spacer(modifier = Modifier.height(8.dp))
                 Spacer(modifier = Modifier.height(8.dp))
             }
             }
         }
         }

+ 1 - 1
app/src/main/java/com/nextcloud/client/assistant/component/AddTaskAlertDialog.kt

@@ -33,7 +33,7 @@ import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.KeyboardType
 import com.nextcloud.client.assistant.AssistantViewModel
 import com.nextcloud.client.assistant.AssistantViewModel
-import com.nextcloud.ui.composeComponents.SimpleAlertDialog
+import com.nextcloud.ui.composeComponents.alertDialog.SimpleAlertDialog
 import com.owncloud.android.R
 import com.owncloud.android.R
 import com.owncloud.android.lib.resources.assistant.model.TaskType
 import com.owncloud.android.lib.resources.assistant.model.TaskType
 
 

+ 38 - 5
app/src/main/java/com/nextcloud/client/assistant/component/TaskView.kt

@@ -22,9 +22,12 @@
 package com.nextcloud.client.assistant.component
 package com.nextcloud.client.assistant.component
 
 
 import android.annotation.SuppressLint
 import android.annotation.SuppressLint
-import android.widget.Space
+import androidx.compose.animation.animateContentSize
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.background
-import androidx.compose.foundation.clickable
+import androidx.compose.foundation.combinedClickable
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.fillMaxWidth
@@ -43,15 +46,20 @@ import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.dp
+import com.nextcloud.ui.composeComponents.bottomSheet.MoreActionsBottomSheet
 import com.owncloud.android.R
 import com.owncloud.android.R
 import com.owncloud.android.lib.resources.assistant.model.Task
 import com.owncloud.android.lib.resources.assistant.model.Task
 
 
+@OptIn(ExperimentalFoundationApi::class)
 @SuppressLint("ResourceAsColor")
 @SuppressLint("ResourceAsColor")
 @Composable
 @Composable
 fun TaskView(
 fun TaskView(
     task: Task,
     task: Task,
+    deleteTask: () -> Unit,
 ) {
 ) {
     var expanded by remember { mutableStateOf(false) }
     var expanded by remember { mutableStateOf(false) }
+    var showMoreActionsBottomSheet by remember { mutableStateOf(false) }
+
 
 
     // TODO Check color
     // TODO Check color
     Column(
     Column(
@@ -59,6 +67,11 @@ fun TaskView(
             .fillMaxWidth()
             .fillMaxWidth()
             .clip(RoundedCornerShape(16.dp))
             .clip(RoundedCornerShape(16.dp))
             .background(Color(R.color.primary))
             .background(Color(R.color.primary))
+            .combinedClickable(onClick = {
+                expanded = !expanded
+            }, onLongClick = {
+                showMoreActionsBottomSheet = true
+            })
     ) {
     ) {
         Spacer(modifier = Modifier.height(8.dp))
         Spacer(modifier = Modifier.height(8.dp))
 
 
@@ -81,15 +94,19 @@ fun TaskView(
             color = Color.White,
             color = Color.White,
             modifier = Modifier
             modifier = Modifier
                 .padding(4.dp)
                 .padding(4.dp)
-                .clickable { expanded = !expanded }
         )
         )
 
 
         Text(
         Text(
             text = if (expanded) task.output else task.output.take(100) + "...",
             text = if (expanded) task.output else task.output.take(100) + "...",
             color = Color.White,
             color = Color.White,
             modifier = Modifier
             modifier = Modifier
+                .animateContentSize(
+                    animationSpec = spring(
+                        dampingRatio = Spring.DampingRatioLowBouncy,
+                        stiffness = Spring.StiffnessLow
+                    )
+                )
                 .padding(4.dp)
                 .padding(4.dp)
-                .clickable { expanded = !expanded }
         )
         )
 
 
         if (task.output.length >= 100) {
         if (task.output.length >= 100) {
@@ -104,7 +121,23 @@ fun TaskView(
                 modifier = Modifier
                 modifier = Modifier
                     .fillMaxWidth()
                     .fillMaxWidth()
                     .padding(16.dp)
                     .padding(16.dp)
-                    .clickable { expanded = true }
+            )
+        }
+
+        if (showMoreActionsBottomSheet) {
+            val bottomSheetAction = listOf(
+                Triple(
+                    R.drawable.ic_delete,
+                    R.string.assistant_screen_task_more_actions_bottom_sheet_delete_action
+                ) {
+                    deleteTask()
+                },
+            )
+
+            MoreActionsBottomSheet(
+                title = task.input,
+                actions = bottomSheetAction,
+                dismiss = { showMoreActionsBottomSheet = false }
             )
             )
         }
         }
     }
     }

+ 3 - 10
app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepository.kt

@@ -24,6 +24,7 @@ package com.nextcloud.client.assistant.repository
 import com.nextcloud.common.NextcloudClient
 import com.nextcloud.common.NextcloudClient
 import com.owncloud.android.lib.common.operations.RemoteOperationResult
 import com.owncloud.android.lib.common.operations.RemoteOperationResult
 import com.owncloud.android.lib.resources.assistant.CreateTaskRemoteOperation
 import com.owncloud.android.lib.resources.assistant.CreateTaskRemoteOperation
+import com.owncloud.android.lib.resources.assistant.DeleteTaskRemoteOperation
 import com.owncloud.android.lib.resources.assistant.GetTaskListRemoteOperation
 import com.owncloud.android.lib.resources.assistant.GetTaskListRemoteOperation
 import com.owncloud.android.lib.resources.assistant.GetTaskTypesRemoteOperation
 import com.owncloud.android.lib.resources.assistant.GetTaskTypesRemoteOperation
 import com.owncloud.android.lib.resources.assistant.model.TaskList
 import com.owncloud.android.lib.resources.assistant.model.TaskList
@@ -46,15 +47,7 @@ class AssistantRepository(private val client: NextcloudClient) : AssistantReposi
         return GetTaskListRemoteOperation(appId).execute(client)
         return GetTaskListRemoteOperation(appId).execute(client)
     }
     }
 
 
-    /*
-    // TODO Check return type
-    override fun deleteTask(id: String): CreatedTask? {
-        return operation.delete("/ocs/v2.php/textprocessing/task/$id", TaskTypes::class.java)
+    override fun deleteTask(id: Long): RemoteOperationResult<Void> {
+        return DeleteTaskRemoteOperation(id).execute(client)
     }
     }
-
-    // TODO Check return type
-    override fun getTask(id: String): CreatedTask? {
-        return operation.get("/ocs/v2.php/textprocessing/task/$id", TaskTypes::class.java)
-    }
-     */
 }
 }

+ 1 - 10
app/src/main/java/com/nextcloud/client/assistant/repository/AssistantRepositoryType.kt

@@ -35,14 +35,5 @@ interface AssistantRepositoryType {
 
 
     fun getTaskList(appId: String): RemoteOperationResult<TaskList>
     fun getTaskList(appId: String): RemoteOperationResult<TaskList>
 
 
-    /*
-     fun getTask(id: String): CreatedTask?
-
-    fun deleteTask(id: String): CreatedTask?
-
-
-
-
-     */
-
+    fun deleteTask(id: Long): RemoteOperationResult<Void>
 }
 }

+ 1 - 1
app/src/main/java/com/nextcloud/ui/composeComponents/SimpleAlertDialog.kt → app/src/main/java/com/nextcloud/ui/composeComponents/alertDialog/SimpleAlertDialog.kt

@@ -19,7 +19,7 @@
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
  */
  */
 
 
-package com.nextcloud.ui.composeComponents
+package com.nextcloud.ui.composeComponents.alertDialog
 
 
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.Spacer

+ 101 - 0
app/src/main/java/com/nextcloud/ui/composeComponents/bottomSheet/MoreActionsBottomSheet.kt

@@ -0,0 +1,101 @@
+package com.nextcloud.ui.composeComponents.bottomSheet
+
+import android.annotation.SuppressLint
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.ModalBottomSheet
+import androidx.compose.material3.Text
+import androidx.compose.material3.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.owncloud.android.R
+import kotlinx.coroutines.launch
+
+@SuppressLint("ResourceAsColor")
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun MoreActionsBottomSheet(
+    title: String? = null,
+    actions: List<Triple<Int, Int, () -> Unit>>,
+    dismiss: () -> Unit
+) {
+    val sheetState = rememberModalBottomSheetState()
+    val scope = rememberCoroutineScope()
+
+    ModalBottomSheet(
+        modifier = Modifier.padding(top = 32.dp),
+        onDismissRequest = {
+            dismiss()
+        },
+        sheetState = sheetState
+    ) {
+        Column(
+            horizontalAlignment = Alignment.Start,
+            verticalArrangement = Arrangement.Top,
+            modifier = Modifier
+                .fillMaxWidth()
+                .padding(all = 8.dp)
+        ) {
+            title?.let {
+                Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxWidth()) {
+                    Text(text = title, fontSize = 18.sp)
+                }
+            }
+
+            Spacer(modifier = Modifier.height(16.dp))
+
+            actions.forEach { action ->
+                Row(
+                    modifier = Modifier
+                        .fillMaxWidth()
+                        .clickable {
+                            scope
+                                .launch { sheetState.hide() }
+                                .invokeOnCompletion {
+                                    if (!sheetState.isVisible) {
+                                        action.third()
+                                        dismiss()
+                                    }
+                                }
+                        }
+                        .padding(all = 16.dp),
+                    verticalAlignment = Alignment.CenterVertically,
+                ) {
+                    Icon(
+                        painter = painterResource(id = action.first),
+                        contentDescription = "action icon",
+                        tint = Color(R.color.secondary_button_background_color),
+                        modifier = Modifier.size(20.dp)
+                    )
+
+                    Spacer(modifier = Modifier.width(16.dp))
+
+                    Text(
+                        text = stringResource(action.second),
+                        fontSize = 16.sp,
+                    )
+                }
+            }
+
+            Spacer(modifier = Modifier.height(32.dp))
+        }
+    }
+}

+ 3 - 0
app/src/main/res/values/strings.xml

@@ -21,7 +21,10 @@
     <string name="assistant_screen_loading">Task List are loading, please wait</string>
     <string name="assistant_screen_loading">Task List are loading, please wait</string>
     <string name="assistant_screen_no_task_available_text">No task available, you can create a new task from bottom right.</string>
     <string name="assistant_screen_no_task_available_text">No task available, you can create a new task from bottom right.</string>
 
 
+    <string name="assistant_screen_task_more_actions_bottom_sheet_delete_action">Delete Task</string>
+
     <string name="assistant_screen_task_create_success_message">Task successfully created</string>
     <string name="assistant_screen_task_create_success_message">Task successfully created</string>
+    <string name="assistant_screen_task_delete_success_message">Task deleted</string>
     <string name="assistant_screen_create_task_alert_dialog_input_field_placeholder">Type some text</string>
     <string name="assistant_screen_create_task_alert_dialog_input_field_placeholder">Type some text</string>
 
 
     <string name="assistant_screen_task_view_input">Input: </string>
     <string name="assistant_screen_task_view_input">Input: </string>