Skip to content

Commit

Permalink
feat: add profile recovery screen (#581)
Browse files Browse the repository at this point in the history
* add named args to login use case

* add more tests to api config repository

* minor refactor to active account monitor test

* update l10n

* add recover screen to my account

* update DI
  • Loading branch information
AkesiSeli authored Nov 26, 2024
1 parent 26cbf77 commit 50960b8
Show file tree
Hide file tree
Showing 17 changed files with 275 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -492,4 +492,13 @@ internal val DeStrings =
override val formatStrikethrough = "Durchgestrichen"
override val formatUnderlined = "Unterstrichen"
override val formatMonospace = "Einfarbig"
override val actionRemoveFromFavorites = "Aus den Favoriten entfernen"
override val actionRemoveFromBookmarks = "Aus den Lesezeichen entfernen"
override val messageAuthIssue =
"Es ist ein Fehler aufgetreten, vielleicht ist Ihr Token abgelaufen."
override val messageAuthIssueHintsTitle =
"Sie können eine der folgenden Aktionen versuchen:"
override val messageAuthIssueHint1 = "Aktualisieren erzwingen"
override val messageAuthIssueHint2 = "erneut anmelden"
override val messageAuthIssueHint3 = "Löschen Sie die Anwendungsdaten"
}
Original file line number Diff line number Diff line change
Expand Up @@ -491,4 +491,9 @@ internal open class DefaultStrings : Strings {
override val formatMonospace = "Monospace"
override val actionRemoveFromFavorites = "Remove from favorites"
override val actionRemoveFromBookmarks = "Remove from bookmarks"
override val messageAuthIssue = "An error occurred, maybe your token has expired."
override val messageAuthIssueHintsTitle = "You can try one of the following actions:"
override val messageAuthIssueHint1 = "force refresh"
override val messageAuthIssueHint2 = "log in again"
override val messageAuthIssueHint3 = "clear the application data"
}
Original file line number Diff line number Diff line change
Expand Up @@ -492,4 +492,11 @@ internal val EsStrings =
override val formatStrikethrough = "Tachado"
override val formatUnderlined = "Subrayado"
override val formatMonospace = "Monospace"
override val actionRemoveFromFavorites = "Eliminar de favoritos"
override val actionRemoveFromBookmarks = "Eliminar de favoritos"
override val messageAuthIssue = "Se ha producido un error, puede que tu token haya caducado."
override val messageAuthIssueHintsTitle = "Puedes intentar una de las siguientes acciones:"
override val messageAuthIssueHint1 = "forzar actualización"
override val messageAuthIssueHint2 = "iniciar sesión de nuevo"
override val messageAuthIssueHint3 = "borrar los datos de la aplicación"
}
Original file line number Diff line number Diff line change
Expand Up @@ -479,4 +479,11 @@ internal val FiStrings =
override val formatStrikethrough = "Yliviivattu"
override val formatUnderlined = "Alleviivattu"
override val formatMonospace = "Monospace"
override val actionRemoveFromFavorites = "Poista suosikeista"
override val actionRemoveFromBookmarks = "Poista kirjanmerkeistä"
override val messageAuthIssue = "Tapahtui virhe, ehkä tunnuksesi on vanhentunut."
override val messageAuthIssueHintsTitle = "Voit kokeilla jotakin seuraavista toimista:"
override val messageAuthIssueHint1 = "pakota päivitys"
override val messageAuthIssueHint2 = "kirjaudu uudelleen sisään"
override val messageAuthIssueHint3 = "tyhjennä sovelluksen tiedot"
}
Original file line number Diff line number Diff line change
Expand Up @@ -497,4 +497,12 @@ internal val FrStrings =
override val formatStrikethrough = "Barré"
override val formatUnderlined = "Souligné"
override val formatMonospace = "Monospace"
override val actionRemoveFromFavorites = "Supprimer des favoris"
override val actionRemoveFromBookmarks = "Supprimer des signets"
override val messageAuthIssue = "Une erreur s'est produite, votre jeton a peut-être expiré."
override val messageAuthIssueHintsTitle =
"Vous pouvez essayer l'une des actions suivantes :"
override val messageAuthIssueHint1 = "forcer l'actualisation"
override val messageAuthIssueHint2 = "se reconnecter"
override val messageAuthIssueHint3 = "effacer les données de l'application"
}
Original file line number Diff line number Diff line change
Expand Up @@ -492,4 +492,11 @@ internal val ItStrings =
override val formatStrikethrough = "Barrato"
override val formatUnderlined = "Sottolineato"
override val formatMonospace = "Larghezza fissa"
override val actionRemoveFromFavorites = "Rimuovi dai preferiti"
override val actionRemoveFromBookmarks = "Rimuovi dai segnalibri"
override val messageAuthIssue = "Si è verificato un errore, forse il token attuale è scaduto."
override val messageAuthIssueHintsTitle = "Si consiglia di intraprendere una delle seguenti azioni:"
override val messageAuthIssueHint1 = "forzare l'aggiornamento"
override val messageAuthIssueHint2 = "effettuare nuovamente l'accesso"
override val messageAuthIssueHint3 = "cancellare i dati dell'applicazione"
}
Original file line number Diff line number Diff line change
Expand Up @@ -490,4 +490,11 @@ internal val PlStrings =
override val formatStrikethrough = "Przekreślenie"
override val formatUnderlined = "Podkreślony"
override val formatMonospace = "Monospace"
override val actionRemoveFromFavorites = "Usuń z ulubionych"
override val actionRemoveFromBookmarks = "Usuń z zakładek"
override val messageAuthIssue = "Wystąpił błąd, być może Twój token wygasł."
override val messageAuthIssueHintsTitle = "Możesz wypróbować jedno z następujących działań:"
override val messageAuthIssueHint1 = "wymuś odświeżenie"
override val messageAuthIssueHint2 = "zaloguj się ponownie"
override val messageAuthIssueHint3 = "wyczyść dane aplikacji"
}
Original file line number Diff line number Diff line change
Expand Up @@ -496,4 +496,11 @@ internal val PtStrings =
override val formatStrikethrough = "Riscado"
override val formatUnderlined = "Sublinhado"
override val formatMonospace = "Monoespaço"
override val actionRemoveFromFavorites = "Remover dos favoritos"
override val actionRemoveFromBookmarks = "Remover dos favoritos"
override val messageAuthIssue = "Ocorreu um erro, talvez o seu token tenha expirado."
override val messageAuthIssueHintsTitle = "Pode tentar uma das seguintes acções:"
override val messageAuthIssueHint1 = "forçar a atualização"
override val messageAuthIssueHint2 = "iniciar sessão novamente"
override val messageAuthIssueHint3 = "limpar os dados da aplicação"
}
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,11 @@ interface Strings {
val formatMonospace: String
val actionRemoveFromFavorites: String
val actionRemoveFromBookmarks: String
val messageAuthIssue: String
val messageAuthIssueHintsTitle: String
val messageAuthIssueHint1: String
val messageAuthIssueHint2: String
val messageAuthIssueHint3: String
}

object Locales {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -499,4 +499,13 @@ internal val UaStrings =
override val formatStrikethrough = "Закреслений"
override val formatUnderlined = "Підкреслений"
override val formatMonospace = "Монопростір"
override val actionRemoveFromFavorites = "Видалити з обраного"
override val actionRemoveFromBookmarks = "Видалити з закладок"
override val messageAuthIssue =
"Виникла помилка, можливо, термін дії вашого токена закінчився."
override val messageAuthIssueHintsTitle =
"Ви можете спробувати виконати одну з наступних дій:"
override val messageAuthIssueHint1 = "примусове оновлення"
override val messageAuthIssueHint2 = "увійти ще раз"
override val messageAuthIssueHint3 = "очистити дані додатку"
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@ package com.livefast.eattrash.raccoonforfriendica.domain.identity.repository
import com.livefast.eattrash.raccoonforfriendica.core.api.provider.ServiceProvider
import com.livefast.eattrash.raccoonforfriendica.core.preferences.TemporaryKeyStore
import dev.mokkery.MockMode
import dev.mokkery.answering.calls
import dev.mokkery.answering.returns
import dev.mokkery.every
import dev.mokkery.everySuspend
import dev.mokkery.matcher.any
import dev.mokkery.mock
import dev.mokkery.verify
import dev.mokkery.verifySuspend
import kotlinx.coroutines.delay
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
import kotlin.time.Duration.Companion.seconds

class DefaultApiConfigurationRepositoryTest {
private val keyStore =
Expand All @@ -29,7 +32,7 @@ class DefaultApiConfigurationRepositoryTest {
private val credentialsRepository =
mock<CredentialsRepository> {
everySuspend {
validateApplicationCredentials(any(), any())
validateApplicationCredentials(node = any(), credentials = any())
} returns true
}

Expand Down Expand Up @@ -99,7 +102,10 @@ class DefaultApiConfigurationRepositoryTest {
fun `given invalid OAuth credentials stored when hasCachedAuthCredentials then result is as expected`() =
runTest {
everySuspend {
credentialsRepository.validateApplicationCredentials(any(), any())
credentialsRepository.validateApplicationCredentials(
node = any(),
credentials = any(),
)
} returns false

val res = sut.hasCachedAuthCredentials()
Expand All @@ -120,7 +126,10 @@ class DefaultApiConfigurationRepositoryTest {
every { keyStore["lastCred1", any<String>()] } returns "fake1"
every { keyStore["lastCred2", any<String>()] } returns "fake2"
everySuspend {
credentialsRepository.validateApplicationCredentials(any(), any())
credentialsRepository.validateApplicationCredentials(
node = any(),
credentials = any(),
)
} returns false

val res = sut.hasCachedAuthCredentials()
Expand All @@ -133,4 +142,42 @@ class DefaultApiConfigurationRepositoryTest {
)
}
}

@Test
fun `given timeout when hasCachedAuthCredentials then result is as expected`() =
runTest {
everySuspend {
credentialsRepository.validateApplicationCredentials(
node = any(),
credentials = any(),
)
} calls {
delay(10.seconds)
true
}

val res = sut.hasCachedAuthCredentials()

assertFalse(res)
verifySuspend {
credentialsRepository.validateApplicationCredentials(
"default-instance",
ApiCredentials.OAuth2(accessToken = "fake-access-token", refreshToken = ""),
)
}
}

@Test
fun `given valid credentials when hasCachedAuthCredentials then result is as expected`() =
runTest {
val res = sut.hasCachedAuthCredentials()

assertTrue(res)
verifySuspend {
credentialsRepository.validateApplicationCredentials(
"default-instance",
ApiCredentials.OAuth2(accessToken = "fake-access-token", refreshToken = ""),
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ internal class DefaultLoginUseCase(
) {
apiConfigurationRepository.changeNode(node)
apiConfigurationRepository.setAuth(credentials)
val user = credentialsRepository.validate(node, credentials)
val user = credentialsRepository.validate(node = node, credentials = credentials)
checkNotNull(user) { "Invalid credentials" }

val handle =
Expand All @@ -47,7 +47,7 @@ internal class DefaultLoginUseCase(
}

accountRepository.getBy(handle)?.also { account ->
accountCredentialsCache.save(account.id, credentials)
accountCredentialsCache.save(accountId = account.id, credentials = credentials)

val anonymousAccountId = accountRepository.getBy(handle = "")?.id ?: 0
val oldSettings = settingsRepository.get(account.id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import dev.mokkery.every
import dev.mokkery.everySuspend
import dev.mokkery.matcher.any
import dev.mokkery.mock
import dev.mokkery.verify
import dev.mokkery.verifySuspend
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.Channel
Expand Down Expand Up @@ -81,10 +80,8 @@ class DefaultActiveAccountMonitorTest {
sut.start()
accountChannel.send(account)

verify {
apiConfigurationRepository.setAuth(null)
}
verifySuspend {
apiConfigurationRepository.setAuth(null)
supportedFeatureRepository.refresh()
identityRepository.refreshCurrentUser(null)
supportedFeatureRepository.refresh()
Expand All @@ -98,7 +95,7 @@ class DefaultActiveAccountMonitorTest {
}

@Test
fun `given active account when started then interactions are as expected`() =
fun `given active account and valid credentials when started then interactions are as expected`() =
runTest {
val accountId = 1L
val userId = "fake-user-id"
Expand All @@ -107,7 +104,6 @@ class DefaultActiveAccountMonitorTest {
val accountChannel = Channel<AccountModel?>()
every { accountRepository.getActiveAsFlow() } returns accountChannel.receiveAsFlow()
everySuspend { accountRepository.getActive() } returns account
every { apiConfigurationRepository.node } returns MutableStateFlow("test-instance")
val credentials = ApiCredentials.OAuth2("fake-access-token", "")
every { accountCredentialsCache.get(any()) } returns credentials
val settings = SettingsModel()
Expand All @@ -116,10 +112,8 @@ class DefaultActiveAccountMonitorTest {
sut.start()
accountChannel.send(account)

verify {
apiConfigurationRepository.setAuth(credentials)
}
verifySuspend {
apiConfigurationRepository.setAuth(credentials)
supportedFeatureRepository.refresh()
identityRepository.refreshCurrentUser(userId)
contentPreloadManager.preload(userId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ val featureProfileModule =
paginationManager = get(),
timelineEntryRepository = get(),
settingsRepository = get(),
apiConfigurationRepository = get(),
logout = get(),
hapticFeedback = get(),
notificationCenter = get(),
imagePreloadManager = get(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ interface MyAccountMviModel :
data class CopyToClipboard(
val entry: TimelineEntryModel,
) : Intent

data object Logout : Intent
}

data class State(
Expand Down
Loading

0 comments on commit 50960b8

Please sign in to comment.