diff --git a/app/src/main/java/org/greenstand/android/TreeTracker/database/TreeTrackerDAO.kt b/app/src/main/java/org/greenstand/android/TreeTracker/database/TreeTrackerDAO.kt index ef15f51d..3e2e5432 100644 --- a/app/src/main/java/org/greenstand/android/TreeTracker/database/TreeTrackerDAO.kt +++ b/app/src/main/java/org/greenstand/android/TreeTracker/database/TreeTrackerDAO.kt @@ -74,6 +74,10 @@ interface TreeTrackerDAO { @Query("SELECT * FROM user WHERE power_user = 1") suspend fun getPowerUser(): UserEntity? + @Query("DELETE FROM user WHERE wallet = :wallet") + suspend fun deleteUserByWallet(wallet: String): Int + + @Query("UPDATE user SET power_user = :isPowerUser WHERE _id = :userId") suspend fun updatePowerUserStatus(userId: Long, isPowerUser: Boolean) diff --git a/app/src/main/java/org/greenstand/android/TreeTracker/di/AppModule.kt b/app/src/main/java/org/greenstand/android/TreeTracker/di/AppModule.kt index 0393bb68..85a29860 100644 --- a/app/src/main/java/org/greenstand/android/TreeTracker/di/AppModule.kt +++ b/app/src/main/java/org/greenstand/android/TreeTracker/di/AppModule.kt @@ -106,7 +106,7 @@ val appModule = module { viewModel { OrgPickerViewModel(get()) } - viewModel { UserSelectViewModel(null, get(), get(), get()) } + viewModel { UserSelectViewModel(null, get(), get(), get(), get()) } viewModel { DevOptionsViewModel(get(), get()) } diff --git a/app/src/main/java/org/greenstand/android/TreeTracker/models/NavRoute.kt b/app/src/main/java/org/greenstand/android/TreeTracker/models/NavRoute.kt index 7c022d6a..1af4c6fa 100644 --- a/app/src/main/java/org/greenstand/android/TreeTracker/models/NavRoute.kt +++ b/app/src/main/java/org/greenstand/android/TreeTracker/models/NavRoute.kt @@ -39,6 +39,7 @@ import org.greenstand.android.TreeTracker.messages.individualmeassagelist.Indivi import org.greenstand.android.TreeTracker.messages.survey.SurveyScreen import org.greenstand.android.TreeTracker.orgpicker.AddOrgScreen import org.greenstand.android.TreeTracker.orgpicker.OrgPickerScreen +import org.greenstand.android.TreeTracker.profile.DeleteProfileScreen import org.greenstand.android.TreeTracker.profile.ProfileScreen import org.greenstand.android.TreeTracker.profile.ProfileSelectScreen import org.greenstand.android.TreeTracker.sessionnote.SessionNoteScreen @@ -81,6 +82,13 @@ sealed class NavRoute : KoinComponent { override val route: String = "signup-flow" } + object DeleteProfile : NavRoute() { + override val content: @Composable (NavBackStackEntry) -> Unit = { + DeleteProfileScreen() + } + override val route: String = "delete-profile" + } + object Org : NavRoute() { override val content: @Composable (NavBackStackEntry) -> Unit = { OrgPickerScreen() diff --git a/app/src/main/java/org/greenstand/android/TreeTracker/models/user/UserRepo.kt b/app/src/main/java/org/greenstand/android/TreeTracker/models/user/UserRepo.kt index b27bfe08..ce020c19 100644 --- a/app/src/main/java/org/greenstand/android/TreeTracker/models/user/UserRepo.kt +++ b/app/src/main/java/org/greenstand/android/TreeTracker/models/user/UserRepo.kt @@ -54,6 +54,11 @@ class UserRepo( suspend fun getUserWithWallet(wallet: String): User? { return createUser(dao.getUserByWallet(wallet)) } + suspend fun deleteUser(wallet: String): Boolean { + val deletedRows = dao.deleteUserByWallet(wallet) + return deletedRows > 0 + } + suspend fun checkForUnreadMessagesPerUser(wallet: String): Boolean { return messagesDao.getUnreadMessageCountForWallet(wallet) >= 1 diff --git a/app/src/main/java/org/greenstand/android/TreeTracker/profile/DeleteProfileScreen.kt b/app/src/main/java/org/greenstand/android/TreeTracker/profile/DeleteProfileScreen.kt new file mode 100644 index 00000000..bed52712 --- /dev/null +++ b/app/src/main/java/org/greenstand/android/TreeTracker/profile/DeleteProfileScreen.kt @@ -0,0 +1,108 @@ +package org.greenstand.android.TreeTracker.profile + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.lifecycle.viewmodel.compose.viewModel +import org.greenstand.android.TreeTracker.R +import org.greenstand.android.TreeTracker.models.NavRoute +import org.greenstand.android.TreeTracker.root.LocalNavHostController +import org.greenstand.android.TreeTracker.root.LocalViewModelFactory +import org.greenstand.android.TreeTracker.userselect.DeleteProfileState +import org.greenstand.android.TreeTracker.userselect.UserSelect +import org.greenstand.android.TreeTracker.userselect.UserSelectState +import org.greenstand.android.TreeTracker.userselect.UserSelectViewModel +import org.greenstand.android.TreeTracker.view.AppButtonColors +import org.greenstand.android.TreeTracker.view.AppColors.Red +import org.greenstand.android.TreeTracker.view.UserButton +import org.greenstand.android.TreeTracker.view.dialogs.CustomDialog + +@Composable +fun DeleteProfileScreen() { + val navController = LocalNavHostController.current + val viewModel: UserSelectViewModel = viewModel(factory = LocalViewModelFactory.current) + val state by viewModel.state.collectAsState(UserSelectState()) + Box( + modifier = Modifier + .fillMaxSize() + ) { + UserSelect( + navigationButtonColors = AppButtonColors.ProgressGreen, + isCreateUserEnabled = true, + isNotificationEnabled = true, + onNavigateForward = { user -> + viewModel.selectUser(user) + viewModel.updateDeleteProfileState(DeleteProfileState.SHOWDIALOG) + } + ) + + if (state.deleteProfileState == DeleteProfileState.SHOWDIALOG) { + CustomDialog( + title = stringResource(R.string.delete_account_title), + textContent = stringResource(R.string.delete_account_dialog_message), + onPositiveClick = { + viewModel.deleteUser() + }, + onNegativeClick = { + viewModel.updateDeleteProfileState(DeleteProfileState.DISMISSDIALOG) + }, + content = { + state.selectedUser?.let { user -> + UserButton( + user = user, + isSelected = false, + buttonColors = AppButtonColors.Default, + selectedColor = Red, + onClick = { + } + ) + } + + } + + ) + } + if ((state.deleteProfileState == DeleteProfileState.ACCOUNTDELETEDLOCALLY ) || (state.deleteProfileState == DeleteProfileState.ACCOUNTDELETEDANDADMINREQUESTED )) { + CustomDialog( + title = stringResource(R.string.account_deleted), + textContent = stringResource(R.string.account_deleted_message), + onPositiveClick = { + viewModel.deleteUser() + if(state.selectedUser?.isPowerUser == true){ + navController.navigate(NavRoute.SignupFlow.route) { + popUpTo(navController.graph.id) { + inclusive = true + } + launchSingleTop = true + } + + } else { + navController.navigate(NavRoute.Settings.route) { + popUpTo(NavRoute.Settings.route) { inclusive = true } + launchSingleTop = true + } + } + }, + content = { + state.selectedUser?.let { user -> + UserButton( + user = user, + isSelected = false, + buttonColors = AppButtonColors.Default, + selectedColor = Red, + onClick = { + } + ) + } + + } + + ) + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/org/greenstand/android/TreeTracker/profile/ProfileScreen.kt b/app/src/main/java/org/greenstand/android/TreeTracker/profile/ProfileScreen.kt index 88f4ae98..41e94b9e 100644 --- a/app/src/main/java/org/greenstand/android/TreeTracker/profile/ProfileScreen.kt +++ b/app/src/main/java/org/greenstand/android/TreeTracker/profile/ProfileScreen.kt @@ -84,7 +84,7 @@ fun ProfileScreen( ActionBar( centerAction = { Text( - text = stringResource(id = R.string.settings), + text = stringResource(id = R.string.profile_title), color = AppColors.Green, fontWeight = FontWeight.Bold, style = CustomTheme.typography.large, diff --git a/app/src/main/java/org/greenstand/android/TreeTracker/root/Host.kt b/app/src/main/java/org/greenstand/android/TreeTracker/root/Host.kt index e566c00d..a03425e5 100644 --- a/app/src/main/java/org/greenstand/android/TreeTracker/root/Host.kt +++ b/app/src/main/java/org/greenstand/android/TreeTracker/root/Host.kt @@ -55,6 +55,7 @@ fun Host() { NavRoute.Settings, NavRoute.Profile, NavRoute.ProfileSelect, + NavRoute.DeleteProfile, NavRoute.DevOptions, ).forEach { addNavRoute(it) } } diff --git a/app/src/main/java/org/greenstand/android/TreeTracker/settings/SettingsScreen.kt b/app/src/main/java/org/greenstand/android/TreeTracker/settings/SettingsScreen.kt index fc52f5b0..f0afac92 100644 --- a/app/src/main/java/org/greenstand/android/TreeTracker/settings/SettingsScreen.kt +++ b/app/src/main/java/org/greenstand/android/TreeTracker/settings/SettingsScreen.kt @@ -129,7 +129,9 @@ fun SettingsScreen() { iconResId = R.drawable.delete, // Replace with your delete icon titleResId = R.string.delete_account_title, descriptionResId = R.string.delete_account_description, - onClick = { /* Handle delete account click */ } + onClick = { + navController.navigate(NavRoute.DeleteProfile.route) + } ) } if (state.showPrivacyPolicyDialog == true) { @@ -141,7 +143,12 @@ fun SettingsScreen() { textContent = stringResource(R.string.logout_dialog_message), onPositiveClick = { viewModel.logout() - navController.navigate(NavRoute.SignupFlow.route) + navController.navigate(NavRoute.SignupFlow.route) { + popUpTo(navController.graph.id) { + inclusive = true + } + launchSingleTop = true + } }, onNegativeClick = { viewModel.updateLogoutDialogVisibility(false) diff --git a/app/src/main/java/org/greenstand/android/TreeTracker/userselect/UserSelectViewModel.kt b/app/src/main/java/org/greenstand/android/TreeTracker/userselect/UserSelectViewModel.kt index a3cb8bd6..727f6946 100644 --- a/app/src/main/java/org/greenstand/android/TreeTracker/userselect/UserSelectViewModel.kt +++ b/app/src/main/java/org/greenstand/android/TreeTracker/userselect/UserSelectViewModel.kt @@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.greenstand.android.TreeTracker.models.UserRepo import org.greenstand.android.TreeTracker.models.location.LocationDataCapturer +import org.greenstand.android.TreeTracker.models.messages.MessagesRepo import org.greenstand.android.TreeTracker.models.setupflow.CaptureSetupScopeManager import org.greenstand.android.TreeTracker.models.user.User import org.greenstand.android.TreeTracker.preferences.Preferences @@ -38,11 +39,20 @@ data class UserSelectState( val users: List = emptyList(), val selectedUser: User? = null, val editMode: Boolean = false, + val deleteProfileState: DeleteProfileState = DeleteProfileState.INITIAL ) +enum class DeleteProfileState(){ + INITIAL, + SHOWDIALOG, + DISMISSDIALOG, + ACCOUNTDELETEDLOCALLY, + ACCOUNTDELETEDANDADMINREQUESTED +} class UserSelectViewModel( userId: Long?, private val userRepo: UserRepo, + private val messageRepo: MessagesRepo, locationDataCapturer: LocationDataCapturer, private val prefs: Preferences, ) : ViewModel() { @@ -76,7 +86,19 @@ class UserSelectViewModel( selectedUser = user ) } - + fun deleteUser(){ + viewModelScope.launch { + if(userRepo.deleteUser(_state.value.selectedUser?.wallet ?: "") ){ + _state.value = _state.value.copy(deleteProfileState = DeleteProfileState.ACCOUNTDELETEDLOCALLY) + messageRepo.saveMessage( + wallet = _state.value.selectedUser?.wallet ?: "", + to = "admin", + body = "Hi admin, I would like to delete my account with the wallet ${_state.value.selectedUser?.wallet} I understand that all my data will be lost." + ) + messageRepo.syncMessages() + } + } + } // 🔁 Toggle edit mode fun updateEditEnabled() { _state.value = _state.value.copy(editMode = !_state.value.editMode) @@ -109,12 +131,16 @@ class UserSelectViewModel( userRepo.updateUser(user) } } + + fun updateDeleteProfileState(deleteProfileState: DeleteProfileState) { + _state.value= _state.value.copy(deleteProfileState = deleteProfileState) + } } class UserSelectViewModelFactory(private val userId: Long) : ViewModelProvider.Factory, KoinComponent { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class): T { - return UserSelectViewModel(userId, get(), get(),get(),) as T + return UserSelectViewModel(userId, get(), get(),get(), get()) as T } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4a829ed9..4e27e51a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -180,10 +180,12 @@ Privacy Policy Log out Log out from your account. Delete Account + Account Deleted + All your Data has been deleted Initiate the process to delete your account completely. Are you sure you want to logout? - If you logout you will have to re-enter email or phone number again to access all trees captured and your account - Are you sure you want your account deleted? \n This is irreversible and you would loose all your data. + Are you sure you want to delete this account? + Are you sure you want your account deleted? \n This is irreversible and you would loose all your data. \n You need to be connected to the internet for this to be completed. Are you sure you want to log out from your account? No GPS found on device