Moves home and Notification bubbles to the adaptive feed.
This commit is contained in:
@@ -10,7 +10,7 @@ object ChatroomListKnownFeedFilter : FeedFilter<Note>() {
|
|||||||
override fun feed(): List<Note> {
|
override fun feed(): List<Note> {
|
||||||
val me = account.userProfile()
|
val me = account.userProfile()
|
||||||
|
|
||||||
val privateChatrooms = account.userProfile().privateChatrooms
|
val privateChatrooms = me.privateChatrooms
|
||||||
val messagingWith = privateChatrooms.keys.filter {
|
val messagingWith = privateChatrooms.keys.filter {
|
||||||
me.hasSentMessagesTo(it) && account.isAcceptable(it)
|
me.hasSentMessagesTo(it) && account.isAcceptable(it)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.navigation
|
package com.vitorpamplona.amethyst.ui.navigation
|
||||||
|
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
|
import android.util.Log
|
||||||
import android.view.ViewTreeObserver
|
import android.view.ViewTreeObserver
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
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.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import kotlin.time.ExperimentalTime
|
||||||
|
import kotlin.time.measureTimedValue
|
||||||
|
|
||||||
val bottomNavigationItems = listOf(
|
val bottomNavigationItems = listOf(
|
||||||
Route.Home,
|
Route.Home,
|
||||||
@@ -135,66 +138,75 @@ fun AppBottomBar(navController: NavHostController, accountViewModel: AccountView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTime::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun NotifiableIcon(route: Route, selected: Boolean, accountViewModel: AccountViewModel) {
|
private fun NotifiableIcon(route: Route, selected: Boolean, accountViewModel: AccountViewModel) {
|
||||||
Box(Modifier.size(if ("Home" == route.base) 25.dp else 23.dp)) {
|
println("Notifiable Icon")
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
val accountState by accountViewModel.accountLiveData.observeAsState()
|
val (value, elapsed) = measureTimedValue {
|
||||||
val account = accountState?.account ?: return
|
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
|
println("Notifiable Icon")
|
||||||
val dbState = LocalCache.live.observeAsState()
|
|
||||||
val db = dbState.value ?: return
|
|
||||||
|
|
||||||
val notifState = NotificationCache.live.observeAsState()
|
val accountState by accountViewModel.accountLiveData.observeAsState()
|
||||||
val notif = notifState.value ?: return
|
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) {
|
val notifState = NotificationCache.live.observeAsState()
|
||||||
withContext(Dispatchers.IO) {
|
val notif = notifState.value ?: return
|
||||||
hasNewItems = route.hasNewItems(account, notif.cache)
|
|
||||||
|
var hasNewItems by remember { mutableStateOf<Boolean>(false) }
|
||||||
|
|
||||||
|
LaunchedEffect(key1 = notif) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
hasNewItems = route.hasNewItems(account, notif.cache, emptySet())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(key1 = db) {
|
LaunchedEffect(key1 = db) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
hasNewItems = route.hasNewItems(account, notif.cache)
|
hasNewItems = route.hasNewItems(account, notif.cache, db)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (hasNewItems) {
|
if (hasNewItems) {
|
||||||
Box(
|
|
||||||
Modifier
|
|
||||||
.width(10.dp)
|
|
||||||
.height(10.dp)
|
|
||||||
.align(Alignment.TopEnd)
|
|
||||||
) {
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
Modifier
|
||||||
.width(10.dp)
|
.width(10.dp)
|
||||||
.height(10.dp)
|
.height(10.dp)
|
||||||
.clip(shape = CircleShape)
|
.align(Alignment.TopEnd)
|
||||||
.background(MaterialTheme.colors.primary),
|
|
||||||
contentAlignment = Alignment.TopEnd
|
|
||||||
) {
|
) {
|
||||||
Text(
|
Box(
|
||||||
"",
|
|
||||||
color = Color.White,
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
fontSize = 12.sp,
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.wrapContentHeight()
|
.width(10.dp)
|
||||||
.align(Alignment.TopEnd)
|
.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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import androidx.navigation.navArgument
|
|||||||
import com.vitorpamplona.amethyst.NotificationCache
|
import com.vitorpamplona.amethyst.NotificationCache
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.model.Account
|
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.ChatroomListKnownFeedFilter
|
||||||
import com.vitorpamplona.amethyst.ui.dal.HomeNewThreadFeedFilter
|
import com.vitorpamplona.amethyst.ui.dal.HomeNewThreadFeedFilter
|
||||||
import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter
|
import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter
|
||||||
@@ -17,7 +18,7 @@ import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter
|
|||||||
sealed class Route(
|
sealed class Route(
|
||||||
val route: String,
|
val route: String,
|
||||||
val icon: Int,
|
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 arguments: List<NamedNavArgument> = emptyList()
|
||||||
) {
|
) {
|
||||||
val base: String
|
val base: String
|
||||||
@@ -30,7 +31,7 @@ sealed class Route(
|
|||||||
navArgument("scrollToTop") { type = NavType.BoolType; defaultValue = false },
|
navArgument("scrollToTop") { type = NavType.BoolType; defaultValue = false },
|
||||||
navArgument("nip47") { type = NavType.StringType; nullable = true; defaultValue = null }
|
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(
|
object Search : Route(
|
||||||
@@ -43,13 +44,13 @@ sealed class Route(
|
|||||||
route = "Notification?scrollToTop={scrollToTop}",
|
route = "Notification?scrollToTop={scrollToTop}",
|
||||||
icon = R.drawable.ic_notifications,
|
icon = R.drawable.ic_notifications,
|
||||||
arguments = listOf(navArgument("scrollToTop") { type = NavType.BoolType; defaultValue = false }),
|
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(
|
object Message : Route(
|
||||||
route = "Message",
|
route = "Message",
|
||||||
icon = R.drawable.ic_dm,
|
icon = R.drawable.ic_dm,
|
||||||
hasNewItems = { accountViewModel, cache -> messagesHasNewItems(accountViewModel, cache) }
|
hasNewItems = { accountViewModel, cache, newNotes -> MessagesLatestItem.hasNewItems(accountViewModel, cache, newNotes) }
|
||||||
)
|
)
|
||||||
|
|
||||||
object BlockedUsers : Route(
|
object BlockedUsers : Route(
|
||||||
@@ -108,36 +109,69 @@ fun currentRoute(navController: NavHostController): String? {
|
|||||||
return navBackStackEntry?.destination?.route
|
return navBackStackEntry?.destination?.route
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun homeHasNewItems(account: Account, cache: NotificationCache): Boolean {
|
object HomeLatestItem {
|
||||||
val lastTime = cache.load("HomeFollows")
|
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 (
|
if (newestItem == null) {
|
||||||
HomeNewThreadFeedFilter.feed().firstOrNull { it.createdAt() != null }?.createdAt()
|
newestItem = HomeNewThreadFeedFilter.feed().firstOrNull { it.createdAt() != null }
|
||||||
?: 0
|
} else {
|
||||||
) > lastTime
|
newestItem =
|
||||||
|
HomeNewThreadFeedFilter.sort(
|
||||||
|
HomeNewThreadFeedFilter.applyFilter(newNotes + newestItem!!)
|
||||||
|
).first()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (newestItem?.createdAt() ?: 0) > lastTime
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun notificationHasNewItems(account: Account, cache: NotificationCache): Boolean {
|
object NotificationLatestItem {
|
||||||
val lastTime = cache.load("Notification")
|
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 (
|
if (newestItem == null) {
|
||||||
NotificationFeedFilter.feed().firstOrNull { it.createdAt() != null }?.createdAt()
|
newestItem = NotificationFeedFilter.feed().firstOrNull { it.createdAt() != null }
|
||||||
?: 0
|
} else {
|
||||||
) > lastTime
|
newestItem = HomeNewThreadFeedFilter.sort(
|
||||||
|
NotificationFeedFilter.applyFilter(newNotes) + newestItem!!
|
||||||
|
).first()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (newestItem?.createdAt() ?: 0) > lastTime
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun messagesHasNewItems(account: Account, cache: NotificationCache): Boolean {
|
object MessagesLatestItem {
|
||||||
ChatroomListKnownFeedFilter.account = account
|
private var newestItem: Note? = null
|
||||||
|
|
||||||
val note = ChatroomListKnownFeedFilter.feed().firstOrNull {
|
fun hasNewItems(
|
||||||
it.createdAt() != null && it.channel() == null && it.author != account.userProfile()
|
account: Account,
|
||||||
} ?: return false
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user