From 2c5c6dde7d2ff7224a3d2227e26c514d5808fb18 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 17 Jan 2026 16:22:18 +0900 Subject: [PATCH 01/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20Kermit=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80=20ConventionPlugin=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/convention/KermitConventionPlugin.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 build-logic/convention/src/main/kotlin/com/twix/convention/KermitConventionPlugin.kt diff --git a/build-logic/convention/src/main/kotlin/com/twix/convention/KermitConventionPlugin.kt b/build-logic/convention/src/main/kotlin/com/twix/convention/KermitConventionPlugin.kt new file mode 100644 index 0000000..454c3bc --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/twix/convention/KermitConventionPlugin.kt @@ -0,0 +1,12 @@ +package com.twix.convention + +import com.twix.convention.extension.implementation +import com.twix.convention.extension.library +import com.twix.convention.extension.libs +import org.gradle.kotlin.dsl.dependencies + +class KermitConventionPlugin: BuildLogicConventionPlugin({ + dependencies { + implementation(libs.library("kermit")) + } +}) \ No newline at end of file From 13021fb3c04eaafa86ccbe47766beab3503d2d15 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 17 Jan 2026 16:22:43 +0900 Subject: [PATCH 02/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20Kermit=20Logging=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle/libs.versions.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 095e860..a184442 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -55,6 +55,9 @@ androidx-test-ext-junit = "1.3.0" turbine = "1.2.1" jetbrains-kotlin-jvm = "2.1.0" +# Logging +kermit = "2.0.8" + [libraries] # AndroidX androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core-ktx" } @@ -115,6 +118,9 @@ kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-t turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" } kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test", version.ref = "kotlin" } +# Logging +kermit = { group = "co.touchlab", name = "kermit", version.ref = "kermit" } + [bundles] androidx = [ "androidx-core-ktx", From 64b4c1485d686924eb6af9ee022e5fa6fee4851c Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 17 Jan 2026 16:22:58 +0900 Subject: [PATCH 03/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20KermitConventionPlugi?= =?UTF-8?q?n=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/kotlin/com/twix/convention/FeatureConventionPlugin.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/build-logic/convention/src/main/kotlin/com/twix/convention/FeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/com/twix/convention/FeatureConventionPlugin.kt index fb110ff..8bf351b 100644 --- a/build-logic/convention/src/main/kotlin/com/twix/convention/FeatureConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/com/twix/convention/FeatureConventionPlugin.kt @@ -8,6 +8,7 @@ class FeatureConventionPlugin : BuildLogicConventionPlugin({ apply() apply() apply() + apply< KermitConventionPlugin>() dependencies { implementation(project(":core:analytics")) From 138d1bc31379b6b3dff57a23cb4368035fe06920 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 17 Jan 2026 16:55:28 +0900 Subject: [PATCH 04/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20KermitConventionPlugi?= =?UTF-8?q?n=20=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build-logic/convention/build.gradle.kts | 4 ++++ gradle/libs.versions.toml | 1 + 2 files changed, 5 insertions(+) diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index 289fe7d..8460246 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -51,5 +51,9 @@ gradlePlugin { id = "twix.data" implementationClass = "com.twix.convention.DataConventionPlugin" } + register("kermit") { + id = "twix.kermit" + implementationClass = "com.twix.convention.KermitConventionPlugin" + } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a184442..927e4a9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -192,3 +192,4 @@ twix-feature = { id = "twix.feature", version = "unspecified" } twix-koin = { id = "twix.koin", version = "unspecified" } twix-java-library = { id = "twix.java.library", version = "unspecified" } twix-data = { id = "twix.data", version = "unspecified" } +twix-kermit = { id = "twix.kermit", version = "unspecified" } From 7d3b0b6bc15d48f26caf497ea7ed590f27cd4599 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 17 Jan 2026 16:55:51 +0900 Subject: [PATCH 05/62] =?UTF-8?q?=E2=9C=A8=20Faet:=20Kermit=20=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/ui/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 2f73a37..73482c8 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -1,6 +1,7 @@ plugins { alias(libs.plugins.twix.android.library) alias(libs.plugins.twix.android.compose) + alias(libs.plugins.twix.kermit) } android { From c2d42f285341c5c8871fc3ce7e1762949e693bac Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 17 Jan 2026 16:56:02 +0900 Subject: [PATCH 06/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20BaseViewModel=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/twix/ui/base/BaseViewModel.kt | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt diff --git a/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt b/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt new file mode 100644 index 0000000..f75efaa --- /dev/null +++ b/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt @@ -0,0 +1,117 @@ +package com.twix.ui.base + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import co.touchlab.kermit.Logger +import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.consumeAsFlow +import kotlinx.coroutines.launch +import kotlin.coroutines.cancellation.CancellationException + +abstract class BaseViewModel( + initialState: S +): ViewModel() { + + protected open val logger: Logger = + Logger.withTag(this::class.simpleName ?: "BaseViewModel") + + // State + private val stateHolder = StateHolder(initialState) + val state: StateFlow = stateHolder.state + protected val currentState: S get() = stateHolder.current + + // SideEffect + private val sideEffectHolder = SideEffectHolder() + val sideEffect: Flow = sideEffectHolder.flow + + // Intent + private val intentChannel = Channel(Channel.BUFFERED) + + init { + // Intent 순차 처리 + viewModelScope.launch { + intentChannel.consumeAsFlow().collect { intent -> + try { + handleIntent(intent) + } catch (t: Throwable) { + if (t is CancellationException) throw t + handleError(t) + } + } + } + } + + /** + * UI에서 Intent를 발생시키는 메서드 + * */ + fun dispatch(intent: I) { + val result = intentChannel.trySend(intent) + if (result.isFailure) { + logger.w { "이벤트 유실: $intent, 원인 = ${result.exceptionOrNull()}" } + } + } + + /** + * State를 변경하는 메서드 + * */ + protected fun reduce(reducer: S.() -> S) { + stateHolder.reduce(reducer) + } + + /** + * SideEffect를 발생시키는 메서드 + * */ + protected suspend fun emitSideEffect(effect: SE) { + sideEffectHolder.emit(effect) + } + + /** + * Intent를 처리하는 메서드 + * */ + protected abstract suspend fun handleIntent(intent: I) + + + /** + * 서버 통신 메서드 호출 및 응답을 처리하는 헬퍼 메서드 + * */ + protected fun launchResult( + onStart: (() -> Unit)? = null, // 비동기 시작 전 처리해야 할 로직 ex) 로딩 + onFinally: (() -> Unit)? = null, // 비동기 종료 후 리소스 정리 + onSuccess: (D) -> Unit, // 비동기 메서드 호출이 성공했을 때 처리해야 할 로직 + onError: ((Throwable) -> Unit)? = null, // 비동기 메서드 호출에 실패했을 때 처리해야 할 로직 + block: suspend () -> Result // 비동기 메서드 ex) 서버 통신 메서드 + ): Job = viewModelScope.launch { + try { + onStart?.invoke() + + val result = block.invoke() + result.fold( + onSuccess = { data -> onSuccess(data) }, + onFailure = { t -> + if (t is CancellationException) throw t + + handleError(t) + onError?.invoke(t) + } + ) + } finally { + onFinally?.invoke() + } + } + + /** + * 에러 핸들링 메서드 + * */ + protected open fun handleError(t: Throwable) { + logger.e { "에러 발생: ${t.stackTraceToString()}" } + } + + // 리소스 정리 + override fun onCleared() { + intentChannel.close() + super.onCleared() + } +} \ No newline at end of file From 63f510308a18ced59daf0bb30ef69ec42beccbb4 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 17 Jan 2026 16:56:13 +0900 Subject: [PATCH 07/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20Intent=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/ui/src/main/java/com/twix/ui/base/Intent.kt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 core/ui/src/main/java/com/twix/ui/base/Intent.kt diff --git a/core/ui/src/main/java/com/twix/ui/base/Intent.kt b/core/ui/src/main/java/com/twix/ui/base/Intent.kt new file mode 100644 index 0000000..955f75c --- /dev/null +++ b/core/ui/src/main/java/com/twix/ui/base/Intent.kt @@ -0,0 +1,3 @@ +package com.twix.ui.base + +interface Intent \ No newline at end of file From 5d335b3a1fd64a8304d970a8c81e609bfa5ad2ec Mon Sep 17 00:00:00 2001 From: dogmania Date: Sun, 18 Jan 2026 16:44:33 +0900 Subject: [PATCH 08/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20:feature:main=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/main/.gitignore | 1 + feature/main/build.gradle.kts | 3 +++ feature/main/consumer-rules.pro | 0 feature/main/proguard-rules.pro | 21 ++++++++++++++++ .../com/twix/main/ExampleInstrumentedTest.kt | 24 +++++++++++++++++++ feature/main/src/main/AndroidManifest.xml | 4 ++++ .../java/com/twix/main/ExampleUnitTest.kt | 17 +++++++++++++ settings.gradle.kts | 1 + 8 files changed, 71 insertions(+) create mode 100644 feature/main/.gitignore create mode 100644 feature/main/build.gradle.kts create mode 100644 feature/main/consumer-rules.pro create mode 100644 feature/main/proguard-rules.pro create mode 100644 feature/main/src/androidTest/java/com/twix/main/ExampleInstrumentedTest.kt create mode 100644 feature/main/src/main/AndroidManifest.xml create mode 100644 feature/main/src/test/java/com/twix/main/ExampleUnitTest.kt diff --git a/feature/main/.gitignore b/feature/main/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/feature/main/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/main/build.gradle.kts b/feature/main/build.gradle.kts new file mode 100644 index 0000000..b22d35b --- /dev/null +++ b/feature/main/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + alias(libs.plugins.twix.feature) +} \ No newline at end of file diff --git a/feature/main/consumer-rules.pro b/feature/main/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/feature/main/proguard-rules.pro b/feature/main/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/feature/main/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/main/src/androidTest/java/com/twix/main/ExampleInstrumentedTest.kt b/feature/main/src/androidTest/java/com/twix/main/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..c381d85 --- /dev/null +++ b/feature/main/src/androidTest/java/com/twix/main/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.twix.main + +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.twix.main.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/main/src/main/AndroidManifest.xml b/feature/main/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a5918e6 --- /dev/null +++ b/feature/main/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/feature/main/src/test/java/com/twix/main/ExampleUnitTest.kt b/feature/main/src/test/java/com/twix/main/ExampleUnitTest.kt new file mode 100644 index 0000000..a919746 --- /dev/null +++ b/feature/main/src/test/java/com/twix/main/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.twix.main + +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) + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index e8d75d0..29626c0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -35,3 +35,4 @@ include(":core:navigation") include(":core:design-system") include(":core:network") include(":core:analytics") +include(":feature:main") From bf4561dfbb0d75c71096dff6019802cffebb8915 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sun, 18 Jan 2026 17:31:51 +0900 Subject: [PATCH 09/62] =?UTF-8?q?=F0=9F=8D=B1=20Chore:=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EB=A6=AC=EC=86=8C=EC=8A=A4=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 --- .../src/main/res/drawable/ic_couple_selected.xml | 11 +++++++++++ .../main/res/drawable/ic_couple_unselected.xml | 11 +++++++++++ .../src/main/res/drawable/ic_home_selected.xml | 11 +++++++++++ .../src/main/res/drawable/ic_home_unselected.xml | 11 +++++++++++ .../src/main/res/drawable/ic_stats_selected.xml | 15 +++++++++++++++ .../src/main/res/drawable/ic_stats_unselected.xml | 12 ++++++++++++ 6 files changed, 71 insertions(+) create mode 100644 core/design-system/src/main/res/drawable/ic_couple_selected.xml create mode 100644 core/design-system/src/main/res/drawable/ic_couple_unselected.xml create mode 100644 core/design-system/src/main/res/drawable/ic_home_selected.xml create mode 100644 core/design-system/src/main/res/drawable/ic_home_unselected.xml create mode 100644 core/design-system/src/main/res/drawable/ic_stats_selected.xml create mode 100644 core/design-system/src/main/res/drawable/ic_stats_unselected.xml diff --git a/core/design-system/src/main/res/drawable/ic_couple_selected.xml b/core/design-system/src/main/res/drawable/ic_couple_selected.xml new file mode 100644 index 0000000..1cfef09 --- /dev/null +++ b/core/design-system/src/main/res/drawable/ic_couple_selected.xml @@ -0,0 +1,11 @@ + + + diff --git a/core/design-system/src/main/res/drawable/ic_couple_unselected.xml b/core/design-system/src/main/res/drawable/ic_couple_unselected.xml new file mode 100644 index 0000000..131fa13 --- /dev/null +++ b/core/design-system/src/main/res/drawable/ic_couple_unselected.xml @@ -0,0 +1,11 @@ + + + diff --git a/core/design-system/src/main/res/drawable/ic_home_selected.xml b/core/design-system/src/main/res/drawable/ic_home_selected.xml new file mode 100644 index 0000000..b247a23 --- /dev/null +++ b/core/design-system/src/main/res/drawable/ic_home_selected.xml @@ -0,0 +1,11 @@ + + + diff --git a/core/design-system/src/main/res/drawable/ic_home_unselected.xml b/core/design-system/src/main/res/drawable/ic_home_unselected.xml new file mode 100644 index 0000000..6979d2f --- /dev/null +++ b/core/design-system/src/main/res/drawable/ic_home_unselected.xml @@ -0,0 +1,11 @@ + + + diff --git a/core/design-system/src/main/res/drawable/ic_stats_selected.xml b/core/design-system/src/main/res/drawable/ic_stats_selected.xml new file mode 100644 index 0000000..7726a1d --- /dev/null +++ b/core/design-system/src/main/res/drawable/ic_stats_selected.xml @@ -0,0 +1,15 @@ + + + + diff --git a/core/design-system/src/main/res/drawable/ic_stats_unselected.xml b/core/design-system/src/main/res/drawable/ic_stats_unselected.xml new file mode 100644 index 0000000..3befa41 --- /dev/null +++ b/core/design-system/src/main/res/drawable/ic_stats_unselected.xml @@ -0,0 +1,12 @@ + + + From 8d5a9a8fe99dfe47aeadcbb16e81992807506440 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sun, 18 Jan 2026 17:32:06 +0900 Subject: [PATCH 10/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20MainTab=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/twix/main/model/MainTab.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 feature/main/src/main/java/com/twix/main/model/MainTab.kt diff --git a/feature/main/src/main/java/com/twix/main/model/MainTab.kt b/feature/main/src/main/java/com/twix/main/model/MainTab.kt new file mode 100644 index 0000000..8e5c5cf --- /dev/null +++ b/feature/main/src/main/java/com/twix/main/model/MainTab.kt @@ -0,0 +1,22 @@ +package com.twix.main.model + +import com.twix.designsystem.R + + +enum class MainTab( + val selectedIcon: Int, + val unselectedIcon: Int, +) { + HOME( + selectedIcon = R.drawable.ic_home_selected, + unselectedIcon = R.drawable.ic_home_unselected + ), + STATS( + selectedIcon = R.drawable.ic_stats_selected, + unselectedIcon = R.drawable.ic_stats_unselected + ), + COUPLE( + selectedIcon = R.drawable.ic_couple_selected, + unselectedIcon = R.drawable.ic_couple_unselected + ) +} \ No newline at end of file From c3aa31a27698e56127d874733a4ea5cca99e8381 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sun, 18 Jan 2026 17:39:06 +0900 Subject: [PATCH 11/62] =?UTF-8?q?=F0=9F=8D=B1=20Chore:=20string=20?= =?UTF-8?q?=EB=A6=AC=EC=86=8C=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 --- core/design-system/src/main/res/values/strings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 core/design-system/src/main/res/values/strings.xml diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml new file mode 100644 index 0000000..8b2cc83 --- /dev/null +++ b/core/design-system/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + + + + 통계 + 커플페이지 + + \ No newline at end of file From f77996b6fd1c8414c1d1036f42b7020a9c58daf7 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sun, 18 Jan 2026 17:39:36 +0900 Subject: [PATCH 12/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20GNB=20=ED=83=AD=20?= =?UTF-8?q?=ED=83=80=EC=9D=B4=ED=8B=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/src/main/java/com/twix/main/model/MainTab.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/feature/main/src/main/java/com/twix/main/model/MainTab.kt b/feature/main/src/main/java/com/twix/main/model/MainTab.kt index 8e5c5cf..eec8b91 100644 --- a/feature/main/src/main/java/com/twix/main/model/MainTab.kt +++ b/feature/main/src/main/java/com/twix/main/model/MainTab.kt @@ -6,17 +6,21 @@ import com.twix.designsystem.R enum class MainTab( val selectedIcon: Int, val unselectedIcon: Int, + val title: Int ) { HOME( selectedIcon = R.drawable.ic_home_selected, - unselectedIcon = R.drawable.ic_home_unselected + unselectedIcon = R.drawable.ic_home_unselected, + title = R.string.word_home ), STATS( selectedIcon = R.drawable.ic_stats_selected, - unselectedIcon = R.drawable.ic_stats_unselected + unselectedIcon = R.drawable.ic_stats_unselected, + title = R.string.word_stats ), COUPLE( selectedIcon = R.drawable.ic_couple_selected, - unselectedIcon = R.drawable.ic_couple_unselected + unselectedIcon = R.drawable.ic_couple_unselected, + title = R.string.word_couple_page ) } \ No newline at end of file From cef4b052dc308328b867136427d6f4a66d2b045a Mon Sep 17 00:00:00 2001 From: dogmania Date: Tue, 20 Jan 2026 21:23:00 +0900 Subject: [PATCH 13/62] =?UTF-8?q?=F0=9F=94=A5=20Remove:=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/main/ExampleInstrumentedTest.kt | 24 ------------------- .../java/com/twix/main/ExampleUnitTest.kt | 17 ------------- 2 files changed, 41 deletions(-) delete mode 100644 feature/main/src/androidTest/java/com/twix/main/ExampleInstrumentedTest.kt delete mode 100644 feature/main/src/test/java/com/twix/main/ExampleUnitTest.kt diff --git a/feature/main/src/androidTest/java/com/twix/main/ExampleInstrumentedTest.kt b/feature/main/src/androidTest/java/com/twix/main/ExampleInstrumentedTest.kt deleted file mode 100644 index c381d85..0000000 --- a/feature/main/src/androidTest/java/com/twix/main/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.twix.main - -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.twix.main.test", appContext.packageName) - } -} \ No newline at end of file diff --git a/feature/main/src/test/java/com/twix/main/ExampleUnitTest.kt b/feature/main/src/test/java/com/twix/main/ExampleUnitTest.kt deleted file mode 100644 index a919746..0000000 --- a/feature/main/src/test/java/com/twix/main/ExampleUnitTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.twix.main - -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) - } -} \ No newline at end of file From 9a0477230bd69f770826e6b4d75b022871dc3859 Mon Sep 17 00:00:00 2001 From: dogmania Date: Tue, 20 Jan 2026 21:23:59 +0900 Subject: [PATCH 14/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20nameSpace=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 --- feature/main/build.gradle.kts | 6 +++++- .../src/main/java/com/twix/main/model/MainTab.kt | 13 ++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/feature/main/build.gradle.kts b/feature/main/build.gradle.kts index b22d35b..049447f 100644 --- a/feature/main/build.gradle.kts +++ b/feature/main/build.gradle.kts @@ -1,3 +1,7 @@ plugins { alias(libs.plugins.twix.feature) -} \ No newline at end of file +} + +android { + namespace = "com.twix.main" +} diff --git a/feature/main/src/main/java/com/twix/main/model/MainTab.kt b/feature/main/src/main/java/com/twix/main/model/MainTab.kt index eec8b91..17da6b4 100644 --- a/feature/main/src/main/java/com/twix/main/model/MainTab.kt +++ b/feature/main/src/main/java/com/twix/main/model/MainTab.kt @@ -2,25 +2,24 @@ package com.twix.main.model import com.twix.designsystem.R - enum class MainTab( val selectedIcon: Int, val unselectedIcon: Int, - val title: Int + val title: Int, ) { HOME( selectedIcon = R.drawable.ic_home_selected, unselectedIcon = R.drawable.ic_home_unselected, - title = R.string.word_home + title = R.string.word_home, ), STATS( selectedIcon = R.drawable.ic_stats_selected, unselectedIcon = R.drawable.ic_stats_unselected, - title = R.string.word_stats + title = R.string.word_stats, ), COUPLE( selectedIcon = R.drawable.ic_couple_selected, unselectedIcon = R.drawable.ic_couple_unselected, - title = R.string.word_couple_page - ) -} \ No newline at end of file + title = R.string.word_couple_page, + ), +} From c7ec1c0ba2e645fe376a78ab90b3b02880746309 Mon Sep 17 00:00:00 2001 From: dogmania Date: Tue, 20 Jan 2026 21:26:57 +0900 Subject: [PATCH 15/62] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20ktlint?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/twix/ui/base/BaseViewModel.kt | 55 +++++++++---------- .../src/main/java/com/twix/ui/base/Intent.kt | 2 +- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt b/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt index f75efaa..dd13860 100644 --- a/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt +++ b/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt @@ -11,10 +11,9 @@ import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.launch import kotlin.coroutines.cancellation.CancellationException -abstract class BaseViewModel( - initialState: S -): ViewModel() { - +abstract class BaseViewModel( + initialState: S, +) : ViewModel() { protected open val logger: Logger = Logger.withTag(this::class.simpleName ?: "BaseViewModel") @@ -73,34 +72,34 @@ abstract class BaseViewModel( * */ protected abstract suspend fun handleIntent(intent: I) - /** * 서버 통신 메서드 호출 및 응답을 처리하는 헬퍼 메서드 * */ protected fun launchResult( - onStart: (() -> Unit)? = null, // 비동기 시작 전 처리해야 할 로직 ex) 로딩 - onFinally: (() -> Unit)? = null, // 비동기 종료 후 리소스 정리 - onSuccess: (D) -> Unit, // 비동기 메서드 호출이 성공했을 때 처리해야 할 로직 - onError: ((Throwable) -> Unit)? = null, // 비동기 메서드 호출에 실패했을 때 처리해야 할 로직 - block: suspend () -> Result // 비동기 메서드 ex) 서버 통신 메서드 - ): Job = viewModelScope.launch { - try { - onStart?.invoke() - - val result = block.invoke() - result.fold( - onSuccess = { data -> onSuccess(data) }, - onFailure = { t -> - if (t is CancellationException) throw t - - handleError(t) - onError?.invoke(t) - } - ) - } finally { - onFinally?.invoke() + onStart: (() -> Unit)? = null, // 비동기 시작 전 처리해야 할 로직 ex) 로딩 + onFinally: (() -> Unit)? = null, // 비동기 종료 후 리소스 정리 + onSuccess: (D) -> Unit, // 비동기 메서드 호출이 성공했을 때 처리해야 할 로직 + onError: ((Throwable) -> Unit)? = null, // 비동기 메서드 호출에 실패했을 때 처리해야 할 로직 + block: suspend () -> Result, // 비동기 메서드 ex) 서버 통신 메서드 + ): Job = + viewModelScope.launch { + try { + onStart?.invoke() + + val result = block.invoke() + result.fold( + onSuccess = { data -> onSuccess(data) }, + onFailure = { t -> + if (t is CancellationException) throw t + + handleError(t) + onError?.invoke(t) + }, + ) + } finally { + onFinally?.invoke() + } } - } /** * 에러 핸들링 메서드 @@ -114,4 +113,4 @@ abstract class BaseViewModel( intentChannel.close() super.onCleared() } -} \ No newline at end of file +} diff --git a/core/ui/src/main/java/com/twix/ui/base/Intent.kt b/core/ui/src/main/java/com/twix/ui/base/Intent.kt index 955f75c..a8d758d 100644 --- a/core/ui/src/main/java/com/twix/ui/base/Intent.kt +++ b/core/ui/src/main/java/com/twix/ui/base/Intent.kt @@ -1,3 +1,3 @@ package com.twix.ui.base -interface Intent \ No newline at end of file +interface Intent From ec40906308eab962b30cfdef6e599cd7207b2542 Mon Sep 17 00:00:00 2001 From: dogmania Date: Tue, 20 Jan 2026 23:38:54 +0900 Subject: [PATCH 16/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20NoSideEffect=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/ui/src/main/java/com/twix/ui/base/NoSideEffect.kt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 core/ui/src/main/java/com/twix/ui/base/NoSideEffect.kt diff --git a/core/ui/src/main/java/com/twix/ui/base/NoSideEffect.kt b/core/ui/src/main/java/com/twix/ui/base/NoSideEffect.kt new file mode 100644 index 0000000..2165c2e --- /dev/null +++ b/core/ui/src/main/java/com/twix/ui/base/NoSideEffect.kt @@ -0,0 +1,3 @@ +package com.twix.ui.base + +data object NoSideEffect : SideEffect From a59bdb19da9b1f8a9f49e9c124c261b549006df8 Mon Sep 17 00:00:00 2001 From: dogmania Date: Tue, 20 Jan 2026 23:39:09 +0900 Subject: [PATCH 17/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20MainIntent=20?= =?UTF-8?q?=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/main/src/main/java/com/twix/main/MainIntent.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 feature/main/src/main/java/com/twix/main/MainIntent.kt diff --git a/feature/main/src/main/java/com/twix/main/MainIntent.kt b/feature/main/src/main/java/com/twix/main/MainIntent.kt new file mode 100644 index 0000000..bcdb3b9 --- /dev/null +++ b/feature/main/src/main/java/com/twix/main/MainIntent.kt @@ -0,0 +1,10 @@ +package com.twix.main + +import com.twix.main.model.MainTab +import com.twix.ui.base.Intent + +sealed interface MainIntent : Intent { + data class SelectTab( + val tab: MainTab, + ) : MainIntent +} From 4334c7a0eb196e4d4dc154d62db5c9cff504ec05 Mon Sep 17 00:00:00 2001 From: dogmania Date: Tue, 20 Jan 2026 23:39:33 +0900 Subject: [PATCH 18/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20MainScreen=20?= =?UTF-8?q?=EC=BB=A8=ED=85=8C=EC=9D=B4=EB=84=88=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EC=A0=80=EB=B8=94=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/twix/main/MainScreen.kt | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 feature/main/src/main/java/com/twix/main/MainScreen.kt diff --git a/feature/main/src/main/java/com/twix/main/MainScreen.kt b/feature/main/src/main/java/com/twix/main/MainScreen.kt new file mode 100644 index 0000000..07dcf35 --- /dev/null +++ b/feature/main/src/main/java/com/twix/main/MainScreen.kt @@ -0,0 +1,62 @@ +package com.twix.main + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.twix.designsystem.theme.CommonColor +import com.twix.main.component.MainBottomBar +import com.twix.main.model.MainTab +import org.koin.androidx.compose.koinViewModel + +@Composable +fun MainRoute(viewModel: MainViewModel = koinViewModel()) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + MainScreen( + selectedTab = uiState.selectedTab, + onTabClick = { tab -> viewModel.dispatch(MainIntent.SelectTab(tab)) }, + content = { tab -> + when (tab) { + MainTab.HOME -> Box(modifier = Modifier.fillMaxSize()) + MainTab.STATS -> Box(modifier = Modifier.fillMaxSize()) + MainTab.COUPLE -> Box(modifier = Modifier.fillMaxSize()) + } + }, + ) +} + +@Composable +private fun MainScreen( + selectedTab: MainTab, + onTabClick: (MainTab) -> Unit, + content: @Composable (MainTab) -> Unit, +) { + Scaffold( + modifier = + Modifier + .fillMaxSize() + .background(CommonColor.White), + bottomBar = { + MainBottomBar( + selectedTab = selectedTab, + onTabClick = onTabClick, + ) + }, + ) { padding -> + Box( + modifier = + Modifier + .fillMaxSize() + .background(CommonColor.White) + .padding(padding), + ) { + content(selectedTab) + } + } +} From 66a00f34a7ce195c7c62ceb1885fb3293fb6529c Mon Sep 17 00:00:00 2001 From: dogmania Date: Tue, 20 Jan 2026 23:39:52 +0900 Subject: [PATCH 19/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20GNB=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/main/component/MainBottomBar.kt | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 feature/main/src/main/java/com/twix/main/component/MainBottomBar.kt diff --git a/feature/main/src/main/java/com/twix/main/component/MainBottomBar.kt b/feature/main/src/main/java/com/twix/main/component/MainBottomBar.kt new file mode 100644 index 0000000..ee623d8 --- /dev/null +++ b/feature/main/src/main/java/com/twix/main/component/MainBottomBar.kt @@ -0,0 +1,95 @@ +package com.twix.main.component + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +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.height +import androidx.compose.foundation.layout.size +import androidx.compose.material3.HorizontalDivider +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.twix.designsystem.components.text.AppText +import com.twix.designsystem.theme.CommonColor +import com.twix.designsystem.theme.GrayColor +import com.twix.domain.model.enums.AppTextStyle +import com.twix.main.model.MainTab + +@Composable +fun MainBottomBar( + selectedTab: MainTab, + onTabClick: (MainTab) -> Unit, +) { + Column { + HorizontalDivider(thickness = 1.dp, color = GrayColor.C100) + + Row( + modifier = + Modifier + .fillMaxWidth() + .height(58.dp) + .background(CommonColor.White), + verticalAlignment = Alignment.CenterVertically, + ) { + MainTab.entries.forEach { tab -> + MainBottomBarItem( + modifier = Modifier.weight(1f), + tab = tab, + selected = selectedTab == tab, + onClick = { onTabClick(tab) }, + ) + } + } + } +} + +@Composable +private fun MainBottomBarItem( + modifier: Modifier = Modifier, + tab: MainTab, + selected: Boolean, + onClick: () -> Unit, +) { + val haptic = LocalHapticFeedback.current + val interactionSource = remember { MutableInteractionSource() } + val icon = if (selected) tab.selectedIcon else tab.unselectedIcon + + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(4.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Image( + painter = painterResource(icon), + contentDescription = null, + modifier = + Modifier + .size(24.dp) + .clickable( + indication = null, + interactionSource = interactionSource, + onClick = { + haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove) + onClick() + }, + ), + ) + + AppText( + text = stringResource(tab.title), + color = GrayColor.C500, + style = AppTextStyle.C2, + ) + } +} From b40d4806a20801708a05b5c418ad120684d625ae Mon Sep 17 00:00:00 2001 From: dogmania Date: Tue, 20 Jan 2026 23:40:30 +0900 Subject: [PATCH 20/62] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20state=20?= =?UTF-8?q?->=20uiState=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 --- core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt b/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt index dd13860..74fbaf5 100644 --- a/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt +++ b/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt @@ -19,7 +19,7 @@ abstract class BaseViewModel( // State private val stateHolder = StateHolder(initialState) - val state: StateFlow = stateHolder.state + val uiState: StateFlow = stateHolder.state protected val currentState: S get() = stateHolder.current // SideEffect From 3fe39c5740679f8dd84316b35e117f12c9279c28 Mon Sep 17 00:00:00 2001 From: dogmania Date: Tue, 20 Jan 2026 23:40:50 +0900 Subject: [PATCH 21/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20:feature:main=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f84666b..53d4876 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -34,6 +34,7 @@ dependencies { implementation(projects.data) implementation(projects.domain) implementation(projects.feature.login) + implementation(projects.feature.main) // Firebase implementation(platform(libs.google.firebase.bom)) From 48125e09d6071633b39dd0e9e9780424281f26d7 Mon Sep 17 00:00:00 2001 From: dogmania Date: Tue, 20 Jan 2026 23:41:14 +0900 Subject: [PATCH 22/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20mainModule=20DI=20?= =?UTF-8?q?=EC=BB=A8=ED=85=8C=EC=9D=B4=EB=84=88=20=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/yapp/twix/di/FeatureModules.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/yapp/twix/di/FeatureModules.kt b/app/src/main/java/com/yapp/twix/di/FeatureModules.kt index 2636772..fc7086d 100644 --- a/app/src/main/java/com/yapp/twix/di/FeatureModules.kt +++ b/app/src/main/java/com/yapp/twix/di/FeatureModules.kt @@ -1,9 +1,11 @@ package com.yapp.twix.di import com.twix.login.di.loginModule +import com.twix.main.di.mainModule import org.koin.core.module.Module val featureModules: List = listOf( loginModule, + mainModule, ) From 16bfe46f2defb9218556603a3e76be87b07df1f7 Mon Sep 17 00:00:00 2001 From: dogmania Date: Tue, 20 Jan 2026 23:41:40 +0900 Subject: [PATCH 23/62] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=8B=9C=EC=9E=91=20=ED=99=94=EB=A9=B4=EC=9D=84=20=EC=9E=84?= =?UTF-8?q?=EC=8B=9C=EB=A1=9C=20MainGraph=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 --- core/navigation/src/main/java/com/twix/navigation/AppNavHost.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/navigation/src/main/java/com/twix/navigation/AppNavHost.kt b/core/navigation/src/main/java/com/twix/navigation/AppNavHost.kt index 6a20fb4..6502732 100644 --- a/core/navigation/src/main/java/com/twix/navigation/AppNavHost.kt +++ b/core/navigation/src/main/java/com/twix/navigation/AppNavHost.kt @@ -23,7 +23,7 @@ fun AppNavHost() { } val start = contributors - .firstOrNull { it.graphRoute == NavRoutes.LoginGraph } + .firstOrNull { it.graphRoute == NavRoutes.MainGraph } ?.graphRoute ?: error("해당 Graph를 찾을 수 없습니다.") val duration = 300 From c6d62c9d14af96a144d64300ad62f2b4724f09f1 Mon Sep 17 00:00:00 2001 From: dogmania Date: Tue, 20 Jan 2026 23:42:07 +0900 Subject: [PATCH 24/62] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20Login=20?= =?UTF-8?q?->=20LoginRoute=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 --- .../main/java/com/twix/navigation/NavRoutes.kt | 16 +++++++++------- .../com/twix/login/navigation/LoginNavGraph.kt | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt b/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt index 5a10871..625bc78 100644 --- a/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt +++ b/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt @@ -10,15 +10,17 @@ package com.twix.navigation sealed class NavRoutes( val route: String, ) { + /** + * LoginGraph + * */ object LoginGraph : NavRoutes("login_graph") - object Login : NavRoutes("login") + object LoginRoute : NavRoutes("login") - object HomeGraph : NavRoutes("home_graph") + /** + * MainGraph + * */ + object MainGraph : NavRoutes("main_graph") - object Home : NavRoutes("home") - - object HomeDetail : NavRoutes("home_detail/{id}") { - fun createRoute(id: String) = "home_detail/$id" - } + object MainRoute : NavRoutes("main") } diff --git a/feature/login/src/main/java/com/twix/login/navigation/LoginNavGraph.kt b/feature/login/src/main/java/com/twix/login/navigation/LoginNavGraph.kt index 5d0d2e7..2d609eb 100644 --- a/feature/login/src/main/java/com/twix/login/navigation/LoginNavGraph.kt +++ b/feature/login/src/main/java/com/twix/login/navigation/LoginNavGraph.kt @@ -12,14 +12,14 @@ object LoginNavGraph : NavGraphContributor { override val graphRoute: NavRoutes get() = NavRoutes.LoginGraph override val startDestination: String - get() = NavRoutes.Login.route + get() = NavRoutes.LoginRoute.route override fun NavGraphBuilder.registerGraph(navController: NavHostController) { navigation( route = graphRoute.route, startDestination = startDestination, ) { - composable(NavRoutes.Login.route) { + composable(NavRoutes.LoginRoute.route) { LoginScreen() } } From 32ba42cb2946cbd29d506e6bd17c76d3fcf5fc9e Mon Sep 17 00:00:00 2001 From: dogmania Date: Tue, 20 Jan 2026 23:42:50 +0900 Subject: [PATCH 25/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20=ED=83=AD=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=20Intent=20=EC=B2=98=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/twix/main/MainViewModel.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 feature/main/src/main/java/com/twix/main/MainViewModel.kt diff --git a/feature/main/src/main/java/com/twix/main/MainViewModel.kt b/feature/main/src/main/java/com/twix/main/MainViewModel.kt new file mode 100644 index 0000000..9dd246d --- /dev/null +++ b/feature/main/src/main/java/com/twix/main/MainViewModel.kt @@ -0,0 +1,20 @@ +package com.twix.main + +import com.twix.main.model.MainTab +import com.twix.main.model.MainUiState +import com.twix.ui.base.BaseViewModel +import com.twix.ui.base.NoSideEffect + +class MainViewModel : BaseViewModel(MainUiState()) { + override suspend fun handleIntent(intent: MainIntent) { + when (intent) { + is MainIntent.SelectTab -> selectTab(intent.tab) + } + } + + private fun selectTab(tab: MainTab) { + if (currentState.selectedTab == tab) return + + reduce { copy(selectedTab = tab) } + } +} From 3c2abcd4f8e8e928461904cef6c3240d61f6f5a4 Mon Sep 17 00:00:00 2001 From: dogmania Date: Tue, 20 Jan 2026 23:43:04 +0900 Subject: [PATCH 26/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20MainModule=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/twix/main/di/MainModule.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 feature/main/src/main/java/com/twix/main/di/MainModule.kt diff --git a/feature/main/src/main/java/com/twix/main/di/MainModule.kt b/feature/main/src/main/java/com/twix/main/di/MainModule.kt new file mode 100644 index 0000000..60b3a8f --- /dev/null +++ b/feature/main/src/main/java/com/twix/main/di/MainModule.kt @@ -0,0 +1,15 @@ +package com.twix.main.di + +import com.twix.main.MainViewModel +import com.twix.main.navigation.MainNavGraph +import com.twix.navigation.NavRoutes +import com.twix.navigation.base.NavGraphContributor +import org.koin.core.module.dsl.viewModelOf +import org.koin.core.qualifier.named +import org.koin.dsl.module + +val mainModule = + module { + viewModelOf(::MainViewModel) + single(named(NavRoutes.MainGraph.route)) { MainNavGraph } + } From 59bbb29df7dfcc081908f30815101c32ed36b82a Mon Sep 17 00:00:00 2001 From: dogmania Date: Tue, 20 Jan 2026 23:43:32 +0900 Subject: [PATCH 27/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20selectedTab=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EB=B3=80=EC=88=98=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/twix/main/model/MainUiState.kt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 feature/main/src/main/java/com/twix/main/model/MainUiState.kt diff --git a/feature/main/src/main/java/com/twix/main/model/MainUiState.kt b/feature/main/src/main/java/com/twix/main/model/MainUiState.kt new file mode 100644 index 0000000..2b2222f --- /dev/null +++ b/feature/main/src/main/java/com/twix/main/model/MainUiState.kt @@ -0,0 +1,9 @@ +package com.twix.main.model + +import androidx.compose.runtime.Immutable +import com.twix.ui.base.State + +@Immutable +data class MainUiState( + val selectedTab: MainTab = MainTab.HOME, +) : State From 1d58ba6e7837642e59f2d30f6af67200752bdbff Mon Sep 17 00:00:00 2001 From: dogmania Date: Tue, 20 Jan 2026 23:43:43 +0900 Subject: [PATCH 28/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20MainNavGraph=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/main/navigation/MainNavGraph.kt | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 feature/main/src/main/java/com/twix/main/navigation/MainNavGraph.kt diff --git a/feature/main/src/main/java/com/twix/main/navigation/MainNavGraph.kt b/feature/main/src/main/java/com/twix/main/navigation/MainNavGraph.kt new file mode 100644 index 0000000..a96a4e1 --- /dev/null +++ b/feature/main/src/main/java/com/twix/main/navigation/MainNavGraph.kt @@ -0,0 +1,27 @@ +package com.twix.main.navigation + +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.compose.composable +import androidx.navigation.navigation +import com.twix.main.MainRoute +import com.twix.navigation.NavRoutes +import com.twix.navigation.base.NavGraphContributor + +object MainNavGraph : NavGraphContributor { + override val graphRoute: NavRoutes + get() = NavRoutes.MainGraph + override val startDestination: String + get() = NavRoutes.MainRoute.route + + override fun NavGraphBuilder.registerGraph(navController: NavHostController) { + navigation( + route = graphRoute.route, + startDestination = startDestination, + ) { + composable(NavRoutes.MainRoute.route) { + MainRoute() + } + } + } +} From 5997f8efeec5f31205fdf6aeb5f4f4ea158b14f3 Mon Sep 17 00:00:00 2001 From: dogmania Date: Wed, 21 Jan 2026 02:00:39 +0900 Subject: [PATCH 29/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20HomeModule=20?= =?UTF-8?q?=EB=B7=B0=EB=AA=A8=EB=8D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/src/main/java/com/twix/home/di/HomeModule.kt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 feature/main/src/main/java/com/twix/home/di/HomeModule.kt diff --git a/feature/main/src/main/java/com/twix/home/di/HomeModule.kt b/feature/main/src/main/java/com/twix/home/di/HomeModule.kt new file mode 100644 index 0000000..8db9b9a --- /dev/null +++ b/feature/main/src/main/java/com/twix/home/di/HomeModule.kt @@ -0,0 +1,9 @@ +package com.twix.home.di + +import com.twix.home.HomeViewModel +import org.koin.core.module.dsl.viewModelOf +import org.koin.dsl.module + +val homeModule = module { + viewModelOf(::HomeViewModel) +} \ No newline at end of file From 4282ca3766f27c648274953eb728e8f8f0aa70be Mon Sep 17 00:00:00 2001 From: dogmania Date: Wed, 21 Jan 2026 02:00:54 +0900 Subject: [PATCH 30/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20HomeModule=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/yapp/twix/di/FeatureModules.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/yapp/twix/di/FeatureModules.kt b/app/src/main/java/com/yapp/twix/di/FeatureModules.kt index fc7086d..0711a25 100644 --- a/app/src/main/java/com/yapp/twix/di/FeatureModules.kt +++ b/app/src/main/java/com/yapp/twix/di/FeatureModules.kt @@ -1,5 +1,6 @@ package com.yapp.twix.di +import com.twix.home.di.homeModule import com.twix.login.di.loginModule import com.twix.main.di.mainModule import org.koin.core.module.Module @@ -8,4 +9,5 @@ val featureModules: List = listOf( loginModule, mainModule, + homeModule ) From a1a32209d3a5f697cef60fc6d0613a2f9c0e7962 Mon Sep 17 00:00:00 2001 From: dogmania Date: Wed, 21 Jan 2026 02:01:12 +0900 Subject: [PATCH 31/62] =?UTF-8?q?=F0=9F=8D=B1=20Chore:=20=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=EC=97=B4=20=EB=A6=AC=EC=86=8C=EC=8A=A4=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 --- core/design-system/src/main/res/values/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index 8b2cc83..b199721 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -5,4 +5,7 @@ 통계 커플페이지 + + 오늘 우리 목표 + \ No newline at end of file From 6c558e5f63036a7073a30d2422f6eec3be000c16 Mon Sep 17 00:00:00 2001 From: dogmania Date: Wed, 21 Jan 2026 02:01:44 +0900 Subject: [PATCH 32/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20HomeModule=20?= =?UTF-8?q?=EC=B4=88=EA=B8=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/yapp/twix/di/FeatureModules.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/yapp/twix/di/FeatureModules.kt b/app/src/main/java/com/yapp/twix/di/FeatureModules.kt index 0711a25..7365a49 100644 --- a/app/src/main/java/com/yapp/twix/di/FeatureModules.kt +++ b/app/src/main/java/com/yapp/twix/di/FeatureModules.kt @@ -9,5 +9,5 @@ val featureModules: List = listOf( loginModule, mainModule, - homeModule + homeModule, ) From 6527cea0328e73649533b6be757adc306b64a247 Mon Sep 17 00:00:00 2001 From: dogmania Date: Wed, 21 Jan 2026 02:01:58 +0900 Subject: [PATCH 33/62] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20ktlint?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/main/src/main/java/com/twix/home/di/HomeModule.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/feature/main/src/main/java/com/twix/home/di/HomeModule.kt b/feature/main/src/main/java/com/twix/home/di/HomeModule.kt index 8db9b9a..4ddd9c0 100644 --- a/feature/main/src/main/java/com/twix/home/di/HomeModule.kt +++ b/feature/main/src/main/java/com/twix/home/di/HomeModule.kt @@ -4,6 +4,7 @@ import com.twix.home.HomeViewModel import org.koin.core.module.dsl.viewModelOf import org.koin.dsl.module -val homeModule = module { - viewModelOf(::HomeViewModel) -} \ No newline at end of file +val homeModule = + module { + viewModelOf(::HomeViewModel) + } From c5223599c4c66afcd3ad64dabd6cfe462098f67e Mon Sep 17 00:00:00 2001 From: dogmania Date: Wed, 21 Jan 2026 02:02:25 +0900 Subject: [PATCH 34/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20HomeRoute=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 --- feature/main/src/main/java/com/twix/main/MainScreen.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/feature/main/src/main/java/com/twix/main/MainScreen.kt b/feature/main/src/main/java/com/twix/main/MainScreen.kt index 07dcf35..d29f456 100644 --- a/feature/main/src/main/java/com/twix/main/MainScreen.kt +++ b/feature/main/src/main/java/com/twix/main/MainScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.twix.designsystem.theme.CommonColor +import com.twix.home.HomeRoute import com.twix.main.component.MainBottomBar import com.twix.main.model.MainTab import org.koin.androidx.compose.koinViewModel @@ -23,7 +24,7 @@ fun MainRoute(viewModel: MainViewModel = koinViewModel()) { onTabClick = { tab -> viewModel.dispatch(MainIntent.SelectTab(tab)) }, content = { tab -> when (tab) { - MainTab.HOME -> Box(modifier = Modifier.fillMaxSize()) + MainTab.HOME -> HomeRoute() MainTab.STATS -> Box(modifier = Modifier.fillMaxSize()) MainTab.COUPLE -> Box(modifier = Modifier.fillMaxSize()) } From 14c23722c8b45c0cd8cedf3fddadd37d4bcc3dea Mon Sep 17 00:00:00 2001 From: dogmania Date: Wed, 21 Jan 2026 02:02:46 +0900 Subject: [PATCH 35/62] =?UTF-8?q?=F0=9F=8D=B1=20Chore:=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EB=A6=AC=EC=86=8C=EC=8A=A4=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 --- .../src/main/res/drawable/ic_alert.xml | 12 ++++++++++++ .../src/main/res/drawable/ic_drop_down_arrow.xml | 13 +++++++++++++ .../src/main/res/drawable/ic_setting.xml | 16 ++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 core/design-system/src/main/res/drawable/ic_alert.xml create mode 100644 core/design-system/src/main/res/drawable/ic_drop_down_arrow.xml create mode 100644 core/design-system/src/main/res/drawable/ic_setting.xml diff --git a/core/design-system/src/main/res/drawable/ic_alert.xml b/core/design-system/src/main/res/drawable/ic_alert.xml new file mode 100644 index 0000000..945fe48 --- /dev/null +++ b/core/design-system/src/main/res/drawable/ic_alert.xml @@ -0,0 +1,12 @@ + + + diff --git a/core/design-system/src/main/res/drawable/ic_drop_down_arrow.xml b/core/design-system/src/main/res/drawable/ic_drop_down_arrow.xml new file mode 100644 index 0000000..8caeb18 --- /dev/null +++ b/core/design-system/src/main/res/drawable/ic_drop_down_arrow.xml @@ -0,0 +1,13 @@ + + + diff --git a/core/design-system/src/main/res/drawable/ic_setting.xml b/core/design-system/src/main/res/drawable/ic_setting.xml new file mode 100644 index 0000000..cd02394 --- /dev/null +++ b/core/design-system/src/main/res/drawable/ic_setting.xml @@ -0,0 +1,16 @@ + + + + From c52ec9dc41800c7aa319e2e4c70b2384be27c5fa Mon Sep 17 00:00:00 2001 From: dogmania Date: Wed, 21 Jan 2026 02:04:19 +0900 Subject: [PATCH 36/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20SelectDate=20?= =?UTF-8?q?=EC=9D=B8=ED=85=90=ED=8A=B8=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/main/src/main/java/com/twix/home/HomeIntent.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 feature/main/src/main/java/com/twix/home/HomeIntent.kt diff --git a/feature/main/src/main/java/com/twix/home/HomeIntent.kt b/feature/main/src/main/java/com/twix/home/HomeIntent.kt new file mode 100644 index 0000000..dd3459c --- /dev/null +++ b/feature/main/src/main/java/com/twix/home/HomeIntent.kt @@ -0,0 +1,10 @@ +package com.twix.home + +import com.twix.ui.base.Intent +import java.time.LocalDate + +sealed interface HomeIntent : Intent { + data class SelectDate( + val date: LocalDate, + ) : HomeIntent +} From 12b5a8de53d476c05af9037ee31467136e050321 Mon Sep 17 00:00:00 2001 From: dogmania Date: Wed, 21 Jan 2026 02:04:40 +0900 Subject: [PATCH 37/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EB=B0=94=ED=85=80?= =?UTF-8?q?=EC=8B=9C=ED=8A=B8=20=EB=A0=8C=EB=8D=94=EB=A7=81=20=EC=82=AC?= =?UTF-8?q?=EC=9D=B4=EB=93=9C=20=EC=9D=B4=ED=8E=99=ED=8A=B8=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/main/src/main/java/com/twix/home/HomeSideEffect.kt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 feature/main/src/main/java/com/twix/home/HomeSideEffect.kt diff --git a/feature/main/src/main/java/com/twix/home/HomeSideEffect.kt b/feature/main/src/main/java/com/twix/home/HomeSideEffect.kt new file mode 100644 index 0000000..5b1046d --- /dev/null +++ b/feature/main/src/main/java/com/twix/home/HomeSideEffect.kt @@ -0,0 +1,7 @@ +package com.twix.home + +import com.twix.ui.base.SideEffect + +interface HomeSideEffect : SideEffect { + data object ShowMonthPickerBottomSheet : HomeSideEffect +} From 414ac630b8417bbc3c6282632d7f0696d2a54a62 Mon Sep 17 00:00:00 2001 From: dogmania Date: Wed, 21 Jan 2026 02:05:14 +0900 Subject: [PATCH 38/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20SelectDate=20?= =?UTF-8?q?=EC=9D=B8=ED=85=90=ED=8A=B8=20=EC=B2=98=EB=A6=AC=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/twix/home/HomeViewModel.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 feature/main/src/main/java/com/twix/home/HomeViewModel.kt diff --git a/feature/main/src/main/java/com/twix/home/HomeViewModel.kt b/feature/main/src/main/java/com/twix/home/HomeViewModel.kt new file mode 100644 index 0000000..9f925a5 --- /dev/null +++ b/feature/main/src/main/java/com/twix/home/HomeViewModel.kt @@ -0,0 +1,22 @@ +package com.twix.home + +import com.twix.home.model.HomeUiState +import com.twix.ui.base.BaseViewModel +import java.time.LocalDate + +class HomeViewModel : + BaseViewModel( + HomeUiState(), + ) { + override suspend fun handleIntent(intent: HomeIntent) { + when (intent) { + is HomeIntent.SelectDate -> updateDate(intent.date) + } + } + + private fun updateDate(date: LocalDate) { + if (currentState.selectedDate == date) return + + reduce { copy(selectedDate = date) } + } +} From 4b3929bb674cbe7af3a20d51fc4537c821252fbd Mon Sep 17 00:00:00 2001 From: dogmania Date: Wed, 21 Jan 2026 02:05:28 +0900 Subject: [PATCH 39/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20HomeTopBar=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/home/component/HomeTopBar.kt | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 feature/main/src/main/java/com/twix/home/component/HomeTopBar.kt diff --git a/feature/main/src/main/java/com/twix/home/component/HomeTopBar.kt b/feature/main/src/main/java/com/twix/home/component/HomeTopBar.kt new file mode 100644 index 0000000..8f96797 --- /dev/null +++ b/feature/main/src/main/java/com/twix/home/component/HomeTopBar.kt @@ -0,0 +1,114 @@ +package com.twix.home.component + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +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.size +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.twix.designsystem.R +import com.twix.designsystem.components.text.AppText +import com.twix.designsystem.theme.GrayColor +import com.twix.domain.model.enums.AppTextStyle + +@Composable +fun HomeTopBar( + monthYearText: String, + onNotificationClick: () -> Unit, + onSettingClick: () -> Unit, +) { + Row( + modifier = + Modifier + .fillMaxWidth() + .padding(start = 20.dp, end = 10.dp), + verticalAlignment = Alignment.Bottom, + ) { + Column( + horizontalAlignment = Alignment.Start, + ) { + Spacer(Modifier.height(12.dp)) + + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + AppText( + text = monthYearText, + style = AppTextStyle.T3, + color = GrayColor.C400, + ) + + Image( + painter = painterResource(R.drawable.ic_drop_down_arrow), + contentDescription = "drop down arrow", + modifier = Modifier.size(20.dp), + colorFilter = ColorFilter.tint(GrayColor.C400), + ) + } + + Spacer(Modifier.height(4.dp)) + + AppText( + text = stringResource(R.string.home_today_goal), + style = AppTextStyle.H3, + color = GrayColor.C400, + ) + + Spacer(Modifier.height(2.dp)) + } + + Spacer(Modifier.weight(1f)) + + TopBarButton( + icon = painterResource(id = R.drawable.ic_alert), + contentDescription = "notification", + onClick = onNotificationClick, + ) + + TopBarButton( + icon = painterResource(id = R.drawable.ic_setting), + contentDescription = "setting", + onClick = onSettingClick, + ) + } +} + +@Composable +private fun TopBarButton( + icon: Painter, + contentDescription: String, + onClick: () -> Unit, +) { + val interactionSource = remember { MutableInteractionSource() } + + Box( + modifier = + Modifier + .size(40.dp) + .clickable( + indication = null, + interactionSource = interactionSource, + onClick = onClick, + ), + contentAlignment = Alignment.Center, + ) { + Image( + painter = icon, + contentDescription = contentDescription, + ) + } +} From fc9ac3028593217e78adf9f3f22abbe6f16fbef0 Mon Sep 17 00:00:00 2001 From: dogmania Date: Wed, 21 Jan 2026 02:05:52 +0900 Subject: [PATCH 40/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20HomeScreen=20TopBar?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/twix/home/HomeScreen.kt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 feature/main/src/main/java/com/twix/home/HomeScreen.kt diff --git a/feature/main/src/main/java/com/twix/home/HomeScreen.kt b/feature/main/src/main/java/com/twix/home/HomeScreen.kt new file mode 100644 index 0000000..3396d63 --- /dev/null +++ b/feature/main/src/main/java/com/twix/home/HomeScreen.kt @@ -0,0 +1,38 @@ +package com.twix.home + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.twix.home.component.HomeTopBar +import com.twix.home.model.HomeUiState +import org.koin.androidx.compose.koinViewModel + +@Composable +fun HomeRoute(viewModel: HomeViewModel = koinViewModel()) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + HomeScreen( + uiState = uiState, + ) +} + +@Composable +fun HomeScreen(uiState: HomeUiState) { + Column( + modifier = + Modifier + .fillMaxSize(), + ) { + HomeTopBar( + monthYearText = uiState.monthYear, + onNotificationClick = {}, + onSettingClick = {}, + ) + + Spacer(Modifier.weight(1f)) + } +} From 0add1e7680fd3f1ecf615113d6385f44f231505a Mon Sep 17 00:00:00 2001 From: dogmania Date: Wed, 21 Jan 2026 02:06:23 +0900 Subject: [PATCH 41/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20HomeUiState=20selecte?= =?UTF-8?q?dDate=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/twix/home/model/DateItemUiModel.kt | 6 ++++++ .../main/java/com/twix/home/model/HomeUiState.kt | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 feature/main/src/main/java/com/twix/home/model/DateItemUiModel.kt create mode 100644 feature/main/src/main/java/com/twix/home/model/HomeUiState.kt diff --git a/feature/main/src/main/java/com/twix/home/model/DateItemUiModel.kt b/feature/main/src/main/java/com/twix/home/model/DateItemUiModel.kt new file mode 100644 index 0000000..24bf669 --- /dev/null +++ b/feature/main/src/main/java/com/twix/home/model/DateItemUiModel.kt @@ -0,0 +1,6 @@ +package com.twix.home.model + +data class DateItemUiModel( + val dayOfMonth: Int, + val isSelected: Boolean, +) diff --git a/feature/main/src/main/java/com/twix/home/model/HomeUiState.kt b/feature/main/src/main/java/com/twix/home/model/HomeUiState.kt new file mode 100644 index 0000000..1f127c9 --- /dev/null +++ b/feature/main/src/main/java/com/twix/home/model/HomeUiState.kt @@ -0,0 +1,16 @@ +package com.twix.home.model + +import androidx.compose.runtime.Immutable +import com.twix.ui.base.State +import java.time.LocalDate +import java.time.YearMonth + +@Immutable +data class HomeUiState( + val month: YearMonth = YearMonth.now(), + val selectedDate: LocalDate = LocalDate.now(), + val dateItems: List = emptyList(), +) : State { + val monthYear: String + get() = "${selectedDate.month.value}월 ${selectedDate.year}" +} From 3cc468c4695d9273c4db07ef96831ce7ac29c9a1 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 24 Jan 2026 16:05:40 +0900 Subject: [PATCH 42/62] =?UTF-8?q?=F0=9F=8D=B1=20Chore:=20=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=EC=97=B4=20=EB=A6=AC=EC=86=8C=EC=8A=A4=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 --- core/design-system/src/main/res/values/strings.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index b199721..37b63e2 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -4,6 +4,15 @@ 통계 커플페이지 + 오늘 + + + + + + + + 오늘 우리 목표 From 33c214815ea0ccd961dde91c40031a10f964188c Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 24 Jan 2026 16:05:55 +0900 Subject: [PATCH 43/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20=ED=99=95=EC=9E=A5=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/ui/extension/NoRippleClickable.kt | 15 +++++++++++++++ .../java/com/twix/ui/extension/WeekStartSunday.kt | 10 ++++++++++ 2 files changed, 25 insertions(+) create mode 100644 core/ui/src/main/java/com/twix/ui/extension/NoRippleClickable.kt create mode 100644 core/ui/src/main/java/com/twix/ui/extension/WeekStartSunday.kt diff --git a/core/ui/src/main/java/com/twix/ui/extension/NoRippleClickable.kt b/core/ui/src/main/java/com/twix/ui/extension/NoRippleClickable.kt new file mode 100644 index 0000000..a6186e5 --- /dev/null +++ b/core/ui/src/main/java/com/twix/ui/extension/NoRippleClickable.kt @@ -0,0 +1,15 @@ +package com.twix.ui.extension + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier + +@Composable +fun Modifier.noRippleClickable(onClick: () -> Unit): Modifier = + clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() }, + onClick = onClick, + ) diff --git a/core/ui/src/main/java/com/twix/ui/extension/WeekStartSunday.kt b/core/ui/src/main/java/com/twix/ui/extension/WeekStartSunday.kt new file mode 100644 index 0000000..d6a5f61 --- /dev/null +++ b/core/ui/src/main/java/com/twix/ui/extension/WeekStartSunday.kt @@ -0,0 +1,10 @@ +package com.twix.ui.extension + +import java.time.DayOfWeek +import java.time.LocalDate +import java.time.temporal.TemporalAdjusters + +/** + * 주 시작을 일요일로 맞추는 메서드 + */ +fun LocalDate.weekStartSunday(): LocalDate = this.with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY)) From d8b6d81ddc53c2e4e331aade44b48b6a933199ac Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 24 Jan 2026 16:06:08 +0900 Subject: [PATCH 44/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20WeeklyCalendar=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/home/component/WeeklyCalendar.kt | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 feature/main/src/main/java/com/twix/home/component/WeeklyCalendar.kt diff --git a/feature/main/src/main/java/com/twix/home/component/WeeklyCalendar.kt b/feature/main/src/main/java/com/twix/home/component/WeeklyCalendar.kt new file mode 100644 index 0000000..c0c3de9 --- /dev/null +++ b/feature/main/src/main/java/com/twix/home/component/WeeklyCalendar.kt @@ -0,0 +1,143 @@ +package com.twix.home.component + +import androidx.compose.foundation.border +import androidx.compose.foundation.gestures.detectHorizontalDragGestures +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.twix.designsystem.R +import com.twix.designsystem.components.text.AppText +import com.twix.designsystem.theme.GrayColor +import com.twix.domain.model.enums.AppTextStyle +import com.twix.ui.extension.noRippleClickable +import com.twix.ui.extension.weekStartSunday +import java.time.LocalDate +import kotlin.math.abs + +@Composable +fun WeeklyCalendar( + selectedDate: LocalDate, + referenceDate: LocalDate, + onSelectDate: (LocalDate) -> Unit, + onPreviousWeek: () -> Unit, + onNextWeek: () -> Unit, + onUpdateMonthYear: (LocalDate) -> Unit +) { + val today = remember { LocalDate.now() } + val weekStart = remember(referenceDate) { referenceDate.weekStartSunday() } + val days = remember(weekStart) { (0..6).map { weekStart.plusDays(it.toLong()) } } + + val dayLabels = + listOf( + stringResource(R.string.word_sunday), + stringResource(R.string.word_monday), + stringResource(R.string.word_tuesday), + stringResource(R.string.word_wednesday), + stringResource(R.string.word_thursday), + stringResource(R.string.word_friday), + stringResource(R.string.word_saturday), + ) + // 스와이프 처리용 누적 드래그 + var dragSumPx by remember { mutableFloatStateOf(0f) } + + LaunchedEffect(days.first()) { + if (dragSumPx > 0) onUpdateMonthYear(days.first()) + else onUpdateMonthYear(days.last()) + } + + Column( + modifier = + Modifier + .fillMaxWidth() + .pointerInput(Unit) { + detectHorizontalDragGestures( + onDragStart = { dragSumPx = 0f }, + onHorizontalDrag = { _, dragAmount -> + dragSumPx += dragAmount + }, + onDragEnd = { + if (abs(dragSumPx) < 120f) return@detectHorizontalDragGestures + + if (dragSumPx > 0f) onPreviousWeek() else onNextWeek() + }, + ) + }.padding(horizontal = 12.dp), + ) { + Row( + modifier = Modifier.fillMaxWidth(), + ) { + days.forEachIndexed { index, date -> + val header = if (date == today) stringResource(R.string.word_today) else dayLabels[index] + + WeekDayCell( + header = header, + dayOfMonth = date.dayOfMonth, + selected = date == selectedDate, + onClick = { onSelectDate(date) }, + modifier = Modifier.weight(1f), + ) + } + } + } +} + +@Composable +private fun WeekDayCell( + header: String, + dayOfMonth: Int, + selected: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = + modifier + .padding(vertical = 6.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + AppText( + text = header, + style = AppTextStyle.C1, + color = GrayColor.C300, + modifier = Modifier.padding(bottom = 10.dp), + ) + + Box( + modifier = + Modifier + .size(44.dp) + .then( + if (selected) Modifier.border(1.dp, GrayColor.C500, CircleShape) else Modifier, + ), + contentAlignment = Alignment.Center, + ) { + AppText( + text = dayOfMonth.toString(), + style = AppTextStyle.B1, + color = GrayColor.C400, + modifier = + Modifier + .fillMaxSize() + .wrapContentSize(Alignment.Center) + .noRippleClickable { onClick() }, + ) + } + } +} From 0cc7027e0900323ca7aa72ea83a2c49e72ab4c24 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 24 Jan 2026 16:06:45 +0900 Subject: [PATCH 45/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=A3=BC=EA=B0=84=20?= =?UTF-8?q?=EB=8B=AC=EB=A0=A5=20=EC=A1=B0=EC=9E=91=20Intent=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/main/src/main/java/com/twix/home/HomeIntent.kt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/feature/main/src/main/java/com/twix/home/HomeIntent.kt b/feature/main/src/main/java/com/twix/home/HomeIntent.kt index dd3459c..8f406e6 100644 --- a/feature/main/src/main/java/com/twix/home/HomeIntent.kt +++ b/feature/main/src/main/java/com/twix/home/HomeIntent.kt @@ -7,4 +7,12 @@ sealed interface HomeIntent : Intent { data class SelectDate( val date: LocalDate, ) : HomeIntent + + data object PreviousWeek : HomeIntent + + data object NextWeek : HomeIntent + + data class UpdateMonthYear( + val date: LocalDate, + ) : HomeIntent } From 09b42d9c256120e7dfe6919474498273cdc43a26 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 24 Jan 2026 16:08:22 +0900 Subject: [PATCH 46/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20=ED=99=88=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EC=83=81=EB=8B=A8=20VisibleDate=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/main/src/main/java/com/twix/home/HomeIntent.kt | 2 +- .../src/main/java/com/twix/home/component/WeeklyCalendar.kt | 6 +++--- .../main/src/main/java/com/twix/home/model/HomeUiState.kt | 5 +++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/feature/main/src/main/java/com/twix/home/HomeIntent.kt b/feature/main/src/main/java/com/twix/home/HomeIntent.kt index 8f406e6..449af8f 100644 --- a/feature/main/src/main/java/com/twix/home/HomeIntent.kt +++ b/feature/main/src/main/java/com/twix/home/HomeIntent.kt @@ -12,7 +12,7 @@ sealed interface HomeIntent : Intent { data object NextWeek : HomeIntent - data class UpdateMonthYear( + data class UpdateVisibleDate( val date: LocalDate, ) : HomeIntent } diff --git a/feature/main/src/main/java/com/twix/home/component/WeeklyCalendar.kt b/feature/main/src/main/java/com/twix/home/component/WeeklyCalendar.kt index c0c3de9..878eda4 100644 --- a/feature/main/src/main/java/com/twix/home/component/WeeklyCalendar.kt +++ b/feature/main/src/main/java/com/twix/home/component/WeeklyCalendar.kt @@ -38,7 +38,7 @@ fun WeeklyCalendar( onSelectDate: (LocalDate) -> Unit, onPreviousWeek: () -> Unit, onNextWeek: () -> Unit, - onUpdateMonthYear: (LocalDate) -> Unit + onUpdateVisibleDate: (LocalDate) -> Unit ) { val today = remember { LocalDate.now() } val weekStart = remember(referenceDate) { referenceDate.weekStartSunday() } @@ -58,8 +58,8 @@ fun WeeklyCalendar( var dragSumPx by remember { mutableFloatStateOf(0f) } LaunchedEffect(days.first()) { - if (dragSumPx > 0) onUpdateMonthYear(days.first()) - else onUpdateMonthYear(days.last()) + if (dragSumPx > 0) onUpdateVisibleDate(days.first()) + else onUpdateVisibleDate(days.last()) } Column( diff --git a/feature/main/src/main/java/com/twix/home/model/HomeUiState.kt b/feature/main/src/main/java/com/twix/home/model/HomeUiState.kt index 1f127c9..0eab724 100644 --- a/feature/main/src/main/java/com/twix/home/model/HomeUiState.kt +++ b/feature/main/src/main/java/com/twix/home/model/HomeUiState.kt @@ -8,9 +8,10 @@ import java.time.YearMonth @Immutable data class HomeUiState( val month: YearMonth = YearMonth.now(), + val visibleDate: LocalDate = LocalDate.now(), // 홈 화면 상단에 존재하는 월, 년 텍스트를 위한 상태 변수 val selectedDate: LocalDate = LocalDate.now(), - val dateItems: List = emptyList(), + val referenceDate: LocalDate = LocalDate.now(), // 7일 달력을 생성하기 위한 레퍼런스 날짜 ) : State { val monthYear: String - get() = "${selectedDate.month.value}월 ${selectedDate.year}" + get() = "${visibleDate.month.value}월 ${visibleDate.year}" } From 11d979235e8f6ba03ae8492ccb69dde1bce50c4b Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 24 Jan 2026 16:08:57 +0900 Subject: [PATCH 47/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EB=8B=AC=EB=A0=A5=20?= =?UTF-8?q?=EC=8A=A4=EC=99=80=EC=9D=B4=ED=94=84,=20visibleDate=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/twix/home/HomeViewModel.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/feature/main/src/main/java/com/twix/home/HomeViewModel.kt b/feature/main/src/main/java/com/twix/home/HomeViewModel.kt index 9f925a5..9025291 100644 --- a/feature/main/src/main/java/com/twix/home/HomeViewModel.kt +++ b/feature/main/src/main/java/com/twix/home/HomeViewModel.kt @@ -11,12 +11,29 @@ class HomeViewModel : override suspend fun handleIntent(intent: HomeIntent) { when (intent) { is HomeIntent.SelectDate -> updateDate(intent.date) + HomeIntent.NextWeek -> shiftWeek(1) + HomeIntent.PreviousWeek -> shiftWeek(-1) + is HomeIntent.UpdateVisibleDate -> updateVisibleDate(intent.date) } } private fun updateDate(date: LocalDate) { if (currentState.selectedDate == date) return + if (date.month != currentState.visibleDate.month) updateVisibleDate(date) + reduce { copy(selectedDate = date) } } + + private fun shiftWeek(offset: Long) { + val newReference = currentState.referenceDate.plusDays(offset * 7) + if (currentState.referenceDate == newReference) return + reduce { copy(referenceDate = newReference) } + } + + private fun updateVisibleDate(date: LocalDate) { + if (currentState.visibleDate.month == date.month) return + + reduce { copy(visibleDate = date) } + } } From 43bf66bbe8838d23897dc871cd9d8260a9e93a49 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 24 Jan 2026 16:09:20 +0900 Subject: [PATCH 48/62] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=ED=95=98=EB=8B=A8=20=ED=8C=A8=EB=94=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/twix/home/component/HomeTopBar.kt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/feature/main/src/main/java/com/twix/home/component/HomeTopBar.kt b/feature/main/src/main/java/com/twix/home/component/HomeTopBar.kt index 8f96797..a94a79d 100644 --- a/feature/main/src/main/java/com/twix/home/component/HomeTopBar.kt +++ b/feature/main/src/main/java/com/twix/home/component/HomeTopBar.kt @@ -19,10 +19,12 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.painter.Painter 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.twix.designsystem.R import com.twix.designsystem.components.text.AppText import com.twix.designsystem.theme.GrayColor +import com.twix.designsystem.theme.TwixTheme import com.twix.domain.model.enums.AppTextStyle @Composable @@ -35,7 +37,8 @@ fun HomeTopBar( modifier = Modifier .fillMaxWidth() - .padding(start = 20.dp, end = 10.dp), + .padding(start = 20.dp, end = 10.dp) + .padding(bottom = 10.dp), verticalAlignment = Alignment.Bottom, ) { Column( @@ -112,3 +115,15 @@ private fun TopBarButton( ) } } + +@Preview(showBackground = true) +@Composable +private fun Preview() { + TwixTheme { + HomeTopBar( + monthYearText = "1월 22일", + {}, + {}, + ) + } +} From 9bd271043f0ad5b65018b1fee2cfbff782d3c386 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 24 Jan 2026 16:09:52 +0900 Subject: [PATCH 49/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20=ED=99=88=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EC=A3=BC=EA=B0=84=20=EB=8B=AC=EB=A0=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/twix/home/HomeScreen.kt | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/feature/main/src/main/java/com/twix/home/HomeScreen.kt b/feature/main/src/main/java/com/twix/home/HomeScreen.kt index 3396d63..cfd6a1e 100644 --- a/feature/main/src/main/java/com/twix/home/HomeScreen.kt +++ b/feature/main/src/main/java/com/twix/home/HomeScreen.kt @@ -8,8 +8,10 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.twix.home.component.HomeTopBar +import com.twix.home.component.WeeklyCalendar import com.twix.home.model.HomeUiState import org.koin.androidx.compose.koinViewModel +import java.time.LocalDate @Composable fun HomeRoute(viewModel: HomeViewModel = koinViewModel()) { @@ -17,11 +19,21 @@ fun HomeRoute(viewModel: HomeViewModel = koinViewModel()) { HomeScreen( uiState = uiState, + onSelectDate = { viewModel.dispatch(HomeIntent.SelectDate(it)) }, + onPreviousWeek = { viewModel.dispatch(HomeIntent.PreviousWeek) }, + onNextWeek = { viewModel.dispatch(HomeIntent.NextWeek) }, + onUpdateVisibleDate = { viewModel.dispatch(HomeIntent.UpdateVisibleDate(it)) }, ) } @Composable -fun HomeScreen(uiState: HomeUiState) { +fun HomeScreen( + uiState: HomeUiState, + onSelectDate: (LocalDate) -> Unit, + onPreviousWeek: () -> Unit, + onNextWeek: () -> Unit, + onUpdateVisibleDate: (LocalDate) -> Unit, +) { Column( modifier = Modifier @@ -33,6 +45,15 @@ fun HomeScreen(uiState: HomeUiState) { onSettingClick = {}, ) + WeeklyCalendar( + selectedDate = uiState.selectedDate, + referenceDate = uiState.referenceDate, + onSelectDate = onSelectDate, + onPreviousWeek = onPreviousWeek, + onNextWeek = onNextWeek, + onUpdateVisibleDate = onUpdateVisibleDate, + ) + Spacer(Modifier.weight(1f)) } } From 1fb98cc6dbce228b14230597ba4df709125bf56d Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 24 Jan 2026 16:36:51 +0900 Subject: [PATCH 50/62] =?UTF-8?q?=F0=9F=8D=B1=20Chore:=20=EB=A6=AC?= =?UTF-8?q?=EC=86=8C=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 --- .../src/main/res/drawable/ic_empty_face.xml | 57 +++++++++++++++++++ .../main/res/drawable/ic_empty_goal_arrow.xml | 9 +++ .../src/main/res/values/strings.xml | 1 + 3 files changed, 67 insertions(+) create mode 100644 core/design-system/src/main/res/drawable/ic_empty_face.xml create mode 100644 core/design-system/src/main/res/drawable/ic_empty_goal_arrow.xml diff --git a/core/design-system/src/main/res/drawable/ic_empty_face.xml b/core/design-system/src/main/res/drawable/ic_empty_face.xml new file mode 100644 index 0000000..a67495f --- /dev/null +++ b/core/design-system/src/main/res/drawable/ic_empty_face.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + diff --git a/core/design-system/src/main/res/drawable/ic_empty_goal_arrow.xml b/core/design-system/src/main/res/drawable/ic_empty_goal_arrow.xml new file mode 100644 index 0000000..865123a --- /dev/null +++ b/core/design-system/src/main/res/drawable/ic_empty_goal_arrow.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index 37b63e2..e81fc34 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -16,5 +16,6 @@ 오늘 우리 목표 + 첫 목표를 세워볼까요? \ No newline at end of file From e30c97ea935b4a3b942da0668661a21c29a8c684 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 24 Jan 2026 16:37:06 +0900 Subject: [PATCH 51/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20EmptyGoalGuide=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/home/component/EmptyGoalGuide.kt | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 feature/main/src/main/java/com/twix/home/component/EmptyGoalGuide.kt diff --git a/feature/main/src/main/java/com/twix/home/component/EmptyGoalGuide.kt b/feature/main/src/main/java/com/twix/home/component/EmptyGoalGuide.kt new file mode 100644 index 0000000..41db519 --- /dev/null +++ b/feature/main/src/main/java/com/twix/home/component/EmptyGoalGuide.kt @@ -0,0 +1,59 @@ +package com.twix.home.component + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +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.unit.dp +import com.twix.designsystem.R +import com.twix.designsystem.components.text.AppText +import com.twix.designsystem.theme.GrayColor +import com.twix.domain.model.enums.AppTextStyle + +@Composable +fun EmptyGoalGuide( + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Image( + painter = painterResource(R.drawable.ic_empty_face), + contentDescription = "empty face", + modifier = Modifier + .padding(horizontal = 9.dp, vertical = 6.dp) + .size(width = 34.dp, height = 40.dp) + + ) + + Spacer(Modifier.height(10.dp)) + + AppText( + text = stringResource(R.string.homt_empty_goal_guide), + style = AppTextStyle.T2, + color = GrayColor.C200 + ) + + Spacer(Modifier.height(12.dp)) + + Image( + painter = painterResource(R.drawable.ic_empty_goal_arrow), + contentDescription = "empty goal arrow", + modifier = Modifier + .offset(x = 32.dp) + ) + } +} From ad2980c07a86c34ae3b83581d9b48b2fd4279daa Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 24 Jan 2026 16:37:31 +0900 Subject: [PATCH 52/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20=ED=99=88=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20EmptyGoalGuide=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/main/src/main/java/com/twix/home/HomeScreen.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/feature/main/src/main/java/com/twix/home/HomeScreen.kt b/feature/main/src/main/java/com/twix/home/HomeScreen.kt index cfd6a1e..f8bbc29 100644 --- a/feature/main/src/main/java/com/twix/home/HomeScreen.kt +++ b/feature/main/src/main/java/com/twix/home/HomeScreen.kt @@ -3,10 +3,13 @@ package com.twix.home import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.twix.home.component.EmptyGoalGuide import com.twix.home.component.HomeTopBar import com.twix.home.component.WeeklyCalendar import com.twix.home.model.HomeUiState @@ -54,6 +57,8 @@ fun HomeScreen( onUpdateVisibleDate = onUpdateVisibleDate, ) - Spacer(Modifier.weight(1f)) + Spacer(Modifier.height(12.dp)) + + EmptyGoalGuide(modifier = Modifier.weight(1f)) } } From 598d7a7219ae46e09007088ff504606dafc22010 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 24 Jan 2026 16:49:23 +0900 Subject: [PATCH 53/62] =?UTF-8?q?=F0=9F=8D=B1=20Chore:=20=EB=A6=AC?= =?UTF-8?q?=EC=86=8C=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 --- core/design-system/src/main/res/drawable/ic_plus.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 core/design-system/src/main/res/drawable/ic_plus.xml diff --git a/core/design-system/src/main/res/drawable/ic_plus.xml b/core/design-system/src/main/res/drawable/ic_plus.xml new file mode 100644 index 0000000..1785a79 --- /dev/null +++ b/core/design-system/src/main/res/drawable/ic_plus.xml @@ -0,0 +1,12 @@ + + + From 4f8a0789a595f10e3ed39d467d1ba39e05a63daa Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 24 Jan 2026 16:51:11 +0900 Subject: [PATCH 54/62] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20ktlint?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/home/component/EmptyGoalGuide.kt | 26 +++++++++---------- .../com/twix/home/component/WeeklyCalendar.kt | 9 ++++--- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/feature/main/src/main/java/com/twix/home/component/EmptyGoalGuide.kt b/feature/main/src/main/java/com/twix/home/component/EmptyGoalGuide.kt index 41db519..f4e204c 100644 --- a/feature/main/src/main/java/com/twix/home/component/EmptyGoalGuide.kt +++ b/feature/main/src/main/java/com/twix/home/component/EmptyGoalGuide.kt @@ -21,22 +21,21 @@ import com.twix.designsystem.theme.GrayColor import com.twix.domain.model.enums.AppTextStyle @Composable -fun EmptyGoalGuide( - modifier: Modifier = Modifier, -) { +fun EmptyGoalGuide(modifier: Modifier = Modifier) { Column( - modifier = modifier - .fillMaxWidth(), + modifier = + modifier + .fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center + verticalArrangement = Arrangement.Center, ) { Image( painter = painterResource(R.drawable.ic_empty_face), contentDescription = "empty face", - modifier = Modifier - .padding(horizontal = 9.dp, vertical = 6.dp) - .size(width = 34.dp, height = 40.dp) - + modifier = + Modifier + .padding(horizontal = 9.dp, vertical = 6.dp) + .size(width = 34.dp, height = 40.dp), ) Spacer(Modifier.height(10.dp)) @@ -44,7 +43,7 @@ fun EmptyGoalGuide( AppText( text = stringResource(R.string.homt_empty_goal_guide), style = AppTextStyle.T2, - color = GrayColor.C200 + color = GrayColor.C200, ) Spacer(Modifier.height(12.dp)) @@ -52,8 +51,9 @@ fun EmptyGoalGuide( Image( painter = painterResource(R.drawable.ic_empty_goal_arrow), contentDescription = "empty goal arrow", - modifier = Modifier - .offset(x = 32.dp) + modifier = + Modifier + .offset(x = 32.dp), ) } } diff --git a/feature/main/src/main/java/com/twix/home/component/WeeklyCalendar.kt b/feature/main/src/main/java/com/twix/home/component/WeeklyCalendar.kt index 878eda4..7ce948a 100644 --- a/feature/main/src/main/java/com/twix/home/component/WeeklyCalendar.kt +++ b/feature/main/src/main/java/com/twix/home/component/WeeklyCalendar.kt @@ -38,7 +38,7 @@ fun WeeklyCalendar( onSelectDate: (LocalDate) -> Unit, onPreviousWeek: () -> Unit, onNextWeek: () -> Unit, - onUpdateVisibleDate: (LocalDate) -> Unit + onUpdateVisibleDate: (LocalDate) -> Unit, ) { val today = remember { LocalDate.now() } val weekStart = remember(referenceDate) { referenceDate.weekStartSunday() } @@ -58,8 +58,11 @@ fun WeeklyCalendar( var dragSumPx by remember { mutableFloatStateOf(0f) } LaunchedEffect(days.first()) { - if (dragSumPx > 0) onUpdateVisibleDate(days.first()) - else onUpdateVisibleDate(days.last()) + if (dragSumPx > 0) { + onUpdateVisibleDate(days.first()) + } else { + onUpdateVisibleDate(days.last()) + } } Column( From a21cd0ff5eaeaf4a801ee6536eb6b74f8deb5877 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sat, 24 Jan 2026 16:51:22 +0900 Subject: [PATCH 55/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20AddGoalButton=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/twix/home/HomeScreen.kt | 86 +++++++++++++++---- 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/feature/main/src/main/java/com/twix/home/HomeScreen.kt b/feature/main/src/main/java/com/twix/home/HomeScreen.kt index f8bbc29..289dab6 100644 --- a/feature/main/src/main/java/com/twix/home/HomeScreen.kt +++ b/feature/main/src/main/java/com/twix/home/HomeScreen.kt @@ -1,14 +1,27 @@ package com.twix.home +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.twix.designsystem.R +import com.twix.designsystem.theme.GrayColor import com.twix.home.component.EmptyGoalGuide import com.twix.home.component.HomeTopBar import com.twix.home.component.WeeklyCalendar @@ -37,28 +50,69 @@ fun HomeScreen( onNextWeek: () -> Unit, onUpdateVisibleDate: (LocalDate) -> Unit, ) { - Column( + Box( modifier = Modifier .fillMaxSize(), ) { - HomeTopBar( - monthYearText = uiState.monthYear, - onNotificationClick = {}, - onSettingClick = {}, - ) + Column( + modifier = + Modifier + .fillMaxSize(), + ) { + HomeTopBar( + monthYearText = uiState.monthYear, + onNotificationClick = {}, + onSettingClick = {}, + ) - WeeklyCalendar( - selectedDate = uiState.selectedDate, - referenceDate = uiState.referenceDate, - onSelectDate = onSelectDate, - onPreviousWeek = onPreviousWeek, - onNextWeek = onNextWeek, - onUpdateVisibleDate = onUpdateVisibleDate, - ) + WeeklyCalendar( + selectedDate = uiState.selectedDate, + referenceDate = uiState.referenceDate, + onSelectDate = onSelectDate, + onPreviousWeek = onPreviousWeek, + onNextWeek = onNextWeek, + onUpdateVisibleDate = onUpdateVisibleDate, + ) + + Spacer(Modifier.height(12.dp)) - Spacer(Modifier.height(12.dp)) + EmptyGoalGuide(modifier = Modifier.weight(1f)) + } + + AddGoalButton( + modifier = + Modifier + .align(Alignment.BottomEnd) + .padding(bottom = 12.dp, end = 16.dp), + onClick = {}, + ) + } +} - EmptyGoalGuide(modifier = Modifier.weight(1f)) +@Composable +private fun AddGoalButton( + modifier: Modifier = Modifier, + onClick: () -> Unit, +) { + Box( + modifier = + modifier + .size(56.dp) + .background(GrayColor.C500, CircleShape) + .clickable( + indication = null, + onClick = onClick, + interactionSource = remember { MutableInteractionSource() }, + ), + contentAlignment = Alignment.Center, + ) { + Image( + painter = painterResource(R.drawable.ic_plus), + contentDescription = "add goal", + modifier = + Modifier + .size(40.dp), + ) } } From b29e7574104945e039a47d5e1074f9f1687053d9 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sun, 25 Jan 2026 18:24:57 +0900 Subject: [PATCH 56/62] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/design-system/src/main/res/values/strings.xml | 2 +- .../src/main/java/com/twix/home/component/EmptyGoalGuide.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index e81fc34..da500dd 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -16,6 +16,6 @@ 오늘 우리 목표 - 첫 목표를 세워볼까요? + 첫 목표를 세워볼까요? \ No newline at end of file diff --git a/feature/main/src/main/java/com/twix/home/component/EmptyGoalGuide.kt b/feature/main/src/main/java/com/twix/home/component/EmptyGoalGuide.kt index f4e204c..16b116c 100644 --- a/feature/main/src/main/java/com/twix/home/component/EmptyGoalGuide.kt +++ b/feature/main/src/main/java/com/twix/home/component/EmptyGoalGuide.kt @@ -41,7 +41,7 @@ fun EmptyGoalGuide(modifier: Modifier = Modifier) { Spacer(Modifier.height(10.dp)) AppText( - text = stringResource(R.string.homt_empty_goal_guide), + text = stringResource(R.string.home_empty_goal_guide), style = AppTextStyle.T2, color = GrayColor.C200, ) From 9625d73b7584422edb61e9fe4b6ab330bd898388 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sun, 25 Jan 2026 18:29:19 +0900 Subject: [PATCH 57/62] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20noRipple?= =?UTF-8?q?Click=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/main/src/main/java/com/twix/home/HomeScreen.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/feature/main/src/main/java/com/twix/home/HomeScreen.kt b/feature/main/src/main/java/com/twix/home/HomeScreen.kt index 289dab6..c3ad835 100644 --- a/feature/main/src/main/java/com/twix/home/HomeScreen.kt +++ b/feature/main/src/main/java/com/twix/home/HomeScreen.kt @@ -26,6 +26,7 @@ import com.twix.home.component.EmptyGoalGuide import com.twix.home.component.HomeTopBar import com.twix.home.component.WeeklyCalendar import com.twix.home.model.HomeUiState +import com.twix.ui.extension.noRippleClickable import org.koin.androidx.compose.koinViewModel import java.time.LocalDate @@ -100,11 +101,7 @@ private fun AddGoalButton( modifier .size(56.dp) .background(GrayColor.C500, CircleShape) - .clickable( - indication = null, - onClick = onClick, - interactionSource = remember { MutableInteractionSource() }, - ), + .noRippleClickable(onClick = onClick), contentAlignment = Alignment.Center, ) { Image( From 81f99a94fd4f39f1883b1e7044d65ca7126262ef Mon Sep 17 00:00:00 2001 From: dogmania Date: Sun, 25 Jan 2026 18:34:25 +0900 Subject: [PATCH 58/62] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20consumeA?= =?UTF-8?q?sFlow=20->=20receiveAsFlow=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 --- core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt b/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt index 74fbaf5..54b1005 100644 --- a/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt +++ b/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.consumeAsFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import kotlin.coroutines.cancellation.CancellationException @@ -32,7 +33,7 @@ abstract class BaseViewModel( init { // Intent 순차 처리 viewModelScope.launch { - intentChannel.consumeAsFlow().collect { intent -> + intentChannel.receiveAsFlow().collect { intent -> try { handleIntent(intent) } catch (t: Throwable) { From 9a555661d90378eb61460cd1d95440db39b18c9a Mon Sep 17 00:00:00 2001 From: dogmania Date: Sun, 25 Jan 2026 18:48:06 +0900 Subject: [PATCH 59/62] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=A3=BC=EA=B0=84=20=EB=8B=AC=EB=A0=A5=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/domain/model/enums/WeekNavigation.kt | 7 +++++++ .../src/main/java/com/twix/home/HomeViewModel.kt | 14 ++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 domain/src/main/java/com/twix/domain/model/enums/WeekNavigation.kt diff --git a/domain/src/main/java/com/twix/domain/model/enums/WeekNavigation.kt b/domain/src/main/java/com/twix/domain/model/enums/WeekNavigation.kt new file mode 100644 index 0000000..d8fb8ea --- /dev/null +++ b/domain/src/main/java/com/twix/domain/model/enums/WeekNavigation.kt @@ -0,0 +1,7 @@ +package com.twix.domain.model.enums + +enum class WeekNavigation { + NEXT, + PREVIOUS, + TODAY, +} diff --git a/feature/main/src/main/java/com/twix/home/HomeViewModel.kt b/feature/main/src/main/java/com/twix/home/HomeViewModel.kt index 9025291..9a8f31c 100644 --- a/feature/main/src/main/java/com/twix/home/HomeViewModel.kt +++ b/feature/main/src/main/java/com/twix/home/HomeViewModel.kt @@ -1,5 +1,6 @@ package com.twix.home +import com.twix.domain.model.enums.WeekNavigation import com.twix.home.model.HomeUiState import com.twix.ui.base.BaseViewModel import java.time.LocalDate @@ -11,8 +12,8 @@ class HomeViewModel : override suspend fun handleIntent(intent: HomeIntent) { when (intent) { is HomeIntent.SelectDate -> updateDate(intent.date) - HomeIntent.NextWeek -> shiftWeek(1) - HomeIntent.PreviousWeek -> shiftWeek(-1) + HomeIntent.NextWeek -> shiftWeek(WeekNavigation.NEXT) + HomeIntent.PreviousWeek -> shiftWeek(WeekNavigation.PREVIOUS) is HomeIntent.UpdateVisibleDate -> updateVisibleDate(intent.date) } } @@ -25,8 +26,13 @@ class HomeViewModel : reduce { copy(selectedDate = date) } } - private fun shiftWeek(offset: Long) { - val newReference = currentState.referenceDate.plusDays(offset * 7) + private fun shiftWeek(action: WeekNavigation) { + val newReference = + when (action) { + WeekNavigation.NEXT -> currentState.referenceDate.plusWeeks(1) + WeekNavigation.PREVIOUS -> currentState.referenceDate.minusWeeks(1) + WeekNavigation.TODAY -> LocalDate.now() + } if (currentState.referenceDate == newReference) return reduce { copy(referenceDate = newReference) } } From 682cde9fc9697c71ad4bd98f210c83e42489b912 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sun, 25 Jan 2026 18:48:33 +0900 Subject: [PATCH 60/62] =?UTF-8?q?=F0=9F=94=A5=20Remove:=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EC=9D=98=EC=A1=B4=EC=84=B1=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt | 1 - feature/main/src/main/java/com/twix/home/HomeScreen.kt | 3 --- 2 files changed, 4 deletions(-) diff --git a/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt b/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt index 54b1005..3bc9fd5 100644 --- a/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt +++ b/core/ui/src/main/java/com/twix/ui/base/BaseViewModel.kt @@ -7,7 +7,6 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import kotlin.coroutines.cancellation.CancellationException diff --git a/feature/main/src/main/java/com/twix/home/HomeScreen.kt b/feature/main/src/main/java/com/twix/home/HomeScreen.kt index c3ad835..a12a43b 100644 --- a/feature/main/src/main/java/com/twix/home/HomeScreen.kt +++ b/feature/main/src/main/java/com/twix/home/HomeScreen.kt @@ -2,8 +2,6 @@ package com.twix.home import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -14,7 +12,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource From 65fb738fe68216e5931e7dd95f662ff61bbde093 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sun, 25 Jan 2026 19:22:17 +0900 Subject: [PATCH 61/62] =?UTF-8?q?=F0=9F=8D=B1=20Chore:=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EB=A6=AC=EC=86=8C=EC=8A=A4=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 --- .../src/main/res/drawable/ic_refresh.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 core/design-system/src/main/res/drawable/ic_refresh.xml diff --git a/core/design-system/src/main/res/drawable/ic_refresh.xml b/core/design-system/src/main/res/drawable/ic_refresh.xml new file mode 100644 index 0000000..1bf4d68 --- /dev/null +++ b/core/design-system/src/main/res/drawable/ic_refresh.xml @@ -0,0 +1,13 @@ + + + From 13c0abea99befbc42a9bf96bd0469639a4de0b31 Mon Sep 17 00:00:00 2001 From: dogmania Date: Sun, 25 Jan 2026 19:24:34 +0900 Subject: [PATCH 62/62] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=A3=BC=EA=B0=84=20?= =?UTF-8?q?=EB=8B=AC=EB=A0=A5=20=EC=98=A4=EB=8A=98=20=EB=82=A0=EC=A7=9C?= =?UTF-8?q?=EB=A1=9C=20=EB=90=98=EB=8F=8C=EB=A6=AC=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/twix/home/HomeIntent.kt | 2 + .../src/main/java/com/twix/home/HomeScreen.kt | 3 ++ .../main/java/com/twix/home/HomeViewModel.kt | 1 + .../com/twix/home/component/HomeTopBar.kt | 46 +++++++++++-------- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/feature/main/src/main/java/com/twix/home/HomeIntent.kt b/feature/main/src/main/java/com/twix/home/HomeIntent.kt index 449af8f..d41c316 100644 --- a/feature/main/src/main/java/com/twix/home/HomeIntent.kt +++ b/feature/main/src/main/java/com/twix/home/HomeIntent.kt @@ -12,6 +12,8 @@ sealed interface HomeIntent : Intent { data object NextWeek : HomeIntent + data object MoveToToday : HomeIntent + data class UpdateVisibleDate( val date: LocalDate, ) : HomeIntent diff --git a/feature/main/src/main/java/com/twix/home/HomeScreen.kt b/feature/main/src/main/java/com/twix/home/HomeScreen.kt index a12a43b..93a1b42 100644 --- a/feature/main/src/main/java/com/twix/home/HomeScreen.kt +++ b/feature/main/src/main/java/com/twix/home/HomeScreen.kt @@ -37,6 +37,7 @@ fun HomeRoute(viewModel: HomeViewModel = koinViewModel()) { onPreviousWeek = { viewModel.dispatch(HomeIntent.PreviousWeek) }, onNextWeek = { viewModel.dispatch(HomeIntent.NextWeek) }, onUpdateVisibleDate = { viewModel.dispatch(HomeIntent.UpdateVisibleDate(it)) }, + onMoveToToday = { viewModel.dispatch(HomeIntent.MoveToToday) }, ) } @@ -47,6 +48,7 @@ fun HomeScreen( onPreviousWeek: () -> Unit, onNextWeek: () -> Unit, onUpdateVisibleDate: (LocalDate) -> Unit, + onMoveToToday: () -> Unit, ) { Box( modifier = @@ -62,6 +64,7 @@ fun HomeScreen( monthYearText = uiState.monthYear, onNotificationClick = {}, onSettingClick = {}, + onMoveToToday = onMoveToToday, ) WeeklyCalendar( diff --git a/feature/main/src/main/java/com/twix/home/HomeViewModel.kt b/feature/main/src/main/java/com/twix/home/HomeViewModel.kt index 9a8f31c..4503fed 100644 --- a/feature/main/src/main/java/com/twix/home/HomeViewModel.kt +++ b/feature/main/src/main/java/com/twix/home/HomeViewModel.kt @@ -14,6 +14,7 @@ class HomeViewModel : is HomeIntent.SelectDate -> updateDate(intent.date) HomeIntent.NextWeek -> shiftWeek(WeekNavigation.NEXT) HomeIntent.PreviousWeek -> shiftWeek(WeekNavigation.PREVIOUS) + HomeIntent.MoveToToday -> shiftWeek(WeekNavigation.TODAY) is HomeIntent.UpdateVisibleDate -> updateVisibleDate(intent.date) } } diff --git a/feature/main/src/main/java/com/twix/home/component/HomeTopBar.kt b/feature/main/src/main/java/com/twix/home/component/HomeTopBar.kt index a94a79d..1b162ae 100644 --- a/feature/main/src/main/java/com/twix/home/component/HomeTopBar.kt +++ b/feature/main/src/main/java/com/twix/home/component/HomeTopBar.kt @@ -1,8 +1,8 @@ package com.twix.home.component +import androidx.annotation.DrawableRes import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -12,11 +12,9 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -26,12 +24,14 @@ import com.twix.designsystem.components.text.AppText import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme import com.twix.domain.model.enums.AppTextStyle +import com.twix.ui.extension.noRippleClickable @Composable fun HomeTopBar( monthYearText: String, onNotificationClick: () -> Unit, onSettingClick: () -> Unit, + onMoveToToday: () -> Unit, ) { Row( modifier = @@ -65,11 +65,22 @@ fun HomeTopBar( Spacer(Modifier.height(4.dp)) - AppText( - text = stringResource(R.string.home_today_goal), - style = AppTextStyle.H3, - color = GrayColor.C400, - ) + Row( + verticalAlignment = Alignment.Top, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + AppText( + text = stringResource(R.string.home_today_goal), + style = AppTextStyle.H3, + color = GrayColor.C400, + ) + + Image( + painter = painterResource(R.drawable.ic_refresh), + contentDescription = "refresh", + modifier = Modifier.noRippleClickable(onClick = onMoveToToday), + ) + } Spacer(Modifier.height(2.dp)) } @@ -77,13 +88,13 @@ fun HomeTopBar( Spacer(Modifier.weight(1f)) TopBarButton( - icon = painterResource(id = R.drawable.ic_alert), + iconRes = R.drawable.ic_alert, contentDescription = "notification", onClick = onNotificationClick, ) TopBarButton( - icon = painterResource(id = R.drawable.ic_setting), + iconRes = R.drawable.ic_setting, contentDescription = "setting", onClick = onSettingClick, ) @@ -92,25 +103,19 @@ fun HomeTopBar( @Composable private fun TopBarButton( - icon: Painter, + @DrawableRes iconRes: Int, contentDescription: String, onClick: () -> Unit, ) { - val interactionSource = remember { MutableInteractionSource() } - Box( modifier = Modifier .size(40.dp) - .clickable( - indication = null, - interactionSource = interactionSource, - onClick = onClick, - ), + .noRippleClickable(onClick = onClick), contentAlignment = Alignment.Center, ) { Image( - painter = icon, + painter = painterResource(id = iconRes), contentDescription = contentDescription, ) } @@ -124,6 +129,7 @@ private fun Preview() { monthYearText = "1월 22일", {}, {}, + {}, ) } }