Skip to content

๐Ÿ”€ :: (#835) ๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ • ๊ตฌํ˜„#841

Merged
uson1004 merged 2 commits intodevelopfrom
feature/840-๋น„๋ฐ€๋ฒˆํ˜ธ-์žฌ์„ค์ •-๊ตฌํ˜„
Mar 17, 2026

Hidden character warning

The head ref may contain hidden characters: "feature/840-\ube44\ubc00\ubc88\ud638-\uc7ac\uc124\uc815-\uad6c\ud604"
Merged

๐Ÿ”€ :: (#835) ๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ • ๊ตฌํ˜„#841
uson1004 merged 2 commits intodevelopfrom
feature/840-๋น„๋ฐ€๋ฒˆํ˜ธ-์žฌ์„ค์ •-๊ตฌํ˜„

Conversation

@uson1004
Copy link
Copy Markdown
Member

@uson1004 uson1004 commented Mar 17, 2026

๊ฐœ์š”

์ž‘์—…์‚ฌํ•ญ

์ถ”๊ฐ€ ๋กœ ํ•  ๋ง

Summary by CodeRabbit

  • New Features
    • Added password reset functionality accessible from the sign-in screen with a multi-step guided flow
    • Users can verify account ID, confirm name and email, validate identity via email verification code with a 5-minute countdown, and set a new password
    • Includes validation error messages and appropriate user feedback throughout the reset process

@uson1004 uson1004 requested a review from a team March 17, 2026 12:36
@uson1004 uson1004 self-assigned this Mar 17, 2026
@uson1004 uson1004 added the feat ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ ํ•  ๊ฒฝ์šฐ label Mar 17, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 17, 2026

๐Ÿ“ Walkthrough

Walkthrough

This PR introduces a complete password reset feature for the DMS Android application. It adds a new navigation key, integrates a multi-step reset password screen with Jetpack Compose, includes a ViewModel managing server interactions and form validation, and wires the feature into the existing Sign In flow.

Changes

Cohort / File(s) Summary
Navigation Framework
app/.../navigation/DmsNavKeys.kt, app/.../DmsApp.kt
Added ResetPasswordScreenNav navigation key and integrated ResetPasswordRoute into the app's navigation graph with back navigation and snackbar handling.
Reset Password Feature - Core
feature/.../resetpassword/ResetPasswordScreen.kt, feature/.../resetpassword/ResetPasswordViewModel.kt
Introduced ResetPasswordScreen composable with multi-step UI flow (InputId, InputUserInfo, InputEmailVerificationCode, InputNewPassword) and a ViewModel managing step transitions, form validation, 5-minute email verification timer, and server interactions (checking ID, sending/validating codes, resetting password).
Reset Password Feature - Components
feature/.../resetpassword/component/InputIdContent.kt, feature/.../resetpassword/component/InputUserInfoContent.kt, feature/.../resetpassword/component/EmailVerificationContent.kt, feature/.../resetpassword/component/InputNewPasswordContent.kt
Added four reusable composable components rendering UI for each reset password step: account ID input, user info input (name/email), email verification code input with timer, and new password/confirmation input with validation error display.
Reset Password Route Integration
feature/.../resetpassword/navigation/ResetPasswordNavigation.kt
Added ResetPasswordRoute composable wrapping ResetPasswordScreen and exposing navigation and snackbar callbacks.
Reset Password Models
feature/.../resetpassword/model/ResetPasswordTextFieldError.kt
Introduced ResetPasswordTextFieldError sealed interface with error variants (InvalidEmailVerificationCode, EmailVerificationCodeTimeExpired, InvalidPasswordFormat, PasswordMismatch) and an isError() extension function.
Sign In Integration
feature/.../signin/navigation/SignInRoute.kt, feature/.../signin/ui/SigninScreen.kt
Updated SignInRoute and SignInScreen to accept and forward navigateToResetPassword callback, enabling the "๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ •" action to navigate to the password reset flow.

Sequence Diagram

sequenceDiagram
    actor User
    participant SignIn as SignIn Screen
    participant ResetPW as ResetPassword Screen
    participant ViewModel as ResetPassword ViewModel
    participant AuthRepo as AuthRepository
    participant StudentRepo as StudentRepository
    participant Server as Server

    User->>SignIn: Click "๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ •"
    SignIn->>ResetPW: Navigate to Reset Password
    
    rect rgba(100, 150, 255, 0.5)
    Note over User,Server: Step 1: Check ID
    User->>ResetPW: Enter account ID
    User->>ResetPW: Click "๋‹ค์Œ"
    ResetPW->>ViewModel: moveNext(InputId)
    ViewModel->>AuthRepo: checkIdExists(accountId)
    AuthRepo->>Server: GET /check-id
    Server-->>AuthRepo: {hashEmail, ...}
    AuthRepo-->>ViewModel: Success with hashEmail
    ViewModel->>ResetPW: Store hashEmail, advance to InputUserInfo
    end

    rect rgba(100, 150, 255, 0.5)
    Note over User,Server: Step 2: Input User Info
    User->>ResetPW: Enter name & email
    User->>ResetPW: Click "๋‹ค์Œ"
    ResetPW->>ViewModel: moveNext(InputUserInfo)
    ViewModel->>AuthRepo: sendEmailVerificationCode(email, PASSWORD)
    AuthRepo->>Server: POST /send-verification-code
    Server-->>AuthRepo: Success
    AuthRepo-->>ViewModel: Success, start 5min timer
    ViewModel->>ResetPW: Start countdown timer, advance to InputEmailVerificationCode
    end

    rect rgba(100, 150, 255, 0.5)
    Note over User,Server: Step 3: Verify Email Code
    User->>ResetPW: Enter verification code
    User->>ResetPW: Click "๋‹ค์Œ"
    ResetPW->>ViewModel: moveNext(InputEmailVerificationCode)
    ViewModel->>AuthRepo: checkEmailVerificationCode(code)
    AuthRepo->>Server: POST /verify-code
    Server-->>AuthRepo: Valid/Invalid response
    AuthRepo-->>ViewModel: Success or error
    ViewModel->>ResetPW: Advance to InputNewPassword or show error
    end

    rect rgba(100, 150, 255, 0.5)
    Note over User,Server: Step 4: Reset Password
    User->>ResetPW: Enter new password & confirm
    User->>ResetPW: Click "์™„๋ฃŒ"
    ResetPW->>ViewModel: moveNext(InputNewPassword)
    ViewModel->>StudentRepo: resetPassword(newPassword)
    StudentRepo->>Server: PUT /reset-password
    Server-->>StudentRepo: Success/Error response
    StudentRepo-->>ViewModel: Password updated
    ViewModel->>ResetPW: Show success snackbar, navigate back
    ResetPW->>SignIn: Navigate back to SignIn
    end
Loading

Estimated code review effort

๐ŸŽฏ 4 (Complex) | โฑ๏ธ ~45 minutes

Possibly related PRs

Suggested reviewers

  • parkuiery

Poem

๐Ÿฐ A password lost? No need to fret!
Five hoppy steps, and you've reset yet.
Verify, validate, timer ticks down,
New password reigns o'er the whole town! ๐Ÿ”โœจ

๐Ÿšฅ Pre-merge checks | โœ… 2 | โŒ 1

โŒ Failed checks (1 warning)

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.
โœ… Passed checks (2 passed)
Check name Status Explanation
Description Check โœ… Passed Check skipped - CodeRabbitโ€™s high-level summary is enabled.
Title check โœ… Passed The title accurately summarizes the main change: implementing password reset functionality (๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ • ๊ตฌํ˜„), which is the core objective reflected across all modified files.

โœ๏ธ Tip: You can configure your own custom pre-merge checks in the settings.

โœจ Finishing Touches
  • ๐Ÿ“ Generate docstrings (stacked PR)
  • ๐Ÿ“ Generate docstrings (commit on current branch)
๐Ÿงช Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/840-๋น„๋ฐ€๋ฒˆํ˜ธ-์žฌ์„ค์ •-๊ตฌํ˜„
๐Ÿ“ Coding Plan
  • Generate coding plan for human review comments

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.

@uson1004 uson1004 changed the title Release v2.1.0 ๐Ÿ”€ :: (#835) Mar 17, 2026
@uson1004 uson1004 changed the title ๐Ÿ”€ :: (#835) ๐Ÿ”€ :: (#835) ๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ • ๊ตฌํ˜„ Mar 17, 2026
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: 1

๐Ÿงน Nitpick comments (2)
feature/src/main/kotlin/team/aliens/dms/android/feature/resetpassword/ResetPasswordViewModel.kt (1)

31-44: Consider trimming whitespace from user inputs.

The accountId is used directly from state without trimming. Users might accidentally include leading/trailing spaces. Consider trimming inputs before making API calls.

โ™ป๏ธ Optional improvement
 private fun checkIdExists() = viewModelScope.launch {
     setState { it.copy(isLoading = true) }
-    authRepository.checkIdExists(accountId = uiState.value.accountId)
+    authRepository.checkIdExists(accountId = uiState.value.accountId.trim())

Similar trimming could be applied to name and email in other methods.

๐Ÿค– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/src/main/kotlin/team/aliens/dms/android/feature/resetpassword/ResetPasswordViewModel.kt`
around lines 31 - 44, The checkIdExists coroutine reads accountId directly from
uiState without trimming; before calling authRepository.checkIdExists in the
checkIdExists function, trim whitespace from uiState.value.accountId (e.g.,
compute a local trimmedAccountId = uiState.value.accountId.trim()) and pass that
to authRepository.checkIdExists(accountId = trimmedAccountId); also update any
state uses (like setState or hashEmail assignment) or effects that should use
the trimmed value so the repository call and subsequent behavior use the cleaned
input.
feature/src/main/kotlin/team/aliens/dms/android/feature/resetpassword/ResetPasswordScreen.kt (1)

66-73: Extract duplicated back-navigation branch into one handler.

Line 66-73 and Line 139-145 implement the same logic; consolidating this avoids divergence later.

โ™ป๏ธ Suggested refactor
+    val handleBack = {
+        if (currentStepIndex > 0) {
+            currentStepIndex--
+            viewModel.onStepChanged(steps[currentStepIndex])
+        } else {
+            onNavigateBack()
+        }
+    }
+
     BackHandler {
-        if (currentStepIndex > 0) {
-            currentStepIndex--
-            viewModel.onStepChanged(steps[currentStepIndex])
-        } else {
-            onNavigateBack()
-        }
+        handleBack()
     }
@@
-        onBackClick = {
-            if (currentStepIndex > 0) {
-                currentStepIndex--
-                viewModel.onStepChanged(steps[currentStepIndex])
-            } else {
-                onNavigateBack()
-            }
-        },
+        onBackClick = handleBack,

Also applies to: 139-145

๐Ÿค– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/src/main/kotlin/team/aliens/dms/android/feature/resetpassword/ResetPasswordScreen.kt`
around lines 66 - 73, Duplicate back-navigation logic in the BackHandler blocks
should be extracted into a single helper to avoid divergence: create a function
(e.g., handleBackNavigation or onBackPressed) that contains the current logic
checking currentStepIndex, decrementing it, calling
viewModel.onStepChanged(steps[currentStepIndex]) or calling onNavigateBack()
when at index 0; then replace both BackHandler lambdas (the ones using
currentStepIndex, viewModel.onStepChanged, steps, and onNavigateBack) with a
single call to that helper to centralize behavior.
๐Ÿค– Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@feature/src/main/kotlin/team/aliens/dms/android/feature/resetpassword/ResetPasswordScreen.kt`:
- Around line 84-87: The handler for ResetPasswordSideEffect.MoveToNext
unconditionally increments currentStepIndex and then accesses
steps[currentStepIndex], which can throw IndexOutOfBoundsException; update the
logic to guard advancement by checking currentStepIndex < steps.lastIndex (or
compute nextIndex = min(currentStepIndex + 1, steps.lastIndex)) before
incrementing/using it, only call viewModel.onStepChanged(steps[nextIndex]) when
within bounds, and avoid advancing or handle completion when already on the
final step (references: ResetPasswordSideEffect.MoveToNext, currentStepIndex,
steps, viewModel.onStepChanged, ResetPasswordStep.InputEmailVerificationCode).

---

Nitpick comments:
In
`@feature/src/main/kotlin/team/aliens/dms/android/feature/resetpassword/ResetPasswordScreen.kt`:
- Around line 66-73: Duplicate back-navigation logic in the BackHandler blocks
should be extracted into a single helper to avoid divergence: create a function
(e.g., handleBackNavigation or onBackPressed) that contains the current logic
checking currentStepIndex, decrementing it, calling
viewModel.onStepChanged(steps[currentStepIndex]) or calling onNavigateBack()
when at index 0; then replace both BackHandler lambdas (the ones using
currentStepIndex, viewModel.onStepChanged, steps, and onNavigateBack) with a
single call to that helper to centralize behavior.

In
`@feature/src/main/kotlin/team/aliens/dms/android/feature/resetpassword/ResetPasswordViewModel.kt`:
- Around line 31-44: The checkIdExists coroutine reads accountId directly from
uiState without trimming; before calling authRepository.checkIdExists in the
checkIdExists function, trim whitespace from uiState.value.accountId (e.g.,
compute a local trimmedAccountId = uiState.value.accountId.trim()) and pass that
to authRepository.checkIdExists(accountId = trimmedAccountId); also update any
state uses (like setState or hashEmail assignment) or effects that should use
the trimmed value so the repository call and subsequent behavior use the cleaned
input.

โ„น๏ธ Review info
โš™๏ธ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 589c59e8-4dfe-4972-8b82-80b2bb5720b8

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between 4f54a95 and 2c89577.

๐Ÿ“’ Files selected for processing (12)
  • app/src/main/kotlin/team/aliens/dms/android/app/navigation/DmsNavKeys.kt
  • app/src/main/kotlin/team/aliens/dms/android/app/ui/DmsApp.kt
  • feature/src/main/kotlin/team/aliens/dms/android/feature/resetpassword/ResetPasswordScreen.kt
  • feature/src/main/kotlin/team/aliens/dms/android/feature/resetpassword/ResetPasswordViewModel.kt
  • feature/src/main/kotlin/team/aliens/dms/android/feature/resetpassword/component/EmailVerificationContent.kt
  • feature/src/main/kotlin/team/aliens/dms/android/feature/resetpassword/component/InputIdContent.kt
  • feature/src/main/kotlin/team/aliens/dms/android/feature/resetpassword/component/InputNewPasswordContent.kt
  • feature/src/main/kotlin/team/aliens/dms/android/feature/resetpassword/component/InputUserInfoContent.kt
  • feature/src/main/kotlin/team/aliens/dms/android/feature/resetpassword/model/ResetPasswordTextFieldError.kt
  • feature/src/main/kotlin/team/aliens/dms/android/feature/resetpassword/navigation/ResetPasswordNavigation.kt
  • feature/src/main/kotlin/team/aliens/dms/android/feature/signin/navigation/SignInRoute.kt
  • feature/src/main/kotlin/team/aliens/dms/android/feature/signin/ui/SigninScreen.kt

Comment on lines +84 to +87
is ResetPasswordSideEffect.MoveToNext -> {
currentStepIndex++
viewModel.onStepChanged(steps[currentStepIndex])
if (steps[currentStepIndex] == ResetPasswordStep.InputEmailVerificationCode) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

โš ๏ธ Potential issue | ๐ŸŸ  Major

Guard step advancement to prevent step-index overflow crash.

Line 85 increments first, then Line 86/87 dereference steps[currentStepIndex] without a bound check. If MoveToNext arrives on the final step, this can throw IndexOutOfBoundsException.

๐Ÿ’ก Suggested fix
                 is ResetPasswordSideEffect.MoveToNext -> {
-                    currentStepIndex++
-                    viewModel.onStepChanged(steps[currentStepIndex])
-                    if (steps[currentStepIndex] == ResetPasswordStep.InputEmailVerificationCode) {
-                        timerRunning = true
-                    }
+                    if (currentStepIndex < steps.lastIndex) {
+                        currentStepIndex++
+                        val nextStep = steps[currentStepIndex]
+                        viewModel.onStepChanged(nextStep)
+                        if (nextStep == ResetPasswordStep.InputEmailVerificationCode) {
+                            timerRunning = true
+                        }
+                    }
                 }
๐Ÿค– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/src/main/kotlin/team/aliens/dms/android/feature/resetpassword/ResetPasswordScreen.kt`
around lines 84 - 87, The handler for ResetPasswordSideEffect.MoveToNext
unconditionally increments currentStepIndex and then accesses
steps[currentStepIndex], which can throw IndexOutOfBoundsException; update the
logic to guard advancement by checking currentStepIndex < steps.lastIndex (or
compute nextIndex = min(currentStepIndex + 1, steps.lastIndex)) before
incrementing/using it, only call viewModel.onStepChanged(steps[nextIndex]) when
within bounds, and avoid advancing or handle completion when already on the
final step (references: ResetPasswordSideEffect.MoveToNext, currentStepIndex,
steps, viewModel.onStepChanged, ResetPasswordStep.InputEmailVerificationCode).

@uson1004 uson1004 merged commit e5cbdbb into develop Mar 17, 2026
2 checks passed
@uson1004 uson1004 deleted the feature/840-๋น„๋ฐ€๋ฒˆํ˜ธ-์žฌ์„ค์ •-๊ตฌํ˜„ branch March 17, 2026 13:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ ํ•  ๊ฒฝ์šฐ

Projects

None yet

Development

Successfully merging this pull request may close these issues.

๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ • ๊ตฌํ˜„

1 participant