1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 05:47:05 +09:00

App | Enhancement | Logout warning (#2121)

This commit is contained in:
Evgenii Kozlov 2022-02-23 19:47:15 +03:00 committed by GitHub
parent 793f0cc180
commit 3d2239bee3
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 306 additions and 64 deletions

View file

@ -11,6 +11,7 @@ import com.anytypeio.anytype.di.feature.sets.viewer.ViewerCardSizeSelectModule
import com.anytypeio.anytype.di.feature.sets.viewer.ViewerImagePreviewSelectModule
import com.anytypeio.anytype.di.feature.settings.AboutAppModule
import com.anytypeio.anytype.di.feature.settings.AccountAndDataModule
import com.anytypeio.anytype.di.feature.settings.LogoutWarningModule
import com.anytypeio.anytype.di.feature.wallpaper.WallpaperSelectModule
import com.anytypeio.anytype.di.main.MainComponent
@ -607,10 +608,15 @@ class ComponentManager(private val main: MainComponent) {
val aboutAppComponent = Component {
main.aboutAppComponent().module(AboutAppModule).build()
}
val accountAndDataComponent = Component {
main.accountAndDataComponent().module(AccountAndDataModule).build()
}
val logoutWarningComponent = Component {
main.logoutWarningComponent().module(LogoutWarningModule).build()
}
class Component<T>(private val builder: () -> T) {
private var instance: T? = null

View file

@ -30,11 +30,9 @@ object AccountAndDataModule {
@Provides
@PerScreen
fun provideViewModelFactory(
clearFileCache: ClearFileCache,
logout: Logout
clearFileCache: ClearFileCache
): AccountAndDataViewModel.Factory = AccountAndDataViewModel.Factory(
clearFileCache = clearFileCache,
logout = logout
clearFileCache = clearFileCache
)
@JvmStatic

View file

@ -0,0 +1,39 @@
package com.anytypeio.anytype.di.feature.settings
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
import com.anytypeio.anytype.domain.auth.interactor.Logout
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
import com.anytypeio.anytype.ui.settings.LogoutWarningFragment
import com.anytypeio.anytype.ui_settings.account.LogoutWarningViewModel
import dagger.Module
import dagger.Provides
import dagger.Subcomponent
@Subcomponent(modules = [LogoutWarningModule::class])
@PerScreen
interface LogoutWarningSubComponent {
@Subcomponent.Builder
interface Builder {
fun module(module: LogoutWarningModule): Builder
fun build(): LogoutWarningSubComponent
}
fun inject(fragment: LogoutWarningFragment)
}
@Module
object LogoutWarningModule {
@JvmStatic
@Provides
@PerScreen
fun provideViewModelFactory(
logout: Logout
): LogoutWarningViewModel.Factory = LogoutWarningViewModel.Factory(logout = logout)
@JvmStatic
@Provides
@PerScreen
fun logout(repo: AuthRepository): Logout = Logout(repo)
}

View file

@ -4,6 +4,7 @@ import com.anytypeio.anytype.app.AndroidApplication
import com.anytypeio.anytype.di.feature.*
import com.anytypeio.anytype.di.feature.settings.AboutAppSubComponent
import com.anytypeio.anytype.di.feature.settings.AccountAndDataSubComponent
import com.anytypeio.anytype.di.feature.settings.LogoutWarningSubComponent
import com.anytypeio.anytype.di.feature.wallpaper.WallpaperSelectSubComponent
import dagger.Component
import javax.inject.Singleton
@ -54,6 +55,7 @@ interface MainComponent {
fun debugSettingsBuilder(): DebugSettingsSubComponent.Builder
fun keychainPhraseComponentBuilder(): KeychainPhraseSubComponent.Builder
fun otherSettingsComponentBuilder(): OtherSettingsSubComponent.Builder
fun logoutWarningComponent() : LogoutWarningSubComponent.Builder
//endregion
}

View file

@ -9,9 +9,6 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_utils.ext.toast
@ -20,7 +17,6 @@ import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.ui.dashboard.ClearCacheAlertFragment
import com.anytypeio.anytype.ui_settings.account.AccountAndDataScreen
import com.anytypeio.anytype.ui_settings.account.AccountAndDataViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
class AccountAndDataFragment : BaseBottomSheetComposeFragment() {
@ -34,6 +30,10 @@ class AccountAndDataFragment : BaseBottomSheetComposeFragment() {
findNavController().navigate(R.id.keychainDialog)
}
private val onLogoutClicked = {
findNavController().navigate(R.id.logoutWarningScreen)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@ -48,7 +48,7 @@ class AccountAndDataFragment : BaseBottomSheetComposeFragment() {
onClearFileCachedClicked = { proceedWithClearFileCacheWarning() },
onDeleteAccountClicked = { toast(resources.getString(R.string.coming_soon)) },
onResetAccountClicked = { toast(resources.getString(R.string.coming_soon)) },
onLogoutClicked = { vm.onLogoutClicked() },
onLogoutClicked = onLogoutClicked,
onPinCodeClicked = { toast(resources.getString(R.string.coming_soon)) },
isLogoutInProgress = vm.isLoggingOut.collectAsState().value,
isClearCacheInProgress = vm.isClearFileCacheInProgress.collectAsState().value
@ -58,21 +58,6 @@ class AccountAndDataFragment : BaseBottomSheetComposeFragment() {
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
vm.commands.collect { command ->
when(command) {
AccountAndDataViewModel.Command.Logout -> {
findNavController().navigate(R.id.actionLogout)
}
}
}
}
}
}
private fun proceedWithClearFileCacheWarning() {
val dialog = ClearCacheAlertFragment.new()
dialog.onClearAccepted = { vm.onClearFileCacheAccepted() }

View file

@ -0,0 +1,99 @@
package com.anytypeio.anytype.ui.settings
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.stringResource
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_ui.foundation.Warning
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.ui_settings.account.LogoutWarningViewModel
import com.google.android.material.bottomsheet.BottomSheetBehavior
import kotlinx.coroutines.launch
import javax.inject.Inject
class LogoutWarningFragment : BaseBottomSheetComposeFragment() {
@Inject
lateinit var factory: LogoutWarningViewModel.Factory
private val vm by viewModels<LogoutWarningViewModel> { factory }
private val onBackupPhraseClicked = {
findNavController().navigate(R.id.keychainDialog)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme(typography = typography) {
Warning(
actionButtonText = stringResource(R.string.log_out),
cancelButtonText = stringResource(R.string.back_up_your_phrase),
title = stringResource(R.string.have_you_back_up_your_keychain),
subtitle = stringResource(R.string.you_will_need_to_sign_in),
onNegativeClick = onBackupPhraseClicked,
onPositiveClick = { vm.onLogoutClicked() },
isInProgress = vm.isLoggingOut.collectAsState().value
)
}
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
vm.commands.collect { command ->
when (command) {
LogoutWarningViewModel.Command.Logout -> {
findNavController().navigate(R.id.actionLogout)
}
}
}
}
launch {
vm.isLoggingOut.collect { isLoggingOut ->
isCancelable = isLoggingOut == false
}
}
}
}
}
override fun setCancelable(cancelable: Boolean) {
super.setCancelable(cancelable)
dialog?.let { d ->
d.setCanceledOnTouchOutside(cancelable)
d.window?.decorView?.findViewById<View>(R.id.design_bottom_sheet)?.let {
BottomSheetBehavior.from(it).isHideable = cancelable
}
}
}
override fun injectDependencies() {
componentManager().logoutWarningComponent.get().inject(this)
}
override fun releaseDependencies() {
componentManager().logoutWarningComponent.release()
}
}

View file

@ -4,6 +4,6 @@
android:viewportWidth="36"
android:viewportHeight="4">
<path
android:fillColor="#ACA996"
android:fillColor="@color/shape_primary"
android:pathData="M2,0L34,0A2,2 0,0 1,36 2L36,2A2,2 0,0 1,34 4L2,4A2,2 0,0 1,0 2L0,2A2,2 0,0 1,2 0z" />
</vector>

View file

@ -2,5 +2,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="8dp" />
<solid android:color="#F3F2EC" />
<solid android:color="@color/shape_transparent" />
</shape>

View file

@ -189,6 +189,16 @@
android:id="@+id/aboutAppScreen"
android:name="com.anytypeio.anytype.ui.settings.AboutAppFragment" />
<dialog
android:id="@+id/logoutWarningScreen"
android:name="com.anytypeio.anytype.ui.settings.LogoutWarningFragment" >
<action
android:id="@+id/actionLogout"
app:destination="@+id/main_navigation"
app:popUpTo="@+id/main_navigation"
app:popUpToInclusive="true" />
</dialog>
<fragment
android:id="@+id/createAccountScreen"
android:name="com.anytypeio.anytype.ui.auth.account.CreateAccountFragment"

View file

@ -263,4 +263,5 @@ Do the computation of an expensive paragraph of text on a background thread:
<string name="download_from_node">All files will be deleted from your current device. They can be downloaded again from a backup node or another device.</string>
<string name="new_profile">new profile</string>
<string name="back_up_your_phrase">Back up</string>
</resources>

View file

@ -3,18 +3,23 @@ package com.anytypeio.anytype.core_ui.foundation
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.anytypeio.anytype.core_ui.R
@Composable
@ -100,4 +105,82 @@ fun Arrow() {
end = 20.dp
)
)
}
@Composable
fun Warning(
title: String,
subtitle: String,
actionButtonText: String,
cancelButtonText: String,
onNegativeClick: () -> Unit,
onPositiveClick: () -> Unit,
isInProgress: Boolean = false
) {
Column {
Text(
style = MaterialTheme.typography.h2,
text = title,
modifier = Modifier.padding(
top = 24.dp,
start = 20.dp,
end = 20.dp
),
color = colorResource(R.color.text_primary)
)
Text(
text = subtitle,
modifier = Modifier.padding(
top = 12.dp,
start = 20.dp,
end = 20.dp
),
color = colorResource(R.color.text_primary)
)
Row(
modifier = Modifier.height(68.dp).padding(
top = 8.dp,
start = 20.dp,
end = 20.dp
).fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier.height(48.dp).border(
width = 1.dp,
color = colorResource(R.color.shape_primary),
shape = RoundedCornerShape(10.dp)
).weight(1.0f, true).clickable(onClick = onNegativeClick),
contentAlignment = Alignment.Center
) {
Text(
text = cancelButtonText,
color = colorResource(R.color.text_primary),
fontSize = 17.sp
)
}
Spacer(modifier = Modifier.width(10.dp))
Box(
modifier = Modifier.height(48.dp).background(
color = colorResource(R.color.anytype_text_red),
shape = RoundedCornerShape(10.dp)
).weight(1.0f, true).clickable(onClick = onPositiveClick),
contentAlignment = Alignment.Center
) {
Text(
text = actionButtonText,
color = Color.White,
fontSize = 17.sp,
fontWeight = FontWeight.SemiBold
)
if (isInProgress) {
CircularProgressIndicator(
modifier = Modifier.align(Alignment.CenterEnd).padding(end = 20.dp).size(16.dp),
color = Color.White,
strokeWidth = 2.dp
)
}
}
}
}
}

View file

@ -3,21 +3,14 @@ package com.anytypeio.anytype.ui_settings.account
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.domain.auth.interactor.Logout
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.base.Interactor
import com.anytypeio.anytype.domain.device.ClearFileCache
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import timber.log.Timber
class AccountAndDataViewModel(
private val clearFileCache: ClearFileCache,
private val logout: Logout,
) : ViewModel() {
val commands = MutableSharedFlow<Command>(replay = 0)
class AccountAndDataViewModel(private val clearFileCache: ClearFileCache) : ViewModel() {
val isClearFileCacheInProgress = MutableStateFlow(false)
val isLoggingOut = MutableStateFlow(false)
@ -43,40 +36,12 @@ class AccountAndDataViewModel(
}
}
fun onLogoutClicked() {
viewModelScope.launch {
logout(params = BaseUseCase.None).collect { status ->
when (status) {
is Interactor.Status.Started -> {
isLoggingOut.value = true
}
is Interactor.Status.Success -> {
isLoggingOut.value = false
commands.emit(Command.Logout)
}
is Interactor.Status.Error -> {
isLoggingOut.value = true
Timber.e(status.throwable, "Error while logging out")
}
}
}
}
}
class Factory(
private val clearFileCache: ClearFileCache,
private val logout: Logout,
) : ViewModelProvider.Factory {
class Factory(private val clearFileCache: ClearFileCache) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return AccountAndDataViewModel(
clearFileCache = clearFileCache,
logout = logout
clearFileCache = clearFileCache
) as T
}
}
sealed class Command {
object Logout : Command()
}
}

View file

@ -0,0 +1,49 @@
package com.anytypeio.anytype.ui_settings.account
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.domain.auth.interactor.Logout
import com.anytypeio.anytype.domain.base.BaseUseCase
import com.anytypeio.anytype.domain.base.Interactor
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import timber.log.Timber
class LogoutWarningViewModel(private val logout: Logout) : ViewModel() {
val commands = MutableSharedFlow<Command>(replay = 0)
val isLoggingOut = MutableStateFlow(false)
fun onLogoutClicked() {
viewModelScope.launch {
logout(params = BaseUseCase.None).collect { status ->
when (status) {
is Interactor.Status.Started -> {
isLoggingOut.value = true
}
is Interactor.Status.Success -> {
isLoggingOut.value = false
commands.emit(Command.Logout)
}
is Interactor.Status.Error -> {
isLoggingOut.value = true
Timber.e(status.throwable, "Error while logging out")
}
}
}
}
}
class Factory(private val logout: Logout) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return LogoutWarningViewModel(logout = logout) as T
}
}
sealed class Command {
object Logout : Command()
}
}

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="about">About</string>
<string name="app_version">App version</string>
<string name="library">Library</string>
@ -17,4 +18,8 @@
<string name="pin_code">Pin code</string>
<string name="access">Access</string>
<string name="wallpaper">Wallpaper</string>
<string name="have_you_back_up_your_keychain">Have you backed up your keychain phrase?</string>
<string name="you_will_need_to_sign_in">You will need it to sign in. Keep it in a safe place. If you lose it, you can no longer access your account.</string>
</resources>