improve logout (#2579)

* improve logout

* fix tests

* add db migration

* delete wrongly committed file again

* improve LogoutUsecase
This commit is contained in:
Konrad Pozniak 2022-06-20 16:45:54 +02:00 committed by GitHub
commit f419e83c16
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 185 additions and 91 deletions

View file

@ -0,0 +1,66 @@
package com.keylesspalace.tusky.usecase
import android.content.Context
import com.keylesspalace.tusky.components.drafts.DraftHelper
import com.keylesspalace.tusky.components.notifications.NotificationHelper
import com.keylesspalace.tusky.components.notifications.disableUnifiedPushNotificationsForAccount
import com.keylesspalace.tusky.db.AccountManager
import com.keylesspalace.tusky.db.AppDatabase
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.removeShortcut
import javax.inject.Inject
class LogoutUsecase @Inject constructor(
private val context: Context,
private val api: MastodonApi,
private val db: AppDatabase,
private val accountManager: AccountManager,
private val draftHelper: DraftHelper
) {
/**
* Logs the current account out and clears all caches associated with it
* @return true if the user is logged in with other accounts, false if it was the only one
*/
suspend fun logout(): Boolean {
accountManager.activeAccount?.let { activeAccount ->
// invalidate the oauth token, if we have the client id & secret
// (could be missing if user logged in with a previous version of Tusky)
val clientId = activeAccount.clientId
val clientSecret = activeAccount.clientSecret
if (clientId != null && clientSecret != null) {
api.revokeOAuthToken(
clientId = clientId,
clientSecret = clientSecret,
token = activeAccount.accessToken
)
}
// disable push notifications
disableUnifiedPushNotificationsForAccount(context, activeAccount)
// disable pull notifications
if (!NotificationHelper.areNotificationsEnabled(context, accountManager)) {
NotificationHelper.disablePullNotifications(context)
}
// clear notification channels
NotificationHelper.deleteNotificationChannelsForAccount(activeAccount, context)
// remove account from local AccountManager
val otherAccountAvailable = accountManager.logActiveAccountOut() != null
// clear the database - this could trigger network calls so do it last when all tokens are gone
db.timelineDao().removeAll(activeAccount.id)
db.conversationDao().deleteForAccount(activeAccount.id)
draftHelper.deleteAllDraftsAndAttachmentsForAccount(activeAccount.id)
// remove shortcut associated with the account
removeShortcut(context, activeAccount)
return otherAccountAvailable
}
return false
}
}

View file

@ -0,0 +1,147 @@
/* Copyright 2018 charlag
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.usecase
import android.util.Log
import com.keylesspalace.tusky.appstore.BlockEvent
import com.keylesspalace.tusky.appstore.BookmarkEvent
import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.appstore.FavoriteEvent
import com.keylesspalace.tusky.appstore.MuteConversationEvent
import com.keylesspalace.tusky.appstore.MuteEvent
import com.keylesspalace.tusky.appstore.PinEvent
import com.keylesspalace.tusky.appstore.PollVoteEvent
import com.keylesspalace.tusky.appstore.ReblogEvent
import com.keylesspalace.tusky.appstore.StatusDeletedEvent
import com.keylesspalace.tusky.entity.DeletedStatus
import com.keylesspalace.tusky.entity.Poll
import com.keylesspalace.tusky.entity.Status
import com.keylesspalace.tusky.network.MastodonApi
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.addTo
import javax.inject.Inject
/**
* Created by charlag on 3/24/18.
*/
class TimelineCases @Inject constructor(
private val mastodonApi: MastodonApi,
private val eventHub: EventHub
) {
/**
* Unused yet but can be use for cancellation later. It's always a good idea to save
* Disposables.
*/
private val cancelDisposable = CompositeDisposable()
fun reblog(statusId: String, reblog: Boolean): Single<Status> {
val call = if (reblog) {
mastodonApi.reblogStatus(statusId)
} else {
mastodonApi.unreblogStatus(statusId)
}
return call.doAfterSuccess {
eventHub.dispatch(ReblogEvent(statusId, reblog))
}
}
fun favourite(statusId: String, favourite: Boolean): Single<Status> {
val call = if (favourite) {
mastodonApi.favouriteStatus(statusId)
} else {
mastodonApi.unfavouriteStatus(statusId)
}
return call.doAfterSuccess {
eventHub.dispatch(FavoriteEvent(statusId, favourite))
}
}
fun bookmark(statusId: String, bookmark: Boolean): Single<Status> {
val call = if (bookmark) {
mastodonApi.bookmarkStatus(statusId)
} else {
mastodonApi.unbookmarkStatus(statusId)
}
return call.doAfterSuccess {
eventHub.dispatch(BookmarkEvent(statusId, bookmark))
}
}
fun muteConversation(statusId: String, mute: Boolean): Single<Status> {
val call = if (mute) {
mastodonApi.muteConversation(statusId)
} else {
mastodonApi.unmuteConversation(statusId)
}
return call.doAfterSuccess {
eventHub.dispatch(MuteConversationEvent(statusId, mute))
}
}
fun mute(statusId: String, notifications: Boolean, duration: Int?) {
mastodonApi.muteAccount(statusId, notifications, duration)
.subscribe(
{
eventHub.dispatch(MuteEvent(statusId))
},
{ t ->
Log.w("Failed to mute account", t)
}
)
.addTo(cancelDisposable)
}
fun block(statusId: String) {
mastodonApi.blockAccount(statusId)
.subscribe(
{
eventHub.dispatch(BlockEvent(statusId))
},
{ t ->
Log.w("Failed to block account", t)
}
)
.addTo(cancelDisposable)
}
fun delete(statusId: String): Single<DeletedStatus> {
return mastodonApi.deleteStatus(statusId)
.doAfterSuccess {
eventHub.dispatch(StatusDeletedEvent(statusId))
}
}
fun pin(statusId: String, pin: Boolean): Single<Status> {
// Replace with extension method if we use RxKotlin
return (if (pin) mastodonApi.pinStatus(statusId) else mastodonApi.unpinStatus(statusId))
.doAfterSuccess {
eventHub.dispatch(PinEvent(statusId, pin))
}
}
fun voteInPoll(statusId: String, pollId: String, choices: List<Int>): Single<Poll> {
if (choices.isEmpty()) {
return Single.error(IllegalStateException())
}
return mastodonApi.voteInPoll(pollId, choices).doAfterSuccess {
eventHub.dispatch(PollVoteEvent(statusId, it))
}
}
}