Skip to content

Commit

Permalink
chore: Add dropdown menu on NotificationsInboxFragment
Browse files Browse the repository at this point in the history
- Added ability to mark all the notifications as read at once
- User can now navigate to notification preferences screen from inbox

fix: LEARNER-10341
  • Loading branch information
omerhabib26 committed Jan 10, 2025
1 parent 2613dd0 commit 8561166
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 19 deletions.
3 changes: 2 additions & 1 deletion app/src/main/java/org/openedx/app/AppRouter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import org.openedx.discussion.presentation.responses.DiscussionResponsesFragment
import org.openedx.discussion.presentation.search.DiscussionSearchThreadFragment
import org.openedx.discussion.presentation.threads.DiscussionAddThreadFragment
import org.openedx.discussion.presentation.threads.DiscussionThreadsFragment
import org.openedx.notifications.presentation.NotificationsRouter
import org.openedx.notifications.presentation.inbox.NotificationsInboxFragment
import org.openedx.notifications.presentation.settings.NotificationsSettingsFragment
import org.openedx.profile.domain.model.Account
Expand All @@ -58,7 +59,7 @@ import org.openedx.whatsnew.WhatsNewRouter
import org.openedx.whatsnew.presentation.whatsnew.WhatsNewFragment

class AppRouter : AuthRouter, DiscoveryRouter, DashboardRouter, CourseRouter, DiscussionRouter,
ProfileRouter, AppUpgradeRouter, WhatsNewRouter {
ProfileRouter, AppUpgradeRouter, WhatsNewRouter, NotificationsRouter {

//region AuthRouter
override fun navigateToMain(
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/org/openedx/app/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ import org.openedx.discussion.system.notifier.DiscussionNotifier
import org.openedx.notifications.PushManager
import org.openedx.notifications.data.storage.NotificationsPreferences
import org.openedx.notifications.presentation.NotificationsAnalytics
import org.openedx.notifications.presentation.NotificationsRouter
import org.openedx.profile.data.storage.ProfilePreferences
import org.openedx.profile.presentation.ProfileAnalytics
import org.openedx.profile.presentation.ProfileRouter
Expand Down Expand Up @@ -132,6 +133,7 @@ val appModule = module {
single<WhatsNewRouter> { get<AppRouter>() }
single<AppUpgradeRouter> { get<AppRouter>() }
single { DeepLinkRouter(get(), get(), get(), get(), get(), get()) }
single<NotificationsRouter> { get<AppRouter>() }

single { NetworkConnection(get()) }

Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/org/openedx/app/di/ScreenModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ val screenModule = module {
single { NotificationsRepository(get()) }
factory { NotificationsInteractor(get()) }

viewModel { NotificationsInboxViewModel(get(), get()) }
viewModel { NotificationsInboxViewModel(get(), get(), get()) }
viewModel { NotificationsSettingsViewModel(get(), get(), get()) }

single { IAPRepository(get()) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package org.openedx.notifications.data.model
import com.google.gson.annotations.SerializedName

data class MarkNotificationReadBody(
@SerializedName("app_name")
val appName: String,
@SerializedName("notification_id")
val notificationId: Int,
val notificationId: Int?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.openedx.notifications.data.model

import androidx.annotation.StringRes
import org.openedx.notifications.R

enum class NotificationsMenuType(@StringRes val title: Int) {
MARK_ALL_READ(R.string.notifications_menu_mark_all_read),
NOTIFICATION_SETTINGS(R.string.notifications_menu_settings),
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ class NotificationsRepository(
).message.isNotNull()
}

suspend fun markNotificationAsRead(notificationId: Int): Boolean {
suspend fun markNotificationAsRead(notificationId: Int?): Boolean {
return api.markNotificationAsRead(
MarkNotificationReadBody(
appName = APIConstants.APP_NAME_DISCUSSION,
notificationId = notificationId,
)
).message.isNotNull()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ class NotificationsInteractor(
isDiscussionPushEnabled = isDiscussionPushEnabled,
)
}

suspend fun markAllNotificationsAsRead(): Boolean {
return repository.markNotificationAsRead(notificationId = null)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.openedx.notifications.presentation

import androidx.fragment.app.FragmentManager

interface NotificationsRouter {
fun navigateToPushNotificationsSettings(fm: FragmentManager)
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Surface
Expand All @@ -39,6 +41,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
Expand All @@ -63,15 +66,19 @@ import org.openedx.core.ui.HandleUIMessage
import org.openedx.core.ui.OpenEdXPrimaryButton
import org.openedx.core.ui.WindowSize
import org.openedx.core.ui.WindowType
import org.openedx.core.ui.crop
import org.openedx.core.ui.displayCutoutForLandscape
import org.openedx.core.ui.rememberWindowSize
import org.openedx.core.ui.shouldLoadMore
import org.openedx.core.ui.statusBarsInset
import org.openedx.core.ui.theme.AppShapes
import org.openedx.core.ui.theme.OpenEdXTheme
import org.openedx.core.ui.theme.appColors
import org.openedx.core.ui.theme.appShapes
import org.openedx.core.ui.theme.appTypography
import org.openedx.core.ui.windowSizeValue
import org.openedx.notifications.R
import org.openedx.notifications.data.model.NotificationsMenuType
import org.openedx.notifications.domain.model.InboxSection
import org.openedx.notifications.domain.model.NotificationContent
import org.openedx.notifications.domain.model.NotificationItem
Expand Down Expand Up @@ -104,8 +111,16 @@ class NotificationsInboxFragment : Fragment() {
onBackClick = {
requireActivity().supportFragmentManager.popBackStack()
},
onSettingsClick = {
onSettingsClick = { menuType ->
when (menuType) {
NotificationsMenuType.MARK_ALL_READ -> {
viewModel.markAllNotificationsAsRead()
}

NotificationsMenuType.NOTIFICATION_SETTINGS -> {
viewModel.navigateToPushNotificationsSettings(requireActivity().supportFragmentManager)
}
}
},
onReloadNotifications = {
viewModel.onReloadNotifications()
Expand All @@ -132,7 +147,7 @@ private fun InboxView(
uiMessage: UIMessage?,
canLoadMore: Boolean,
onBackClick: () -> Unit,
onSettingsClick: () -> Unit,
onSettingsClick: (NotificationsMenuType) -> Unit,
onReloadNotifications: () -> Unit,
paginationCallBack: () -> Unit,
markNotificationAsRead: (notificationItem: NotificationItem, inboxSection: InboxSection) -> Unit,
Expand Down Expand Up @@ -267,7 +282,7 @@ private fun InboxView(
private fun Header(
modifier: Modifier = Modifier,
onBackClick: () -> Unit,
onSettingsClick: () -> Unit,
onSettingsClick: (NotificationsMenuType) -> Unit,
) {
Box(
modifier = modifier
Expand All @@ -291,17 +306,14 @@ private fun Header(
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center,
)
NotificationsDropdownMenu(
modifier = Modifier
.align(Alignment.CenterEnd)
.padding(end = 16.dp),
onItemClick = onSettingsClick
)


IconButton(
modifier = Modifier.align(Alignment.CenterEnd),
onClick = { onSettingsClick() }
) {
Icon(
imageVector = Icons.Default.MoreVert,
tint = MaterialTheme.appColors.primary,
contentDescription = stringResource(id = R.string.notifications_accessibility_settings)
)
}
}
}

Expand All @@ -324,6 +336,63 @@ private fun SectionHeader(
}
}

@Composable
private fun NotificationsDropdownMenu(
modifier: Modifier = Modifier,
onItemClick: (NotificationsMenuType) -> Unit,
) {
var expanded by remember { mutableStateOf(false) }

Column(
modifier = modifier.padding(vertical = 8.dp)
) {
Row(
modifier = Modifier
.clickable {
expanded = true
},
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Default.MoreVert,
tint = MaterialTheme.appColors.primary,
contentDescription = stringResource(id = R.string.notifications_accessibility_settings)
)
}

MaterialTheme(
colors = MaterialTheme.colors.copy(surface = MaterialTheme.appColors.background),
shapes = MaterialTheme.shapes.copy(MaterialTheme.appShapes.material.medium)
) {
DropdownMenu(
modifier = Modifier
.crop(vertical = 8.dp)
.widthIn(min = 182.dp)
.background(MaterialTheme.appColors.surface),
expanded = expanded,
onDismissRequest = { expanded = false }
) {
for (menuItem in NotificationsMenuType.entries) {
DropdownMenuItem(
modifier = Modifier
.background(MaterialTheme.appColors.surface),
onClick = {
onItemClick(menuItem)
expanded = false
}
) {
Text(
text = stringResource(id = menuItem.title),
style = MaterialTheme.appTypography.titleSmall,
color = MaterialTheme.appColors.textPrimary
)
}
}
}
}
}
}

@Composable
private fun NotificationItemView(
modifier: Modifier = Modifier,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.openedx.notifications.presentation.inbox

import androidx.fragment.app.FragmentManager
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -13,11 +14,13 @@ import org.openedx.core.system.ResourceManager
import org.openedx.notifications.domain.interactor.NotificationsInteractor
import org.openedx.notifications.domain.model.InboxSection
import org.openedx.notifications.domain.model.NotificationItem
import org.openedx.notifications.presentation.NotificationsRouter
import java.util.Date
import org.openedx.core.R as coreR

class NotificationsInboxViewModel(
private val interactor: NotificationsInteractor,
private val notificationsRouter: NotificationsRouter,
private val resourceManager: ResourceManager,
) : BaseViewModel() {

Expand Down Expand Up @@ -114,7 +117,7 @@ class NotificationsInboxViewModel(
) {
viewModelScope.launch {
try {
if (notification.isUnread() && interactor.markNotificationAsRead(notification.id)) {
if (interactor.markNotificationAsRead(notification.id)) {
val currentSection = notifications[inboxSection] ?: return@launch

val index = currentSection.indexOfFirst { it.id == notification.id }
Expand Down Expand Up @@ -150,4 +153,26 @@ class NotificationsInboxViewModel(
)
}
}

fun navigateToPushNotificationsSettings(fm: FragmentManager) {
notificationsRouter.navigateToPushNotificationsSettings(fm)
}

fun markAllNotificationsAsRead() {
viewModelScope.launch {
try {
interactor.markAllNotificationsAsRead()

notifications.forEach { (section, notificationItems) ->
notifications[section] =
notificationItems.map { it.copy(lastRead = Date()) }.toMutableList()
}

_uiState.value = InboxUIState.Data(notifications = notifications.toMap())
} catch (e: Exception) {
e.printStackTrace()
emitErrorMessage(e)
}
}
}
}
3 changes: 3 additions & 0 deletions notifications/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
<string name="notification_now">Now</string>
<string name="notifications_no_notifications_yet">No notifications yet</string>
<string name="notifications_no_notifications_yet_description">When you receive notifications they’ll show up here</string>

<string name="notifications_menu_settings">Notification Settings</string>
<string name="notifications_menu_mark_all_read">Mark all as read</string>
<plurals name="notifications_date_format_seconds">
<item quantity="one">%1$s second</item>
<item quantity="other">%1$s seconds</item>
Expand Down

0 comments on commit 8561166

Please sign in to comment.