Conversation
* feat: History 피처 MVI 컨트랙트 정의 및 UI 상태 모델 추가 MVI(Model-View-Intent) 패턴 적용을 위해 `HistoryUiState`, `HistoryUiIntent`, `HistoryUiEffect`를 추가하고 관련 UI 메시지 모델을 정의했습니다. * `HistoryUiState`: `Loading`, `LoadFailed` 상태 정의 * `HistoryUiIntent`: `FetchData` 의도 추가 * `HistoryUiEffect`: `ShowMessage`를 통한 일회성 이벤트(Side Effect) 처리 구조 추가 * `HistoryUiMessage`: 데이터 로드 실패 등 UI 알림을 위한 열거형 정의 * refactor: HistoryViewModel의 BaseViewModel 상속 및 구조 변경 * `ViewModel`을 `BaseViewModel`로 교체하여 MVI 기반 상태 관리 로직 적용 * `onIntent` 메서드 오버라이드 및 기본 구현 (TODO) * feat: History 피처 문자열 리소스 추가 * 데이터 로드 실패 시 표시할 국문 메시지 리소스 추가 (`feature_history_impl_fetch_data_failed`) * refactor: HistoryScreen 리팩터링 및 Hilt 연동 * `HistoryViewModel`을 주입받는 내부 `HistoryScreen` 컴포저블 추가 * 기존 UI 상태 파일을 `contract` 패키지로 이동 및 정리
히스토리 피처의 기본 화면 구조를 설계하고, 발표 목록 및 탭 전환을 위한 UI 컴포넌트를 구현했습니다.
* **화면 및 네비게이션 구현**:
* `HistoryScreen`: `HorizontalPager`를 활용하여 '준비 중인 발표'와 '완료한 발표' 탭 간의 화면 전환 구조 구현
* `HistoryViewModel`: 데이터 페칭을 위한 `FetchData` 인텐트 처리 로직 및 MVI 기반 구조 적용
* `LocalSnackbarHostState`를 통한 데이터 로드 실패 시 에러 메시지 표시 로직 추가
* **UI 컴포넌트 추가**:
* `HistoryHeadSection`: 상단 앱바와 `PrezelTabs`를 포함하는 헤더 섹션 구현
* `HistoryItemList`: `LazyColumn`을 사용하여 발표 아이템(`PresentationItem`) 리스트 표시
* `HistoryPresentationCard`: 발표 제목, D-Day, 날짜 및 관련 키워드(Chip)를 포함하는 카드 UI 구현
* `HistoryChip`: 강조 여부에 따라 스타일(Filled/Outlined)이 적용된 커스텀 칩 컴포넌트 추가
* **데이터 모델 및 리소스**:
* `PresentationItem`, `HistoryChipUiModel` 등 화면 구현에 필요한 데이터 모델 정의
* 히스토리 타이틀 및 에러 메시지 관련 문자열 리소스 추가
* `kotlinx.collections.immutable` 의존성 추가 및 `persistentListOf` 적용으로 안정성 강화
* refactor: `PresentationItem`을 `HistoryUiModel`로 변경 및 구조 개선 기존 `PresentationItem`을 `HistoryUiModel`로 명칭을 변경하고, 발표 카테고리 관리를 위해 `Category` 필드를 추가했습니다. * `PresentationItem.kt` 삭제 및 `HistoryUiModel.kt` 생성 * `HistoryUiModel` 내 `Category` 타입의 `category` 프로퍼티 추가 * `feature:history:impl` 모듈에 `core:model` 의존성 추가 * feat: `HistoryUiState` 내 콘텐츠 상태 정의 및 데이터 바인딩 발표 준비 중인 항목과 완료된 항목을 구분하여 관리할 수 있도록 `HistoryUiState`를 개선하고 ViewModel에서 초기 데이터를 주입했습니다. * `HistoryUiState.Content` 클래스 추가 (`preparingPresentations`, `completedPresentations` 리스트 포함) * `HistoryViewModel` 내 `fetchData`를 통해 더미 데이터 로드 로직 구현 * `HistoryScreen`에서 `UiState`를 기반으로 페이지 데이터를 생성하는 `toPages()` 확장 함수 추가 * refactor: History 관련 컴포넌트 및 프리뷰 최신화 UI 모델 변경 사항을 관련 컴포넌트에 반영하고 프리뷰 데이터를 실제와 유사하게 수정했습니다. * `HistoryItemList` 및 `HistoryPresentationCard`에서 사용하는 모델을 `HistoryUiModel`로 업데이트 * `HistoryScreenPreview`에서 `HistoryUiState.Content`를 사용하여 실제 화면 구성과 동일하게 프리뷰 구현 * `HistoryItemList` 내 프리뷰용 더미 데이터에 `Category` 정보 추가
… 매퍼를 추가했습니다.
* **feat: core:model 내 발표 관련 도메인 모델 추가**
* 발표의 목적(`Purpose`), 스타일(`Style`), 대상(`Audience`)을 정의하는 enum 클래스 추가
* **feat: 히스토리 발표 카드 컴포넌트 및 리소스 추가**
* `HistoryPresentationCard`: 날짜, 제목, 카테고리 및 메타 정보 칩을 포함하는 카드 UI 구현
* `HistoryChipLabelMapper`: 도메인 모델을 UI 문자열 리소스 ID로 변환하는 확장 함수 추가
* `feature:history:impl`에 카테고리, 목적, 스타일, 대상별 국문 문자열 리소스 추가
* **refactor: 히스토리 UI 모델 및 데이터 구조 개선**
* `HistoryUiModel`에서 자유 형식의 `HistoryChipUiModel` 리스트를 제거하고, `Category`, `Purpose`, `Style`, `Audience`를 직접 보유하도록 구조 변경
* `HistoryItemList` 및 `HistoryScreen`의 프리뷰 데이터를 신규 모델 구조에 맞춰 업데이트
* `HistoryViewModel`의 초기 상태 데이터를 상세 모델 기반으로 변경
* **refactor: 히스토리 컴포넌트 구조 분리**
* `HistoryItemList.kt`에 포함되어 있던 카드 구현부를 `HistoryPresentationCard.kt`로 분리하여 모듈화 및 가독성 개선
* feat: 히스토리 탭 정의 및 문자열 리소스 추가 히스토리 화면에서 사용하는 탭 항목을 리소스화하고 관련 로직을 추가했습니다. * `historyTabs()` 함수 추가: "준비 중인 발표", "완료한 발표" 탭 이름을 `stringResource`로 관리 * 기존 하드코딩된 `historyTabs` 변수 제거 및 호출부 수정 * feat: HistoryPresentationCard 클릭 기능 추가 및 UI 개선 히스토리 목록의 각 아이템 카드에 클릭 이벤트를 지원하도록 구조를 변경했습니다. * `HistoryPresentationCard`: `onClick` 파라미터 추가 및 `PrezelTouchArea` 적용 * `HistoryItemList`: `onClickItem` 콜백 추가 및 각 카드에 전달 * refactor: HistoryScreen 내 탭 및 목록 처리 로직 수정 * `HistoryScreen` 및 `HistoryHeadSection`: 신규 `historyTabs()` 함수를 사용하도록 수정 * `HistoryItemList` 호출 시 클릭 리스너 연결 (현재 빈 블록) * 프리뷰 코드 내 탭 데이터 참조 방식 업데이트
* **feat: 히스토리 빈 화면 컴포넌트 추가**
* `HistoryEmptyContent`: 발표 내역이 없을 때 표시되는 공통 UI 컴포넌트 추가
* 탭 종류(준비 중/완료)에 따라 서로 다른 안내 문구 및 '발표 추가하기' 버튼 노출 로직 구현
* 비어있는 상태를 나타내는 신규 이미지 리소스(`feature_history_impl_empty_no_presentation`) 및 문자열 추가
* **feat: HistoryScreen 내 빈 화면 처리 로직 적용**
* 각 탭의 아이템 리스트(`items`)가 비어있는 경우 `HistoryEmptyContent`를 표시하도록 분기 로직 추가
* **feat: 히스토리 발표 추가 버튼 구현**
* `HistoryAddPresentationButton`: `PrezelButton`을 활용한 커스텀 버튼 컴포넌트 추가 및 디자인 가이드 반영 (Small size, Outlined type)
* refactor: `HistoryScreen` 컴포넌트 구조 분리 및 가독성 개선 `HistoryScreen` 내부에 혼재되어 있던 UI 로직을 `HistoryContent`, `HistoryPagerContent`로 분리하여 가독성을 높이고 상태에 따른 분기 처리를 명확하게 개선했습니다. * `HistoryUiState`에 따른 로딩/콘텐츠 렌더링 분기 추가 * `HorizontalPager`를 별도의 `HistoryPagerContent` 컴포posable로 추출 * 기존 `toPages` 확장 함수를 제거하고 `HistoryPagerContent` 내부에서 직접 페이지 리스트 정의 * feat: 히스토리 아이템 클릭 처리 로직 추가 히스토리 목록의 아이템 클릭 이벤트를 처리하기 위한 인터페이스를 추가하고 상태별 분기를 정의했습니다. * `HistoryUiModel`에 `HistoryPresentationStatus` (PREPARING, COMPLETED) 필드 추가 * `HistoryScreen` 및 `HistoryItemList`에 `onClickHistoryItem` 콜백 추가 * 클릭 시 아이템의 상태(`status`)에 따른 처리 로직 구조 마련 * refactor: 히스토리 UI 모델 및 관련 컴포넌트 수정 * `HistoryUiModel`: 프리젠테이션 상태 관리를 위한 `status` 프로퍼티 추가 * `HistoryPresentationCard`: 내부 칩(Chips) 레이아웃 최적화를 위해 `@OptIn(ExperimentalLayoutApi::class)` 추가 * `HistoryEmptyContent`: `Image` 컴포넌트에 전달되던 `modifier` 오적용 수정 (`Modifier.size` 직접 사용) * `HistoryViewModel` 및 Preview: 신규 필드(`status`) 추가에 따른 더미 데이터 업데이트
`HistoryViewModel`의 `fetchData` 메서드 내에 인라인으로 정의되어 있던 더미 데이터 생성 로직을 별도의 프로퍼티와 함수로 분리하여 가독성을 개선했습니다. * `preparingPresentations` 및 `completedPresentations`를 별도의 `private val`로 추출 * `HistoryUiState.Content` 생성을 담당하는 `createHistoryContentState` 함수 추가
📝 WalkthroughWalkthrough프레젠테이션 도메인 모델에 세 가지 열거형(Audience, Purpose, Style)을 추가하고, History 기능의 상태 주도적 UI 구현을 완료했습니다. ViewModel, UI 컴포넌트, 상태 관리 계약, 매퍼, 리소스를 포괄적으로 구현했습니다. Changes
Possibly related PRs
🚥 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. 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: 3
🧹 Nitpick comments (1)
Prezel/feature/history/impl/src/main/res/drawable/feature_history_impl_empty_no_presentation.xml (1)
7-20: 빈 상태 아이콘 색상은 리소스로 추출해 두는 편이 유지보수에 유리합니다.
현재 하드코딩 색상 반복으로 다크모드/테마 확장 시 수정 지점이 늘어납니다.♻️ 제안 변경안
- android:fillColor="#AFB3BA" + android:fillColor="@color/feature_history_impl_empty_illustration"- android:fillColor="#AFB3BA" + android:fillColor="@color/feature_history_impl_empty_illustration"- android:fillColor="#AFB3BA" + android:fillColor="@color/feature_history_impl_empty_illustration"- android:fillColor="#AFB3BA" + android:fillColor="@color/feature_history_impl_empty_illustration"- android:fillColor="#AFB3BA" + android:fillColor="@color/feature_history_impl_empty_illustration"추가로 아래처럼 색상 리소스를 정의해 두면 됩니다(파일 외 변경):
<!-- res/values/colors.xml --> <color name="feature_history_impl_empty_illustration">#AFB3BA</color>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Prezel/feature/history/impl/src/main/res/drawable/feature_history_impl_empty_no_presentation.xml` around lines 7 - 20, Replace the repeated hard-coded color "#AFB3BA" in the drawable by referencing a color resource: add a color named feature_history_impl_empty_illustration to your colors.xml and update every android:fillColor attribute in the drawable (all path elements using android:fillColor="#AFB3BA") to use `@color/feature_history_impl_empty_illustration` instead so the icon color is centralized for theming and dark mode support.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@Prezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/component/HistoryPresentationCard.kt`:
- Around line 108-117: The Row containing the chips (Row with modifier,
HistoryCategoryChip and three HistoryMetaChip calls) forces all chips on one
line causing truncation on small screens; replace that Row with a wrapping
FlowRow (e.g., androidx.compose.foundation.layout.FlowRow or accompanist
FlowRow) so chips can wrap to new lines, preserve the modifier, horizontal
spacing Arrangement.spacedBy(PrezelTheme.spacing.V8) (or FlowRow's
mainAxisSpacing/crossAxisSpacing equivalents) and verticalAlignment behavior,
and add the necessary import for FlowRow.
In
`@Prezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/HistoryScreen.kt`:
- Around line 149-153: The empty-state CTA is currently a no-op because
HistoryEmptyContent is called with an empty lambda for onClickAddPresentation;
fix it by wiring a real callback (or a nullable callback that the caller
provides) into HistoryScreen so the button triggers the intended action, or
explicitly disable the button when no callback is supplied. Locate the
HistoryScreen invocation that builds HistoryEmptyContent (reference
HistoryEmptyContent and its onClickAddPresentation parameter) and either forward
a provided onAddPresentation callback from the parent into HistoryScreen or
change the parameter to accept a nullable (() -> Unit)? and handle
enabling/disabling the CTA accordingly so the button is never a silent no-op.
- Around line 95-97: The tab selection currently waits for
pagerState.animateScrollToPage to finish (called inside the onClickTab lambda),
causing the selected tab/indicator to update late; replace the async animated
call with an immediate pagerState.scrollToPage call (or implement the
PrezelTabsPager pattern: detect currentPage and only call animateScrollToPage
for adjacent pages) and remove the scope.launch wrapper so the selection updates
synchronously in PrezelTabs when onClickTab is invoked; update usages of
pagerState.animateScrollToPage in the onClickTab lambda accordingly to use
pagerState.scrollToPage or the adjacent-animation logic.
---
Nitpick comments:
In
`@Prezel/feature/history/impl/src/main/res/drawable/feature_history_impl_empty_no_presentation.xml`:
- Around line 7-20: Replace the repeated hard-coded color "#AFB3BA" in the
drawable by referencing a color resource: add a color named
feature_history_impl_empty_illustration to your colors.xml and update every
android:fillColor attribute in the drawable (all path elements using
android:fillColor="#AFB3BA") to use
`@color/feature_history_impl_empty_illustration` instead so the icon color is
centralized for theming and dark mode support.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 3af2992a-3f83-4fcb-a70c-0cf51c6a4c7b
📒 Files selected for processing (19)
Prezel/core/model/src/main/java/com/team/prezel/core/model/presentation/Audience.ktPrezel/core/model/src/main/java/com/team/prezel/core/model/presentation/Purpose.ktPrezel/core/model/src/main/java/com/team/prezel/core/model/presentation/Style.ktPrezel/feature/history/impl/build.gradle.ktsPrezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/HistoryScreen.ktPrezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/HistoryUiState.ktPrezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/HistoryViewModel.ktPrezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/component/HistoryEmptyContent.ktPrezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/component/HistoryHeadSection.ktPrezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/component/HistoryItemList.ktPrezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/component/HistoryPresentationCard.ktPrezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/contract/HistoryUiEffect.ktPrezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/contract/HistoryUiIntent.ktPrezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/contract/HistoryUiState.ktPrezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/mapper/HistoryChipLabelMapper.ktPrezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/model/HistoryUiMessage.ktPrezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/model/HistoryUiModel.ktPrezel/feature/history/impl/src/main/res/drawable/feature_history_impl_empty_no_presentation.xmlPrezel/feature/history/impl/src/main/res/values/strings.xml
💤 Files with no reviewable changes (1)
- Prezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/HistoryUiState.kt
...impl/src/main/java/com/team/prezel/feature/history/impl/component/HistoryPresentationCard.kt
Show resolved
Hide resolved
| onClickTab = { pageIndex -> | ||
| scope.launch { pagerState.animateScrollToPage(pageIndex) } | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== HistoryHeadSection =="
fd -i '^HistoryHeadSection\.kt$' | while read -r file; do
echo "--- $file ---"
sed -n '1,220p' "$file"
done
echo
echo "== PrezelTabs =="
fd -i '^PrezelTabs\.kt$' | while read -r file; do
echo "--- $file ---"
sed -n '1,180p' "$file"
doneRepository: Team-Prezel/Prezel-Android
Length of output: 5201
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check full HistoryScreen.kt for context around the lines
echo "=== HistoryScreen.kt around lines 95-97 ==="
fd -i '^HistoryScreen\.kt$' | while read -r file; do
wc -l "$file"
echo "--- Full file (relevant sections) ---"
cat -n "$file" | sed -n '70,130p'
done
echo
echo "=== Check for targetPage usage in codebase ==="
rg "targetPage" --type kt -B2 -A2Repository: Team-Prezel/Prezel-Android
Length of output: 2344
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check for targetPage usage in codebase ==="
rg "targetPage" -A2 -B2 --type-list | grep -i kotlin
echo
rg "targetPage" -A2 -B2
echo
echo "=== Check if other PagerState scroll methods are used ==="
rg "scrollToPage|animateScrollToPage" -A1 -B1 | head -40Repository: Team-Prezel/Prezel-Android
Length of output: 523
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check for targetPage usage in codebase ==="
rg "targetPage" -A2 -B2
echo
echo "=== Check if other PagerState scroll methods are used ==="
rg "scrollToPage|animateScrollToPage" -A1 -B1 | head -60Repository: Team-Prezel/Prezel-Android
Length of output: 116
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check for scrollToPage usage ==="
rg "scrollToPage" -B2 -A2
echo
echo "=== Check for animateScrollToPage usage in entire codebase ==="
rg "animateScrollToPage" -B2 -A2Repository: Team-Prezel/Prezel-Android
Length of output: 2977
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Full PrezelTabsPager.kt to understand the pattern ==="
fd -i '^PrezelTabsPager\.kt$' | while read -r file; do
echo "--- $file ---"
cat -n "$file"
doneRepository: Team-Prezel/Prezel-Android
Length of output: 5289
탭 선택 표시가 사용자 클릭 후 한 박자 늦게 바뀝니다.
PrezelTabs가 pagerState.currentPage로 선택 상태와 인디케이터를 렌더링하는데, 여기서는 animateScrollToPage()를 비동기로 호출하고 있습니다. 그 결과 탭을 누른 직후에는 선택 표시가 이전 탭에 남았다가, 애니메이션이 완료된 뒤에야 바뀌는 UI 지연이 발생합니다. 스와이프를 비활성화한 상태이므로, HomeScreen처럼 scrollToPage()로 즉시 전환하거나 PrezelTabsPager 패턴처럼 인접 탭만 애니메이션하는 방식이 더 자연스럽습니다.
개선 제안
onClickTab = { pageIndex ->
- scope.launch { pagerState.animateScrollToPage(pageIndex) }
+ scope.launch { pagerState.scrollToPage(pageIndex) }
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| onClickTab = { pageIndex -> | |
| scope.launch { pagerState.animateScrollToPage(pageIndex) } | |
| }, | |
| onClickTab = { pageIndex -> | |
| scope.launch { pagerState.scrollToPage(pageIndex) } | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@Prezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/HistoryScreen.kt`
around lines 95 - 97, The tab selection currently waits for
pagerState.animateScrollToPage to finish (called inside the onClickTab lambda),
causing the selected tab/indicator to update late; replace the async animated
call with an immediate pagerState.scrollToPage call (or implement the
PrezelTabsPager pattern: detect currentPage and only call animateScrollToPage
for adjacent pages) and remove the scope.launch wrapper so the selection updates
synchronously in PrezelTabs when onClickTab is invoked; update usages of
pagerState.animateScrollToPage in the onClickTab lambda accordingly to use
pagerState.scrollToPage or the adjacent-animation logic.
📌 작업 내용
🧩 관련 이슈
📸 스크린샷
📢 논의하고 싶은 내용
D-day 남은 발표, D-day 지난 발표카드 클릭시 넘어가는 페이지가 달라 status 값을 uiModel에 넣고 분기처리하려고 합니다.
Summary by CodeRabbit
새로운 기능