From 292a8c5c1f179727444568fef8a81b45d955c336 Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Fri, 31 Jul 2026 18:32:20 +0900
Subject: [PATCH 01/22] add mypage feature module
---
feature/mypage/.gitignore | 1 +
feature/mypage/build.gradle.kts | 31 +++++++++++++++++++
feature/mypage/consumer-rules.pro | 0
feature/mypage/proguard-rules.pro | 21 +++++++++++++
.../kikidan/mypage/ExampleInstrumentedTest.kt | 24 ++++++++++++++
feature/mypage/src/main/AndroidManifest.xml | 4 +++
.../com/kikidan/mypage/ExampleUnitTest.kt | 17 ++++++++++
settings.gradle.kts | 1 +
8 files changed, 99 insertions(+)
create mode 100644 feature/mypage/.gitignore
create mode 100644 feature/mypage/build.gradle.kts
create mode 100644 feature/mypage/consumer-rules.pro
create mode 100644 feature/mypage/proguard-rules.pro
create mode 100644 feature/mypage/src/androidTest/java/com/kikidan/mypage/ExampleInstrumentedTest.kt
create mode 100644 feature/mypage/src/main/AndroidManifest.xml
create mode 100644 feature/mypage/src/test/java/com/kikidan/mypage/ExampleUnitTest.kt
diff --git a/feature/mypage/.gitignore b/feature/mypage/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/feature/mypage/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/feature/mypage/build.gradle.kts b/feature/mypage/build.gradle.kts
new file mode 100644
index 0000000..d3660b9
--- /dev/null
+++ b/feature/mypage/build.gradle.kts
@@ -0,0 +1,31 @@
+plugins {
+ alias(libs.plugins.android.library)
+}
+
+android {
+ namespace = "com.kikidan.mypage"
+ compileSdk {
+ version = release(37)
+ }
+
+ defaultConfig {
+ minSdk = 26
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles("consumer-rules.pro")
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+
+}
+
+dependencies {
+ implementation(libs.androidx.appcompat)
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.material)
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.espresso.core)
+ androidTestImplementation(libs.androidx.junit)
+}
diff --git a/feature/mypage/consumer-rules.pro b/feature/mypage/consumer-rules.pro
new file mode 100644
index 0000000..e69de29
diff --git a/feature/mypage/proguard-rules.pro b/feature/mypage/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/feature/mypage/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/feature/mypage/src/androidTest/java/com/kikidan/mypage/ExampleInstrumentedTest.kt b/feature/mypage/src/androidTest/java/com/kikidan/mypage/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..babd854
--- /dev/null
+++ b/feature/mypage/src/androidTest/java/com/kikidan/mypage/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.kikidan.mypage
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("com.kikidan.mypage.test", appContext.packageName)
+ }
+}
diff --git a/feature/mypage/src/main/AndroidManifest.xml b/feature/mypage/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8bdb7e1
--- /dev/null
+++ b/feature/mypage/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/feature/mypage/src/test/java/com/kikidan/mypage/ExampleUnitTest.kt b/feature/mypage/src/test/java/com/kikidan/mypage/ExampleUnitTest.kt
new file mode 100644
index 0000000..a62a916
--- /dev/null
+++ b/feature/mypage/src/test/java/com/kikidan/mypage/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.kikidan.mypage
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index c4176d5..48a0d32 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -36,3 +36,4 @@ include(":core:data-remote")
include(":core:navigation")
include(":feature:login")
include(":core:designsystem")
+include(":feature:mypage")
From 564c31c7b06ed9d84bf28978673398956119b41a Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Fri, 31 Jul 2026 18:33:07 +0900
Subject: [PATCH 02/22] apply feature convention plugin to mypage module
---
feature/mypage/build.gradle.kts | 26 +-------------------------
1 file changed, 1 insertion(+), 25 deletions(-)
diff --git a/feature/mypage/build.gradle.kts b/feature/mypage/build.gradle.kts
index d3660b9..f0d4cbf 100644
--- a/feature/mypage/build.gradle.kts
+++ b/feature/mypage/build.gradle.kts
@@ -1,31 +1,7 @@
plugins {
- alias(libs.plugins.android.library)
+ id("todakun.feature")
}
android {
namespace = "com.kikidan.mypage"
- compileSdk {
- version = release(37)
- }
-
- defaultConfig {
- minSdk = 26
-
- testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles("consumer-rules.pro")
- }
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_11
- targetCompatibility = JavaVersion.VERSION_11
- }
-
-}
-
-dependencies {
- implementation(libs.androidx.appcompat)
- implementation(libs.androidx.core.ktx)
- implementation(libs.material)
- testImplementation(libs.junit)
- androidTestImplementation(libs.androidx.espresso.core)
- androidTestImplementation(libs.androidx.junit)
}
From b9ff15baf7c4010ceed1f655a2f71541ffa8663c Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Fri, 31 Jul 2026 19:13:53 +0900
Subject: [PATCH 03/22] =?UTF-8?q?[Feat]=20=EB=A7=88=EC=9D=B4=ED=8E=98?=
=?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=99=88=20ViewModel=20=EB=B0=8F=20UI=20?=
=?UTF-8?q?=EC=83=81=ED=83=9C=20=EA=B5=AC=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat: add MyPageHomeViewModel using Orbit MVI
* feat: define MyPageHomeUiState, MyPageHomeUiModel, and MyPageHomeSideEffect
---
.../mypage/home/MyPageHomeViewModel.kt | 44 +++++++++++++++++++
.../mypage/home/model/MyPageHomeSideEffect.kt | 3 ++
.../mypage/home/model/MyPageHomeUiModel.kt | 11 +++++
.../mypage/home/model/MyPageHomeUiState.kt | 7 +++
4 files changed, 65 insertions(+)
create mode 100644 feature/mypage/src/main/java/com/kikidan/mypage/home/MyPageHomeViewModel.kt
create mode 100644 feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeSideEffect.kt
create mode 100644 feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiModel.kt
create mode 100644 feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiState.kt
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/MyPageHomeViewModel.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/MyPageHomeViewModel.kt
new file mode 100644
index 0000000..dd5d019
--- /dev/null
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/MyPageHomeViewModel.kt
@@ -0,0 +1,44 @@
+package com.kikidan.mypage.home
+
+import androidx.lifecycle.ViewModel
+import com.kikidan.domain.usecase.mypage.GetMyPageInfoUseCase
+import com.kikidan.mypage.home.model.MyPageHomeSideEffect
+import com.kikidan.mypage.home.model.MyPageHomeUiModel
+import com.kikidan.mypage.home.model.MyPageHomeUiState
+import dagger.hilt.android.lifecycle.HiltViewModel
+import org.orbitmvi.orbit.Container
+import org.orbitmvi.orbit.ContainerHost
+import org.orbitmvi.orbit.syntax.Syntax
+import org.orbitmvi.orbit.viewmodel.container
+import javax.inject.Inject
+
+
+
+private typealias MyPageHomeSyntax = Syntax
+
+@HiltViewModel
+class MyPageHomeViewModel @Inject constructor(
+ private val getMyPageInfoUseCase: GetMyPageInfoUseCase,
+) : ViewModel(), ContainerHost {
+ override val container: Container = container(MyPageHomeUiState.Loading) {
+ repeatOnSubscription {
+ loadMyPageInfo()
+ }
+ }
+
+ fun loadMyPageInfo() = intent {
+ getMyPageInfoUseCase()
+ .onSuccess { info ->
+ reduce { MyPageHomeUiState.Success(MyPageHomeUiModel(info.user, info.sajuPalja)) }
+ }
+ .onFailure { throwable ->
+ reduce { MyPageHomeUiState.Fail(throwable) }
+ }
+ }
+
+ private suspend fun MyPageHomeSyntax.reduceIfSuccess(reducer: MyPageHomeUiState.Success.() -> MyPageHomeUiState) {
+ reduce {
+ (state as? MyPageHomeUiState.Success)?.reducer() ?: state
+ }
+ }
+}
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeSideEffect.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeSideEffect.kt
new file mode 100644
index 0000000..66236d5
--- /dev/null
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeSideEffect.kt
@@ -0,0 +1,3 @@
+package com.kikidan.mypage.home.model
+
+sealed interface MyPageHomeSideEffect
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiModel.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiModel.kt
new file mode 100644
index 0000000..245c883
--- /dev/null
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiModel.kt
@@ -0,0 +1,11 @@
+package com.kikidan.mypage.home.model
+
+import com.kikidan.domain.model.saju.SajuPalja
+import com.kikidan.domain.model.user.User
+
+
+
+data class MyPageHomeUiModel(
+ val user: User,
+ val sajuPalja: SajuPalja
+)
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiState.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiState.kt
new file mode 100644
index 0000000..7a9d91d
--- /dev/null
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiState.kt
@@ -0,0 +1,7 @@
+package com.kikidan.mypage.home.model
+
+sealed interface MyPageHomeUiState {
+ data object Loading : MyPageHomeUiState
+ data class Success(val model: MyPageHomeUiModel) : MyPageHomeUiState
+ data class Fail(val e: Throwable) : MyPageHomeUiState
+}
From e269132504ede387e0109ffad835d5f57fb4ea77 Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Fri, 31 Jul 2026 19:35:56 +0900
Subject: [PATCH 04/22] =?UTF-8?q?[Chore]=20MyPage=20feature=20=EB=AA=A8?=
=?UTF-8?q?=EB=93=88=20=EC=BD=94=EB=93=9C=20=EC=8A=A4=ED=83=80=EC=9D=BC=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
* chore: feature:mypage 모듈 내 코드 포맷팅 및 임포트 정리
* chore: MyPageHomeViewModel 및 UI State 클래스 스타일 수정
---
.../kikidan/mypage/ExampleInstrumentedTest.kt | 6 +--
.../mypage/home/MyPageHomeViewModel.kt | 48 ++++++++++---------
.../mypage/home/model/MyPageHomeUiModel.kt | 4 +-
.../mypage/home/model/MyPageHomeUiState.kt | 10 +++-
.../com/kikidan/mypage/ExampleUnitTest.kt | 3 +-
5 files changed, 38 insertions(+), 33 deletions(-)
diff --git a/feature/mypage/src/androidTest/java/com/kikidan/mypage/ExampleInstrumentedTest.kt b/feature/mypage/src/androidTest/java/com/kikidan/mypage/ExampleInstrumentedTest.kt
index babd854..437840d 100644
--- a/feature/mypage/src/androidTest/java/com/kikidan/mypage/ExampleInstrumentedTest.kt
+++ b/feature/mypage/src/androidTest/java/com/kikidan/mypage/ExampleInstrumentedTest.kt
@@ -1,13 +1,11 @@
package com.kikidan.mypage
-import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
-
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.Assert.*
-
/**
* Instrumented test, which will execute on an Android device.
*
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/MyPageHomeViewModel.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/MyPageHomeViewModel.kt
index dd5d019..a39fa04 100644
--- a/feature/mypage/src/main/java/com/kikidan/mypage/home/MyPageHomeViewModel.kt
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/MyPageHomeViewModel.kt
@@ -12,33 +12,37 @@ import org.orbitmvi.orbit.syntax.Syntax
import org.orbitmvi.orbit.viewmodel.container
import javax.inject.Inject
-
-
private typealias MyPageHomeSyntax = Syntax
@HiltViewModel
-class MyPageHomeViewModel @Inject constructor(
- private val getMyPageInfoUseCase: GetMyPageInfoUseCase,
-) : ViewModel(), ContainerHost {
- override val container: Container = container(MyPageHomeUiState.Loading) {
- repeatOnSubscription {
- loadMyPageInfo()
- }
- }
-
- fun loadMyPageInfo() = intent {
- getMyPageInfoUseCase()
- .onSuccess { info ->
- reduce { MyPageHomeUiState.Success(MyPageHomeUiModel(info.user, info.sajuPalja)) }
+class MyPageHomeViewModel
+ @Inject
+ constructor(
+ private val getMyPageInfoUseCase: GetMyPageInfoUseCase,
+ ) : ViewModel(),
+ ContainerHost {
+ override val container: Container =
+ container(MyPageHomeUiState.Loading) {
+ repeatOnSubscription {
+ loadMyPageInfo()
+ }
}
- .onFailure { throwable ->
- reduce { MyPageHomeUiState.Fail(throwable) }
+
+ fun loadMyPageInfo() =
+ intent {
+ getMyPageInfoUseCase()
+ .onSuccess { info ->
+ reduce { MyPageHomeUiState.Success(MyPageHomeUiModel(info.user, info.sajuPalja)) }
+ }.onFailure { throwable ->
+ reduce { MyPageHomeUiState.Fail(throwable) }
+ }
}
- }
- private suspend fun MyPageHomeSyntax.reduceIfSuccess(reducer: MyPageHomeUiState.Success.() -> MyPageHomeUiState) {
- reduce {
- (state as? MyPageHomeUiState.Success)?.reducer() ?: state
+ private suspend fun MyPageHomeSyntax.reduceIfSuccess(
+ reducer: MyPageHomeUiState.Success.() -> MyPageHomeUiState,
+ ) {
+ reduce {
+ (state as? MyPageHomeUiState.Success)?.reducer() ?: state
+ }
}
}
-}
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiModel.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiModel.kt
index 245c883..b9fa8b1 100644
--- a/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiModel.kt
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiModel.kt
@@ -3,9 +3,7 @@ package com.kikidan.mypage.home.model
import com.kikidan.domain.model.saju.SajuPalja
import com.kikidan.domain.model.user.User
-
-
data class MyPageHomeUiModel(
val user: User,
- val sajuPalja: SajuPalja
+ val sajuPalja: SajuPalja,
)
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiState.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiState.kt
index 7a9d91d..f5f510e 100644
--- a/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiState.kt
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiState.kt
@@ -2,6 +2,12 @@ package com.kikidan.mypage.home.model
sealed interface MyPageHomeUiState {
data object Loading : MyPageHomeUiState
- data class Success(val model: MyPageHomeUiModel) : MyPageHomeUiState
- data class Fail(val e: Throwable) : MyPageHomeUiState
+
+ data class Success(
+ val model: MyPageHomeUiModel,
+ ) : MyPageHomeUiState
+
+ data class Fail(
+ val e: Throwable,
+ ) : MyPageHomeUiState
}
diff --git a/feature/mypage/src/test/java/com/kikidan/mypage/ExampleUnitTest.kt b/feature/mypage/src/test/java/com/kikidan/mypage/ExampleUnitTest.kt
index a62a916..c74490d 100644
--- a/feature/mypage/src/test/java/com/kikidan/mypage/ExampleUnitTest.kt
+++ b/feature/mypage/src/test/java/com/kikidan/mypage/ExampleUnitTest.kt
@@ -1,8 +1,7 @@
package com.kikidan.mypage
-import org.junit.Test
-
import org.junit.Assert.*
+import org.junit.Test
/**
* Example local unit test, which will execute on the development machine (host).
From ed8fa0865a231ae4f79a542e8dda30d93fb5875f Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Fri, 31 Jul 2026 19:38:45 +0900
Subject: [PATCH 05/22] remove example tests from mypage module
---
.../kikidan/mypage/ExampleInstrumentedTest.kt | 22 -------------------
.../com/kikidan/mypage/ExampleUnitTest.kt | 16 --------------
2 files changed, 38 deletions(-)
delete mode 100644 feature/mypage/src/androidTest/java/com/kikidan/mypage/ExampleInstrumentedTest.kt
delete mode 100644 feature/mypage/src/test/java/com/kikidan/mypage/ExampleUnitTest.kt
diff --git a/feature/mypage/src/androidTest/java/com/kikidan/mypage/ExampleInstrumentedTest.kt b/feature/mypage/src/androidTest/java/com/kikidan/mypage/ExampleInstrumentedTest.kt
deleted file mode 100644
index 437840d..0000000
--- a/feature/mypage/src/androidTest/java/com/kikidan/mypage/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.kikidan.mypage
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import org.junit.Assert.*
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-@RunWith(AndroidJUnit4::class)
-class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.kikidan.mypage.test", appContext.packageName)
- }
-}
diff --git a/feature/mypage/src/test/java/com/kikidan/mypage/ExampleUnitTest.kt b/feature/mypage/src/test/java/com/kikidan/mypage/ExampleUnitTest.kt
deleted file mode 100644
index c74490d..0000000
--- a/feature/mypage/src/test/java/com/kikidan/mypage/ExampleUnitTest.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.kikidan.mypage
-
-import org.junit.Assert.*
-import org.junit.Test
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
From 1a79909332c4eca09ddc51f2f1aa57e61f5c4207 Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Sat, 1 Aug 2026 14:28:41 +0900
Subject: [PATCH 06/22] register feature convention plugin in version catalog
---
feature/mypage/build.gradle.kts | 3 ++-
gradle/libs.versions.toml | 1 +
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/feature/mypage/build.gradle.kts b/feature/mypage/build.gradle.kts
index f0d4cbf..c32b8bb 100644
--- a/feature/mypage/build.gradle.kts
+++ b/feature/mypage/build.gradle.kts
@@ -1,7 +1,8 @@
plugins {
- id("todakun.feature")
+ alias(libs.plugins.todakun.feature)
}
android {
namespace = "com.kikidan.mypage"
}
+
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 81712ff..8c67c8a 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -85,4 +85,5 @@ ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlintGradlePlugin" }
todakun-android-signing = { id = "todakun.android.signing" }
+todakun-feature = { id = "todakun.feature" }
From ff5beae38eb459b479b72eab793118e9bd029640 Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Sat, 1 Aug 2026 15:34:12 +0900
Subject: [PATCH 07/22] feat: add ic_manage_saju_info icon
---
.../main/res/drawable/ic_manage_saju_info.xml | 45 +++++++++++++++++++
1 file changed, 45 insertions(+)
create mode 100644 core/designsystem/src/main/res/drawable/ic_manage_saju_info.xml
diff --git a/core/designsystem/src/main/res/drawable/ic_manage_saju_info.xml b/core/designsystem/src/main/res/drawable/ic_manage_saju_info.xml
new file mode 100644
index 0000000..5886812
--- /dev/null
+++ b/core/designsystem/src/main/res/drawable/ic_manage_saju_info.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
From c0bc03feabd4acd0970dcb6a12d2c9b43c0c2a55 Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Sat, 1 Aug 2026 15:38:05 +0900
Subject: [PATCH 08/22] add ic_setting drawable
---
.../src/main/res/drawable/ic_setting.xml | 29 +++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 core/designsystem/src/main/res/drawable/ic_setting.xml
diff --git a/core/designsystem/src/main/res/drawable/ic_setting.xml b/core/designsystem/src/main/res/drawable/ic_setting.xml
new file mode 100644
index 0000000..2a6fafe
--- /dev/null
+++ b/core/designsystem/src/main/res/drawable/ic_setting.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
From 1bf24ac70bbe66480f054730dccdb54a5b87ba4c Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Sat, 1 Aug 2026 15:40:18 +0900
Subject: [PATCH 09/22] add mail icon
---
.../src/main/res/drawable/ic_mail.xml | 33 +++++++++++++++++++
1 file changed, 33 insertions(+)
create mode 100644 core/designsystem/src/main/res/drawable/ic_mail.xml
diff --git a/core/designsystem/src/main/res/drawable/ic_mail.xml b/core/designsystem/src/main/res/drawable/ic_mail.xml
new file mode 100644
index 0000000..152a94a
--- /dev/null
+++ b/core/designsystem/src/main/res/drawable/ic_mail.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
From 1dc9b6f89bf2824ebd6eb28fdbc0f9d10e92137c Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Sat, 1 Aug 2026 15:41:14 +0900
Subject: [PATCH 10/22] add ic_logout icon
---
.../src/main/res/drawable/ic_logout.xml | 39 +++++++++++++++++++
1 file changed, 39 insertions(+)
create mode 100644 core/designsystem/src/main/res/drawable/ic_logout.xml
diff --git a/core/designsystem/src/main/res/drawable/ic_logout.xml b/core/designsystem/src/main/res/drawable/ic_logout.xml
new file mode 100644
index 0000000..6ca2812
--- /dev/null
+++ b/core/designsystem/src/main/res/drawable/ic_logout.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
From e6d3e05331e5ec1d4526f95136310a6d92f433f0 Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Sat, 1 Aug 2026 16:06:22 +0900
Subject: [PATCH 11/22] add ic_chevron_right drawable
---
.../main/res/drawable/ic_chevron_right.xml | 24 +++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 core/designsystem/src/main/res/drawable/ic_chevron_right.xml
diff --git a/core/designsystem/src/main/res/drawable/ic_chevron_right.xml b/core/designsystem/src/main/res/drawable/ic_chevron_right.xml
new file mode 100644
index 0000000..2b42b44
--- /dev/null
+++ b/core/designsystem/src/main/res/drawable/ic_chevron_right.xml
@@ -0,0 +1,24 @@
+
+
+
+
From 501ccf20aadc7b562cb140ad829c4855894e140d Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Mon, 3 Aug 2026 23:30:21 +0900
Subject: [PATCH 12/22] add MyPage and common string resources
---
.../src/main/res/values/strings.xml | 21 +++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/core/designsystem/src/main/res/values/strings.xml b/core/designsystem/src/main/res/values/strings.xml
index c9eade1..1670ad0 100644
--- a/core/designsystem/src/main/res/values/strings.xml
+++ b/core/designsystem/src/main/res/values/strings.xml
@@ -47,4 +47,25 @@
토닥이
행운 액션
마이
+
+
+ My page
+ 사주 정보 관리
+ 알림 설정
+ 앱 설정
+ 1:1 문의
+ 로그아웃
+ 앱 버전
+ v1.0.0 (최신 버전)
+ 만세력 보기
+ 수정
+
+
+ ·
+ 남성
+ 여성
+ 양력
+ 음력
+ %1$d시 %2$02d분
+ -%1$s, %2$s
From 4509c41ef5f081d20e6ebfce1d929b020ae2ec45 Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Mon, 3 Aug 2026 23:30:42 +0900
Subject: [PATCH 13/22] =?UTF-8?q?[Domain]=20User=20=EB=AA=A8=EB=8D=B8=20?=
=?UTF-8?q?=EB=82=B4=20name=20=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat: User 모델에 name 프로퍼티 추가
* feat: FakeUserRepository에 테스트용 이름 반영
---
.../main/java/com/kikidan/data/repository/FakeUserRepository.kt | 1 +
core/domain/src/main/java/com/kikidan/domain/model/user/User.kt | 1 +
2 files changed, 2 insertions(+)
diff --git a/core/data/src/main/java/com/kikidan/data/repository/FakeUserRepository.kt b/core/data/src/main/java/com/kikidan/data/repository/FakeUserRepository.kt
index f815cc1..c7d46a3 100644
--- a/core/data/src/main/java/com/kikidan/data/repository/FakeUserRepository.kt
+++ b/core/data/src/main/java/com/kikidan/data/repository/FakeUserRepository.kt
@@ -16,6 +16,7 @@ class FakeUserRepository
Result.success(
User(
id = "fake-user-id",
+ name = "토닥이",
gender = Gender.FEMALE,
birth =
Birth(
diff --git a/core/domain/src/main/java/com/kikidan/domain/model/user/User.kt b/core/domain/src/main/java/com/kikidan/domain/model/user/User.kt
index 8588949..760f193 100644
--- a/core/domain/src/main/java/com/kikidan/domain/model/user/User.kt
+++ b/core/domain/src/main/java/com/kikidan/domain/model/user/User.kt
@@ -5,6 +5,7 @@ import java.time.LocalTime
data class User(
val id: String,
+ val name: String,
val gender: Gender,
val birth: Birth,
)
From 01a15cf159476f140663965ac32131175f9a1d69 Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Mon, 3 Aug 2026 23:31:01 +0900
Subject: [PATCH 14/22] [Feat] Implement MyPage home screen UI
* feat: add MyPageHomeRoute and MyPageHomeScreen to handle UI states
* feat: implement MyPageMenuItem and SajuCard reusable components
* feat: add MyPageHomeHeader for the MyPage landing screen
* feat: implement profile card and menu list layout in MyPageHomeScreen
---
.../kikidan/mypage/home/ui/MyPageHomeRoute.kt | 18 ++
.../mypage/home/ui/MyPageHomeScreen.kt | 211 ++++++++++++++++++
.../home/ui/component/MyPageHomeHeader.kt | 23 ++
.../home/ui/component/MyPageMenuItem.kt | 62 +++++
.../mypage/home/ui/component/SajuCard.kt | 77 +++++++
5 files changed, 391 insertions(+)
create mode 100644 feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeRoute.kt
create mode 100644 feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt
create mode 100644 feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/MyPageHomeHeader.kt
create mode 100644 feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/MyPageMenuItem.kt
create mode 100644 feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuCard.kt
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeRoute.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeRoute.kt
new file mode 100644
index 0000000..b34d4ec
--- /dev/null
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeRoute.kt
@@ -0,0 +1,18 @@
+package com.kikidan.mypage.home.ui
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.hilt.navigation.compose.hiltViewModel
+import com.kikidan.mypage.home.MyPageHomeViewModel
+import org.orbitmvi.orbit.compose.collectAsState
+
+@Composable
+fun MyPageHomeRoute(
+ viewModel: MyPageHomeViewModel = hiltViewModel(),
+) {
+ val uiState by viewModel.collectAsState()
+
+ MyPageHomeScreen(
+ uiState = uiState,
+ )
+}
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt
new file mode 100644
index 0000000..146d053
--- /dev/null
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt
@@ -0,0 +1,211 @@
+package com.kikidan.mypage.home.ui
+
+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.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.kikidan.designsystem.R
+import com.kikidan.designsystem.component.TodakunDivider
+import com.kikidan.designsystem.component.TodakunDividerType
+import com.kikidan.designsystem.theme.TodakunColor
+import com.kikidan.designsystem.theme.TodakunTheme
+import com.kikidan.designsystem.theme.TodakunTypography
+import com.kikidan.domain.model.saju.CheonGan
+import com.kikidan.domain.model.saju.JiJi
+import com.kikidan.domain.model.saju.SajuPalja
+import com.kikidan.domain.model.saju.SajuPillar
+import com.kikidan.domain.model.user.Birth
+import com.kikidan.domain.model.user.BirthTime
+import com.kikidan.domain.model.user.DateType
+import com.kikidan.domain.model.user.Gender
+import com.kikidan.domain.model.user.User
+import com.kikidan.mypage.home.model.MyPageHomeUiModel
+import com.kikidan.mypage.home.model.MyPageHomeUiState
+import com.kikidan.mypage.home.ui.component.MyPageHomeHeader
+import com.kikidan.mypage.home.ui.component.MyPageMenuItem
+import com.kikidan.mypage.home.ui.component.ProfileCard
+import java.time.LocalDate
+
+@Composable
+fun MyPageHomeScreen(
+ uiState: MyPageHomeUiState,
+ modifier: Modifier = Modifier,
+ onEditClick: () -> Unit = {},
+ onViewMansaeryeokClick: () -> Unit = {},
+ onSajuInfoClick: () -> Unit = {},
+ onNotificationSettingClick: () -> Unit = {},
+ onAppSettingClick: () -> Unit = {},
+ onInquiryClick: () -> Unit = {},
+ onLogoutClick: () -> Unit = {},
+) {
+ Column(
+ modifier =
+ modifier
+ .fillMaxSize()
+ .background(TodakunColor.white),
+ ) {
+ MyPageHomeHeader(modifier = Modifier.fillMaxWidth())
+
+ when (uiState) {
+ is MyPageHomeUiState.Loading -> MyPageHomeLoading(modifier = Modifier.weight(1f))
+ is MyPageHomeUiState.Fail -> MyPageHomeError(modifier = Modifier.weight(1f))
+ is MyPageHomeUiState.Success ->
+ MyPageHomeContent(
+ model = uiState.model,
+ onEditClick = onEditClick,
+ onViewMansaeryeokClick = onViewMansaeryeokClick,
+ onSajuInfoClick = onSajuInfoClick,
+ onNotificationSettingClick = onNotificationSettingClick,
+ onAppSettingClick = onAppSettingClick,
+ onInquiryClick = onInquiryClick,
+ onLogoutClick = onLogoutClick,
+ modifier = Modifier.weight(1f),
+ )
+ }
+ }
+}
+
+@Composable
+private fun MyPageHomeLoading(modifier: Modifier = Modifier) {
+ Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center){
+
+ }
+}
+
+@Composable
+private fun MyPageHomeError(modifier: Modifier = Modifier) {
+ Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
+
+ }
+}
+
+@Composable
+private fun MyPageHomeContent(
+ model: MyPageHomeUiModel,
+ onEditClick: () -> Unit,
+ onViewMansaeryeokClick: () -> Unit,
+ onSajuInfoClick: () -> Unit,
+ onNotificationSettingClick: () -> Unit,
+ onAppSettingClick: () -> Unit,
+ onInquiryClick: () -> Unit,
+ onLogoutClick: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ Column(modifier = modifier.fillMaxSize()) {
+ ProfileCard(
+ user = model.user,
+ sajuPalja = model.sajuPalja,
+ onEditClick = onEditClick,
+ onViewMansaeryeokClick = onViewMansaeryeokClick,
+ modifier = Modifier.padding(horizontal = 20.dp),
+ )
+
+ Spacer(modifier = Modifier.height(24.dp))
+ TodakunDivider(type = TodakunDividerType.Section)
+
+ MyPageMenuItem(
+ painter = painterResource(R.drawable.ic_manage_saju_info),
+ label = stringResource(R.string.mypage_menu_saju_info),
+ onClick = onSajuInfoClick,
+ modifier = Modifier.padding(horizontal = 20.dp),
+ )
+ MyPageMenuItem(
+ painter = painterResource(R.drawable.ic_bell),
+ label = stringResource(R.string.mypage_menu_notification_setting),
+ onClick = onNotificationSettingClick,
+ modifier = Modifier.padding(horizontal = 20.dp),
+ )
+ MyPageMenuItem(
+ painter = painterResource(R.drawable.ic_setting),
+ label = stringResource(R.string.mypage_menu_app_setting),
+ onClick = onAppSettingClick,
+ modifier = Modifier.padding(horizontal = 20.dp),
+ )
+ MyPageMenuItem(
+ painter = painterResource(R.drawable.ic_mail),
+ label = stringResource(R.string.mypage_menu_inquiry),
+ onClick = onInquiryClick,
+ modifier = Modifier.padding(horizontal = 20.dp),
+ )
+ MyPageMenuItem(
+ painter = painterResource(R.drawable.ic_logout),
+ label = stringResource(R.string.mypage_menu_logout),
+ onClick = onLogoutClick,
+ showChevron = false,
+ textColor = TodakunColor.gray600,
+ iconTint = TodakunColor.gray500,
+ modifier = Modifier.padding(horizontal = 20.dp),
+ )
+
+ Spacer(modifier = Modifier.weight(1f))
+
+ Row(
+ modifier =
+ Modifier
+ .fillMaxWidth()
+ .padding(vertical = 20.dp),
+ horizontalArrangement = Arrangement.Center,
+ ) {
+ Text(
+ text = stringResource(R.string.mypage_app_version_label),
+ style = TodakunTypography.caption1Regular,
+ color = TodakunColor.gray400,
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ Text(
+ text = stringResource(R.string.mypage_app_version_value),
+ style = TodakunTypography.caption1Regular,
+ color = TodakunColor.gray400,
+ )
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun MyPageHomeScreenPreview() {
+ TodakunTheme {
+ MyPageHomeScreen(
+ uiState =
+ MyPageHomeUiState.Success(
+ MyPageHomeUiModel(
+ user =
+ User(
+ id = "preview",
+ name = "토닥이",
+ gender = Gender.FEMALE,
+ birth =
+ Birth(
+ dateType = DateType.SOLAR,
+ date = LocalDate.of(1999, 2, 13),
+ time = BirthTime.SIN,
+ ),
+ ),
+ sajuPalja =
+ SajuPalja(
+ yearPillar = SajuPillar(CheonGan.SIN, JiJi.MI),
+ monthPillar = SajuPillar(CheonGan.GI, JiJi.SA),
+ dayPillar = SajuPillar(CheonGan.GYE, JiJi.MYO),
+ hourPillar = SajuPillar(CheonGan.JEONG, JiJi.CHUK),
+ ),
+ ),
+ ),
+ )
+ }
+}
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/MyPageHomeHeader.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/MyPageHomeHeader.kt
new file mode 100644
index 0000000..eb53971
--- /dev/null
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/MyPageHomeHeader.kt
@@ -0,0 +1,23 @@
+package com.kikidan.mypage.home.ui.component
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import com.kikidan.designsystem.R
+import com.kikidan.designsystem.theme.TodakunColor
+import com.kikidan.designsystem.theme.TodakunTypography
+
+@Composable
+internal fun MyPageHomeHeader(
+ modifier: Modifier = Modifier,
+) {
+ Text(
+ text = stringResource(R.string.mypage_home_title),
+ style = TodakunTypography.heading4Bold,
+ color = TodakunColor.black,
+ modifier = modifier.padding(horizontal = 20.dp, vertical = 16.dp),
+ )
+}
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/MyPageMenuItem.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/MyPageMenuItem.kt
new file mode 100644
index 0000000..b8b4ec4
--- /dev/null
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/MyPageMenuItem.kt
@@ -0,0 +1,62 @@
+package com.kikidan.mypage.home.ui.component
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+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.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.painter.Painter
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import com.kikidan.designsystem.R
+import com.kikidan.designsystem.theme.TodakunColor
+import com.kikidan.designsystem.theme.TodakunTypography
+
+@Composable
+internal fun MyPageMenuItem(
+ painter: Painter,
+ label: String,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ iconTint: Color = TodakunColor.gray975,
+ textColor: Color = TodakunColor.black,
+ showChevron: Boolean = true,
+) {
+ Row(
+ modifier =
+ modifier
+ .fillMaxWidth()
+ .clickable(onClick = onClick)
+ .padding(vertical = 16.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ painter = painter,
+ contentDescription = null,
+ tint = iconTint,
+ modifier = Modifier.size(20.dp),
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ Text(
+ text = label,
+ style = TodakunTypography.body2Medium,
+ color = textColor,
+ modifier = Modifier.weight(1f),
+ )
+ if (showChevron) {
+ Icon(
+ painter = painterResource(R.drawable.ic_chevron_right),
+ contentDescription = null,
+ tint = TodakunColor.gray400,
+ )
+ }
+ }
+}
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuCard.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuCard.kt
new file mode 100644
index 0000000..7c22a98
--- /dev/null
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuCard.kt
@@ -0,0 +1,77 @@
+package com.kikidan.mypage.home.ui.component
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.padding
+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.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.kikidan.designsystem.R
+import com.kikidan.designsystem.theme.TodakunColor
+import com.kikidan.designsystem.theme.TodakunTheme
+import com.kikidan.designsystem.theme.TodakunTypography
+import com.kikidan.domain.model.saju.Ohaeng
+
+@Composable
+internal fun SajuCard(
+ hanja: String,
+ reading: String,
+ ohaeng: Ohaeng,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ modifier =
+ modifier
+ .aspectRatio(1f)
+ .clip(RoundedCornerShape(12.dp))
+ .background(ohaeng.containerColor())
+ .padding(horizontal = 6.dp, vertical = 9.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center,
+ ) {
+ Text(
+ text = hanja,
+ style = TodakunTypography.body1Bold,
+ color = TodakunColor.gray975
+ )
+ Text(
+ text = stringResource(R.string.saju_card_label_format, reading, ohaeng.hanja),
+ style = TodakunTypography.caption3Regular,
+ color = TodakunColor.gray975
+ )
+ }
+}
+
+private fun Ohaeng.containerColor(): Color =
+ when (this) {
+ Ohaeng.MOK -> TodakunColor.teal200
+ Ohaeng.HWA -> TodakunColor.red200
+ Ohaeng.TO -> TodakunColor.orange200
+ Ohaeng.GEUM -> TodakunColor.coolGray300
+ Ohaeng.SU -> TodakunColor.sky200
+ }
+
+@Preview(showBackground = true)
+@Composable
+private fun SajuCardPreview() {
+ TodakunTheme {
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ SajuCard(hanja = "辛", reading = "신", ohaeng = Ohaeng.GEUM, modifier = Modifier.width(72.dp))
+ SajuCard(hanja = "己", reading = "기", ohaeng = Ohaeng.TO, modifier = Modifier.width(72.dp))
+ SajuCard(hanja = "癸", reading = "계", ohaeng = Ohaeng.SU, modifier = Modifier.width(72.dp))
+ SajuCard(hanja = "丁", reading = "정", ohaeng = Ohaeng.HWA, modifier = Modifier.width(72.dp))
+ SajuCard(hanja = "卯", reading = "묘", ohaeng = Ohaeng.MOK, modifier = Modifier.width(72.dp))
+ }
+ }
+}
From a2bb6bb5d282b5fe49c1a90cf4a4c2c2aa8212ac Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Mon, 3 Aug 2026 23:50:12 +0900
Subject: [PATCH 15/22] implement MyPage home UI components and refactor menu
logic
* feat: add `MyPageMenuType` enum to manage menu resources and styles
* feat: add `ProfileCard` and `SajuPaljaGrid` UI components to display user information and Saju data
* refactor: update `MyPageHomeScreen` to render menu items dynamically using `MyPageMenuType`
* refactor: simplify navigation callbacks in `MyPageHomeScreen` by using a single menu click listener
---
.../mypage/home/model/MyPageMenuType.kt | 27 +++
.../mypage/home/ui/MyPageHomeScreen.kt | 63 ++----
.../mypage/home/ui/component/ProfileCard.kt | 212 ++++++++++++++++++
.../mypage/home/ui/component/SajuPaljaGrid.kt | 76 +++++++
4 files changed, 330 insertions(+), 48 deletions(-)
create mode 100644 feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageMenuType.kt
create mode 100644 feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/ProfileCard.kt
create mode 100644 feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuPaljaGrid.kt
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageMenuType.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageMenuType.kt
new file mode 100644
index 0000000..a7ae68b
--- /dev/null
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageMenuType.kt
@@ -0,0 +1,27 @@
+package com.kikidan.mypage.home.model
+
+import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+import androidx.compose.ui.graphics.Color
+import com.kikidan.designsystem.R
+import com.kikidan.designsystem.theme.TodakunColor
+
+enum class MyPageMenuType(
+ @DrawableRes val iconRes: Int,
+ @StringRes val labelRes: Int,
+ val showChevron: Boolean = true,
+ val iconTint: Color = TodakunColor.gray975,
+ val textColor: Color = TodakunColor.black,
+) {
+ SAJU_INFO(iconRes = R.drawable.ic_manage_saju_info, labelRes = R.string.mypage_menu_saju_info),
+ NOTIFICATION_SETTING(iconRes = R.drawable.ic_bell, labelRes = R.string.mypage_menu_notification_setting),
+ APP_SETTING(iconRes = R.drawable.ic_setting, labelRes = R.string.mypage_menu_app_setting),
+ INQUIRY(iconRes = R.drawable.ic_mail, labelRes = R.string.mypage_menu_inquiry),
+ LOGOUT(
+ iconRes = R.drawable.ic_logout,
+ labelRes = R.string.mypage_menu_logout,
+ showChevron = false,
+ iconTint = TodakunColor.gray500,
+ textColor = TodakunColor.gray600,
+ ),
+}
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt
index 146d053..ec40a70 100644
--- a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt
@@ -37,6 +37,7 @@ import com.kikidan.domain.model.user.Gender
import com.kikidan.domain.model.user.User
import com.kikidan.mypage.home.model.MyPageHomeUiModel
import com.kikidan.mypage.home.model.MyPageHomeUiState
+import com.kikidan.mypage.home.model.MyPageMenuType
import com.kikidan.mypage.home.ui.component.MyPageHomeHeader
import com.kikidan.mypage.home.ui.component.MyPageMenuItem
import com.kikidan.mypage.home.ui.component.ProfileCard
@@ -48,11 +49,7 @@ fun MyPageHomeScreen(
modifier: Modifier = Modifier,
onEditClick: () -> Unit = {},
onViewMansaeryeokClick: () -> Unit = {},
- onSajuInfoClick: () -> Unit = {},
- onNotificationSettingClick: () -> Unit = {},
- onAppSettingClick: () -> Unit = {},
- onInquiryClick: () -> Unit = {},
- onLogoutClick: () -> Unit = {},
+ onMenuItemClick: (MyPageMenuType) -> Unit = {},
) {
Column(
modifier =
@@ -70,11 +67,7 @@ fun MyPageHomeScreen(
model = uiState.model,
onEditClick = onEditClick,
onViewMansaeryeokClick = onViewMansaeryeokClick,
- onSajuInfoClick = onSajuInfoClick,
- onNotificationSettingClick = onNotificationSettingClick,
- onAppSettingClick = onAppSettingClick,
- onInquiryClick = onInquiryClick,
- onLogoutClick = onLogoutClick,
+ onMenuItemClick = onMenuItemClick,
modifier = Modifier.weight(1f),
)
}
@@ -100,11 +93,7 @@ private fun MyPageHomeContent(
model: MyPageHomeUiModel,
onEditClick: () -> Unit,
onViewMansaeryeokClick: () -> Unit,
- onSajuInfoClick: () -> Unit,
- onNotificationSettingClick: () -> Unit,
- onAppSettingClick: () -> Unit,
- onInquiryClick: () -> Unit,
- onLogoutClick: () -> Unit,
+ onMenuItemClick: (MyPageMenuType) -> Unit,
modifier: Modifier = Modifier,
) {
Column(modifier = modifier.fillMaxSize()) {
@@ -119,39 +108,17 @@ private fun MyPageHomeContent(
Spacer(modifier = Modifier.height(24.dp))
TodakunDivider(type = TodakunDividerType.Section)
- MyPageMenuItem(
- painter = painterResource(R.drawable.ic_manage_saju_info),
- label = stringResource(R.string.mypage_menu_saju_info),
- onClick = onSajuInfoClick,
- modifier = Modifier.padding(horizontal = 20.dp),
- )
- MyPageMenuItem(
- painter = painterResource(R.drawable.ic_bell),
- label = stringResource(R.string.mypage_menu_notification_setting),
- onClick = onNotificationSettingClick,
- modifier = Modifier.padding(horizontal = 20.dp),
- )
- MyPageMenuItem(
- painter = painterResource(R.drawable.ic_setting),
- label = stringResource(R.string.mypage_menu_app_setting),
- onClick = onAppSettingClick,
- modifier = Modifier.padding(horizontal = 20.dp),
- )
- MyPageMenuItem(
- painter = painterResource(R.drawable.ic_mail),
- label = stringResource(R.string.mypage_menu_inquiry),
- onClick = onInquiryClick,
- modifier = Modifier.padding(horizontal = 20.dp),
- )
- MyPageMenuItem(
- painter = painterResource(R.drawable.ic_logout),
- label = stringResource(R.string.mypage_menu_logout),
- onClick = onLogoutClick,
- showChevron = false,
- textColor = TodakunColor.gray600,
- iconTint = TodakunColor.gray500,
- modifier = Modifier.padding(horizontal = 20.dp),
- )
+ MyPageMenuType.entries.forEach { menuType ->
+ MyPageMenuItem(
+ painter = painterResource(menuType.iconRes),
+ label = stringResource(menuType.labelRes),
+ onClick = { onMenuItemClick(menuType) },
+ showChevron = menuType.showChevron,
+ iconTint = menuType.iconTint,
+ textColor = menuType.textColor,
+ modifier = Modifier.padding(horizontal = 20.dp),
+ )
+ }
Spacer(modifier = Modifier.weight(1f))
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/ProfileCard.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/ProfileCard.kt
new file mode 100644
index 0000000..2971bc5
--- /dev/null
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/ProfileCard.kt
@@ -0,0 +1,212 @@
+package com.kikidan.mypage.home.ui.component
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+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.draw.clip
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.kikidan.designsystem.R
+import com.kikidan.designsystem.component.button.PrimaryButton
+import com.kikidan.designsystem.component.button.TodakunButtonSize
+import com.kikidan.designsystem.theme.TodakunColor
+import com.kikidan.designsystem.theme.TodakunTheme
+import com.kikidan.designsystem.theme.TodakunTypography
+import com.kikidan.domain.model.saju.CheonGan
+import com.kikidan.domain.model.saju.JiJi
+import com.kikidan.domain.model.saju.SajuPalja
+import com.kikidan.domain.model.saju.SajuPillar
+import com.kikidan.domain.model.user.Birth
+import com.kikidan.domain.model.user.BirthTime
+import com.kikidan.domain.model.user.DateType
+import com.kikidan.domain.model.user.Gender
+import com.kikidan.domain.model.user.User
+import java.time.LocalDate
+import java.time.format.DateTimeFormatter
+
+@Composable
+internal fun ProfileCard(
+ user: User,
+ sajuPalja: SajuPalja,
+ onEditClick: () -> Unit,
+ onViewMansaeryeokClick: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ Column(
+ modifier =
+ modifier
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(20.dp))
+ .background(TodakunColor.white)
+ .border(1.dp, TodakunColor.gray100, RoundedCornerShape(20.dp))
+ .padding(20.dp),
+ ) {
+ Row(verticalAlignment = Alignment.Top) {
+ Column(modifier = Modifier.weight(1f)) {
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Text(
+ text = user.name,
+ style = TodakunTypography.heading3Bold,
+ color = TodakunColor.gray975
+ )
+ Spacer(modifier = Modifier.width(4.dp))
+ Text(
+ text = stringResource(R.string.separator_dot),
+ style = TodakunTypography.body2Regular,
+ color = TodakunColor.gray700
+ )
+ Spacer(modifier = Modifier.width(4.dp))
+ Text(
+ text = user.gender.displayName(),
+ style = TodakunTypography.body2Regular,
+ color = TodakunColor.gray700,
+ )
+ }
+ Spacer(modifier = Modifier.height(4.dp))
+ UserBirthDateTime(birth = user.birth)
+ }
+ EditButton(onClick = onEditClick)
+ }
+
+ Spacer(modifier = Modifier.height(20.dp))
+ SajuPaljaGrid(sajuPalja = sajuPalja)
+ Spacer(modifier = Modifier.height(20.dp))
+
+ PrimaryButton(
+ text = stringResource(R.string.mypage_view_mansaeryeok_button),
+ onClick = onViewMansaeryeokClick,
+ size = TodakunButtonSize.Medium,
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
+}
+
+@Composable
+private fun UserBirthDateTime(
+ birth: Birth
+) {
+ val dateText = "${birth.date.format(BirthDateFormatter)} ${birth.dateType.displayName()}"
+
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Text(
+ text = dateText,
+ style = TodakunTypography.body3Medium,
+ color = TodakunColor.gray700,
+ )
+ if (birth.time != BirthTime.UNKNOWN) {
+ Text(
+ text = stringResource(R.string.separator_dot),
+ style = TodakunTypography.body3Medium,
+ color = TodakunColor.gray700,
+ modifier = Modifier.padding(horizontal = 8.dp)
+ )
+ Text(
+ text = birth.time.displayText(),
+ style = TodakunTypography.body3Medium,
+ color = TodakunColor.gray700
+ )
+ }
+ }
+}
+
+@Composable
+@Preview(showBackground = true)
+private fun UserBirthDateTimePreview() {
+ val birth = Birth(
+ dateType = DateType.SOLAR,
+ date = LocalDate.of(1999, 2, 13),
+ time = BirthTime.SIN,
+ )
+ UserBirthDateTime(
+ birth = birth
+ )
+}
+
+@Composable
+private fun EditButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
+ Box(
+ modifier =
+ modifier
+ .clip(RoundedCornerShape(8.dp))
+ .border(
+ 1.dp,
+ TodakunColor.primary500,
+ RoundedCornerShape(8.dp)
+ )
+ .clickable(onClick = onClick)
+ .background(TodakunColor.white)
+ .padding(horizontal = 19.dp, vertical = 8.dp),
+ ) {
+ Text(
+ text = stringResource(R.string.mypage_edit_button),
+ style = TodakunTypography.caption1SemiBold,
+ color = TodakunColor.primary700
+ )
+ }
+}
+
+private val BirthDateFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy.MM.dd")
+
+@Composable
+private fun Gender.displayName(): String =
+ when (this) {
+ Gender.MALE -> stringResource(R.string.gender_male)
+ Gender.FEMALE -> stringResource(R.string.gender_female)
+ }
+
+@Composable
+private fun DateType.displayName(): String =
+ when (this) {
+ DateType.SOLAR -> stringResource(R.string.date_type_solar)
+ DateType.LUNAR -> stringResource(R.string.date_type_lunar)
+ }
+
+@Composable
+private fun BirthTime.displayText(): String {
+ val start = startTime ?: return ""
+ return stringResource(R.string.birth_time_format, start.hour, start.minute)
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun ProfileCardPreview() {
+ TodakunTheme {
+ ProfileCard(
+ user =
+ User(
+ id = "preview",
+ name = "토닥이",
+ gender = Gender.FEMALE,
+ birth =
+ Birth(
+ dateType = DateType.SOLAR,
+ date = LocalDate.of(1999, 2, 13),
+ time = BirthTime.SIN,
+ ),
+ ),
+ sajuPalja =
+ SajuPalja(
+ yearPillar = SajuPillar(CheonGan.SIN, JiJi.MI),
+ monthPillar = SajuPillar(CheonGan.GI, JiJi.SA),
+ dayPillar = SajuPillar(CheonGan.GYE, JiJi.MYO),
+ hourPillar = SajuPillar(CheonGan.JEONG, JiJi.CHUK),
+ ),
+ onEditClick = {},
+ onViewMansaeryeokClick = {},
+ )
+ }
+}
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuPaljaGrid.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuPaljaGrid.kt
new file mode 100644
index 0000000..924fdf8
--- /dev/null
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuPaljaGrid.kt
@@ -0,0 +1,76 @@
+package com.kikidan.mypage.home.ui.component
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.kikidan.designsystem.theme.TodakunColor
+import com.kikidan.designsystem.theme.TodakunTheme
+import com.kikidan.domain.model.saju.CheonGan
+import com.kikidan.domain.model.saju.JiJi
+import com.kikidan.domain.model.saju.SajuPalja
+import com.kikidan.domain.model.saju.SajuPillar
+
+@Composable
+internal fun SajuPaljaGrid(
+ sajuPalja: SajuPalja,
+ modifier: Modifier = Modifier
+) {
+ val pillars: List =
+ listOfNotNull(sajuPalja.yearPillar, sajuPalja.monthPillar, sajuPalja.dayPillar, sajuPalja.hourPillar)
+
+ Column(
+ modifier =
+ modifier
+ .fillMaxWidth()
+ .clip(RoundedCornerShape(16.dp))
+ .background(TodakunColor.coolGray50)
+ .padding(12.dp),
+ verticalArrangement = Arrangement.spacedBy(8.dp),
+ ) {
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ pillars.forEach { pillar ->
+ SajuCard(
+ hanja = pillar.cheonGan.hanja,
+ reading = pillar.cheonGan.displayName,
+ ohaeng = pillar.cheonGan.ohaeng,
+ modifier = Modifier.weight(1f),
+ )
+ }
+ }
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ pillars.forEach { pillar ->
+ SajuCard(
+ hanja = pillar.jiJi.hanja,
+ reading = pillar.jiJi.displayName,
+ ohaeng = pillar.jiJi.ohaeng,
+ modifier = Modifier.weight(1f),
+ )
+ }
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun SajuPaljaGridPreview() {
+ TodakunTheme {
+ SajuPaljaGrid(
+ sajuPalja =
+ SajuPalja(
+ yearPillar = SajuPillar(CheonGan.SIN, JiJi.MI),
+ monthPillar = SajuPillar(CheonGan.GI, JiJi.SA),
+ dayPillar = SajuPillar(CheonGan.GYE, JiJi.MYO),
+ hourPillar = SajuPillar(CheonGan.JEONG, JiJi.CHUK),
+ ),
+ )
+ }
+}
From a7d84914513ac694a65024fcfab217a0f5a0f3cc Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Mon, 3 Aug 2026 23:52:00 +0900
Subject: [PATCH 16/22] add @param use-site target to MyPageMenuType
annotations
---
.../main/java/com/kikidan/mypage/home/model/MyPageMenuType.kt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageMenuType.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageMenuType.kt
index a7ae68b..90c7535 100644
--- a/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageMenuType.kt
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageMenuType.kt
@@ -7,8 +7,8 @@ import com.kikidan.designsystem.R
import com.kikidan.designsystem.theme.TodakunColor
enum class MyPageMenuType(
- @DrawableRes val iconRes: Int,
- @StringRes val labelRes: Int,
+ @param:DrawableRes val iconRes: Int,
+ @param:StringRes val labelRes: Int,
val showChevron: Boolean = true,
val iconTint: Color = TodakunColor.gray975,
val textColor: Color = TodakunColor.black,
From 7f81902e8d6819d779e37f9424861fde78eba396 Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Tue, 4 Aug 2026 18:04:02 +0900
Subject: [PATCH 17/22] add comments for loading and error states in
MyPageHomeScreen
---
.../main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt | 2 ++
1 file changed, 2 insertions(+)
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt
index ec40a70..cbc42d1 100644
--- a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt
@@ -76,6 +76,7 @@ fun MyPageHomeScreen(
@Composable
private fun MyPageHomeLoading(modifier: Modifier = Modifier) {
+ //로딩 요구사항 없음
Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center){
}
@@ -83,6 +84,7 @@ private fun MyPageHomeLoading(modifier: Modifier = Modifier) {
@Composable
private fun MyPageHomeError(modifier: Modifier = Modifier) {
+ //에러 요구사항 없음
Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
}
From f90b6807b9d17a2e7bf72fc4f89fbc1ef48c3040 Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Tue, 4 Aug 2026 18:49:31 +0900
Subject: [PATCH 18/22] =?UTF-8?q?[Feat]=20mypage=20feature=20=EC=97=B0?=
=?UTF-8?q?=EA=B2=B0=20=EB=B0=8F=20UI=20=EA=B0=9C=EC=84=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* feat: app 모듈에 feature:mypage 의존성 추가
* refactor: MyPageHomeRoute에 Modifier 파라미터 추가 및 전달
---
app/build.gradle.kts | 1 +
.../main/java/com/kikidan/mypage/home/ui/MyPageHomeRoute.kt | 3 +++
2 files changed, 4 insertions(+)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 4e3d769..0cdc27a 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -64,6 +64,7 @@ dependencies {
implementation(projects.core.dataLocal)
implementation(projects.core.navigation)
implementation(projects.core.designsystem)
+ implementation(projects.feature.mypage)
implementation(libs.androidx.navigation3.runtime)
implementation(libs.androidx.navigation3.ui)
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeRoute.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeRoute.kt
index b34d4ec..c3f9ada 100644
--- a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeRoute.kt
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeRoute.kt
@@ -2,17 +2,20 @@ package com.kikidan.mypage.home.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
import com.kikidan.mypage.home.MyPageHomeViewModel
import org.orbitmvi.orbit.compose.collectAsState
@Composable
fun MyPageHomeRoute(
+ modifier: Modifier = Modifier,
viewModel: MyPageHomeViewModel = hiltViewModel(),
) {
val uiState by viewModel.collectAsState()
MyPageHomeScreen(
uiState = uiState,
+ modifier = modifier,
)
}
From 46ef86ea5b01d0674288f17995aa3d8e741631a5 Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Tue, 4 Aug 2026 18:55:17 +0900
Subject: [PATCH 19/22] [Style] reformat code and apply trailing commas in
mypage feature
* apply trailing commas to UI components and function calls
* reformat function signatures and when expressions for consistency
* clean up spacing in comments and remove unused lines
---
feature/mypage/build.gradle.kts | 1 -
.../mypage/home/ui/MyPageHomeScreen.kt | 21 +++++++----
.../home/ui/component/MyPageHomeHeader.kt | 4 +-
.../mypage/home/ui/component/ProfileCard.kt | 37 ++++++++++---------
.../mypage/home/ui/component/SajuCard.kt | 4 +-
.../mypage/home/ui/component/SajuPaljaGrid.kt | 2 +-
6 files changed, 36 insertions(+), 33 deletions(-)
diff --git a/feature/mypage/build.gradle.kts b/feature/mypage/build.gradle.kts
index c32b8bb..acec1f7 100644
--- a/feature/mypage/build.gradle.kts
+++ b/feature/mypage/build.gradle.kts
@@ -5,4 +5,3 @@ plugins {
android {
namespace = "com.kikidan.mypage"
}
-
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt
index cbc42d1..ef6bbdf 100644
--- a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt
@@ -60,9 +60,15 @@ fun MyPageHomeScreen(
MyPageHomeHeader(modifier = Modifier.fillMaxWidth())
when (uiState) {
- is MyPageHomeUiState.Loading -> MyPageHomeLoading(modifier = Modifier.weight(1f))
- is MyPageHomeUiState.Fail -> MyPageHomeError(modifier = Modifier.weight(1f))
- is MyPageHomeUiState.Success ->
+ is MyPageHomeUiState.Loading -> {
+ MyPageHomeLoading(modifier = Modifier.weight(1f))
+ }
+
+ is MyPageHomeUiState.Fail -> {
+ MyPageHomeError(modifier = Modifier.weight(1f))
+ }
+
+ is MyPageHomeUiState.Success -> {
MyPageHomeContent(
model = uiState.model,
onEditClick = onEditClick,
@@ -70,23 +76,22 @@ fun MyPageHomeScreen(
onMenuItemClick = onMenuItemClick,
modifier = Modifier.weight(1f),
)
+ }
}
}
}
@Composable
private fun MyPageHomeLoading(modifier: Modifier = Modifier) {
- //로딩 요구사항 없음
- Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center){
-
+ // 로딩 요구사항 없음
+ Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
}
}
@Composable
private fun MyPageHomeError(modifier: Modifier = Modifier) {
- //에러 요구사항 없음
+ // 에러 요구사항 없음
Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
-
}
}
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/MyPageHomeHeader.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/MyPageHomeHeader.kt
index eb53971..6250dbe 100644
--- a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/MyPageHomeHeader.kt
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/MyPageHomeHeader.kt
@@ -11,9 +11,7 @@ import com.kikidan.designsystem.theme.TodakunColor
import com.kikidan.designsystem.theme.TodakunTypography
@Composable
-internal fun MyPageHomeHeader(
- modifier: Modifier = Modifier,
-) {
+internal fun MyPageHomeHeader(modifier: Modifier = Modifier) {
Text(
text = stringResource(R.string.mypage_home_title),
style = TodakunTypography.heading4Bold,
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/ProfileCard.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/ProfileCard.kt
index 2971bc5..87ba42b 100644
--- a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/ProfileCard.kt
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/ProfileCard.kt
@@ -61,13 +61,13 @@ internal fun ProfileCard(
Text(
text = user.name,
style = TodakunTypography.heading3Bold,
- color = TodakunColor.gray975
+ color = TodakunColor.gray975,
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = stringResource(R.string.separator_dot),
style = TodakunTypography.body2Regular,
- color = TodakunColor.gray700
+ color = TodakunColor.gray700,
)
Spacer(modifier = Modifier.width(4.dp))
Text(
@@ -96,9 +96,7 @@ internal fun ProfileCard(
}
@Composable
-private fun UserBirthDateTime(
- birth: Birth
-) {
+private fun UserBirthDateTime(birth: Birth) {
val dateText = "${birth.date.format(BirthDateFormatter)} ${birth.dateType.displayName()}"
Row(verticalAlignment = Alignment.CenterVertically) {
@@ -112,12 +110,12 @@ private fun UserBirthDateTime(
text = stringResource(R.string.separator_dot),
style = TodakunTypography.body3Medium,
color = TodakunColor.gray700,
- modifier = Modifier.padding(horizontal = 8.dp)
+ modifier = Modifier.padding(horizontal = 8.dp),
)
Text(
text = birth.time.displayText(),
style = TodakunTypography.body3Medium,
- color = TodakunColor.gray700
+ color = TodakunColor.gray700,
)
}
}
@@ -126,18 +124,22 @@ private fun UserBirthDateTime(
@Composable
@Preview(showBackground = true)
private fun UserBirthDateTimePreview() {
- val birth = Birth(
- dateType = DateType.SOLAR,
- date = LocalDate.of(1999, 2, 13),
- time = BirthTime.SIN,
- )
+ val birth =
+ Birth(
+ dateType = DateType.SOLAR,
+ date = LocalDate.of(1999, 2, 13),
+ time = BirthTime.SIN,
+ )
UserBirthDateTime(
- birth = birth
+ birth = birth,
)
}
@Composable
-private fun EditButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
+private fun EditButton(
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
Box(
modifier =
modifier
@@ -145,16 +147,15 @@ private fun EditButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
.border(
1.dp,
TodakunColor.primary500,
- RoundedCornerShape(8.dp)
- )
- .clickable(onClick = onClick)
+ RoundedCornerShape(8.dp),
+ ).clickable(onClick = onClick)
.background(TodakunColor.white)
.padding(horizontal = 19.dp, vertical = 8.dp),
) {
Text(
text = stringResource(R.string.mypage_edit_button),
style = TodakunTypography.caption1SemiBold,
- color = TodakunColor.primary700
+ color = TodakunColor.primary700,
)
}
}
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuCard.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuCard.kt
index 7c22a98..1c88575 100644
--- a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuCard.kt
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuCard.kt
@@ -43,12 +43,12 @@ internal fun SajuCard(
Text(
text = hanja,
style = TodakunTypography.body1Bold,
- color = TodakunColor.gray975
+ color = TodakunColor.gray975,
)
Text(
text = stringResource(R.string.saju_card_label_format, reading, ohaeng.hanja),
style = TodakunTypography.caption3Regular,
- color = TodakunColor.gray975
+ color = TodakunColor.gray975,
)
}
}
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuPaljaGrid.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuPaljaGrid.kt
index 924fdf8..55cd720 100644
--- a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuPaljaGrid.kt
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/component/SajuPaljaGrid.kt
@@ -22,7 +22,7 @@ import com.kikidan.domain.model.saju.SajuPillar
@Composable
internal fun SajuPaljaGrid(
sajuPalja: SajuPalja,
- modifier: Modifier = Modifier
+ modifier: Modifier = Modifier,
) {
val pillars: List =
listOfNotNull(sajuPalja.yearPillar, sajuPalja.monthPillar, sajuPalja.dayPillar, sajuPalja.hourPillar)
From 803f9745de14c3b0f88b080570fff7f011ce8be0 Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Tue, 4 Aug 2026 19:08:31 +0900
Subject: [PATCH 20/22] refactor: remove unused Orbit syntax and helper
function in MyPageHomeViewModel
---
.../com/kikidan/mypage/home/MyPageHomeViewModel.kt | 11 -----------
1 file changed, 11 deletions(-)
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/MyPageHomeViewModel.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/MyPageHomeViewModel.kt
index a39fa04..df4274a 100644
--- a/feature/mypage/src/main/java/com/kikidan/mypage/home/MyPageHomeViewModel.kt
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/MyPageHomeViewModel.kt
@@ -8,12 +8,9 @@ import com.kikidan.mypage.home.model.MyPageHomeUiState
import dagger.hilt.android.lifecycle.HiltViewModel
import org.orbitmvi.orbit.Container
import org.orbitmvi.orbit.ContainerHost
-import org.orbitmvi.orbit.syntax.Syntax
import org.orbitmvi.orbit.viewmodel.container
import javax.inject.Inject
-private typealias MyPageHomeSyntax = Syntax
-
@HiltViewModel
class MyPageHomeViewModel
@Inject
@@ -37,12 +34,4 @@ class MyPageHomeViewModel
reduce { MyPageHomeUiState.Fail(throwable) }
}
}
-
- private suspend fun MyPageHomeSyntax.reduceIfSuccess(
- reducer: MyPageHomeUiState.Success.() -> MyPageHomeUiState,
- ) {
- reduce {
- (state as? MyPageHomeUiState.Success)?.reducer() ?: state
- }
- }
}
From d36bb7fbe2e0fc6900d12eb95c4160c057515262 Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Wed, 5 Aug 2026 18:17:58 +0900
Subject: [PATCH 21/22] =?UTF-8?q?Chore:=20=EC=95=B1=20=EC=B5=9C=EC=8B=A0?=
=?UTF-8?q?=20=EB=B2=84=EC=A0=84=20string=20resource=20=EC=82=AD=EC=A0=9C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
core/designsystem/src/main/res/values/strings.xml | 1 -
1 file changed, 1 deletion(-)
diff --git a/core/designsystem/src/main/res/values/strings.xml b/core/designsystem/src/main/res/values/strings.xml
index 1670ad0..010585b 100644
--- a/core/designsystem/src/main/res/values/strings.xml
+++ b/core/designsystem/src/main/res/values/strings.xml
@@ -56,7 +56,6 @@
1:1 문의
로그아웃
앱 버전
- v1.0.0 (최신 버전)
만세력 보기
수정
From cf64267c06beec6a95f8a463ec6db356d389eb25 Mon Sep 17 00:00:00 2001
From: Moony-H <53536205+Moony-H@users.noreply.github.com>
Date: Sat, 8 Aug 2026 17:12:33 +0900
Subject: [PATCH 22/22] =?UTF-8?q?Fix:=20=EC=95=B1=20=EB=B2=84=EC=A0=84=20?=
=?UTF-8?q?=EC=B2=B4=ED=81=AC=20=ED=99=94=EB=A9=B4=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../designsystem/component/TodakunTab.kt | 11 +++++++
.../src/main/res/values/strings.xml | 2 ++
feature/mypage/build.gradle.kts | 5 ++++
.../kikidan/mypage/home/AppVersionChecker.kt | 30 +++++++++++++++++++
.../mypage/home/MyPageHomeViewModel.kt | 13 +++++++-
.../mypage/home/model/MyPageHomeUiModel.kt | 2 ++
.../mypage/home/ui/MyPageHomeScreen.kt | 13 +++++---
gradle/libs.versions.toml | 3 ++
8 files changed, 74 insertions(+), 5 deletions(-)
create mode 100644 feature/mypage/src/main/java/com/kikidan/mypage/home/AppVersionChecker.kt
diff --git a/core/designsystem/src/main/java/com/kikidan/designsystem/component/TodakunTab.kt b/core/designsystem/src/main/java/com/kikidan/designsystem/component/TodakunTab.kt
index 57df7cd..bcabff4 100644
--- a/core/designsystem/src/main/java/com/kikidan/designsystem/component/TodakunTab.kt
+++ b/core/designsystem/src/main/java/com/kikidan/designsystem/component/TodakunTab.kt
@@ -24,6 +24,14 @@ import com.kikidan.designsystem.theme.TodakunColor
import com.kikidan.designsystem.theme.TodakunTheme
import com.kikidan.designsystem.theme.TodakunTypography
+/**
+ * A custom tab component for the Todakun design system.
+ *
+ * @param text The text to be displayed on the tab.
+ * @param selected Whether the tab is currently selected.
+ * @param onClick Callback to be invoked when the tab is clicked.
+ * @param modifier The modifier to be applied to the tab.
+ */
@Composable
fun TodakunTab(
text: String,
@@ -62,6 +70,9 @@ fun TodakunTab(
}
}
+/**
+ * Default values for [TodakunTab].
+ */
private object TodakunTabDefaults {
val Shape = RoundedCornerShape(99.dp)
}
diff --git a/core/designsystem/src/main/res/values/strings.xml b/core/designsystem/src/main/res/values/strings.xml
index 010585b..f2c3186 100644
--- a/core/designsystem/src/main/res/values/strings.xml
+++ b/core/designsystem/src/main/res/values/strings.xml
@@ -56,8 +56,10 @@
1:1 문의
로그아웃
앱 버전
+ v %1$s
만세력 보기
수정
+ (최신 버전)
·
diff --git a/feature/mypage/build.gradle.kts b/feature/mypage/build.gradle.kts
index acec1f7..95da1d6 100644
--- a/feature/mypage/build.gradle.kts
+++ b/feature/mypage/build.gradle.kts
@@ -5,3 +5,8 @@ plugins {
android {
namespace = "com.kikidan.mypage"
}
+
+dependencies {
+ implementation(libs.play.app.update.ktx)
+ implementation(libs.kotlinx.coroutines.play.services)
+}
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/AppVersionChecker.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/AppVersionChecker.kt
new file mode 100644
index 0000000..db56857
--- /dev/null
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/AppVersionChecker.kt
@@ -0,0 +1,30 @@
+package com.kikidan.mypage.home
+
+import android.content.Context
+import com.google.android.play.core.appupdate.AppUpdateManagerFactory
+import com.google.android.play.core.install.model.UpdateAvailability
+import dagger.hilt.android.qualifiers.ApplicationContext
+import kotlinx.coroutines.tasks.await
+import javax.inject.Inject
+
+class AppVersionChecker
+ @Inject
+ constructor(
+ @ApplicationContext private val context: Context,
+ ) {
+ fun getCurrentVersionName(): String =
+ context.packageManager
+ .getPackageInfo(context.packageName, 0)
+ .versionName
+ .orEmpty()
+
+ suspend fun isLatestVersion(): Boolean =
+ try {
+ val appUpdateManager = AppUpdateManagerFactory.create(context)
+ val appUpdateInfo = appUpdateManager.appUpdateInfo.await()
+ appUpdateInfo.updateAvailability() != UpdateAvailability.UPDATE_AVAILABLE
+ } catch (e: Exception) {
+ // Play Store가 없는 기기(에뮬레이터 등)에서도 마이페이지 조회 자체는 실패하면 안 되므로 조회 실패 시 최신 버전으로 간주한다.
+ true
+ }
+ }
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/MyPageHomeViewModel.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/MyPageHomeViewModel.kt
index df4274a..78e9cc5 100644
--- a/feature/mypage/src/main/java/com/kikidan/mypage/home/MyPageHomeViewModel.kt
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/MyPageHomeViewModel.kt
@@ -16,6 +16,7 @@ class MyPageHomeViewModel
@Inject
constructor(
private val getMyPageInfoUseCase: GetMyPageInfoUseCase,
+ private val appVersionChecker: AppVersionChecker,
) : ViewModel(),
ContainerHost {
override val container: Container =
@@ -29,7 +30,17 @@ class MyPageHomeViewModel
intent {
getMyPageInfoUseCase()
.onSuccess { info ->
- reduce { MyPageHomeUiState.Success(MyPageHomeUiModel(info.user, info.sajuPalja)) }
+ val isLatestVersion = appVersionChecker.isLatestVersion()
+ reduce {
+ MyPageHomeUiState.Success(
+ MyPageHomeUiModel(
+ user = info.user,
+ sajuPalja = info.sajuPalja,
+ appVersionName = appVersionChecker.getCurrentVersionName(),
+ isLatestVersion = isLatestVersion,
+ ),
+ )
+ }
}.onFailure { throwable ->
reduce { MyPageHomeUiState.Fail(throwable) }
}
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiModel.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiModel.kt
index b9fa8b1..1c1563f 100644
--- a/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiModel.kt
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/model/MyPageHomeUiModel.kt
@@ -6,4 +6,6 @@ import com.kikidan.domain.model.user.User
data class MyPageHomeUiModel(
val user: User,
val sajuPalja: SajuPalja,
+ val appVersionName: String,
+ val isLatestVersion: Boolean = true,
)
diff --git a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt
index ef6bbdf..693196d 100644
--- a/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt
+++ b/feature/mypage/src/main/java/com/kikidan/mypage/home/ui/MyPageHomeScreen.kt
@@ -138,13 +138,16 @@ private fun MyPageHomeContent(
) {
Text(
text = stringResource(R.string.mypage_app_version_label),
- style = TodakunTypography.caption1Regular,
+ style = TodakunTypography.body3Medium,
color = TodakunColor.gray400,
)
- Spacer(modifier = Modifier.width(8.dp))
+ Spacer(modifier = Modifier.width(16.dp))
+ val versionText =
+ stringResource(R.string.mypage_app_version_format, model.appVersionName) +
+ if (model.isLatestVersion) " " + stringResource(R.string.mypage_latest_version) else ""
Text(
- text = stringResource(R.string.mypage_app_version_value),
- style = TodakunTypography.caption1Regular,
+ text = versionText,
+ style = TodakunTypography.body3Medium,
color = TodakunColor.gray400,
)
}
@@ -178,6 +181,8 @@ private fun MyPageHomeScreenPreview() {
dayPillar = SajuPillar(CheonGan.GYE, JiJi.MYO),
hourPillar = SajuPillar(CheonGan.JEONG, JiJi.CHUK),
),
+ appVersionName = "1.0.0",
+ isLatestVersion = false,
),
),
)
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 8c67c8a..39989b6 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -25,6 +25,7 @@ material3AdaptiveNav3 = "1.3.0-rc01"
ktlint = "1.8.0"
ktlintGradlePlugin = "14.2.0"
composeRules = "0.6.2"
+playAppUpdate = "2.1.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -68,6 +69,8 @@ orbit-test = { group = "org.orbit-mvi", name = "orbit-test", version.ref = "orbi
# Navigation 3 libraries
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
+kotlinx-coroutines-play-services = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-play-services", version.ref = "kotlinx-coroutines" }
+play-app-update-ktx = { group = "com.google.android.play", name = "app-update-ktx", version.ref = "playAppUpdate" }
androidx-navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "nav3Core" }
androidx-navigation3-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "nav3Core" }
androidx-lifecycle-viewmodel-navigation3 = { module = "androidx.lifecycle:lifecycle-viewmodel-navigation3", version.ref = "lifecycleViewmodelNav3" }