[feat] #178 select-album 모듈 구현 및 upload-album 전환#185
[feat] #178 select-album 모듈 구현 및 upload-album 전환#185
Conversation
- feature:select-album (api, impl) 모듈 신규 생성 - app 모듈 및 settings.gradle.kts에 신규 모듈 의존성 추가
Walkthrough이 PR은 사진 업로드 앨범 선택 플로우를 재구조화합니다. 기존의 Changes
Sequence Diagram(s)sequenceDiagram
participant MainActivity as MainActivity
participant MainVM as MainViewModel
participant SelectAlbumVM as SelectAlbumViewModel
participant Repo as Repository
participant UploadUC as Upload Use Case
MainActivity->>MainVM: ClickUploadWithAlbum 또는<br/>ClickUploadWithoutAlbum Intent
MainVM->>MainVM: SelectAlbumAction 생성<br/>(QR 또는 Gallery)
MainVM->>MainActivity: NavigateToSelectAlbum<br/>(action) Side Effect
MainActivity->>SelectAlbumVM: SelectAlbumRoute로<br/>네비게이션
SelectAlbumVM->>Repo: 앨범/폴더 데이터 로드
Repo-->>SelectAlbumVM: 앨범 목록
SelectAlbumVM->>SelectAlbumVM: State 업데이트
Note over SelectAlbumVM: isLoading=true → false<br/>albums 리스트 반영
SelectAlbumVM->>MainActivity: ClickConfirmButton Intent
SelectAlbumVM->>UploadUC: uploadSinglePhoto 또는<br/>uploadMultiplePhoto 호출
UploadUC-->>SelectAlbumVM: 업로드 결과
SelectAlbumVM->>SelectAlbumVM: isUploading 해제<br/>Success Toast 발행
SelectAlbumVM->>MainActivity: SendUploadResult<br/>Side Effect
MainActivity->>MainActivity: 앨범 상세 화면으로<br/>네비게이션
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt (1)
125-148: 앨범 생성 중 로딩 표시 부재
handleAddAlbum에서 네트워크 요청 중 로딩 상태를 설정하지 않습니다. 사용자가 "추가하기" 버튼을 여러 번 클릭하면 중복 요청이 발생할 수 있습니다.🔧 로딩 상태 추가 제안
private fun handleAddAlbum( albumName: String, reduce: (SelectAlbumState.() -> SelectAlbumState) -> Unit, postSideEffect: (SelectAlbumSideEffect) -> Unit, ) { viewModelScope.launch { + reduce { copy(isLoading = true) } folderRepository.createFolder(name = albumName) .onSuccess { fetchFolders(reduce) reduce { copy( + isLoading = false, isShowAddAlbumBottomSheet = false, albumNameTextState = TextFieldState(), ) } postSideEffect(SelectAlbumSideEffect.ShowToastMessage("새로운 앨범을 추가했어요")) } .onFailure { e -> Timber.e(e) - reduce { copy(isShowAddAlbumBottomSheet = false, albumNameTextState = TextFieldState()) } + reduce { copy(isLoading = false, isShowAddAlbumBottomSheet = false, albumNameTextState = TextFieldState()) } postSideEffect(SelectAlbumSideEffect.ShowToastMessage("앨범 추가에 실패했어요")) } } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt` around lines 125 - 148, The add-album flow in handleAddAlbum does not set a loading flag so duplicate taps can trigger multiple requests; before launching folderRepository.createFolder inside viewModelScope.launch, call reduce to set a new SelectAlbumState flag (e.g., isAddingAlbum or isLoadingAlbum = true) and disable the add button in the UI based on that flag, then after onSuccess and onFailure ensure you reset that flag to false (alongside the existing isShowAddAlbumBottomSheet and albumNameTextState updates); optionally, guard at the top of handleAddAlbum by checking the state flag and returning early if already loading to fully prevent concurrent requests.
🤖 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/select-album/api/src/main/java/com/neki/android/feature/select_album/api/SelectAlbumNavKey.kt`:
- Line 7: Remove the unnecessary consecutive blank line in SelectAlbumNavKey.kt
that triggers Detekt's NoConsecutiveBlankLines rule (the extra empty line around
line 7); open the file containing the SelectAlbumNavKey declaration and delete
the extra blank line so there is only a single empty line between
sections/statements, then run Detekt/CI to confirm the warning is resolved.
In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt`:
- Around line 69-73: SelectAlbumViewModel currently passes the raw album name
from the intent handler to handleAddAlbum without defensive
trimming/empty-checking; update the ClickAddAlbumConfirmButton handling in
SelectAlbumViewModel so that before calling handleAddAlbum you call .trim() on
state.albumNameTextState.text and skip/return early (or do not call
handleAddAlbum) if the trimmed string is empty, matching the pattern used in
ArchiveMainViewModel.handleAddAlbum; ensure reduce and postSideEffect are
preserved when calling handleAddAlbum.
---
Nitpick comments:
In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt`:
- Around line 125-148: The add-album flow in handleAddAlbum does not set a
loading flag so duplicate taps can trigger multiple requests; before launching
folderRepository.createFolder inside viewModelScope.launch, call reduce to set a
new SelectAlbumState flag (e.g., isAddingAlbum or isLoadingAlbum = true) and
disable the add button in the UI based on that flag, then after onSuccess and
onFailure ensure you reset that flag to false (alongside the existing
isShowAddAlbumBottomSheet and albumNameTextState updates); optionally, guard at
the top of handleAddAlbum by checking the state flag and returning early if
already loading to fully prevent concurrent requests.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: e6db1ab7-926d-427f-a80a-f2f6d956babe
📒 Files selected for processing (21)
app/build.gradle.ktsapp/src/main/java/com/neki/android/app/MainActivity.ktapp/src/main/java/com/neki/android/app/main/MainContract.ktapp/src/main/java/com/neki/android/app/main/MainScreen.ktapp/src/main/java/com/neki/android/app/main/MainViewModel.ktfeature/photo-upload/api/src/main/java/com/neki/android/feature/photo_upload/api/PhotoUploadNavKey.ktfeature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumContract.ktfeature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumScreen.ktfeature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumViewModel.ktfeature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/component/UploadAlbumTopBar.ktfeature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/di/PhotoUploadEntryProvider.ktfeature/select-album/api/build.gradle.ktsfeature/select-album/api/src/main/java/com/neki/android/feature/select_album/api/SelectAlbumNavKey.ktfeature/select-album/api/src/main/java/com/neki/android/feature/select_album/api/SelectAlbumResult.ktfeature/select-album/impl/build.gradle.ktsfeature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumContract.ktfeature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumScreen.ktfeature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumTopBar.ktfeature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.ktfeature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/navigation/SelectAlbumEntryProvider.ktsettings.gradle.kts
💤 Files with no reviewable changes (6)
- feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/di/PhotoUploadEntryProvider.kt
- feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/component/UploadAlbumTopBar.kt
- feature/photo-upload/api/src/main/java/com/neki/android/feature/photo_upload/api/PhotoUploadNavKey.kt
- feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumContract.kt
- feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumScreen.kt
- feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumViewModel.kt
...elect-album/api/src/main/java/com/neki/android/feature/select_album/api/SelectAlbumNavKey.kt
Outdated
Show resolved
Hide resolved
...-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt
Show resolved
Hide resolved
...-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt
Show resolved
Hide resolved
There was a problem hiding this comment.
♻️ Duplicate comments (1)
feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt (1)
65-69:⚠️ Potential issue | 🟡 Minor공백 앨범명이 생성 요청으로 전달될 수 있습니다.
Line 65-67에서 입력값을 그대로 넘겨
" "같은 값이 요청으로 전달될 수 있습니다. ViewModel에서trim()후 빈 값이면 조기 반환해 주세요. (이 이슈는 이전 리뷰에서도 이미 지적된 항목입니다.)제안 수정안
- SelectAlbumIntent.ClickAddAlbumConfirmButton -> handleAddAlbum( - albumName = state.albumNameTextState.text.toString(), - reduce = reduce, - postSideEffect = postSideEffect, - ) + SelectAlbumIntent.ClickAddAlbumConfirmButton -> { + val trimmedAlbumName = state.albumNameTextState.text.toString().trim() + if (trimmedAlbumName.isEmpty()) return + handleAddAlbum( + albumName = trimmedAlbumName, + reduce = reduce, + postSideEffect = postSideEffect, + ) + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt` around lines 65 - 69, When handling SelectAlbumIntent.ClickAddAlbumConfirmButton, sanitize the input before calling handleAddAlbum: retrieve state.albumNameTextState.text, call trim() and if the resulting string is empty return early (do not call handleAddAlbum or post side effects); otherwise pass the trimmed string as albumName into handleAddAlbum so no whitespace-only names are sent to the request.
🧹 Nitpick comments (1)
feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt (1)
130-141: 성공/실패 분기의 바텀시트 초기화 로직이 중복됩니다.Line 130-135와 Line 140에서 동일 상태 초기화가 반복됩니다. 헬퍼로 추출하면 추후 상태 변경 시 누락 위험을 줄일 수 있습니다.
리팩터링 예시
+ private fun resetAddAlbumSheetState( + reduce: (SelectAlbumState.() -> SelectAlbumState) -> Unit, + ) { + reduce { + copy( + isShowAddAlbumBottomSheet = false, + albumNameTextState = TextFieldState(), + ) + } + } + private fun handleAddAlbum( albumName: String, reduce: (SelectAlbumState.() -> SelectAlbumState) -> Unit, postSideEffect: (SelectAlbumSideEffect) -> Unit, ) { viewModelScope.launch { folderRepository.createFolder(name = albumName) .onSuccess { fetchFolders(reduce) - reduce { - copy( - isShowAddAlbumBottomSheet = false, - albumNameTextState = TextFieldState(), - ) - } + resetAddAlbumSheetState(reduce) postSideEffect(SelectAlbumSideEffect.ShowToastMessage("새로운 앨범을 추가했어요")) } .onFailure { e -> Timber.e(e) - reduce { copy(isShowAddAlbumBottomSheet = false, albumNameTextState = TextFieldState()) } + resetAddAlbumSheetState(reduce) postSideEffect(SelectAlbumSideEffect.ShowToastMessage("앨범 추가에 실패했어요")) } } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt` around lines 130 - 141, The reduce logic that resets isShowAddAlbumBottomSheet and albumNameTextState is duplicated in both the success and failure branches of the add-album flow; extract a single helper (e.g., a private method resetAddAlbumUiState or an inline function resetAddAlbumState) and call it from both the success path (after reduce { ... } currently) and the onFailure path before posting side effects, updating uses of reduce, SelectAlbumSideEffect, albumNameTextState, and isShowAddAlbumBottomSheet so the UI-reset behavior is centralized and only maintained in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt`:
- Around line 65-69: When handling SelectAlbumIntent.ClickAddAlbumConfirmButton,
sanitize the input before calling handleAddAlbum: retrieve
state.albumNameTextState.text, call trim() and if the resulting string is empty
return early (do not call handleAddAlbum or post side effects); otherwise pass
the trimmed string as albumName into handleAddAlbum so no whitespace-only names
are sent to the request.
---
Nitpick comments:
In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt`:
- Around line 130-141: The reduce logic that resets isShowAddAlbumBottomSheet
and albumNameTextState is duplicated in both the success and failure branches of
the add-album flow; extract a single helper (e.g., a private method
resetAddAlbumUiState or an inline function resetAddAlbumState) and call it from
both the success path (after reduce { ... } currently) and the onFailure path
before posting side effects, updating uses of reduce, SelectAlbumSideEffect,
albumNameTextState, and isShowAddAlbumBottomSheet so the UI-reset behavior is
centralized and only maintained in one place.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 90a557b9-6173-48e5-8338-ed285d72aa0a
📒 Files selected for processing (1)
feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.kt
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumScreen.kt (1)
124-131: 앨범명 중복 검사에서favoriteAlbum누락 가능성
existingAlbumNames가uiState.albums만 포함하고uiState.favoriteAlbum.title은 포함하지 않습니다. 즐겨찾기 앨범명과 동일한 이름으로 새 앨범을 생성하려는 경우 중복 검사가 작동하지 않을 수 있습니다. 서버에서 중복 검사를 수행한다면 문제없지만, 클라이언트 UX 측면에서 검토가 필요합니다.수정 제안
- val existingAlbumNames = remember(uiState.albums) { uiState.albums.map { it.title } } + val existingAlbumNames = remember(uiState.albums, uiState.favoriteAlbum) { + uiState.albums.map { it.title } + uiState.favoriteAlbum.title + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumScreen.kt` around lines 124 - 131, existingAlbumNames is built only from uiState.albums so the favorite album title (uiState.favoriteAlbum.title) can be omitted and allow a duplicate name; update the logic in SelectAlbumScreen to include the favorite title when computing existingAlbumNames (e.g., build the list from uiState.albums plus uiState.favoriteAlbum?.title if non-null) and adjust the remember dependencies to include uiState.favoriteAlbum (or its title) so errorMessage's derivedStateOf correctly re-evaluates; ensure null-safe handling of uiState.favoriteAlbum before adding it to the list.
🤖 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/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumScreen.kt`:
- Around line 101-103: SelectAlbumScreen currently renders
FavoriteAlbumRowComponent without wiring its onClick; update the item block that
uses FavoriteAlbumRowComponent (with uiState.favoriteAlbum) to pass onClick = {
onIntent(AllAlbumIntent.ClickFavoriteAlbum) } so the favorite-album selection
flow matches AllAlbumScreen; ensure you reference the existing onIntent handler
and AllAlbumIntent.ClickFavoriteAlbum when adding the onClick param.
---
Nitpick comments:
In
`@feature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumScreen.kt`:
- Around line 124-131: existingAlbumNames is built only from uiState.albums so
the favorite album title (uiState.favoriteAlbum.title) can be omitted and allow
a duplicate name; update the logic in SelectAlbumScreen to include the favorite
title when computing existingAlbumNames (e.g., build the list from
uiState.albums plus uiState.favoriteAlbum?.title if non-null) and adjust the
remember dependencies to include uiState.favoriteAlbum (or its title) so
errorMessage's derivedStateOf correctly re-evaluates; ensure null-safe handling
of uiState.favoriteAlbum before adding it to the list.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: f34af0bb-1f34-4318-a2f4-dbc95a31106f
📒 Files selected for processing (11)
app/src/main/java/com/neki/android/app/MainActivity.ktapp/src/main/java/com/neki/android/app/main/MainContract.ktapp/src/main/java/com/neki/android/app/main/MainScreen.ktapp/src/main/java/com/neki/android/app/main/MainViewModel.ktfeature/select-album/api/src/main/java/com/neki/android/feature/select_album/api/SelectAlbumAction.ktfeature/select-album/api/src/main/java/com/neki/android/feature/select_album/api/SelectAlbumNavKey.ktfeature/select-album/impl/build.gradle.ktsfeature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumContract.ktfeature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumScreen.ktfeature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumViewModel.ktfeature/select-album/impl/src/main/java/com/neki/android/feature/select_album/impl/navigation/SelectAlbumEntryProvider.kt
✅ Files skipped from review due to trivial changes (1)
- feature/select-album/api/src/main/java/com/neki/android/feature/select_album/api/SelectAlbumAction.kt
🚧 Files skipped from review as they are similar to previous changes (5)
- app/src/main/java/com/neki/android/app/MainActivity.kt
- feature/select-album/impl/build.gradle.kts
- app/src/main/java/com/neki/android/app/main/MainContract.kt
- app/src/main/java/com/neki/android/app/main/MainScreen.kt
- app/src/main/java/com/neki/android/app/main/MainViewModel.kt
...ect-album/impl/src/main/java/com/neki/android/feature/select_album/impl/SelectAlbumScreen.kt
Show resolved
Hide resolved
There was a problem hiding this comment.
해당 컴포넌트는 component 디렉토리 만들어서 하위에 위치시키죠 !
| val title: String = "모든 앨범", | ||
| val multiSelect: Boolean = true, |
There was a problem hiding this comment.
navigateToSelectAlbum()에서 title과 multiSelect의 초기값이 동일하게 지정되어 넘어오는데 SelectAlbum 내부에서는 초기값 정의하지 않아도 되지 않을까요?
| uploadSingleImage(state.scannedImageUrl, reduce = reduce, postSideEffect = postSideEffect) | ||
| } else { | ||
| postSideEffect(MainSideEffect.NavigateToUploadAlbumWithGallery(state.selectedUris.map { it.toString() })) | ||
| uploadMultipleImages(state.selectedUris, reduce = reduce, postSideEffect = postSideEffect) |
There was a problem hiding this comment.
일관되게 첫번째 파라미터명 명시해주시면 좋을 것 같습니다
| SelectAlbumIntent.EnterSelectAlbumScreen -> fetchInitialData(reduce) | ||
| SelectAlbumIntent.ClickBackIcon -> postSideEffect(SelectAlbumSideEffect.NavigateBack) | ||
| SelectAlbumIntent.ClickConfirmButton -> { | ||
| if (state.isUploading) return |
There was a problem hiding this comment.
방어적으로 작성하기 위함인건 이해했으나 (isLoading || isUploading) 일 때 LoadingDialog()가 노출되어 사실 n장 업로드 버튼을 선택할 수 없는데 해당 부분에서만 유독 방어적으로 작성할 필요가 있을까요.?.?
UIState에 isConfirmEnabled 플래그를 통해 한 번 방어하고 있기도 하구요!
🔗 관련 이슈
📙 작업 설명
feature/select-album모듈(api/impl) 생성 및 의존성 설정upload-album제거 후select-album으로 전환SelectAlbumActionsealed interface 도입: 네비게이션 시 업로드 유형(QR/갤러리) 전달🧪 테스트 내역 (선택)
📸 스크린샷 또는 시연 영상 (선택)
Screen_recording_20260410_015713.mp4
💬 추가 설명 or 리뷰 포인트 (선택)
SelectAlbumAction을 NavKey에 담아 전달:UploadFromQR(imageUrl)/UploadFromGallery(imageUriStrings)AlbumDetailResult전송 → Archive 자동 갱신 (별도 ResultBus 이벤트 불필요)Summary by CodeRabbit
릴리스 노트
새로운 기능
개선 사항