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:
parent
bd09b197f0
commit
594204bdfd
|
@ -46,7 +46,6 @@ import com.keylesspalace.tusky.network.FilterModel
|
||||||
import com.keylesspalace.tusky.network.MastodonApi
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
import com.keylesspalace.tusky.settings.PrefKeys
|
import com.keylesspalace.tusky.settings.PrefKeys
|
||||||
import com.keylesspalace.tusky.usecase.TimelineCases
|
import com.keylesspalace.tusky.usecase.TimelineCases
|
||||||
import com.keylesspalace.tusky.util.EmptyPagingSource
|
|
||||||
import com.keylesspalace.tusky.util.deserialize
|
import com.keylesspalace.tusky.util.deserialize
|
||||||
import com.keylesspalace.tusky.util.serialize
|
import com.keylesspalace.tusky.util.serialize
|
||||||
import com.keylesspalace.tusky.viewdata.NotificationViewData
|
import com.keylesspalace.tusky.viewdata.NotificationViewData
|
||||||
|
@ -87,6 +86,8 @@ class NotificationsViewModel @Inject constructor(
|
||||||
/** Map from notification id to translation. */
|
/** Map from notification id to translation. */
|
||||||
private val translations = MutableStateFlow(mapOf<String, TranslationViewData>())
|
private val translations = MutableStateFlow(mapOf<String, TranslationViewData>())
|
||||||
|
|
||||||
|
private val account = accountManager.activeAccount!!
|
||||||
|
|
||||||
private var remoteMediator = NotificationsRemoteMediator(accountManager, api, db, excludes.value)
|
private var remoteMediator = NotificationsRemoteMediator(accountManager, api, db, excludes.value)
|
||||||
|
|
||||||
private var readingOrder: ReadingOrder =
|
private var readingOrder: ReadingOrder =
|
||||||
|
@ -100,12 +101,7 @@ class NotificationsViewModel @Inject constructor(
|
||||||
),
|
),
|
||||||
remoteMediator = remoteMediator,
|
remoteMediator = remoteMediator,
|
||||||
pagingSourceFactory = {
|
pagingSourceFactory = {
|
||||||
val activeAccount = accountManager.activeAccount
|
db.notificationsDao().getNotifications(account.id)
|
||||||
if (activeAccount == null) {
|
|
||||||
EmptyPagingSource()
|
|
||||||
} else {
|
|
||||||
db.notificationsDao().getNotifications(activeAccount.id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
).flow
|
).flow
|
||||||
.cachedIn(viewModelScope)
|
.cachedIn(viewModelScope)
|
||||||
|
@ -142,8 +138,6 @@ class NotificationsViewModel @Inject constructor(
|
||||||
|
|
||||||
fun updateNotificationFilters(newFilters: Set<Notification.Type>) {
|
fun updateNotificationFilters(newFilters: Set<Notification.Type>) {
|
||||||
if (newFilters != _excludes.value) {
|
if (newFilters != _excludes.value) {
|
||||||
val account = accountManager.activeAccount
|
|
||||||
if (account != null) {
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
account.notificationsFilter = serialize(newFilters)
|
account.notificationsFilter = serialize(newFilters)
|
||||||
accountManager.saveAccount(account)
|
accountManager.saveAccount(account)
|
||||||
|
@ -154,7 +148,6 @@ class NotificationsViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun shouldFilterStatus(notificationViewData: NotificationViewData): Filter.Action {
|
private fun shouldFilterStatus(notificationViewData: NotificationViewData): Filter.Action {
|
||||||
return when ((notificationViewData as? NotificationViewData.Concrete)?.type) {
|
return when ((notificationViewData as? NotificationViewData.Concrete)?.type) {
|
||||||
|
@ -165,6 +158,7 @@ class NotificationsViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
Filter.Action.NONE
|
Filter.Action.NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> Filter.Action.NONE
|
else -> Filter.Action.NONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,7 +172,7 @@ class NotificationsViewModel @Inject constructor(
|
||||||
}.fold(
|
}.fold(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
// since the follow request has been responded, the notification can be deleted. The Ui will update automatically.
|
// 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) {
|
if (accept) {
|
||||||
// refresh the notifications so the new follow notification will be loaded
|
// refresh the notifications so the new follow notification will be loaded
|
||||||
refreshTrigger.value++
|
refreshTrigger.value++
|
||||||
|
@ -230,33 +224,33 @@ class NotificationsViewModel @Inject constructor(
|
||||||
fun changeExpanded(expanded: Boolean, status: StatusViewData.Concrete) {
|
fun changeExpanded(expanded: Boolean, status: StatusViewData.Concrete) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
db.timelineStatusDao()
|
db.timelineStatusDao()
|
||||||
.setExpanded(accountManager.activeAccount!!.id, status.id, expanded)
|
.setExpanded(account.id, status.id, expanded)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changeContentShowing(isShowing: Boolean, status: StatusViewData.Concrete) {
|
fun changeContentShowing(isShowing: Boolean, status: StatusViewData.Concrete) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
db.timelineStatusDao()
|
db.timelineStatusDao()
|
||||||
.setContentShowing(accountManager.activeAccount!!.id, status.id, isShowing)
|
.setContentShowing(account.id, status.id, isShowing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changeContentCollapsed(isCollapsed: Boolean, status: StatusViewData.Concrete) {
|
fun changeContentCollapsed(isCollapsed: Boolean, status: StatusViewData.Concrete) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
db.timelineStatusDao()
|
db.timelineStatusDao()
|
||||||
.setContentCollapsed(accountManager.activeAccount!!.id, status.id, isCollapsed)
|
.setContentCollapsed(account.id, status.id, isCollapsed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun remove(notificationId: String) {
|
fun remove(notificationId: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
db.notificationsDao().delete(accountManager.activeAccount!!.id, notificationId)
|
db.notificationsDao().delete(account.id, notificationId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearWarning(status: StatusViewData.Concrete) {
|
fun clearWarning(status: StatusViewData.Concrete) {
|
||||||
viewModelScope.launch {
|
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 {
|
viewModelScope.launch {
|
||||||
api.clearNotifications().fold(
|
api.clearNotifications().fold(
|
||||||
{
|
{
|
||||||
db.notificationsDao().cleanupNotifications(accountManager.activeAccount!!.id, 0)
|
db.notificationsDao().cleanupNotifications(account.id, 0)
|
||||||
},
|
},
|
||||||
{ t ->
|
{ t ->
|
||||||
Log.w(TAG, "failed to clear notifications", t)
|
Log.w(TAG, "failed to clear notifications", t)
|
||||||
|
@ -293,17 +287,16 @@ class NotificationsViewModel @Inject constructor(
|
||||||
try {
|
try {
|
||||||
val notificationsDao = db.notificationsDao()
|
val notificationsDao = db.notificationsDao()
|
||||||
|
|
||||||
val activeAccount = accountManager.activeAccount!!
|
|
||||||
|
|
||||||
notificationsDao.insertNotification(
|
notificationsDao.insertNotification(
|
||||||
Placeholder(placeholderId, loading = true).toNotificationEntity(
|
Placeholder(placeholderId, loading = true).toNotificationEntity(
|
||||||
activeAccount.id
|
account.id
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val response = db.withTransaction {
|
val response = db.withTransaction {
|
||||||
val idAbovePlaceholder = notificationsDao.getIdAbove(activeAccount.id, placeholderId)
|
val idAbovePlaceholder = notificationsDao.getIdAbove(account.id, placeholderId)
|
||||||
val idBelowPlaceholder = notificationsDao.getIdBelow(activeAccount.id, placeholderId)
|
val idBelowPlaceholder = notificationsDao.getIdBelow(account.id, placeholderId)
|
||||||
when (readingOrder) {
|
when (readingOrder) {
|
||||||
// Using minId, loads up to LOAD_AT_ONCE statuses with IDs immediately
|
// Using minId, loads up to LOAD_AT_ONCE statuses with IDs immediately
|
||||||
// after minId and no larger than maxId
|
// after minId and no larger than maxId
|
||||||
|
@ -334,11 +327,11 @@ class NotificationsViewModel @Inject constructor(
|
||||||
val accountDao = db.timelineAccountDao()
|
val accountDao = db.timelineAccountDao()
|
||||||
|
|
||||||
db.withTransaction {
|
db.withTransaction {
|
||||||
notificationsDao.delete(activeAccount.id, placeholderId)
|
notificationsDao.delete(account.id, placeholderId)
|
||||||
|
|
||||||
val overlappedNotifications = if (notifications.isNotEmpty()) {
|
val overlappedNotifications = if (notifications.isNotEmpty()) {
|
||||||
notificationsDao.deleteRange(
|
notificationsDao.deleteRange(
|
||||||
activeAccount.id,
|
account.id,
|
||||||
notifications.last().id,
|
notifications.last().id,
|
||||||
notifications.first().id
|
notifications.first().id
|
||||||
)
|
)
|
||||||
|
@ -347,26 +340,28 @@ class NotificationsViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
for (notification in notifications) {
|
for (notification in notifications) {
|
||||||
accountDao.insert(notification.account.toEntity(activeAccount.id))
|
accountDao.insert(notification.account.toEntity(account.id))
|
||||||
notification.report?.let { report ->
|
notification.report?.let { report ->
|
||||||
accountDao.insert(report.targetAccount.toEntity(activeAccount.id))
|
accountDao.insert(report.targetAccount.toEntity(account.id))
|
||||||
notificationsDao.insertReport(report.toEntity(activeAccount.id))
|
notificationsDao.insertReport(report.toEntity(account.id))
|
||||||
}
|
}
|
||||||
notification.status?.let { status ->
|
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(
|
statusDao.insert(
|
||||||
status.toEntity(
|
status.toEntity(
|
||||||
tuskyAccountId = activeAccount.id,
|
tuskyAccountId = account.id,
|
||||||
expanded = activeAccount.alwaysOpenSpoiler,
|
expanded = account.alwaysOpenSpoiler,
|
||||||
contentShowing = activeAccount.alwaysShowSensitiveMedia || !status.sensitive,
|
contentShowing = account.alwaysShowSensitiveMedia || !status.sensitive,
|
||||||
contentCollapsed = true
|
contentCollapsed = true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
notificationsDao.insertNotification(
|
notificationsDao.insertNotification(
|
||||||
notification.toEntity(
|
notification.toEntity(
|
||||||
activeAccount.id
|
account.id
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -385,7 +380,7 @@ class NotificationsViewModel @Inject constructor(
|
||||||
Placeholder(
|
Placeholder(
|
||||||
idToConvert,
|
idToConvert,
|
||||||
loading = false
|
loading = false
|
||||||
).toNotificationEntity(activeAccount.id)
|
).toNotificationEntity(account.id)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,6 @@ import com.keylesspalace.tusky.entity.Poll
|
||||||
import com.keylesspalace.tusky.network.FilterModel
|
import com.keylesspalace.tusky.network.FilterModel
|
||||||
import com.keylesspalace.tusky.network.MastodonApi
|
import com.keylesspalace.tusky.network.MastodonApi
|
||||||
import com.keylesspalace.tusky.usecase.TimelineCases
|
import com.keylesspalace.tusky.usecase.TimelineCases
|
||||||
import com.keylesspalace.tusky.util.EmptyPagingSource
|
|
||||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||||
import com.keylesspalace.tusky.viewdata.TranslationViewData
|
import com.keylesspalace.tusky.viewdata.TranslationViewData
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
@ -78,6 +77,8 @@ class CachedTimelineViewModel @Inject constructor(
|
||||||
filterModel
|
filterModel
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
private val account = accountManager.activeAccount!!
|
||||||
|
|
||||||
private var currentPagingSource: PagingSource<Int, HomeTimelineData>? = null
|
private var currentPagingSource: PagingSource<Int, HomeTimelineData>? = null
|
||||||
|
|
||||||
/** Map from status id to translation. */
|
/** Map from status id to translation. */
|
||||||
|
@ -90,12 +91,7 @@ class CachedTimelineViewModel @Inject constructor(
|
||||||
),
|
),
|
||||||
remoteMediator = CachedTimelineRemoteMediator(accountManager, api, db),
|
remoteMediator = CachedTimelineRemoteMediator(accountManager, api, db),
|
||||||
pagingSourceFactory = {
|
pagingSourceFactory = {
|
||||||
val activeAccount = accountManager.activeAccount
|
db.timelineDao().getHomeTimeline(account.id).also { newPagingSource ->
|
||||||
if (activeAccount == null) {
|
|
||||||
EmptyPagingSource()
|
|
||||||
} else {
|
|
||||||
db.timelineDao().getHomeTimeline(activeAccount.id)
|
|
||||||
}.also { newPagingSource ->
|
|
||||||
this.currentPagingSource = newPagingSource
|
this.currentPagingSource = newPagingSource
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,27 +121,27 @@ class CachedTimelineViewModel @Inject constructor(
|
||||||
override fun changeExpanded(expanded: Boolean, status: StatusViewData.Concrete) {
|
override fun changeExpanded(expanded: Boolean, status: StatusViewData.Concrete) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
db.timelineStatusDao()
|
db.timelineStatusDao()
|
||||||
.setExpanded(accountManager.activeAccount!!.id, status.actionableId, expanded)
|
.setExpanded(account.id, status.actionableId, expanded)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun changeContentShowing(isShowing: Boolean, status: StatusViewData.Concrete) {
|
override fun changeContentShowing(isShowing: Boolean, status: StatusViewData.Concrete) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
db.timelineStatusDao()
|
db.timelineStatusDao()
|
||||||
.setContentShowing(accountManager.activeAccount!!.id, status.actionableId, isShowing)
|
.setContentShowing(account.id, status.actionableId, isShowing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun changeContentCollapsed(isCollapsed: Boolean, status: StatusViewData.Concrete) {
|
override fun changeContentCollapsed(isCollapsed: Boolean, status: StatusViewData.Concrete) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
db.timelineStatusDao()
|
db.timelineStatusDao()
|
||||||
.setContentCollapsed(accountManager.activeAccount!!.id, status.actionableId, isCollapsed)
|
.setContentCollapsed(account.id, status.actionableId, isCollapsed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearWarning(status: StatusViewData.Concrete) {
|
override fun clearWarning(status: StatusViewData.Concrete) {
|
||||||
viewModelScope.launch {
|
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 statusDao = db.timelineStatusDao()
|
||||||
val accountDao = db.timelineAccountDao()
|
val accountDao = db.timelineAccountDao()
|
||||||
|
|
||||||
val activeAccount = accountManager.activeAccount!!
|
|
||||||
|
|
||||||
timelineDao.insertHomeTimelineItem(
|
timelineDao.insertHomeTimelineItem(
|
||||||
Placeholder(placeholderId, loading = true).toEntity(
|
Placeholder(placeholderId, loading = true).toEntity(tuskyAccountId = account.id)
|
||||||
activeAccount.id
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val response = db.withTransaction {
|
val response = db.withTransaction {
|
||||||
val idAbovePlaceholder = timelineDao.getIdAbove(activeAccount.id, placeholderId)
|
val idAbovePlaceholder = timelineDao.getIdAbove(account.id, placeholderId)
|
||||||
val idBelowPlaceholder = timelineDao.getIdBelow(activeAccount.id, placeholderId)
|
val idBelowPlaceholder = timelineDao.getIdBelow(account.id, placeholderId)
|
||||||
when (readingOrder) {
|
when (readingOrder) {
|
||||||
// Using minId, loads up to LOAD_AT_ONCE statuses with IDs immediately
|
// Using minId, loads up to LOAD_AT_ONCE statuses with IDs immediately
|
||||||
// after minId and no larger than maxId
|
// after minId and no larger than maxId
|
||||||
|
@ -196,11 +188,11 @@ class CachedTimelineViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
db.withTransaction {
|
db.withTransaction {
|
||||||
timelineDao.deleteHomeTimelineItem(activeAccount.id, placeholderId)
|
timelineDao.deleteHomeTimelineItem(account.id, placeholderId)
|
||||||
|
|
||||||
val overlappedStatuses = if (statuses.isNotEmpty()) {
|
val overlappedStatuses = if (statuses.isNotEmpty()) {
|
||||||
timelineDao.deleteRange(
|
timelineDao.deleteRange(
|
||||||
activeAccount.id,
|
account.id,
|
||||||
statuses.last().id,
|
statuses.last().id,
|
||||||
statuses.first().id
|
statuses.first().id
|
||||||
)
|
)
|
||||||
|
@ -209,22 +201,23 @@ class CachedTimelineViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
for (status in statuses) {
|
for (status in statuses) {
|
||||||
accountDao.insert(status.account.toEntity(activeAccount.id))
|
accountDao.insert(status.account.toEntity(account.id))
|
||||||
status.reblog?.account?.toEntity(activeAccount.id)
|
status.reblog?.account?.toEntity(account.id)
|
||||||
?.let { rebloggedAccount ->
|
?.let { rebloggedAccount ->
|
||||||
accountDao.insert(rebloggedAccount)
|
accountDao.insert(rebloggedAccount)
|
||||||
}
|
}
|
||||||
|
val account = accountManager.getAccountById(account.id) ?: return@withTransaction
|
||||||
statusDao.insert(
|
statusDao.insert(
|
||||||
status.actionableStatus.toEntity(
|
status.actionableStatus.toEntity(
|
||||||
tuskyAccountId = activeAccount.id,
|
tuskyAccountId = account.id,
|
||||||
expanded = activeAccount.alwaysOpenSpoiler,
|
expanded = account.alwaysOpenSpoiler,
|
||||||
contentShowing = activeAccount.alwaysShowSensitiveMedia || !status.actionableStatus.sensitive,
|
contentShowing = account.alwaysShowSensitiveMedia || !status.actionableStatus.sensitive,
|
||||||
contentCollapsed = true
|
contentCollapsed = true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
timelineDao.insertHomeTimelineItem(
|
timelineDao.insertHomeTimelineItem(
|
||||||
HomeTimelineEntity(
|
HomeTimelineEntity(
|
||||||
tuskyAccountId = activeAccount.id,
|
tuskyAccountId = account.id,
|
||||||
id = status.id,
|
id = status.id,
|
||||||
statusId = status.actionableId,
|
statusId = status.actionableId,
|
||||||
reblogAccountId = if (status.reblog != null) {
|
reblogAccountId = if (status.reblog != null) {
|
||||||
|
@ -250,7 +243,7 @@ class CachedTimelineViewModel @Inject constructor(
|
||||||
Placeholder(
|
Placeholder(
|
||||||
idToConvert,
|
idToConvert,
|
||||||
loading = false
|
loading = false
|
||||||
).toEntity(activeAccount.id)
|
).toEntity(account.id)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -286,7 +279,7 @@ class CachedTimelineViewModel @Inject constructor(
|
||||||
|
|
||||||
override suspend fun invalidate() {
|
override suspend fun invalidate() {
|
||||||
// invalidating when we don't have statuses yet can cause empty timelines because it cancels the network load
|
// 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()
|
currentPagingSource?.invalidate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue