Skip to content

Commit

Permalink
fix: Ensure logging out accounts completes (#515)
Browse files Browse the repository at this point in the history
The account logout process could fail due to API exceptions; network
errors for example, or if the user had already revoked the app's token
for that account. This would prevent the rest of the logout process
(cleaning database, etc) from completing.

Fix this by ignoring network errors during the logout process, and
always cleaning up account content in the database.

Fix a related issue where a deleted account might be recreated in a
partial state if the account's visible position was saved after it was
deleted. The recreated account couldn't do anything as it had no tokens,
but is very confusing.
  • Loading branch information
nikclayton authored Mar 10, 2024
1 parent 0105a81 commit 0445e18
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 8 deletions.
15 changes: 10 additions & 5 deletions app/src/main/java/app/pachli/usecase/LogoutUsecase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.util.removeShortcut
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import timber.log.Timber

class LogoutUsecase @Inject constructor(
@ApplicationContext private val context: Context,
Expand All @@ -37,11 +38,15 @@ class LogoutUsecase @Inject constructor(
val clientId = activeAccount.clientId
val clientSecret = activeAccount.clientSecret
if (clientId != null && clientSecret != null) {
api.revokeOAuthToken(
clientId = clientId,
clientSecret = clientSecret,
token = activeAccount.accessToken,
)
try {
api.revokeOAuthToken(
clientId = clientId,
clientSecret = clientSecret,
token = activeAccount.accessToken,
)
} catch (e: Exception) {
Timber.e(e, "Could not revoke OAuth token, continuing")
}
}

// disable push notifications
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,25 @@ class AccountManager @Inject constructor(
* @param account the account to save
*/
fun saveAccount(account: AccountEntity) {
if (account.id != 0L) {
Timber.d("saveAccount: saving account with id %d", account.id)
accountDao.insertOrReplace(account)
if (account.id == 0L) {
Timber.e("Trying to save account with ID = 0, ignoring")
return
}

// Work around saveAccount() being called after account deletion
// For example:
// - Have two accounts, A and B, signed in with A, looking at home timeline for A
// - Log out of A. This triggers deletion of account A from the database
// - Shortly afterwards the timeline activity/fragment ends, and it tries to save
// the visible ID back to the database, which creates the AccountEntity record
// that was just deleted, but in a partial state.
if (accounts.find { it.id == account.id } == null) {
Timber.e("Trying to save account with ID = %d which does not exist, ignoring", account.id)
return
}

Timber.d("saveAccount: saving account with id %d", account.id)
accountDao.insertOrReplace(account)
}

/**
Expand Down

0 comments on commit 0445e18

Please sign in to comment.