diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/settings/FilesStorageDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/settings/FilesStorageDI.kt index 3b03c3a502..30db004d74 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/settings/FilesStorageDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/settings/FilesStorageDI.kt @@ -5,6 +5,7 @@ import com.anytypeio.anytype.analytics.base.Analytics import com.anytypeio.anytype.core_utils.di.scope.PerScreen import com.anytypeio.anytype.device.BuildProvider import com.anytypeio.anytype.di.common.ComponentDependencies +import com.anytypeio.anytype.domain.account.DeleteAccount import com.anytypeio.anytype.domain.auth.interactor.GetAccount import com.anytypeio.anytype.domain.auth.repo.AuthRepository import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers @@ -100,6 +101,14 @@ object FilesStorageModule { dispatchers: AppCoroutineDispatchers ): GetAccount = GetAccount(repo = repo, dispatcher = dispatchers) + @JvmStatic + @Provides + @PerScreen + fun provideDeleteAccountUseCase( + repo: AuthRepository, + dispatchers: AppCoroutineDispatchers + ): DeleteAccount = DeleteAccount(repo = repo) + @Module interface Declarations { diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/settings/ProfileDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/settings/ProfileDI.kt index 99899b49f2..64edfdca91 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/settings/ProfileDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/settings/ProfileDI.kt @@ -16,8 +16,8 @@ import com.anytypeio.anytype.domain.misc.UrlBuilder import com.anytypeio.anytype.domain.`object`.SetObjectDetails import com.anytypeio.anytype.domain.workspace.SpaceManager import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider -import com.anytypeio.anytype.ui.settings.ProfileFragment -import com.anytypeio.anytype.ui_settings.account.ProfileViewModel +import com.anytypeio.anytype.ui.settings.ProfileSettingsFragment +import com.anytypeio.anytype.ui_settings.account.ProfileSettingsViewModel import dagger.Module import dagger.Provides import dagger.Subcomponent @@ -32,7 +32,7 @@ interface ProfileSubComponent { fun build(): ProfileSubComponent } - fun inject(fragment: ProfileFragment) + fun inject(fragment: ProfileSettingsFragment) } @Module(includes = [ProfileModule.Bindings::class]) @@ -50,7 +50,7 @@ object ProfileModule { urlBuilder: UrlBuilder, setDocumentImageIcon: SetDocumentImageIcon, spaceGradientProvider: SpaceGradientProvider - ): ProfileViewModel.Factory = ProfileViewModel.Factory( + ): ProfileSettingsViewModel.Factory = ProfileSettingsViewModel.Factory( deleteAccount = deleteAccount, analytics = analytics, storelessSubscriptionContainer = storelessSubscriptionContainer, diff --git a/app/src/main/java/com/anytypeio/anytype/ui/settings/FilesStorageFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/settings/FilesStorageFragment.kt index 71892c816b..efd7f8cc65 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/settings/FilesStorageFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/settings/FilesStorageFragment.kt @@ -27,6 +27,7 @@ import com.anytypeio.anytype.core_utils.ui.proceed import com.anytypeio.anytype.di.common.componentManager import com.anytypeio.anytype.presentation.settings.FilesStorageViewModel import com.anytypeio.anytype.presentation.settings.FilesStorageViewModel.Event +import com.anytypeio.anytype.ui.auth.account.DeleteAccountWarning import com.anytypeio.anytype.ui.dashboard.ClearCacheAlertFragment import com.anytypeio.anytype.ui_settings.fstorage.LocalStorageScreen import com.anytypeio.anytype.ui_settings.fstorage.RemoteStorageScreen @@ -63,7 +64,8 @@ class FilesStorageFragment : BaseBottomSheetComposeFragment() { } else { LocalStorageScreen( data = vm.state.collectAsStateWithLifecycle().value, - onOffloadFilesClicked = { throttle { vm.event(Event.OnOffloadFilesClicked) } } + onOffloadFilesClicked = { throttle { vm.event(Event.OnOffloadFilesClicked) } }, + onDeleteAccountClicked = { proceedWithAccountDeletion() } ) } } @@ -130,6 +132,16 @@ class FilesStorageFragment : BaseBottomSheetComposeFragment() { ) } + private fun proceedWithAccountDeletion() { + vm.proceedWithAccountDeletion() + val dialog = DeleteAccountWarning() + dialog.onDeletionAccepted = { + dialog.dismiss() + vm.onDeleteAccountClicked() + } + dialog.show(childFragmentManager, null) + } + private fun generateSupportMail( account: Id, name: String, diff --git a/app/src/main/java/com/anytypeio/anytype/ui/settings/ProfileFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/settings/ProfileSettingsFragment.kt similarity index 93% rename from app/src/main/java/com/anytypeio/anytype/ui/settings/ProfileFragment.kt rename to app/src/main/java/com/anytypeio/anytype/ui/settings/ProfileSettingsFragment.kt index 03a777c4f5..54cf1c15e8 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/settings/ProfileFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/settings/ProfileSettingsFragment.kt @@ -32,20 +32,20 @@ import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment import com.anytypeio.anytype.di.common.componentManager import com.anytypeio.anytype.ui.auth.account.DeleteAccountWarning import com.anytypeio.anytype.ui.profile.KeychainPhraseDialog -import com.anytypeio.anytype.ui_settings.account.ProfileScreen -import com.anytypeio.anytype.ui_settings.account.ProfileViewModel +import com.anytypeio.anytype.ui_settings.account.ProfileSettingsScreen +import com.anytypeio.anytype.ui_settings.account.ProfileSettingsViewModel import javax.inject.Inject import timber.log.Timber -class ProfileFragment : BaseBottomSheetComposeFragment() { +class ProfileSettingsFragment : BaseBottomSheetComposeFragment() { @Inject - lateinit var factory: ProfileViewModel.Factory + lateinit var factory: ProfileSettingsViewModel.Factory @Inject lateinit var toggles: FeatureToggles - private val vm by viewModels { factory } + private val vm by viewModels { factory } private val onKeychainPhraseClicked = { val bundle = @@ -74,9 +74,8 @@ class ProfileFragment : BaseBottomSheetComposeFragment() { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { MaterialTheme(typography = typography) { - ProfileScreen( + ProfileSettingsScreen( onKeychainPhraseClicked = onKeychainPhraseClicked, - onDeleteAccountClicked = { throttle { proceedWithAccountDeletion() } }, onLogoutClicked = onLogoutClicked, isLogoutInProgress = vm.isLoggingOut.collectAsState().value, onNameChange = { vm.onNameChange(it) }, diff --git a/app/src/main/res/navigation/graph.xml b/app/src/main/res/navigation/graph.xml index 1a799102d8..470b8c3140 100644 --- a/app/src/main/res/navigation/graph.xml +++ b/app/src/main/res/navigation/graph.xml @@ -228,7 +228,7 @@ + android:name="com.anytypeio.anytype.ui.settings.ProfileSettingsFragment"> (replay = 0) val commands = MutableSharedFlow(replay = 0) @@ -76,7 +80,6 @@ class FilesStorageViewModel( private val _state = MutableStateFlow(ScreenState.empty()) val state: StateFlow = _state - val toasts = MutableSharedFlow(replay = 0) private val jobs = mutableListOf() @@ -168,7 +171,7 @@ class FilesStorageViewModel( is Interactor.Status.Error -> { isClearFileCacheInProgress.value = false Timber.e(status.throwable, "Error while clearing file cache") - toasts.emit("Error while clearing the file cache") + sendToast("Error while clearing the file cache") } Interactor.Status.Success -> { viewModelScope.sendEvent( @@ -317,6 +320,32 @@ class FilesStorageViewModel( } } + fun proceedWithAccountDeletion() { + viewModelScope.launch { + analytics.sendScreenSettingsDeleteEvent() + } + } + + fun onDeleteAccountClicked() { + Timber.d("onDeleteAccountClicked, ") + jobs += viewModelScope.launch { + deleteAccount(BaseUseCase.None).process( + success = { + sendEvent( + analytics = analytics, + eventName = EventsDictionary.deleteAccount + ) + Timber.d("Successfully deleted account, status") + }, + failure = { + Timber.e(it, "Error while deleting account").also { + sendToast("Error while deleting account") + } + } + ) + } + } + sealed class Event { object OnManageFilesClicked : Event() object OnOffloadFilesClicked : Event() @@ -340,7 +369,8 @@ class FilesStorageViewModel( private val fileSpaceUsage: FileSpaceUsage, private val interceptFileLimitEvents: InterceptFileLimitEvents, private val buildProvider: BuildProvider, - private val getAccount: GetAccount + private val getAccount: GetAccount, + private val deleteAccount: DeleteAccount ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create( @@ -356,7 +386,8 @@ class FilesStorageViewModel( fileSpaceUsage = fileSpaceUsage, interceptFileLimitEvents = interceptFileLimitEvents, buildProvider = buildProvider, - getAccount = getAccount + getAccount = getAccount, + deleteAccount = deleteAccount ) as T } diff --git a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/account/ProfileScreen.kt b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/account/ProfileScreen.kt index 8138a5cc08..7a43398002 100644 --- a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/account/ProfileScreen.kt +++ b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/account/ProfileScreen.kt @@ -63,14 +63,13 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter @Composable -fun ProfileScreen( +fun ProfileSettingsScreen( onKeychainPhraseClicked: () -> Unit, - onDeleteAccountClicked: () -> Unit, onLogoutClicked: () -> Unit, isLogoutInProgress: Boolean, onNameChange: (String) -> Unit, onProfileIconClick: () -> Unit, - account: ProfileViewModel.AccountProfile, + account: ProfileSettingsViewModel.AccountProfile, onAppearanceClicked: () -> Unit, onDataManagementClicked: () -> Unit, onAboutClicked: () -> Unit @@ -106,6 +105,7 @@ fun ProfileScreen( text = stringResource(R.string.about), onClick = onAboutClicked ) + Divider(paddingStart = 60.dp) Section(stringResource(R.string.access)) Option( image = R.drawable.ic_keychain_phrase, @@ -113,13 +113,6 @@ fun ProfileScreen( onClick = onKeychainPhraseClicked ) Divider(paddingStart = 60.dp) - Section(stringResource(R.string.account)) - Action( - name = stringResource(R.string.delete_account), - color = colorResource(R.color.text_primary), - onClick = onDeleteAccountClicked - ) - Divider() ActionWithProgressBar( name = stringResource(R.string.log_out), color = colorResource(R.color.palette_dark_red), @@ -252,12 +245,12 @@ fun ActionWithProgressBar( @Composable private fun Header( modifier: Modifier = Modifier, - account: ProfileViewModel.AccountProfile, + account: ProfileSettingsViewModel.AccountProfile, onProfileIconClick: () -> Unit, onNameSet: (String) -> Unit ) { when (account) { - is ProfileViewModel.AccountProfile.Data -> { + is ProfileSettingsViewModel.AccountProfile.Data -> { Box(modifier = modifier.padding(vertical = 6.dp)) { Dragger() } @@ -273,7 +266,7 @@ private fun Header( } ProfileNameBlock(name = account.name, onNameSet = onNameSet) } - is ProfileViewModel.AccountProfile.Idle -> {} + is ProfileSettingsViewModel.AccountProfile.Idle -> {} } } diff --git a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/account/ProfileViewModel.kt b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/account/ProfileSettingsViewModel.kt similarity index 98% rename from ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/account/ProfileViewModel.kt rename to ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/account/ProfileSettingsViewModel.kt index fcf888b20d..d77d127d03 100644 --- a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/account/ProfileViewModel.kt +++ b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/account/ProfileSettingsViewModel.kt @@ -32,7 +32,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import timber.log.Timber -class ProfileViewModel( +class ProfileSettingsViewModel( private val analytics: Analytics, private val deleteAccount: DeleteAccount, private val storelessSubscriptionContainer: StorelessSubscriptionContainer, @@ -168,7 +168,7 @@ class ProfileViewModel( ) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T { - return ProfileViewModel( + return ProfileSettingsViewModel( deleteAccount = deleteAccount, analytics = analytics, storelessSubscriptionContainer = storelessSubscriptionContainer, diff --git a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/fstorage/FilesStorageScreen.kt b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/fstorage/FilesStorageScreen.kt index fa7cded157..23a6c0daa1 100644 --- a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/fstorage/FilesStorageScreen.kt +++ b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/fstorage/FilesStorageScreen.kt @@ -36,6 +36,7 @@ import com.anytypeio.anytype.core_ui.views.BodyCalloutMedium import com.anytypeio.anytype.core_ui.views.BodyCalloutRegular import com.anytypeio.anytype.core_ui.views.ButtonSecondary import com.anytypeio.anytype.core_ui.views.ButtonSize +import com.anytypeio.anytype.core_ui.views.ButtonWarning import com.anytypeio.anytype.core_ui.views.PreviewTitle2Medium import com.anytypeio.anytype.core_ui.views.Relations3 import com.anytypeio.anytype.core_ui.views.Title1 @@ -48,7 +49,8 @@ import com.anytypeio.anytype.ui_settings.main.SpaceImageBlock @Composable fun LocalStorageScreen( data: ScreenState, - onOffloadFilesClicked: () -> Unit + onOffloadFilesClicked: () -> Unit, + onDeleteAccountClicked: () -> Unit ) { Card( modifier = Modifier.fillMaxSize(), @@ -68,7 +70,13 @@ fun LocalStorageScreen( ) { Dragger() } - Header(stringResource(id = R.string.local_storage)) + Header(stringResource(id = R.string.data_management)) + Spacer(modifier = Modifier.height(24.dp)) + Text( + text = stringResource(id = R.string.local_storage), + style = Title1, + color = colorResource(id = R.color.text_primary) + ) Spacer(modifier = Modifier.height(16.dp)) Text( text = stringResource(id = R.string.in_order_to_save), @@ -127,6 +135,28 @@ fun LocalStorageScreen( contentPadding = PaddingValues(12.dp, 7.dp, 12.dp, 7.dp) } ) + Spacer(modifier = Modifier.height(24.dp)) + Text( + text = stringResource(id = R.string.danger_zone), + style = Title1, + color = colorResource(id = R.color.text_primary) + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = stringResource(id = R.string.deleted_account_danger_zone_msg), + style = BodyCalloutRegular, + color = colorResource(id = R.color.text_primary), + modifier = Modifier.fillMaxWidth() + ) + Spacer(modifier = Modifier.height(12.dp)) + ButtonWarning( + text = stringResource(id = R.string.delete_account), + onClick = onDeleteAccountClicked, + size = ButtonSize.SmallSecondary.apply { + contentPadding = PaddingValues(12.dp, 7.dp, 12.dp, 7.dp) + } + ) + Spacer(modifier = Modifier.height(24.dp)) } } } @@ -304,7 +334,8 @@ fun PreviewRemoteStorageScreen() { fun PreviewLocalStorageScreen() { LocalStorageScreen( data = mockData, - onOffloadFilesClicked = {} + onOffloadFilesClicked = {}, + onDeleteAccountClicked = {} ) } diff --git a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/main/MainSettingScreen.kt b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/main/MainSettingScreen.kt index f068b5fcb9..d2ce6940f0 100644 --- a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/main/MainSettingScreen.kt +++ b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/main/MainSettingScreen.kt @@ -40,6 +40,7 @@ import com.anytypeio.anytype.presentation.settings.MainSettingsViewModel import com.anytypeio.anytype.presentation.spaces.SpaceIconView import com.anytypeio.anytype.ui_settings.R +@Deprecated("To be deleted") @Composable fun MainSettingScreen( workspace: MainSettingsViewModel.WorkspaceAndAccount, diff --git a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/main/Views.kt b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/main/Views.kt index 9890301ee8..23506aaddc 100644 --- a/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/main/Views.kt +++ b/ui-settings/src/main/java/com/anytypeio/anytype/ui_settings/main/Views.kt @@ -105,7 +105,7 @@ fun SpaceNameBlock( @Composable fun SpaceNameBlock() { Text( - text = stringResource(id = R.string.personal_space), + text = stringResource(id = R.string.space_settings), style = Title1, color = colorResource(id = R.color.text_primary) ) diff --git a/ui-settings/src/main/res/values/strings.xml b/ui-settings/src/main/res/values/strings.xml index 1083c5b309..38c31ee3ea 100644 --- a/ui-settings/src/main/res/values/strings.xml +++ b/ui-settings/src/main/res/values/strings.xml @@ -69,4 +69,6 @@ Account name Space settings Data management + Once you request your account to be deleted, you have 30 days to cancel this request. After 30 days, your encrypted account data is permanently removed from the backup node, you won\'t be able to sign into Anytype on new devices. + Danger zone \ No newline at end of file