From 4d7b4f7b7e1635ee4b249b2a8c7699c4d2493423 Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Fri, 6 Feb 2026 21:03:31 +0900
Subject: [PATCH 01/24] =?UTF-8?q?[refactor]=20#78:=20=EC=95=84=EC=B9=B4?=
=?UTF-8?q?=EC=9D=B4=EB=B8=8C=20=EC=95=A8=EB=B2=94=20LazyColumn=EC=9D=98?=
=?UTF-8?q?=20verticalArrangement=20=EC=A0=9C=EA=B1=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
앨범 전체 보기 화면의 `LazyColumn`에서 `verticalArrangement` 속성을 제거하여 아이템 간의 간격을 조정했습니다.
---
.../neki/android/feature/archive/impl/album/AllAlbumScreen.kt | 1 -
1 file changed, 1 deletion(-)
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album/AllAlbumScreen.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album/AllAlbumScreen.kt
index 2648fc37a..128007df8 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album/AllAlbumScreen.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album/AllAlbumScreen.kt
@@ -90,7 +90,6 @@ internal fun AllAlbumScreen(
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(vertical = 8.dp),
- verticalArrangement = Arrangement.spacedBy(20.dp),
) {
item {
FavoriteAlbumRowComponent(
From c3120b538bae8e3f57338c16b9e9a307dcf73f62 Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Fri, 6 Feb 2026 21:04:38 +0900
Subject: [PATCH 02/24] =?UTF-8?q?[chore]=20#78:=20=EC=A6=90=EA=B2=A8?=
=?UTF-8?q?=EC=B0=BE=EB=8A=94=20=EC=82=AC=EC=A7=84=20=EC=95=A8=EB=B2=94?=
=?UTF-8?q?=EB=AA=85=20=EC=A6=90=EA=B2=A8=EC=B0=BE=EA=B8=B0=EB=A1=9C=20?=
=?UTF-8?q?=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/neki/android/core/data/remote/api/PhotoService.kt | 2 +-
.../com/neki/android/core/ui/component/AlbumRowComponent.kt | 4 ++--
.../android/feature/archive/impl/album/AllAlbumScreen.kt | 5 ++---
.../feature/archive/impl/album_detail/AlbumDetailScreen.kt | 2 +-
.../android/feature/archive/impl/main/ArchiveMainScreen.kt | 4 ++--
.../archive/impl/main/component/ArchiveMainAlbumList.kt | 2 +-
.../feature/photo_upload/impl/album/UploadAlbumScreen.kt | 4 ++--
7 files changed, 11 insertions(+), 12 deletions(-)
diff --git a/core/data/src/main/java/com/neki/android/core/data/remote/api/PhotoService.kt b/core/data/src/main/java/com/neki/android/core/data/remote/api/PhotoService.kt
index 051d14236..2ef868aec 100644
--- a/core/data/src/main/java/com/neki/android/core/data/remote/api/PhotoService.kt
+++ b/core/data/src/main/java/com/neki/android/core/data/remote/api/PhotoService.kt
@@ -54,7 +54,7 @@ class PhotoService @Inject constructor(
}.body()
}
- // 즐겨찾기 사진 조회
+ // 즐겨찾는 앨범 조회
suspend fun getFavoritePhotos(
page: Int = 0,
size: Int = 20,
diff --git a/core/ui/src/main/java/com/neki/android/core/ui/component/AlbumRowComponent.kt b/core/ui/src/main/java/com/neki/android/core/ui/component/AlbumRowComponent.kt
index 606c7b9f7..c48cc51b1 100644
--- a/core/ui/src/main/java/com/neki/android/core/ui/component/AlbumRowComponent.kt
+++ b/core/ui/src/main/java/com/neki/android/core/ui/component/AlbumRowComponent.kt
@@ -45,7 +45,7 @@ fun FavoriteAlbumRowComponent(
)
AlbumInfo(
- title = "즐겨찾는 사진",
+ title = "즐겨찾기",
photoCount = album.photoCount,
)
}
@@ -168,7 +168,7 @@ private fun FavoriteAlbumRowComponentPreview() {
FavoriteAlbumRowComponent(
album = AlbumPreview(
id = 0,
- title = "즐겨찾는 사진",
+ title = "즐겨찾기",
),
)
}
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album/AllAlbumScreen.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album/AllAlbumScreen.kt
index 128007df8..3fa5f9545 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album/AllAlbumScreen.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album/AllAlbumScreen.kt
@@ -2,7 +2,6 @@ package com.neki.android.feature.archive.impl.album
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
@@ -164,7 +163,7 @@ private fun AllAlbumScreenPreview() {
NekiTheme {
AllAlbumScreen(
uiState = AllAlbumState(
- favoriteAlbum = AlbumPreview(id = 0, title = "즐겨찾는 사진", photoCount = 3),
+ favoriteAlbum = AlbumPreview(id = 0, title = "즐겨찾기", photoCount = 3),
albums = persistentListOf(
AlbumPreview(id = 1, title = "제주도 여행 2024", photoCount = 4),
AlbumPreview(id = 2, title = "가족 생일파티", photoCount = 2),
@@ -182,7 +181,7 @@ private fun AllAlbumScreenSelectingPreview() {
NekiTheme {
AllAlbumScreen(
uiState = AllAlbumState(
- favoriteAlbum = AlbumPreview(id = 0, title = "즐겨찾는 사진", photoCount = 3),
+ favoriteAlbum = AlbumPreview(id = 0, title = "즐겨찾기", photoCount = 3),
albums = persistentListOf(
AlbumPreview(id = 1, title = "제주도 여행 2024", photoCount = 4),
AlbumPreview(id = 2, title = "가족 생일파티", photoCount = 2),
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/AlbumDetailScreen.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/AlbumDetailScreen.kt
index aad0acc6e..23eb27bbe 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/AlbumDetailScreen.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/AlbumDetailScreen.kt
@@ -107,7 +107,7 @@ internal fun AlbumDetailScreen(
) {
AlbumDetailTopBar(
hasNoPhoto = isEmpty,
- title = if (uiState.isFavoriteAlbum) "즐겨찾는 사진" else uiState.title,
+ title = if (uiState.isFavoriteAlbum) "즐겨찾기" else uiState.title,
selectMode = uiState.selectMode,
onClickBack = { onIntent(AlbumDetailIntent.ClickBackIcon) },
onClickSelect = { onIntent(AlbumDetailIntent.ClickSelectButton) },
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainScreen.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainScreen.kt
index 8f79a6d99..3c531d100 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainScreen.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainScreen.kt
@@ -275,7 +275,7 @@ private fun ArchiveMainScreenPreview() {
val favoriteAlbum = AlbumPreview(
id = 0,
- title = "즐겨찾는 사진",
+ title = "즐겨찾기",
thumbnailUrl = "https://picsum.photos/seed/fav1/200/300",
photoCount = 5,
)
@@ -296,7 +296,7 @@ private fun ArchiveMainScreenPreview() {
private fun ArchiveMainScreenEmptyPreview() {
val favoriteAlbum = AlbumPreview(
id = 0,
- title = "즐겨찾는 사진",
+ title = "즐겨찾기",
)
NekiTheme {
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainAlbumList.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainAlbumList.kt
index c897679e9..043f1600e 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainAlbumList.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainAlbumList.kt
@@ -247,7 +247,7 @@ private fun FavoriteAlbumFolderPreview() {
NekiTheme {
AlbumFolder(
isFavorite = true,
- title = "즐겨찾는 사진",
+ title = "즐겨찾기",
photoCount = 12,
)
}
diff --git a/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumScreen.kt b/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumScreen.kt
index 0c46218ce..d55004bd5 100644
--- a/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumScreen.kt
+++ b/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumScreen.kt
@@ -103,7 +103,7 @@ private fun UploadAlbumScreenPreview() {
NekiTheme {
UploadAlbumScreen(
uiState = UploadAlbumState(
- favoriteAlbum = AlbumPreview(id = 0, title = "즐겨찾는 사진", photoCount = 3),
+ favoriteAlbum = AlbumPreview(id = 0, title = "즐겨찾기", photoCount = 3),
albums = persistentListOf(
AlbumPreview(id = 1, title = "제주도 여행 2024", photoCount = 4),
AlbumPreview(id = 2, title = "가족 생일파티", photoCount = 2),
@@ -121,7 +121,7 @@ private fun UploadAlbumScreenSelectingPreview() {
NekiTheme {
UploadAlbumScreen(
uiState = UploadAlbumState(
- favoriteAlbum = AlbumPreview(id = 0, title = "즐겨찾는 사진", photoCount = 3),
+ favoriteAlbum = AlbumPreview(id = 0, title = "즐겨찾기", photoCount = 3),
albums = persistentListOf(
AlbumPreview(id = 1, title = "제주도 여행 2024", photoCount = 4),
AlbumPreview(id = 2, title = "가족 생일파티", photoCount = 2),
From 938970bd9a0d4b5199a31f7cdb0acfb50dc3d405 Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Fri, 6 Feb 2026 21:17:34 +0900
Subject: [PATCH 03/24] =?UTF-8?q?[feat]=20#78:=20=EB=A1=9C=EB=94=A9=20?=
=?UTF-8?q?=EC=95=A0=EB=8B=88=EB=A9=94=EC=9D=B4=EC=85=98=20Lottie=20?=
=?UTF-8?q?=EC=A2=85=EC=86=8D=EC=84=B1=20=EB=B0=8F=20=EB=A6=AC=EC=86=8C?=
=?UTF-8?q?=EC=8A=A4=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Lottie 애니메이션을 표시하기 위해 `lottie-compose` 라이브러리 종속성을 추가하고, `loading_animation.json` 파일을 리소스로 추가했습니다.
---
core/ui/build.gradle.kts | 2 ++
core/ui/src/main/res/raw/loading_animation.json | 1 +
gradle/libs.versions.toml | 3 +++
3 files changed, 6 insertions(+)
create mode 100644 core/ui/src/main/res/raw/loading_animation.json
diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts
index d4a92c5a3..5be11ac34 100644
--- a/core/ui/build.gradle.kts
+++ b/core/ui/build.gradle.kts
@@ -18,6 +18,8 @@ dependencies {
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.kotlinx.collections.immutable)
+ implementation(libs.lottie.compose)
+
api(libs.coil.compose)
api(libs.coil.network.okhttp)
}
diff --git a/core/ui/src/main/res/raw/loading_animation.json b/core/ui/src/main/res/raw/loading_animation.json
new file mode 100644
index 000000000..cc841a1d7
--- /dev/null
+++ b/core/ui/src/main/res/raw/loading_animation.json
@@ -0,0 +1 @@
+{"nm":"Main Scene","ddd":0,"h":1080,"w":1920,"meta":{"g":"@lottiefiles/creator 1.74.0"},"layers":[{"ty":4,"nm":"Shape Layer 4","sr":1,"st":0,"op":360,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-284,92,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[50,50,100],"t":25},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[75,75,100],"t":39},{"s":[50,50,100],"t":55}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[1142,540],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[1142,540,0],"t":25,"ti":[0,0,0],"to":[0,-6.667,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[1142,500,0],"t":39,"ti":[0,-6.667,0],"to":[0,0,0]},{"s":[1142,540,0],"t":55}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[25],"t":25},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":39},{"s":[25],"t":55}],"ix":11}},"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[120,120],"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,0.3373,0.2784],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-284,92],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1},{"ty":4,"nm":"Shape Layer 3","sr":1,"st":0,"op":360,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-284,92,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[50,50,100],"t":17},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[75,75,100],"t":31},{"s":[50,50,100],"t":47}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[1022,540],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[1022,540,0],"t":17,"ti":[0,0,0],"to":[0,-6.667,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[1022,500,0],"t":31,"ti":[0,-6.667,0],"to":[0,0,0]},{"s":[1022,540,0],"t":47}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[25],"t":17},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":31},{"s":[25],"t":47}],"ix":11}},"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[120,120],"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,0.3373,0.2784],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-284,92],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":2},{"ty":4,"nm":"Shape Layer 2","sr":1,"st":0,"op":360,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-284,92,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[50,50,100],"t":9},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[75,75,100],"t":23},{"s":[50,50,100],"t":39}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[902,540],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[902,540,0],"t":9,"ti":[0,0,0],"to":[0,-6.667,0]},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[902,500,0],"t":23,"ti":[0,0,0],"to":[0,0,0]},{"s":[902,540,0],"t":39}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[25],"t":9},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":23},{"s":[25],"t":39}],"ix":11}},"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[120,120],"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,0.3373,0.2784],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-284,92],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":3},{"ty":4,"nm":"Shape Layer 1","sr":1,"st":0,"op":360,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-284,92,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[50,50,100],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[75,75,100],"t":14},{"s":[50,50,100],"t":30}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[782,540],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[782,500,0],"t":14,"ti":[0,-6.667,0],"to":[0,0,0]},{"s":[782,540,0],"t":30}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[25],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":14},{"s":[25],"t":30}],"ix":11}},"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[120,120],"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,0.3373,0.2784],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-284,92],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":4}],"v":"5.7.0","fr":60,"op":81,"ip":0,"assets":[]}
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index ce6124c21..1ef1cf618 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -37,6 +37,7 @@ barcodeScanning = "17.3.0"
kakao = "2.23.1"
coil = "3.3.0"
haze = "1.7.1"
+lottie = "6.7.1"
[libraries]
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraX" }
@@ -105,6 +106,8 @@ coil-network-okhttp = { group = "io.coil-kt.coil3", name = "coil-network-okhttp"
haze = { group = "dev.chrisbanes.haze", name = "haze", version.ref = "haze" }
haze-materials = { group = "dev.chrisbanes.haze", name = "haze-materials", version.ref = "haze" }
+lottie-compose = { group = "com.airbnb.android", name = "lottie-compose", version.ref = "lottie" }
+
androidx-paging-runtime = { group = "androidx.paging", name = "paging-runtime", version.ref = "paging" }
# Dependencies of the included build-logic
From 22480193ec83a2b38555551949e28f4f1f2fc520 Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Fri, 6 Feb 2026 21:17:46 +0900
Subject: [PATCH 04/24] =?UTF-8?q?[feat]=20#78:=20=EB=A1=9C=EB=94=A9=20?=
=?UTF-8?q?=EC=9D=B8=EB=94=94=EC=BC=80=EC=9D=B4=ED=84=B0=20Lottie=20?=
=?UTF-8?q?=EC=95=A0=EB=8B=88=EB=A9=94=EC=9D=B4=EC=85=98=EC=9C=BC=EB=A1=9C?=
=?UTF-8?q?=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
기존 `CircularProgressIndicator`를 사용하던 로딩 인디케이터를 Lottie 애니메이션으로 교체했습니다.
---
.../core/ui/component/LoadingIndicator.kt | 49 +++++++++++++------
1 file changed, 33 insertions(+), 16 deletions(-)
diff --git a/core/ui/src/main/java/com/neki/android/core/ui/component/LoadingIndicator.kt b/core/ui/src/main/java/com/neki/android/core/ui/component/LoadingIndicator.kt
index c6f16c7db..89e89829b 100644
--- a/core/ui/src/main/java/com/neki/android/core/ui/component/LoadingIndicator.kt
+++ b/core/ui/src/main/java/com/neki/android/core/ui/component/LoadingIndicator.kt
@@ -1,20 +1,26 @@
package com.neki.android.core.ui.component
-import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
+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.neki.android.core.designsystem.ComponentPreview
import com.neki.android.core.designsystem.ui.theme.NekiTheme
+import com.neki.android.core.ui.R
@Composable
fun LoadingDialog(
modifier: Modifier = Modifier,
- circleColor: Color = NekiTheme.colorScheme.primary300,
- backgroundColor: Color = NekiTheme.colorScheme.primary100,
+ size: Dp = 200.dp,
properties: DialogProperties = DialogProperties(),
onDismissRequest: () -> Unit = {},
) {
@@ -22,11 +28,8 @@ fun LoadingDialog(
onDismissRequest = onDismissRequest,
properties = properties,
) {
- CircularProgressIndicator(
- modifier = modifier,
- strokeWidth = 6.dp,
- color = circleColor,
- trackColor = backgroundColor,
+ LoadingIndicator(
+ modifier = modifier.size(size),
)
}
}
@@ -34,14 +37,20 @@ fun LoadingDialog(
@Composable
fun LoadingIndicator(
modifier: Modifier = Modifier,
- circleColor: Color = NekiTheme.colorScheme.primary300,
- backgroundColor: Color = NekiTheme.colorScheme.primary100,
+ size: Dp = 80.dp,
) {
- CircularProgressIndicator(
- modifier = modifier,
- strokeWidth = 6.dp,
- color = circleColor,
- trackColor = backgroundColor,
+ val composition by rememberLottieComposition(
+ spec = LottieCompositionSpec.RawRes(R.raw.loading_animation),
+ )
+ val progress by animateLottieCompositionAsState(
+ composition = composition,
+ iterations = LottieConstants.IterateForever,
+ )
+
+ LottieAnimation(
+ composition = composition,
+ progress = { progress },
+ modifier = modifier.size(size),
)
}
@@ -54,3 +63,11 @@ private fun LoadingDialogPreview() {
)
}
}
+
+@ComponentPreview
+@Composable
+private fun LoadingIndicatorPreview() {
+ NekiTheme {
+ LoadingIndicator()
+ }
+}
From 28b14ca518a45f9757ab9ad4a3a7c3b5c3dde37b Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Fri, 6 Feb 2026 21:23:49 +0900
Subject: [PATCH 05/24] =?UTF-8?q?[feat]=20#78:=20=EB=84=A4=ED=82=A4=20?=
=?UTF-8?q?=ED=83=80=EC=9D=B4=ED=8F=AC=20=EB=A1=9C=EA=B3=A0=20=EC=95=84?=
=?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
네키 타이포 로고(Neki Typo Logo) 벡터 아이콘과 이를 사용하는 컴포저블 함수(`PrimaryNekiTypoLogo`, `GrayNekiTypoLogo`, `WhiteNekiTypoLogo`)를 추가했습니다.
---
.../core/designsystem/logo/NekiTypoLogo.kt | 79 +++++++++++++++++++
.../main/res/drawable/icon_neki_logo_typo.xml | 18 +++++
.../impl/main/component/ArchiveMainTopBar.kt | 8 +-
3 files changed, 99 insertions(+), 6 deletions(-)
create mode 100644 core/designsystem/src/main/java/com/neki/android/core/designsystem/logo/NekiTypoLogo.kt
create mode 100644 core/designsystem/src/main/res/drawable/icon_neki_logo_typo.xml
diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/logo/NekiTypoLogo.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/logo/NekiTypoLogo.kt
new file mode 100644
index 000000000..a3fe50d5c
--- /dev/null
+++ b/core/designsystem/src/main/java/com/neki/android/core/designsystem/logo/NekiTypoLogo.kt
@@ -0,0 +1,79 @@
+package com.neki.android.core.designsystem.logo
+
+import androidx.compose.material3.Icon
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.tooling.preview.Preview
+import com.neki.android.core.designsystem.ComponentPreview
+import com.neki.android.core.designsystem.R
+import com.neki.android.core.designsystem.ui.theme.NekiTheme
+
+@Composable
+private fun NekiTypoLogo(
+ color: Color,
+ modifier: Modifier = Modifier,
+) {
+ Icon(
+ imageVector = ImageVector.vectorResource(R.drawable.icon_neki_logo_typo),
+ contentDescription = null,
+ tint = color,
+ modifier = modifier,
+ )
+}
+
+@Composable
+fun PrimaryNekiTypoLogo(
+ modifier: Modifier = Modifier,
+) {
+ NekiTypoLogo(
+ color = NekiTheme.colorScheme.primary400,
+ modifier = modifier,
+ )
+}
+
+@Composable
+fun GrayNekiTypoLogo(
+ modifier: Modifier = Modifier,
+) {
+ NekiTypoLogo(
+ color = NekiTheme.colorScheme.gray900,
+ modifier = modifier,
+ )
+}
+
+@Composable
+fun WhiteNekiTypoLogo(
+ modifier: Modifier = Modifier,
+) {
+ NekiTypoLogo(
+ color = NekiTheme.colorScheme.white,
+ modifier = modifier,
+ )
+}
+
+@ComponentPreview
+@Composable
+private fun PrimaryNekiTypoLogoPreview() {
+ NekiTheme {
+ PrimaryNekiTypoLogo()
+ }
+}
+
+@ComponentPreview
+@Composable
+private fun GrayNekiTypoLogoPreview() {
+ NekiTheme {
+ GrayNekiTypoLogo()
+ }
+}
+
+@Preview
+@Composable
+private fun WhiteNekiTypoLogoPreview() {
+ NekiTheme {
+ WhiteNekiTypoLogo()
+ }
+}
diff --git a/core/designsystem/src/main/res/drawable/icon_neki_logo_typo.xml b/core/designsystem/src/main/res/drawable/icon_neki_logo_typo.xml
new file mode 100644
index 000000000..2a701695d
--- /dev/null
+++ b/core/designsystem/src/main/res/drawable/icon_neki_logo_typo.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTopBar.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTopBar.kt
index 0533bfee6..d2a2f00ac 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTopBar.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTopBar.kt
@@ -30,6 +30,7 @@ import androidx.compose.ui.window.PopupProperties
import com.neki.android.core.designsystem.ComponentPreview
import com.neki.android.core.designsystem.R
import com.neki.android.core.designsystem.button.NekiIconButton
+import com.neki.android.core.designsystem.logo.PrimaryNekiTypoLogo
import com.neki.android.core.designsystem.modifier.dropdownShadow
import com.neki.android.core.designsystem.topbar.NekiLeftTitleTopBar
import com.neki.android.core.designsystem.ui.theme.NekiTheme
@@ -50,12 +51,7 @@ internal fun ArchiveMainTopBar(
NekiLeftTitleTopBar(
modifier = modifier,
title = {
- Box(
- modifier = Modifier
- .height(28.dp)
- .width(56.dp)
- .background(color = Color(0xFFB7B9C3)),
- )
+ PrimaryNekiTypoLogo()
},
actions = {
Row(
From 1ec67f484731ca134044fbed7e5b514521e0d760 Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Fri, 6 Feb 2026 22:09:53 +0900
Subject: [PATCH 06/24] =?UTF-8?q?[refactor]=20#78:=20=EC=95=A8=EB=B2=94=20?=
=?UTF-8?q?=EC=BB=A4=EB=B2=84=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EA=B0=9C?=
=?UTF-8?q?=EC=84=A0=20=EB=B0=8F=20=EB=B8=94=EB=9F=AC=20=ED=9A=A8=EA=B3=BC?=
=?UTF-8?q?(Haze)=20=EC=A0=81=EC=9A=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
아카이브 화면의 앨범 커버 디자인을 개선하고, `dev.chrisbanes.haze` 라이브러리를 사용해 블러 효과를 적용했습니다.
- 앨범 커버를 `drawBehind` 대신 `Shape`으로 구현하여 재사용성을 높이고, `clip`을 사용하도록 변경
- `backgroundHazeBlur` Modifier 확장 함수를 추가하여 블러 효과를 공통으로 처리
- `AlbumFolderLayout`에 `Haze`를 적용하고, `RandomPoseTutorialOverlay`에도 이를 재사용하여 적용
- 앨범 커버 내 아이콘, 텍스트 스타일 및 레이아웃 등 전반적인 UI 디테일 수정
- `PhotoTitleRow` 파일명을 `ArchiveMainTitleRow`로 변경하여 명확성 개선
---
.../core/designsystem/modifier/Background.kt | 19 +-
.../main/component/ArchiveMainAlbumList.kt | 179 +++++++++++-------
...hotoTitleRow.kt => ArchiveMainTitleRow.kt} | 2 +-
.../component/RandomPoseTutorialOverlay.kt | 12 +-
4 files changed, 130 insertions(+), 82 deletions(-)
rename feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/{PhotoTitleRow.kt => ArchiveMainTitleRow.kt} (97%)
diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
index dfb5ffbea..df95109ae 100644
--- a/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
+++ b/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
@@ -2,13 +2,12 @@ package com.neki.android.core.designsystem.modifier
import androidx.compose.foundation.background
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
-import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import dev.chrisbanes.haze.HazeState
@@ -44,23 +43,25 @@ fun Modifier.photoBackground(
* @param defaultBackgroundColor 블러 비활성화 시 적용될 기본 배경 색상
* @param blurRadius 블러 효과의 반경
*/
-@Composable
fun Modifier.backgroundHazeBlur(
hazeState: HazeState,
+ alpha: Float,
+ color: Color,
+ blurRadius: Dp,
enabled: Boolean = true,
- color: Color = Color(0xFF202227).copy(alpha = 0.9f),
defaultBackgroundColor: Color = color,
- blurRadius: Dp = 12.dp,
+ shape: Shape = RectangleShape,
): Modifier =
if (enabled) {
this.hazeEffect(
state = hazeState,
style = HazeStyle(
backgroundColor = color,
- tint = HazeTint(
- color.copy(alpha = if (color.luminance() >= 0.5) 0.6f else 0.65f),
- ),
+ tint = HazeTint(color.copy(alpha = alpha)),
blurRadius = blurRadius,
),
)
- } else this.background(color = defaultBackgroundColor)
+ } else this.background(
+ color = defaultBackgroundColor,
+ shape = shape,
+ )
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainAlbumList.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainAlbumList.kt
index 043f1600e..f812bff67 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainAlbumList.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainAlbumList.kt
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@@ -22,31 +23,100 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
-import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Path
-import androidx.compose.ui.graphics.drawscope.Fill
-import androidx.compose.ui.graphics.drawscope.scale
+import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import com.neki.android.core.designsystem.ComponentPreview
import com.neki.android.core.designsystem.R
+import com.neki.android.core.designsystem.modifier.backgroundHazeBlur
import com.neki.android.core.designsystem.modifier.cardShadow
import com.neki.android.core.designsystem.modifier.noRippleClickable
import com.neki.android.core.designsystem.ui.theme.NekiTheme
import com.neki.android.core.model.AlbumPreview
+import dev.chrisbanes.haze.HazeState
+import dev.chrisbanes.haze.hazeSource
+import dev.chrisbanes.haze.rememberHazeState
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
+import kotlinx.collections.immutable.toImmutableList
private const val VIEWPORT_W = 124f
private const val VIEWPORT_H = 65f
+private val AlbumFolderShape = object : Shape {
+ override fun createOutline(
+ size: Size,
+ layoutDirection: LayoutDirection,
+ density: Density,
+ ): Outline {
+ val scaleX = size.width / VIEWPORT_W
+ val scaleY = size.height / VIEWPORT_H
+
+ val path = Path().apply {
+ moveTo(124f * scaleX, 57f * scaleY)
+ cubicTo(124f * scaleX, 61.42f * scaleY, 120.42f * scaleX, 65f * scaleY, 116f * scaleX, 65f * scaleY)
+ lineTo(8f * scaleX, 65f * scaleY)
+ cubicTo(
+ 3.58f * scaleX,
+ 65f * scaleY,
+ 0f,
+ 61.42f * scaleY,
+ 0f,
+ 57f * scaleY,
+ )
+ lineTo(0f, 8f * scaleY)
+ cubicTo(
+ 0f,
+ 3.58f * scaleY,
+ 3.58f * scaleX,
+ 0f,
+ 8f * scaleX,
+ 0f,
+ )
+ lineTo(58.54f * scaleX, 0f)
+ cubicTo(
+ 61.07f * scaleX,
+ 0f,
+ 63.45f * scaleX,
+ 1.2f * scaleY,
+ 64.96f * scaleX,
+ 3.23f * scaleY,
+ )
+ lineTo(69.2f * scaleX, 8.93f * scaleY)
+ cubicTo(
+ 70.7f * scaleX,
+ 10.95f * scaleY,
+ 73.06f * scaleX,
+ 12.15f * scaleY,
+ 75.58f * scaleX,
+ 12.16f * scaleY,
+ )
+ lineTo(116.04f * scaleX, 12.35f * scaleY)
+ cubicTo(
+ 120.44f * scaleX,
+ 12.36f * scaleY,
+ 124f * scaleX,
+ 15.94f * scaleY,
+ 124f * scaleX,
+ 20.34f * scaleY,
+ )
+ lineTo(124f * scaleX, 57f * scaleY)
+ close()
+ }
+ return Outline.Generic(path)
+ }
+}
+
@Composable
internal fun ArchiveMainAlbumList(
favoriteAlbum: AlbumPreview,
@@ -87,11 +157,13 @@ internal fun ArchiveMainAlbumList(
private fun ArchiveAlbumItem(
title: String,
photoCount: Int,
- thumbnailImage: String? = null,
modifier: Modifier = Modifier,
+ thumbnailImage: String? = null,
isFavorite: Boolean = false,
onClick: () -> Unit = {},
) {
+ val hazeState = rememberHazeState()
+
Box(
modifier = modifier
.height(166.dp)
@@ -102,6 +174,7 @@ private fun ArchiveAlbumItem(
modifier = Modifier
.cardShadow(shape = RoundedCornerShape(8.dp))
.matchParentSize()
+ .hazeSource(hazeState)
.then(
if (!thumbnailImage.isNullOrBlank()) Modifier
else Modifier.background(color = NekiTheme.colorScheme.gray50),
@@ -112,6 +185,7 @@ private fun ArchiveAlbumItem(
)
AlbumFolder(
modifier = Modifier.align(Alignment.BottomCenter),
+ hazeState = hazeState,
title = title,
photoCount = photoCount,
isFavorite = isFavorite,
@@ -124,38 +198,40 @@ private fun AlbumFolder(
title: String,
photoCount: Int,
modifier: Modifier = Modifier,
+ hazeState: HazeState = rememberHazeState(),
isFavorite: Boolean = false,
) {
AlbumFolderLayout(
modifier = modifier.width(124.dp),
- color = if (isFavorite) NekiTheme.colorScheme.favoriteAlbumCover.copy(alpha = 0.9f)
- else NekiTheme.colorScheme.defaultAlbumCover.copy(alpha = 0.92f),
+ hazeState = hazeState,
+ color = if (isFavorite) NekiTheme.colorScheme.favoriteAlbumCover
+ else NekiTheme.colorScheme.defaultAlbumCover,
) {
Row(
modifier = Modifier
+ .fillMaxWidth()
.padding(
- top = 15.dp,
- bottom = 10.dp,
+ top = 10.dp,
start = 10.dp,
+ bottom = 8.dp,
end = 8.dp,
),
- horizontalArrangement = Arrangement.spacedBy(10.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
) {
Column(
- modifier = Modifier.width(80.dp),
+ modifier = Modifier.widthIn(max = 86.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(
text = "${photoCount}장",
- style = NekiTheme.typography.caption12Medium,
+ style = NekiTheme.typography.body14Medium,
color = NekiTheme.colorScheme.white.copy(alpha = 0.7f),
)
Text(
- modifier = Modifier.widthIn(max = 80.dp),
text = title,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
- style = NekiTheme.typography.body14SemiBold,
+ style = NekiTheme.typography.body16SemiBold,
color = NekiTheme.colorScheme.white,
)
}
@@ -163,7 +239,7 @@ private fun AlbumFolder(
if (isFavorite) {
Box(
modifier = Modifier
- .padding(bottom = 3.dp)
+ .padding(bottom = 2.dp)
.align(Alignment.Bottom)
.clip(CircleShape)
.background(
@@ -173,7 +249,7 @@ private fun AlbumFolder(
.padding(4.dp),
) {
Icon(
- modifier = Modifier.size(8.dp),
+ modifier = Modifier.size(12.dp),
imageVector = ImageVector.vectorResource(R.drawable.icon_heart),
contentDescription = null,
tint = NekiTheme.colorScheme.white,
@@ -187,53 +263,30 @@ private fun AlbumFolder(
@Composable
private fun AlbumFolderLayout(
modifier: Modifier = Modifier,
+ hazeState: HazeState = rememberHazeState(),
color: Color = Color(0xFFFF5647),
content: @Composable BoxScope.() -> Unit = {},
) {
Box(
modifier = modifier
- .drawBehind {
- val scaleX = size.width / VIEWPORT_W
- val scaleY = size.height / VIEWPORT_H
-
- scale(scaleX, scaleY, pivot = Offset.Zero) {
- val path = Path().apply {
- // 오른쪽 하단에서 시작 (코너 8만큼 위)
- moveTo(124f, 57f)
- // 오른쪽 하단 코너
- cubicTo(124f, 61.42f, 120.42f, 65f, 116f, 65f)
- // 하단 직선
- lineTo(8f, 65f)
- // 왼쪽 하단 코너
- cubicTo(3.58f, 65f, 0f, 61.42f, 0f, 57f)
- // 왼쪽 직선
- lineTo(0f, 8f)
- // 왼쪽 상단 코너
- cubicTo(0f, 3.58f, 3.58f, 0f, 8f, 0f)
- // 상단 탭 모양
- lineTo(58.54f, 0f)
- cubicTo(61.07f, 0f, 63.45f, 1.2f, 64.96f, 3.23f)
- lineTo(69.2f, 8.93f)
- cubicTo(70.7f, 10.95f, 73.06f, 12.15f, 75.58f, 12.16f)
- lineTo(116.04f, 12.35f)
- cubicTo(120.44f, 12.36f, 124f, 15.94f, 124f, 20.34f)
- // 오른쪽 직선
- lineTo(124f, 57f)
- close()
- }
- drawPath(path, color, style = Fill)
- }
- },
+ .clip(AlbumFolderShape)
+ .backgroundHazeBlur(
+ hazeState = hazeState,
+ color = color,
+ blurRadius = 4.dp,
+ alpha = 0.92f,
+ ),
content = content,
)
}
@ComponentPreview
@Composable
-private fun ArchiveAlbumItemPreview() {
+private fun FavoriteAlbumItemPreview() {
NekiTheme {
Box(modifier = Modifier.padding(8.dp)) {
ArchiveAlbumItem(
+ isFavorite = true,
title = "네키 화이팅화이팅",
photoCount = 10,
)
@@ -243,34 +296,24 @@ private fun ArchiveAlbumItemPreview() {
@ComponentPreview
@Composable
-private fun FavoriteAlbumFolderPreview() {
+private fun ArchiveAlbumItemPreview() {
NekiTheme {
- AlbumFolder(
- isFavorite = true,
- title = "즐겨찾기",
- photoCount = 12,
- )
+ Box(modifier = Modifier.padding(8.dp)) {
+ ArchiveAlbumItem(
+ title = "네키 화이팅화이팅",
+ photoCount = 10,
+ )
+ }
}
}
@ComponentPreview
@Composable
-private fun DefaultAlbumFolderPreview() {
- NekiTheme {
- AlbumFolder(
- isFavorite = false,
- title = "네키 화이팅화이팅",
- photoCount = 10,
- )
- }
-}
-
-@Preview
-@Composable
private fun ArchiveMainAlbumListPreview() {
NekiTheme {
ArchiveMainAlbumList(
favoriteAlbum = AlbumPreview(),
+ albumList = List(3) { AlbumPreview(id = it.toLong()) }.toImmutableList(),
)
}
}
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/PhotoTitleRow.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTitleRow.kt
similarity index 97%
rename from feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/PhotoTitleRow.kt
rename to feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTitleRow.kt
index 38fb4d530..fcf2c6693 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/PhotoTitleRow.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTitleRow.kt
@@ -33,7 +33,7 @@ internal fun ArchiveMainTitleRow(
) {
Text(
text = title,
- style = NekiTheme.typography.title20SemiBold,
+ style = NekiTheme.typography.title20Bold,
color = NekiTheme.colorScheme.gray900,
)
NekiTextButton(
diff --git a/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/random/component/RandomPoseTutorialOverlay.kt b/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/random/component/RandomPoseTutorialOverlay.kt
index c8952e9db..4146216e7 100644
--- a/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/random/component/RandomPoseTutorialOverlay.kt
+++ b/feature/pose/impl/src/main/java/com/neki/android/feature/pose/impl/random/component/RandomPoseTutorialOverlay.kt
@@ -23,7 +23,6 @@ import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import com.neki.android.core.designsystem.ComponentPreview
import com.neki.android.core.designsystem.modifier.backgroundHazeBlur
-import com.neki.android.core.designsystem.modifier.noRippleClickable
import com.neki.android.core.designsystem.ui.theme.NekiTheme
import com.neki.android.core.ui.compose.HorizontalSpacer
import com.neki.android.core.ui.compose.VerticalSpacer
@@ -35,15 +34,20 @@ import dev.chrisbanes.haze.rememberHazeState
@Composable
internal fun RandomPoseTutorialOverlay(
onClickStart: () -> Unit,
- hazeState: HazeState = rememberHazeState(),
modifier: Modifier = Modifier,
+ hazeState: HazeState = rememberHazeState(),
) {
Box(
- modifier = modifier.noRippleClickable {}, // 터치 이벤트 소비용
+ modifier = modifier,
) {
Column(
modifier = Modifier
- .backgroundHazeBlur(hazeState)
+ .backgroundHazeBlur(
+ hazeState = hazeState,
+ color = Color(0xFF202227),
+ blurRadius = 12.dp,
+ alpha = 0.9f,
+ )
.fillMaxSize()
.padding(top = 60.dp, bottom = 34.dp),
) {
From b55efade40e12f11df7cb86fd490b67bed201cf5 Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Fri, 6 Feb 2026 22:33:17 +0900
Subject: [PATCH 07/24] =?UTF-8?q?[feat]=20#78:=20=EA=B3=B5=ED=86=B5=20`Too?=
=?UTF-8?q?lTipPopup`=20=EC=BB=B4=ED=8F=AC=EC=A0=80=EB=B8=94=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=EC=A0=81=EC=9A=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
기존 `ArchiveMainTopBar` 내부에만 있던 툴팁 로직을, 디자인시스템의 범용 `ToolTipPopup` 컴포저블로 분리하고 이를 적용했습니다.
---
.../core/designsystem/popup/ToolTipPopup.kt | 114 ++++++++++++++++++
.../impl/main/component/ArchiveMainTopBar.kt | 71 ++---------
2 files changed, 123 insertions(+), 62 deletions(-)
create mode 100644 core/designsystem/src/main/java/com/neki/android/core/designsystem/popup/ToolTipPopup.kt
diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/popup/ToolTipPopup.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/popup/ToolTipPopup.kt
new file mode 100644
index 000000000..ffb0845c4
--- /dev/null
+++ b/core/designsystem/src/main/java/com/neki/android/core/designsystem/popup/ToolTipPopup.kt
@@ -0,0 +1,114 @@
+package com.neki.android.core.designsystem.popup
+
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Popup
+import com.neki.android.core.designsystem.ComponentPreview
+import com.neki.android.core.designsystem.ui.theme.NekiTheme
+
+@Composable
+fun ToolTipPopup(
+ tooltipText: String,
+ color: Color,
+ offset: IntOffset,
+ alignment: Alignment,
+) {
+ Popup(
+ alignment = alignment,
+ offset = offset,
+ ) {
+ ToolTipContent(
+ tooltipText = tooltipText,
+ color = color,
+ )
+ }
+}
+
+@Composable
+private fun ToolTipContent(
+ tooltipText: String,
+ color: Color,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ modifier = modifier.width(IntrinsicSize.Max),
+ ) {
+ // 꼬리 (오른쪽 정렬, 오른쪽에서 16dp)
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(end = 16.dp),
+ contentAlignment = Alignment.CenterEnd,
+ ) {
+ Canvas(
+ modifier = Modifier.size(width = 10.dp, height = 8.dp),
+ ) {
+ val cornerRadius = 1.dp.toPx()
+ val path = Path().apply {
+ // 왼쪽 하단에서 시작
+ moveTo(0f, size.height)
+ // 왼쪽 하단 -> 꼭대기 (둥근 모서리)
+ lineTo(
+ size.width / 2 - cornerRadius,
+ cornerRadius,
+ )
+ quadraticTo(
+ size.width / 2,
+ 0f,
+ size.width / 2 + cornerRadius,
+ cornerRadius,
+ )
+ // 꼭대기 -> 오른쪽 하단
+ lineTo(size.width, size.height)
+ close()
+ }
+ drawPath(path, color)
+ }
+ }
+
+ // 몸통
+ Box(
+ modifier = Modifier
+ .background(
+ color = color,
+ shape = RoundedCornerShape(8.dp),
+ )
+ .padding(horizontal = 12.dp, vertical = 8.dp),
+ ) {
+ Text(
+ text = tooltipText,
+ style = NekiTheme.typography.body14Medium,
+ color = NekiTheme.colorScheme.white,
+ )
+ }
+ }
+}
+
+@ComponentPreview
+@Composable
+private fun ToolTipPopupPreview() {
+ NekiTheme {
+ Box(modifier = Modifier.padding(16.dp)) {
+ ToolTipContent(
+ tooltipText = "툴팁 메시지입니다",
+ color = NekiTheme.colorScheme.gray800,
+ )
+ }
+ }
+}
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTopBar.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTopBar.kt
index d2a2f00ac..0e513b807 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTopBar.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTopBar.kt
@@ -1,6 +1,5 @@
package com.neki.android.feature.archive.impl.main.component
-import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -9,17 +8,14 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.vectorResource
@@ -32,6 +28,7 @@ import com.neki.android.core.designsystem.R
import com.neki.android.core.designsystem.button.NekiIconButton
import com.neki.android.core.designsystem.logo.PrimaryNekiTypoLogo
import com.neki.android.core.designsystem.modifier.dropdownShadow
+import com.neki.android.core.designsystem.popup.ToolTipPopup
import com.neki.android.core.designsystem.topbar.NekiLeftTitleTopBar
import com.neki.android.core.designsystem.ui.theme.NekiTheme
import com.neki.android.feature.archive.impl.R as ArchiveR
@@ -77,7 +74,7 @@ internal fun ArchiveMainTopBar(
)
}
if (showTooltip) {
- ToolTip()
+ ArchiveToolTip()
}
}
NekiIconButton(
@@ -95,68 +92,18 @@ internal fun ArchiveMainTopBar(
}
@Composable
-private fun ToolTip() {
- val caretColor = NekiTheme.colorScheme.gray800
+private fun ArchiveToolTip() {
val density = LocalDensity.current
-
val popupOffsetX = with(density) { 1.dp.toPx().toInt() }
val popupOffsetY = with(density) { 47.dp.toPx().toInt() }
+ val offset = IntOffset(popupOffsetX, popupOffsetY)
- Popup(
+ ToolTipPopup(
+ tooltipText = "버튼을 눌러 네컷을 추가할 수 있어요",
+ color = NekiTheme.colorScheme.gray800,
+ offset = offset,
alignment = Alignment.TopEnd,
- offset = IntOffset(popupOffsetX, popupOffsetY),
- ) {
- Column(
- modifier = Modifier.width(IntrinsicSize.Max),
- ) {
- // 꼬리 (오른쪽 정렬, 오른쪽에서 16dp)
- Box(
- modifier = Modifier
- .fillMaxWidth()
- .padding(end = 16.dp),
- contentAlignment = Alignment.CenterEnd,
- ) {
- Canvas(modifier = Modifier.size(width = 10.dp, height = 8.dp)) {
- val cornerRadius = 2.dp.toPx()
- val path = Path().apply {
- // 왼쪽 하단에서 시작
- moveTo(0f, size.height)
- // 왼쪽 하단 -> 꼭대기 (둥근 모서리)
- lineTo(
- size.width / 2 - cornerRadius,
- cornerRadius,
- )
- quadraticTo(
- size.width / 2,
- 0f,
- size.width / 2 + cornerRadius,
- cornerRadius,
- )
- // 꼭대기 -> 오른쪽 하단
- lineTo(size.width, size.height)
- close()
- }
- drawPath(path, caretColor)
- }
- }
-
- // 몸통
- Box(
- modifier = Modifier
- .background(
- color = NekiTheme.colorScheme.gray800,
- shape = RoundedCornerShape(8.dp),
- )
- .padding(horizontal = 12.dp, vertical = 8.dp),
- ) {
- Text(
- text = "버튼을 눌러 네컷을 추가할 수 있어요",
- style = NekiTheme.typography.body14Medium,
- color = NekiTheme.colorScheme.white,
- )
- }
- }
- }
+ )
}
@Composable
From cb882099fe709903c25edef391f7a9036eb2f086 Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Fri, 6 Feb 2026 22:57:20 +0900
Subject: [PATCH 08/24] =?UTF-8?q?[feat]=20#78:=20=ED=8F=AC=ED=86=A0=20?=
=?UTF-8?q?=EA=B7=B8=EB=A6=AC=EB=93=9C=20=EC=95=84=EC=9D=B4=ED=85=9C=20?=
=?UTF-8?q?=EC=98=A4=EB=B2=84=EB=A0=88=EC=9D=B4=20=EC=BB=B4=ED=8F=AC?=
=?UTF-8?q?=EC=A0=80=EB=B8=94=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=A0=81?=
=?UTF-8?q?=EC=9A=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
기존 `PhotoComponent`에 종속되어 있던 그라데이션 및 선택 효과 로직을 `PhotoGridItemOverlay`와 `SelectedPhotoGridItemOverlay` 컴포저블로 분리했습니다. 이를 통해 사진 그리드 아이템의 UI를 보다 독립적으로 관리하고 재사용성을 높였습니다.
- `PhotoGridItemOverlay`, `SelectedPhotoGridItemOverlay` 컴포저블 추가
- `photoBackground` Modifier를 `photoGridBackground`로 변경하고, 그라데이션 효과를 상단에서 시작하도록 수정
- `PhotoComponent`에서 불필요한 `additionalContent` 파라미터 제거
- 아카이브 화면의 `SelectablePhotoItem`과 `ArchiveMainPhotoItem`에 새로운 오버레이 컴포저블 적용
---
.../core/designsystem/modifier/Background.kt | 17 ++---
.../android/core/ui/component/ItemOverlay.kt | 48 ++++++++++++
.../core/ui/component/PhotoComponent.kt | 4 -
.../impl/component/SelcetablePhotoItem.kt | 75 +++++++++++--------
.../main/component/ArchiveMainPhotoItem.kt | 20 +++--
5 files changed, 114 insertions(+), 50 deletions(-)
create mode 100644 core/ui/src/main/java/com/neki/android/core/ui/component/ItemOverlay.kt
diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
index df95109ae..5f8bee341 100644
--- a/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
+++ b/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
@@ -3,7 +3,6 @@ package com.neki.android.core.designsystem.modifier
import androidx.compose.foundation.background
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RectangleShape
@@ -16,20 +15,16 @@ import dev.chrisbanes.haze.HazeTint
import dev.chrisbanes.haze.hazeEffect
/**
- * 사진 컴포넌트에 적용되는 그라데이션 배경
- * 좌하단에서 우상단으로 갈수록 어두워지는 효과
+ * 사진, 포즈 컴포넌트에 적용되는 그라데이션 배경
*/
-fun Modifier.photoBackground(
- shape: Shape = RoundedCornerShape(12.dp),
+fun Modifier.photoGridBackground(
+ shape: Shape = RoundedCornerShape(8.dp),
): Modifier = this.background(
- brush = Brush.linearGradient(
+ brush = Brush.verticalGradient(
colorStops = arrayOf(
- 0f to Color.Black.copy(alpha = 0f),
- 0.7f to Color.Black.copy(alpha = 0.09f),
- 1f to Color.Black.copy(alpha = 0.3f),
+ 0f to Color.Black.copy(alpha = 0.2f),
+ 134f / 242f to Color.Black.copy(alpha = 0f),
),
- start = Offset(0f, Float.POSITIVE_INFINITY),
- end = Offset(Float.POSITIVE_INFINITY, 0f),
),
shape = shape,
)
diff --git a/core/ui/src/main/java/com/neki/android/core/ui/component/ItemOverlay.kt b/core/ui/src/main/java/com/neki/android/core/ui/component/ItemOverlay.kt
new file mode 100644
index 000000000..e4ef4e566
--- /dev/null
+++ b/core/ui/src/main/java/com/neki/android/core/ui/component/ItemOverlay.kt
@@ -0,0 +1,48 @@
+package com.neki.android.core.ui.component
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.unit.dp
+import com.neki.android.core.designsystem.ComponentPreview
+import com.neki.android.core.designsystem.modifier.photoGridBackground
+import com.neki.android.core.designsystem.ui.theme.NekiTheme
+
+@Composable
+fun PhotoGridItemOverlay(
+ modifier: Modifier = Modifier,
+ shape: Shape = RoundedCornerShape(8.dp),
+) {
+ Box(
+ modifier = modifier.photoGridBackground(shape = shape),
+ )
+}
+
+@Composable
+fun SelectedPhotoGridItemOverlay(
+ modifier: Modifier = Modifier,
+ shape: Shape = RoundedCornerShape(8.dp),
+) {
+ Box(
+ modifier = modifier.background(
+ color = Color.Black.copy(alpha = 0.2f),
+ shape = shape,
+ ),
+ )
+}
+
+@ComponentPreview
+@Composable
+private fun PhotoGridItemOverlayPreview() {
+ NekiTheme {
+ PhotoGridItemOverlay(
+ modifier = Modifier
+ .size(80.dp),
+ )
+ }
+}
diff --git a/core/ui/src/main/java/com/neki/android/core/ui/component/PhotoComponent.kt b/core/ui/src/main/java/com/neki/android/core/ui/component/PhotoComponent.kt
index 2cb84b113..60cd8035d 100644
--- a/core/ui/src/main/java/com/neki/android/core/ui/component/PhotoComponent.kt
+++ b/core/ui/src/main/java/com/neki/android/core/ui/component/PhotoComponent.kt
@@ -1,7 +1,6 @@
package com.neki.android.core.ui.component
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
@@ -22,7 +21,6 @@ fun PhotoComponent(
photo: Photo,
modifier: Modifier = Modifier,
onClickItem: (Photo) -> Unit = {},
- additionalContent: @Composable BoxScope.() -> Unit = {},
) {
Box(
modifier = modifier
@@ -37,8 +35,6 @@ fun PhotoComponent(
contentDescription = null,
contentScale = ContentScale.FillWidth,
)
-
- additionalContent()
}
}
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelcetablePhotoItem.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelcetablePhotoItem.kt
index efc06c283..75d1332a7 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelcetablePhotoItem.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelcetablePhotoItem.kt
@@ -2,6 +2,7 @@ package com.neki.android.feature.archive.impl.component
import androidx.compose.foundation.background
import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -17,48 +18,52 @@ import androidx.compose.ui.unit.dp
import com.neki.android.core.designsystem.ComponentPreview
import com.neki.android.core.designsystem.R
import com.neki.android.core.designsystem.modifier.noRippleClickable
-import com.neki.android.core.designsystem.modifier.photoBackground
import com.neki.android.core.designsystem.ui.theme.NekiTheme
import com.neki.android.core.model.Photo
import com.neki.android.core.ui.component.PhotoComponent
+import com.neki.android.core.ui.component.PhotoGridItemOverlay
+import com.neki.android.core.ui.component.SelectedPhotoGridItemOverlay
import com.neki.android.core.ui.component.SelectionCheckbox
@Composable
internal fun SelectablePhotoItem(
photo: Photo,
isSelected: Boolean,
- isSelectMode: Boolean = false,
modifier: Modifier = Modifier,
+ isSelectMode: Boolean = false,
onClickItem: (Photo) -> Unit = {},
onClickSelect: (Photo) -> Unit = {},
- onClickFavorite: (Photo) -> Unit = {},
) {
- PhotoComponent(
- photo = photo,
- modifier = modifier.then(
- if (isSelected) Modifier
- .border(
- width = 2.dp,
- color = NekiTheme.colorScheme.primary400,
- shape = RoundedCornerShape(12.dp),
- )
- .background(
- color = Color.Black.copy(alpha = 0.2f),
- shape = RoundedCornerShape(12.dp),
- ) else Modifier
- .clip(RoundedCornerShape(12.dp))
- .photoBackground(),
- ),
- onClickItem = onClickItem,
+ Box(
+ modifier = modifier,
) {
- if (isSelectMode) {
- SelectionCheckbox(
- isSelected = isSelected,
- modifier = Modifier
- .align(Alignment.TopStart)
- .padding(top = 12.dp, start = 12.dp)
- .noRippleClickable { onClickSelect(photo) },
- unselectedColor = NekiTheme.colorScheme.white.copy(alpha = 0.2f),
+ PhotoComponent(
+ photo = photo,
+ modifier = Modifier.then(
+ if (isSelected) Modifier
+ .border(
+ width = 2.dp,
+ color = NekiTheme.colorScheme.primary400,
+ shape = RoundedCornerShape(12.dp),
+ )
+ .background(
+ color = Color.Black.copy(alpha = 0.2f),
+ shape = RoundedCornerShape(12.dp),
+ ) else Modifier
+ .clip(RoundedCornerShape(12.dp)),
+ ),
+ onClickItem = onClickItem,
+ )
+
+ if (isSelected) {
+ SelectedPhotoGridItemOverlay(
+ modifier = Modifier.matchParentSize(),
+ shape = RoundedCornerShape(8.dp),
+ )
+ } else {
+ PhotoGridItemOverlay(
+ modifier = Modifier.matchParentSize(),
+ shape = RoundedCornerShape(8.dp),
)
}
@@ -67,13 +72,23 @@ internal fun SelectablePhotoItem(
modifier = Modifier
.align(Alignment.TopEnd)
.padding(top = 10.dp, end = 10.dp)
- .size(20.dp)
- .noRippleClickable { onClickFavorite(photo) },
+ .size(20.dp),
imageVector = ImageVector.vectorResource(R.drawable.icon_heart),
contentDescription = null,
tint = NekiTheme.colorScheme.white,
)
}
+
+ if (isSelectMode) {
+ SelectionCheckbox(
+ isSelected = isSelected,
+ modifier = Modifier
+ .align(Alignment.TopStart)
+ .padding(top = 12.dp, start = 12.dp)
+ .noRippleClickable { onClickSelect(photo) },
+ unselectedColor = NekiTheme.colorScheme.white.copy(alpha = 0.2f),
+ )
+ }
}
}
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainPhotoItem.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainPhotoItem.kt
index e2fe110d8..375b9d3ee 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainPhotoItem.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainPhotoItem.kt
@@ -1,7 +1,9 @@
package com.neki.android.feature.archive.impl.main.component
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -11,10 +13,10 @@ import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import com.neki.android.core.designsystem.ComponentPreview
import com.neki.android.core.designsystem.R
-import com.neki.android.core.designsystem.modifier.photoBackground
import com.neki.android.core.designsystem.ui.theme.NekiTheme
import com.neki.android.core.model.Photo
import com.neki.android.core.ui.component.PhotoComponent
+import com.neki.android.core.ui.component.PhotoGridItemOverlay
@Composable
internal fun ArchiveMainPhotoItem(
@@ -22,11 +24,19 @@ internal fun ArchiveMainPhotoItem(
modifier: Modifier = Modifier,
onClickItem: (Photo) -> Unit = {},
) {
- PhotoComponent(
- photo = photo,
- modifier = modifier.photoBackground(),
- onClickItem = onClickItem,
+ Box(
+ modifier = modifier,
) {
+ PhotoComponent(
+ photo = photo,
+ onClickItem = onClickItem,
+ )
+
+ PhotoGridItemOverlay(
+ modifier = Modifier.matchParentSize(),
+ shape = RoundedCornerShape(8.dp),
+ )
+
if (photo.isFavorite) {
Icon(
modifier = Modifier
From 0210ea20f499a686c2836006719eb25ae4ae94ae Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Fri, 6 Feb 2026 23:33:47 +0900
Subject: [PATCH 09/24] =?UTF-8?q?[feat]=20#78:=20Empty=20=EB=B7=B0=20?=
=?UTF-8?q?=EC=88=98=EC=A0=95=EC=82=AC=ED=95=AD=20=EC=A0=81=EC=9A=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../main/res/drawable/icon_empty_content.xml | 126 +++++++++++++++
.../impl/album_detail/AlbumDetailScreen.kt | 150 ++++++++++--------
.../component/AlbumDetailTopBar.kt | 64 ++++++++
.../component/EmptyContent.kt | 59 ++++---
.../archive/impl/photo/AllPhotoScreen.kt | 67 +++++---
.../photo/component/AllPhotoEmptyContent.kt | 76 +++++++++
6 files changed, 429 insertions(+), 113 deletions(-)
create mode 100644 core/designsystem/src/main/res/drawable/icon_empty_content.xml
create mode 100644 feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/component/AlbumDetailTopBar.kt
rename feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/{album_detail => }/component/EmptyContent.kt (50%)
create mode 100644 feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/component/AllPhotoEmptyContent.kt
diff --git a/core/designsystem/src/main/res/drawable/icon_empty_content.xml b/core/designsystem/src/main/res/drawable/icon_empty_content.xml
new file mode 100644
index 000000000..4206622d3
--- /dev/null
+++ b/core/designsystem/src/main/res/drawable/icon_empty_content.xml
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/AlbumDetailScreen.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/AlbumDetailScreen.kt
index 23eb27bbe..b95138364 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/AlbumDetailScreen.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/AlbumDetailScreen.kt
@@ -9,33 +9,36 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.paging.LoadState
+import androidx.paging.PagingData
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemKey
-import com.neki.android.core.designsystem.topbar.BackTitleTextButtonTopBar
-import com.neki.android.core.designsystem.topbar.BackTitleTopBar
import com.neki.android.core.designsystem.ui.theme.NekiTheme
import com.neki.android.core.model.Photo
import com.neki.android.core.ui.component.DoubleButtonOptionBottomSheet
import com.neki.android.core.ui.component.LoadingDialog
import com.neki.android.core.ui.compose.collectWithLifecycle
import com.neki.android.core.ui.toast.NekiToast
-import com.neki.android.feature.archive.impl.album_detail.component.EmptyContent
+import com.neki.android.feature.archive.impl.album_detail.component.AlbumDetailTopBar
import com.neki.android.feature.archive.impl.component.DeletePhotoDialog
+import com.neki.android.feature.archive.impl.component.EmptyContent
import com.neki.android.feature.archive.impl.component.SelectablePhotoItem
import com.neki.android.feature.archive.impl.const.ArchiveConst.ARCHIVE_GRID_ITEM_SPACING
import com.neki.android.feature.archive.impl.const.ArchiveConst.PHOTO_GRAY_LAYOUT_BOTTOM_PADDING
@@ -45,6 +48,7 @@ import com.neki.android.feature.archive.impl.model.SelectMode
import com.neki.android.feature.archive.impl.photo.component.PhotoActionBar
import com.neki.android.feature.archive.impl.util.ImageDownloader
import kotlinx.collections.immutable.toImmutableList
+import kotlinx.coroutines.flow.flowOf
@Composable
internal fun AlbumDetailRoute(
@@ -93,20 +97,72 @@ internal fun AlbumDetailScreen(
) {
val lazyState = rememberLazyStaggeredGridState()
- val isRefreshing = pagingItems.loadState.refresh is LoadState.Loading
- val isEmpty = pagingItems.itemCount == 0 && pagingItems.loadState.refresh is LoadState.NotLoading
+ val isRefreshing by remember {
+ derivedStateOf { pagingItems.loadState.refresh is LoadState.Loading }
+ }
+ val isEmpty by remember {
+ derivedStateOf { pagingItems.itemCount == 0 && pagingItems.loadState.refresh is LoadState.NotLoading }
+ }
BackHandler(enabled = true) {
onIntent(AlbumDetailIntent.OnBackPressed)
}
+ if (isEmpty) {
+ EmptyContent(
+ title = if (uiState.isFavoriteAlbum) "즐겨찾기" else uiState.title,
+ onClickBack = { onIntent(AlbumDetailIntent.ClickBackIcon) },
+ )
+ } else {
+ AlbumDetailContent(
+ uiState = uiState,
+ pagingItems = pagingItems,
+ lazyState = lazyState,
+ onIntent = onIntent,
+ )
+ }
+
+ if (isRefreshing || uiState.isLoading) {
+ LoadingDialog()
+ }
+
+ if (uiState.isShowDeleteDialog) {
+ DeletePhotoDialog(
+ onDismissRequest = { onIntent(AlbumDetailIntent.DismissDeleteDialog) },
+ onClickDelete = { onIntent(AlbumDetailIntent.ClickDeleteDialogConfirmButton) },
+ onClickCancel = { onIntent(AlbumDetailIntent.ClickDeleteDialogCancelButton) },
+ )
+ }
+
+ if (uiState.isShowDeleteBottomSheet) {
+ DoubleButtonOptionBottomSheet(
+ title = "사진을 삭제하시겠어요?",
+ options = PhotoDeleteOption.entries.toImmutableList(),
+ selectedOption = uiState.selectedDeleteOption,
+ primaryButtonText = "삭제하기",
+ secondaryButtonText = "취소",
+ onDismissRequest = { onIntent(AlbumDetailIntent.DismissDeleteBottomSheet) },
+ onClickSecondaryButton = { onIntent(AlbumDetailIntent.ClickDeleteBottomSheetCancelButton) },
+ onClickPrimaryButton = { onIntent(AlbumDetailIntent.ClickDeleteBottomSheetConfirmButton) },
+ onOptionSelect = { onIntent(AlbumDetailIntent.SelectDeleteOption(it)) },
+ )
+ }
+}
+
+@Composable
+fun AlbumDetailContent(
+ uiState: AlbumDetailState,
+ pagingItems: LazyPagingItems,
+ lazyState: LazyStaggeredGridState,
+ modifier: Modifier = Modifier,
+ onIntent: (AlbumDetailIntent) -> Unit = {},
+) {
Column(
- modifier = Modifier
+ modifier = modifier
.fillMaxSize()
.background(NekiTheme.colorScheme.white),
) {
AlbumDetailTopBar(
- hasNoPhoto = isEmpty,
title = if (uiState.isFavoriteAlbum) "즐겨찾기" else uiState.title,
selectMode = uiState.selectMode,
onClickBack = { onIntent(AlbumDetailIntent.ClickBackIcon) },
@@ -173,73 +229,29 @@ internal fun AlbumDetailScreen(
onClickDelete = { onIntent(AlbumDetailIntent.ClickDeleteIcon) },
)
}
+}
- if (isRefreshing || uiState.isLoading) {
- LoadingDialog()
- }
-
- if (isEmpty) {
- EmptyContent(
- isFavorite = uiState.isFavoriteAlbum,
+@Preview
+@Composable
+private fun AlbumDetailScreenPreview() {
+ val photos = (0..10).map {
+ Photo(
+ id = it.toLong(),
+ imageUrl = "",
+ isFavorite = false,
+ date = "2024-04-2$it",
)
}
- if (uiState.isShowDeleteDialog) {
- DeletePhotoDialog(
- onDismissRequest = { onIntent(AlbumDetailIntent.DismissDeleteDialog) },
- onClickDelete = { onIntent(AlbumDetailIntent.ClickDeleteDialogConfirmButton) },
- onClickCancel = { onIntent(AlbumDetailIntent.ClickDeleteDialogCancelButton) },
- )
- }
+ val pagingData = PagingData.from(photos)
+ val pagingItems = flowOf(pagingData).collectAsLazyPagingItems()
- if (uiState.isShowDeleteBottomSheet) {
- DoubleButtonOptionBottomSheet(
- title = "사진을 삭제하시겠어요?",
- options = PhotoDeleteOption.entries.toImmutableList(),
- selectedOption = uiState.selectedDeleteOption,
- primaryButtonText = "삭제하기",
- secondaryButtonText = "취소",
- onDismissRequest = { onIntent(AlbumDetailIntent.DismissDeleteBottomSheet) },
- onClickSecondaryButton = { onIntent(AlbumDetailIntent.ClickDeleteBottomSheetCancelButton) },
- onClickPrimaryButton = { onIntent(AlbumDetailIntent.ClickDeleteBottomSheetConfirmButton) },
- onOptionSelect = { onIntent(AlbumDetailIntent.SelectDeleteOption(it)) },
- )
- }
-}
-
-@Composable
-private fun AlbumDetailTopBar(
- title: String,
- selectMode: SelectMode,
- onClickBack: () -> Unit,
- onClickSelect: () -> Unit,
- onClickCancel: () -> Unit,
- modifier: Modifier = Modifier,
- hasNoPhoto: Boolean = false,
-) {
- if (hasNoPhoto) {
- BackTitleTopBar(
- modifier = modifier,
- title = title,
- onBack = onClickBack,
- )
- } else {
- BackTitleTextButtonTopBar(
- modifier = modifier,
- title = title,
- buttonLabel = when (selectMode) {
- SelectMode.DEFAULT -> "선택"
- SelectMode.SELECTING -> "취소"
- },
- enabledTextColor = when (selectMode) {
- SelectMode.DEFAULT -> NekiTheme.colorScheme.primary500
- SelectMode.SELECTING -> NekiTheme.colorScheme.gray800
- },
- onBack = onClickBack,
- onClickTextButton = when (selectMode) {
- SelectMode.DEFAULT -> onClickSelect
- SelectMode.SELECTING -> onClickCancel
- },
+ NekiTheme {
+ AlbumDetailScreen(
+ uiState = AlbumDetailState(
+ title = "앨범 상세",
+ ),
+ pagingItems = pagingItems,
)
}
}
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/component/AlbumDetailTopBar.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/component/AlbumDetailTopBar.kt
new file mode 100644
index 000000000..193ee33e2
--- /dev/null
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/component/AlbumDetailTopBar.kt
@@ -0,0 +1,64 @@
+package com.neki.android.feature.archive.impl.album_detail.component
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.neki.android.core.designsystem.ComponentPreview
+import com.neki.android.core.designsystem.topbar.BackTitleTextButtonTopBar
+import com.neki.android.core.designsystem.ui.theme.NekiTheme
+import com.neki.android.feature.archive.impl.model.SelectMode
+
+@Composable
+internal fun AlbumDetailTopBar(
+ title: String,
+ selectMode: SelectMode,
+ onClickBack: () -> Unit,
+ onClickSelect: () -> Unit,
+ onClickCancel: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ BackTitleTextButtonTopBar(
+ modifier = modifier,
+ title = title,
+ buttonLabel = when (selectMode) {
+ SelectMode.DEFAULT -> "선택"
+ SelectMode.SELECTING -> "취소"
+ },
+ enabledTextColor = when (selectMode) {
+ SelectMode.DEFAULT -> NekiTheme.colorScheme.primary500
+ SelectMode.SELECTING -> NekiTheme.colorScheme.gray800
+ },
+ onBack = onClickBack,
+ onClickTextButton = when (selectMode) {
+ SelectMode.DEFAULT -> onClickSelect
+ SelectMode.SELECTING -> onClickCancel
+ },
+ )
+}
+
+@ComponentPreview
+@Composable
+private fun AlbumDetailTopBarPreview() {
+ NekiTheme {
+ AlbumDetailTopBar(
+ title = "Album Title",
+ selectMode = SelectMode.DEFAULT,
+ onClickBack = {},
+ onClickSelect = {},
+ onClickCancel = {},
+ )
+ }
+}
+
+@ComponentPreview
+@Composable
+private fun AlbumDetailTopBarSelectingPreview() {
+ NekiTheme {
+ AlbumDetailTopBar(
+ title = "Album Title",
+ selectMode = SelectMode.SELECTING,
+ onClickBack = {},
+ onClickSelect = {},
+ onClickCancel = {},
+ )
+ }
+}
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/component/EmptyContent.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/EmptyContent.kt
similarity index 50%
rename from feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/component/EmptyContent.kt
rename to feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/EmptyContent.kt
index b7709dec4..402073e14 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/component/EmptyContent.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/EmptyContent.kt
@@ -1,47 +1,53 @@
-package com.neki.android.feature.archive.impl.album_detail.component
+package com.neki.android.feature.archive.impl.component
-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.size
-import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.neki.android.core.designsystem.ComponentPreview
+import com.neki.android.core.designsystem.R
+import com.neki.android.core.designsystem.topbar.BackTitleTopBar
import com.neki.android.core.designsystem.ui.theme.NekiTheme
+private const val EMPTY_TEXT = "아직 등록된 사진이 없어요\n새로운 사진을 등록하고 앨범에 추가해보세요!"
+
@Composable
internal fun EmptyContent(
- isFavorite: Boolean,
+ title: String,
modifier: Modifier = Modifier,
+ emptyText: String = EMPTY_TEXT,
+ onClickBack: () -> Unit = {},
) {
Box(
modifier = modifier.fillMaxSize(),
) {
+ EmptyTopBar(
+ title = title,
+ onClickBack = onClickBack,
+ )
+
Column(
modifier = Modifier.align(Alignment.Center),
horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.spacedBy(22.dp),
+ verticalArrangement = Arrangement.spacedBy(16.dp),
) {
- Box(
- modifier = Modifier
- .size(104.dp)
- .clip(CircleShape)
- .background(
- color = NekiTheme.colorScheme.gray50,
- shape = CircleShape,
- ),
+ Icon(
+ imageVector = ImageVector.vectorResource(R.drawable.icon_empty_content),
+ contentDescription = null,
+ tint = Color.Unspecified,
)
Text(
- text = if (isFavorite) "아직 등록된 사진이 없어요\n아카이빙 페이지에서 추가해보세요!"
- else "아직 등록된 사진이 없어요\n아카이빙 페이지에서 추가해보세요!",
+ text = emptyText,
style = NekiTheme.typography.body14Medium,
color = NekiTheme.colorScheme.gray300,
textAlign = TextAlign.Center,
@@ -50,14 +56,17 @@ internal fun EmptyContent(
}
}
-@ComponentPreview
@Composable
-private fun FavoriteEmptyContentPreview() {
- NekiTheme {
- EmptyContent(
- isFavorite = true,
- )
- }
+private fun EmptyTopBar(
+ title: String,
+ onClickBack: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ BackTitleTopBar(
+ modifier = modifier,
+ title = title,
+ onBack = onClickBack,
+ )
}
@ComponentPreview
@@ -65,7 +74,7 @@ private fun FavoriteEmptyContentPreview() {
private fun EmptyContentPreview() {
NekiTheme {
EmptyContent(
- isFavorite = false,
+ title = "즐겨찾기",
)
}
}
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/AllPhotoScreen.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/AllPhotoScreen.kt
index f56d9dfb8..4282a8bd0 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/AllPhotoScreen.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/AllPhotoScreen.kt
@@ -41,6 +41,7 @@ import com.neki.android.core.ui.component.LoadingDialog
import com.neki.android.core.ui.compose.collectWithLifecycle
import com.neki.android.core.ui.toast.NekiToast
import com.neki.android.feature.archive.impl.component.DeletePhotoDialog
+import com.neki.android.feature.archive.impl.component.EmptyContent
import com.neki.android.feature.archive.impl.component.SelectablePhotoItem
import com.neki.android.feature.archive.impl.const.ArchiveConst.ARCHIVE_GRID_ITEM_SPACING
import com.neki.android.feature.archive.impl.const.ArchiveConst.PHOTO_GRAY_LAYOUT_BOTTOM_PADDING
@@ -109,6 +110,52 @@ internal fun AllPhotoScreen(
pagingItems: LazyPagingItems,
lazyState: LazyStaggeredGridState = rememberLazyStaggeredGridState(),
onIntent: (AllPhotoIntent) -> Unit = {},
+) {
+ val isRefreshing by remember(pagingItems) {
+ derivedStateOf { pagingItems.loadState.refresh is LoadState.Loading }
+ }
+ val isEmpty by remember(pagingItems) {
+ derivedStateOf { pagingItems.itemCount == 0 && pagingItems.loadState.refresh is LoadState.NotLoading }
+ }
+
+ BackHandler(enabled = true) {
+ onIntent(AllPhotoIntent.OnBackPressed)
+ }
+
+ if (isEmpty) {
+ EmptyContent(
+ title = "모든 사진",
+ onClickBack = { onIntent(AllPhotoIntent.ClickTopBarBackIcon) },
+ )
+ } else {
+ AllPhotoContent(
+ uiState = uiState,
+ pagingItems = pagingItems,
+ lazyState = lazyState,
+ onIntent = onIntent,
+ )
+ }
+
+ if (isRefreshing || uiState.isLoading) {
+ LoadingDialog()
+ }
+
+ if (uiState.isShowDeleteDialog) {
+ DeletePhotoDialog(
+ onDismissRequest = { onIntent(AllPhotoIntent.DismissDeleteDialog) },
+ onClickDelete = { onIntent(AllPhotoIntent.ClickDeleteDialogConfirmButton) },
+ onClickCancel = { onIntent(AllPhotoIntent.DismissDeleteDialog) },
+ )
+ }
+}
+
+@Composable
+private fun AllPhotoContent(
+ uiState: AllPhotoState,
+ pagingItems: LazyPagingItems,
+ lazyState: LazyStaggeredGridState,
+ modifier: Modifier = Modifier,
+ onIntent: (AllPhotoIntent) -> Unit = {},
) {
val density = LocalDensity.current
var filterBarHeightPx by remember { mutableIntStateOf(0) }
@@ -121,14 +168,8 @@ internal fun AllPhotoScreen(
}
}
- val isRefreshing by remember { derivedStateOf { pagingItems.loadState.refresh is LoadState.Loading } }
-
- BackHandler(enabled = true) {
- onIntent(AllPhotoIntent.OnBackPressed)
- }
-
Column(
- modifier = Modifier
+ modifier = modifier
.fillMaxSize()
.background(NekiTheme.colorScheme.white),
) {
@@ -214,18 +255,6 @@ internal fun AllPhotoScreen(
onClickDelete = { onIntent(AllPhotoIntent.ClickDeleteIcon) },
)
}
-
- if (isRefreshing || uiState.isLoading) {
- LoadingDialog()
- }
-
- if (uiState.isShowDeleteDialog) {
- DeletePhotoDialog(
- onDismissRequest = { onIntent(AllPhotoIntent.DismissDeleteDialog) },
- onClickDelete = { onIntent(AllPhotoIntent.ClickDeleteDialogConfirmButton) },
- onClickCancel = { onIntent(AllPhotoIntent.DismissDeleteDialog) },
- )
- }
}
@DevicePreview
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/component/AllPhotoEmptyContent.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/component/AllPhotoEmptyContent.kt
new file mode 100644
index 000000000..5bcad94c1
--- /dev/null
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/component/AllPhotoEmptyContent.kt
@@ -0,0 +1,76 @@
+package com.neki.android.feature.archive.impl.photo.component
+
+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.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import com.neki.android.core.designsystem.ComponentPreview
+import com.neki.android.core.designsystem.R
+import com.neki.android.core.designsystem.ui.theme.NekiTheme
+import com.neki.android.feature.archive.impl.photo.AllPhotoIntent
+import com.neki.android.feature.archive.impl.photo.AllPhotoState
+
+@Composable
+internal fun AllPhotoEmptyContent(
+ uiState: AllPhotoState,
+ modifier: Modifier = Modifier,
+ onIntent: (AllPhotoIntent) -> Unit = {},
+) {
+ Column(
+ modifier = modifier
+ .fillMaxSize()
+ .background(NekiTheme.colorScheme.white),
+ ) {
+ AllPhotoTopBar(
+ selectMode = uiState.selectMode,
+ onClickBack = { onIntent(AllPhotoIntent.ClickTopBarBackIcon) },
+ onClickSelect = { },
+ onClickCancel = { },
+ )
+
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .weight(1f),
+ contentAlignment = Alignment.Center,
+ ) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(16.dp),
+ ) {
+ Icon(
+ imageVector = ImageVector.vectorResource(R.drawable.icon_empty_content),
+ contentDescription = null,
+ tint = Color.Unspecified,
+ )
+ Text(
+ text = "아직 등록된 사진이 없어요\n새로운 사진을 등록하고 앨범에 추가해보세요!",
+ style = NekiTheme.typography.body14Medium,
+ color = NekiTheme.colorScheme.gray300,
+ textAlign = TextAlign.Center,
+ )
+ }
+ }
+ }
+}
+
+@ComponentPreview
+@Composable
+private fun AllPhotoEmptyContentPreview() {
+ NekiTheme {
+ AllPhotoEmptyContent(
+ uiState = AllPhotoState(),
+ )
+ }
+}
From 019bfef399eb97d97fc2d7fba92573997c9a0205 Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Sat, 7 Feb 2026 00:53:43 +0900
Subject: [PATCH 10/24] =?UTF-8?q?[refactor]=20#78:=20=ED=8F=AC=ED=86=A0=20?=
=?UTF-8?q?=EA=B7=B8=EB=A6=AC=EB=93=9C=20=EB=B0=B0=EA=B2=BD=EC=97=90=20alp?=
=?UTF-8?q?ha=200.04f=EC=9D=98=20=EA=B2=80=EC=9D=80=EC=83=89=20=EB=B0=B0?=
=?UTF-8?q?=EA=B2=BD=20=EB=A0=88=EC=9D=B4=EC=96=B4=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
포토 그리드 아이템의 배경에 `Color.Black.copy(alpha = 0.04f)` 속성의 배경을 추가하여, 기존 그래디언트 배경 위에 새로운 레이어를 더했습니다.
---
.../core/designsystem/modifier/Background.kt | 21 ++++++++++++-------
1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
index 5f8bee341..94ee51e09 100644
--- a/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
+++ b/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
@@ -19,15 +19,20 @@ import dev.chrisbanes.haze.hazeEffect
*/
fun Modifier.photoGridBackground(
shape: Shape = RoundedCornerShape(8.dp),
-): Modifier = this.background(
- brush = Brush.verticalGradient(
- colorStops = arrayOf(
- 0f to Color.Black.copy(alpha = 0.2f),
- 134f / 242f to Color.Black.copy(alpha = 0f),
+): Modifier = this
+ .background(
+ color = Color.Black.copy(alpha = 0.04f),
+ shape = shape,
+ )
+ .background(
+ brush = Brush.verticalGradient(
+ colorStops = arrayOf(
+ 0f to Color.Black.copy(alpha = 0.2f),
+ 134f / 242f to Color.Black.copy(alpha = 0f),
+ ),
),
- ),
- shape = shape,
-)
+ shape = shape,
+ )
/**
* 블러 효과가 적용된 배경을 설정하는 Modifier 확장 함수
From 17a0e2eed23d1bc8f07c3e2e53ca70db930ed16a Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Sat, 7 Feb 2026 01:39:34 +0900
Subject: [PATCH 11/24] =?UTF-8?q?[fix]=20#78:=20=EC=9D=B4=EB=AF=B8?=
=?UTF-8?q?=EC=A7=80=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EC=8B=9C=20EXIF=20?=
=?UTF-8?q?=ED=9A=8C=EC=A0=84=20=EB=B0=A9=ED=96=A5=20=EB=B3=B4=EC=A0=95=20?=
=?UTF-8?q?=EC=A0=81=EC=9A=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
core/common/build.gradle.kts | 1 +
.../android/core/common/util/ByteArray.kt | 41 ++++++++++++++++++-
gradle/libs.versions.toml | 2 +
3 files changed, 43 insertions(+), 1 deletion(-)
diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts
index b3377e80e..18926ea0f 100644
--- a/core/common/build.gradle.kts
+++ b/core/common/build.gradle.kts
@@ -12,5 +12,6 @@ dependencies {
api(libs.timber)
implementation(libs.androidx.security.crypto)
implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.exifinterface)
}
diff --git a/core/common/src/main/java/com/neki/android/core/common/util/ByteArray.kt b/core/common/src/main/java/com/neki/android/core/common/util/ByteArray.kt
index 3b3610dee..d88bf2feb 100644
--- a/core/common/src/main/java/com/neki/android/core/common/util/ByteArray.kt
+++ b/core/common/src/main/java/com/neki/android/core/common/util/ByteArray.kt
@@ -3,7 +3,9 @@ package com.neki.android.core.common.util
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
+import android.graphics.Matrix
import android.net.Uri
+import androidx.exifinterface.media.ExifInterface
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.ByteArrayOutputStream
@@ -16,8 +18,22 @@ fun Uri.toByteArray(
quality: Int = DEFAULT_QUALITY,
format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG,
): ByteArray? {
+ val orientation = context.contentResolver.openInputStream(this)?.use { input ->
+ ExifInterface(input).getAttributeInt(
+ ExifInterface.TAG_ORIENTATION,
+ ExifInterface.ORIENTATION_UNDEFINED,
+ )
+ } ?: ExifInterface.ORIENTATION_UNDEFINED
val bytes = context.contentResolver.openInputStream(this)?.use { it.readBytes() } ?: return null
- return bytes.compress(quality, format)
+ val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size) ?: return null
+ val rotatedBitmap = bitmap.applyOrientation(orientation)
+
+ return ByteArrayOutputStream().use { outputStream ->
+ rotatedBitmap.compress(format, quality, outputStream)
+ if (rotatedBitmap !== bitmap) bitmap.recycle()
+ rotatedBitmap.recycle()
+ outputStream.toByteArray()
+ }
}
suspend fun String.urlToByteArray(
@@ -28,6 +44,29 @@ suspend fun String.urlToByteArray(
bytes.compress(quality, format)
}
+private fun Bitmap.applyOrientation(orientation: Int): Bitmap {
+ val matrix = Matrix()
+ when (orientation) {
+ ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90f)
+ ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180f)
+ ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270f)
+ ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.postScale(-1f, 1f)
+ ExifInterface.ORIENTATION_FLIP_VERTICAL -> matrix.postScale(1f, -1f)
+ ExifInterface.ORIENTATION_TRANSPOSE -> {
+ matrix.postRotate(90f)
+ matrix.postScale(-1f, 1f)
+ }
+
+ ExifInterface.ORIENTATION_TRANSVERSE -> {
+ matrix.postRotate(270f)
+ matrix.postScale(-1f, 1f)
+ }
+
+ else -> return this
+ }
+ return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true)
+}
+
private fun ByteArray.compress(
quality: Int,
format: Bitmap.CompressFormat,
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 1ef1cf618..6d1b8bd00 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -38,6 +38,7 @@ kakao = "2.23.1"
coil = "3.3.0"
haze = "1.7.1"
lottie = "6.7.1"
+exifinterface = "1.4.2"
[libraries]
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraX" }
@@ -115,6 +116,7 @@ kotlin-gradle-plugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-p
android-gradle-plugin = { group = "com.android.tools.build", name = "gradle", version.ref = "agp" }
compose-compiler-gradle-plugin = { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
+androidx-exifinterface = { group = "androidx.exifinterface", name = "exifinterface", version.ref = "exifinterface" }
[plugins]
# Plugins defined by this project
From 94e557ec36ca449a0ec4ed1e961c287efa4d88c3 Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Sat, 7 Feb 2026 01:39:42 +0900
Subject: [PATCH 12/24] =?UTF-8?q?[refactor]=20#78:=20backgroundHazeBlur=20?=
=?UTF-8?q?KDoc=20=EC=A3=BC=EC=84=9D=20=EC=97=85=EB=8D=B0=EC=9D=B4?=
=?UTF-8?q?=ED=8A=B8=20=EB=B0=8F=20=EB=AF=B8=EC=82=AC=EC=9A=A9=20=ED=8C=8C?=
=?UTF-8?q?=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=A0=9C=EA=B1=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../neki/android/core/designsystem/modifier/Background.kt | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
index 94ee51e09..8f29c761a 100644
--- a/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
+++ b/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
@@ -5,7 +5,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@@ -38,10 +37,11 @@ fun Modifier.photoGridBackground(
* 블러 효과가 적용된 배경을 설정하는 Modifier 확장 함수
*
* @param hazeState Haze 블러 효과를 관리하는 상태 객체
- * @param enabled 블러 효과 활성화 여부 (false일 경우 단색 배경 적용)
+ * @param alpha 블러 틴트에 적용될 색상의 알파 값
* @param color 블러 효과에 적용될 배경 색상
- * @param defaultBackgroundColor 블러 비활성화 시 적용될 기본 배경 색상
* @param blurRadius 블러 효과의 반경
+ * @param enabled 블러 효과 활성화 여부 (false일 경우 단색 배경 적용)
+ * @param defaultBackgroundColor 블러 비활성화 시 적용될 기본 배경 색상
*/
fun Modifier.backgroundHazeBlur(
hazeState: HazeState,
@@ -50,7 +50,6 @@ fun Modifier.backgroundHazeBlur(
blurRadius: Dp,
enabled: Boolean = true,
defaultBackgroundColor: Color = color,
- shape: Shape = RectangleShape,
): Modifier =
if (enabled) {
this.hazeEffect(
@@ -63,5 +62,4 @@ fun Modifier.backgroundHazeBlur(
)
} else this.background(
color = defaultBackgroundColor,
- shape = shape,
)
From f18f6d790fbfc9b0fa9d945e2e5cfbb05f97ad4a Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Sat, 7 Feb 2026 01:39:52 +0900
Subject: [PATCH 13/24] =?UTF-8?q?[feat]=20#78:=20ToolTipPopup=20onDismissR?=
=?UTF-8?q?equest=20=EC=BD=9C=EB=B0=B1=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20?=
=?UTF-8?q?=ED=8C=9D=EC=97=85=20dismiss=20=EB=B6=84=EB=A6=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../core/designsystem/popup/ToolTipPopup.kt | 2 ++
.../archive/impl/main/ArchiveMainContract.kt | 3 ++-
.../feature/archive/impl/main/ArchiveMainScreen.kt | 3 ++-
.../archive/impl/main/ArchiveMainViewModel.kt | 3 ++-
.../impl/main/component/ArchiveMainTopBar.kt | 14 ++++++++++----
5 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/popup/ToolTipPopup.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/popup/ToolTipPopup.kt
index ffb0845c4..1ea95a9bd 100644
--- a/core/designsystem/src/main/java/com/neki/android/core/designsystem/popup/ToolTipPopup.kt
+++ b/core/designsystem/src/main/java/com/neki/android/core/designsystem/popup/ToolTipPopup.kt
@@ -28,10 +28,12 @@ fun ToolTipPopup(
color: Color,
offset: IntOffset,
alignment: Alignment,
+ onDismissRequest: () -> Unit,
) {
Popup(
alignment = alignment,
offset = offset,
+ onDismissRequest = onDismissRequest,
) {
ToolTipContent(
tooltipText = tooltipText,
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainContract.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainContract.kt
index beac2bf90..dd9debe4d 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainContract.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainContract.kt
@@ -32,7 +32,8 @@ sealed interface ArchiveMainIntent {
// TopBar Intent
data object ClickAddIcon : ArchiveMainIntent
- data object DismissAddDialog : ArchiveMainIntent
+ data object DismissAddPopup : ArchiveMainIntent
+ data object DismissToolTipPopup : ArchiveMainIntent
data object ClickQRScanRow : ArchiveMainIntent
data object ClickGalleryUploadRow : ArchiveMainIntent
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainScreen.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainScreen.kt
index 3c531d100..392c89161 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainScreen.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainScreen.kt
@@ -119,7 +119,8 @@ internal fun ArchiveMainScreen(
onClickQRScan = { onIntent(ArchiveMainIntent.ClickQRScanRow) },
onClickGallery = { onIntent(ArchiveMainIntent.ClickGalleryUploadRow) },
onClickNewAlbum = { onIntent(ArchiveMainIntent.ClickAddNewAlbumRow) },
- onDismissPopup = { onIntent(ArchiveMainIntent.DismissAddDialog) },
+ onDismissAddPopup = { onIntent(ArchiveMainIntent.DismissAddPopup) },
+ onDismissToolTipPopup = { onIntent(ArchiveMainIntent.DismissToolTipPopup) },
)
ArchiveMainContent(
uiState = uiState,
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainViewModel.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainViewModel.kt
index e148ac045..48d58e1d2 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainViewModel.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/ArchiveMainViewModel.kt
@@ -60,7 +60,8 @@ class ArchiveMainViewModel @Inject constructor(
// TopBar Intent
ArchiveMainIntent.ClickAddIcon -> reduce { copy(isShowAddDialog = true) }
- ArchiveMainIntent.DismissAddDialog -> reduce { copy(isShowAddDialog = false) }
+ ArchiveMainIntent.DismissAddPopup -> reduce { copy(isShowAddDialog = false) }
+ ArchiveMainIntent.DismissToolTipPopup -> reduce { copy(isFirstEntered = false) }
ArchiveMainIntent.ClickQRScanRow -> {
reduce { copy(isShowAddDialog = false) }
postSideEffect(ArchiveMainSideEffect.NavigateToQRScan)
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTopBar.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTopBar.kt
index 0e513b807..7b1629d81 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTopBar.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTopBar.kt
@@ -42,7 +42,8 @@ internal fun ArchiveMainTopBar(
onClickQRScan: () -> Unit = {},
onClickGallery: () -> Unit = {},
onClickNewAlbum: () -> Unit = {},
- onDismissPopup: () -> Unit = {},
+ onDismissAddPopup: () -> Unit = {},
+ onDismissToolTipPopup: () -> Unit = {},
showTooltip: Boolean = true,
) {
NekiLeftTitleTopBar(
@@ -67,14 +68,16 @@ internal fun ArchiveMainTopBar(
if (showAddPopup) {
AddPhotoPopup(
- onDismissRequest = onDismissPopup,
+ onDismissRequest = onDismissAddPopup,
onClickQRScan = onClickQRScan,
onClickGallery = onClickGallery,
onClickNewAlbum = onClickNewAlbum,
)
}
if (showTooltip) {
- ArchiveToolTip()
+ ArchiveToolTip(
+ onDismissRequest = onDismissToolTipPopup,
+ )
}
}
NekiIconButton(
@@ -92,7 +95,9 @@ internal fun ArchiveMainTopBar(
}
@Composable
-private fun ArchiveToolTip() {
+private fun ArchiveToolTip(
+ onDismissRequest: () -> Unit = {},
+) {
val density = LocalDensity.current
val popupOffsetX = with(density) { 1.dp.toPx().toInt() }
val popupOffsetY = with(density) { 47.dp.toPx().toInt() }
@@ -103,6 +108,7 @@ private fun ArchiveToolTip() {
color = NekiTheme.colorScheme.gray800,
offset = offset,
alignment = Alignment.TopEnd,
+ onDismissRequest = onDismissRequest,
)
}
From 1eaf057edca14b7c889b323e852c0a1b34ca720e Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Sat, 7 Feb 2026 01:43:57 +0900
Subject: [PATCH 14/24] =?UTF-8?q?[fix]=20#78:=20PhotoComponent=20cornerRad?=
=?UTF-8?q?ius=2012dp=EC=97=90=EC=84=9C=208dp=EB=A1=9C=20=EC=88=98?=
=?UTF-8?q?=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../core/ui/component/PhotoComponent.kt | 2 +-
.../photo/component/AllPhotoEmptyContent.kt | 76 -------------------
2 files changed, 1 insertion(+), 77 deletions(-)
delete mode 100644 feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/component/AllPhotoEmptyContent.kt
diff --git a/core/ui/src/main/java/com/neki/android/core/ui/component/PhotoComponent.kt b/core/ui/src/main/java/com/neki/android/core/ui/component/PhotoComponent.kt
index 60cd8035d..da60ed37f 100644
--- a/core/ui/src/main/java/com/neki/android/core/ui/component/PhotoComponent.kt
+++ b/core/ui/src/main/java/com/neki/android/core/ui/component/PhotoComponent.kt
@@ -24,7 +24,7 @@ fun PhotoComponent(
) {
Box(
modifier = modifier
- .clip(RoundedCornerShape(12.dp))
+ .clip(RoundedCornerShape(8.dp))
.noRippleClickable { onClickItem(photo) },
) {
AsyncImage(
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/component/AllPhotoEmptyContent.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/component/AllPhotoEmptyContent.kt
deleted file mode 100644
index 5bcad94c1..000000000
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/photo/component/AllPhotoEmptyContent.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-package com.neki.android.feature.archive.impl.photo.component
-
-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.material3.Icon
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.vector.ImageVector
-import androidx.compose.ui.res.vectorResource
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.unit.dp
-import com.neki.android.core.designsystem.ComponentPreview
-import com.neki.android.core.designsystem.R
-import com.neki.android.core.designsystem.ui.theme.NekiTheme
-import com.neki.android.feature.archive.impl.photo.AllPhotoIntent
-import com.neki.android.feature.archive.impl.photo.AllPhotoState
-
-@Composable
-internal fun AllPhotoEmptyContent(
- uiState: AllPhotoState,
- modifier: Modifier = Modifier,
- onIntent: (AllPhotoIntent) -> Unit = {},
-) {
- Column(
- modifier = modifier
- .fillMaxSize()
- .background(NekiTheme.colorScheme.white),
- ) {
- AllPhotoTopBar(
- selectMode = uiState.selectMode,
- onClickBack = { onIntent(AllPhotoIntent.ClickTopBarBackIcon) },
- onClickSelect = { },
- onClickCancel = { },
- )
-
- Box(
- modifier = Modifier
- .fillMaxSize()
- .weight(1f),
- contentAlignment = Alignment.Center,
- ) {
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.spacedBy(16.dp),
- ) {
- Icon(
- imageVector = ImageVector.vectorResource(R.drawable.icon_empty_content),
- contentDescription = null,
- tint = Color.Unspecified,
- )
- Text(
- text = "아직 등록된 사진이 없어요\n새로운 사진을 등록하고 앨범에 추가해보세요!",
- style = NekiTheme.typography.body14Medium,
- color = NekiTheme.colorScheme.gray300,
- textAlign = TextAlign.Center,
- )
- }
- }
- }
-}
-
-@ComponentPreview
-@Composable
-private fun AllPhotoEmptyContentPreview() {
- NekiTheme {
- AllPhotoEmptyContent(
- uiState = AllPhotoState(),
- )
- }
-}
From ce04f3027375d50743269632a7f0e309438f09d4 Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Sat, 7 Feb 2026 01:44:07 +0900
Subject: [PATCH 15/24] =?UTF-8?q?[refactor]=20#78:=20SelectablePhotoItem?=
=?UTF-8?q?=20=EC=84=A0=ED=83=9D=20=EC=8B=9C=20=EA=B2=80=EC=9D=80=20?=
=?UTF-8?q?=EB=B0=B0=EA=B2=BD=20=EC=98=A4=EB=B2=84=EB=A0=88=EC=9D=B4=20?=
=?UTF-8?q?=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20=EB=AF=B8=EC=82=AC=EC=9A=A9=20?=
=?UTF-8?q?import=20=EC=A0=95=EB=A6=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../archive/impl/component/SelcetablePhotoItem.kt | 14 ++++----------
1 file changed, 4 insertions(+), 10 deletions(-)
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelcetablePhotoItem.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelcetablePhotoItem.kt
index 75d1332a7..d985a4041 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelcetablePhotoItem.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelcetablePhotoItem.kt
@@ -1,6 +1,5 @@
package com.neki.android.feature.archive.impl.component
-import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
@@ -11,7 +10,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
@@ -40,17 +38,13 @@ internal fun SelectablePhotoItem(
PhotoComponent(
photo = photo,
modifier = Modifier.then(
- if (isSelected) Modifier
- .border(
+ if (isSelected)
+ Modifier.border(
width = 2.dp,
color = NekiTheme.colorScheme.primary400,
shape = RoundedCornerShape(12.dp),
- )
- .background(
- color = Color.Black.copy(alpha = 0.2f),
- shape = RoundedCornerShape(12.dp),
- ) else Modifier
- .clip(RoundedCornerShape(12.dp)),
+ ) else
+ Modifier.clip(RoundedCornerShape(12.dp)),
),
onClickItem = onClickItem,
)
From aa10101415516fddb530d099cf352f5ea63fea3d Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Sat, 7 Feb 2026 01:44:15 +0900
Subject: [PATCH 16/24] =?UTF-8?q?[refactor]=20#78:=20AlbumDetailContent=20?=
=?UTF-8?q?=EC=A0=91=EA=B7=BC=EC=A0=9C=EC=96=B4=EC=9E=90=20internal?=
=?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../feature/archive/impl/album_detail/AlbumDetailScreen.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/AlbumDetailScreen.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/AlbumDetailScreen.kt
index b95138364..7021384ba 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/AlbumDetailScreen.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/AlbumDetailScreen.kt
@@ -150,7 +150,7 @@ internal fun AlbumDetailScreen(
}
@Composable
-fun AlbumDetailContent(
+internal fun AlbumDetailContent(
uiState: AlbumDetailState,
pagingItems: LazyPagingItems,
lazyState: LazyStaggeredGridState,
From d71f0d11aab422c583c5466955641e20a446cec3 Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Sat, 7 Feb 2026 01:52:09 +0900
Subject: [PATCH 17/24] =?UTF-8?q?[refactor]=20#78:=20=EC=95=84=EC=B9=B4?=
=?UTF-8?q?=EC=9D=B4=EB=B8=8C=20=EC=A0=84=EC=B2=B4=EB=B3=B4=EA=B8=B0=20?=
=?UTF-8?q?=EB=B2=84=ED=8A=BC=20=ED=84=B0=EC=B9=98=20=EC=98=81=EC=97=AD=20?=
=?UTF-8?q?=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
`offset`으로 구현되어 있던 버튼의 패딩을 `contentPadding`을 사용하도록 변경하여 터치 영역을 확장했습니다.
---
.../feature/archive/impl/main/component/ArchiveMainTitleRow.kt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTitleRow.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTitleRow.kt
index fcf2c6693..e6a2c9834 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTitleRow.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTitleRow.kt
@@ -1,6 +1,7 @@
package com.neki.android.feature.archive.impl.main.component
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset
@@ -37,7 +38,7 @@ internal fun ArchiveMainTitleRow(
color = NekiTheme.colorScheme.gray900,
)
NekiTextButton(
- modifier = Modifier.offset(x = ARCHIVE_ROW_TEXT_BUTTON_PADDING.dp),
+ contentPadding = PaddingValues(vertical = 10.dp),
onClick = onClickShowAllAlbum,
) {
Row(
From b63b96b0acc30313efcfec42a4bca03ca1434073 Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Sat, 7 Feb 2026 01:52:53 +0900
Subject: [PATCH 18/24] =?UTF-8?q?[refactor]=20#78:=20BuildConfig=20?=
=?UTF-8?q?=ED=95=84=EB=93=9C=EB=AA=85=20=EC=98=A4=ED=83=80=20=EC=88=98?=
=?UTF-8?q?=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
`build.gradle.kts` 파일에 정의된 일부 BuildConfig 필드명의 오타를 수정합니다.
- `PHOTOISM_IMG_URL_MIME_TYPE` → `PHOTOISM_IMAGE_URL_MIME_TYPE`
- `LIFE_FOUR_CUT_URL_MIME_TYPE` → `LIFE_FOUR_CUT_IMAGE_URL_MIME_TYPE`
수정된 필드명을 참조하도록 `PhotoWebViewClient.kt` 파일도 함께 변경했습니다.
---
feature/photo-upload/impl/build.gradle.kts | 6 ++++--
.../photo_upload/impl/qrscan/util/PhotoWebViewClient.kt | 4 ++--
2 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/feature/photo-upload/impl/build.gradle.kts b/feature/photo-upload/impl/build.gradle.kts
index 9f0dac88c..58c6d266d 100644
--- a/feature/photo-upload/impl/build.gradle.kts
+++ b/feature/photo-upload/impl/build.gradle.kts
@@ -21,12 +21,14 @@ android {
defaultConfig {
buildConfigField("String", "BRAND_PROPOSAL_URL", properties["BRAND_PROPOSAL_URL"].toString())
+
buildConfigField("String", "PHOTOISM_URL", properties["PHOTOISM_URL"].toString())
buildConfigField("String", "PHOTOISM_IMAGE_URL", properties["PHOTOISM_IMAGE_URL"].toString())
- buildConfigField("String", "PHOTOISM_IMG_URL_MIME_TYPE", properties["PHOTOISM_IMG_URL_MIME_TYPE"].toString())
+ buildConfigField("String", "PHOTOISM_IMAGE_URL_MIME_TYPE", properties["PHOTOISM_IMAGE_URL_MIME_TYPE"].toString())
+
buildConfigField("String", "LIFE_FOUR_CUT_URL", properties["LIFE_FOUR_CUT_URL"].toString())
buildConfigField("String", "LIFE_FOUR_CUT_IMAGE_URL", properties["LIFE_FOUR_CUT_IMAGE_URL"].toString())
- buildConfigField("String", "LIFE_FOUR_CUT_URL_MIME_TYPE", properties["LIFE_FOUR_CUT_URL_MIME_TYPE"].toString())
+ buildConfigField("String", "LIFE_FOUR_CUT_IMAGE_URL_MIME_TYPE", properties["LIFE_FOUR_CUT_IMAGE_URL_MIME_TYPE"].toString())
}
}
diff --git a/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/util/PhotoWebViewClient.kt b/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/util/PhotoWebViewClient.kt
index dbbc554d6..60554d2a4 100644
--- a/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/util/PhotoWebViewClient.kt
+++ b/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/qrscan/util/PhotoWebViewClient.kt
@@ -20,13 +20,13 @@ class PhotoWebViewClient(
when {
// 포토이즘
- url.startsWith(BuildConfig.PHOTOISM_IMAGE_URL) && url.endsWith(BuildConfig.PHOTOISM_IMG_URL_MIME_TYPE) -> {
+ url.startsWith(BuildConfig.PHOTOISM_IMAGE_URL) && url.endsWith(BuildConfig.PHOTOISM_IMAGE_URL_MIME_TYPE) -> {
Timber.d("포토이즘 이미지")
onImageUrlDetected(url)
}
// 인생네컷
- url.startsWith(BuildConfig.LIFE_FOUR_CUT_IMAGE_URL) && url.endsWith(BuildConfig.LIFE_FOUR_CUT_URL_MIME_TYPE) -> {
+ url.startsWith(BuildConfig.LIFE_FOUR_CUT_IMAGE_URL) && url.endsWith(BuildConfig.LIFE_FOUR_CUT_IMAGE_URL_MIME_TYPE) -> {
Timber.d("인생네컷 이미지")
onImageUrlDetected(url)
}
From d1833d2c298fabf768e4874890c8f5aaf4f01162 Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Sat, 7 Feb 2026 11:31:26 +0900
Subject: [PATCH 19/24] =?UTF-8?q?[refactor]=20#78:=20`AlbumDetailTopBar`?=
=?UTF-8?q?=EC=9D=98=20=ED=81=B4=EB=A6=AD=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?=
=?UTF-8?q?=ED=95=B8=EB=93=A4=EB=9F=AC=EC=97=90=20=EA=B8=B0=EB=B3=B8?=
=?UTF-8?q?=EA=B0=92=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
`AlbumDetailTopBar` 컴포저블의 `onClickBack`, `onClickSelect`, `onClickCancel` 파라미터에 기본으로 빈 람다 `{}`를 할당했습니다. 이를 통해 호출부에서 불필요한 빈 콜백을 매번 전달해야 하는 코드를 간소화했습니다.
---
.../impl/album_detail/component/AlbumDetailTopBar.kt | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/component/AlbumDetailTopBar.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/component/AlbumDetailTopBar.kt
index 193ee33e2..780b10473 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/component/AlbumDetailTopBar.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/album_detail/component/AlbumDetailTopBar.kt
@@ -11,10 +11,10 @@ import com.neki.android.feature.archive.impl.model.SelectMode
internal fun AlbumDetailTopBar(
title: String,
selectMode: SelectMode,
- onClickBack: () -> Unit,
- onClickSelect: () -> Unit,
- onClickCancel: () -> Unit,
modifier: Modifier = Modifier,
+ onClickBack: () -> Unit = {},
+ onClickSelect: () -> Unit = {},
+ onClickCancel: () -> Unit = {},
) {
BackTitleTextButtonTopBar(
modifier = modifier,
@@ -42,9 +42,6 @@ private fun AlbumDetailTopBarPreview() {
AlbumDetailTopBar(
title = "Album Title",
selectMode = SelectMode.DEFAULT,
- onClickBack = {},
- onClickSelect = {},
- onClickCancel = {},
)
}
}
@@ -56,9 +53,6 @@ private fun AlbumDetailTopBarSelectingPreview() {
AlbumDetailTopBar(
title = "Album Title",
selectMode = SelectMode.SELECTING,
- onClickBack = {},
- onClickSelect = {},
- onClickCancel = {},
)
}
}
From 1fcc6c46ffed85732290b96b0a80fd39ee1d633d Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Sat, 7 Feb 2026 11:31:49 +0900
Subject: [PATCH 20/24] =?UTF-8?q?[fix]=20#78:=20`SelectablePhotoItem`=20?=
=?UTF-8?q?=EC=BB=B4=ED=8F=AC=EC=A0=80=EB=B8=94=20=ED=8C=8C=EC=9D=BC?=
=?UTF-8?q?=EB=AA=85=20=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95=20=EB=B0=8F?=
=?UTF-8?q?=20radius=20=EA=B0=92=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
`SelcetablePhotoItem`의 파일명을 `SelectablePhotoItem`으로 수정하고, 선택된 사진 아이템의 테두리 radius 값을 12dp에서 8dp로 변경했습니다.
---
.../{SelcetablePhotoItem.kt => SelectablePhotoItem.kt} | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
rename feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/{SelcetablePhotoItem.kt => SelectablePhotoItem.kt} (96%)
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelcetablePhotoItem.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelectablePhotoItem.kt
similarity index 96%
rename from feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelcetablePhotoItem.kt
rename to feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelectablePhotoItem.kt
index d985a4041..f5ce6067b 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelcetablePhotoItem.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/component/SelectablePhotoItem.kt
@@ -42,9 +42,8 @@ internal fun SelectablePhotoItem(
Modifier.border(
width = 2.dp,
color = NekiTheme.colorScheme.primary400,
- shape = RoundedCornerShape(12.dp),
- ) else
- Modifier.clip(RoundedCornerShape(12.dp)),
+ shape = RoundedCornerShape(8.dp),
+ ) else Modifier.clip(RoundedCornerShape(8.dp)),
),
onClickItem = onClickItem,
)
From 9565c8edd57c8bafa04fb8b500c51c45c143d615 Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Sat, 7 Feb 2026 11:32:02 +0900
Subject: [PATCH 21/24] =?UTF-8?q?[refactor]=20#78:=20=EC=95=A8=EB=B2=94=20?=
=?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20Arrangement.?=
=?UTF-8?q?spacedBy(20.dp)=20=EC=86=8D=EC=84=B1=20=EC=A0=9C=EA=B1=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../android/feature/photo_upload/impl/album/UploadAlbumScreen.kt | 1 -
1 file changed, 1 deletion(-)
diff --git a/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumScreen.kt b/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumScreen.kt
index d55004bd5..2d84cdf7d 100644
--- a/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumScreen.kt
+++ b/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumScreen.kt
@@ -71,7 +71,6 @@ internal fun UploadAlbumScreen(
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(vertical = 8.dp),
- verticalArrangement = Arrangement.spacedBy(20.dp),
) {
item {
FavoriteAlbumRowComponent(album = uiState.favoriteAlbum)
From b233af27ec134c0d1e381c9906a98ca519773336 Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Sat, 7 Feb 2026 11:35:50 +0900
Subject: [PATCH 22/24] =?UTF-8?q?[refactor]=20#78:=20ItemOverlay=EC=97=90?=
=?UTF-8?q?=EC=84=9C=20photoGridBackground=20Modifier=20=EC=A0=9C=EA=B1=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
`photoGridBackground` Modifier 확장 함수를 제거하고, `ItemOverlay` 컴포저블 내부에서 `Modifier.background`를 직접 사용하도록 수정했습니다.
---
.../core/designsystem/modifier/Background.kt | 24 -------------------
.../android/core/ui/component/ItemOverlay.kt | 17 +++++++++++--
2 files changed, 15 insertions(+), 26 deletions(-)
diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
index 8f29c761a..626dbe6d8 100644
--- a/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
+++ b/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
@@ -1,38 +1,14 @@
package com.neki.android.core.designsystem.modifier
import androidx.compose.foundation.background
-import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.HazeStyle
import dev.chrisbanes.haze.HazeTint
import dev.chrisbanes.haze.hazeEffect
-/**
- * 사진, 포즈 컴포넌트에 적용되는 그라데이션 배경
- */
-fun Modifier.photoGridBackground(
- shape: Shape = RoundedCornerShape(8.dp),
-): Modifier = this
- .background(
- color = Color.Black.copy(alpha = 0.04f),
- shape = shape,
- )
- .background(
- brush = Brush.verticalGradient(
- colorStops = arrayOf(
- 0f to Color.Black.copy(alpha = 0.2f),
- 134f / 242f to Color.Black.copy(alpha = 0f),
- ),
- ),
- shape = shape,
- )
-
/**
* 블러 효과가 적용된 배경을 설정하는 Modifier 확장 함수
*
diff --git a/core/ui/src/main/java/com/neki/android/core/ui/component/ItemOverlay.kt b/core/ui/src/main/java/com/neki/android/core/ui/component/ItemOverlay.kt
index e4ef4e566..661f1104e 100644
--- a/core/ui/src/main/java/com/neki/android/core/ui/component/ItemOverlay.kt
+++ b/core/ui/src/main/java/com/neki/android/core/ui/component/ItemOverlay.kt
@@ -6,11 +6,11 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.dp
import com.neki.android.core.designsystem.ComponentPreview
-import com.neki.android.core.designsystem.modifier.photoGridBackground
import com.neki.android.core.designsystem.ui.theme.NekiTheme
@Composable
@@ -19,7 +19,20 @@ fun PhotoGridItemOverlay(
shape: Shape = RoundedCornerShape(8.dp),
) {
Box(
- modifier = modifier.photoGridBackground(shape = shape),
+ modifier = modifier
+ .background(
+ color = Color.Black.copy(alpha = 0.04f),
+ shape = shape,
+ )
+ .background(
+ brush = Brush.verticalGradient(
+ colorStops = arrayOf(
+ 0f to Color.Black.copy(alpha = 0.2f),
+ 134f / 242f to Color.Black.copy(alpha = 0f),
+ ),
+ ),
+ shape = shape,
+ ),
)
}
From 7545aa4747d1d411239b938ce85ba0f540e6074a Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Sat, 7 Feb 2026 11:37:26 +0900
Subject: [PATCH 23/24] =?UTF-8?q?[chore]=20#78:=20detekt=20=EB=A6=B0?=
=?UTF-8?q?=ED=8A=B8=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../feature/archive/impl/main/component/ArchiveMainTitleRow.kt | 2 --
.../feature/photo_upload/impl/album/UploadAlbumScreen.kt | 1 -
2 files changed, 3 deletions(-)
diff --git a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTitleRow.kt b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTitleRow.kt
index e6a2c9834..e3a790323 100644
--- a/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTitleRow.kt
+++ b/feature/archive/impl/src/main/kotlin/com/neki/android/feature/archive/impl/main/component/ArchiveMainTitleRow.kt
@@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
@@ -18,7 +17,6 @@ import com.neki.android.core.designsystem.ComponentPreview
import com.neki.android.core.designsystem.R
import com.neki.android.core.designsystem.button.NekiTextButton
import com.neki.android.core.designsystem.ui.theme.NekiTheme
-import com.neki.android.feature.archive.impl.const.ArchiveConst.ARCHIVE_ROW_TEXT_BUTTON_PADDING
@Composable
internal fun ArchiveMainTitleRow(
diff --git a/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumScreen.kt b/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumScreen.kt
index 2d84cdf7d..8153999c0 100644
--- a/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumScreen.kt
+++ b/feature/photo-upload/impl/src/main/java/com/neki/android/feature/photo_upload/impl/album/UploadAlbumScreen.kt
@@ -1,7 +1,6 @@
package com.neki.android.feature.photo_upload.impl.album
import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
From f9f49af7a9146b6f69eb76bbe0ad8c339395078d Mon Sep 17 00:00:00 2001
From: ikseong00 <127182222+ikseong00@users.noreply.github.com>
Date: Sat, 7 Feb 2026 13:29:26 +0900
Subject: [PATCH 24/24] =?UTF-8?q?[fix]=20#78:=20=EB=9E=9C=EB=8D=A4=20?=
=?UTF-8?q?=ED=8F=AC=EC=A6=88=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=ED=9D=90?=
=?UTF-8?q?=EB=A6=BC=20=ED=9A=A8=EA=B3=BC=20=EB=B0=8F=20=EA=B7=B8=EB=9D=BC?=
=?UTF-8?q?=EB=8D=B0=EC=9D=B4=EC=85=98=20=EB=B0=B0=EA=B2=BD=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
포즈피드 랜덤 아이템 UI에 그라데이션 배경과 흐림(blur) 효과를 적용했습니다.
---
.../neki/android/core/designsystem/modifier/Background.kt | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt b/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
index da2baa458..df3370121 100644
--- a/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
+++ b/core/designsystem/src/main/java/com/neki/android/core/designsystem/modifier/Background.kt
@@ -1,9 +1,14 @@
package com.neki.android.core.designsystem.modifier
import androidx.compose.foundation.background
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.HazeStyle
import dev.chrisbanes.haze.HazeTint