From 9db1bea1175ded29ab2e19771393eb2f31412bcd Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Tue, 14 Jan 2025 16:36:08 +0100 Subject: [PATCH 1/7] Add MessageAction.NoAction --- .../bdh/engagehf/bluetooth/data/mapper/MessageActionMapper.kt | 2 +- .../edu/stanford/bdh/engagehf/messages/MessagesAction.kt | 1 + .../edu/stanford/bdh/engagehf/messages/MessagesHandler.kt | 1 + .../edu/stanford/spezi/module/account/login/LoginScreen.kt | 3 --- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/mapper/MessageActionMapper.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/mapper/MessageActionMapper.kt index f98cef3af..f928b2241 100644 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/mapper/MessageActionMapper.kt +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/mapper/MessageActionMapper.kt @@ -14,7 +14,7 @@ class MessageActionMapper @Inject constructor() { fun map(action: String?): Result { return runCatching { when { - action.isNullOrBlank() -> error("Invalid action type") + action.isNullOrBlank() -> MessagesAction.NoAction videoSectionRegex.matches(action) -> { mapVideoSectionAction(action).getOrThrow() } diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessagesAction.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessagesAction.kt index 6106b9234..54ab272d6 100644 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessagesAction.kt +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessagesAction.kt @@ -1,6 +1,7 @@ package edu.stanford.bdh.engagehf.messages sealed class MessagesAction { + data object NoAction : MessagesAction() data class VideoSectionAction(val videoSectionVideo: VideoSectionVideo) : MessagesAction() data object MedicationsAction : MessagesAction() data object MeasurementsAction : MessagesAction() diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessagesHandler.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessagesHandler.kt index 79d2b331d..8287150af 100644 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessagesHandler.kt +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessagesHandler.kt @@ -30,6 +30,7 @@ class MessagesHandler @Inject constructor( val actionResult = messagesActionMapper.map(action = message.action) var failure = actionResult.exceptionOrNull() when (val messagesAction = actionResult.getOrNull()) { + is MessagesAction.NoAction -> Unit is MessagesAction.HealthSummaryAction -> { failure = healthSummaryService.generateHealthSummaryPdf().exceptionOrNull() } diff --git a/modules/account/src/main/kotlin/edu/stanford/spezi/module/account/login/LoginScreen.kt b/modules/account/src/main/kotlin/edu/stanford/spezi/module/account/login/LoginScreen.kt index b177ecb6b..283afcf7a 100644 --- a/modules/account/src/main/kotlin/edu/stanford/spezi/module/account/login/LoginScreen.kt +++ b/modules/account/src/main/kotlin/edu/stanford/spezi/module/account/login/LoginScreen.kt @@ -47,7 +47,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import edu.stanford.spezi.core.design.component.AsyncTextButton -import edu.stanford.spezi.core.design.component.VerticalSpacer import edu.stanford.spezi.core.design.component.validated.outlinedtextfield.ValidatedOutlinedTextField import edu.stanford.spezi.core.design.theme.Colors import edu.stanford.spezi.core.design.theme.Spacings @@ -56,8 +55,6 @@ import edu.stanford.spezi.core.design.theme.TextStyles.bodyLarge import edu.stanford.spezi.core.design.theme.TextStyles.titleLarge import edu.stanford.spezi.core.utils.extensions.testIdentifier import edu.stanford.spezi.module.account.R -import edu.stanford.spezi.module.account.login.components.SignInWithGoogleButton -import edu.stanford.spezi.module.account.login.components.TextDivider import edu.stanford.spezi.module.account.register.FieldState import edu.stanford.spezi.module.account.register.IconLeadingContent import edu.stanford.spezi.core.design.R as DesignR From 5ac2d0c11e70649aedbfdea3ddf986f82d2b5249 Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Tue, 14 Jan 2025 16:47:12 +0100 Subject: [PATCH 2/7] Remove Message.isLoading --- .../stanford/bdh/engagehf/messages/Message.kt | 1 - .../bdh/engagehf/messages/MessageItem.kt | 20 ++++++++----------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/Message.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/Message.kt index 654592c10..69a771082 100644 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/Message.kt +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/Message.kt @@ -15,7 +15,6 @@ data class Message( val description: String? = null, val action: String?, val isDismissible: Boolean = true, - val isLoading: Boolean = false, val isExpanded: Boolean = false, ) { diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessageItem.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessageItem.kt index 95f33bcdd..099bd8a21 100644 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessageItem.kt +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessageItem.kt @@ -16,7 +16,6 @@ import androidx.compose.material.icons.filled.KeyboardArrowDown import androidx.compose.material.icons.filled.KeyboardArrowUp import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Text @@ -125,17 +124,14 @@ fun MessageItem( ) } Spacer(modifier = Modifier.weight(1f)) - Button( - modifier = Modifier.testIdentifier(MessageItemTestIdentifiers.ACTION_BUTTON), - colors = ButtonDefaults.buttonColors(containerColor = primary), - enabled = !message.isLoading, - onClick = { - onAction(Action.MessageItemClicked(message)) - }, - ) { - if (message.isLoading) { - CircularProgressIndicator(modifier = Modifier.size(Sizes.Content.small)) - } else { + if (message.action != null || message.isDismissible) { + Button( + modifier = Modifier.testIdentifier(MessageItemTestIdentifiers.ACTION_BUTTON), + colors = ButtonDefaults.buttonColors(containerColor = primary), + onClick = { + onAction(Action.MessageItemClicked(message)) + }, + ) { Text( text = stringResource(R.string.message_item_button_action_text), color = Colors.onPrimary, From eb49697297dc359c1bbb07b7b712baceae02974d Mon Sep 17 00:00:00 2001 From: Paul Kraft Date: Sat, 18 Jan 2025 19:15:18 +0100 Subject: [PATCH 3/7] update --- .../bdh/engagehf/MainActivityViewModel.kt | 18 ++--- .../bdh/engagehf/bluetooth/HomeViewModel.kt | 20 ++--- .../data/mapper/MessageActionMapper.kt | 26 +++---- .../engagehf/bluetooth/data/models/Action.kt | 5 +- .../engagehf/bluetooth/data/models/UiState.kt | 24 +++++- .../engagehf/bluetooth/screen/HomeScreen.kt | 21 +++--- .../medication/ui/MedicationViewModel.kt | 6 +- .../messages/FirestoreMessageMapper.kt | 7 +- .../stanford/bdh/engagehf/messages/Message.kt | 53 +------------ .../bdh/engagehf/messages/MessageAction.kt | 12 +++ .../bdh/engagehf/messages/MessageItem.kt | 42 +++++------ .../engagehf/messages/MessageRepository.kt | 2 +- .../bdh/engagehf/messages/MessagesAction.kt | 12 --- .../bdh/engagehf/messages/MessagesHandler.kt | 74 ++++++++++--------- .../bdh/engagehf/MainActivityViewModelTest.kt | 21 +++--- .../engagehf/bluetooth/HomeViewModelTest.kt | 16 ++-- .../data/mapper/MessageActionMapperTest.kt | 22 +++--- .../medication/ui/MedicationViewModelTest.kt | 16 ++-- .../messages/FirestoreMessageMapperTest.kt | 13 ++-- .../messages/MessageRepositoryTest.kt | 4 +- .../engagehf/messages/MessagesHandlerTest.kt | 37 +++++----- 21 files changed, 210 insertions(+), 241 deletions(-) create mode 100644 app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessageAction.kt delete mode 100644 app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessagesAction.kt diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/MainActivityViewModel.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/MainActivityViewModel.kt index 1a011f785..edf1cd443 100644 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/MainActivityViewModel.kt +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/MainActivityViewModel.kt @@ -4,8 +4,8 @@ import android.content.Intent import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import edu.stanford.bdh.engagehf.messages.Message -import edu.stanford.bdh.engagehf.messages.MessageType +import edu.stanford.bdh.engagehf.bluetooth.data.mapper.MessageActionMapper +import edu.stanford.bdh.engagehf.messages.MessageAction import edu.stanford.bdh.engagehf.messages.MessagesHandler import edu.stanford.bdh.engagehf.navigation.AppNavigationEvent import edu.stanford.bdh.engagehf.navigation.Routes @@ -32,6 +32,7 @@ class MainActivityViewModel @Inject constructor( private val navigator: Navigator, private val userSessionManager: UserSessionManager, private val messageNotifier: MessageNotifier, + private val messageActionMapper: MessageActionMapper, private val messagesHandler: MessagesHandler, ) : ViewModel() { private val logger by speziLogger() @@ -52,15 +53,10 @@ class MainActivityViewModel @Inject constructor( val firebaseMessage = intent.getParcelableExtra(FIREBASE_MESSAGE_KEY) firebaseMessage?.messageId?.let { messageId -> viewModelScope.launch { - messagesHandler.handle( - message = Message( - id = messageId, // Is needed to dismiss the message - type = MessageType.Unknown, // We don't need the type, since we directly use the action - title = "", // We don't need the title, since we directly use the action - action = firebaseMessage.action, // We directly use the action - isDismissible = firebaseMessage.isDismissible == true - ) - ) + messagesHandler.handle(action = + messageActionMapper.map(firebaseMessage.action) + .getOrElse { MessageAction.UnknownAction }, + ).exceptionOrNull() } } } diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/HomeViewModel.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/HomeViewModel.kt index 6eb145da2..3543aa01e 100644 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/HomeViewModel.kt +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/HomeViewModel.kt @@ -11,12 +11,12 @@ import dagger.hilt.android.qualifiers.ApplicationContext import edu.stanford.bdh.engagehf.bluetooth.component.AppScreenEvents import edu.stanford.bdh.engagehf.bluetooth.data.mapper.BluetoothUiStateMapper import edu.stanford.bdh.engagehf.bluetooth.data.models.Action +import edu.stanford.bdh.engagehf.bluetooth.data.models.MessageUiModel import edu.stanford.bdh.engagehf.bluetooth.data.models.UiState import edu.stanford.bdh.engagehf.bluetooth.measurements.MeasurementsRepository import edu.stanford.bdh.engagehf.bluetooth.service.EngageBLEService import edu.stanford.bdh.engagehf.bluetooth.service.EngageBLEServiceEvent import edu.stanford.bdh.engagehf.bluetooth.service.EngageBLEServiceState -import edu.stanford.bdh.engagehf.messages.Message import edu.stanford.bdh.engagehf.messages.MessagesHandler import edu.stanford.bdh.engagehf.navigation.screens.BottomBarItem import edu.stanford.spezi.core.logging.speziLogger @@ -120,7 +120,7 @@ class HomeViewModel @Inject internal constructor( messagesHandler.observeUserMessages().collect { messages -> _uiState.update { it.copy( - messages = messages + messages = messages.map { MessageUiModel(it) } ) } } @@ -144,7 +144,7 @@ class HomeViewModel @Inject internal constructor( } is Action.MessageItemClicked -> { - handleMessage(message = action.message) + handleMessage(model = action.message) } is Action.ToggleExpand -> { @@ -190,12 +190,12 @@ class HomeViewModel @Inject internal constructor( } } - private fun handleMessage(message: Message) { + private fun handleMessage(model: MessageUiModel) { val setLoading = { loading: Boolean -> _uiState.update { it.copy( messages = it.messages.map { current -> - if (current.id == message.id) { + if (current.message.id == model.message.id) { current.copy(isLoading = loading) } else { current @@ -206,7 +206,7 @@ class HomeViewModel @Inject internal constructor( } viewModelScope.launch { setLoading(true) - messagesHandler.handle(message = message) + messagesHandler.handle(message = model.message) setLoading(false) } } @@ -219,11 +219,11 @@ class HomeViewModel @Inject internal constructor( private fun handleToggleExpandAction(action: Action.ToggleExpand) { _uiState.update { it.copy( - messages = it.messages.map { message -> - if (message.id == action.message.id) { - message.copy(isExpanded = !message.isExpanded) + messages = it.messages.map { model -> + if (model.message.id == action.message.message.id) { + model.copy(isExpanded = !model.isExpanded) } else { - message + model } } ) diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/mapper/MessageActionMapper.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/mapper/MessageActionMapper.kt index f928b2241..97fa41b4b 100644 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/mapper/MessageActionMapper.kt +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/mapper/MessageActionMapper.kt @@ -1,7 +1,7 @@ package edu.stanford.bdh.engagehf.bluetooth.data.mapper -import edu.stanford.bdh.engagehf.messages.MessagesAction -import edu.stanford.bdh.engagehf.messages.VideoSectionVideo +import edu.stanford.bdh.engagehf.messages.MessageAction +import edu.stanford.bdh.engagehf.messages.Video import javax.inject.Inject class MessageActionMapper @Inject constructor() { @@ -11,34 +11,32 @@ class MessageActionMapper @Inject constructor() { private val questionnaireRegex = Regex("/?questionnaires/(.+)") } - fun map(action: String?): Result { + fun map(action: String?): Result { return runCatching { when { - action.isNullOrBlank() -> MessagesAction.NoAction + action.isNullOrBlank() -> MessageAction.UnknownAction videoSectionRegex.matches(action) -> { - mapVideoSectionAction(action).getOrThrow() + mapVideoAction(action).getOrThrow() } - - action == "medications" -> MessagesAction.MedicationsAction - action == "observations" -> MessagesAction.MeasurementsAction + action == "medications" -> MessageAction.MedicationsAction + action == "observations" -> MessageAction.MeasurementsAction questionnaireRegex.matches(action) -> { val matchResult = questionnaireRegex.find(action) val (questionnaireId) = matchResult!!.destructured - MessagesAction.QuestionnaireAction(questionnaireId) + MessageAction.QuestionnaireAction(questionnaireId) } - - action == "healthSummary" -> MessagesAction.HealthSummaryAction + action == "healthSummary" -> MessageAction.HealthSummaryAction else -> error("Unknown action type") } } } - fun mapVideoSectionAction(action: String): Result { + fun mapVideoAction(action: String): Result { return runCatching { val matchResult = videoSectionRegex.find(action) val (videoSectionId, videoId) = matchResult!!.destructured - MessagesAction.VideoSectionAction( - VideoSectionVideo( + MessageAction.VideoAction( + Video( videoSectionId, videoId ) diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/models/Action.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/models/Action.kt index 1d3eaf19d..e5715fd08 100644 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/models/Action.kt +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/models/Action.kt @@ -1,13 +1,12 @@ package edu.stanford.bdh.engagehf.bluetooth.data.models import edu.stanford.bdh.engagehf.bluetooth.service.Measurement -import edu.stanford.bdh.engagehf.messages.Message sealed interface Action { data class ConfirmMeasurement(val measurement: Measurement) : Action data object DismissDialog : Action - data class MessageItemClicked(val message: Message) : Action - data class ToggleExpand(val message: Message) : Action + data class MessageItemClicked(val message: MessageUiModel) : Action + data class ToggleExpand(val message: MessageUiModel) : Action data class PermissionResult(val permission: String) : Action data object Resumed : Action data object BLEDevicePairing : Action diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/models/UiState.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/models/UiState.kt index c3fda25b3..3998503d7 100644 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/models/UiState.kt +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/models/UiState.kt @@ -1,6 +1,9 @@ package edu.stanford.bdh.engagehf.bluetooth.data.models import edu.stanford.bdh.engagehf.messages.Message +import edu.stanford.bdh.engagehf.messages.MessageAction +import edu.stanford.spezi.core.design.R +import java.time.format.DateTimeFormatter data class UiState( val bloodPressure: VitalDisplayData = VitalDisplayData( @@ -13,7 +16,26 @@ data class UiState( title = "Weight", ), val missingPermissions: Set = emptySet(), - val messages: List = emptyList(), + val messages: List = emptyList(), val bluetooth: BluetoothUiState = BluetoothUiState.Idle(), val measurementDialog: MeasurementDialogUiState = MeasurementDialogUiState(), ) + +data class MessageUiModel( + val message: Message, + val isExpanded: Boolean = false, + val isLoading: Boolean = false, +) { + val dueDateFormattedString: String? + get() = message.dueDate?.format(DateTimeFormatter.ofPattern("MMM dd, yyyy hh:mm a")) + + val icon: Int = + when (message.action) { + is MessageAction.MedicationsAction -> R.drawable.ic_medication + is MessageAction.MeasurementsAction -> R.drawable.ic_vital_signs + is MessageAction.QuestionnaireAction -> R.drawable.ic_assignment + is MessageAction.VideoAction -> R.drawable.ic_visibility + is MessageAction.UnknownAction -> R.drawable.ic_assignment + is MessageAction.HealthSummaryAction -> R.drawable.ic_vital_signs + } +} diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/screen/HomeScreen.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/screen/HomeScreen.kt index ca444fb0b..b9e720543 100644 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/screen/HomeScreen.kt +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/bluetooth/screen/HomeScreen.kt @@ -39,11 +39,12 @@ import edu.stanford.bdh.engagehf.bluetooth.data.models.Action import edu.stanford.bdh.engagehf.bluetooth.data.models.BluetoothUiState import edu.stanford.bdh.engagehf.bluetooth.data.models.DeviceUiModel import edu.stanford.bdh.engagehf.bluetooth.data.models.MeasurementDialogUiState +import edu.stanford.bdh.engagehf.bluetooth.data.models.MessageUiModel import edu.stanford.bdh.engagehf.bluetooth.data.models.UiState import edu.stanford.bdh.engagehf.bluetooth.data.models.VitalDisplayData import edu.stanford.bdh.engagehf.messages.Message +import edu.stanford.bdh.engagehf.messages.MessageAction import edu.stanford.bdh.engagehf.messages.MessageItem -import edu.stanford.bdh.engagehf.messages.MessageType import edu.stanford.spezi.core.design.component.AsyncTextButton import edu.stanford.spezi.core.design.component.DefaultElevatedCard import edu.stanford.spezi.core.design.component.LifecycleEvent @@ -109,7 +110,7 @@ private fun HomeScreen( if (uiState.messages.isNotEmpty()) { items(uiState.messages) { message -> MessageItem( - message = message, + model = message, onAction = onAction, ) } @@ -332,14 +333,14 @@ private class HomeScreenPreviewProvider : PreviewParameterProvider { formattedWeight = "0.0" ), messages = listOf( - Message( - id = "1", - dueDate = ZonedDateTime.now(), - type = MessageType.WeightGain, - title = "Weight Gained", - description = "You gained weight. Please take action.", - action = "New Weight Entry" - ) + MessageUiModel( + Message( + id = "1", + dueDate = ZonedDateTime.now(), + title = "Weight Gained", + description = "You gained weight. Please take action.", + action = MessageAction.MeasurementsAction + ),), ), weight = VitalDisplayData( title = "Weight", diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/medication/ui/MedicationViewModel.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/medication/ui/MedicationViewModel.kt index 6de0963d0..e3e59600c 100644 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/medication/ui/MedicationViewModel.kt +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/medication/ui/MedicationViewModel.kt @@ -67,11 +67,11 @@ class MedicationViewModel @Inject internal constructor( is Action.InfoClicked -> { viewModelScope.launch { - messageActionMapper.mapVideoSectionAction(action.videoPath).let { result -> + messageActionMapper.mapVideoAction(action.videoPath).let { result -> result.onSuccess { mappedAction -> engageEducationRepository.getVideoBySectionAndVideoId( - mappedAction.videoSectionVideo.videoSectionId, - mappedAction.videoSectionVideo.videoId + mappedAction.video.sectionId, + mappedAction.video.videoId ).getOrNull()?.let { video -> navigator.navigateTo( EducationNavigationEvent.VideoSectionClicked( diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/FirestoreMessageMapper.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/FirestoreMessageMapper.kt index b97e7bcbe..d9f767375 100644 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/FirestoreMessageMapper.kt +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/FirestoreMessageMapper.kt @@ -2,6 +2,7 @@ package edu.stanford.bdh.engagehf.messages import com.google.firebase.Timestamp import com.google.firebase.firestore.DocumentSnapshot +import edu.stanford.bdh.engagehf.bluetooth.data.mapper.MessageActionMapper import edu.stanford.bdh.engagehf.localization.LocalizedMapReader import java.time.ZoneId import java.time.ZonedDateTime @@ -9,27 +10,25 @@ import javax.inject.Inject class FirestoreMessageMapper @Inject constructor( private val localizedMapReader: LocalizedMapReader, + private val messageActionMapper: MessageActionMapper, ) { fun map(document: DocumentSnapshot): Message? { val jsonMap = document.data ?: return null val dueDate = jsonMap["dueDate"] as? Timestamp val completionDate = jsonMap["completionDate"] as? Timestamp - val typeString = jsonMap["type"] as? String? val title = localizedMapReader.get(key = "title", jsonMap = jsonMap) ?: return null val description = localizedMapReader.get(key = "description", jsonMap = jsonMap) val action = jsonMap["action"] as? String? val isDismissible = (jsonMap["isDismissible"] as? Boolean) == true - val type = MessageType.fromString(typeString) return Message( id = document.id, dueDate = dueDate?.toZonedDateTime(), completionDate = completionDate?.toZonedDateTime(), - type = type, title = title, description = description, - action = action, + action = messageActionMapper.map(action).getOrNull() ?: MessageAction.UnknownAction, isDismissible = isDismissible, ) } diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/Message.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/Message.kt index 69a771082..beeafc5a1 100644 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/Message.kt +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/Message.kt @@ -1,64 +1,15 @@ package edu.stanford.bdh.engagehf.messages import com.google.firebase.firestore.IgnoreExtraProperties -import edu.stanford.spezi.core.design.R import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter @IgnoreExtraProperties data class Message( var id: String, val dueDate: ZonedDateTime? = null, val completionDate: ZonedDateTime? = null, - val type: MessageType, val title: String, val description: String? = null, - val action: String?, + val action: MessageAction, val isDismissible: Boolean = true, - val isExpanded: Boolean = false, -) { - - val dueDateFormattedString: String? - get() = dueDate?.format(DateTimeFormatter.ofPattern("MMM dd, yyyy hh:mm a")) - - val icon: Int = - when (type) { - MessageType.WeightGain -> R.drawable.ic_monitor_weight - MessageType.MedicationChange -> R.drawable.ic_medication - MessageType.MedicationUptitration -> R.drawable.ic_medication - MessageType.Welcome -> R.drawable.ic_assignment - MessageType.Vitals -> R.drawable.ic_vital_signs - MessageType.SymptomQuestionnaire -> R.drawable.ic_assignment - MessageType.PreVisit -> R.drawable.ic_groups - MessageType.Unknown -> R.drawable.ic_assignment - } -} - -enum class MessageType { - MedicationChange, - WeightGain, - MedicationUptitration, - Welcome, - Vitals, - SymptomQuestionnaire, - PreVisit, - Unknown, - ; - - companion object { - fun fromString(type: String?): MessageType { - return when (type) { - "MedicationChange" -> MedicationChange - "WeightGain" -> WeightGain - "MedicationUptitration" -> MedicationUptitration - "Welcome" -> Welcome - "Vitals" -> Vitals - "SymptomQuestionnaire" -> SymptomQuestionnaire - "PreVisit" -> PreVisit - else -> { - Unknown - } - } - } - } -} +) diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessageAction.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessageAction.kt new file mode 100644 index 000000000..e7c599cc8 --- /dev/null +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessageAction.kt @@ -0,0 +1,12 @@ +package edu.stanford.bdh.engagehf.messages + +sealed interface MessageAction { + data object UnknownAction : MessageAction + data class VideoAction(val video: Video) : MessageAction + data object MedicationsAction : MessageAction + data object MeasurementsAction : MessageAction + data class QuestionnaireAction(val questionnaireId: String) : MessageAction + data object HealthSummaryAction : MessageAction +} + +data class Video(val sectionId: String, val videoId: String) diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessageItem.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessageItem.kt index 099bd8a21..f198e1592 100644 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessageItem.kt +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessageItem.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import edu.stanford.bdh.engagehf.R import edu.stanford.bdh.engagehf.bluetooth.data.models.Action +import edu.stanford.bdh.engagehf.bluetooth.data.models.MessageUiModel import edu.stanford.spezi.core.design.component.DefaultElevatedCard import edu.stanford.spezi.core.design.theme.Colors import edu.stanford.spezi.core.design.theme.Colors.primary @@ -45,7 +46,7 @@ private const val TEXT_WEIGHT = 0.9f @Composable fun MessageItem( modifier: Modifier = Modifier, - message: Message, + model: MessageUiModel, onAction: (Action) -> Unit, ) { DefaultElevatedCard( @@ -63,21 +64,21 @@ fun MessageItem( modifier = Modifier.padding(top = Spacings.small), verticalAlignment = Alignment.CenterVertically, ) { - MessageIcon(message.icon) + MessageIcon(model.icon) Spacer(modifier = Modifier.width(Spacings.small)) Text( modifier = Modifier.testIdentifier(MessageItemTestIdentifiers.TITLE), - text = message.title, + text = model.message.title, style = TextStyles.titleMedium, color = Colors.onBackground, ) } - message.description?.let { + model.message.description?.let { Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, ) { - if (message.isExpanded) { + if (model.isExpanded) { Text( modifier = Modifier .weight(TEXT_WEIGHT) @@ -99,11 +100,11 @@ fun MessageItem( IconButton( modifier = Modifier.width(Sizes.Icon.small), onClick = { - onAction(Action.ToggleExpand(message)) + onAction(Action.ToggleExpand(model)) }) { Icon( - imageVector = if (message.isExpanded) Icons.Filled.KeyboardArrowUp else Icons.Filled.KeyboardArrowDown, - contentDescription = if (message.isExpanded) "Show less" else "Show more", + imageVector = if (model.isExpanded) Icons.Filled.KeyboardArrowUp else Icons.Filled.KeyboardArrowDown, + contentDescription = if (model.isExpanded) "Show less" else "Show more", ) } } @@ -116,7 +117,7 @@ fun MessageItem( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start, ) { - message.dueDateFormattedString?.let { + model.dueDateFormattedString?.let { Text( modifier = Modifier.testIdentifier(MessageItemTestIdentifiers.DUE_DATE), text = "Due Date: $it", @@ -124,12 +125,12 @@ fun MessageItem( ) } Spacer(modifier = Modifier.weight(1f)) - if (message.action != null || message.isDismissible) { + if (model.message.action != null || model.message.isDismissible) { Button( modifier = Modifier.testIdentifier(MessageItemTestIdentifiers.ACTION_BUTTON), colors = ButtonDefaults.buttonColors(containerColor = primary), onClick = { - onAction(Action.MessageItemClicked(message)) + onAction(Action.MessageItemClicked(model)) }, ) { Text( @@ -168,45 +169,44 @@ enum class MessageItemTestIdentifiers { fun MessageListPreview() { SpeziTheme(isPreview = true) { LazyColumn(modifier = Modifier.padding(Spacings.medium)) { - items(sampleMessages) { message -> - MessageItem(message = message, onAction = { }) + items(sampleMessageModels) { model -> + MessageItem(model = model, onAction = { }) } } } } -private val sampleMessages = listOf( +private val sampleMessageModels = listOf( Message( id = java.util.UUID.randomUUID().toString(), dueDate = ZonedDateTime.now().plusDays(1), completionDate = null, - type = MessageType.WeightGain, title = "Weight Gained", description = "You gained weight. Please take action.", - action = "New Weight Entry", + action = MessageAction.MeasurementsAction, ), Message( id = java.util.UUID.randomUUID().toString(), dueDate = ZonedDateTime.now().plusDays(2), completionDate = null, - type = MessageType.MedicationChange, title = "Medication Change", description = "Your medication has been changed. Please take action. " + "Your medication has been changed. Please take action. Your medication " + "has been changed. " + "Please take action.", - action = "Go to medication", + action = MessageAction.MedicationsAction, ), Message( id = java.util.UUID.randomUUID().toString(), dueDate = ZonedDateTime.now().plusDays(2), completionDate = null, - type = MessageType.Unknown, title = "Medication Change", description = "Your medication has been changed. Please take action. " + "Your medication has been changed. Please take action. Your medication " + "has been changed. " + "Please take action.", - action = "Go to medication", + action = MessageAction.MedicationsAction, ), -) +).map { + MessageUiModel(it) +} diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessageRepository.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessageRepository.kt index 7e2578a6d..3267ba1b0 100644 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessageRepository.kt +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessageRepository.kt @@ -55,7 +55,7 @@ class MessageRepository @Inject constructor( } } - fun completeMessage(messageId: String) { + fun dismissMessage(messageId: String) { ioScope.launch { runCatching { val uid = userSessionManager.getUserUid() diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessagesAction.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessagesAction.kt deleted file mode 100644 index 54ab272d6..000000000 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessagesAction.kt +++ /dev/null @@ -1,12 +0,0 @@ -package edu.stanford.bdh.engagehf.messages - -sealed class MessagesAction { - data object NoAction : MessagesAction() - data class VideoSectionAction(val videoSectionVideo: VideoSectionVideo) : MessagesAction() - data object MedicationsAction : MessagesAction() - data object MeasurementsAction : MessagesAction() - data class QuestionnaireAction(val questionnaireId: String) : MessagesAction() - data object HealthSummaryAction : MessagesAction() -} - -data class VideoSectionVideo(val videoSectionId: String, val videoId: String) diff --git a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessagesHandler.kt b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessagesHandler.kt index 8287150af..6350feefe 100644 --- a/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessagesHandler.kt +++ b/app/src/main/kotlin/edu/stanford/bdh/engagehf/messages/MessagesHandler.kt @@ -2,7 +2,6 @@ package edu.stanford.bdh.engagehf.messages import edu.stanford.bdh.engagehf.R import edu.stanford.bdh.engagehf.bluetooth.component.AppScreenEvents -import edu.stanford.bdh.engagehf.bluetooth.data.mapper.MessageActionMapper import edu.stanford.bdh.engagehf.education.EngageEducationRepository import edu.stanford.bdh.engagehf.navigation.AppNavigationEvent import edu.stanford.bdh.engagehf.navigation.screens.BottomBarItem @@ -14,7 +13,6 @@ import javax.inject.Inject @Suppress("LongParameterList") class MessagesHandler @Inject constructor( - private val messagesActionMapper: MessageActionMapper, private val appScreenEvents: AppScreenEvents, private val healthSummaryService: HealthSummaryService, private val engageEducationRepository: EngageEducationRepository, @@ -27,42 +25,10 @@ class MessagesHandler @Inject constructor( fun observeUserMessages() = messageRepository.observeUserMessages() suspend fun handle(message: Message) { - val actionResult = messagesActionMapper.map(action = message.action) - var failure = actionResult.exceptionOrNull() - when (val messagesAction = actionResult.getOrNull()) { - is MessagesAction.NoAction -> Unit - is MessagesAction.HealthSummaryAction -> { - failure = healthSummaryService.generateHealthSummaryPdf().exceptionOrNull() - } - - is MessagesAction.VideoSectionAction -> { - val sectionVideo = messagesAction.videoSectionVideo - failure = engageEducationRepository.getVideoBySectionAndVideoId( - sectionId = sectionVideo.videoSectionId, - videoId = sectionVideo.videoId, - ).onSuccess { video -> - navigator.navigateTo(EducationNavigationEvent.VideoSectionClicked(video)) - }.exceptionOrNull() - } - - is MessagesAction.MeasurementsAction -> { - appScreenEvents.emit(AppScreenEvents.Event.DoNewMeasurement) - } - - is MessagesAction.MedicationsAction -> { - appScreenEvents.emit(AppScreenEvents.Event.NavigateToTab(BottomBarItem.MEDICATION)) - } - - is MessagesAction.QuestionnaireAction -> { - navigator.navigateTo( - AppNavigationEvent.QuestionnaireScreen(messagesAction.questionnaireId) - ) - } - else -> Unit - } + val failure = handle(message.action).exceptionOrNull() val messageId = message.id if (failure == null && message.isDismissible) { - messageRepository.completeMessage(messageId = messageId) + messageRepository.dismissMessage(messageId = messageId) } else if (failure != null) { logger.e(failure) { "Error while handling message: $messageId" } messageNotifier.notify(messageId = R.string.error_while_handling_message_action) @@ -70,4 +36,40 @@ class MessagesHandler @Inject constructor( logger.i { "Message $messageId handled successfully" } } } + + suspend fun handle(action: MessageAction): Result { + return runCatching { + when (action) { + is MessageAction.UnknownAction -> Unit + + is MessageAction.HealthSummaryAction -> { + healthSummaryService.generateHealthSummaryPdf() + } + + is MessageAction.VideoAction -> { + val sectionVideo = action.video + engageEducationRepository.getVideoBySectionAndVideoId( + sectionId = sectionVideo.sectionId, + videoId = sectionVideo.videoId, + ).onSuccess { video -> + navigator.navigateTo(EducationNavigationEvent.VideoSectionClicked(video)) + } + } + + is MessageAction.MeasurementsAction -> { + appScreenEvents.emit(AppScreenEvents.Event.DoNewMeasurement) + } + + is MessageAction.MedicationsAction -> { + appScreenEvents.emit(AppScreenEvents.Event.NavigateToTab(BottomBarItem.MEDICATION)) + } + + is MessageAction.QuestionnaireAction -> { + navigator.navigateTo( + AppNavigationEvent.QuestionnaireScreen(action.questionnaireId) + ) + } + } + } + } } diff --git a/app/src/test/kotlin/edu/stanford/bdh/engagehf/MainActivityViewModelTest.kt b/app/src/test/kotlin/edu/stanford/bdh/engagehf/MainActivityViewModelTest.kt index 2bf3083d6..e982ad5b7 100644 --- a/app/src/test/kotlin/edu/stanford/bdh/engagehf/MainActivityViewModelTest.kt +++ b/app/src/test/kotlin/edu/stanford/bdh/engagehf/MainActivityViewModelTest.kt @@ -2,8 +2,8 @@ package edu.stanford.bdh.engagehf import android.content.Intent import com.google.common.truth.Truth.assertThat -import edu.stanford.bdh.engagehf.messages.Message -import edu.stanford.bdh.engagehf.messages.MessageType +import edu.stanford.bdh.engagehf.bluetooth.data.mapper.MessageActionMapper +import edu.stanford.bdh.engagehf.messages.MessageAction import edu.stanford.bdh.engagehf.messages.MessagesHandler import edu.stanford.bdh.engagehf.navigation.AppNavigationEvent import edu.stanford.bdh.engagehf.navigation.Routes @@ -40,6 +40,7 @@ class MainActivityViewModelTest { private val navigator: Navigator = mockk(relaxed = true) private val userSessionManager: UserSessionManager = mockk() private val messagesHandler: MessagesHandler = mockk(relaxed = true) + private val messageActionMapper: MessageActionMapper = mockk(relaxed = true) private val messageNotifier: MessageNotifier = mockk(relaxed = true) private lateinit var viewModel: MainActivityViewModel @@ -119,20 +120,17 @@ class MainActivityViewModelTest { val someMessageId = "some-message-id" val firebaseMessage: FirebaseMessage = mockk { every { messageId } returns someMessageId - every { action } returns "action" + every { action } returns "medications" every { isDismissible } returns true } - val expectedMessage = Message( - id = someMessageId, - type = MessageType.Unknown, - title = "", - action = firebaseMessage.action, - isDismissible = firebaseMessage.isDismissible == true - ) + val expectedAction = MessageAction.MedicationsAction val intent = mockk() every { intent.getParcelableExtra(FirebaseMessage.FIREBASE_MESSAGE_KEY) } returns firebaseMessage + every { + messageActionMapper.map("medications") + } returns Result.success(expectedAction) createViewModel() // when @@ -140,7 +138,7 @@ class MainActivityViewModelTest { // then coVerify { - messagesHandler.handle(message = expectedMessage) + messagesHandler.handle(action = expectedAction) } } @@ -310,6 +308,7 @@ class MainActivityViewModelTest { navigator = navigator, userSessionManager = userSessionManager, messagesHandler = messagesHandler, + messageActionMapper = messageActionMapper, messageNotifier = messageNotifier, ) } diff --git a/app/src/test/kotlin/edu/stanford/bdh/engagehf/bluetooth/HomeViewModelTest.kt b/app/src/test/kotlin/edu/stanford/bdh/engagehf/bluetooth/HomeViewModelTest.kt index 9dfb32022..27ff03b33 100644 --- a/app/src/test/kotlin/edu/stanford/bdh/engagehf/bluetooth/HomeViewModelTest.kt +++ b/app/src/test/kotlin/edu/stanford/bdh/engagehf/bluetooth/HomeViewModelTest.kt @@ -10,6 +10,7 @@ import edu.stanford.bdh.engagehf.bluetooth.data.mapper.BluetoothUiStateMapper import edu.stanford.bdh.engagehf.bluetooth.data.models.Action import edu.stanford.bdh.engagehf.bluetooth.data.models.BluetoothUiState import edu.stanford.bdh.engagehf.bluetooth.data.models.MeasurementDialogUiState +import edu.stanford.bdh.engagehf.bluetooth.data.models.MessageUiModel import edu.stanford.bdh.engagehf.bluetooth.data.models.VitalDisplayData import edu.stanford.bdh.engagehf.bluetooth.measurements.MeasurementsRepository import edu.stanford.bdh.engagehf.bluetooth.service.EngageBLEService @@ -17,7 +18,7 @@ import edu.stanford.bdh.engagehf.bluetooth.service.EngageBLEServiceEvent import edu.stanford.bdh.engagehf.bluetooth.service.EngageBLEServiceState import edu.stanford.bdh.engagehf.bluetooth.service.Measurement import edu.stanford.bdh.engagehf.messages.Message -import edu.stanford.bdh.engagehf.messages.MessageType +import edu.stanford.bdh.engagehf.messages.MessageAction import edu.stanford.bdh.engagehf.messages.MessagesHandler import edu.stanford.bdh.engagehf.navigation.screens.BottomBarItem import edu.stanford.spezi.core.notification.NotificationPermissions @@ -54,7 +55,6 @@ class HomeViewModelTest { private val messageId = "some-id" private val message: Message = mockk { every { id } returns messageId - every { isExpanded } returns false } @get:Rule @@ -286,7 +286,7 @@ class HomeViewModelTest { fun `it should invoke messages handler on message item clicked`() { // given val message: Message = mockk() - val action = Action.MessageItemClicked(message = message) + val action = Action.MessageItemClicked(message = MessageUiModel(message)) createViewModel() // when @@ -318,9 +318,11 @@ class HomeViewModelTest { dueDate = ZonedDateTime.now(), description = "", title = "", - action = "", - type = MessageType.MedicationChange, - isExpanded = isExpanded, + action = MessageAction.UnknownAction, + ) + val model = MessageUiModel( + message = message, + isExpanded = isExpanded ) every { this@HomeViewModelTest.message.id } returns "new-id" @@ -333,7 +335,7 @@ class HomeViewModelTest { createViewModel() // when - viewModel.onAction(Action.ToggleExpand(message)) + viewModel.onAction(Action.ToggleExpand(model)) // then assertThat( diff --git a/app/src/test/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/mapper/MessageActionMapperTest.kt b/app/src/test/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/mapper/MessageActionMapperTest.kt index b25272c47..bf13c3f21 100644 --- a/app/src/test/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/mapper/MessageActionMapperTest.kt +++ b/app/src/test/kotlin/edu/stanford/bdh/engagehf/bluetooth/data/mapper/MessageActionMapperTest.kt @@ -1,7 +1,7 @@ package edu.stanford.bdh.engagehf.bluetooth.data.mapper import com.google.common.truth.Truth.assertThat -import edu.stanford.bdh.engagehf.messages.MessagesAction +import edu.stanford.bdh.engagehf.messages.MessageAction import org.junit.Test class MessageActionMapperTest { @@ -32,10 +32,10 @@ class MessageActionMapperTest { val result = mapper.map(action) // then - val messagesAction = result.getOrThrow() as MessagesAction.VideoSectionAction + val messagesAction = result.getOrThrow() as MessageAction.VideoAction with(messagesAction) { - assertThat(videoSectionVideo.videoSectionId).isEqualTo(sectionId) - assertThat(videoSectionVideo.videoId).isEqualTo(videoId) + assertThat(video.sectionId).isEqualTo(sectionId) + assertThat(video.videoId).isEqualTo(videoId) } } @@ -50,10 +50,10 @@ class MessageActionMapperTest { val result = mapper.map(action) // then - val messagesAction = result.getOrThrow() as MessagesAction.VideoSectionAction + val messagesAction = result.getOrThrow() as MessageAction.VideoAction with(messagesAction) { - assertThat(videoSectionVideo.videoSectionId).isEqualTo(sectionId) - assertThat(videoSectionVideo.videoId).isEqualTo(videoId) + assertThat(video.sectionId).isEqualTo(sectionId) + assertThat(video.videoId).isEqualTo(videoId) } } @@ -67,7 +67,7 @@ class MessageActionMapperTest { // then assertThat(result.isSuccess).isTrue() - assertThat(result.getOrThrow()).isEqualTo(MessagesAction.MedicationsAction) + assertThat(result.getOrThrow()).isEqualTo(MessageAction.MedicationsAction) } @Test @@ -80,7 +80,7 @@ class MessageActionMapperTest { // then assertThat(result.isSuccess).isTrue() - assertThat(result.getOrThrow()).isEqualTo(MessagesAction.MeasurementsAction) + assertThat(result.getOrThrow()).isEqualTo(MessageAction.MeasurementsAction) } @Test @@ -93,7 +93,7 @@ class MessageActionMapperTest { val result = mapper.map(action) // then - val messagesAction = result.getOrThrow() as MessagesAction.QuestionnaireAction + val messagesAction = result.getOrThrow() as MessageAction.QuestionnaireAction assertThat(messagesAction.questionnaireId).isEqualTo(questionnaireId) } @@ -107,7 +107,7 @@ class MessageActionMapperTest { // then assertThat(result.isSuccess).isTrue() - assertThat(result.getOrThrow()).isEqualTo(MessagesAction.HealthSummaryAction) + assertThat(result.getOrThrow()).isEqualTo(MessageAction.HealthSummaryAction) } @Test diff --git a/app/src/test/kotlin/edu/stanford/bdh/engagehf/medication/ui/MedicationViewModelTest.kt b/app/src/test/kotlin/edu/stanford/bdh/engagehf/medication/ui/MedicationViewModelTest.kt index 996f3af69..a28c91462 100644 --- a/app/src/test/kotlin/edu/stanford/bdh/engagehf/medication/ui/MedicationViewModelTest.kt +++ b/app/src/test/kotlin/edu/stanford/bdh/engagehf/medication/ui/MedicationViewModelTest.kt @@ -6,7 +6,7 @@ import edu.stanford.bdh.engagehf.education.EngageEducationRepository import edu.stanford.bdh.engagehf.medication.data.MedicationRecommendation import edu.stanford.bdh.engagehf.medication.data.MedicationRecommendationType import edu.stanford.bdh.engagehf.medication.data.MedicationRepository -import edu.stanford.bdh.engagehf.messages.MessagesAction +import edu.stanford.bdh.engagehf.messages.MessageAction import edu.stanford.spezi.core.navigation.Navigator import edu.stanford.spezi.core.testing.CoroutineTestRule import edu.stanford.spezi.core.testing.runTestUnconfined @@ -144,13 +144,13 @@ class MedicationViewModelTest { val videoPath = "/videoSections/1/videos/1" val videoSectionId = "1" val videoId = "1" - val video = mockk