diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipFragment.kt b/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipFragment.kt index 7b5e984931..878bbfd918 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipFragment.kt +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipFragment.kt @@ -93,7 +93,11 @@ class RelationshipFragment : FragmentGlobalAbstract(), RelationshipView { is RelationshipResult.Success -> { relationshipSection?.let { relationshipSection -> - presenter.addRelationship(it.teiUidToAddAsRelationship, relationshipSection) + relationShipsViewModel.onAddRelationship( + selectedTeiUid = it.teiUidToAddAsRelationship, + relationshipTypeUid = relationshipSection.relationshipType.uid(), + direction = relationshipSection.direction, + ) } } } @@ -136,6 +140,8 @@ class RelationshipFragment : FragmentGlobalAbstract(), RelationshipView { val uiState by relationShipsViewModel.relationshipsUiState.collectAsState() val relationshipSelectionState by relationShipsViewModel.relationshipSelectionState.collectAsState() val showDeleteConfirmation by relationShipsViewModel.showDeleteConfirmation.collectAsState() + val snackbarMessage = + relationShipsViewModel.snackbarMessage.collectAsState(initial = null) when (showMap) { true -> RelationshipMapScreen(savedInstanceState) @@ -157,6 +163,7 @@ class RelationshipFragment : FragmentGlobalAbstract(), RelationshipView { ) }, onRelationShipSelected = relationShipsViewModel::updateSelectedList, + snackbarMessage = snackbarMessage.value, ) } diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipModule.java b/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipModule.java index 8c243f102e..a2078c7cf3 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipModule.java +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipModule.java @@ -5,8 +5,9 @@ import org.dhis2.commons.data.ProgramConfigurationRepository; import org.dhis2.commons.date.DateLabelProvider; import org.dhis2.commons.date.DateUtils; -import org.dhis2.commons.di.dagger.PerActivity; import org.dhis2.commons.di.dagger.PerFragment; +import org.dhis2.commons.network.NetworkUtils; +import org.dhis2.commons.resources.D2ErrorUtils; import org.dhis2.commons.resources.MetadataIconProvider; import org.dhis2.commons.resources.ResourceManager; import org.dhis2.commons.viewmodel.DispatcherProvider; @@ -20,6 +21,7 @@ import org.dhis2.tracker.relationships.data.EventRelationshipsRepository; import org.dhis2.tracker.relationships.data.RelationshipsRepository; import org.dhis2.tracker.relationships.data.TrackerRelationshipsRepository; +import org.dhis2.tracker.relationships.domain.AddRelationship; import org.dhis2.tracker.relationships.domain.DeleteRelationships; import org.dhis2.tracker.relationships.domain.GetRelationshipsByType; import org.dhis2.tracker.relationships.ui.RelationshipsViewModel; @@ -150,12 +152,16 @@ TeiAttributesProvider teiAttributesProvider(D2 d2) { RelationshipsViewModel provideRelationshipsViewModel( GetRelationshipsByType getRelationshipsByType, DeleteRelationships deleteRelationships, - DispatcherProvider dispatcherProvider + DispatcherProvider dispatcherProvider, + AddRelationship addRelationship, + D2ErrorUtils d2ErrorUtils ) { return new RelationshipsViewModel( + dispatcherProvider, getRelationshipsByType, deleteRelationships, - dispatcherProvider + addRelationship, + d2ErrorUtils ); } @@ -188,6 +194,15 @@ DeleteRelationships provideDeleteRelationships( return new DeleteRelationships(relationshipsRepository); } + @Provides + @PerFragment + AddRelationship provideAddRelationship( + DispatcherProvider dispatcherProvider, + RelationshipsRepository relationshipsRepository + ) { + return new AddRelationship(dispatcherProvider, relationshipsRepository); + } + @Provides @PerFragment RelationshipsRepository provideRelationshipsRepository( @@ -233,4 +248,12 @@ AvatarProvider provideAvatarProvider( ) { return new AvatarProvider(metadataIconProvider); } + + @Provides + @PerFragment + D2ErrorUtils provideD2ErrorUtils( + NetworkUtils networkUtils + ) { + return new D2ErrorUtils(moduleContext, networkUtils); + } } diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipPresenter.kt b/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipPresenter.kt index 9a2f45950f..ce83ff457b 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipPresenter.kt +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipPresenter.kt @@ -19,22 +19,16 @@ import org.dhis2.maps.layer.basemaps.BaseMapStyle import org.dhis2.maps.model.MapItemModel import org.dhis2.maps.usecases.MapStyleConfiguration import org.dhis2.tracker.relationships.data.RelationshipsRepository -import org.dhis2.tracker.relationships.model.RelationshipDirection import org.dhis2.tracker.relationships.model.RelationshipModel import org.dhis2.tracker.relationships.model.RelationshipOwnerType -import org.dhis2.tracker.relationships.model.RelationshipSection import org.dhis2.tracker.ui.AvatarProvider import org.dhis2.utils.analytics.AnalyticsHelper import org.dhis2.utils.analytics.CLICK -import org.dhis2.utils.analytics.DELETE_RELATIONSHIP import org.dhis2.utils.analytics.NEW_RELATIONSHIP import org.hisp.dhis.android.core.D2 import org.hisp.dhis.android.core.common.State -import org.hisp.dhis.android.core.maintenance.D2Error -import org.hisp.dhis.android.core.relationship.RelationshipHelper import org.hisp.dhis.android.core.relationship.RelationshipType import org.hisp.dhis.mobile.ui.designsystem.component.AdditionalInfoItem -import timber.log.Timber class RelationshipPresenter internal constructor( private val view: RelationshipView, @@ -144,81 +138,9 @@ class RelationshipPresenter internal constructor( } } - fun deleteRelationship(relationshipUid: String) { - try { - d2.relationshipModule().relationships().withItems().uid(relationshipUid) - .blockingDelete() - } catch (e: D2Error) { - Timber.d(e) - } finally { - analyticsHelper.setEvent(DELETE_RELATIONSHIP, CLICK, DELETE_RELATIONSHIP) - updateRelationships.onNext(true) - } - } - - fun addRelationship(selectedTei: String, relationshipSection: RelationshipSection) { - if (teiUid != null) { - addTeiToTeiRelationship(teiUid, selectedTei, relationshipSection) - } else if (eventUid != null) { - addEventToTeiRelationship( - eventUid, - selectedTei, - relationshipSection.relationshipType.uid(), - ) - } - } - - private fun addTeiToTeiRelationship( - teiUid: String, - selectedTei: String, - relationshipSection: RelationshipSection, - ) { - try { - val relationshipTypeUid = relationshipSection.relationshipType.uid() - val relationship = when (relationshipSection.direction) { - RelationshipDirection.FROM -> RelationshipHelper.teiToTeiRelationship( - selectedTei, - teiUid, - relationshipTypeUid, - ) - - RelationshipDirection.TO -> RelationshipHelper.teiToTeiRelationship( - teiUid, - selectedTei, - relationshipTypeUid, - ) - } - d2.relationshipModule().relationships().blockingAdd(relationship) - } catch (e: D2Error) { - view.displayMessage(e.errorDescription()) - } finally { - updateRelationships.onNext(true) - } - } - - private fun addEventToTeiRelationship( - eventUid: String, - selectedTei: String, - relationshipTypeUid: String, - ) { - try { - val relationship = - RelationshipHelper.eventToTeiRelationship( - eventUid, - selectedTei, - relationshipTypeUid, - ) - d2.relationshipModule().relationships().blockingAdd(relationship) - } catch (e: D2Error) { - view.displayMessage(e.errorDescription()) - } finally { - updateRelationships.onNext(true) - } - } - fun openDashboard(teiUid: String) { if (d2.trackedEntityModule() - .trackedEntityInstances().uid(teiUid).blockingGet()!!.state() != + .trackedEntityInstances().uid(teiUid).blockingGet()?.aggregatedSyncState() != State.RELATIONSHIP ) { if (d2.enrollmentModule().enrollments() @@ -239,7 +161,7 @@ class RelationshipPresenter internal constructor( } } - fun openEvent(eventUid: String, eventProgramUid: String) { + private fun openEvent(eventUid: String, eventProgramUid: String) { view.openEventFor(eventUid, eventProgramUid) } diff --git a/app/src/test/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipPresenterTest.kt b/app/src/test/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipPresenterTest.kt index 3d59c88f4f..c725f7ca2f 100644 --- a/app/src/test/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipPresenterTest.kt +++ b/app/src/test/java/org/dhis2/usescases/teiDashboard/dashboardfragments/relationships/RelationshipPresenterTest.kt @@ -9,8 +9,6 @@ import org.dhis2.maps.usecases.MapStyleConfiguration import org.dhis2.tracker.relationships.data.RelationshipsRepository import org.dhis2.tracker.ui.AvatarProvider import org.dhis2.utils.analytics.AnalyticsHelper -import org.dhis2.utils.analytics.CLICK -import org.dhis2.utils.analytics.DELETE_RELATIONSHIP import org.hisp.dhis.android.core.D2 import org.hisp.dhis.android.core.common.ObjectWithUid import org.hisp.dhis.android.core.common.State @@ -24,7 +22,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.mockito.Mockito -import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.times @@ -106,23 +103,6 @@ class RelationshipPresenterTest { verify(view, times(1)).showPermissionError() } - @Test - fun `Should delete relationship`() { - presenter.deleteRelationship(getMockedRelationship().uid()!!) - verify(analyticsHelper).setEvent(DELETE_RELATIONSHIP, CLICK, DELETE_RELATIONSHIP) - } - - @Test - fun `Should create a relationship`() { - whenever( - d2.relationshipModule().relationshipTypes().withConstraints().uid("relationshipTypeUid") - .blockingGet(), - ) doReturn getMockedRelationshipType(true) - presenter.addRelationship("selectedTei", "relationshipTypeUid") - - verify(view, times(0)).displayMessage(any()) - } - @Test fun `Should open dashboard`() { whenever( diff --git a/tracker/src/main/kotlin/org/dhis2/tracker/relationships/data/EventRelationshipsRepository.kt b/tracker/src/main/kotlin/org/dhis2/tracker/relationships/data/EventRelationshipsRepository.kt index a1c8991021..533cce756b 100644 --- a/tracker/src/main/kotlin/org/dhis2/tracker/relationships/data/EventRelationshipsRepository.kt +++ b/tracker/src/main/kotlin/org/dhis2/tracker/relationships/data/EventRelationshipsRepository.kt @@ -11,6 +11,7 @@ import org.hisp.dhis.android.core.D2 import org.hisp.dhis.android.core.common.State import org.hisp.dhis.android.core.event.Event import org.hisp.dhis.android.core.relationship.Relationship +import org.hisp.dhis.android.core.relationship.RelationshipHelper import org.hisp.dhis.android.core.relationship.RelationshipItem import org.hisp.dhis.android.core.relationship.RelationshipItemEvent import org.hisp.dhis.android.core.relationship.RelationshipType @@ -57,6 +58,20 @@ class EventRelationshipsRepository( ) } + override fun createRelationship( + selectedTeiUid: String, + relationshipTypeUid: String, + direction: RelationshipDirection, + ): Relationship { + val (fromUid, toUid) = when (direction) { + RelationshipDirection.FROM -> Pair(selectedTeiUid, eventUid) + RelationshipDirection.TO -> Pair(eventUid, selectedTeiUid) + } + return RelationshipHelper.eventToTeiRelationship( + fromUid,toUid,relationshipTypeUid + ) + } + override fun getRelationships(): Flow> { return flowOf( d2.relationshipModule().relationships().getByItem( diff --git a/tracker/src/main/kotlin/org/dhis2/tracker/relationships/data/RelationshipsRepository.kt b/tracker/src/main/kotlin/org/dhis2/tracker/relationships/data/RelationshipsRepository.kt index da288d3ad8..5bdc73560c 100644 --- a/tracker/src/main/kotlin/org/dhis2/tracker/relationships/data/RelationshipsRepository.kt +++ b/tracker/src/main/kotlin/org/dhis2/tracker/relationships/data/RelationshipsRepository.kt @@ -11,9 +11,11 @@ import org.dhis2.tracker.relationships.model.RelationshipOwnerType import org.hisp.dhis.android.core.D2 import org.hisp.dhis.android.core.common.ObjectStyle import org.hisp.dhis.android.core.event.Event +import org.hisp.dhis.android.core.maintenance.D2Error import org.hisp.dhis.android.core.organisationunit.OrganisationUnit import org.hisp.dhis.android.core.program.ProgramStage import org.hisp.dhis.android.core.program.ProgramType +import org.hisp.dhis.android.core.relationship.Relationship import org.hisp.dhis.android.core.relationship.RelationshipConstraint import org.hisp.dhis.android.core.relationship.RelationshipType import org.hisp.dhis.android.core.systeminfo.DHISVersion @@ -29,7 +31,15 @@ abstract class RelationshipsRepository( ) { abstract fun getRelationshipTypes(): Flow>> abstract fun getRelationships(): Flow> - abstract fun getRelationshipDirectionInfo(relationshipType: RelationshipType): Pair + abstract fun getRelationshipDirectionInfo( + relationshipType: RelationshipType + ): Pair + + abstract fun createRelationship( + selectedTeiUid: String, + relationshipTypeUid: String, + direction: RelationshipDirection, + ): Relationship protected fun orgUnitInScope(orgUnitUid: String?): Boolean { return orgUnitUid?.let { @@ -235,4 +245,13 @@ abstract class RelationshipsRepository( .uid(relationshipUid) .blockingDelete() } + + fun addRelationship(relationship: Relationship): Result { + return try { + val relationshipUid = d2.relationshipModule().relationships().blockingAdd(relationship) + Result.success(relationshipUid) + } catch (error: D2Error) { + Result.failure(error) + } + } } diff --git a/tracker/src/main/kotlin/org/dhis2/tracker/relationships/data/TrackerRelationshipsRepository.kt b/tracker/src/main/kotlin/org/dhis2/tracker/relationships/data/TrackerRelationshipsRepository.kt index 4799988a8b..3bba91962f 100644 --- a/tracker/src/main/kotlin/org/dhis2/tracker/relationships/data/TrackerRelationshipsRepository.kt +++ b/tracker/src/main/kotlin/org/dhis2/tracker/relationships/data/TrackerRelationshipsRepository.kt @@ -10,6 +10,8 @@ import org.dhis2.tracker.relationships.model.RelationshipOwnerType import org.hisp.dhis.android.core.D2 import org.hisp.dhis.android.core.common.Geometry import org.hisp.dhis.android.core.common.State +import org.hisp.dhis.android.core.relationship.Relationship +import org.hisp.dhis.android.core.relationship.RelationshipHelper import org.hisp.dhis.android.core.relationship.RelationshipItem import org.hisp.dhis.android.core.relationship.RelationshipItemTrackedEntityInstance import org.hisp.dhis.android.core.relationship.RelationshipType @@ -227,6 +229,20 @@ class TrackerRelationshipsRepository( ) } + override fun createRelationship( + selectedTeiUid: String, + relationshipTypeUid: String, + direction: RelationshipDirection, + ): Relationship { + val (fromUid, toUid) = when (direction) { + RelationshipDirection.FROM -> Pair(selectedTeiUid, teiUid) + RelationshipDirection.TO -> Pair(teiUid, selectedTeiUid) + } + return RelationshipHelper.teiToTeiRelationship( + fromUid, toUid, relationshipTypeUid + ) + } + override fun getRelationshipDirectionInfo(relationshipType: RelationshipType): Pair { val teiTypeUid = d2.trackedEntityModule().trackedEntityInstances() .uid(teiUid) diff --git a/tracker/src/main/kotlin/org/dhis2/tracker/relationships/domain/AddRelationship.kt b/tracker/src/main/kotlin/org/dhis2/tracker/relationships/domain/AddRelationship.kt new file mode 100644 index 0000000000..f175ab1325 --- /dev/null +++ b/tracker/src/main/kotlin/org/dhis2/tracker/relationships/domain/AddRelationship.kt @@ -0,0 +1,24 @@ +package org.dhis2.tracker.relationships.domain + +import kotlinx.coroutines.withContext +import org.dhis2.commons.viewmodel.DispatcherProvider +import org.dhis2.tracker.relationships.data.RelationshipsRepository +import org.dhis2.tracker.relationships.model.RelationshipDirection + +class AddRelationship( + private val dispatcher: DispatcherProvider, + private val repository: RelationshipsRepository, +) { + suspend operator fun invoke( + selectedTeiUid: String, + relationshipTypeUid: String, + direction: RelationshipDirection, + ): Result = withContext(dispatcher.io()) { + val relationship = repository.createRelationship( + selectedTeiUid = selectedTeiUid, + relationshipTypeUid = relationshipTypeUid, + direction = direction, + ) + repository.addRelationship(relationship) + } +} diff --git a/tracker/src/main/kotlin/org/dhis2/tracker/relationships/ui/RelationshipsScreen.kt b/tracker/src/main/kotlin/org/dhis2/tracker/relationships/ui/RelationshipsScreen.kt index b547e75bb4..86ccec79ce 100644 --- a/tracker/src/main/kotlin/org/dhis2/tracker/relationships/ui/RelationshipsScreen.kt +++ b/tracker/src/main/kotlin/org/dhis2/tracker/relationships/ui/RelationshipsScreen.kt @@ -16,13 +16,17 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material.Scaffold +import androidx.compose.material.SnackbarHost import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Add import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.ErrorOutline +import androidx.compose.material.rememberScaffoldState import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -74,58 +78,75 @@ fun RelationShipsScreen( relationshipSelectionState: ListSelectionState, onCreateRelationshipClick: (RelationshipSection) -> Unit, onRelationshipClick: (RelationshipItem) -> Unit, - onRelationShipSelected: (String) -> Unit + onRelationShipSelected: (String) -> Unit, + snackbarMessage: String?, ) { - LazyColumn( - modifier = Modifier - .fillMaxSize() - .background(color = Color.White, shape = Shape.LargeTop) - .padding(), - verticalArrangement = spacedBy(Spacing.Spacing4), - ) { - when (uiState) { - is RelationshipsUiState.Loading -> { - item { - Box( - modifier = Modifier - .fillMaxWidth() - .height(200.dp), - contentAlignment = Alignment.Center, - ) { - ProgressIndicator(type = ProgressIndicatorType.CIRCULAR) + val scaffoldState = rememberScaffoldState() + val snackbarHostState = scaffoldState.snackbarHostState + + LaunchedEffect(snackbarMessage) { + snackbarMessage?.let { message -> + snackbarHostState.showSnackbar(message) + } + } + + Scaffold( + scaffoldState = scaffoldState, + snackbarHost = { SnackbarHost(hostState = snackbarHostState) } + ) { contentPadding -> + LazyColumn( + modifier = Modifier + .fillMaxSize() + .background(color = Color.White, shape = Shape.LargeTop) + .padding(), + verticalArrangement = spacedBy(Spacing.Spacing4), + contentPadding = contentPadding, + ) { + when (uiState) { + is RelationshipsUiState.Loading -> { + item { + Box( + modifier = Modifier + .fillMaxWidth() + .height(200.dp), + contentAlignment = Alignment.Center, + ) { + ProgressIndicator(type = ProgressIndicatorType.CIRCULAR) + } } } - } - is RelationshipsUiState.Empty, - is RelationshipsUiState.Error -> { - item { NoRelationships() } - } + is RelationshipsUiState.Empty, + is RelationshipsUiState.Error -> { + item { NoRelationships() } + } - is RelationshipsUiState.Success -> { - items(uiState.data) { item -> - RelationShipTypeSection( - title = item.title, - description = if (item.relationships.isEmpty()) { - stringResource(id = R.string.no_data) - } else { - null - }, - relationships = item.relationships, - canAddRelationship = item.canAddRelationship(), - relationshipSelectionState = relationshipSelectionState, - onCreateRelationshipClick = { - onCreateRelationshipClick(item) - }, - onRelationshipClick = { - onRelationshipClick(it) - }, - onRelationshipSelected = onRelationShipSelected, - ) + is RelationshipsUiState.Success -> { + items(uiState.data) { item -> + RelationShipTypeSection( + title = item.title, + description = if (item.relationships.isEmpty()) { + stringResource(id = R.string.no_data) + } else { + null + }, + relationships = item.relationships, + canAddRelationship = item.canAddRelationship(), + relationshipSelectionState = relationshipSelectionState, + onCreateRelationshipClick = { + onCreateRelationshipClick(item) + }, + onRelationshipClick = { + onRelationshipClick(it) + }, + onRelationshipSelected = onRelationShipSelected, + ) + } } } } } + } @Composable @@ -353,6 +374,7 @@ fun RelationShipScreenPreview() { onCreateRelationshipClick = {}, onRelationshipClick = {}, onRelationShipSelected = {}, + snackbarMessage = null, ) } diff --git a/tracker/src/main/kotlin/org/dhis2/tracker/relationships/ui/RelationshipsViewModel.kt b/tracker/src/main/kotlin/org/dhis2/tracker/relationships/ui/RelationshipsViewModel.kt index 407fe4ad7d..79c4bdb416 100644 --- a/tracker/src/main/kotlin/org/dhis2/tracker/relationships/ui/RelationshipsViewModel.kt +++ b/tracker/src/main/kotlin/org/dhis2/tracker/relationships/ui/RelationshipsViewModel.kt @@ -3,35 +3,46 @@ package org.dhis2.tracker.relationships.ui import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import org.dhis2.commons.resources.D2ErrorUtils import org.dhis2.commons.viewmodel.DispatcherProvider +import org.dhis2.tracker.relationships.domain.AddRelationship import org.dhis2.tracker.relationships.domain.DeleteRelationships import org.dhis2.tracker.relationships.domain.GetRelationshipsByType import org.dhis2.tracker.relationships.model.ListSelectionState +import org.dhis2.tracker.relationships.model.RelationshipDirection import org.dhis2.tracker.relationships.model.RelationshipSection @OptIn(ExperimentalCoroutinesApi::class) class RelationshipsViewModel( + private val dispatcher: DispatcherProvider, private val getRelationshipsByType: GetRelationshipsByType, private val deleteRelationships: DeleteRelationships, - private val dispatcher: DispatcherProvider, + private val addRelationship: AddRelationship, + private val d2ErrorUtils: D2ErrorUtils, ) : ViewModel() { - - private val _relationshipsUiState = MutableStateFlow>>(RelationshipsUiState.Loading) - val relationshipsUiState: StateFlow>> = _relationshipsUiState.asStateFlow() + private val _relationshipsUiState = + MutableStateFlow>>(RelationshipsUiState.Loading) + val relationshipsUiState: StateFlow>> = + _relationshipsUiState.asStateFlow() private val _relationshipSelectionState = MutableStateFlow(ListSelectionState()) val relationshipSelectionState = _relationshipSelectionState.asStateFlow() private val _showDeleteConfirmation = MutableStateFlow(false) - var showDeleteConfirmation = _showDeleteConfirmation.asStateFlow() + val showDeleteConfirmation = _showDeleteConfirmation.asStateFlow() + + private val _snackbarMessage = MutableSharedFlow() + val snackbarMessage = _snackbarMessage.asSharedFlow() fun refreshRelationships() { viewModelScope.launch(dispatcher.io()) { @@ -97,10 +108,10 @@ class RelationshipsViewModel( fun stopSelectingMode() { viewModelScope.launch(dispatcher.io()) { _relationshipSelectionState.update { - it.copy( + it.copy( selectingMode = false, selectedItems = emptyList() - ) + ) } } } @@ -125,4 +136,33 @@ class RelationshipsViewModel( fun onDismissDelete() { _showDeleteConfirmation.value = false } + + fun onAddRelationship( + selectedTeiUid: String, + relationshipTypeUid: String, + direction: RelationshipDirection, + ) { + viewModelScope.launch(dispatcher.io()) { + addRelationship( + selectedTeiUid = selectedTeiUid, + relationshipTypeUid = relationshipTypeUid, + direction = direction, + ).fold( + onSuccess = { + refreshRelationships() + }, + onFailure = { d2Error -> + d2ErrorUtils.getErrorMessage(d2Error)?.let { + showSnackbar(it) + } + } + ) + } + } + + private fun showSnackbar(message: String) { + viewModelScope.launch { + _snackbarMessage.emit(message) + } + } } \ No newline at end of file