Making sure ViewModels are deleted when switching accounts.
This commit is contained in:
@@ -81,7 +81,7 @@ import kotlinx.coroutines.withContext
|
||||
@Composable
|
||||
fun JoinUserOrChannelView(onClose: () -> Unit, accountViewModel: AccountViewModel, nav: (String) -> Unit) {
|
||||
val searchBarViewModel: SearchBarViewModel = viewModel(
|
||||
key = accountViewModel.account.userProfile().pubkeyHex + "SearchBarViewModel",
|
||||
key = "SearchBarViewModel",
|
||||
factory = SearchBarViewModel.Factory(
|
||||
accountViewModel.account
|
||||
)
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.vitorpamplona.amethyst.ui.actions
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
@@ -631,6 +632,7 @@ open class NewPostViewModel() : ViewModel() {
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
Log.d("Init", "OnCleared: ${this.javaClass.simpleName}")
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
locUtil?.stop()
|
||||
}
|
||||
|
||||
@@ -667,6 +667,7 @@ class FollowListViewModel(val account: Account) : ViewModel() {
|
||||
|
||||
override fun onCleared() {
|
||||
collectorJob?.cancel()
|
||||
Log.d("Init", "OnCleared: ${this.javaClass.simpleName}")
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
|
||||
@@ -81,9 +81,7 @@ fun PollNote(
|
||||
accountViewModel: AccountViewModel,
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
val pollViewModel: PollNoteViewModel = viewModel(
|
||||
key = baseNote.idHex + "PollNoteViewModel"
|
||||
)
|
||||
val pollViewModel: PollNoteViewModel = viewModel(key = "PollNoteViewModel")
|
||||
|
||||
pollViewModel.load(accountViewModel.account, baseNote)
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ fun UpdateReactionTypeDialog(
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
val postViewModel: UpdateReactionTypeViewModel = viewModel(
|
||||
key = accountViewModel.userProfile().pubkeyHex,
|
||||
key = "UpdateReactionTypeViewModel",
|
||||
factory = UpdateReactionTypeViewModel.Factory(accountViewModel.account)
|
||||
)
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ fun UpdateZapAmountDialog(
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val postViewModel: UpdateZapAmountViewModel = viewModel(
|
||||
key = accountViewModel.userProfile().pubkeyHex,
|
||||
key = "UpdateZapAmountViewModel",
|
||||
factory = UpdateZapAmountViewModel.Factory(accountViewModel.account)
|
||||
)
|
||||
|
||||
|
||||
@@ -366,6 +366,7 @@ class UserReactionsViewModel(val account: Account) : ViewModel() {
|
||||
override fun onCleared() {
|
||||
collectorJob?.cancel()
|
||||
bundlerInsert.cancel()
|
||||
Log.d("Init", "OnCleared: ${this.javaClass.simpleName}")
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
|
||||
@@ -8,13 +8,18 @@ import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.ViewModelStore
|
||||
import androidx.lifecycle.ViewModelStoreOwner
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.model.Account
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.MainScreen
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedOff.LoginPage
|
||||
@@ -40,26 +45,53 @@ fun AccountScreen(
|
||||
LoginPage(accountStateViewModel, isFirstLogin = true)
|
||||
}
|
||||
is AccountState.LoggedIn -> {
|
||||
val accountViewModel: AccountViewModel = viewModel(
|
||||
key = state.account.hashCode().toString(),
|
||||
factory = AccountViewModel.Factory(state.account, sharedPreferencesViewModel.sharedPrefs)
|
||||
)
|
||||
|
||||
MainScreen(accountViewModel, accountStateViewModel, sharedPreferencesViewModel)
|
||||
CompositionLocalProvider(
|
||||
LocalViewModelStoreOwner provides state.currentViewModelStore
|
||||
) {
|
||||
LoggedInPage(
|
||||
state.account,
|
||||
accountStateViewModel,
|
||||
sharedPreferencesViewModel
|
||||
)
|
||||
}
|
||||
}
|
||||
is AccountState.LoggedInViewOnly -> {
|
||||
val accountViewModel: AccountViewModel = viewModel(
|
||||
key = state.account.hashCode().toString(),
|
||||
factory = AccountViewModel.Factory(state.account, sharedPreferencesViewModel.sharedPrefs)
|
||||
)
|
||||
|
||||
MainScreen(accountViewModel, accountStateViewModel, sharedPreferencesViewModel)
|
||||
CompositionLocalProvider(
|
||||
LocalViewModelStoreOwner provides state.currentViewModelStore
|
||||
) {
|
||||
LoggedInPage(
|
||||
state.account,
|
||||
accountStateViewModel,
|
||||
sharedPreferencesViewModel
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LoggedInPage(
|
||||
account: Account,
|
||||
accountStateViewModel: AccountStateViewModel,
|
||||
sharedPreferencesViewModel: SharedPreferencesViewModel
|
||||
) {
|
||||
val accountViewModel: AccountViewModel = viewModel(
|
||||
key = "AccountStateViewModel",
|
||||
factory = AccountViewModel.Factory(
|
||||
account,
|
||||
sharedPreferencesViewModel.sharedPrefs
|
||||
)
|
||||
)
|
||||
|
||||
MainScreen(accountViewModel, accountStateViewModel, sharedPreferencesViewModel)
|
||||
}
|
||||
|
||||
class AccountCentricViewModelStore(val account: Account) : ViewModelStoreOwner {
|
||||
override val viewModelStore = ViewModelStore()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LoadingAccounts() {
|
||||
Column(
|
||||
|
||||
@@ -5,6 +5,10 @@ import com.vitorpamplona.amethyst.model.Account
|
||||
sealed class AccountState {
|
||||
object Loading : AccountState()
|
||||
object LoggedOff : AccountState()
|
||||
class LoggedInViewOnly(val account: Account) : AccountState()
|
||||
class LoggedIn(val account: Account) : AccountState()
|
||||
class LoggedInViewOnly(val account: Account) : AccountState() {
|
||||
val currentViewModelStore = AccountCentricViewModelStore(account)
|
||||
}
|
||||
class LoggedIn(val account: Account) : AccountState() {
|
||||
val currentViewModelStore = AccountCentricViewModelStore(account)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,9 +103,15 @@ class AccountStateViewModel() : ViewModel() {
|
||||
when (val state = _accountContent.value) {
|
||||
is AccountState.LoggedIn -> {
|
||||
state.account.saveable.removeObserver(saveListener)
|
||||
withContext(Dispatchers.IO) {
|
||||
state.currentViewModelStore.viewModelStore.clear()
|
||||
}
|
||||
}
|
||||
is AccountState.LoggedInViewOnly -> {
|
||||
state.account.saveable.removeObserver(saveListener)
|
||||
withContext(Dispatchers.IO) {
|
||||
state.currentViewModelStore.viewModelStore.clear()
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
@@ -370,6 +370,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel() {
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
Log.d("Init", "OnCleared: ${this.javaClass.simpleName}")
|
||||
clear()
|
||||
bundlerInsert.cancel()
|
||||
bundler.cancel()
|
||||
|
||||
@@ -322,7 +322,7 @@ abstract class FeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), I
|
||||
private var collectorJob: Job? = null
|
||||
|
||||
init {
|
||||
Log.d("Init", "${this.javaClass.simpleName}")
|
||||
Log.d("Init", "Starting new Model: ${this.javaClass.simpleName}")
|
||||
collectorJob = viewModelScope.launch(Dispatchers.IO) {
|
||||
LocalCache.live.newEventBundles.collect { newNotes ->
|
||||
checkNotInMainThread()
|
||||
@@ -340,6 +340,7 @@ abstract class FeedViewModel(val localFilter: FeedFilter<Note>) : ViewModel(), I
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
Log.d("Init", "OnCleared: ${this.javaClass.simpleName}")
|
||||
bundlerInsert.cancel()
|
||||
bundler.cancel()
|
||||
collectorJob?.cancel()
|
||||
|
||||
@@ -95,6 +95,7 @@ open class LnZapFeedViewModel(val dataSource: FeedFilter<ZapReqResponse>) : View
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
Log.d("Init", "OnCleared: ${this.javaClass.simpleName}")
|
||||
bundler.cancel()
|
||||
collectorJob?.cancel()
|
||||
super.onCleared()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
@@ -96,6 +97,7 @@ class RelayFeedViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
Log.d("Init", "OnCleared: ${this.javaClass.simpleName}")
|
||||
bundler.cancel()
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@ open class StringFeedViewModel(val dataSource: FeedFilter<String>) : ViewModel()
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
Log.d("Init", "OnCleared: ${this.javaClass.simpleName}")
|
||||
bundler.cancel()
|
||||
collectorJob?.cancel()
|
||||
super.onCleared()
|
||||
|
||||
@@ -126,6 +126,7 @@ open class UserFeedViewModel(val dataSource: FeedFilter<User>) : ViewModel(), In
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
Log.d("Init", "OnCleared: ${this.javaClass.simpleName}")
|
||||
bundler.cancel()
|
||||
collectorJob?.cancel()
|
||||
super.onCleared()
|
||||
|
||||
+16
-10
@@ -792,7 +792,7 @@ class AccountViewModel(val account: Account, val settings: SettingsState) : View
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshMarkAsReadObservers() {
|
||||
suspend fun refreshMarkAsReadObservers() {
|
||||
updateNotificationDots()
|
||||
accountMarkAsReadUpdates.value++
|
||||
}
|
||||
@@ -862,25 +862,26 @@ class AccountViewModel(val account: Account, val settings: SettingsState) : View
|
||||
private var collectorJob: Job? = null
|
||||
val notificationDots = HasNotificationDot(bottomNavigationItems, account)
|
||||
|
||||
fun updateNotificationDots(newNotes: Set<Note> = emptySet()) {
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
val (value, elapsed) = measureTimedValue {
|
||||
notificationDots.update(newNotes)
|
||||
}
|
||||
Log.d("Rendering Metrics", "Notification Dots Calculation in $elapsed for ${newNotes.size} new notes")
|
||||
suspend fun updateNotificationDots(newNotes: Set<Note> = emptySet()) {
|
||||
val (value, elapsed) = measureTimedValue {
|
||||
notificationDots.update(newNotes)
|
||||
}
|
||||
Log.d("Rendering Metrics", "Notification Dots Calculation in $elapsed for ${newNotes.size} new notes")
|
||||
}
|
||||
|
||||
init {
|
||||
Log.d("Init", "AccountViewModel")
|
||||
collectorJob = viewModelScope.launch(Dispatchers.IO) {
|
||||
LocalCache.live.newEventBundles.collect { newNotes ->
|
||||
Log.d("Rendering Metrics", "Notification Dots Calculation refresh ${this@AccountViewModel}")
|
||||
|
||||
updateNotificationDots(newNotes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
Log.d("Init", "AccountViewModel onCleared")
|
||||
collectorJob?.cancel()
|
||||
super.onCleared()
|
||||
}
|
||||
@@ -920,11 +921,16 @@ class HasNotificationDot(bottomNavigationItems: ImmutableList<Route>, val accoun
|
||||
val hasNewItems = bottomNavigationItems.associateWith { MutableStateFlow(false) }
|
||||
|
||||
fun update(newNotes: Set<Note>) {
|
||||
checkNotInMainThread()
|
||||
|
||||
hasNewItems.forEach {
|
||||
val newResult = it.key.hasNewItems(account, newNotes)
|
||||
if (newResult != it.value.value) {
|
||||
it.value.value = newResult
|
||||
val (value, elapsed) = measureTimedValue {
|
||||
val newResult = it.key.hasNewItems(account, newNotes)
|
||||
if (newResult != it.value.value) {
|
||||
it.value.value = newResult
|
||||
}
|
||||
}
|
||||
Log.d("Rendering Metrics", "Notification Dots Calculation for ${it.key.route} in $elapsed for ${newNotes.size} new notes")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,58 +141,58 @@ fun MainScreen(
|
||||
}
|
||||
|
||||
val followLists: FollowListViewModel = viewModel(
|
||||
key = accountViewModel.userProfile().pubkeyHex + "FollowListViewModel",
|
||||
key = "FollowListViewModel",
|
||||
factory = FollowListViewModel.Factory(accountViewModel.account)
|
||||
)
|
||||
|
||||
// Avoids creating ViewModels for performance reasons (up to 1 second delays)
|
||||
val homeFeedViewModel: NostrHomeFeedViewModel = viewModel(
|
||||
key = accountViewModel.userProfile().pubkeyHex + "NostrHomeFeedViewModel",
|
||||
key = "NostrHomeFeedViewModel",
|
||||
factory = NostrHomeFeedViewModel.Factory(accountViewModel.account)
|
||||
)
|
||||
|
||||
val repliesFeedViewModel: NostrHomeRepliesFeedViewModel = viewModel(
|
||||
key = accountViewModel.userProfile().pubkeyHex + "NostrHomeRepliesFeedViewModel",
|
||||
key = "NostrHomeRepliesFeedViewModel",
|
||||
factory = NostrHomeRepliesFeedViewModel.Factory(accountViewModel.account)
|
||||
)
|
||||
|
||||
val videoFeedViewModel: NostrVideoFeedViewModel = viewModel(
|
||||
key = accountViewModel.userProfile().pubkeyHex + "NostrVideoFeedViewModel",
|
||||
key = "NostrVideoFeedViewModel",
|
||||
factory = NostrVideoFeedViewModel.Factory(accountViewModel.account)
|
||||
)
|
||||
|
||||
val discoveryLiveFeedViewModel: NostrDiscoverLiveFeedViewModel = viewModel(
|
||||
key = accountViewModel.userProfile().pubkeyHex + "NostrDiscoveryLiveFeedViewModel",
|
||||
key = "NostrDiscoveryLiveFeedViewModel",
|
||||
factory = NostrDiscoverLiveFeedViewModel.Factory(accountViewModel.account)
|
||||
)
|
||||
|
||||
val discoveryCommunityFeedViewModel: NostrDiscoverCommunityFeedViewModel = viewModel(
|
||||
key = accountViewModel.userProfile().pubkeyHex + "NostrDiscoveryCommunityFeedViewModel",
|
||||
key = "NostrDiscoveryCommunityFeedViewModel",
|
||||
factory = NostrDiscoverCommunityFeedViewModel.Factory(accountViewModel.account)
|
||||
)
|
||||
|
||||
val discoveryChatFeedViewModel: NostrDiscoverChatFeedViewModel = viewModel(
|
||||
key = accountViewModel.userProfile().pubkeyHex + "NostrDiscoveryChatFeedViewModel",
|
||||
key = "NostrDiscoveryChatFeedViewModel",
|
||||
factory = NostrDiscoverChatFeedViewModel.Factory(accountViewModel.account)
|
||||
)
|
||||
|
||||
val notifFeedViewModel: NotificationViewModel = viewModel(
|
||||
key = accountViewModel.userProfile().pubkeyHex + "NotificationViewModel",
|
||||
key = "NotificationViewModel",
|
||||
factory = NotificationViewModel.Factory(accountViewModel.account)
|
||||
)
|
||||
|
||||
val userReactionsStatsModel: UserReactionsViewModel = viewModel(
|
||||
key = accountViewModel.userProfile().pubkeyHex + "UserReactionsViewModel",
|
||||
key = "UserReactionsViewModel",
|
||||
factory = UserReactionsViewModel.Factory(accountViewModel.account)
|
||||
)
|
||||
|
||||
val knownFeedViewModel: NostrChatroomListKnownFeedViewModel = viewModel(
|
||||
key = accountViewModel.userProfile().pubkeyHex + "NostrChatroomListKnownFeedViewModel",
|
||||
key = "NostrChatroomListKnownFeedViewModel",
|
||||
factory = NostrChatroomListKnownFeedViewModel.Factory(accountViewModel.account)
|
||||
)
|
||||
|
||||
val newFeedViewModel: NostrChatroomListNewFeedViewModel = viewModel(
|
||||
key = accountViewModel.userProfile().pubkeyHex + "NostrChatroomListNewFeedViewModel",
|
||||
key = "NostrChatroomListNewFeedViewModel",
|
||||
factory = NostrChatroomListNewFeedViewModel.Factory(accountViewModel.account)
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -86,7 +87,7 @@ fun SearchScreen(
|
||||
nav: (String) -> Unit
|
||||
) {
|
||||
val searchBarViewModel: SearchBarViewModel = viewModel(
|
||||
key = accountViewModel.account.userProfile().pubkeyHex + "SearchBarViewModel",
|
||||
key = "SearchBarViewModel",
|
||||
factory = SearchBarViewModel.Factory(
|
||||
accountViewModel.account
|
||||
)
|
||||
@@ -198,6 +199,7 @@ class SearchBarViewModel(val account: Account) : ViewModel() {
|
||||
|
||||
override fun onCleared() {
|
||||
bundler.cancel()
|
||||
Log.d("Init", "OnCleared: ${this.javaClass.simpleName}")
|
||||
super.onCleared()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user