From 3e27567532ae00224fe7cb78b1f174ff38562273 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Thu, 20 Apr 2023 14:09:26 -0400 Subject: [PATCH] Moves home and Notification bubbles to the adaptive feed. --- .../ui/dal/ChatroomListKnownFeedFilter.kt | 2 +- .../amethyst/ui/navigation/AppBottomBar.kt | 96 +++++++++++-------- .../amethyst/ui/navigation/Routes.kt | 84 +++++++++++----- 3 files changed, 114 insertions(+), 68 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/ChatroomListKnownFeedFilter.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/ChatroomListKnownFeedFilter.kt index ddafd9e4a..396933e94 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/ChatroomListKnownFeedFilter.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/dal/ChatroomListKnownFeedFilter.kt @@ -10,7 +10,7 @@ object ChatroomListKnownFeedFilter : FeedFilter() { override fun feed(): List { val me = account.userProfile() - val privateChatrooms = account.userProfile().privateChatrooms + val privateChatrooms = me.privateChatrooms val messagingWith = privateChatrooms.keys.filter { me.hasSentMessagesTo(it) && account.isAcceptable(it) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppBottomBar.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppBottomBar.kt index e275cf4f6..dd08efd7e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppBottomBar.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppBottomBar.kt @@ -1,6 +1,7 @@ package com.vitorpamplona.amethyst.ui.navigation import android.graphics.Rect +import android.util.Log import android.view.ViewTreeObserver import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -42,6 +43,8 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import kotlin.time.ExperimentalTime +import kotlin.time.measureTimedValue val bottomNavigationItems = listOf( Route.Home, @@ -135,66 +138,75 @@ fun AppBottomBar(navController: NavHostController, accountViewModel: AccountView } } +@OptIn(ExperimentalTime::class) @Composable private fun NotifiableIcon(route: Route, selected: Boolean, accountViewModel: AccountViewModel) { - Box(Modifier.size(if ("Home" == route.base) 25.dp else 23.dp)) { - Icon( - painter = painterResource(id = route.icon), - contentDescription = null, - modifier = Modifier.size(if ("Home" == route.base) 24.dp else 20.dp), - tint = if (selected) MaterialTheme.colors.primary else Color.Unspecified - ) + println("Notifiable Icon") - val accountState by accountViewModel.accountLiveData.observeAsState() - val account = accountState?.account ?: return + val (value, elapsed) = measureTimedValue { + Box(Modifier.size(if ("Home" == route.base) 25.dp else 23.dp)) { + Icon( + painter = painterResource(id = route.icon), + contentDescription = null, + modifier = Modifier.size(if ("Home" == route.base) 24.dp else 20.dp), + tint = if (selected) MaterialTheme.colors.primary else Color.Unspecified + ) - // Notification - val dbState = LocalCache.live.observeAsState() - val db = dbState.value ?: return + println("Notifiable Icon") - val notifState = NotificationCache.live.observeAsState() - val notif = notifState.value ?: return + val accountState by accountViewModel.accountLiveData.observeAsState() + val account = accountState?.account ?: return - var hasNewItems by remember { mutableStateOf(false) } + // Notification + val dbState = LocalCache.live.observeAsState() + val db = dbState.value ?: return - LaunchedEffect(key1 = notif) { - withContext(Dispatchers.IO) { - hasNewItems = route.hasNewItems(account, notif.cache) + val notifState = NotificationCache.live.observeAsState() + val notif = notifState.value ?: return + + var hasNewItems by remember { mutableStateOf(false) } + + LaunchedEffect(key1 = notif) { + withContext(Dispatchers.IO) { + hasNewItems = route.hasNewItems(account, notif.cache, emptySet()) + } } - } - LaunchedEffect(key1 = db) { - withContext(Dispatchers.IO) { - hasNewItems = route.hasNewItems(account, notif.cache) + LaunchedEffect(key1 = db) { + withContext(Dispatchers.IO) { + hasNewItems = route.hasNewItems(account, notif.cache, db) + } } - } - if (hasNewItems) { - Box( - Modifier - .width(10.dp) - .height(10.dp) - .align(Alignment.TopEnd) - ) { + if (hasNewItems) { Box( - modifier = Modifier + Modifier .width(10.dp) .height(10.dp) - .clip(shape = CircleShape) - .background(MaterialTheme.colors.primary), - contentAlignment = Alignment.TopEnd + .align(Alignment.TopEnd) ) { - Text( - "", - color = Color.White, - textAlign = TextAlign.Center, - fontSize = 12.sp, + Box( modifier = Modifier - .wrapContentHeight() - .align(Alignment.TopEnd) - ) + .width(10.dp) + .height(10.dp) + .clip(shape = CircleShape) + .background(MaterialTheme.colors.primary), + contentAlignment = Alignment.TopEnd + ) { + Text( + "", + color = Color.White, + textAlign = TextAlign.Center, + fontSize = 12.sp, + modifier = Modifier + .wrapContentHeight() + .align(Alignment.TopEnd) + ) + } } } } } + + Log.d("Notification time", "$elapsed") } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt index ddbe4d307..216a5531f 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt @@ -10,6 +10,7 @@ import androidx.navigation.navArgument import com.vitorpamplona.amethyst.NotificationCache import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.model.Account +import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.ui.dal.ChatroomListKnownFeedFilter import com.vitorpamplona.amethyst.ui.dal.HomeNewThreadFeedFilter import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter @@ -17,7 +18,7 @@ import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter sealed class Route( val route: String, val icon: Int, - val hasNewItems: (Account, NotificationCache) -> Boolean = { _, _ -> false }, + val hasNewItems: (Account, NotificationCache, Set) -> Boolean = { _, _, _ -> false }, val arguments: List = emptyList() ) { val base: String @@ -30,7 +31,7 @@ sealed class Route( navArgument("scrollToTop") { type = NavType.BoolType; defaultValue = false }, navArgument("nip47") { type = NavType.StringType; nullable = true; defaultValue = null } ), - hasNewItems = { accountViewModel, cache -> homeHasNewItems(accountViewModel, cache) } + hasNewItems = { accountViewModel, cache, newNotes -> HomeLatestItem.hasNewItems(accountViewModel, cache, newNotes) } ) object Search : Route( @@ -43,13 +44,13 @@ sealed class Route( route = "Notification?scrollToTop={scrollToTop}", icon = R.drawable.ic_notifications, arguments = listOf(navArgument("scrollToTop") { type = NavType.BoolType; defaultValue = false }), - hasNewItems = { accountViewModel, cache -> notificationHasNewItems(accountViewModel, cache) } + hasNewItems = { accountViewModel, cache, newNotes -> NotificationLatestItem.hasNewItems(accountViewModel, cache, newNotes) } ) object Message : Route( route = "Message", icon = R.drawable.ic_dm, - hasNewItems = { accountViewModel, cache -> messagesHasNewItems(accountViewModel, cache) } + hasNewItems = { accountViewModel, cache, newNotes -> MessagesLatestItem.hasNewItems(accountViewModel, cache, newNotes) } ) object BlockedUsers : Route( @@ -108,36 +109,69 @@ fun currentRoute(navController: NavHostController): String? { return navBackStackEntry?.destination?.route } -private fun homeHasNewItems(account: Account, cache: NotificationCache): Boolean { - val lastTime = cache.load("HomeFollows") +object HomeLatestItem { + private var newestItem: Note? = null - HomeNewThreadFeedFilter.account = account + fun hasNewItems( + account: Account, + cache: NotificationCache, + newNotes: Set + ): Boolean { + val lastTime = cache.load("HomeFollows") + HomeNewThreadFeedFilter.account = account - return ( - HomeNewThreadFeedFilter.feed().firstOrNull { it.createdAt() != null }?.createdAt() - ?: 0 - ) > lastTime + if (newestItem == null) { + newestItem = HomeNewThreadFeedFilter.feed().firstOrNull { it.createdAt() != null } + } else { + newestItem = + HomeNewThreadFeedFilter.sort( + HomeNewThreadFeedFilter.applyFilter(newNotes + newestItem!!) + ).first() + } + + return (newestItem?.createdAt() ?: 0) > lastTime + } } -private fun notificationHasNewItems(account: Account, cache: NotificationCache): Boolean { - val lastTime = cache.load("Notification") +object NotificationLatestItem { + private var newestItem: Note? = null - NotificationFeedFilter.account = account + fun hasNewItems( + account: Account, + cache: NotificationCache, + newNotes: Set + ): Boolean { + val lastTime = cache.load("Notification") + NotificationFeedFilter.account = account - return ( - NotificationFeedFilter.feed().firstOrNull { it.createdAt() != null }?.createdAt() - ?: 0 - ) > lastTime + if (newestItem == null) { + newestItem = NotificationFeedFilter.feed().firstOrNull { it.createdAt() != null } + } else { + newestItem = HomeNewThreadFeedFilter.sort( + NotificationFeedFilter.applyFilter(newNotes) + newestItem!! + ).first() + } + + return (newestItem?.createdAt() ?: 0) > lastTime + } } -private fun messagesHasNewItems(account: Account, cache: NotificationCache): Boolean { - ChatroomListKnownFeedFilter.account = account +object MessagesLatestItem { + private var newestItem: Note? = null - val note = ChatroomListKnownFeedFilter.feed().firstOrNull { - it.createdAt() != null && it.channel() == null && it.author != account.userProfile() - } ?: return false + fun hasNewItems( + account: Account, + cache: NotificationCache, + newNotes: Set + ): Boolean { + ChatroomListKnownFeedFilter.account = account - val lastTime = cache.load("Room/${note.author?.pubkeyHex}") + val note = ChatroomListKnownFeedFilter.loadTop().firstOrNull { + it.createdAt() != null && it.channel() == null && it.author != account.userProfile() + } ?: return false - return (note.createdAt() ?: 0) > lastTime + val lastTime = cache.load("Room/${note.author?.pubkeyHex}") + + return (note.createdAt() ?: 0) > lastTime + } }