Skip to content

Delete Button#109

Merged
isiahpwilliams merged 8 commits intomainfrom
preston/delete
Apr 21, 2026
Merged

Delete Button#109
isiahpwilliams merged 8 commits intomainfrom
preston/delete

Conversation

@isiahpwilliams
Copy link
Copy Markdown
Contributor

@isiahpwilliams isiahpwilliams commented Apr 15, 2026

Overview

Created a delete account button in the settings page

Changes Made

Implemented the design for the button and the popup from Figma (SettingsScreen.kt and DeleteDialog.kt)
Implemented networking for delete and fixed networking for log out (UserInfoRepository.kt and SettingsViewModel.kt)

Test Coverage

Manually tested the log out and delete functionality

Summary by CodeRabbit

  • New Features

    • Account deletion added to Settings with a confirmation dialog.
  • Changes

    • “Reminders” option removed from Settings.
    • Log out now returns users to the onboarding flow.
    • New trash icon and a red color added for delete-related UI.
    • Sign-out and account-deletion flows improved for more reliable session clearing.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 15, 2026

Warning

Rate limit exceeded

@isiahpwilliams has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 32 minutes and 13 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 32 minutes and 13 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 74c4fe4e-9703-4a94-984c-5e204b0b5952

📥 Commits

Reviewing files that changed from the base of the PR and between 22114fe and 0e27835.

📒 Files selected for processing (1)
  • app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/nav/RootNavigationViewModel.kt
📝 Walkthrough

Walkthrough

Session management now persists a netId; repository and viewmodel add account-deletion and make sign-out asynchronous; UI adds a delete-account confirmation dialog and removes reminders; small UI and theme adjustments and a navigation event emission were introduced.

Changes

Cohort / File(s) Summary
Session & Token Management
app/src/main/java/com/cornellappdev/uplift/data/auth/SessionManager.kt, app/src/main/java/com/cornellappdev/uplift/data/auth/TokenManager.kt
startSession / saveUserSession signatures extended to include netId; TokenManager persists "net_id" and exposes getNetId(); SessionManager adds val netId: String? accessor.
User Repository & Auth flows
app/src/main/java/com/cornellappdev/uplift/data/repositories/UserInfoRepository.kt
Login stores additional fields (including netId); signOut() changed to suspend; added suspend deleteAccount() performing GraphQL DeleteUserMutation, attempts Firebase deletion, then logs out and clears datastore; datastore netId fallback uses sessionManager.netId.
Settings UI & Screens
app/src/main/java/com/cornellappdev/uplift/ui/components/profile/DeleteDialog.kt, app/src/main/java/com/cornellappdev/uplift/ui/screens/profile/SettingsScreen.kt
Added DeleteAccountConfirmationDialog composable and preview; SettingsScreen manages dialog visibility, removes reminders option, adds delete-account action and hooks it to ViewModel delete call.
ViewModel: Settings & Navigation
app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/profile/SettingsViewModel.kt, app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/nav/RootNavigationViewModel.kt
SettingsViewModel removes reminders handler, updates onLogOut() to navigate to Onboarding, and adds onDeleteAccount() that calls repository delete and navigates on success. RootNavigationViewModel now emits a navEvent alongside startDestination when session state changes.
Composable utilities & previews
app/src/main/java/com/cornellappdev/uplift/ui/components/goalsetting/DeleteDialog.kt
Made CloseButton public with configurable modifier and tint, moved click/size behavior to call site, and added preview for delete dialog.
Resources & Theme
app/src/main/res/drawable/ic_trash_2.xml, app/src/main/java/com/cornellappdev/uplift/ui/theme/Color.kt
Added a 36dp trash drawable ic_trash_2.xml; added Red color constant to theme.

Sequence Diagram

sequenceDiagram
    actor User
    participant SettingsScreen
    participant DeleteDialog
    participant SettingsViewModel
    participant UserInfoRepository
    participant GraphQL as GraphQL API
    participant Firebase
    participant SessionManager
    participant DataStore

    User->>SettingsScreen: Tap "Delete account"
    SettingsScreen->>DeleteDialog: show dialog
    User->>DeleteDialog: Confirm deletion
    DeleteDialog->>SettingsViewModel: onDeleteAccount()
    SettingsViewModel->>UserInfoRepository: deleteAccount()
    UserInfoRepository->>SessionManager: read userId
    UserInfoRepository->>GraphQL: DeleteUserMutation(userId)
    GraphQL-->>UserInfoRepository: response (success/error)
    UserInfoRepository->>Firebase: attempt Firebase user deletion
    Firebase-->>UserInfoRepository: deletion result (logged)
    UserInfoRepository->>SessionManager: logout()
    UserInfoRepository->>DataStore: clear user data
    UserInfoRepository-->>SettingsViewModel: return success/failure
    SettingsViewModel->>SettingsScreen: navigate to Onboarding (on success)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • AndrewCheung360

Poem

🐰 I hopped in the code, left a tiny trash can,
NetID tucked safe in the session's small van,
Dialog pops up, a choice to be made,
Delete then depart, the old paths unmade,
Off to Onboarding now — a fresh-start plan ✨🗑️

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Delete Button' is vague and overly generic; it does not convey the main purpose of adding account deletion functionality to the Settings screen. Revise the title to be more specific and descriptive, such as 'Add delete account button to Settings screen' or 'Implement account deletion functionality'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description covers the main changes (UI implementation, networking, testing) but lacks specific technical details and could be more comprehensive.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch preston/delete

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/main/java/com/cornellappdev/uplift/data/repositories/UserInfoRepository.kt`:
- Around line 243-255: In deleteAccount(), the caught exception from
firebaseAuth.currentUser?.delete()?.await() currently allows execution to
continue and return true; change the flow so a failure to delete from the
provider aborts account teardown: when the delete await throws, log the error
(keep Log.e) and return false (or rethrow) immediately instead of proceeding to
sessionManager.logout(), dataStore.edit { it.clear() }, and the final Log.d;
ensure the success path only reaches those calls and returns true when
firebaseAuth.currentUser?.delete()?.await() completes without exception.

In
`@app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/profile/SettingsViewModel.kt`:
- Around line 58-66: The delete-account failure path in
SettingsViewModel.onDeleteAccount is currently silent; update the ViewModel to
surface errors by adding an error field or one-shot event to SettingsUiState
(e.g., settingsError: String? or a SingleLiveEvent/Channel) and set it when
userInfoRepository.deleteAccount() returns false, then consume it in the UI to
show a Snackbar/dialog; also log the failure and clear or reset the one-shot
event after it is displayed. Ensure references: onDeleteAccount,
userInfoRepository.deleteAccount, rootNavigationRepository.navigate, and
SettingsUiState are updated so the UI receives and displays the error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c26a61a0-953d-4b62-9e74-06a238984f80

📥 Commits

Reviewing files that changed from the base of the PR and between 4e81463 and 40e3d9a.

📒 Files selected for processing (7)
  • app/src/main/java/com/cornellappdev/uplift/data/auth/SessionManager.kt
  • app/src/main/java/com/cornellappdev/uplift/data/auth/TokenManager.kt
  • app/src/main/java/com/cornellappdev/uplift/data/repositories/UserInfoRepository.kt
  • app/src/main/java/com/cornellappdev/uplift/ui/components/profile/DeleteDialog.kt
  • app/src/main/java/com/cornellappdev/uplift/ui/screens/profile/SettingsScreen.kt
  • app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/profile/SettingsViewModel.kt
  • app/src/main/res/drawable/ic_trash_2.xml

Comment thread app/src/main/java/com/cornellappdev/uplift/ui/screens/profile/SettingsScreen.kt Outdated
Comment thread app/src/main/java/com/cornellappdev/uplift/ui/screens/profile/SettingsScreen.kt Outdated
Comment thread app/src/main/java/com/cornellappdev/uplift/ui/screens/profile/SettingsScreen.kt Outdated
Comment thread app/src/main/java/com/cornellappdev/uplift/ui/components/profile/DeleteDialog.kt Outdated
Comment thread app/src/main/java/com/cornellappdev/uplift/ui/components/profile/DeleteDialog.kt Outdated
Comment thread app/src/main/java/com/cornellappdev/uplift/ui/components/profile/DeleteDialog.kt Outdated
Comment thread app/src/main/java/com/cornellappdev/uplift/ui/components/profile/DeleteDialog.kt Outdated
Comment thread app/src/main/java/com/cornellappdev/uplift/ui/components/profile/DeleteDialog.kt Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/nav/RootNavigationViewModel.kt (1)

61-77: ⚠️ Potential issue | 🟠 Major

Initial StateFlow emission causes duplicate back-stack entry on startup.

sessionManager.isLoggedIn is a StateFlow with eager sharing, so it emits its current value immediately upon collection. This triggers the initial navEvent emission in the viewModelScope.launch block (line 73), which fires before any user interaction occurs.

Since MainNavigationWrapper calls navController.navigate(it) unconditionally at line 121 without launchSingleTop or popUpTo, the navigation to the same destination as NavHost's startDestination adds a duplicate back-stack entry (e.g., Home → Home). Users can then press back from the home screen unintentionally.

Consider one of:

  • Skip the first emission (e.g., .drop(1)) to only emit navEvent on real login/logout/delete transitions.
  • Track the previous isLoggedIn value and only emit when it actually transitions.
  • Add popUpTo(..) { inclusive = true } at the navigation site to make re-navigation idempotent.
🔧 Example: only emit on transitions
         viewModelScope.launch {
-            sessionManager.isLoggedIn.collect { loggedIn ->
+            var previous: Boolean? = null
+            sessionManager.isLoggedIn.collect { loggedIn ->
                 applyMutation {
                     copy(isLoggedIn = loggedIn)
                 }

                 val hasSkipped = userInfoRepository.getSkipFromDataStore()
                 val shouldShowHome = loggedIn || hasSkipped || !ONBOARDING_FLAG
                 val newRoute = if (shouldShowHome) UpliftRootRoute.Home else UpliftRootRoute.Onboarding
-                applyMutation {
-                    copy(
-                        startDestination = newRoute,
-                        navEvent = UIEvent(newRoute)
-                    )
-                }
+                val isTransition = previous != null && previous != loggedIn
+                previous = loggedIn
+                applyMutation {
+                    copy(
+                        startDestination = newRoute,
+                        navEvent = if (isTransition) UIEvent(newRoute) else navEvent
+                    )
+                }
             }
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/nav/RootNavigationViewModel.kt`
around lines 61 - 77, The view model currently reacts to the eager initial
emission from sessionManager.isLoggedIn and emits a navEvent causing duplicate
navigation; change the collection to skip the initial emission (e.g., use
sessionManager.isLoggedIn.drop(1).collect { ... }) inside the
viewModelScope.launch so applyMutation that sets startDestination and navEvent
only runs on real transitions, preventing the duplicate back-stack entry
(alternatively, you can track previous isLoggedIn and only emit when it changes
or handle idempotent navigation at MainNavigationWrapper by using
popUpTo/launchSingleTop).
🧹 Nitpick comments (3)
app/src/main/java/com/cornellappdev/uplift/ui/screens/profile/SettingsScreen.kt (2)

45-45: Consider rememberSaveable so the dialog survives configuration changes.

Using remember { mutableStateOf(false) } means the dialog will be dismissed on rotation or process death restoration. If that's undesirable, switch to rememberSaveable.

♻️ Proposed change
-    var showDeleteDialog by remember { mutableStateOf(false)}
+    var showDeleteDialog by rememberSaveable { mutableStateOf(false) }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/cornellappdev/uplift/ui/screens/profile/SettingsScreen.kt`
at line 45, The dialog visibility state uses remember { mutableStateOf(false) }
which won't survive configuration changes; change the declaration of
showDeleteDialog in SettingsScreen (the var showDeleteDialog by remember {
mutableStateOf(false) }) to use rememberSaveable (e.g., rememberSaveable {
mutableStateOf(false) }) and add the necessary import for
androidx.compose.runtime.saveable.rememberSaveable so the dialog state persists
across rotations and process restoration.

111-161: DRY: LogOutButton and DeleteAccountButton are near-duplicates.

The two composables differ only in icon, content description, label, and icon tint. Consider extracting a shared SettingsActionButton(icon, label, iconTint, onClick) to reduce duplication and keep styling consistent as the design system evolves.

♻️ Sketch
`@Composable`
private fun SettingsActionButton(
    `@DrawableRes` icon: Int,
    label: String,
    iconTint: Color = Color.Unspecified,
    textColor: Color = AppColors.Red,
    onClick: () -> Unit,
) {
    Row(
        modifier = Modifier.clickable(onClick = onClick),
        horizontalArrangement = Arrangement.spacedBy(8.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Icon(painterResource(id = icon), contentDescription = label, tint = iconTint)
        Text(
            text = label,
            fontSize = 16.sp,
            fontFamily = montserratFamily,
            fontWeight = FontWeight.Medium,
            color = textColor,
        )
    }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/cornellappdev/uplift/ui/screens/profile/SettingsScreen.kt`
around lines 111 - 161, LogOutButton and DeleteAccountButton are
near-duplicates; extract a single reusable composable (e.g.,
SettingsActionButton) that accepts parameters for icon resource id, content
description/label, iconTint, textColor, and onClick, then replace both
LogOutButton and DeleteAccountButton to call SettingsActionButton with their
specific icon, label, tint, and click handler; keep the common Row layout,
spacing, font (montserratFamily), fontSize (16.sp), and fontWeight
(FontWeight.Medium) in the new SettingsActionButton so styling remains
centralized.
app/src/main/java/com/cornellappdev/uplift/ui/components/goalsetting/DeleteDialog.kt (1)

73-81: Replace hardcoded offset with Align/padding and clean up the modifier chain.

A few issues in this call site:

  • The position .offset(x = 206.dp, y = 12.dp) is hardcoded against the Card's width(250.dp) and icon size(32.dp). Any future change to the dialog width or close-icon size silently breaks the layout. The sibling caller in ui/components/profile/DeleteDialog.kt already uses the idiomatic Modifier.align(Alignment.TopEnd).padding(top = 12.dp, end = 12.dp) pattern — worth mirroring here for consistency.
  • The chain .size(32.dp).\n clickable(...) with the trailing dot on its own line is valid Kotlin but easy to misread as a typo; standard style is to start each call with the leading dot.
  • offset applied after clickable means the click region is translated visually but the node is still laid out at (0,0) inside the Box, which also explains why an explicit offset is needed instead of proper alignment.
♻️ Proposed refactor
-                    CloseButton(modifier = Modifier
-                        .size(32.dp).
-                        clickable(
-                        interactionSource = remember { MutableInteractionSource() },
-                        indication = null
-                    ) {
-                        onDismiss()
-                    }
-                        .offset(x = 206.dp, y = 12.dp))
+                    CloseButton(
+                        modifier = Modifier
+                            .align(Alignment.TopEnd)
+                            .padding(top = 12.dp, end = 12.dp)
+                            .size(32.dp)
+                            .clickable(
+                                interactionSource = remember { MutableInteractionSource() },
+                                indication = null
+                            ) {
+                                onDismiss()
+                            }
+                    )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/cornellappdev/uplift/ui/components/goalsetting/DeleteDialog.kt`
around lines 73 - 81, The CloseButton placement uses a hardcoded offset and a
confusing modifier chain; replace the .offset(...) positioning with idiomatic
alignment and padding (use Modifier.align(Alignment.TopEnd).padding(top = 12.dp,
end = 12.dp)) and reorder the modifier calls so each chained call starts with a
leading dot and clickable follows size (e.g., .size(32.dp).clickable(...)) to
ensure the visual position and click target match; update the CloseButton call
in DeleteDialog.kt to use Modifier.align(Alignment.TopEnd).padding(...) plus the
existing remember { MutableInteractionSource() } and indication = null, and
remove the hardcoded .offset and stray trailing dot.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/nav/RootNavigationViewModel.kt`:
- Around line 61-77: The view model currently reacts to the eager initial
emission from sessionManager.isLoggedIn and emits a navEvent causing duplicate
navigation; change the collection to skip the initial emission (e.g., use
sessionManager.isLoggedIn.drop(1).collect { ... }) inside the
viewModelScope.launch so applyMutation that sets startDestination and navEvent
only runs on real transitions, preventing the duplicate back-stack entry
(alternatively, you can track previous isLoggedIn and only emit when it changes
or handle idempotent navigation at MainNavigationWrapper by using
popUpTo/launchSingleTop).

---

Nitpick comments:
In
`@app/src/main/java/com/cornellappdev/uplift/ui/components/goalsetting/DeleteDialog.kt`:
- Around line 73-81: The CloseButton placement uses a hardcoded offset and a
confusing modifier chain; replace the .offset(...) positioning with idiomatic
alignment and padding (use Modifier.align(Alignment.TopEnd).padding(top = 12.dp,
end = 12.dp)) and reorder the modifier calls so each chained call starts with a
leading dot and clickable follows size (e.g., .size(32.dp).clickable(...)) to
ensure the visual position and click target match; update the CloseButton call
in DeleteDialog.kt to use Modifier.align(Alignment.TopEnd).padding(...) plus the
existing remember { MutableInteractionSource() } and indication = null, and
remove the hardcoded .offset and stray trailing dot.

In
`@app/src/main/java/com/cornellappdev/uplift/ui/screens/profile/SettingsScreen.kt`:
- Line 45: The dialog visibility state uses remember { mutableStateOf(false) }
which won't survive configuration changes; change the declaration of
showDeleteDialog in SettingsScreen (the var showDeleteDialog by remember {
mutableStateOf(false) }) to use rememberSaveable (e.g., rememberSaveable {
mutableStateOf(false) }) and add the necessary import for
androidx.compose.runtime.saveable.rememberSaveable so the dialog state persists
across rotations and process restoration.
- Around line 111-161: LogOutButton and DeleteAccountButton are near-duplicates;
extract a single reusable composable (e.g., SettingsActionButton) that accepts
parameters for icon resource id, content description/label, iconTint, textColor,
and onClick, then replace both LogOutButton and DeleteAccountButton to call
SettingsActionButton with their specific icon, label, tint, and click handler;
keep the common Row layout, spacing, font (montserratFamily), fontSize (16.sp),
and fontWeight (FontWeight.Medium) in the new SettingsActionButton so styling
remains centralized.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 98f43585-323b-4346-9a3a-ce585174bb15

📥 Commits

Reviewing files that changed from the base of the PR and between 40e3d9a and 22114fe.

📒 Files selected for processing (6)
  • app/src/main/java/com/cornellappdev/uplift/data/repositories/UserInfoRepository.kt
  • app/src/main/java/com/cornellappdev/uplift/ui/components/goalsetting/DeleteDialog.kt
  • app/src/main/java/com/cornellappdev/uplift/ui/components/profile/DeleteDialog.kt
  • app/src/main/java/com/cornellappdev/uplift/ui/screens/profile/SettingsScreen.kt
  • app/src/main/java/com/cornellappdev/uplift/ui/theme/Color.kt
  • app/src/main/java/com/cornellappdev/uplift/ui/viewmodels/nav/RootNavigationViewModel.kt
✅ Files skipped from review due to trivial changes (1)
  • app/src/main/java/com/cornellappdev/uplift/ui/theme/Color.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/src/main/java/com/cornellappdev/uplift/ui/components/profile/DeleteDialog.kt
  • app/src/main/java/com/cornellappdev/uplift/data/repositories/UserInfoRepository.kt

Copy link
Copy Markdown
Member

@AndrewCheung360 AndrewCheung360 left a comment

Choose a reason for hiding this comment

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

LGTM. Nice work! Just one thing about the root nav model about applyMutation.

@isiahpwilliams isiahpwilliams merged commit 338cee3 into main Apr 21, 2026
3 checks passed
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.

2 participants