Bind account early in timeline & notification view models

This will make it less likely that data from different accounts will
get mixed up
This commit is contained in:
ivk 2024-07-13 18:18:53 +02:00 committed by charlag
parent bd09b197f0
commit 594204bdfd
No known key found for this signature in database
GPG Key ID: 5B96E7C76F0CA558
2 changed files with 55 additions and 67 deletions

View File

@ -46,7 +46,6 @@ import com.keylesspalace.tusky.network.FilterModel
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.settings.PrefKeys
import com.keylesspalace.tusky.usecase.TimelineCases
import com.keylesspalace.tusky.util.EmptyPagingSource
import com.keylesspalace.tusky.util.deserialize
import com.keylesspalace.tusky.util.serialize
import com.keylesspalace.tusky.viewdata.NotificationViewData
@ -87,6 +86,8 @@ class NotificationsViewModel @Inject constructor(
/** Map from notification id to translation. */
private val translations = MutableStateFlow(mapOf<String, TranslationViewData>())
private val account = accountManager.activeAccount!!
private var remoteMediator = NotificationsRemoteMediator(accountManager, api, db, excludes.value)
private var readingOrder: ReadingOrder =
@ -100,12 +101,7 @@ class NotificationsViewModel @Inject constructor(
),
remoteMediator = remoteMediator,
pagingSourceFactory = {
val activeAccount = accountManager.activeAccount
if (activeAccount == null) {
EmptyPagingSource()
} else {
db.notificationsDao().getNotifications(activeAccount.id)
}
db.notificationsDao().getNotifications(account.id)
}
).flow
.cachedIn(viewModelScope)
@ -142,16 +138,13 @@ class NotificationsViewModel @Inject constructor(
fun updateNotificationFilters(newFilters: Set<Notification.Type>) {
if (newFilters != _excludes.value) {
val account = accountManager.activeAccount
if (account != null) {
viewModelScope.launch {
account.notificationsFilter = serialize(newFilters)
accountManager.saveAccount(account)
remoteMediator.excludes = newFilters
db.notificationsDao().cleanupNotifications(account.id, 0)
refreshTrigger.value++
_excludes.value = newFilters
}
viewModelScope.launch {
account.notificationsFilter = serialize(newFilters)
accountManager.saveAccount(account)
remoteMediator.excludes = newFilters
db.notificationsDao().cleanupNotifications(account.id, 0)
refreshTrigger.value++
_excludes.value = newFilters
}
}
}
@ -165,6 +158,7 @@ class NotificationsViewModel @Inject constructor(
}
Filter.Action.NONE
}
else -> Filter.Action.NONE
}
}
@ -178,7 +172,7 @@ class NotificationsViewModel @Inject constructor(
}.fold(
onSuccess = {
// since the follow request has been responded, the notification can be deleted. The Ui will update automatically.
db.notificationsDao().delete(accountManager.activeAccount!!.id, notificationId)
db.notificationsDao().delete(account.id, notificationId)
if (accept) {
// refresh the notifications so the new follow notification will be loaded
refreshTrigger.value++
@ -230,33 +224,33 @@ class NotificationsViewModel @Inject constructor(
fun changeExpanded(expanded: Boolean, status: StatusViewData.Concrete) {
viewModelScope.launch {
db.timelineStatusDao()
.setExpanded(accountManager.activeAccount!!.id, status.id, expanded)
.setExpanded(account.id, status.id, expanded)
}
}
fun changeContentShowing(isShowing: Boolean, status: StatusViewData.Concrete) {
viewModelScope.launch {
db.timelineStatusDao()
.setContentShowing(accountManager.activeAccount!!.id, status.id, isShowing)
.setContentShowing(account.id, status.id, isShowing)
}
}
fun changeContentCollapsed(isCollapsed: Boolean, status: StatusViewData.Concrete) {
viewModelScope.launch {
db.timelineStatusDao()
.setContentCollapsed(accountManager.activeAccount!!.id, status.id, isCollapsed)
.setContentCollapsed(account.id, status.id, isCollapsed)
}
}
fun remove(notificationId: String) {
viewModelScope.launch {
db.notificationsDao().delete(accountManager.activeAccount!!.id, notificationId)
db.notificationsDao().delete(account.id, notificationId)
}
}
fun clearWarning(status: StatusViewData.Concrete) {
viewModelScope.launch {
db.timelineStatusDao().clearWarning(accountManager.activeAccount!!.id, status.actionableId)
db.timelineStatusDao().clearWarning(account.id, status.actionableId)
}
}
@ -264,7 +258,7 @@ class NotificationsViewModel @Inject constructor(
viewModelScope.launch {
api.clearNotifications().fold(
{
db.notificationsDao().cleanupNotifications(accountManager.activeAccount!!.id, 0)
db.notificationsDao().cleanupNotifications(account.id, 0)
},
{ t ->
Log.w(TAG, "failed to clear notifications", t)
@ -293,17 +287,16 @@ class NotificationsViewModel @Inject constructor(
try {
val notificationsDao = db.notificationsDao()
val activeAccount = accountManager.activeAccount!!
notificationsDao.insertNotification(
Placeholder(placeholderId, loading = true).toNotificationEntity(
activeAccount.id
account.id
)
)
val response = db.withTransaction {
val idAbovePlaceholder = notificationsDao.getIdAbove(activeAccount.id, placeholderId)
val idBelowPlaceholder = notificationsDao.getIdBelow(activeAccount.id, placeholderId)
val idAbovePlaceholder = notificationsDao.getIdAbove(account.id, placeholderId)
val idBelowPlaceholder = notificationsDao.getIdBelow(account.id, placeholderId)
when (readingOrder) {
// Using minId, loads up to LOAD_AT_ONCE statuses with IDs immediately
// after minId and no larger than maxId
@ -334,11 +327,11 @@ class NotificationsViewModel @Inject constructor(
val accountDao = db.timelineAccountDao()
db.withTransaction {
notificationsDao.delete(activeAccount.id, placeholderId)
notificationsDao.delete(account.id, placeholderId)
val overlappedNotifications = if (notifications.isNotEmpty()) {
notificationsDao.deleteRange(
activeAccount.id,
account.id,
notifications.last().id,
notifications.first().id
)
@ -347,26 +340,28 @@ class NotificationsViewModel @Inject constructor(
}
for (notification in notifications) {
accountDao.insert(notification.account.toEntity(activeAccount.id))
accountDao.insert(notification.account.toEntity(account.id))
notification.report?.let { report ->
accountDao.insert(report.targetAccount.toEntity(activeAccount.id))
notificationsDao.insertReport(report.toEntity(activeAccount.id))
accountDao.insert(report.targetAccount.toEntity(account.id))
notificationsDao.insertReport(report.toEntity(account.id))
}
notification.status?.let { status ->
accountDao.insert(status.account.toEntity(activeAccount.id))
accountDao.insert(status.account.toEntity(account.id))
// get updated account in case some prefs have changed
val account = accountManager.getAccountById(this@NotificationsViewModel.account.id) ?: this@NotificationsViewModel.account
statusDao.insert(
status.toEntity(
tuskyAccountId = activeAccount.id,
expanded = activeAccount.alwaysOpenSpoiler,
contentShowing = activeAccount.alwaysShowSensitiveMedia || !status.sensitive,
tuskyAccountId = account.id,
expanded = account.alwaysOpenSpoiler,
contentShowing = account.alwaysShowSensitiveMedia || !status.sensitive,
contentCollapsed = true
)
)
}
notificationsDao.insertNotification(
notification.toEntity(
activeAccount.id
account.id
)
)
}
@ -385,7 +380,7 @@ class NotificationsViewModel @Inject constructor(
Placeholder(
idToConvert,
loading = false
).toNotificationEntity(activeAccount.id)
).toNotificationEntity(account.id)
)
}
}

View File

@ -45,7 +45,6 @@ import com.keylesspalace.tusky.entity.Poll
import com.keylesspalace.tusky.network.FilterModel
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.usecase.TimelineCases
import com.keylesspalace.tusky.util.EmptyPagingSource
import com.keylesspalace.tusky.viewdata.StatusViewData
import com.keylesspalace.tusky.viewdata.TranslationViewData
import dagger.hilt.android.lifecycle.HiltViewModel
@ -78,6 +77,8 @@ class CachedTimelineViewModel @Inject constructor(
filterModel
) {
private val account = accountManager.activeAccount!!
private var currentPagingSource: PagingSource<Int, HomeTimelineData>? = null
/** Map from status id to translation. */
@ -90,12 +91,7 @@ class CachedTimelineViewModel @Inject constructor(
),
remoteMediator = CachedTimelineRemoteMediator(accountManager, api, db),
pagingSourceFactory = {
val activeAccount = accountManager.activeAccount
if (activeAccount == null) {
EmptyPagingSource()
} else {
db.timelineDao().getHomeTimeline(activeAccount.id)
}.also { newPagingSource ->
db.timelineDao().getHomeTimeline(account.id).also { newPagingSource ->
this.currentPagingSource = newPagingSource
}
}
@ -125,27 +121,27 @@ class CachedTimelineViewModel @Inject constructor(
override fun changeExpanded(expanded: Boolean, status: StatusViewData.Concrete) {
viewModelScope.launch {
db.timelineStatusDao()
.setExpanded(accountManager.activeAccount!!.id, status.actionableId, expanded)
.setExpanded(account.id, status.actionableId, expanded)
}
}
override fun changeContentShowing(isShowing: Boolean, status: StatusViewData.Concrete) {
viewModelScope.launch {
db.timelineStatusDao()
.setContentShowing(accountManager.activeAccount!!.id, status.actionableId, isShowing)
.setContentShowing(account.id, status.actionableId, isShowing)
}
}
override fun changeContentCollapsed(isCollapsed: Boolean, status: StatusViewData.Concrete) {
viewModelScope.launch {
db.timelineStatusDao()
.setContentCollapsed(accountManager.activeAccount!!.id, status.actionableId, isCollapsed)
.setContentCollapsed(account.id, status.actionableId, isCollapsed)
}
}
override fun clearWarning(status: StatusViewData.Concrete) {
viewModelScope.launch {
db.timelineStatusDao().clearWarning(accountManager.activeAccount!!.id, status.actionableId)
db.timelineStatusDao().clearWarning(account.id, status.actionableId)
}
}
@ -160,17 +156,13 @@ class CachedTimelineViewModel @Inject constructor(
val statusDao = db.timelineStatusDao()
val accountDao = db.timelineAccountDao()
val activeAccount = accountManager.activeAccount!!
timelineDao.insertHomeTimelineItem(
Placeholder(placeholderId, loading = true).toEntity(
activeAccount.id
)
Placeholder(placeholderId, loading = true).toEntity(tuskyAccountId = account.id)
)
val response = db.withTransaction {
val idAbovePlaceholder = timelineDao.getIdAbove(activeAccount.id, placeholderId)
val idBelowPlaceholder = timelineDao.getIdBelow(activeAccount.id, placeholderId)
val idAbovePlaceholder = timelineDao.getIdAbove(account.id, placeholderId)
val idBelowPlaceholder = timelineDao.getIdBelow(account.id, placeholderId)
when (readingOrder) {
// Using minId, loads up to LOAD_AT_ONCE statuses with IDs immediately
// after minId and no larger than maxId
@ -196,11 +188,11 @@ class CachedTimelineViewModel @Inject constructor(
}
db.withTransaction {
timelineDao.deleteHomeTimelineItem(activeAccount.id, placeholderId)
timelineDao.deleteHomeTimelineItem(account.id, placeholderId)
val overlappedStatuses = if (statuses.isNotEmpty()) {
timelineDao.deleteRange(
activeAccount.id,
account.id,
statuses.last().id,
statuses.first().id
)
@ -209,22 +201,23 @@ class CachedTimelineViewModel @Inject constructor(
}
for (status in statuses) {
accountDao.insert(status.account.toEntity(activeAccount.id))
status.reblog?.account?.toEntity(activeAccount.id)
accountDao.insert(status.account.toEntity(account.id))
status.reblog?.account?.toEntity(account.id)
?.let { rebloggedAccount ->
accountDao.insert(rebloggedAccount)
}
val account = accountManager.getAccountById(account.id) ?: return@withTransaction
statusDao.insert(
status.actionableStatus.toEntity(
tuskyAccountId = activeAccount.id,
expanded = activeAccount.alwaysOpenSpoiler,
contentShowing = activeAccount.alwaysShowSensitiveMedia || !status.actionableStatus.sensitive,
tuskyAccountId = account.id,
expanded = account.alwaysOpenSpoiler,
contentShowing = account.alwaysShowSensitiveMedia || !status.actionableStatus.sensitive,
contentCollapsed = true
)
)
timelineDao.insertHomeTimelineItem(
HomeTimelineEntity(
tuskyAccountId = activeAccount.id,
tuskyAccountId = account.id,
id = status.id,
statusId = status.actionableId,
reblogAccountId = if (status.reblog != null) {
@ -250,7 +243,7 @@ class CachedTimelineViewModel @Inject constructor(
Placeholder(
idToConvert,
loading = false
).toEntity(activeAccount.id)
).toEntity(account.id)
)
}
}
@ -286,7 +279,7 @@ class CachedTimelineViewModel @Inject constructor(
override suspend fun invalidate() {
// invalidating when we don't have statuses yet can cause empty timelines because it cancels the network load
if (db.timelineDao().getHomeTimelineItemCount(accountManager.activeAccount!!.id) > 0) {
if (db.timelineDao().getHomeTimelineItemCount(account.id) > 0) {
currentPagingSource?.invalidate()
}
}