Add buttons to mark DMs and chatrooms as read #172

This commit is contained in:
maxmoney21m
2023-03-03 02:24:13 +08:00
parent 0da542fede
commit 8d7897601b
4 changed files with 174 additions and 50 deletions
@@ -9,21 +9,30 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavController
import com.google.accompanist.swiperefresh.SwipeRefresh
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import com.vitorpamplona.amethyst.NotificationCache
import com.vitorpamplona.amethyst.ui.note.ChatroomCompose
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@Composable
fun ChatroomListFeedView(viewModel: FeedViewModel, accountViewModel: AccountViewModel, navController: NavController) {
fun ChatroomListFeedView(
viewModel: FeedViewModel,
accountViewModel: AccountViewModel,
navController: NavController,
markAsRead: MutableState<Boolean>
) {
val feedState by viewModel.feedContent.collectAsStateWithLifecycle()
var isRefreshing by remember { mutableStateOf(false) }
@@ -45,21 +54,27 @@ fun ChatroomListFeedView(viewModel: FeedViewModel, accountViewModel: AccountView
},
) {
Column() {
Crossfade(targetState = feedState, animationSpec = tween(durationMillis = 100)) { state ->
Crossfade(
targetState = feedState,
animationSpec = tween(durationMillis = 100)
) { state ->
when (state) {
is FeedState.Empty -> {
FeedEmpty {
isRefreshing = true
}
}
is FeedState.FeedError -> {
FeedError(state.errorMessage) {
isRefreshing = true
}
}
is FeedState.Loaded -> {
FeedLoaded(state, accountViewModel, navController)
FeedLoaded(state, accountViewModel, navController, markAsRead)
}
FeedState.Loading -> {
LoadingFeed()
}
@@ -73,10 +88,43 @@ fun ChatroomListFeedView(viewModel: FeedViewModel, accountViewModel: AccountView
private fun FeedLoaded(
state: FeedState.Loaded,
accountViewModel: AccountViewModel,
navController: NavController
navController: NavController,
markAsRead: MutableState<Boolean>,
) {
val listState = rememberLazyListState()
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
val notificationCacheState = NotificationCache.live.observeAsState()
val notificationCache = notificationCacheState.value ?: return
val context = LocalContext.current.applicationContext
if (markAsRead.value) {
LaunchedEffect(key1 = notificationCache) {
for (note in state.feed.value) {
note.event?.let {
var route = ""
if (note.channel != null) {
route = "Channel/${note.channel!!.idHex}"
} else {
val replyAuthorBase = note.mentions?.first()
var userToComposeOn = note.author!!
if (replyAuthorBase != null) {
if (note.author == account.userProfile()) {
userToComposeOn = replyAuthorBase
}
}
route = "Room/${userToComposeOn.pubkeyHex}"
}
notificationCache.cache.markAsRead(route, it.createdAt, context)
}
}
markAsRead.value = false
}
}
LazyColumn(
contentPadding = PaddingValues(
top = 10.dp,
@@ -84,7 +132,9 @@ private fun FeedLoaded(
),
state = listState
) {
itemsIndexed(state.feed.value, key = { index, item -> if (index == 0) index else item.idHex }) { index, item ->
itemsIndexed(
state.feed.value,
key = { index, item -> if (index == 0) index else item.idHex }) { index, item ->
ChatroomCompose(
item,
accountViewModel = accountViewModel,
@@ -113,7 +113,4 @@ class AccountViewModel(private val account: Account): ViewModel() {
account.prefer(source, target, preference)
}
}
@@ -1,31 +1,42 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
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.Tab
import androidx.compose.material.TabRow
import androidx.compose.material.TabRowDefaults
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.pagerTabIndicatorOffset
import com.google.accompanist.pager.rememberPagerState
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.service.NostrChatroomListDataSource
import com.vitorpamplona.amethyst.ui.dal.ChatroomListKnownFeedFilter
import com.vitorpamplona.amethyst.ui.dal.ChatroomListNewFeedFilter
@@ -33,7 +44,6 @@ import com.vitorpamplona.amethyst.ui.screen.ChatroomListFeedView
import com.vitorpamplona.amethyst.ui.screen.NostrChatroomListKnownFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrChatroomListNewFeedViewModel
import kotlinx.coroutines.launch
import com.vitorpamplona.amethyst.R
@OptIn(ExperimentalPagerApi::class)
@Composable
@@ -41,48 +51,78 @@ fun ChatroomListScreen(accountViewModel: AccountViewModel, navController: NavCon
val pagerState = rememberPagerState()
val coroutineScope = rememberCoroutineScope()
Column(Modifier.fillMaxHeight()) {
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
TabRow(
backgroundColor = MaterialTheme.colors.background,
selectedTabIndex = pagerState.currentPage,
indicator = { tabPositions ->
TabRowDefaults.Indicator(
Modifier.pagerTabIndicatorOffset(pagerState, tabPositions),
color = MaterialTheme.colors.primary
)
},
) {
Tab(
selected = pagerState.currentPage == 0,
onClick = { coroutineScope.launch { pagerState.animateScrollToPage(0) } },
text = {
Text(text = stringResource(R.string.known))
}
)
var moreActionsExpanded by remember { mutableStateOf(false) }
val markKnownAsRead = remember { mutableStateOf(false) }
val markNewAsRead = remember { mutableStateOf(false) }
Tab(
selected = pagerState.currentPage == 1,
onClick = { coroutineScope.launch { pagerState.animateScrollToPage(1) } },
text = {
Text(text = stringResource(R.string.new_requests))
Box(Modifier.fillMaxSize()) {
Column(Modifier.fillMaxHeight()) {
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
TabRow(
backgroundColor = MaterialTheme.colors.background,
selectedTabIndex = pagerState.currentPage,
indicator = { tabPositions ->
TabRowDefaults.Indicator(
Modifier.pagerTabIndicatorOffset(pagerState, tabPositions),
color = MaterialTheme.colors.primary
)
},
) {
Tab(
selected = pagerState.currentPage == 0,
onClick = { coroutineScope.launch { pagerState.animateScrollToPage(0) } },
text = {
Text(text = stringResource(R.string.known))
}
)
Tab(
selected = pagerState.currentPage == 1,
onClick = { coroutineScope.launch { pagerState.animateScrollToPage(1) } },
text = {
Text(text = stringResource(R.string.new_requests))
}
)
}
HorizontalPager(count = 2, state = pagerState) {
when (pagerState.currentPage) {
0 -> TabKnown(accountViewModel, navController, markKnownAsRead,)
1 -> TabNew(accountViewModel, navController, markNewAsRead)
}
)
}
HorizontalPager(count = 2, state = pagerState) {
when (pagerState.currentPage) {
0 -> TabKnown(accountViewModel, navController)
1 -> TabNew(accountViewModel, navController)
}
}
}
IconButton(
modifier = Modifier
.padding(0.dp)
.size(30.dp)
.align(Alignment.TopEnd),
onClick = { moreActionsExpanded = true }
) {
Icon(
imageVector = Icons.Default.MoreVert,
contentDescription = null,
tint = MaterialTheme.colors.onSurface.copy(alpha = 0.32f),
)
ChatroomTabMenu(
moreActionsExpanded,
{ moreActionsExpanded = false },
{ markKnownAsRead.value = true },
{ markNewAsRead.value = true },
)
}
}
}
@Composable
fun TabKnown(accountViewModel: AccountViewModel, navController: NavController) {
fun TabKnown(
accountViewModel: AccountViewModel,
navController: NavController,
markAsRead: MutableState<Boolean>,
) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
@@ -98,13 +138,17 @@ fun TabKnown(accountViewModel: AccountViewModel, navController: NavController) {
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
ChatroomListFeedView(feedViewModel, accountViewModel, navController)
ChatroomListFeedView(feedViewModel, accountViewModel, navController, markAsRead)
}
}
}
@Composable
fun TabNew(accountViewModel: AccountViewModel, navController: NavController) {
fun TabNew(
accountViewModel: AccountViewModel,
navController: NavController,
markAsRead: MutableState<Boolean>
) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return
@@ -120,7 +164,37 @@ fun TabNew(accountViewModel: AccountViewModel, navController: NavController) {
Column(
modifier = Modifier.padding(vertical = 0.dp)
) {
ChatroomListFeedView(feedViewModel, accountViewModel, navController)
ChatroomListFeedView(feedViewModel, accountViewModel, navController, markAsRead)
}
}
}
}
@Composable
fun ChatroomTabMenu(
expanded: Boolean,
onDismiss: () -> Unit,
onMarkKnownAsRead: () -> Unit,
onMarkNewAsRead: () -> Unit,
) {
DropdownMenu(expanded = expanded, onDismissRequest = onDismiss) {
DropdownMenuItem(onClick = {
onMarkKnownAsRead()
onDismiss()
}) {
Text(stringResource(R.string.mark_all_known_as_read))
}
DropdownMenuItem(onClick = {
onMarkNewAsRead()
onDismiss()
}) {
Text(stringResource(R.string.mark_all_new_as_read))
}
DropdownMenuItem(onClick = {
onMarkKnownAsRead()
onMarkNewAsRead()
onDismiss()
}) {
Text(stringResource(R.string.mark_all_as_read))
}
}
}