Moves home and Notification bubbles to the adaptive feed.

This commit is contained in:
Vitor Pamplona
2023-04-20 14:09:26 -04:00
parent 0a15e079dc
commit 3e27567532
3 changed files with 114 additions and 68 deletions

View File

@@ -10,7 +10,7 @@ object ChatroomListKnownFeedFilter : FeedFilter<Note>() {
override fun feed(): List<Note> {
val me = account.userProfile()
val privateChatrooms = account.userProfile().privateChatrooms
val privateChatrooms = me.privateChatrooms
val messagingWith = privateChatrooms.keys.filter {
me.hasSentMessagesTo(it) && account.isAcceptable(it)
}

View File

@@ -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<Boolean>(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<Boolean>(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")
}

View File

@@ -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<com.vitorpamplona.amethyst.model.Note>) -> Boolean = { _, _, _ -> false },
val arguments: List<NamedNavArgument> = 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<Note>
): 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<Note>
): 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<Note>
): 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
}
}