Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
e09ae2c
feat: History 피처 MVI 아키텍처 도입 및 기본 구조 설계
HamBeomJoon Apr 13, 2026
edeb48d
feat: 히스토리(History) 화면 구현 및 UI 컴포넌트 추가
HamBeomJoon Apr 13, 2026
d599415
refactor: History 피처 UI 모델 개선 및 데이터 연동 구조 구현
HamBeomJoon Apr 13, 2026
328182f
feat: 히스토리 피처의 발표 카드 UI를 고도화하고, 관련 도메인 모델(Audience, Purpose, Style) 및…
HamBeomJoon Apr 13, 2026
adff6fe
refactor: History 피처의 목록 화면 및 카드 컴포넌트 개선
HamBeomJoon Apr 13, 2026
4fc4dab
feat: 히스토리 화면의 발표 내역이 없을 때 표시되는 빈 화면(Empty Content) UI를 구현했습니다.
HamBeomJoon Apr 13, 2026
4bd9051
feat: History 피처 내 히스토리 목록 화면(HistoryScreen) 구조 개선 및 아이템 클릭 로직 추가
HamBeomJoon Apr 13, 2026
563356a
refactor: HistoryViewModel 내 더미 데이터 생성 로직 분리
HamBeomJoon Apr 13, 2026
4b6de83
feat: StatusView 공통 컴포넌트 추가 및 History 피처 적용
HamBeomJoon Apr 15, 2026
e4857ff
feat: LoadingView 및 Lottie 애니메이션 컴포넌트 추가
HamBeomJoon Apr 15, 2026
d698887
refactor: StatusView 통합 및 구조 개선
HamBeomJoon Apr 15, 2026
a058aaf
feat: History 피처 데이터 구조 개선 및 날짜 처리 로직 고도화
HamBeomJoon Apr 15, 2026
29ca9b8
refactor: History 화면 구조 개선 및 `PrezelTabsPager` 도입
HamBeomJoon Apr 15, 2026
671ac85
refactor: 히스토리 화면의 탭 구조를 유연하게 관리하기 위해 `HistoryPageUiModel`을 도입하고 UI 상…
HamBeomJoon Apr 15, 2026
42ebb5e
refactor: history 피처의 UI 모델 변환 로직 및 화면 렌더링 구조 개선
HamBeomJoon Apr 16, 2026
1635eca
feat: Presentation 모델 통합 및 필드 확장
HamBeomJoon Apr 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.team.prezel.core.model.presentation

enum class Audience {
GENERAL_AUDIENCE,
EXPERT,
TEAMMATES,
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import kotlin.time.Clock

data class Presentation(
val id: Long,
val category: Category,
val title: String,
val date: LocalDate,
val category: Category,
val purpose: Purpose,
val style: Style,
val audience: Audience,
) {
fun dDay(now: LocalDate = Clock.System.todayIn(TimeZone.currentSystemDefault())): Int = (date.toEpochDays() - now.toEpochDays()).toInt()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.team.prezel.core.model.presentation

enum class Purpose {
CONTENT_DELIVERY,
IMPROVE_UNDERSTANDING,
BUILD_EMPATHY,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.team.prezel.core.model.presentation

enum class Style {
PROFESSIONAL,
FRIENDLY,
CALM,
COMFORTABLE,
}
5 changes: 5 additions & 0 deletions Prezel/core/ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@ plugins {
android {
namespace = "com.team.prezel.core.ui"
}

dependencies {
implementation(projects.coreDesignsystem)
implementation(libs.lottie.compose)
}
167 changes: 167 additions & 0 deletions Prezel/core/ui/src/main/java/com/team/prezel/core/ui/StatusView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package com.team.prezel.core.ui

import androidx.annotation.RawRes
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.airbnb.lottie.compose.LottieAnimation
import com.airbnb.lottie.compose.LottieCompositionSpec
import com.airbnb.lottie.compose.LottieConstants
import com.airbnb.lottie.compose.animateLottieCompositionAsState
import com.airbnb.lottie.compose.rememberLottieComposition
import com.team.prezel.core.designsystem.component.actions.button.PrezelButton
import com.team.prezel.core.designsystem.component.actions.button.config.ButtonHierarchy
import com.team.prezel.core.designsystem.component.actions.button.config.ButtonSize
import com.team.prezel.core.designsystem.component.actions.button.config.ButtonType
import com.team.prezel.core.designsystem.icon.PrezelIcons
import com.team.prezel.core.designsystem.preview.BasicPreview
import com.team.prezel.core.designsystem.theme.PrezelTheme

@Composable
fun StatusView(
title: String,
modifier: Modifier = Modifier,
description: String? = null,
visual: @Composable (() -> Unit)? = null,
action: @Composable (() -> Unit)? = null,
) {
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
visual?.invoke()

Column(
modifier = Modifier.padding(top = PrezelTheme.spacing.V16),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = title,
style = PrezelTheme.typography.body3Medium,
color = PrezelTheme.colors.textMedium,
textAlign = TextAlign.Center,
)

if (!description.isNullOrBlank()) {
Text(
text = description,
modifier = Modifier.padding(top = PrezelTheme.spacing.V4),
style = PrezelTheme.typography.caption2Regular,
color = PrezelTheme.colors.textSmall,
textAlign = TextAlign.Center,
)
}
}

if (action != null) {
Box(
modifier = Modifier.padding(top = PrezelTheme.spacing.V16),
contentAlignment = Alignment.Center,
) {
action()
}
}
}
}

@Composable
fun StatusLottie(
@RawRes lottieJsonResId: Int,
modifier: Modifier = Modifier.size(80.dp),
) {
val composition by rememberLottieComposition(
LottieCompositionSpec.RawRes(lottieJsonResId),
)
val progress by animateLottieCompositionAsState(
composition = composition,
iterations = LottieConstants.IterateForever,
)

LottieAnimation(
composition = composition,
progress = { progress },
modifier = modifier,
)
}

@BasicPreview
@Composable
private fun StatusViewEmptyPreview() {
PrezelTheme {
StatusView(
title = "아직 준비중인 발표가 없어요",
visual = {
Box(
modifier = Modifier
.size(120.dp)
.background(color = PrezelTheme.colors.bgMedium),
)
},
action = {
PrezelButton(
text = "발표 추가하기",
iconResId = PrezelIcons.Blank,
type = ButtonType.OUTLINED,
size = ButtonSize.SMALL,
isRounded = true,
onClick = { },
)
},
)
}
}

@BasicPreview
@Composable
private fun StatusViewErrorPreview() {
PrezelTheme {
StatusView(
title = "문제가 발생했어요",
description = "음성이 작거나 주변 소음이 많았을 수 있어요.\n조용한 환경에서 다시 녹음해 주세요.",
visual = {
Box(
modifier = Modifier
.size(120.dp)
.background(color = PrezelTheme.colors.bgMedium),
)
},
action = {
PrezelButton(
text = "다시 시도하기",
iconResId = PrezelIcons.Blank,
type = ButtonType.FILLED,
size = ButtonSize.SMALL,
hierarchy = ButtonHierarchy.SECONDARY,
isRounded = true,
onClick = { },
)
},
)
}
}

@BasicPreview
@Composable
private fun StatusViewLoadingPreview() {
PrezelTheme {
StatusView(
title = "분석 중이에요",
description = "잠시만 기다려주세요",
visual = {
StatusLottie(lottieJsonResId = R.raw.asset_loading)
},
)
}
}
Loading