Improve NotificationsScreen UI

This commit is contained in:
Shinokuni 2024-07-31 14:51:42 +02:00
parent 9210b8b7fe
commit 4771831d15
7 changed files with 138 additions and 71 deletions

View File

@ -20,7 +20,6 @@ import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.readrops.app.R
import com.readrops.app.util.theme.MediumSpacer
import com.readrops.app.util.theme.VeryShortSpacer
import com.readrops.app.util.theme.spacing
@Composable
@ -62,13 +61,10 @@ fun NotificationItem(
text = feedName,
style = MaterialTheme.typography.bodyLarge,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
overflow = TextOverflow.Ellipsis
)
if (folderName != null) {
VeryShortSpacer()
Text(
text = folderName,
style = MaterialTheme.typography.bodyMedium,

View File

@ -6,8 +6,6 @@ import android.content.Intent
import android.provider.Settings
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
@ -27,7 +25,6 @@ import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
@ -45,8 +42,11 @@ import com.readrops.app.R
import com.readrops.app.more.preferences.PreferencesScreen
import com.readrops.app.more.preferences.components.BasePreference
import com.readrops.app.util.components.AndroidScreen
import com.readrops.app.util.components.CenteredProgressIndicator
import com.readrops.app.util.components.Placeholder
import com.readrops.app.util.components.ThreeDotsMenu
import com.readrops.app.util.components.dialog.TwoChoicesDialog
import com.readrops.app.util.theme.LargeSpacer
import com.readrops.app.util.theme.MediumSpacer
import com.readrops.app.util.theme.spacing
import com.readrops.db.entities.account.Account
@ -68,9 +68,10 @@ class NotificationsScreen(val account: Account) : AndroidScreen() {
TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
val permissionState = rememberPermissionState(Manifest.permission.POST_NOTIFICATIONS)
val launcher = rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) {
screenModel.refreshNotificationManager()
}
val launcher =
rememberLauncherForActivityResult(contract = ActivityResultContracts.StartActivityForResult()) {
screenModel.refreshNotificationManager()
}
LaunchedEffect(permissionState.status) {
if (permissionState.status.isGranted) {
@ -108,18 +109,25 @@ class NotificationsScreen(val account: Account) : AndroidScreen() {
}
},
actions = {
ThreeDotsMenu(
items = mapOf(
1 to if (state.allFeedNotificationsEnabled) {
stringResource(id = R.string.disable_all)
} else {
stringResource(id = R.string.enable_all)
}
),
onItemClick = {
screenModel.setAllFeedsNotificationsState(!state.allFeedNotificationsEnabled)
if (state.feedsWithFolderState is FeedsWithFolderState.Loaded) {
val loadedState =
state.feedsWithFolderState as FeedsWithFolderState.Loaded
if (loadedState.feedsWithFolder.isNotEmpty()) {
ThreeDotsMenu(
items = mapOf(
1 to if (loadedState.allFeedNotificationsEnabled) {
stringResource(id = R.string.disable_all)
} else {
stringResource(id = R.string.enable_all)
}
),
onItemClick = {
screenModel.setAllFeedsNotificationsState(!loadedState.allFeedNotificationsEnabled)
}
)
}
)
}
},
scrollBehavior = topAppBarScrollBehavior
)
@ -155,28 +163,29 @@ class NotificationsScreen(val account: Account) : AndroidScreen() {
}
item {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = MaterialTheme.spacing.mediumSpacing)
) {
Text(
text = stringResource(id = R.string.enable_notifications)
)
BasePreference(
title = stringResource(R.string.show_notifications_background_sync),
subtitle = stringResource(R.string.background_sync_new_articles),
onClick = {
screenModel.setAccountNotificationsState(!state.areAccountNotificationsEnabled)
Switch(
checked = state.areAccountNotificationsEnabled,
onCheckedChange = {
screenModel.setAccountNotificationsState(it)
if (it) {
screenModel.setBackgroundSyncDialogState(true)
}
if (state.areAccountNotificationsEnabled.not()) {
screenModel.setBackgroundSyncDialogState(true)
}
)
}
},
rightComponent = {
Switch(
checked = state.areAccountNotificationsEnabled,
onCheckedChange = {
screenModel.setAccountNotificationsState(it)
if (it) {
screenModel.setBackgroundSyncDialogState(true)
}
}
)
}
)
MediumSpacer()
@ -187,26 +196,56 @@ class NotificationsScreen(val account: Account) : AndroidScreen() {
)
}
items(
items = state.feedsWithFolder,
key = { it.feed.id }
) { feedWithFolder ->
NotificationItem(
feedName = feedWithFolder.feed.name!!,
iconUrl = feedWithFolder.feed.iconUrl,
folderName = feedWithFolder.folderName,
checked = feedWithFolder.feed.isNotificationEnabled,
enabled = state.areAccountNotificationsEnabled,
onCheckChange = {
if (state.areAccountNotificationsEnabled) {
screenModel.setFeedNotificationsState(feedWithFolder.feed.id, it)
when (state.feedsWithFolderState) {
is FeedsWithFolderState.Loaded -> {
val feedsWithFolder =
(state.feedsWithFolderState as FeedsWithFolderState.Loaded).feedsWithFolder
if (feedsWithFolder.isNotEmpty()) {
items(
items = (state.feedsWithFolderState as FeedsWithFolderState.Loaded).feedsWithFolder,
key = { it.feed.id }
) { feedWithFolder ->
NotificationItem(
feedName = feedWithFolder.feed.name!!,
iconUrl = feedWithFolder.feed.iconUrl,
folderName = feedWithFolder.folderName,
checked = feedWithFolder.feed.isNotificationEnabled,
enabled = state.areAccountNotificationsEnabled,
onCheckChange = {
if (state.areAccountNotificationsEnabled) {
screenModel.setFeedNotificationsState(
feedWithFolder.feed.id,
it
)
}
}
)
}
} else {
item {
LargeSpacer()
Placeholder(
text = stringResource(id = R.string.no_feed),
painter = painterResource(id = R.drawable.ic_rss_feed_grey),
)
}
}
)
}
FeedsWithFolderState.Loading -> {
item {
repeat(4) {
LargeSpacer()
}
CenteredProgressIndicator()
}
}
}
}
}
}
}

View File

@ -18,7 +18,12 @@ class NotificationsScreenModel(
private val preferences: Preferences,
private val notificationManager: NotificationManagerCompat,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) : StateScreenModel<NotificationsState>(NotificationsState(areAccountNotificationsEnabled = account.isNotificationsEnabled)) {
) : StateScreenModel<NotificationsState>(
NotificationsState(
areNotificationsEnabled = notificationManager.areNotificationsEnabled(),
areAccountNotificationsEnabled = account.isNotificationsEnabled
)
) {
init {
screenModelScope.launch(dispatcher) {
@ -31,7 +36,11 @@ class NotificationsScreenModel(
screenModelScope.launch(dispatcher) {
database.feedDao().selectFeedsWithFolderName(account.id)
.collect { feedsWithFolder ->
mutableState.update { it.copy(feedsWithFolder = feedsWithFolder) }
mutableState.update {
it.copy(
feedsWithFolderState = FeedsWithFolderState.Loaded(feedsWithFolder)
)
}
}
}
@ -79,12 +88,18 @@ class NotificationsScreenModel(
data class NotificationsState(
val areAccountNotificationsEnabled: Boolean = false,
val feedsWithFolder: List<FeedWithFolder> = emptyList(),
val feedsWithFolderState: FeedsWithFolderState = FeedsWithFolderState.Loading,
val showBackgroundSyncDialog: Boolean = false,
val isBackGroundSyncEnabled: Boolean = false,
val areNotificationsEnabled: Boolean = false
) {
)
val allFeedNotificationsEnabled
get() = feedsWithFolder.none { !it.feed.isNotificationEnabled }
sealed class FeedsWithFolderState {
data object Loading : FeedsWithFolderState()
data class Loaded(val feedsWithFolder: List<FeedWithFolder>) : FeedsWithFolderState() {
val allFeedNotificationsEnabled
get() = feedsWithFolder.none { !it.feed.isNotificationEnabled }
}
}

View File

@ -427,8 +427,8 @@ object TimelineTab : Tab {
)
Placeholder(
text = stringResource(R.string.no_item),
painter = painterResource(R.drawable.ic_rss_feed_grey)
text = stringResource(R.string.no_article),
painter = painterResource(R.drawable.ic_timeline),
)
}
}

View File

@ -10,19 +10,23 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.readrops.app.util.theme.ShortSpacer
import com.readrops.app.util.toDp
@Composable
fun CenteredColumn(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize()
modifier = modifier.fillMaxSize()
) {
content()
}
@ -32,19 +36,26 @@ fun CenteredColumn(
fun Placeholder(
text: String,
painter: Painter,
modifier: Modifier = Modifier,
textStyle: TextStyle = MaterialTheme.typography.titleLarge,
iconSize: Dp = 48.dp,
tint: Color = MaterialTheme.colorScheme.primary
) {
CenteredColumn {
CenteredColumn(
modifier = modifier
) {
Icon(
painter = painter,
tint = tint,
contentDescription = null,
modifier = Modifier.size(MaterialTheme.typography.displayMedium.toDp() * 1.5f)
modifier = Modifier.size(iconSize)
)
ShortSpacer()
Text(
text = text,
style = MaterialTheme.typography.displaySmall
style = textStyle
)
}
}

View File

@ -192,4 +192,7 @@
<string name="add_to_favorite">Ajouter aux favoris</string>
<string name="grant_access_notifications">Autoriser l\'accès aux notifications</string>
<string name="system_notifications_disabled">Les notfications système sont désactivées. Cliquez ici pour les activer</string>
<string name="show_notifications_background_sync">Afficher les notifications après la synchronisation en arrière-plan</string>
<string name="background_sync_new_articles">Soyez prévenus des nouveaux articles après la synchronisation en arrière-plan</string>
<string name="no_article">Aucun article</string>
</resources>

View File

@ -201,4 +201,7 @@
<string name="add_to_favorite">Add to favorites</string>
<string name="grant_access_notifications">Grant access to Notifications</string>
<string name="system_notifications_disabled">System notifications are currently disabled. Click here to enable them</string>
<string name="show_notifications_background_sync">Show notifications after background synchronization</string>
<string name="background_sync_new_articles">Be alerted of new articles after background synchronization</string>
<string name="no_article">No article</string>
</resources>