diff --git a/androidApp/src/main/java/org/mifos/mobile/navigation/MifosNavGraph.kt b/androidApp/src/main/java/org/mifos/mobile/navigation/MifosNavGraph.kt
index f6761ce22..127e1cbb5 100644
--- a/androidApp/src/main/java/org/mifos/mobile/navigation/MifosNavGraph.kt
+++ b/androidApp/src/main/java/org/mifos/mobile/navigation/MifosNavGraph.kt
@@ -132,7 +132,7 @@ fun RootNavGraph(
)
notificationNavGraph(
- navController = navController
+ navigateBack = navController::popBackStack
)
locationsNavGraph(
diff --git a/feature/notification/build.gradle.kts b/feature/notification/build.gradle.kts
index 72e05e333..56422544d 100644
--- a/feature/notification/build.gradle.kts
+++ b/feature/notification/build.gradle.kts
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
plugins {
alias(libs.plugins.mifos.android.feature)
alias(libs.plugins.mifos.android.library.compose)
@@ -8,16 +17,6 @@ android {
}
dependencies {
- implementation(projects.core.ui)
- implementation(projects.core.common)
- implementation(projects.core.model)
- implementation(projects.core.data)
implementation(projects.core.datastore)
-
- // DBFlow
implementation(libs.dbflow)
-
- testImplementation(libs.junit)
- androidTestImplementation(libs.androidx.test.ext.junit)
- androidTestImplementation(libs.espresso.core)
}
\ No newline at end of file
diff --git a/feature/notification/src/androidTest/java/org/mifos/mobile/feature/notification/ExampleInstrumentedTest.kt b/feature/notification/src/androidTest/java/org/mifos/mobile/feature/notification/ExampleInstrumentedTest.kt
deleted file mode 100644
index 35fb3c53b..000000000
--- a/feature/notification/src/androidTest/java/org/mifos/mobile/feature/notification/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.mifos.mobile.feature.notification
-
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.ext.junit.runners.AndroidJUnit4
-
-import org.junit.Test
-import org.junit.runner.RunWith
-
-import org.junit.Assert.*
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-@RunWith(AndroidJUnit4::class)
-class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("org.mifos.mobile.feature.notification.test", appContext.packageName)
- }
-}
\ No newline at end of file
diff --git a/feature/notification/src/main/AndroidManifest.xml b/feature/notification/src/main/AndroidManifest.xml
index a5918e68a..1f9b243f0 100644
--- a/feature/notification/src/main/AndroidManifest.xml
+++ b/feature/notification/src/main/AndroidManifest.xml
@@ -1,4 +1,13 @@
+
\ No newline at end of file
diff --git a/feature/notification/src/main/java/org/mifos/mobile/feature/notification/NotificationScreen.kt b/feature/notification/src/main/java/org/mifos/mobile/feature/notification/NotificationScreen.kt
index 354f15f41..ab39a3cb8 100644
--- a/feature/notification/src/main/java/org/mifos/mobile/feature/notification/NotificationScreen.kt
+++ b/feature/notification/src/main/java/org/mifos/mobile/feature/notification/NotificationScreen.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.notification
import androidx.compose.foundation.layout.Box
@@ -15,7 +24,6 @@ import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
import androidx.compose.material3.pulltorefresh.PullToRefreshContainer
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.runtime.Composable
@@ -30,7 +38,6 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
@@ -40,15 +47,18 @@ import org.mifos.mobile.core.common.Network
import org.mifos.mobile.core.common.utils.DateHelper
import org.mifos.mobile.core.datastore.model.MifosNotification
import org.mifos.mobile.core.designsystem.components.MifosScaffold
+import org.mifos.mobile.core.designsystem.components.MifosTextButton
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
import org.mifos.mobile.core.ui.component.EmptyDataView
import org.mifos.mobile.core.ui.component.MifosErrorComponent
import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay
+import org.mifos.mobile.core.ui.utils.DevicePreviews
@Composable
-fun NotificationScreen(
- viewModel: NotificationViewModel = hiltViewModel(),
+internal fun NotificationScreen(
navigateBack: () -> Unit,
+ modifier: Modifier = Modifier,
+ viewModel: NotificationViewModel = hiltViewModel(),
) {
val uiState by viewModel.notificationUiState.collectAsStateWithLifecycle()
val isRefreshing by viewModel.isRefreshing.collectAsStateWithLifecycle()
@@ -56,39 +66,41 @@ fun NotificationScreen(
NotificationScreen(
uiState = uiState,
navigateBack = navigateBack,
- onRetry = { viewModel.loadNotifications() },
- dismissNotification = { viewModel.dismissNotification(it) },
- onRefresh = { viewModel.refreshNotifications() },
- isRefreshing = isRefreshing
+ onRetry = viewModel::loadNotifications,
+ dismissNotification = viewModel::dismissNotification,
+ onRefresh = viewModel::refreshNotifications,
+ isRefreshing = isRefreshing,
+ modifier = modifier,
)
}
@Composable
-fun NotificationScreen(
+private fun NotificationScreen(
uiState: NotificationUiState,
navigateBack: () -> Unit,
onRetry: () -> Unit,
dismissNotification: (MifosNotification) -> Unit,
isRefreshing: Boolean,
- onRefresh: () -> Unit
+ onRefresh: () -> Unit,
+ modifier: Modifier = Modifier,
) {
val context = LocalContext.current
+
MifosScaffold(
topBarTitleResId = R.string.notification,
navigateBack = navigateBack,
+ modifier = modifier,
content = {
Box(modifier = Modifier.padding(it)) {
when (uiState) {
- is NotificationUiState.Loading -> {
- MifosProgressIndicatorOverlay()
- }
+ is NotificationUiState.Loading -> MifosProgressIndicatorOverlay()
is NotificationUiState.Error -> {
MifosErrorComponent(
message = uiState.errorMessage,
isNetworkConnected = Network.isConnected(context),
isRetryEnabled = true,
- onRetry = onRetry
+ onRetry = onRetry,
)
}
@@ -97,30 +109,31 @@ fun NotificationScreen(
EmptyDataView(
icon = R.drawable.ic_notifications,
error = R.string.no_notification,
- modifier = Modifier.fillMaxSize()
+ modifier = Modifier.fillMaxSize(),
)
} else {
NotificationContent(
+ isRefreshing = isRefreshing,
notifications = uiState.notifications,
dismissNotification = dismissNotification,
onRefresh = onRefresh,
- isRefreshing = isRefreshing
)
}
}
}
}
- }
+ },
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun NotificationContent(
+private fun NotificationContent(
+ isRefreshing: Boolean,
notifications: List,
dismissNotification: (MifosNotification) -> Unit,
- isRefreshing: Boolean,
- onRefresh: () -> Unit
+ onRefresh: () -> Unit,
+ modifier: Modifier = Modifier,
) {
val pullRefreshState = rememberPullToRefreshState()
@@ -131,20 +144,25 @@ fun NotificationContent(
}
LaunchedEffect(key1 = isRefreshing) {
- if (isRefreshing)
+ if (isRefreshing) {
pullRefreshState.startRefresh()
- else
+ } else {
pullRefreshState.endRefresh()
+ }
}
- Box(modifier = Modifier.fillMaxSize().nestedScroll(pullRefreshState.nestedScrollConnection)) {
+ Box(
+ modifier = modifier
+ .fillMaxSize()
+ .nestedScroll(pullRefreshState.nestedScrollConnection),
+ ) {
LazyColumn(modifier = Modifier.fillMaxSize()) {
itemsIndexed(items = notifications) { index, notification ->
NotificationItem(
notification = notification,
- dismissNotification = dismissNotification
+ dismissNotification = dismissNotification,
)
- if(index < notifications.lastIndex) {
+ if (index < notifications.lastIndex) {
HorizontalDivider(color = MaterialTheme.colorScheme.surfaceDim)
}
}
@@ -157,63 +175,72 @@ fun NotificationContent(
}
@Composable
-fun NotificationItem(
+private fun NotificationItem(
notification: MifosNotification,
- dismissNotification: (MifosNotification) -> Unit
+ dismissNotification: (MifosNotification) -> Unit,
+ modifier: Modifier = Modifier,
) {
- var isRead = rememberSaveable { mutableStateOf(notification.isRead()) }
+ val isRead = rememberSaveable { mutableStateOf(notification.isRead()) }
+
Row(
verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier.padding(8.dp)
+ modifier = modifier.padding(8.dp),
) {
Icon(
painter = painterResource(id = R.drawable.ic_notifications),
contentDescription = null,
- tint = if(isRead.value) MaterialTheme.colorScheme.onSurfaceVariant
- else MaterialTheme.colorScheme.primary
+ tint = if (isRead.value) {
+ MaterialTheme.colorScheme.onSurfaceVariant
+ } else {
+ MaterialTheme.colorScheme.primary
+ },
)
Spacer(modifier = Modifier.width(8.dp))
Column(
- horizontalAlignment = Alignment.End
+ horizontalAlignment = Alignment.End,
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = notification.msg ?: "",
- style = MaterialTheme.typography.bodyMedium
+ style = MaterialTheme.typography.bodyMedium,
)
Text(
modifier = Modifier.alpha(0.7f),
text = DateHelper.getDateAndTimeAsStringFromLong(notification.timeStamp),
style = MaterialTheme.typography.labelMedium,
)
- if(!isRead.value) {
- TextButton(
+ if (!isRead.value) {
+ MifosTextButton(
+ text = stringResource(id = R.string.dialog_action_ok),
onClick = {
isRead.value = true
dismissNotification(notification)
- }
- ) {
- Text(text = stringResource(id = R.string.dialog_action_ok))
- }
+ },
+ )
}
}
}
}
-class NotificationUiStatePreviews : PreviewParameterProvider {
+internal class NotificationUiStatePreviews : PreviewParameterProvider {
override val values: Sequence
get() = sequenceOf(
- NotificationUiState.Success(notifications = listOf(MifosNotification(), MifosNotification())),
+ NotificationUiState.Success(
+ notifications = listOf(
+ MifosNotification(),
+ MifosNotification(),
+ ),
+ ),
NotificationUiState.Error(errorMessage = ""),
NotificationUiState.Loading,
)
}
-
-@Preview(showSystemUi = true)
+@DevicePreviews
@Composable
-fun NotificationScreenPreview(
- @PreviewParameter(NotificationUiStatePreviews::class) notificationUiState: NotificationUiState
+private fun NotificationScreenPreview(
+ @PreviewParameter(NotificationUiStatePreviews::class)
+ notificationUiState: NotificationUiState,
) {
MifosMobileTheme {
NotificationScreen(
@@ -222,7 +249,7 @@ fun NotificationScreenPreview(
onRetry = {},
dismissNotification = {},
onRefresh = {},
- isRefreshing = false
+ isRefreshing = false,
)
}
-}
\ No newline at end of file
+}
diff --git a/feature/notification/src/main/java/org/mifos/mobile/feature/notification/NotificationViewModel.kt b/feature/notification/src/main/java/org/mifos/mobile/feature/notification/NotificationViewModel.kt
index 039e1c065..a639b6f3e 100644
--- a/feature/notification/src/main/java/org/mifos/mobile/feature/notification/NotificationViewModel.kt
+++ b/feature/notification/src/main/java/org/mifos/mobile/feature/notification/NotificationViewModel.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.notification
import androidx.lifecycle.ViewModel
@@ -9,16 +18,18 @@ import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.launch
import org.mifos.mobile.core.data.repository.NotificationRepository
import org.mifos.mobile.core.datastore.model.MifosNotification
+import org.mifos.mobile.feature.notification.NotificationUiState.Loading
import javax.inject.Inject
@HiltViewModel
-class NotificationViewModel @Inject constructor(private val notificationRepositoryImp: NotificationRepository) :
- ViewModel() {
+internal class NotificationViewModel @Inject constructor(
+ private val notificationRepositoryImp: NotificationRepository,
+) : ViewModel() {
- private val _notificationUiState = MutableStateFlow(NotificationUiState.Loading)
+ private val _notificationUiState = MutableStateFlow(Loading)
val notificationUiState: StateFlow get() = _notificationUiState
- private val _isRefreshing = MutableStateFlow(false)
+ private val _isRefreshing = MutableStateFlow(false)
val isRefreshing: StateFlow get() = _isRefreshing
init {
@@ -26,14 +37,16 @@ class NotificationViewModel @Inject constructor(private val notificationReposito
}
fun loadNotifications() {
- _notificationUiState.value = NotificationUiState.Loading
+ _notificationUiState.value = Loading
viewModelScope.launch {
notificationRepositoryImp.loadNotifications()
.catch {
- _notificationUiState.value = NotificationUiState.Error(errorMessage = it.message)
+ _notificationUiState.value =
+ NotificationUiState.Error(errorMessage = it.message)
}.collect { notifications ->
_isRefreshing.emit(false)
- _notificationUiState.value = NotificationUiState.Success(notifications = notifications)
+ _notificationUiState.value =
+ NotificationUiState.Success(notifications = notifications)
}
}
}
@@ -49,9 +62,8 @@ class NotificationViewModel @Inject constructor(private val notificationReposito
}
}
-
-sealed class NotificationUiState {
+internal sealed class NotificationUiState {
data object Loading : NotificationUiState()
data class Success(val notifications: List) : NotificationUiState()
data class Error(val errorMessage: String?) : NotificationUiState()
-}
\ No newline at end of file
+}
diff --git a/feature/notification/src/main/java/org/mifos/mobile/feature/notification/navigation/NotificationNavGraph.kt b/feature/notification/src/main/java/org/mifos/mobile/feature/notification/navigation/NotificationNavGraph.kt
index 038105d33..31eaed005 100644
--- a/feature/notification/src/main/java/org/mifos/mobile/feature/notification/navigation/NotificationNavGraph.kt
+++ b/feature/notification/src/main/java/org/mifos/mobile/feature/notification/navigation/NotificationNavGraph.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.notification.navigation
import androidx.navigation.NavController
@@ -11,14 +20,14 @@ fun NavController.navigateToNotificationScreen() {
}
fun NavGraphBuilder.notificationNavGraph(
- navController: NavController
+ navigateBack: () -> Unit,
) {
navigation(
startDestination = NotificationNavigation.NotificationScreen.route,
route = NotificationNavigation.NotificationBase.route,
) {
notificationScreenRoute(
- navigateBack = navController::popBackStack,
+ navigateBack = navigateBack,
)
}
}
@@ -33,4 +42,4 @@ fun NavGraphBuilder.notificationScreenRoute(
navigateBack = navigateBack,
)
}
-}
\ No newline at end of file
+}
diff --git a/feature/notification/src/main/java/org/mifos/mobile/feature/notification/navigation/NotificationNavigation.kt b/feature/notification/src/main/java/org/mifos/mobile/feature/notification/navigation/NotificationNavigation.kt
index c6070917b..d37ba440d 100644
--- a/feature/notification/src/main/java/org/mifos/mobile/feature/notification/navigation/NotificationNavigation.kt
+++ b/feature/notification/src/main/java/org/mifos/mobile/feature/notification/navigation/NotificationNavigation.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.notification.navigation
const val NOTIFICATION_NAVIGATION_ROUTE_BASE = "notification_base_route"
diff --git a/feature/notification/src/main/res/drawable/ic_notifications.xml b/feature/notification/src/main/res/drawable/ic_notifications.xml
index 94190607d..5f3a73aef 100644
--- a/feature/notification/src/main/res/drawable/ic_notifications.xml
+++ b/feature/notification/src/main/res/drawable/ic_notifications.xml
@@ -1,3 +1,13 @@
+
+
+
Notification
OK
diff --git a/feature/notification/src/test/java/org/mifos/mobile/feature/notification/ExampleUnitTest.kt b/feature/notification/src/test/java/org/mifos/mobile/feature/notification/ExampleUnitTest.kt
deleted file mode 100644
index cf9b3e8f3..000000000
--- a/feature/notification/src/test/java/org/mifos/mobile/feature/notification/ExampleUnitTest.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.mifos.mobile.feature.notification
-
-import org.junit.Test
-
-import org.junit.Assert.*
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
\ No newline at end of file