Moves lastread routes to mutableStateFlow

This commit is contained in:
Vitor Pamplona 2024-08-16 16:42:00 -04:00
parent 6c81fbbcf9
commit 239f973e89
6 changed files with 51 additions and 66 deletions

View File

@ -349,9 +349,15 @@ object LocalPreferences {
putInt(PrefKeys.PROXY_PORT, account.proxyPort)
putBoolean(PrefKeys.WARN_ABOUT_REPORTS, account.warnAboutPostsWithReports)
putBoolean(PrefKeys.FILTER_SPAM_FROM_STRANGERS, account.filterSpamFromStrangers)
val regularMap =
account.lastReadPerRoute.value.mapValues {
it.value.value
}
putString(
PrefKeys.LAST_READ_PER_ROUTE,
Event.mapper.writeValueAsString(account.lastReadPerRoute),
Event.mapper.writeValueAsString(regularMap),
)
putStringSet(PrefKeys.HAS_DONATED_IN_VERSION, account.hasDonatedInVersion)
@ -611,9 +617,10 @@ object LocalPreferences {
val lastReadPerRoute =
try {
getString(PrefKeys.LAST_READ_PER_ROUTE, null)?.let {
Event.mapper.readValue<Map<String, Long>?>(it)
Event.mapper.readValue<Map<String, Long>?>(it)?.mapValues {
MutableStateFlow(it.value)
}
?: mapOf()
} ?: mapOf()
} catch (e: Throwable) {
if (e is CancellationException) throw e
Log.w(
@ -669,7 +676,7 @@ object LocalPreferences {
showSensitiveContent = MutableStateFlow(showSensitiveContent),
warnAboutPostsWithReports = warnAboutReports,
filterSpamFromStrangers = filterSpam,
lastReadPerRoute = lastReadPerRoute,
lastReadPerRoute = MutableStateFlow(lastReadPerRoute),
hasDonatedInVersion = hasDonatedInVersion,
pendingAttestations = MutableStateFlow(pendingAttestations ?: emptyMap()),
)

View File

@ -202,7 +202,7 @@ class Account(
var showSensitiveContent: MutableStateFlow<Boolean?> = MutableStateFlow(null),
var warnAboutPostsWithReports: Boolean = true,
var filterSpamFromStrangers: Boolean = true,
var lastReadPerRoute: Map<String, Long> = mapOf<String, Long>(),
var lastReadPerRoute: MutableStateFlow<Map<String, MutableStateFlow<Long>>> = MutableStateFlow(mapOf()),
var hasDonatedInVersion: Set<String> = setOf<String>(),
var pendingAttestations: MutableStateFlow<Map<HexKey, String>> = MutableStateFlow<Map<HexKey, String>>(mapOf()),
val scope: CoroutineScope = Amethyst.instance.applicationIOScope,
@ -3213,9 +3213,15 @@ class Account(
route: String,
timestampInSecs: Long,
): Boolean {
val lastTime = lastReadPerRoute[route]
return if (lastTime == null || timestampInSecs > lastTime) {
lastReadPerRoute = lastReadPerRoute + Pair(route, timestampInSecs)
val lastTime = lastReadPerRoute.value[route]
return if (lastTime == null) {
lastReadPerRoute.update {
it + Pair(route, MutableStateFlow(timestampInSecs))
}
saveable.invalidateData()
true
} else if (timestampInSecs > lastTime.value) {
lastTime.tryEmit(timestampInSecs)
saveable.invalidateData()
true
} else {
@ -3223,7 +3229,16 @@ class Account(
}
}
fun loadLastRead(route: String): Long = lastReadPerRoute[route] ?: 0
fun loadLastRead(route: String): Long = lastReadPerRoute.value[route]?.value ?: 0
fun loadLastReadFlow(route: String): StateFlow<Long> =
lastReadPerRoute.value[route] ?: run {
val newFlow = MutableStateFlow<Long>(0)
lastReadPerRoute.update {
it + Pair(route, newFlow)
}
newFlow
}
fun hasDonatedInThisVersion(): Boolean = hasDonatedInVersion.contains(BuildConfig.VERSION_NAME)

View File

@ -50,7 +50,6 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
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.ui.Alignment
@ -387,8 +386,6 @@ private fun RenderChannel(
loadRobohash: Boolean,
onClick: () -> Unit,
) {
val hasNewMessages = remember { mutableStateOf(false) }
ChannelName(
channelIdHex = item.idHex,
channelPicture = item.profilePicture(),
@ -400,7 +397,7 @@ private fun RenderChannel(
},
channelLastTime = null,
channelLastContent = item.summary(),
hasNewMessages,
hasNewMessages = false,
onClick = onClick,
loadProfilePicture = loadProfilePicture,
loadRobohash = loadRobohash,

View File

@ -33,7 +33,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
@ -54,6 +53,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.map
import com.patrykandpatrick.vico.core.extension.forEachIndexedExtended
@ -171,15 +171,14 @@ private fun ChannelRoomCompose(
.observeAsState()
val authorName = remember(note, authorState) { authorState?.user?.toBestDisplayName() }
val chanHex = remember { channel.idHex }
val channelState by channel.live.observeAsState()
val channelPicture by remember(note, channelState) { derivedStateOf { channel.profilePicture() } }
val channelName by remember(note, channelState) { derivedStateOf { channel.toBestDisplayName() } }
val channelPicture = channelState?.channel?.profilePicture() ?: channel.profilePicture()
val channelName = channelState?.channel?.toBestDisplayName() ?: channel.toBestDisplayName()
val noteEvent = note.event
val route = remember(note) { "Channel/$chanHex" }
val route = "Channel/${channel.idHex}"
val description =
if (noteEvent is ChannelCreateEvent) {
@ -190,21 +189,15 @@ private fun ChannelRoomCompose(
noteEvent?.content()?.take(200)
}
val hasNewMessages = remember { mutableStateOf<Boolean>(false) }
WatchNotificationChanges(note, route, accountViewModel) { newHasNewMessages ->
if (hasNewMessages.value != newHasNewMessages) {
hasNewMessages.value = newHasNewMessages
}
}
val lastReadTime by accountViewModel.account.loadLastReadFlow(route).collectAsStateWithLifecycle()
ChannelName(
channelIdHex = chanHex,
channelIdHex = channel.idHex,
channelPicture = channelPicture,
channelTitle = { modifier -> ChannelTitleWithLabelInfo(channelName, modifier) },
channelLastTime = remember(note) { note.createdAt() },
channelLastContent = remember(note, authorState) { "$authorName: $description" },
hasNewMessages = hasNewMessages,
channelLastTime = note.createdAt(),
channelLastContent = "$authorName: $description",
hasNewMessages = (noteEvent?.createdAt() ?: Long.MIN_VALUE) > lastReadTime,
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
onClick = { nav(route) },
@ -257,17 +250,9 @@ private fun UserRoomCompose(
accountViewModel: AccountViewModel,
nav: (String) -> Unit,
) {
val hasNewMessages = remember { mutableStateOf<Boolean>(false) }
val route = "Room/${room.hashCode()}"
val route = remember(room) { "Room/${room.hashCode()}" }
val createAt by remember(note) { derivedStateOf { note.createdAt() } }
WatchNotificationChanges(note, route, accountViewModel) { newHasNewMessages ->
if (hasNewMessages.value != newHasNewMessages) {
hasNewMessages.value = newHasNewMessages
}
}
val lastReadTime by accountViewModel.account.loadLastReadFlow(route).collectAsStateWithLifecycle()
LoadDecryptedContentOrNull(note, accountViewModel) { content ->
ChannelName(
@ -279,9 +264,9 @@ private fun UserRoomCompose(
)
},
channelTitle = { RoomNameDisplay(room, it, accountViewModel) },
channelLastTime = createAt,
channelLastTime = note.createdAt(),
channelLastContent = content,
hasNewMessages = hasNewMessages,
hasNewMessages = (note.createdAt() ?: Long.MIN_VALUE) > lastReadTime,
onClick = { nav(route) },
)
}
@ -412,20 +397,6 @@ fun ShortUsernameDisplay(
}
}
@Composable
private fun WatchNotificationChanges(
note: Note,
route: String,
accountViewModel: AccountViewModel,
onNewStatus: (Boolean) -> Unit,
) {
LaunchedEffect(key1 = note, accountViewModel.accountMarkAsReadUpdates.intValue) {
note.event?.createdAt()?.let {
onNewStatus(it > accountViewModel.account.loadLastRead(route))
}
}
}
@Composable
fun LoadUser(
baseUserHex: String,
@ -455,7 +426,7 @@ fun ChannelName(
channelTitle: @Composable (Modifier) -> Unit,
channelLastTime: Long?,
channelLastContent: String?,
hasNewMessages: MutableState<Boolean>,
hasNewMessages: Boolean,
loadProfilePicture: Boolean,
loadRobohash: Boolean,
onClick: () -> Unit,
@ -485,7 +456,7 @@ fun ChannelName(
channelTitle: @Composable (Modifier) -> Unit,
channelLastTime: Long?,
channelLastContent: String?,
hasNewMessages: MutableState<Boolean>,
hasNewMessages: Boolean,
onClick: () -> Unit,
) {
ChatHeaderLayout(
@ -514,7 +485,7 @@ fun ChannelName(
)
}
if (hasNewMessages.value) {
if (hasNewMessages) {
NewItemsBubble()
}
},

View File

@ -27,7 +27,6 @@ import android.util.LruCache
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableIntStateOf
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
@ -150,7 +149,6 @@ class AccountViewModel(
Dao {
val accountLiveData: LiveData<AccountState> = account.live.map { it }
val accountLanguagesLiveData: LiveData<AccountState> = account.liveLanguages.map { it }
val accountMarkAsReadUpdates = mutableIntStateOf(0)
// TODO: contact lists are not notes yet
// val kind3Relays: StateFlow<ContactListEvent?> = observeByAuthor(ContactListEvent.KIND, account.signer.pubKey)
@ -1121,7 +1119,6 @@ class AccountViewModel(
suspend fun refreshMarkAsReadObservers() {
updateNotificationDots()
accountMarkAsReadUpdates.value++
}
fun loadAndMarkAsRead(

View File

@ -380,8 +380,6 @@ private fun DisplaySearchResults(
val channels by searchBarViewModel.searchResultsChannels.collectAsStateWithLifecycle()
val notes by searchBarViewModel.searchResultsNotes.collectAsStateWithLifecycle()
val hasNewMessages = remember { mutableStateOf(false) }
LazyColumn(
modifier = Modifier.fillMaxHeight(),
contentPadding = FeedPadding,
@ -425,7 +423,7 @@ private fun DisplaySearchResults(
},
channelLastTime = null,
channelLastContent = item.summary(),
hasNewMessages = hasNewMessages,
hasNewMessages = false,
loadProfilePicture = accountViewModel.settings.showProfilePictures.value,
loadRobohash = accountViewModel.settings.featureSet != FeatureSetType.PERFORMANCE,
onClick = { nav("Channel/${item.idHex}") },