mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
Refactored select-account-use-case + refactored tests. (#4)
* Refactored select-account-use-case + refactored tests. * Added launch-account-use-case, also added new implementation for defining current user account * Navigation fixes. * Refactored event flow. * Fixed tests
This commit is contained in:
parent
f542e2b7e4
commit
996d9135bb
48 changed files with 419 additions and 170 deletions
|
@ -200,11 +200,11 @@ class SetupSelectedAccountModule {
|
|||
@Provides
|
||||
@PerScreen
|
||||
fun provideSetupSelectedAccountViewModelFactory(
|
||||
selectAccount: SelectAccount,
|
||||
startAccount: StartAccount,
|
||||
pathProvider: PathProvider
|
||||
): SetupSelectedAccountViewModelFactory {
|
||||
return SetupSelectedAccountViewModelFactory(
|
||||
selectAccount = selectAccount,
|
||||
startAccount = startAccount,
|
||||
pathProvider = pathProvider
|
||||
)
|
||||
}
|
||||
|
@ -213,8 +213,8 @@ class SetupSelectedAccountModule {
|
|||
@PerScreen
|
||||
fun provideSelectAccountUseCase(
|
||||
repository: AuthRepository
|
||||
): SelectAccount {
|
||||
return SelectAccount(
|
||||
): StartAccount {
|
||||
return StartAccount(
|
||||
repository = repository
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package com.agileburo.anytype.di.feature
|
|||
|
||||
import com.agileburo.anytype.core_utils.di.scope.PerScreen
|
||||
import com.agileburo.anytype.domain.auth.repo.AuthRepository
|
||||
import com.agileburo.anytype.domain.desktop.interactor.GetAccount
|
||||
import com.agileburo.anytype.domain.desktop.interactor.GetCurrentAccount
|
||||
import com.agileburo.anytype.domain.image.ImageLoader
|
||||
import com.agileburo.anytype.domain.image.LoadImage
|
||||
import com.agileburo.anytype.presentation.desktop.DesktopViewModelFactory
|
||||
|
@ -33,10 +33,10 @@ class DesktopModule {
|
|||
@Provides
|
||||
@PerScreen
|
||||
fun provideDesktopViewModelFactory(
|
||||
getAccount: GetAccount,
|
||||
getCurrentAccount: GetCurrentAccount,
|
||||
loadImage: LoadImage
|
||||
): DesktopViewModelFactory = DesktopViewModelFactory(
|
||||
getAccount = getAccount,
|
||||
getCurrentAccount = getCurrentAccount,
|
||||
loadImage = loadImage
|
||||
)
|
||||
|
||||
|
@ -44,7 +44,7 @@ class DesktopModule {
|
|||
@PerScreen
|
||||
fun provideGetAccountUseCase(
|
||||
repository: AuthRepository
|
||||
): GetAccount = GetAccount(
|
||||
): GetCurrentAccount = GetCurrentAccount(
|
||||
repository = repository
|
||||
)
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package com.agileburo.anytype.di.feature
|
|||
import com.agileburo.anytype.core_utils.di.scope.PerScreen
|
||||
import com.agileburo.anytype.domain.auth.interactor.Logout
|
||||
import com.agileburo.anytype.domain.auth.repo.AuthRepository
|
||||
import com.agileburo.anytype.domain.desktop.interactor.GetAccount
|
||||
import com.agileburo.anytype.domain.desktop.interactor.GetCurrentAccount
|
||||
import com.agileburo.anytype.domain.image.ImageLoader
|
||||
import com.agileburo.anytype.domain.image.LoadImage
|
||||
import com.agileburo.anytype.presentation.profile.ProfileViewModelFactory
|
||||
|
@ -36,11 +36,11 @@ class ProfileModule {
|
|||
fun provideProfileViewModelFactory(
|
||||
logout: Logout,
|
||||
loadImage: LoadImage,
|
||||
getAccount: GetAccount
|
||||
getCurrentAccount: GetCurrentAccount
|
||||
): ProfileViewModelFactory = ProfileViewModelFactory(
|
||||
logout = logout,
|
||||
loadImage = loadImage,
|
||||
getAccount = getAccount
|
||||
getCurrentAccount = getCurrentAccount
|
||||
)
|
||||
|
||||
@Provides
|
||||
|
@ -61,7 +61,7 @@ class ProfileModule {
|
|||
@PerScreen
|
||||
fun provideGetAccountUseCase(
|
||||
authRepository: AuthRepository
|
||||
): GetAccount = GetAccount(
|
||||
): GetCurrentAccount = GetCurrentAccount(
|
||||
repository = authRepository
|
||||
)
|
||||
}
|
|
@ -3,6 +3,8 @@ package com.agileburo.anytype.di.feature
|
|||
import com.agileburo.anytype.core_utils.di.scope.PerScreen
|
||||
import com.agileburo.anytype.domain.auth.interactor.CheckAuthorizationStatus
|
||||
import com.agileburo.anytype.domain.auth.repo.AuthRepository
|
||||
import com.agileburo.anytype.domain.auth.repo.PathProvider
|
||||
import com.agileburo.anytype.domain.launch.LaunchAccount
|
||||
import com.agileburo.anytype.presentation.splash.SplashViewModelFactory
|
||||
import com.agileburo.anytype.ui.splash.SplashFragment
|
||||
import dagger.Module
|
||||
|
@ -33,11 +35,30 @@ class SplashModule {
|
|||
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun provideSplashViewModelFactory(checkAuthorizationStatus: CheckAuthorizationStatus) =
|
||||
SplashViewModelFactory(checkAuthorizationStatus)
|
||||
fun provideSplashViewModelFactory(
|
||||
checkAuthorizationStatus: CheckAuthorizationStatus,
|
||||
launchAccount: LaunchAccount
|
||||
): SplashViewModelFactory = SplashViewModelFactory(
|
||||
checkAuthorizationStatus = checkAuthorizationStatus,
|
||||
launchAccount = launchAccount
|
||||
)
|
||||
|
||||
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun provideCheckAuthorizationStatus(authRepository: AuthRepository) =
|
||||
CheckAuthorizationStatus(authRepository)
|
||||
fun provideCheckAuthorizationStatusUseCase(
|
||||
authRepository: AuthRepository
|
||||
): CheckAuthorizationStatus = CheckAuthorizationStatus(
|
||||
repository = authRepository
|
||||
)
|
||||
|
||||
@PerScreen
|
||||
@Provides
|
||||
fun provideLaunchAccountUseCase(
|
||||
authRepository: AuthRepository,
|
||||
pathProvider: PathProvider
|
||||
): LaunchAccount = LaunchAccount(
|
||||
repository = authRepository,
|
||||
pathProvider = pathProvider
|
||||
)
|
||||
}
|
|
@ -27,7 +27,7 @@ class Navigator : AppNavigation {
|
|||
}
|
||||
|
||||
override fun createProfile() {
|
||||
navController?.navigate(R.id.action_open_sign_up)
|
||||
navController?.navigate(R.id.action_create_profile)
|
||||
}
|
||||
|
||||
override fun setupNewAccount() {
|
||||
|
@ -42,7 +42,7 @@ class Navigator : AppNavigation {
|
|||
navController?.navigate(R.id.action_open_congratulation_screen)
|
||||
}
|
||||
|
||||
override fun chooseProfile() {
|
||||
override fun chooseAccount() {
|
||||
navController?.navigate(R.id.action_select_account)
|
||||
}
|
||||
|
||||
|
|
|
@ -25,11 +25,11 @@ abstract class NavigationFragment(
|
|||
is Command.StartDesktopFromLogin -> navigation.startDesktopFromLogin()
|
||||
is Command.StartDesktopFromSplash -> navigation.startDesktopFromSplash()
|
||||
is Command.OpenStartLoginScreen -> navigation.startLogin()
|
||||
is Command.OpenCreateProfile -> navigation.createProfile()
|
||||
is Command.OpenCreateAccount -> navigation.createProfile()
|
||||
is Command.ChoosePinCodeScreen -> navigation.choosePinCode()
|
||||
is Command.CongratulationScreen -> navigation.congratulation()
|
||||
is Command.EnterKeyChainScreen -> navigation.enterKeychain()
|
||||
is Command.ChooseProfileScreen -> navigation.chooseProfile()
|
||||
is Command.ChooseAccountScreen -> navigation.chooseAccount()
|
||||
is Command.WorkspaceScreen -> navigation.workspace()
|
||||
is Command.SetupNewAccountScreen -> navigation.setupNewAccount()
|
||||
is Command.SetupSelectedAccountScreen -> navigation.setupSelectedAccount(command.id)
|
||||
|
|
|
@ -63,6 +63,7 @@ class ProfileFragment : ViewStateFragment<ViewState<ProfileView>>(R.layout.fragm
|
|||
pinCodeText.setOnClickListener { vm.onPinCodeClicked() }
|
||||
keychainPhrase.setOnClickListener { vm.onKeyChainPhraseClicked() }
|
||||
backButton.setOnClickListener { vm.onBackButtonClicked() }
|
||||
switchProfileButton.setOnClickListener { vm.onAddProfileClicked() }
|
||||
}
|
||||
is ViewState.Success -> {
|
||||
name.text = state.data.name
|
||||
|
|
|
@ -11,11 +11,11 @@
|
|||
android:label="DesktopFragment"
|
||||
tools:layout="@layout/fragment_desktop">
|
||||
<action
|
||||
android:id="@+id/action_open_profile"
|
||||
app:destination="@id/profileScreen"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
android:id="@+id/action_open_profile"
|
||||
app:destination="@id/profileScreen"
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
</fragment>
|
||||
|
||||
|
@ -29,6 +29,41 @@
|
|||
app:destination="@+id/main_navigation"
|
||||
app:popUpTo="@+id/main_navigation"
|
||||
app:popUpToInclusive="true" />
|
||||
<action
|
||||
android:id="@+id/action_create_profile"
|
||||
app:destination="@id/createAccountScreen"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/createAccountScreen"
|
||||
android:name="com.agileburo.anytype.ui.auth.account.CreateAccountFragment"
|
||||
android:label="StartLoginFragment"
|
||||
tools:layout="@layout/fragment_create_account">
|
||||
<action
|
||||
android:id="@+id/action_setup_new_account"
|
||||
app:destination="@id/setupNewAccountScreen"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:popUpTo="@+id/startLoginScreen"
|
||||
app:popUpToInclusive="true" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/setupNewAccountScreen"
|
||||
android:name="com.agileburo.anytype.ui.auth.account.SetupNewAccountFragment"
|
||||
android:label="SetupAccount"
|
||||
tools:layout="@layout/fragment_setup_new_account">
|
||||
<action
|
||||
android:id="@+id/action_open_congratulation_screen"
|
||||
app:destination="@id/congratulationScreen"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popUpTo="@+id/startLoginScreen"
|
||||
app:popUpToInclusive="true" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/splashScreen"
|
||||
|
@ -54,6 +89,11 @@
|
|||
app:popUpTo="@+id/splashScreen"
|
||||
app:popUpToInclusive="true" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/congratulationScreen"
|
||||
android:name="com.agileburo.anytype.ui.auth.CongratulationFragment"
|
||||
android:label="StartLoginFragment"
|
||||
tools:layout="@layout/fragment_congratulation" />
|
||||
<navigation
|
||||
android:id="@+id/login_nav"
|
||||
app:startDestination="@id/startLoginScreen">
|
||||
|
@ -157,7 +197,7 @@
|
|||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:popUpToInclusive="false" />
|
||||
<action
|
||||
android:id="@+id/action_open_sign_up"
|
||||
android:id="@+id/action_create_profile"
|
||||
app:destination="@id/createAccountScreen"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
|
|
|
@ -3,10 +3,17 @@ package com.agileburo.anytype.data.auth.repo
|
|||
import com.agileburo.anytype.data.auth.model.AccountEntity
|
||||
|
||||
interface AuthCache {
|
||||
|
||||
suspend fun saveAccount(account: AccountEntity)
|
||||
suspend fun updateAccount(account: AccountEntity)
|
||||
|
||||
suspend fun saveMnemonic(mnemonic: String)
|
||||
suspend fun getMnemonic(): String
|
||||
suspend fun getAccount(): AccountEntity
|
||||
|
||||
suspend fun getCurrentAccount(): AccountEntity
|
||||
suspend fun getCurrentAccountId(): String
|
||||
|
||||
suspend fun logout()
|
||||
suspend fun getAccounts(): List<AccountEntity>
|
||||
suspend fun setCurrentAccount(id: String)
|
||||
}
|
|
@ -6,7 +6,7 @@ import kotlinx.coroutines.flow.Flow
|
|||
|
||||
class AuthCacheDataStore(private val cache: AuthCache) : AuthDataStore {
|
||||
|
||||
override suspend fun selectAccount(id: String, path: String): AccountEntity {
|
||||
override suspend fun startAccount(id: String, path: String): AccountEntity {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,10 @@ class AuthCacheDataStore(private val cache: AuthCache) : AuthDataStore {
|
|||
cache.saveAccount(account)
|
||||
}
|
||||
|
||||
override suspend fun updateAccount(account: AccountEntity) {
|
||||
cache.updateAccount(account)
|
||||
}
|
||||
|
||||
override fun observeAccounts(): Flow<AccountEntity> {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
@ -44,8 +48,13 @@ class AuthCacheDataStore(private val cache: AuthCache) : AuthDataStore {
|
|||
cache.logout()
|
||||
}
|
||||
|
||||
override suspend fun getStoredAccounts(): List<AccountEntity> =
|
||||
cache.getAccounts()
|
||||
override suspend fun getAccounts() = cache.getAccounts()
|
||||
|
||||
override suspend fun getAccount() = cache.getAccount()
|
||||
override suspend fun getCurrentAccount() = cache.getCurrentAccount()
|
||||
|
||||
override suspend fun getCurrentAccountId() = cache.getCurrentAccountId()
|
||||
|
||||
override suspend fun setCurrentAccount(id: String) {
|
||||
cache.setCurrentAccount(id)
|
||||
}
|
||||
}
|
|
@ -11,16 +11,16 @@ class AuthDataRepository(
|
|||
private val factory: AuthDataStoreFactory
|
||||
) : AuthRepository {
|
||||
|
||||
override suspend fun selectAccount(
|
||||
override suspend fun startAccount(
|
||||
id: String, path: String
|
||||
): Account = factory.remote.selectAccount(id, path).toDomain()
|
||||
): Account = factory.remote.startAccount(id, path).toDomain()
|
||||
|
||||
override suspend fun createAccount(
|
||||
name: String,
|
||||
avatarPath: String?
|
||||
): Account = factory.remote.createAccount(name, avatarPath).toDomain()
|
||||
|
||||
override suspend fun recoverAccount() {
|
||||
override suspend fun startLoadingAccounts() {
|
||||
factory.remote.recoverAccount()
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,10 @@ class AuthDataRepository(
|
|||
factory.cache.saveAccount(account.toEntity())
|
||||
}
|
||||
|
||||
override suspend fun updateAccount(account: Account) {
|
||||
factory.cache.updateAccount(account.toEntity())
|
||||
}
|
||||
|
||||
override fun observeAccounts() = factory.remote.observeAccounts().map { it.toDomain() }
|
||||
|
||||
override suspend fun createWallet(
|
||||
|
@ -38,7 +42,9 @@ class AuthDataRepository(
|
|||
factory.remote.recoverWallet(path, mnemonic)
|
||||
}
|
||||
|
||||
override suspend fun getAccount() = factory.cache.getAccount().toDomain()
|
||||
override suspend fun getCurrentAccount() = factory.cache.getCurrentAccount().toDomain()
|
||||
|
||||
override suspend fun getCurrentAccountId() = factory.cache.getCurrentAccountId()
|
||||
|
||||
override suspend fun saveMnemonic(
|
||||
mnemonic: String
|
||||
|
@ -50,6 +56,9 @@ class AuthDataRepository(
|
|||
factory.cache.logout()
|
||||
}
|
||||
|
||||
override suspend fun getAvailableAccounts(): List<Account> =
|
||||
factory.cache.getStoredAccounts().map { it.toDomain() }
|
||||
override suspend fun getAccounts() = factory.cache.getAccounts().map { it.toDomain() }
|
||||
|
||||
override suspend fun setCurrentAccount(id: String) {
|
||||
factory.cache.setCurrentAccount(id)
|
||||
}
|
||||
}
|
|
@ -5,13 +5,21 @@ import com.agileburo.anytype.data.auth.model.WalletEntity
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AuthDataStore {
|
||||
suspend fun selectAccount(id: String, path: String): AccountEntity
|
||||
|
||||
suspend fun startAccount(id: String, path: String): AccountEntity
|
||||
|
||||
suspend fun createAccount(name: String, avatarPath: String?): AccountEntity
|
||||
|
||||
suspend fun recoverAccount()
|
||||
|
||||
suspend fun saveAccount(account: AccountEntity)
|
||||
|
||||
suspend fun updateAccount(account: AccountEntity)
|
||||
|
||||
fun observeAccounts(): Flow<AccountEntity>
|
||||
|
||||
suspend fun getAccount(): AccountEntity
|
||||
suspend fun getCurrentAccount(): AccountEntity
|
||||
suspend fun getCurrentAccountId(): String
|
||||
|
||||
suspend fun createWallet(path: String): WalletEntity
|
||||
suspend fun recoverWallet(path: String, mnemonic: String)
|
||||
|
@ -19,5 +27,6 @@ interface AuthDataStore {
|
|||
suspend fun getMnemonic(): String
|
||||
|
||||
suspend fun logout()
|
||||
suspend fun getStoredAccounts(): List<AccountEntity>
|
||||
suspend fun getAccounts(): List<AccountEntity>
|
||||
suspend fun setCurrentAccount(id: String)
|
||||
}
|
|
@ -5,7 +5,7 @@ import com.agileburo.anytype.data.auth.model.WalletEntity
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AuthRemote {
|
||||
suspend fun selectAccount(id: String, path: String): AccountEntity
|
||||
suspend fun startAccount(id: String, path: String): AccountEntity
|
||||
suspend fun createAccount(name: String, avatarPath: String?): AccountEntity
|
||||
suspend fun recoverAccount()
|
||||
fun observeAccounts(): Flow<AccountEntity>
|
||||
|
|
|
@ -7,9 +7,9 @@ class AuthRemoteDataStore(
|
|||
private val authRemote: AuthRemote
|
||||
) : AuthDataStore {
|
||||
|
||||
override suspend fun selectAccount(
|
||||
override suspend fun startAccount(
|
||||
id: String, path: String
|
||||
) = authRemote.selectAccount(id, path)
|
||||
) = authRemote.startAccount(id, path)
|
||||
|
||||
override suspend fun createAccount(
|
||||
name: String,
|
||||
|
@ -46,11 +46,23 @@ class AuthRemoteDataStore(
|
|||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override suspend fun getStoredAccounts(): List<AccountEntity> {
|
||||
override suspend fun getAccounts(): List<AccountEntity> {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override suspend fun getAccount(): AccountEntity {
|
||||
override suspend fun getCurrentAccount(): AccountEntity {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override suspend fun setCurrentAccount(id: String) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override suspend fun getCurrentAccountId(): String {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override suspend fun updateAccount(account: AccountEntity) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
|
@ -50,17 +50,17 @@ class AuthDataRepositoryTest {
|
|||
)
|
||||
|
||||
authRemote.stub {
|
||||
onBlocking { selectAccount(id = id, path = path) } doReturn account
|
||||
onBlocking { startAccount(id = id, path = path) } doReturn account
|
||||
}
|
||||
|
||||
repo.selectAccount(
|
||||
repo.startAccount(
|
||||
id = id,
|
||||
path = path
|
||||
)
|
||||
|
||||
verifyZeroInteractions(authCache)
|
||||
|
||||
verify(authRemote, times(1)).selectAccount(
|
||||
verify(authRemote, times(1)).startAccount(
|
||||
id = id,
|
||||
path = path
|
||||
)
|
||||
|
@ -107,7 +107,7 @@ class AuthDataRepositoryTest {
|
|||
onBlocking { recoverAccount() } doReturn Unit
|
||||
}
|
||||
|
||||
repo.recoverAccount()
|
||||
repo.startLoadingAccounts()
|
||||
|
||||
verifyZeroInteractions(authCache)
|
||||
verify(authRemote, times(1)).recoverAccount()
|
||||
|
@ -164,13 +164,13 @@ class AuthDataRepositoryTest {
|
|||
)
|
||||
|
||||
authCache.stub {
|
||||
onBlocking { getAccount() } doReturn account
|
||||
onBlocking { getCurrentAccount() } doReturn account
|
||||
}
|
||||
|
||||
repo.getAccount()
|
||||
repo.getCurrentAccount()
|
||||
|
||||
verifyZeroInteractions(authRemote)
|
||||
verify(authCache, times(1)).getAccount()
|
||||
verify(authCache, times(1)).getCurrentAccount()
|
||||
verifyNoMoreInteractions(authCache)
|
||||
}
|
||||
|
||||
|
@ -235,7 +235,7 @@ class AuthDataRepositoryTest {
|
|||
onBlocking { getAccounts() } doReturn accounts
|
||||
}
|
||||
|
||||
repo.getAvailableAccounts()
|
||||
repo.getAccounts()
|
||||
|
||||
verifyZeroInteractions(authRemote)
|
||||
verify(authCache, times(1)).getAccounts()
|
||||
|
|
|
@ -15,7 +15,7 @@ class CheckAuthorizationStatus(
|
|||
) : BaseUseCase<AuthStatus, Unit>() {
|
||||
|
||||
override suspend fun run(params: Unit) = try {
|
||||
repository.getAvailableAccounts().let { accounts ->
|
||||
repository.getAccounts().let { accounts ->
|
||||
if (accounts.isNotEmpty())
|
||||
Either.Right(AuthStatus.AUTHORIZED)
|
||||
else
|
||||
|
|
|
@ -5,7 +5,7 @@ import com.agileburo.anytype.domain.base.BaseUseCase
|
|||
import com.agileburo.anytype.domain.base.Either
|
||||
|
||||
/**
|
||||
* Creates an account, then stores it.
|
||||
* Creates an account, then stores it and sets as current user account.
|
||||
*/
|
||||
open class CreateAccount(
|
||||
private val repository: AuthRepository
|
||||
|
@ -16,7 +16,10 @@ open class CreateAccount(
|
|||
name = params.name,
|
||||
avatarPath = params.avatarPath
|
||||
).let { account ->
|
||||
repository.saveAccount(account)
|
||||
with(repository) {
|
||||
saveAccount(account)
|
||||
setCurrentAccount(account.id)
|
||||
}
|
||||
}.let {
|
||||
Either.Right(it)
|
||||
}
|
||||
|
|
|
@ -7,16 +7,19 @@ import com.agileburo.anytype.domain.base.Either
|
|||
/**
|
||||
* Use case for selecting user account.
|
||||
*/
|
||||
class SelectAccount(
|
||||
class StartAccount(
|
||||
private val repository: AuthRepository
|
||||
) : BaseUseCase<Unit, SelectAccount.Params>() {
|
||||
) : BaseUseCase<Unit, StartAccount.Params>() {
|
||||
|
||||
override suspend fun run(params: Params) = try {
|
||||
repository.selectAccount(
|
||||
repository.startAccount(
|
||||
id = params.id,
|
||||
path = params.path
|
||||
).let { account ->
|
||||
repository.saveAccount(account)
|
||||
with(repository) {
|
||||
saveAccount(account)
|
||||
setCurrentAccount(account.id)
|
||||
}
|
||||
}.let {
|
||||
Either.Right(it)
|
||||
}
|
|
@ -12,7 +12,7 @@ class StartLoadingAccounts(
|
|||
) : BaseUseCase<Unit, StartLoadingAccounts.Params>() {
|
||||
|
||||
override suspend fun run(params: Params) = try {
|
||||
repository.recoverAccount().let {
|
||||
repository.startLoadingAccounts().let {
|
||||
Either.Right(it)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
|
|
|
@ -5,20 +5,43 @@ import com.agileburo.anytype.domain.auth.model.Wallet
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AuthRepository {
|
||||
suspend fun selectAccount(id: String, path: String): Account
|
||||
|
||||
/**
|
||||
* Launches an account.
|
||||
* @param id user account id
|
||||
* @param path wallet repository path
|
||||
*/
|
||||
suspend fun startAccount(id: String, path: String): Account
|
||||
|
||||
suspend fun createAccount(name: String, avatarPath: String?): Account
|
||||
suspend fun recoverAccount()
|
||||
|
||||
suspend fun startLoadingAccounts()
|
||||
|
||||
suspend fun saveAccount(account: Account)
|
||||
|
||||
suspend fun updateAccount(account: Account)
|
||||
|
||||
fun observeAccounts(): Flow<Account>
|
||||
|
||||
suspend fun getAccount(): Account
|
||||
suspend fun getCurrentAccount(): Account
|
||||
|
||||
suspend fun getCurrentAccountId(): String
|
||||
|
||||
suspend fun createWallet(path: String): Wallet
|
||||
|
||||
suspend fun recoverWallet(path: String, mnemonic: String)
|
||||
|
||||
suspend fun saveMnemonic(mnemonic: String)
|
||||
|
||||
suspend fun getMnemonic(): String
|
||||
|
||||
suspend fun logout()
|
||||
|
||||
suspend fun getAvailableAccounts(): List<Account>
|
||||
suspend fun getAccounts(): List<Account>
|
||||
|
||||
/**
|
||||
* Sets currently selected user account
|
||||
* @param id account's id
|
||||
*/
|
||||
suspend fun setCurrentAccount(id: String)
|
||||
}
|
|
@ -8,12 +8,12 @@ import com.agileburo.anytype.domain.base.Either
|
|||
/** Use case for getting currently selected user account.
|
||||
* @property repository repository containing user account
|
||||
*/
|
||||
class GetAccount(
|
||||
class GetCurrentAccount(
|
||||
private val repository: AuthRepository
|
||||
) : BaseUseCase<Account, BaseUseCase.None>() {
|
||||
|
||||
override suspend fun run(params: None) = try {
|
||||
repository.getAccount().let {
|
||||
repository.getCurrentAccount().let {
|
||||
Either.Right(it)
|
||||
}
|
||||
} catch (t: Throwable) {
|
|
@ -0,0 +1,25 @@
|
|||
package com.agileburo.anytype.domain.launch
|
||||
|
||||
import com.agileburo.anytype.domain.auth.repo.AuthRepository
|
||||
import com.agileburo.anytype.domain.auth.repo.PathProvider
|
||||
import com.agileburo.anytype.domain.base.BaseUseCase
|
||||
import com.agileburo.anytype.domain.base.Either
|
||||
|
||||
class LaunchAccount(
|
||||
private val repository: AuthRepository,
|
||||
private val pathProvider: PathProvider
|
||||
) : BaseUseCase<Unit, BaseUseCase.None>() {
|
||||
|
||||
override suspend fun run(params: None) = try {
|
||||
repository.startAccount(
|
||||
id = repository.getCurrentAccountId(),
|
||||
path = pathProvider.providePath()
|
||||
).let { account ->
|
||||
repository.updateAccount(account)
|
||||
}.let {
|
||||
Either.Right(it)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Either.Left(e)
|
||||
}
|
||||
}
|
|
@ -38,14 +38,14 @@ class CheckAuthorizationStatusTest {
|
|||
fun `should return unauthorized status if account list is empty`() = runBlocking {
|
||||
|
||||
repo.stub {
|
||||
onBlocking { getAvailableAccounts() } doReturn emptyList()
|
||||
onBlocking { getAccounts() } doReturn emptyList()
|
||||
}
|
||||
|
||||
val result = checkAuthorizationStatus.run(params = Unit)
|
||||
|
||||
assertTrue { result == Either.Right(AuthStatus.UNAUTHORIZED) }
|
||||
|
||||
verify(repo, times(1)).getAvailableAccounts()
|
||||
verify(repo, times(1)).getAccounts()
|
||||
verifyNoMoreInteractions(repo)
|
||||
}
|
||||
|
||||
|
@ -59,14 +59,14 @@ class CheckAuthorizationStatusTest {
|
|||
)
|
||||
|
||||
repo.stub {
|
||||
onBlocking { getAvailableAccounts() } doReturn listOf(account)
|
||||
onBlocking { getAccounts() } doReturn listOf(account)
|
||||
}
|
||||
|
||||
val result = checkAuthorizationStatus.run(params = Unit)
|
||||
|
||||
assertTrue { result == Either.Right(AuthStatus.AUTHORIZED) }
|
||||
|
||||
verify(repo, times(1)).getAvailableAccounts()
|
||||
verify(repo, times(1)).getAccounts()
|
||||
verifyNoMoreInteractions(repo)
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ class CreateAccountTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `should create account and save it by calling repository method`() = runBlocking {
|
||||
fun `should create account and save it and set as current user account`() = runBlocking {
|
||||
|
||||
val name = DataFactory.randomString()
|
||||
|
||||
|
@ -56,7 +56,8 @@ class CreateAccountTest {
|
|||
createAccount.run(param)
|
||||
|
||||
verify(repo, times(1)).createAccount(name, path)
|
||||
verify(repo, times(1)).saveAccount(any())
|
||||
verify(repo, times(1)).saveAccount(account)
|
||||
verify(repo, times(1)).setCurrentAccount(account.id)
|
||||
verifyNoMoreInteractions(repo)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package com.agileburo.anytype.domain.auth
|
||||
|
||||
import com.agileburo.anytype.domain.auth.interactor.SelectAccount
|
||||
import com.agileburo.anytype.domain.auth.interactor.StartAccount
|
||||
import com.agileburo.anytype.domain.auth.model.Account
|
||||
import com.agileburo.anytype.domain.auth.repo.AuthRepository
|
||||
import com.agileburo.anytype.domain.base.Either
|
||||
|
@ -16,7 +16,7 @@ import org.mockito.Mock
|
|||
import org.mockito.MockitoAnnotations
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class SelectAccountTest {
|
||||
class StartAccountTest {
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@get:Rule
|
||||
|
@ -25,21 +25,21 @@ class SelectAccountTest {
|
|||
@Mock
|
||||
lateinit var repo: AuthRepository
|
||||
|
||||
lateinit var selectAccount: SelectAccount
|
||||
lateinit var startAccount: StartAccount
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
selectAccount = SelectAccount(repo)
|
||||
startAccount = StartAccount(repo)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should select account and save it`() = runBlocking {
|
||||
fun `should select account, set it as current user account and save it`() = runBlocking {
|
||||
|
||||
val id = DataFactory.randomString()
|
||||
val path = DataFactory.randomString()
|
||||
|
||||
val params = SelectAccount.Params(
|
||||
val params = StartAccount.Params(
|
||||
id = id,
|
||||
path = path
|
||||
)
|
||||
|
@ -52,22 +52,24 @@ class SelectAccountTest {
|
|||
|
||||
repo.stub {
|
||||
onBlocking {
|
||||
selectAccount(
|
||||
startAccount(
|
||||
id = id,
|
||||
path = path
|
||||
)
|
||||
} doReturn account
|
||||
}
|
||||
|
||||
selectAccount.run(params)
|
||||
startAccount.run(params)
|
||||
|
||||
verify(repo, times(1)).selectAccount(
|
||||
verify(repo, times(1)).startAccount(
|
||||
id = id,
|
||||
path = path
|
||||
)
|
||||
|
||||
verify(repo, times(1)).saveAccount(account)
|
||||
|
||||
verify(repo, times(1)).setCurrentAccount(account.id)
|
||||
|
||||
verifyNoMoreInteractions(repo)
|
||||
}
|
||||
|
||||
|
@ -77,7 +79,7 @@ class SelectAccountTest {
|
|||
val id = DataFactory.randomString()
|
||||
val path = DataFactory.randomString()
|
||||
|
||||
val params = SelectAccount.Params(
|
||||
val params = StartAccount.Params(
|
||||
id = id,
|
||||
path = path
|
||||
)
|
||||
|
@ -90,14 +92,14 @@ class SelectAccountTest {
|
|||
|
||||
repo.stub {
|
||||
onBlocking {
|
||||
selectAccount(
|
||||
startAccount(
|
||||
id = id,
|
||||
path = path
|
||||
)
|
||||
} doReturn account
|
||||
}
|
||||
|
||||
val result = selectAccount.run(params)
|
||||
val result = startAccount.run(params)
|
||||
|
||||
assertTrue { result == Either.Right(Unit) }
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
package com.agileburo.anytype.middleware
|
||||
|
||||
import anytype.Events
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface EventProxy {
|
||||
fun flow(): Flow<Event>
|
||||
fun flow(): Flow<Events.Event>
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
package com.agileburo.anytype.middleware.auth
|
||||
|
||||
import anytype.Events
|
||||
import com.agileburo.anytype.data.auth.model.AccountEntity
|
||||
import com.agileburo.anytype.data.auth.model.WalletEntity
|
||||
import com.agileburo.anytype.data.auth.repo.AuthRemote
|
||||
import com.agileburo.anytype.middleware.Event
|
||||
import com.agileburo.anytype.middleware.EventProxy
|
||||
import com.agileburo.anytype.middleware.interactor.Middleware
|
||||
import com.agileburo.anytype.middleware.toEntity
|
||||
|
@ -17,13 +17,13 @@ class AuthMiddleware(
|
|||
private val events: EventProxy
|
||||
) : AuthRemote {
|
||||
|
||||
override suspend fun selectAccount(
|
||||
override suspend fun startAccount(
|
||||
id: String, path: String
|
||||
) = middleware.selectAccount(id, path).let { response ->
|
||||
AccountEntity(
|
||||
id = response.id,
|
||||
name = response.name,
|
||||
avatar = null
|
||||
avatar = response.avatar?.toEntity()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -47,13 +47,13 @@ class AuthMiddleware(
|
|||
|
||||
override fun observeAccounts() = events
|
||||
.flow()
|
||||
.filter { event -> event is Event.AccountAdd }
|
||||
.map { event -> event as Event.AccountAdd }
|
||||
.filter { event -> event.messageCase == Events.Event.MessageCase.ACCOUNTADD }
|
||||
.map { event ->
|
||||
|
||||
AccountEntity(
|
||||
id = event.id,
|
||||
name = event.name,
|
||||
avatar = null
|
||||
id = event.accountAdd.account.id,
|
||||
name = event.accountAdd.account.name,
|
||||
avatar = event.accountAdd.account.avatar.toEntity()
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
package com.agileburo.anytype.middleware.interactor
|
||||
|
||||
import anytype.Events
|
||||
import com.agileburo.anytype.middleware.Event
|
||||
import com.agileburo.anytype.middleware.EventProxy
|
||||
import com.google.protobuf.InvalidProtocolBufferException
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.consumeAsFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import lib.Lib
|
||||
import timber.log.Timber
|
||||
|
||||
|
@ -31,16 +28,7 @@ class Handler : EventProxy {
|
|||
}
|
||||
}
|
||||
|
||||
private fun events(): Flow<Event> = channel
|
||||
.consumeAsFlow()
|
||||
.onEach { Timber.d(it.toString()) }
|
||||
.map { event ->
|
||||
Event.AccountAdd(
|
||||
id = event.accountAdd.account.id,
|
||||
name = event.accountAdd.account.name,
|
||||
index = event.accountAdd.index.toInt()
|
||||
)
|
||||
}
|
||||
private fun events(): Flow<Events.Event> = channel.consumeAsFlow()
|
||||
|
||||
override fun flow(): Flow<Event> = events()
|
||||
override fun flow(): Flow<Events.Event> = events()
|
||||
}
|
|
@ -136,7 +136,8 @@ public class Middleware {
|
|||
} else {
|
||||
return new SelectAccountResponse(
|
||||
response.getAccount().getId(),
|
||||
response.getAccount().getName()
|
||||
response.getAccount().getName(),
|
||||
response.getAccount().getAvatar()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package com.agileburo.anytype.middleware.model
|
||||
|
||||
import anytype.Models
|
||||
|
||||
class SelectAccountResponse(
|
||||
val id: String,
|
||||
val name: String
|
||||
val name: String,
|
||||
val avatar: Models.Image? = null
|
||||
)
|
|
@ -12,4 +12,6 @@ object Config {
|
|||
|
||||
const val QUERY_LAST_ACCOUNT =
|
||||
"SELECT * FROM $ACCOUNT_TABLE_NAME ORDER BY timestamp DESC LIMIT 1"
|
||||
|
||||
const val QUERY_ACCOUNT_BY_ID = "SELECT * FROM $ACCOUNT_TABLE_NAME WHERE id = :id"
|
||||
}
|
|
@ -14,6 +14,9 @@ abstract class AccountDao : BaseDao<AccountTable> {
|
|||
@Query(Config.QUERY_LAST_ACCOUNT)
|
||||
abstract suspend fun lastAccount(): List<AccountTable>
|
||||
|
||||
@Query(Config.QUERY_ACCOUNT_BY_ID)
|
||||
abstract suspend fun getAccount(id: String): AccountTable?
|
||||
|
||||
@Query(Config.GET_ACCOUNTS)
|
||||
abstract suspend fun getAccounts(): List<AccountTable>
|
||||
}
|
|
@ -17,6 +17,20 @@ fun AccountTable.toEntity(): AccountEntity {
|
|||
)
|
||||
}
|
||||
|
||||
fun AccountEntity.toTable(): AccountTable {
|
||||
return AccountTable(
|
||||
id = id,
|
||||
name = name,
|
||||
timestamp = System.currentTimeMillis(),
|
||||
avatar = avatar?.let { avatar ->
|
||||
AccountTable.Avatar(
|
||||
avatarId = avatar.id,
|
||||
sizes = avatar.sizes.map { it.toTable() }
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun ImageEntity.Size.toTable(): AccountTable.Size = when (this) {
|
||||
ImageEntity.Size.SMALL -> AccountTable.Size.SMALL
|
||||
ImageEntity.Size.THUMB -> AccountTable.Size.THUMB
|
||||
|
|
|
@ -6,7 +6,6 @@ import com.agileburo.anytype.data.auth.repo.AuthCache
|
|||
import com.agileburo.anytype.db.AnytypeDatabase
|
||||
import com.agileburo.anytype.mapper.toEntity
|
||||
import com.agileburo.anytype.mapper.toTable
|
||||
import com.agileburo.anytype.model.AccountTable
|
||||
import timber.log.Timber
|
||||
|
||||
class DefaultAuthCache(
|
||||
|
@ -15,26 +14,21 @@ class DefaultAuthCache(
|
|||
) : AuthCache {
|
||||
|
||||
override suspend fun saveAccount(account: AccountEntity) {
|
||||
db.accountDao().insert(
|
||||
AccountTable(
|
||||
id = account.id,
|
||||
name = account.name,
|
||||
timestamp = System.currentTimeMillis(),
|
||||
avatar = account.avatar?.let { avatar ->
|
||||
AccountTable.Avatar(
|
||||
avatarId = avatar.id,
|
||||
sizes = avatar.sizes.map { it.toTable() }
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
db.accountDao().insert(account.toTable())
|
||||
}
|
||||
|
||||
override suspend fun getAccount() = db.accountDao().lastAccount().let { list ->
|
||||
if (list.isEmpty())
|
||||
throw IllegalStateException("Could not found user account")
|
||||
else
|
||||
list.first().toEntity()
|
||||
override suspend fun updateAccount(account: AccountEntity) {
|
||||
db.accountDao().update(account.toTable())
|
||||
}
|
||||
|
||||
override suspend fun getCurrentAccount() = getCurrentAccountId().let { id ->
|
||||
db.accountDao().getAccount(id)?.toEntity()
|
||||
?: throw IllegalStateException("Account with the following id not found: $id")
|
||||
}
|
||||
|
||||
override suspend fun getCurrentAccountId(): String {
|
||||
val id: String? = prefs.getString(CURRENT_ACCOUNT_ID_KEY, null)
|
||||
return id ?: throw IllegalStateException("Current account not set")
|
||||
}
|
||||
|
||||
override suspend fun saveMnemonic(mnemonic: String) {
|
||||
|
@ -52,12 +46,15 @@ class DefaultAuthCache(
|
|||
prefs.edit().putString(MNEMONIC_KEY, null).apply()
|
||||
}
|
||||
|
||||
override suspend fun getAccounts(): List<AccountEntity> =
|
||||
db.accountDao()
|
||||
.getAccounts()
|
||||
.map { it.toEntity() }
|
||||
override suspend fun getAccounts() = db.accountDao().getAccounts().map { it.toEntity() }
|
||||
|
||||
|
||||
override suspend fun setCurrentAccount(id: String) {
|
||||
prefs.edit().putString(CURRENT_ACCOUNT_ID_KEY, id).apply()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MNEMONIC_KEY = "mnemonic"
|
||||
const val CURRENT_ACCOUNT_ID_KEY = "current_account"
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import org.junit.Test
|
|||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
|
@ -86,4 +87,24 @@ class AccountDaoTest {
|
|||
assertTrue { result.size == 1 }
|
||||
assertTrue { result.first() == account }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return expected account when queried using account id`() = runBlocking {
|
||||
|
||||
val account = AccountTable(
|
||||
id = MockDataFactory.randomString(),
|
||||
name = MockDataFactory.randomString(),
|
||||
timestamp = System.currentTimeMillis(),
|
||||
avatar = AccountTable.Avatar(
|
||||
avatarId = MockDataFactory.randomString(),
|
||||
sizes = listOf(AccountTable.Size.LARGE, AccountTable.Size.SMALL)
|
||||
)
|
||||
)
|
||||
|
||||
database.accountDao().insert(account)
|
||||
|
||||
val result = database.accountDao().getAccount(account.id)
|
||||
|
||||
assertEquals(account, result)
|
||||
}
|
||||
}
|
|
@ -4,23 +4,23 @@ import androidx.lifecycle.MutableLiveData
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.agileburo.anytype.core_utils.common.Event
|
||||
import com.agileburo.anytype.domain.auth.interactor.SelectAccount
|
||||
import com.agileburo.anytype.domain.auth.interactor.StartAccount
|
||||
import com.agileburo.anytype.domain.auth.repo.PathProvider
|
||||
import com.agileburo.anytype.presentation.navigation.AppNavigation
|
||||
import com.agileburo.anytype.presentation.navigation.SupportNavigation
|
||||
import timber.log.Timber
|
||||
|
||||
class SetupSelectedAccountViewModel(
|
||||
private val selectAccount: SelectAccount,
|
||||
private val startAccount: StartAccount,
|
||||
private val pathProvider: PathProvider
|
||||
) : ViewModel(), SupportNavigation<Event<AppNavigation.Command>> {
|
||||
|
||||
override val navigation: MutableLiveData<Event<AppNavigation.Command>> = MutableLiveData()
|
||||
|
||||
fun selectAccount(id: String) {
|
||||
selectAccount.invoke(
|
||||
startAccount.invoke(
|
||||
scope = viewModelScope,
|
||||
params = SelectAccount.Params(
|
||||
params = StartAccount.Params(
|
||||
id = id,
|
||||
path = pathProvider.providePath()
|
||||
)
|
||||
|
|
|
@ -2,18 +2,18 @@ package com.agileburo.anytype.presentation.auth.account
|
|||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.agileburo.anytype.domain.auth.interactor.SelectAccount
|
||||
import com.agileburo.anytype.domain.auth.interactor.StartAccount
|
||||
import com.agileburo.anytype.domain.auth.repo.PathProvider
|
||||
|
||||
class SetupSelectedAccountViewModelFactory(
|
||||
private val selectAccount: SelectAccount,
|
||||
private val startAccount: StartAccount,
|
||||
private val pathProvider: PathProvider
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return SetupSelectedAccountViewModel(
|
||||
selectAccount = selectAccount,
|
||||
startAccount = startAccount,
|
||||
pathProvider = pathProvider
|
||||
) as T
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ class KeychainLoginViewModel(
|
|||
) { result ->
|
||||
result.either(
|
||||
fnR = {
|
||||
navigation.postValue(Event(AppNavigation.Command.ChooseProfileScreen))
|
||||
navigation.postValue(Event(AppNavigation.Command.ChooseAccountScreen))
|
||||
},
|
||||
fnL = { Timber.e(it, "Error while saving mnemonic: $mnemonic") }
|
||||
)
|
||||
|
|
|
@ -33,7 +33,7 @@ class StartLoginViewModel(
|
|||
Timber.e(it, "Error while setting up wallet")
|
||||
},
|
||||
fnR = {
|
||||
navigation.postValue(Event(AppNavigation.Command.OpenCreateProfile))
|
||||
navigation.postValue(Event(AppNavigation.Command.OpenCreateAccount))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import com.agileburo.anytype.core_utils.ui.ViewState
|
|||
import com.agileburo.anytype.core_utils.ui.ViewStateViewModel
|
||||
import com.agileburo.anytype.domain.auth.model.Account
|
||||
import com.agileburo.anytype.domain.base.BaseUseCase
|
||||
import com.agileburo.anytype.domain.desktop.interactor.GetAccount
|
||||
import com.agileburo.anytype.domain.desktop.interactor.GetCurrentAccount
|
||||
import com.agileburo.anytype.domain.image.LoadImage
|
||||
import com.agileburo.anytype.presentation.navigation.AppNavigation
|
||||
import com.agileburo.anytype.presentation.navigation.SupportNavigation
|
||||
|
@ -17,7 +17,7 @@ import timber.log.Timber
|
|||
|
||||
class DesktopViewModel(
|
||||
private val loadImage: LoadImage,
|
||||
private val getAccount: GetAccount
|
||||
private val getCurrentAccount: GetCurrentAccount
|
||||
) : ViewStateViewModel<ViewState<List<DesktopView>>>(),
|
||||
SupportNavigation<Event<AppNavigation.Command>> {
|
||||
|
||||
|
@ -33,7 +33,7 @@ class DesktopViewModel(
|
|||
override val navigation = MutableLiveData<Event<AppNavigation.Command>>()
|
||||
|
||||
private fun proceedWithGettingAccount() {
|
||||
getAccount.invoke(viewModelScope, BaseUseCase.None) { result ->
|
||||
getCurrentAccount.invoke(viewModelScope, BaseUseCase.None) { result ->
|
||||
result.either(
|
||||
fnL = { e -> Timber.e(e, "Error while getting account") },
|
||||
fnR = { account ->
|
||||
|
|
|
@ -2,18 +2,18 @@ package com.agileburo.anytype.presentation.desktop
|
|||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.agileburo.anytype.domain.desktop.interactor.GetAccount
|
||||
import com.agileburo.anytype.domain.desktop.interactor.GetCurrentAccount
|
||||
import com.agileburo.anytype.domain.image.LoadImage
|
||||
|
||||
class DesktopViewModelFactory(
|
||||
private val getAccount: GetAccount,
|
||||
private val getCurrentAccount: GetCurrentAccount,
|
||||
private val loadImage: LoadImage
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return DesktopViewModel(
|
||||
getAccount = getAccount,
|
||||
getCurrentAccount = getCurrentAccount,
|
||||
loadImage = loadImage
|
||||
) as T
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.agileburo.anytype.presentation.navigation
|
||||
|
||||
interface AppNavigation {
|
||||
|
||||
fun startLogin()
|
||||
fun createProfile()
|
||||
fun enterKeychain()
|
||||
|
@ -9,7 +10,7 @@ interface AppNavigation {
|
|||
fun setupNewAccount()
|
||||
fun setupSelectedAccount(id: String)
|
||||
fun congratulation()
|
||||
fun chooseProfile()
|
||||
fun chooseAccount()
|
||||
fun workspace()
|
||||
fun openProfile()
|
||||
fun openDocument(id: String)
|
||||
|
@ -20,13 +21,13 @@ interface AppNavigation {
|
|||
|
||||
sealed class Command {
|
||||
object OpenStartLoginScreen : Command()
|
||||
object OpenCreateProfile : Command()
|
||||
object OpenCreateAccount : Command()
|
||||
object ChoosePinCodeScreen : Command()
|
||||
object SetupNewAccountScreen : Command()
|
||||
data class SetupSelectedAccountScreen(val id: String) : Command()
|
||||
data class ConfirmPinCodeScreen(val code: String) : Command()
|
||||
object CongratulationScreen : Command()
|
||||
object ChooseProfileScreen : Command()
|
||||
object ChooseAccountScreen : Command()
|
||||
object EnterKeyChainScreen : Command()
|
||||
object WorkspaceScreen : Command()
|
||||
data class OpenDocument(val id: String) : Command()
|
||||
|
|
|
@ -9,14 +9,14 @@ import com.agileburo.anytype.core_utils.ui.ViewStateViewModel
|
|||
import com.agileburo.anytype.domain.auth.interactor.Logout
|
||||
import com.agileburo.anytype.domain.auth.model.Account
|
||||
import com.agileburo.anytype.domain.base.BaseUseCase
|
||||
import com.agileburo.anytype.domain.desktop.interactor.GetAccount
|
||||
import com.agileburo.anytype.domain.desktop.interactor.GetCurrentAccount
|
||||
import com.agileburo.anytype.domain.image.LoadImage
|
||||
import com.agileburo.anytype.presentation.navigation.AppNavigation
|
||||
import com.agileburo.anytype.presentation.navigation.SupportNavigation
|
||||
import timber.log.Timber
|
||||
|
||||
class ProfileViewModel(
|
||||
private val getAccount: GetAccount,
|
||||
private val getCurrentAccount: GetCurrentAccount,
|
||||
private val loadImage: LoadImage,
|
||||
private val logout: Logout
|
||||
) : ViewStateViewModel<ViewState<ProfileView>>(), SupportNavigation<Event<AppNavigation.Command>> {
|
||||
|
@ -36,8 +36,12 @@ class ProfileViewModel(
|
|||
// TODO dispatch navigation command
|
||||
}
|
||||
|
||||
fun onAddProfileClicked() {
|
||||
navigation.postValue(Event(AppNavigation.Command.OpenCreateAccount))
|
||||
}
|
||||
|
||||
private fun proceedWithGettingAccount() {
|
||||
getAccount.invoke(viewModelScope, BaseUseCase.None) { result ->
|
||||
getCurrentAccount.invoke(viewModelScope, BaseUseCase.None) { result ->
|
||||
result.either(
|
||||
fnL = { e -> Timber.e(e, "Error while getting account") },
|
||||
fnR = { account ->
|
||||
|
|
|
@ -3,12 +3,12 @@ package com.agileburo.anytype.presentation.profile
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.agileburo.anytype.domain.auth.interactor.Logout
|
||||
import com.agileburo.anytype.domain.desktop.interactor.GetAccount
|
||||
import com.agileburo.anytype.domain.desktop.interactor.GetCurrentAccount
|
||||
import com.agileburo.anytype.domain.image.LoadImage
|
||||
|
||||
class ProfileViewModelFactory(
|
||||
private val logout: Logout,
|
||||
private val getAccount: GetAccount,
|
||||
private val getCurrentAccount: GetCurrentAccount,
|
||||
private val loadImage: LoadImage
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
|
@ -16,7 +16,7 @@ class ProfileViewModelFactory(
|
|||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return ProfileViewModel(
|
||||
logout = logout,
|
||||
getAccount = getAccount,
|
||||
getCurrentAccount = getCurrentAccount,
|
||||
loadImage = loadImage
|
||||
) as T
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import androidx.lifecycle.viewModelScope
|
|||
import com.agileburo.anytype.core_utils.common.Event
|
||||
import com.agileburo.anytype.domain.auth.interactor.CheckAuthorizationStatus
|
||||
import com.agileburo.anytype.domain.auth.model.AuthStatus
|
||||
import com.agileburo.anytype.domain.base.BaseUseCase
|
||||
import com.agileburo.anytype.domain.launch.LaunchAccount
|
||||
import com.agileburo.anytype.presentation.navigation.AppNavigation
|
||||
import timber.log.Timber
|
||||
|
||||
|
@ -14,23 +16,33 @@ import timber.log.Timber
|
|||
* email : ki@agileburo.com
|
||||
* on 2019-10-21.
|
||||
*/
|
||||
class SplashViewModel(private val checkAuthorizationStatus: CheckAuthorizationStatus) :
|
||||
ViewModel() {
|
||||
class SplashViewModel(
|
||||
private val checkAuthorizationStatus: CheckAuthorizationStatus,
|
||||
private val launchAccount: LaunchAccount
|
||||
) : ViewModel() {
|
||||
|
||||
val navigation: MutableLiveData<Event<AppNavigation.Command>> = MutableLiveData()
|
||||
|
||||
fun onViewCreated() {
|
||||
checkAuthorizationStatus.invoke(viewModelScope, Unit) {
|
||||
it.either(
|
||||
checkAuthorizationStatus.invoke(viewModelScope, Unit) { result ->
|
||||
result.either(
|
||||
fnL = { e -> Timber.e(e, "Error while checking auth status") },
|
||||
fnR = ::proceedWithAuthStatus
|
||||
fnR = { status ->
|
||||
if (status == AuthStatus.UNAUTHORIZED)
|
||||
navigation.postValue(Event(AppNavigation.Command.OpenStartLoginScreen))
|
||||
else
|
||||
proceedWithLaunchingAccount()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedWithAuthStatus(status: AuthStatus) =
|
||||
if (status == AuthStatus.UNAUTHORIZED)
|
||||
navigation.postValue(Event(AppNavigation.Command.OpenStartLoginScreen))
|
||||
else
|
||||
navigation.postValue(Event(AppNavigation.Command.StartDesktopFromSplash))
|
||||
private fun proceedWithLaunchingAccount() {
|
||||
launchAccount.invoke(viewModelScope, BaseUseCase.None) { result ->
|
||||
result.either(
|
||||
fnR = { navigation.postValue(Event(AppNavigation.Command.StartDesktopFromSplash)) },
|
||||
fnL = { e -> Timber.e(e, "Error while launching account") }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,16 +3,22 @@ package com.agileburo.anytype.presentation.splash
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.agileburo.anytype.domain.auth.interactor.CheckAuthorizationStatus
|
||||
import com.agileburo.anytype.domain.launch.LaunchAccount
|
||||
|
||||
/**
|
||||
* Created by Konstantin Ivanov
|
||||
* email : ki@agileburo.com
|
||||
* on 2019-10-21.
|
||||
*/
|
||||
class SplashViewModelFactory(private val checkAuthorizationStatus: CheckAuthorizationStatus) :
|
||||
ViewModelProvider.Factory {
|
||||
class SplashViewModelFactory(
|
||||
private val checkAuthorizationStatus: CheckAuthorizationStatus,
|
||||
private val launchAccount: LaunchAccount
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
|
||||
SplashViewModel(checkAuthorizationStatus) as T
|
||||
SplashViewModel(
|
||||
checkAuthorizationStatus = checkAuthorizationStatus,
|
||||
launchAccount = launchAccount
|
||||
) as T
|
||||
}
|
|
@ -47,7 +47,7 @@ class StartLoginViewModelTest {
|
|||
|
||||
vm.onSignUpClicked()
|
||||
|
||||
testObserver.assertValue(NavigationCommand.OpenCreateProfile)
|
||||
testObserver.assertValue(NavigationCommand.OpenCreateAccount)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -4,6 +4,7 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
|||
import com.agileburo.anytype.domain.auth.interactor.CheckAuthorizationStatus
|
||||
import com.agileburo.anytype.domain.auth.model.AuthStatus
|
||||
import com.agileburo.anytype.domain.base.Either
|
||||
import com.agileburo.anytype.domain.launch.LaunchAccount
|
||||
import com.agileburo.anytype.presentation.navigation.AppNavigation
|
||||
import com.jraska.livedata.test
|
||||
import com.nhaarman.mockitokotlin2.*
|
||||
|
@ -22,13 +23,19 @@ class SplashViewModelTest {
|
|||
@Mock
|
||||
lateinit var checkAuthorizationStatus: CheckAuthorizationStatus
|
||||
|
||||
@Mock
|
||||
lateinit var launchAccount: LaunchAccount
|
||||
|
||||
lateinit var vm: SplashViewModel
|
||||
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
vm = SplashViewModel(checkAuthorizationStatus)
|
||||
vm = SplashViewModel(
|
||||
checkAuthorizationStatus = checkAuthorizationStatus,
|
||||
launchAccount = launchAccount
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -44,7 +51,7 @@ class SplashViewModelTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `should emit appropriate navigation command if user is authorized`() {
|
||||
fun `should start launching account if user is authorized`() {
|
||||
|
||||
val status = AuthStatus.AUTHORIZED
|
||||
|
||||
|
@ -58,6 +65,30 @@ class SplashViewModelTest {
|
|||
|
||||
vm.onViewCreated()
|
||||
|
||||
verify(launchAccount, times(1)).invoke(any(), any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should emit appropriate navigation command if account is launched`() {
|
||||
|
||||
val status = AuthStatus.AUTHORIZED
|
||||
|
||||
val response = Either.Right(status)
|
||||
|
||||
checkAuthorizationStatus.stub {
|
||||
on { invoke(any(), any(), any()) } doAnswer { answer ->
|
||||
answer.getArgument<(Either<Throwable, AuthStatus>) -> Unit>(2)(response)
|
||||
}
|
||||
}
|
||||
|
||||
launchAccount.stub {
|
||||
on { invoke(any(), any(), any()) } doAnswer { answer ->
|
||||
answer.getArgument<(Either<Throwable, Unit>) -> Unit>(2)(Either.Right(Unit))
|
||||
}
|
||||
}
|
||||
|
||||
vm.onViewCreated()
|
||||
|
||||
vm.navigation.test().assertValue { value ->
|
||||
value.peekContent() == AppNavigation.Command.StartDesktopFromSplash
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue