Skip to content

Conversation

@Mandvii
Copy link
Collaborator

@Mandvii Mandvii commented Nov 12, 2025

Account Deletion

@Mandvii Mandvii requested a review from mikedawson November 12, 2025 14:56
Adds the foundational components for the account deletion feature.

This includes:
- A new `DeleteAccount` route.
- A `DeleteAccountViewModel` with an initial `DeleteAccountUiState`.
- A skeleton `DeleteAccountScreen` composable.
- Registration of the new route in the navigation graph and the view model in Koin.
Adds a "Delete Account" button to the Manage Account screen. Tapping this button navigates the user to the account deletion flow.
This commit introduces the user interface for the account deletion confirmation screen.

Changes include:
- A new layout in `DeleteAccountScreen.kt` with text fields and a confirmation button.
- The `DeleteAccountViewModel` now fetches the user's full name to pre-fill the confirmation field.
- Logic is added to validate that the entered name matches the user's actual name before enabling the delete button.
- New string resources for labels, instructional text, and error messages have been added.
Adds the functionality to delete a person from the local database using their GUID.

This includes:
- Adding `deleteByGuid` to the `PersonDataSource` interface.
- Implementing the deletion logic in `PersonDataSourceDb`, which also removes related roles and relationships within a transaction.
- Adding the corresponding `deleteByPersonGuidHash` query to `PersonEntityDao`.
- Calling the new delete method from `DeleteAccountViewModel`.
This commit introduces the capability for a user to delete their account.

It includes:
- A new `DeleteAccountUseCase` and its client-side implementation.
- A corresponding server-side endpoint (`/person/delete`) to handle the deletion request.
- Integration into the `DeleteAccountViewModel` to call the use case.
- Koin dependency injection wiring for the new components.
This commit introduces the capability for users to delete their accounts.

- A `DeleteAccountUseCaseServer` is implemented to handle the deletion logic on the server-side, which removes the person's record from the database by their GUID hash.
- A new `person/delete` endpoint is added to the server routes to expose this functionality.
- The Koin dependency injection module is updated to provide the `DeleteAccountUseCase`.
- The `UsernameSuggestionUseCaseServer` has been moved to a more generic `account.username` package.
# Conflicts:
#	respect-app-compose/src/androidMain/kotlin/world/respect/AppKoinModule.kt
#	respect-app-compose/src/commonMain/kotlin/world/respect/app/app/AppNavHost.kt
respectEndpointUrl("person/delete")
).apply
{
tokenProvider.provideToken()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use useTokenProvider.
There is no need to include guid as a parameter.

get<RespectTokenManager>().providerFor(id)
}

scoped<DeleteAccountUseCase> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use a factory in the account scope - that will give you the authenticated person uid.

…me` package to better reflect its association with the invitation flow.
…eter from its `invoke` method. Instead of being passed in as a parameter, the user's identity is now determined from the `AuthTokenProvider` context.

This change also updates the Koin module to provide `DeleteAccountUseCase` as a `factory` instead of a `scoped` dependency.
The `DeleteAccountUseCase` is updated to delete the account of the currently authenticated user, removing the need to pass a user GUID as a parameter.

The implementation is changed to use the `authenticatedUser.guid` on the server side. The corresponding `person/delete` route is now placed under authentication to ensure an authenticated user context is available.

route("person") {
PersonDeleteRoute(
deleteAccountUseCase = { it.getSchoolKoinScope().get() },
Copy link
Member

@mikedawson mikedawson Nov 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Mandvii this is wrong: as per call this must use the account scope, not the school scope. This should not use getSchoolKoinScope - that won't work because there won't be a delete account use case in the school scope. I think this should be moved inside the PersonRoute (where it can then use the same account scope approach to get the required use case).

@mikedawson
Copy link
Member

mikedawson commented Nov 18, 2025

@Mandvii :

  • The guid must not be a request parameter. You must use the server side authentication system (if it is a parameter, then anyone who can make any http request could just delete anyone they don't like etc):

a) The Route must be within the authenticate(AUTH_CONFIG_SCHOOL) block (should actually be part of PersonRoute)
b) You must use the account scope, not the school scope on the server side. You must create the DeleteAccountUseCase in the Account Scope of the server's dependency injection (not the school scope) - same as is done with the data sources. You must use the account scope (not getSchoolKoinScope) to get the use case - again the same as is done with PersonRoute, ClassRoute, etc.
c) Please test and get this working end to end and post screenshots / short video showing that once the request is made in the app that it is received by the server and knows which account to delete based on the authenticated user.

…` to `PersonRoute`.

Specifically, the following changes were made:
- Deleted `PersonDeleteRoute.kt` and removed its usage from `Application.kt`.
- Added a `post("person/delete")` endpoint to `PersonRoute.kt` to handle account deletion.
- Updated `DeleteAccountUseCaseClient` to use the new endpoint URL.
- Modified `PersonEntityDao.deletePerson` to return the number of rows deleted.
- Adjusted `DeleteAccountUseCaseServer` to return `true` only if the deletion was successful (rows deleted > 0).
@Mandvii
Copy link
Collaborator Author

Mandvii commented Nov 19, 2025

Screencast.from.2025-11-19.14-35-06.webm

This is the current status account gets deleted and logged out but navigation after end session is not as expected.
I tested logout click seperately it behaves the same,navigation after logged out needs to be fixed

Logs

( OKHttp-CacheInterceptor: intercept: POST http://192.168.1.195:8098/api/school/respect/person/delete

Person(guid=1, userActive=true, status=ACTIVE, lastModified=2025-11-19T09:03:53.260Z, stored=2025-11-19T09:04:46.127Z, metadata=null, userMasterIdentifier=null, username=admin, givenName=Admin, familyName=Admin, middleName=null, gender=UNSPECIFIED, preferredFirstName=null, preferredMiddleName=null, preferredLastName=null, pronouns=null, roles=[PersonRole(isPrimaryRole=true, roleEnum=SYSTEM_ADMINISTRATOR, beginDate=null, endDate=null)], relatedPersonUids=[], dateOfBirth=null, email=null, phoneNumber=null)

No scope found for id 'http://192.168.1.195:8098/'

at world.respect.shared.domain.account.RespectAccountManager.endSession(RespectAccountManager.kt:215)

<string name="delete_account">Delete Account</string>
<string name="permanently_delete">Permanently Delete</string>
<string name="enter_username">Enter your username</string>
<string name="delete_supporting_content">below and then tap on 'Permanently delete' button to delete your account.</string>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be one string that uses string formatting

E.g. Enter your username (%1$s) below and then tap on 'Permanently delete' button to delete your account

val userName: String? = null,
val enteredName: String = "",
val userNameError: UiText? = null,
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please indent properly!!


init {
viewModelScope.launch {
val personSelected = schoolDataSource.personDataSource.findByGuid(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the flow: so you get it fast/offline first.

_uiState.update {
it.copy(
userName = personSelected?.fullName(),
enteredName = personSelected?.fullName().orEmpty()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should not be prefilled. Uses the username, not the person's actual name.

import world.respect.shared.domain.account.deleteaccount.DeleteAccountUseCase

class DeleteAccountUseCaseServer(
private val schoolDb: RespectSchoolDatabase,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use the SchoolDataSource NOT the database.

Adds the ability to delete a person by their GUID.

- Defines `delete(guid: String)` in the `PersonDataSource` interface.
- Implements the delete function in `PersonDataSourceDb` to remove a person from the local database.
- Updates `DeleteAccountUseCaseServer` to use the new `PersonDataSource.delete()` method instead of directly accessing the DAO.
- Adds `TODO` stubs for the HTTP and repository implementations.
The `schoolDb` parameter was unused and has been removed from the `DeleteAccountUseCaseServer` constructor. The implementation now solely relies on the `schoolDataSource` to perform the delete operation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add account deletion support (now a Google Play requirement)

3 participants