diff --git a/browse/src/main/kotlin/com/alfresco/content/browse/processes/details/ProcessDetailViewModel.kt b/browse/src/main/kotlin/com/alfresco/content/browse/processes/details/ProcessDetailViewModel.kt index e2f6a070..6d862cea 100644 --- a/browse/src/main/kotlin/com/alfresco/content/browse/processes/details/ProcessDetailViewModel.kt +++ b/browse/src/main/kotlin/com/alfresco/content/browse/processes/details/ProcessDetailViewModel.kt @@ -192,7 +192,7 @@ class ProcessDetailViewModel( fun startWorkflow() = withState { state -> val items = state.listContents.joinToString(separator = ",") { it.id } viewModelScope.launch { - repository::startWorkflow.asFlow(state.parent, items).execute { + repository::startWorkflow.asFlow(state.parent, items, emptyList()).execute { when (it) { is Loading -> copy(requestStartWorkflow = Loading()) is Fail -> copy(requestStartWorkflow = Fail(it.error)) diff --git a/browse/src/main/kotlin/com/alfresco/content/browse/processes/details/ProcessDetailViewModelExtension.kt b/browse/src/main/kotlin/com/alfresco/content/browse/processes/details/ProcessDetailViewModelExtension.kt index 42327d8c..ae06a25e 100644 --- a/browse/src/main/kotlin/com/alfresco/content/browse/processes/details/ProcessDetailViewModelExtension.kt +++ b/browse/src/main/kotlin/com/alfresco/content/browse/processes/details/ProcessDetailViewModelExtension.kt @@ -1,7 +1,7 @@ package com.alfresco.content.browse.processes.details -import com.alfresco.content.browse.processes.list.UpdateProcessData import com.alfresco.content.browse.tasks.list.UpdateTasksData +import com.alfresco.content.process.ui.UpdateProcessData import com.alfresco.events.EventBus import kotlinx.coroutines.launch diff --git a/browse/src/main/kotlin/com/alfresco/content/browse/processes/list/ProcessesViewModel.kt b/browse/src/main/kotlin/com/alfresco/content/browse/processes/list/ProcessesViewModel.kt index 390d2ce3..72a4d7d5 100644 --- a/browse/src/main/kotlin/com/alfresco/content/browse/processes/list/ProcessesViewModel.kt +++ b/browse/src/main/kotlin/com/alfresco/content/browse/processes/list/ProcessesViewModel.kt @@ -13,6 +13,7 @@ import com.alfresco.content.data.payloads.TaskProcessFiltersPayload import com.alfresco.content.getLocalizedName import com.alfresco.content.listview.processes.ProcessListViewModel import com.alfresco.content.listview.processes.ProcessListViewState +import com.alfresco.content.process.ui.UpdateProcessData import com.alfresco.coroutines.asFlow import com.alfresco.events.on import kotlinx.coroutines.launch @@ -117,8 +118,3 @@ class ProcessesViewModel( fetchInitial() } } - -/** - * Mark as UpdateProcessData data class - */ -data class UpdateProcessData(val isRefresh: Boolean) diff --git a/browse/src/main/kotlin/com/alfresco/content/browse/tasks/detail/TaskDetailViewModelExtension.kt b/browse/src/main/kotlin/com/alfresco/content/browse/tasks/detail/TaskDetailViewModelExtension.kt index 80aab9ea..e52d784d 100644 --- a/browse/src/main/kotlin/com/alfresco/content/browse/tasks/detail/TaskDetailViewModelExtension.kt +++ b/browse/src/main/kotlin/com/alfresco/content/browse/tasks/detail/TaskDetailViewModelExtension.kt @@ -1,11 +1,11 @@ package com.alfresco.content.browse.tasks.detail import com.alfresco.content.actions.Action -import com.alfresco.content.browse.processes.list.UpdateProcessData import com.alfresco.content.browse.tasks.list.UpdateTasksData import com.alfresco.content.data.Entry import com.alfresco.content.data.OfflineRepository import com.alfresco.content.data.UserGroupDetails +import com.alfresco.content.process.ui.UpdateProcessData import com.alfresco.events.EventBus import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch diff --git a/component/src/main/java/com/alfresco/content/component/ComponentSheet.kt b/component/src/main/java/com/alfresco/content/component/ComponentSheet.kt index fdd7d2ea..d8d1d3dd 100644 --- a/component/src/main/java/com/alfresco/content/component/ComponentSheet.kt +++ b/component/src/main/java/com/alfresco/content/component/ComponentSheet.kt @@ -415,6 +415,7 @@ class ComponentSheet : BottomSheetDialogFragment(), MavericksView { id(bucket.hashCode()) data(bucket) clickListener { model, _, _, _ -> + onApply?.invoke("", "", mapOf()) dismiss() } } diff --git a/data/src/main/kotlin/com/alfresco/content/data/TaskRepository.kt b/data/src/main/kotlin/com/alfresco/content/data/TaskRepository.kt index 47d4c87f..1277bfa8 100644 --- a/data/src/main/kotlin/com/alfresco/content/data/TaskRepository.kt +++ b/data/src/main/kotlin/com/alfresco/content/data/TaskRepository.kt @@ -4,6 +4,8 @@ import android.content.SharedPreferences import androidx.preference.PreferenceManager import com.alfresco.content.data.Settings.Companion.IS_PROCESS_ENABLED_KEY import com.alfresco.content.data.payloads.CommentPayload +import com.alfresco.content.data.payloads.FieldType +import com.alfresco.content.data.payloads.FieldsData import com.alfresco.content.data.payloads.LinkContentPayload import com.alfresco.content.data.payloads.SystemPropertiesEntry import com.alfresco.content.data.payloads.TaskProcessFiltersPayload @@ -16,7 +18,6 @@ import com.alfresco.process.apis.ProcessAPI import com.alfresco.process.apis.TaskAPI import com.alfresco.process.models.AssignUserBody import com.alfresco.process.models.CommonOptionModel -import com.alfresco.process.models.GroupInfo import com.alfresco.process.models.ProfileData import com.alfresco.process.models.RequestComment import com.alfresco.process.models.RequestLinkContent @@ -26,7 +27,6 @@ import com.alfresco.process.models.RequestProcessInstancesQuery import com.alfresco.process.models.RequestSaveForm import com.alfresco.process.models.RequestTaskFilters import com.alfresco.process.models.TaskBodyCreate -import com.alfresco.process.models.UserInfo import com.alfresco.process.models.ValuesModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -383,55 +383,46 @@ class TaskRepository { /** * Execute the start flow integration */ - suspend fun startWorkflow(processEntry: ProcessEntry?, items: String) = ProcessEntry.with( + suspend fun startWorkflow(processEntry: ProcessEntry?, items: String, fields: List) = ProcessEntry.with( processesService.createProcessInstance( RequestProcessInstances( name = processEntry?.name, processDefinitionId = processEntry?.id, - values = ValuesModel( - due = processEntry?.formattedDueDate, - message = processEntry?.description, - priority = if (processEntry?.priority != -1) { - CommonOptionModel( - id = getTaskPriority(processEntry?.priority ?: 0).name, - name = getTaskPriority(processEntry?.priority ?: 0).name, - ) - } else { - null - }, - reviewer = getUser(processEntry?.startedBy), - reviewGroups = getGroup(processEntry?.startedBy), - items = items, - sendEmailNotifications = false, - ), + values = convertFieldsToValues(fields), ), ), ) - private fun getUser(userGroupInfo: UserGroupDetails?): UserInfo? { - return if (userGroupInfo?.isGroup == true) { - null - } else { - UserInfo( - id = userGroupInfo?.id, - firstName = userGroupInfo?.firstName, - lastName = userGroupInfo?.lastName, - email = userGroupInfo?.email, - ) + private fun convertFieldsToValues(fields: List): Map { + val values = mutableMapOf() + + fields.forEach { + if (it.type == FieldType.PEOPLE.value() || it.type == FieldType.FUNCTIONAL_GROUP.value()) { + values[it.id] = getUserOrGroup(it.value as UserGroupDetails) + } else { + values[it.id] = it.value + } } + + return values } - private fun getGroup(userGroupInfo: UserGroupDetails?): GroupInfo? { - return if (userGroupInfo?.isGroup == false) { - null + private fun getUserOrGroup(userGroupInfo: UserGroupDetails?): Map { + return if (userGroupInfo?.isGroup == true) { + mapOf( + "id" to userGroupInfo.id, + "name" to userGroupInfo.name, + "externalId" to userGroupInfo.externalId, + "status" to userGroupInfo.status, + "parentGroupId" to userGroupInfo.parentGroupId, + "groups" to userGroupInfo.groups, + ) } else { - GroupInfo( - id = userGroupInfo?.id, - name = userGroupInfo?.name, - externalId = userGroupInfo?.externalId, - status = userGroupInfo?.status, - parentGroupId = userGroupInfo?.parentGroupId, - groups = userGroupInfo?.groups, + mapOf( + "id" to userGroupInfo?.id, + "firstName" to userGroupInfo?.firstName, + "lastName" to userGroupInfo?.lastName, + "email" to userGroupInfo?.email, ) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f8b1eac3..23964392 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,7 +17,7 @@ ui-tooling = "1.5.4" alfresco-auth = "com.alfresco.android:auth:0.8.1-SNAPSHOT" alfresco-content = "com.alfresco.android:content:0.3.4-SNAPSHOT" alfresco-contentKtx = "com.alfresco.android:content-ktx:0.3.2-SNAPSHOT" -alfresco-process = "com.alfresco.android:process:0.1.2-SNAPSHOT" +alfresco-process = "com.alfresco.android:process:0.1.3-SNAPSHOT" android-desugar = "com.android.tools:desugar_jdk_libs:2.0.3" android-gradle = "com.android.tools.build:gradle:8.0.2" diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/FormViewModel.kt b/process-app/src/main/kotlin/com/alfresco/content/process/FormViewModel.kt index 3ac58e9c..97ea1e1a 100644 --- a/process-app/src/main/kotlin/com/alfresco/content/process/FormViewModel.kt +++ b/process-app/src/main/kotlin/com/alfresco/content/process/FormViewModel.kt @@ -27,6 +27,7 @@ data class FormViewState( val formFields: List = emptyList(), val processOutcomes: List = emptyList(), val enabledOutcomes: Boolean = false, + val requestStartWorkflow: Async = Uninitialized, ) : MavericksState { constructor(target: ProcessEntry) : this(parent = target) @@ -85,6 +86,7 @@ class FormViewModel( is Success -> { copy( + parent = processEntry, formFields = it().fields.flatMap { listData -> listData.fields }, processOutcomes = it().outcomes, requestStartForm = Success(it()), @@ -129,6 +131,19 @@ class FormViewModel( setState { updatedState.copy(enabledOutcomes = hasAllRequiredData) } } + fun startWorkflow() = withState { state -> + viewModelScope.launch { + repository::startWorkflow.asFlow(state.parent, "", state.formFields).execute { + when (it) { + is Loading -> copy(requestStartWorkflow = Loading()) + is Fail -> copy(requestStartWorkflow = Fail(it.error)) + is Success -> copy(requestStartWorkflow = Success(it())) + else -> this + } + } + } + } + private fun hasFieldRequiredData(state: FormViewState): Boolean { return !state.formFields.filter { it.required }.any { it.value == null } } diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/FormDetailScreen.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/FormDetailScreen.kt index 2f6050cc..c389e255 100644 --- a/process-app/src/main/kotlin/com/alfresco/content/process/ui/FormDetailScreen.kt +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/FormDetailScreen.kt @@ -67,7 +67,7 @@ fun FormDetailScreen(state: FormViewState, viewModel: FormViewModel, outcomes: L .fillMaxWidth() .align(alignment = Alignment.CenterHorizontally), ) { - Outcomes(outcomes = outcomes, state.enabledOutcomes) + Outcomes(outcomes = outcomes, state.enabledOutcomes, viewModel) } } } diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/UpdateProcessData.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/UpdateProcessData.kt new file mode 100644 index 00000000..c61068eb --- /dev/null +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/UpdateProcessData.kt @@ -0,0 +1,6 @@ +package com.alfresco.content.process.ui + +/** + * Mark as UpdateProcessData data class + */ +data class UpdateProcessData(val isRefresh: Boolean) diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/FloatingActionButton.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/FloatingActionButton.kt index a9c6f413..a88fb452 100644 --- a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/FloatingActionButton.kt +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/FloatingActionButton.kt @@ -12,10 +12,11 @@ import androidx.compose.ui.res.stringResource import com.alfresco.content.component.ComponentBuilder import com.alfresco.content.component.ComponentData import com.alfresco.content.data.OptionsModel +import com.alfresco.content.process.FormViewModel import com.alfresco.content.process.R @Composable -fun FloatingActionButton(outcomes: List, enabledOutcomes: Boolean) { +fun FloatingActionButton(outcomes: List, enabledOutcomes: Boolean, viewModel: FormViewModel) { val context = LocalContext.current ExtendedFloatingActionButton( @@ -28,6 +29,8 @@ fun FloatingActionButton(outcomes: List, enabledOutcomes: Boolean) ) ComponentBuilder(context, componentData) .onApply { name, query, _ -> + + viewModel.startWorkflow() } .onReset { name, query, _ -> } diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/FormScreen.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/FormScreen.kt index 71e9d44e..28a72b10 100644 --- a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/FormScreen.kt +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/FormScreen.kt @@ -1,6 +1,7 @@ package com.alfresco.content.process.ui.components import ComposeTopBar +import android.app.Activity import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.material3.FabPosition @@ -9,9 +10,11 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.navigation.NavController import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.Success import com.airbnb.mvrx.compose.collectAsState import com.airbnb.mvrx.compose.mavericksActivityViewModel import com.alfresco.content.data.OptionsModel @@ -24,6 +27,12 @@ fun FormScreen(navController: NavController) { // This will get or create a ViewModel scoped to the Activity. val viewModel: FormViewModel = mavericksActivityViewModel() val state by viewModel.collectAsState() + val context = LocalContext.current + + if (state.requestStartWorkflow is Success) { + viewModel.updateProcessList() + (context as Activity).finish() + } val customOutcomes = when { state.formFields.isNotEmpty() && state.processOutcomes.isEmpty() -> { @@ -62,7 +71,7 @@ fun FormScreen(navController: NavController) { else -> { Scaffold( topBar = { ComposeTopBar() }, - floatingActionButton = { FloatingActionButton(customOutcomes, state.enabledOutcomes) }, + floatingActionButton = { FloatingActionButton(customOutcomes, state.enabledOutcomes, viewModel) }, floatingActionButtonPosition = FabPosition.End, ) { padding -> val colorScheme = MaterialTheme.colorScheme @@ -74,7 +83,7 @@ fun FormScreen(navController: NavController) { color = colorScheme.background, contentColor = colorScheme.onBackground, ) { - if (state.requestStartForm is Loading) { + if (state.requestStartForm is Loading || state.requestStartWorkflow is Loading) { CustomLinearProgressIndicator(padding) } FormDetailScreen(state, viewModel, emptyList()) diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/FormViewModelExtension.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/FormViewModelExtension.kt new file mode 100644 index 00000000..f3e9dd7b --- /dev/null +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/FormViewModelExtension.kt @@ -0,0 +1,15 @@ +package com.alfresco.content.process.ui.components + +import com.alfresco.content.process.FormViewModel +import com.alfresco.content.process.ui.UpdateProcessData +import com.alfresco.events.EventBus +import kotlinx.coroutines.launch + +/** + * update the list of workflow if new entry created + */ +fun FormViewModel.updateProcessList() { + viewModelScope.launch { + EventBus.default.send(UpdateProcessData(isRefresh = true)) + } +} diff --git a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/Outcomes.kt b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/Outcomes.kt index 2513585e..df57c8d8 100644 --- a/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/Outcomes.kt +++ b/process-app/src/main/kotlin/com/alfresco/content/process/ui/components/Outcomes.kt @@ -10,16 +10,20 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel import com.alfresco.content.data.OptionsModel +import com.alfresco.content.process.FormViewModel @Composable -fun Outcomes(outcomes: List, enabledOutcomes: Boolean) { +fun Outcomes(outcomes: List, enabledOutcomes: Boolean, viewModel: FormViewModel) { outcomes.forEach { Button( modifier = Modifier .fillMaxWidth() .padding(horizontal = 12.dp, vertical = 4.dp), - onClick = { }, + onClick = { + viewModel.startWorkflow() + }, shape = RoundedCornerShape(6.dp), enabled = enabledOutcomes, colors = ButtonDefaults.buttonColors(