diff --git a/.github/workflows/cd_release.yml b/.github/workflows/cd_release.yml
new file mode 100644
index 0000000..c12d935
--- /dev/null
+++ b/.github/workflows/cd_release.yml
@@ -0,0 +1,60 @@
+name: Android CD Release
+
+on:
+ push:
+ branches:
+ - master # master 브랜치에 push 될 때 실행
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+
+ # Secrets를 환경 변수로 정의하여 run 스크립트에서 $변수 형태로 사용 가능
+ env:
+ KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
+ KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
+ KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
+ KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
+
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v4
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+
+ - name: Make gradlew executable
+ run: chmod +x ./gradlew
+
+ # Secrets에서 Base64 문자열을 디코딩하여 app/keystore.jks 파일로 저장
+ - name: Decode Keystore File
+ # base64 -d 명령어를 사용하여 복호화
+ run: echo "$KEYSTORE_BASE64" | base64 -d > app/keystore.jks
+
+ # 서명에 필요한 비밀번호와 별칭 정보를 signing.properties 파일로 생성
+ - name: Set up Signing Properties
+ run: |
+ echo "storeFile=keystore.jks" > ./signing.properties
+ echo "storePassword=$KEYSTORE_PASSWORD" >> ./signing.properties
+ echo "keyAlias=$KEY_ALIAS" >> ./signing.properties
+ echo "keyPassword=$KEY_PASSWORD" >> ./signing.properties
+
+ # 릴리즈 AAB 빌드 실행
+ - name: Build Release AAB
+ # 프로젝트 설정을 통해 서명된다
+ run: ./gradlew bundleRelease
+
+ # GitHub Release 생성 및 고정 메시지 적용
+ - name: Create GitHub Release
+ uses: softprops/action-gh-release@v2
+ # Git Tag가 푸시될 때만 Release를 생성 (예: v1.0.0)
+ if: startsWith(github.ref, 'refs/tags/')
+ with:
+ # 빌드된 AAB 파일 업로드
+ files: app/build/outputs/bundle/release/app-release.aab
+ name: Release ${{ github.ref_name }}
+ body: |
+ 더 나은 모멘토를 위해 버그를 수정하고, 사용성을 개선했어요
\ No newline at end of file
diff --git a/.github/workflows/ci_build.yml b/.github/workflows/ci_build.yml
new file mode 100644
index 0000000..80e6fff
--- /dev/null
+++ b/.github/workflows/ci_build.yml
@@ -0,0 +1,54 @@
+name: Android CI Build
+
+on:
+ push:
+ branches:
+ - develop # develop 브랜치에 push 될 때 실행
+ pull_request:
+ branches:
+ - develop # develop 브랜치로 PR이 열리거나 업데이트 될 때 실행
+
+
+jobs:
+ build:
+ runs-on: ubuntu-latest # 워크플로우를 실행할 가상 환경
+
+ env:
+ GOOGLE_SERVICES_BASE64: ${{ secrets.GOOGLE_SERVICES_BASE64 }}
+ KAKAO_NATIVE_APP_KEY: ${{ secrets.KAKAO_NATIVE_APP_KEY }}
+ NAVER_CLIENT_ID: ${{ secrets.NAVER_CLIENT_ID }}
+ NAVER_CLIENT_SECRET: ${{ secrets.NAVER_CLIENT_SECRET }}
+
+ steps:
+ - name: Checkout Code
+ uses: actions/checkout@v4 # GitHub 저장소 코드를 워크스페이스로 가져오기
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+
+ - name: Make gradlew executable
+ run: chmod +x ./gradlew # gradlew 파일에 실행 권한 부여
+
+ - name: Decode Google Services File
+ run: echo "$GOOGLE_SERVICES_BASE64" | base64 -d > app/google-services.json
+
+ # Secrets를 사용하여 local.properties 파일 생성
+ - name: Create local.properties for CI
+ run: |
+ echo "kakao.native.app.key=$KAKAO_NATIVE_APP_KEY" > local.properties
+ echo "naver.client.id=$NAVER_CLIENT_ID" >> local.properties
+ echo "naver.client.secret=$NAVER_CLIENT_SECRET" >> local.properties
+
+ - name: Build Debug APK
+ run: ./gradlew assembleDebug # 디버그 APK 빌드 명령어 실행
+
+ # 빌드된 APK 아티팩트 저장
+ - name: Upload APK Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: dngo-app-debug
+ path: app/build/outputs/apk/debug/app-debug.apk
+ retention-days: 90
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..32fa39f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,52 @@
+# Built application files
+*.apk
+*.aab
+*.aar
+*.ap_
+
+# Files for the ART/Dalvik VM
+*.dex
+
+# Gradle files
+.gradle/
+build/
+local.properties
+.idea/caches/
+.idea/libraries/
+.idea/workspace.xml
+.idea/tasks.xml
+.idea/gradle.xml
+.idea/misc.xml
+.idea/modules.xml
+.idea/vcs.xml
+
+# Kotlin files
+*.kt.log
+*.kt.test.log
+.kotlin/
+
+# Android Studio 4.x
+.idea/runConfigurations.xml
+
+# Local configuration files (e.g., used by third-party tools)
+.env
+.env.local
+.DS_Store
+
+# IDE specific files
+.idea/
+*.iws
+*.iml
+*.ipr
+.idea/codeStyles/
+.idea/gradle.xml
+.idea/misc.xml
+.idea/modules.xml
+.idea/vcs.xml
+
+# Keystore files
+*.jks
+*.keystore
+
+# firebase
+google-services.json
diff --git a/README.assets/architecture.png b/README.assets/architecture.png
new file mode 100644
index 0000000..f60f3f0
Binary files /dev/null and b/README.assets/architecture.png differ
diff --git a/README.assets/image.png b/README.assets/image.png
new file mode 100644
index 0000000..e1bee00
Binary files /dev/null and b/README.assets/image.png differ
diff --git a/README.assets/image2.png b/README.assets/image2.png
new file mode 100644
index 0000000..d0c8a7b
Binary files /dev/null and b/README.assets/image2.png differ
diff --git a/README.assets/image3.png b/README.assets/image3.png
new file mode 100644
index 0000000..32632e8
Binary files /dev/null and b/README.assets/image3.png differ
diff --git a/README.assets/image4.png b/README.assets/image4.png
new file mode 100644
index 0000000..78779d0
Binary files /dev/null and b/README.assets/image4.png differ
diff --git a/README.assets/image5.png b/README.assets/image5.png
new file mode 100644
index 0000000..181d26c
Binary files /dev/null and b/README.assets/image5.png differ
diff --git a/README.assets/momento.png b/README.assets/momento.png
new file mode 100644
index 0000000..e5d05cd
Binary files /dev/null and b/README.assets/momento.png differ
diff --git a/README.assets/play.png b/README.assets/play.png
new file mode 100644
index 0000000..7a06997
Binary files /dev/null and b/README.assets/play.png differ
diff --git a/README.md b/README.md
index ba05efd..a8f64e4 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,34 @@
-# dngo-android
\ No newline at end of file
+# 모멘토
+
+
+
+
+
+
+
+
+
+
+
+## 🏝️ 여행을 기록하는 가장 감성적인 방법, 모멘토
+
+- 자신의 여행 이야기를 간단하게 기록할 수 있어요
+- 행복했던 순간들을 한눈에 모아 관리해보세요
+- 원한다면 여행기록을 다른 사용자와 공유할 수도 있어요
+- 공유된 여행은 새로운 여행지를 찾는 사람들에게 영감을 줍니다
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Android 앱 아키텍처 (MVVM + Clean Architecture)
+
+
\ No newline at end of file
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
new file mode 100644
index 0000000..f34d9ac
--- /dev/null
+++ b/app/build.gradle.kts
@@ -0,0 +1,144 @@
+import java.util.Properties
+
+// Properties 객체를 생성해 key-value 쌍을 저장할 준비를 한다
+val properties = Properties()
+// 프로젝트의 루트 디렉토리에 있는 local.properties 파일을 가져온다
+val propertiesFile = project.rootProject.file("local.properties")
+// 파일이 존재하는지 확인 후 로드
+if (propertiesFile.exists()) {
+ // 파일의 InputStream을 열어 속성값을 안전하게 불러온다
+ // use 함수를 사용하면 스트림이 자동으로 닫힌다
+ propertiesFile.inputStream().use { properties.load(it) }
+}
+
+// CI/CD 서명 설정 파일 로드
+val signingProps = Properties()
+val signingPropsFile = project.rootProject.file("signing.properties")
+if (signingPropsFile.exists()) {
+ signingPropsFile.inputStream().use { signingProps.load(it) }
+}
+
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.kotlin.compose)
+ alias(libs.plugins.google.services)
+ alias(libs.plugins.ksp)
+ alias(libs.plugins.hilt)
+ alias(libs.plugins.kotlin.serialization)
+ alias(libs.plugins.firebase.crashlytics)
+}
+
+android {
+ namespace = "com.min.dnapp"
+ compileSdk = 36
+
+ defaultConfig {
+ applicationId = "com.min.dnapp"
+ minSdk = 30
+ targetSdk = 36
+ versionCode = 10
+ versionName = "2.0"
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+
+ val kakaoNativeAppKey = properties.getProperty("kakao.native.app.key")
+ val naverClientId = properties.getProperty("naver.client.id")
+ val naverClientSecret = properties.getProperty("naver.client.secret")
+
+ // 네이티브 앱 키를 BuildConfig에 필드로 추가
+ buildConfigField("String", "KAKAO_NATIVE_APP_KEY", "\"${kakaoNativeAppKey}\"")
+ // AndroidManifest.xml에 전달할 플레이스홀더 정의
+ manifestPlaceholders["kakaoNativeAppKey"] = kakaoNativeAppKey
+
+ // 네이버
+ buildConfigField("String", "NAVER_CLIENT_ID", "\"${naverClientId}\"")
+ buildConfigField("String", "NAVER_CLIENT_SECRET", "\"${naverClientSecret}\"")
+ }
+
+ // CI/CD 서명 설정
+ signingConfigs {
+ create("release") {
+ storeFile = if (signingProps.containsKey("storeFile")) file("app/${signingProps["storeFile"] as String}") else null
+ storePassword = signingProps["storePassword"] as String?
+ keyAlias = signingProps["keyAlias"] as String?
+ keyPassword = signingProps["keyPassword"] as String?
+ }
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ // release 빌드 타입에 서명 설정 적용
+ signingConfig = signingConfigs.getByName("release")
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+ }
+ kotlinOptions {
+ jvmTarget = "17"
+ }
+ buildFeatures {
+ compose = true
+ // BuildConfig 파일 생성을 활성화
+ buildConfig = true
+ }
+}
+
+dependencies {
+ val composeBom = platform(libs.androidx.compose.bom)
+ implementation(composeBom)
+ androidTestImplementation(composeBom)
+
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.lifecycle.runtime.ktx)
+ implementation(libs.androidx.activity.compose)
+ implementation(libs.androidx.ui)
+ implementation(libs.androidx.ui.graphics)
+ implementation(libs.androidx.ui.tooling.preview)
+ implementation(libs.androidx.material3)
+
+ // firebase
+ implementation(platform(libs.firebase.bom))
+ implementation(libs.firebase.crashlytics)
+ implementation(libs.firebase.analytics)
+ implementation(libs.firebase.auth)
+ implementation(libs.firebase.firestore)
+ implementation(libs.firebase.storage)
+ // kakao
+ implementation(libs.kakao.sdk)
+ // compose navigation
+ implementation(libs.androidx.navigation.compose)
+ // hilt
+ implementation(libs.hilt.android)
+ ksp(libs.hilt.compiler)
+ implementation(libs.hilt.navigation.compose)
+ // splash screen
+ implementation(libs.splash)
+ // retrofit2 & kotlin serialization
+ implementation(libs.retrofit)
+ implementation(libs.retrofit.converter.kotlinx.serialization)
+ implementation(libs.kotlinx.serialization.json)
+ // okhttp (logging interceptor)
+ implementation(libs.okhttp.logging)
+ // coil
+ implementation(libs.coil)
+ implementation(libs.coil.okhttp)
+ // lottie compose
+ implementation(libs.lottie.compose)
+ // preferences datastore
+ implementation(libs.datastore.preferences)
+
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.junit)
+ androidTestImplementation(libs.androidx.espresso.core)
+ androidTestImplementation(libs.androidx.ui.test.junit4)
+ debugImplementation(libs.androidx.ui.tooling)
+ debugImplementation(libs.androidx.ui.test.manifest)
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/app/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/app/src/androidTest/java/com/min/dnapp/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/min/dnapp/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..137e947
--- /dev/null
+++ b/app/src/androidTest/java/com/min/dnapp/ExampleInstrumentedTest.kt
@@ -0,0 +1,24 @@
+package com.min.dnapp
+
+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.min.dnapp", appContext.packageName)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..bed265b
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/assets/fireworks.json b/app/src/main/assets/fireworks.json
new file mode 100644
index 0000000..4f133f9
--- /dev/null
+++ b/app/src/main/assets/fireworks.json
@@ -0,0 +1 @@
+{"v":"5.1.4","fr":60,"ip":0,"op":240,"w":375,"h":375,"nm":"Comp 2","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[1,1.083]},"o":{"x":[0.012,1],"y":[0,0]},"n":["0p833_1_0p012_0","0p833_1p083_1_0"],"t":0,"s":[0,1],"e":[93.75,1]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0.083]},"n":["0p667_1_0p167_0","0p667_1_0p167_0p083"],"t":15,"s":[93.75,1],"e":[0,0]},{"t":30}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"n":"0p833_0p833_0p333_0","t":0,"s":[-187.5,0],"e":[-93.75,0],"to":[15.625,0],"ti":[-31.25,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"n":"0p667_1_0p167_0p167","t":15,"s":[-93.75,0],"e":[0,0],"to":[31.25,0],"ti":[-15.625,0]},{"t":30}],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.490196078431,0.698039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":15,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":36,"s":[0,0,100],"e":[100,100,100]},{"t":104}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":36,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":104}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.490196078431,0.698039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":36,"s":[60,0],"e":[0,100]},{"t":104}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":12,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":30,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":34,"op":105,"st":28,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":7.5,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":32,"s":[0,0,100],"e":[100,100,100]},{"t":96}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":32,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":96}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.750827205882,0.852413042854,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":32,"s":[60,0],"e":[0,100]},{"t":96}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":24,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":15,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":30,"op":98,"st":28,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":28,"s":[0,0,100],"e":[100,100,100]},{"t":88}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":28,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":88}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.490196078431,0.698039215686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":28,"s":[60,0],"e":[0,100]},{"t":88}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":12,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":30,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":28,"op":91,"st":28,"bm":0}]},{"id":"comp_1","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[1,1.083]},"o":{"x":[0.012,1],"y":[0,0]},"n":["0p833_1_0p012_0","0p833_1p083_1_0"],"t":0,"s":[0,1],"e":[93.75,1]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0.083]},"n":["0p667_1_0p167_0","0p667_1_0p167_0p083"],"t":15,"s":[93.75,1],"e":[0,0]},{"t":30}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"n":"0p833_0p833_0p333_0","t":0,"s":[-187.5,0],"e":[-93.75,0],"to":[15.625,0],"ti":[-31.25,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"n":"0p667_1_0p167_0p167","t":15,"s":[-93.75,0],"e":[0,0],"to":[31.25,0],"ti":[-15.625,0]},{"t":30}],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.486274509804,0.364705882353,0.929411764706,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":15,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":36,"s":[0,0,100],"e":[100,100,100]},{"t":104}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":36,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":104}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.486274509804,0.364705882353,0.929411764706,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":36,"s":[60,0],"e":[0,100]},{"t":104}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":12,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":30,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":34,"op":105,"st":28,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":7.5,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":32,"s":[0,0,100],"e":[100,100,100]},{"t":96}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":32,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":96}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.643137254902,0.556862745098,0.952941176471,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":32,"s":[60,0],"e":[0,100]},{"t":96}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":24,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":15,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":30,"op":98,"st":28,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":28,"s":[0,0,100],"e":[100,100,100]},{"t":88}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":28,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":88}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.486274509804,0.364705882353,0.929411764706,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":28,"s":[60,0],"e":[0,100]},{"t":88}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":12,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":30,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":28,"op":91,"st":28,"bm":0}]},{"id":"comp_2","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[1,1.083]},"o":{"x":[0.012,1],"y":[0,0]},"n":["0p833_1_0p012_0","0p833_1p083_1_0"],"t":0,"s":[0,1],"e":[93.75,1]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0.083]},"n":["0p667_1_0p167_0","0p667_1_0p167_0p083"],"t":15,"s":[93.75,1],"e":[0,0]},{"t":30}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"n":"0p833_0p833_0p333_0","t":0,"s":[-187.5,0],"e":[-93.75,0],"to":[15.625,0],"ti":[-31.25,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"n":"0p667_1_0p167_0p167","t":15,"s":[-93.75,0],"e":[0,0],"to":[31.25,0],"ti":[-15.625,0]},{"t":30}],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.494117647059,0.839215686275,0.956862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":15,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":36,"s":[0,0,100],"e":[100,100,100]},{"t":104}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":36,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":104}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.494117647059,0.839215686275,0.956862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":36,"s":[60,0],"e":[0,100]},{"t":104}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":12,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":30,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":34,"op":105,"st":28,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":7.5,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":32,"s":[0,0,100],"e":[100,100,100]},{"t":96}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":32,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":96}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.730839568493,0.892384906844,0.947457107843,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":32,"s":[60,0],"e":[0,100]},{"t":96}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":24,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":15,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":30,"op":98,"st":28,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":28,"s":[0,0,100],"e":[100,100,100]},{"t":88}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":28,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":88}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.494117647059,0.839215686275,0.956862745098,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":28,"s":[60,0],"e":[0,100]},{"t":88}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":12,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":30,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":28,"op":91,"st":28,"bm":0}]},{"id":"comp_3","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[1,1.083]},"o":{"x":[0.012,1],"y":[0,0]},"n":["0p833_1_0p012_0","0p833_1p083_1_0"],"t":0,"s":[0,1],"e":[93.75,1]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0.083]},"n":["0p667_1_0p167_0","0p667_1_0p167_0p083"],"t":15,"s":[93.75,1],"e":[0,0]},{"t":30}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"n":"0p833_0p833_0p333_0","t":0,"s":[-187.5,0],"e":[-93.75,0],"to":[15.625,0],"ti":[-31.25,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"n":"0p667_1_0p167_0p167","t":15,"s":[-93.75,0],"e":[0,0],"to":[31.25,0],"ti":[-15.625,0]},{"t":30}],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.717647058824,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":15,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":36,"s":[0,0,100],"e":[100,100,100]},{"t":104}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":36,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":104}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.717647058824,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":36,"s":[70,0],"e":[0,100]},{"t":104}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":12,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":30,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":34,"op":105,"st":28,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":7.5,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":32,"s":[0,0,100],"e":[100,100,100]},{"t":96}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":32,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":96}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.82023788153,0.36334252451,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":32,"s":[70,0],"e":[0,100]},{"t":96}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":24,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":15,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":30,"op":98,"st":28,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":28,"s":[0,0,100],"e":[100,100,100]},{"t":88}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":28,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":88}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.717647058824,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":28,"s":[70,0],"e":[0,100]},{"t":88}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":12,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":30,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":28,"op":91,"st":28,"bm":0}]},{"id":"comp_4","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[1,1.083]},"o":{"x":[0.012,1],"y":[0,0]},"n":["0p833_1_0p012_0","0p833_1p083_1_0"],"t":0,"s":[0,1],"e":[93.75,1]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0.083]},"n":["0p667_1_0p167_0","0p667_1_0p167_0p083"],"t":15,"s":[93.75,1],"e":[0,0]},{"t":30}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"n":"0p833_0p833_0p333_0","t":0,"s":[-187.5,0],"e":[-93.75,0],"to":[15.625,0],"ti":[-31.25,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"n":"0p667_1_0p167_0p167","t":15,"s":[-93.75,0],"e":[0,0],"to":[31.25,0],"ti":[-15.625,0]},{"t":30}],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.839215686275,0.827450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":15,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":36,"s":[0,0,100],"e":[100,100,100]},{"t":104}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":36,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":104}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.839215686275,0.827450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":36,"s":[75,0],"e":[0,100]},{"t":104}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":12,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":30,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":34,"op":105,"st":28,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":7.5,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":32,"s":[0,0,100],"e":[100,100,100]},{"t":96}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":32,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":96}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.408093830183,0.822411151961,0.816602998621,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":32,"s":[75,0],"e":[0,100]},{"t":96}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":24,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":15,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":30,"op":98,"st":28,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":28,"s":[0,0,100],"e":[100,100,100]},{"t":88}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":28,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":88}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0.839215686275,0.827450980392,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":28,"s":[75,0],"e":[0,100]},{"t":88}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":12,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":30,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":28,"op":91,"st":28,"bm":0}]},{"id":"comp_5","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[1,1.083]},"o":{"x":[0.012,1],"y":[0,0]},"n":["0p833_1_0p012_0","0p833_1p083_1_0"],"t":0,"s":[0,1],"e":[93.75,1]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0.083]},"n":["0p667_1_0p167_0","0p667_1_0p167_0p083"],"t":15,"s":[93.75,1],"e":[0,0]},{"t":30}],"ix":2},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"n":"0p833_0p833_0p333_0","t":0,"s":[-187.5,0],"e":[-93.75,0],"to":[15.625,0],"ti":[-31.25,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"n":"0p667_1_0p167_0p167","t":15,"s":[-93.75,0],"e":[0,0],"to":[31.25,0],"ti":[-15.625,0]},{"t":30}],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.443137258291,0.372549027205,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":15,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":36,"s":[0,0,100],"e":[100,100,100]},{"t":104}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":36,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":104}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.443137258291,0.372549027205,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":36,"s":[75,0],"e":[0,100]},{"t":104}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":12,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":30,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":34,"op":105,"st":28,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":7.5,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":32,"s":[0,0,100],"e":[100,100,100]},{"t":96}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":32,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":96}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.607843160629,0.560784339905,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":32,"s":[75,0],"e":[0,100]},{"t":96}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":24,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":15,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":30,"op":98,"st":28,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.5,187.5,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0,0.667],"y":[1,1,1]},"o":{"x":[0.03,0.03,0.333],"y":[0,0,0]},"n":["0_1_0p03_0","0_1_0p03_0","0p667_1_0p333_0"],"t":28,"s":[0,0,100],"e":[100,100,100]},{"t":88}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"sr","sy":2,"d":1,"pt":{"a":0,"k":3,"ix":3},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"n":"0p667_1_0p333_0","t":28,"s":[0,0],"e":[0,100],"to":[0,16.6666660308838],"ti":[0,-16.6666660308838]},{"t":88}],"ix":4},"r":{"a":0,"k":0,"ix":5},"or":{"a":0,"k":20,"ix":7},"os":{"a":0,"k":0,"ix":9},"ix":1,"nm":"Polystar Path 1","mn":"ADBE Vector Shape - Star","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.443137258291,0.372549027205,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-5.023,10.539],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0,0],"y":[1,1]},"o":{"x":[0.03,0.03],"y":[0,0]},"n":["0_1_0p03_0","0_1_0p03_0"],"t":28,"s":[75,0],"e":[0,100]},{"t":88}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Polystar 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"rp","c":{"a":0,"k":12,"ix":1},"o":{"a":0,"k":0,"ix":2},"m":1,"ix":2,"tr":{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":30,"ix":4},"so":{"a":0,"k":100,"ix":5},"eo":{"a":0,"k":100,"ix":6},"nm":"Transform"},"nm":"Repeater 1","mn":"ADBE Vector Filter - Repeater","hd":false}],"ip":28,"op":91,"st":28,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Comp 7","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[307.473,158.568,0],"ix":2},"a":{"a":0,"k":[187.5,187.5,0],"ix":1},"s":{"a":0,"k":[50,50,100],"ix":6}},"ao":0,"w":375,"h":375,"ip":104,"op":224,"st":104,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"Comp 6","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[79.867,175.707,0],"ix":2},"a":{"a":0,"k":[187.5,187.5,0],"ix":1},"s":{"a":0,"k":[60,60,100],"ix":6}},"ao":0,"w":375,"h":375,"ip":120,"op":240,"st":120,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"Comp 5","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[184.758,96.182,0],"ix":2},"a":{"a":0,"k":[187.5,187.5,0],"ix":1},"s":{"a":0,"k":[70,70,100],"ix":6}},"ao":0,"w":375,"h":375,"ip":90,"op":210,"st":90,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"Comp 4","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[86.723,167.48,0],"ix":2},"a":{"a":0,"k":[187.5,187.5,0],"ix":1},"s":{"a":0,"k":[60,60,100],"ix":6}},"ao":0,"w":375,"h":375,"ip":25,"op":145,"st":25,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"Comp 3","refId":"comp_4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[286.906,216.84,0],"ix":2},"a":{"a":0,"k":[187.5,187.5,0],"ix":1},"s":{"a":0,"k":[75,75,100],"ix":6}},"ao":0,"w":375,"h":375,"ip":16,"op":136,"st":16,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"Comp 1","refId":"comp_5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[187.5,108.5,0],"ix":2},"a":{"a":0,"k":[187.5,187.5,0],"ix":1},"s":{"a":0,"k":[80,80,100],"ix":6}},"ao":0,"w":375,"h":375,"ip":0,"op":120,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/app/src/main/ic_momento-playstore.png b/app/src/main/ic_momento-playstore.png
new file mode 100644
index 0000000..f4ee45f
Binary files /dev/null and b/app/src/main/ic_momento-playstore.png differ
diff --git a/app/src/main/java/com/min/dnapp/GlobalApp.kt b/app/src/main/java/com/min/dnapp/GlobalApp.kt
new file mode 100644
index 0000000..b7c1a17
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/GlobalApp.kt
@@ -0,0 +1,14 @@
+package com.min.dnapp
+
+import android.app.Application
+import com.kakao.sdk.common.KakaoSdk
+import dagger.hilt.android.HiltAndroidApp
+
+@HiltAndroidApp
+class GlobalApp : Application() {
+ override fun onCreate() {
+ super.onCreate()
+ // 카카오 SDK 초기화
+ KakaoSdk.init(this, BuildConfig.KAKAO_NATIVE_APP_KEY)
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/MainActivity.kt b/app/src/main/java/com/min/dnapp/MainActivity.kt
new file mode 100644
index 0000000..6e1fdb0
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/MainActivity.kt
@@ -0,0 +1,147 @@
+package com.min.dnapp
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.compose.foundation.layout.WindowInsets
+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.core.splashscreen.SplashScreen.Companion.installSplashScreen
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.currentBackStackEntryAsState
+import androidx.navigation.compose.rememberNavController
+import com.min.dnapp.presentation.AppStartViewModel
+import com.min.dnapp.presentation.bell.BellScreen
+import com.min.dnapp.presentation.find.FindDetailScreen
+import com.min.dnapp.presentation.find.FindScreen
+import com.min.dnapp.presentation.home.HomeScreen2
+import com.min.dnapp.presentation.login.LoginScreen2
+import com.min.dnapp.presentation.mypage.MyRecordScreen
+import com.min.dnapp.presentation.mypage.MypageScreen
+import com.min.dnapp.presentation.mypage.SettingScreen
+import com.min.dnapp.presentation.navigation.AppInitHost
+import com.min.dnapp.presentation.ui.component.MomentoBottomNav
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.write.RecordWriteScreen
+import com.min.dnapp.presentation.write.WriteFinishScreen
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ // 스플래시 화면 설정
+ val splashScreen = installSplashScreen()
+
+ super.onCreate(savedInstanceState)
+
+ // 콘텐츠를 화면 끝까지 확장 (Insets 처리는 Composable에게 맡기기)
+ enableEdgeToEdge()
+
+ setContent {
+ DngoTheme {
+ MomentoApp()
+ }
+ }
+ }
+}
+
+@Composable
+fun MomentoApp(
+ appStartViewModel: AppStartViewModel = hiltViewModel()
+) {
+ val isLogin by appStartViewModel.isLogin.collectAsStateWithLifecycle()
+ val startDestination = if (isLogin) "init_host" else "login"
+
+ // root navigation 담당
+ val navController = rememberNavController()
+
+ // root navigation 경로
+ val navBackStackEntry by navController.currentBackStackEntryAsState()
+ val currentRoute = navBackStackEntry?.destination?.route ?: startDestination
+
+ val showBottomBar = when (currentRoute) {
+ "home", "find", "my" -> true
+ else -> false
+ }
+
+ Scaffold(
+ // Padding 중복을 막기 위해 모든 Insets 차단
+ contentWindowInsets = WindowInsets(0, 0, 0, 0),
+ bottomBar = {
+ if (showBottomBar) {
+ MomentoBottomNav(
+ currentRoute = currentRoute,
+ onNavItemClick = { route ->
+ // 현재 경로와 다를때만 navigate 호출
+ if (navController.currentDestination?.route != route) {
+ navController.navigate(route)
+ }
+ }
+ )
+ }
+ }
+ ) { paddingValues ->
+ NavHost(
+ modifier = Modifier.padding(paddingValues),
+ navController = navController,
+ startDestination = startDestination
+ ) {
+ composable("login") {
+ LoginScreen2(
+ navController = navController,
+ onLoginSuccess = {
+ navController.navigate("init_host") {
+ popUpTo("login") { inclusive = true }
+ }
+ }
+ )
+ }
+
+ composable("init_host") {
+ AppInitHost(
+ onInitComplete = {
+ navController.navigate("home") {
+ popUpTo("init_host") { inclusive = true }
+ }
+ }
+ )
+ }
+
+ composable("home") {
+ HomeScreen2(navController = navController)
+ }
+ composable("find") {
+ FindScreen(navController = navController)
+ }
+ composable("my") {
+ MypageScreen(navController = navController)
+ }
+
+ composable("bell") {
+ BellScreen(navController = navController)
+ }
+ composable("find_detail") {
+ FindDetailScreen(navController = navController)
+ }
+ composable("record_write") {
+ RecordWriteScreen(navController = navController)
+ }
+ composable("write_finish") {
+ WriteFinishScreen(navController = navController)
+ }
+ composable("my_record") {
+ MyRecordScreen(navController = navController)
+ }
+ composable("setting") {
+ SettingScreen(navController = navController)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/data/datasource/AppPreferencesDataStore.kt b/app/src/main/java/com/min/dnapp/data/datasource/AppPreferencesDataStore.kt
new file mode 100644
index 0000000..b76409c
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/datasource/AppPreferencesDataStore.kt
@@ -0,0 +1,58 @@
+package com.min.dnapp.data.datasource
+
+import android.content.Context
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.IOException
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.booleanPreferencesKey
+import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.emptyPreferences
+import androidx.datastore.preferences.preferencesDataStore
+import dagger.hilt.android.qualifiers.ApplicationContext
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.map
+import javax.inject.Inject
+
+private val Context.dataStore: DataStore by preferencesDataStore(name = "app_init_prefs")
+
+class AppPreferencesDataStore @Inject constructor(
+ @ApplicationContext private val context: Context
+) {
+ private object PreferencesKey {
+ // 온보딩 완료 여부
+ val IS_ONBOARDING_COMPLETED = booleanPreferencesKey("is_onboarding_completed")
+ // 프로필 설정 완료 여부
+ val IS_PROFILE_SETUP_COMPLETED = booleanPreferencesKey("is_profile_setup_completed")
+ }
+
+ // 온보딩 완료 저장
+ suspend fun setOnboardingCompleted(isCompleted: Boolean) {
+ context.dataStore.edit { preferences ->
+ preferences[PreferencesKey.IS_ONBOARDING_COMPLETED] = isCompleted
+ }
+ }
+
+ // 프로필 설정 완료 저장
+ suspend fun setProfileSetupCompleted(isCompleted: Boolean) {
+ context.dataStore.edit { preferences ->
+ preferences[PreferencesKey.IS_PROFILE_SETUP_COMPLETED] = isCompleted
+ }
+ }
+
+ // 읽기 Flow
+ val initStatusFlow: Flow> = context.dataStore.data
+ .catch { exception ->
+ if (exception is IOException) {
+ emit(emptyPreferences())
+ } else {
+ throw exception
+ }
+ }
+ .map { preferences ->
+ val onboarding = preferences[PreferencesKey.IS_ONBOARDING_COMPLETED] ?: false
+ val profile = preferences[PreferencesKey.IS_PROFILE_SETUP_COMPLETED] ?: false
+ // 온보딩 완료 상태, 프로필 설정 완료 상태 반환
+ Pair(onboarding, profile)
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/data/di/FirebaseModule.kt b/app/src/main/java/com/min/dnapp/data/di/FirebaseModule.kt
new file mode 100644
index 0000000..15a303f
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/di/FirebaseModule.kt
@@ -0,0 +1,33 @@
+package com.min.dnapp.data.di
+
+import com.google.firebase.auth.FirebaseAuth
+import com.google.firebase.firestore.FirebaseFirestore
+import com.google.firebase.storage.FirebaseStorage
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object FirebaseModule {
+
+ @Provides
+ @Singleton
+ fun provideFirebaseAuth(): FirebaseAuth {
+ return FirebaseAuth.getInstance()
+ }
+
+ @Provides
+ @Singleton
+ fun provideFirebaseFirestore(): FirebaseFirestore {
+ return FirebaseFirestore.getInstance()
+ }
+
+ @Provides
+ @Singleton
+ fun provideFirebaseStorage(): FirebaseStorage {
+ return FirebaseStorage.getInstance()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/data/di/NetworkModule.kt b/app/src/main/java/com/min/dnapp/data/di/NetworkModule.kt
new file mode 100644
index 0000000..8186e63
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/di/NetworkModule.kt
@@ -0,0 +1,90 @@
+package com.min.dnapp.data.di
+
+import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
+import com.min.dnapp.BuildConfig
+import com.min.dnapp.data.remote.LocalSearchService
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import kotlinx.serialization.json.Json
+import okhttp3.Interceptor
+import okhttp3.MediaType.Companion.toMediaType
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import retrofit2.Retrofit
+import javax.inject.Qualifier
+import javax.inject.Singleton
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class NaverApiKey
+
+@Module
+@InstallIn(SingletonComponent::class)
+object NetworkModule {
+
+ private const val BASE_URL = "https://openapi.naver.com/"
+
+ @Provides
+ @Singleton
+ fun provideJson(): Json {
+ return Json {
+ // API 응답에서 사용하지 않는 필드는 무시하도록 설정
+ ignoreUnknownKeys = true
+ // 기본값이 있는 필드에 null이 오면 기본값 사용
+ coerceInputValues = true
+ }
+ }
+
+ @NaverApiKey
+ @Provides
+ fun provideNaverApiKey(): Pair {
+ val clientId = BuildConfig.NAVER_CLIENT_ID
+ val clientSecret = BuildConfig.NAVER_CLIENT_SECRET
+ return clientId to clientSecret
+ }
+
+ @Provides
+ @Singleton
+ fun provideAuthInterceptor(@NaverApiKey naverApiKey: Pair): Interceptor {
+ val (clientId, clientSecret) = naverApiKey
+ return Interceptor { chain ->
+ val request = chain.request().newBuilder()
+ .header("X-Naver-Client-Id", clientId)
+ .header("X-Naver-Client-Secret", clientSecret)
+ .build()
+ chain.proceed(request)
+ }
+ }
+
+ @Provides
+ @Singleton
+ fun provideOkHttpClient(authInterceptor: Interceptor): OkHttpClient {
+ val loggingInterceptor = HttpLoggingInterceptor().apply {
+ level = HttpLoggingInterceptor.Level.BODY
+ }
+ return OkHttpClient.Builder()
+ .addInterceptor(authInterceptor)
+ .addInterceptor(loggingInterceptor)
+ .build()
+ }
+
+ @Provides
+ @Singleton
+ fun provideRetrofit(okHttpClient: OkHttpClient, json: Json): Retrofit {
+ val contentType = "application/json".toMediaType()
+
+ return Retrofit.Builder()
+ .baseUrl(BASE_URL)
+ .client(okHttpClient)
+ .addConverterFactory(json.asConverterFactory(contentType))
+ .build()
+ }
+
+ @Provides
+ @Singleton
+ fun provideLocalSearchService(retrofit: Retrofit): LocalSearchService {
+ return retrofit.create(LocalSearchService::class.java)
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/data/di/RepositoryModule.kt b/app/src/main/java/com/min/dnapp/data/di/RepositoryModule.kt
new file mode 100644
index 0000000..2bb6e47
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/di/RepositoryModule.kt
@@ -0,0 +1,52 @@
+package com.min.dnapp.data.di
+
+import com.min.dnapp.data.repository.AppInitRepositoryImpl
+import com.min.dnapp.data.repository.AuthRepositoryImpl
+import com.min.dnapp.data.repository.LocalSearchRepositoryImpl
+import com.min.dnapp.data.repository.RecordRepositoryImpl
+import com.min.dnapp.data.repository.UserRepositoryImpl
+import com.min.dnapp.domain.repository.AppInitRepository
+import com.min.dnapp.domain.repository.AuthRepository
+import com.min.dnapp.domain.repository.LocalSearchRepository
+import com.min.dnapp.domain.repository.RecordRepository
+import com.min.dnapp.domain.repository.UserRepository
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+abstract class RepositoryModule {
+
+ @Binds
+ @Singleton
+ abstract fun bindAuthRepository(
+ authRepositoryImpl: AuthRepositoryImpl
+ ): AuthRepository
+
+ @Binds
+ @Singleton
+ abstract fun bindLocalSearchRepository(
+ localSearchRepositoryImpl: LocalSearchRepositoryImpl
+ ): LocalSearchRepository
+
+ @Binds
+ @Singleton
+ abstract fun bindRecordRepository(
+ recordRepositoryImpl: RecordRepositoryImpl
+ ): RecordRepository
+
+ @Binds
+ @Singleton
+ abstract fun bindUserRepository(
+ userRepositoryImpl: UserRepositoryImpl
+ ): UserRepository
+
+ @Binds
+ @Singleton
+ abstract fun bindAppInitRepository(
+ appInitRepositoryImpl: AppInitRepositoryImpl
+ ): AppInitRepository
+}
diff --git a/app/src/main/java/com/min/dnapp/data/mapper/PlaceMapper.kt b/app/src/main/java/com/min/dnapp/data/mapper/PlaceMapper.kt
new file mode 100644
index 0000000..69d7209
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/mapper/PlaceMapper.kt
@@ -0,0 +1,26 @@
+package com.min.dnapp.data.mapper
+
+import com.min.dnapp.data.remote.dto.PlaceEntity
+import com.min.dnapp.domain.model.LocalPlace
+
+/**
+ * LocalPlace -> PlaceEntity 변환
+ */
+fun LocalPlace.toEntity(): PlaceEntity {
+ return PlaceEntity(
+ title = this.title,
+ category = this.category,
+ roadAddress = this.roadAddress
+ )
+}
+
+/**
+ * PlaceEntity -> LocalPlace 변환
+ */
+fun PlaceEntity.toDomain(): LocalPlace {
+ return LocalPlace(
+ title = this.title ?: "",
+ category = this.category ?: "",
+ roadAddress = this.roadAddress ?: ""
+ )
+}
diff --git a/app/src/main/java/com/min/dnapp/data/mapper/RecordMapper.kt b/app/src/main/java/com/min/dnapp/data/mapper/RecordMapper.kt
new file mode 100644
index 0000000..647a960
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/mapper/RecordMapper.kt
@@ -0,0 +1,72 @@
+package com.min.dnapp.data.mapper
+
+import com.min.dnapp.data.remote.dto.RecordEntity
+import com.min.dnapp.domain.model.TripRecord
+import com.min.dnapp.domain.model.UserData
+import com.min.dnapp.presentation.write.RecordWriteUiState
+
+object RecordMapper {
+ /**
+ * RecordWriteUiState -> TripRecord (Domain) 변환
+ */
+ fun fromUiState(uiState: RecordWriteUiState, imageUrl: String?): TripRecord {
+ return TripRecord(
+ title = uiState.recordTitle,
+ content = uiState.recordContent,
+ startDateMillis = uiState.selectedStartDateMillis ?: 0L,
+ endDateMillis = uiState.selectedEndDateMillis ?: 0L,
+ emotionKey = uiState.selectedEmotion?.key ?: "",
+ weatherKey = uiState.selectedWeather?.key ?: "",
+ selectedPlace = uiState.selectedPlace,
+ overseasPlace = uiState.overseasPlace,
+ isShareChecked = uiState.isShareChecked,
+ // 업로드 후 받은 URL
+ imageUrl = imageUrl ?: "",
+ createdAt = 0L,
+ userId = "",
+ userData = null,
+ )
+ }
+
+ /**
+ * TripRecord (Domain) -> RecordEntity 변환
+ */
+ fun fromDomain(tripRecord: TripRecord, userData: UserData): RecordEntity {
+ return RecordEntity(
+ title = tripRecord.title,
+ content = tripRecord.content,
+ startDateMillis = tripRecord.startDateMillis,
+ endDateMillis = tripRecord.endDateMillis,
+ emotionKey = tripRecord.emotionKey,
+ weatherKey = tripRecord.weatherKey,
+ selectedPlace = tripRecord.selectedPlace?.toEntity(),
+ overseasPlace = tripRecord.overseasPlace,
+ isShareChecked = tripRecord.isShareChecked,
+ // 업로드 후 받은 URL
+ imageUrl = tripRecord.imageUrl,
+ userData = UserDataMapper.fromUserData(userData)
+ )
+ }
+
+ /**
+ * RecordEntity -> TripRecord (Domain) 변환
+ */
+ fun fromEntity(entity: RecordEntity): TripRecord {
+ return TripRecord(
+// recordId = entity.recordId ?: "",
+ userId = entity.userId ?: "",
+ title = entity.title ?: "",
+ content = entity.content ?: "",
+ startDateMillis = entity.startDateMillis ?: 0L,
+ endDateMillis = entity.endDateMillis ?: 0L,
+ emotionKey = entity.emotionKey ?: "",
+ weatherKey = entity.weatherKey ?: "",
+ selectedPlace = entity.selectedPlace?.toDomain(),
+ overseasPlace = entity.overseasPlace ?: "",
+ isShareChecked = entity.isShareChecked ?: false,
+ imageUrl = entity.imageUrl ?: "",
+ createdAt = entity.createdAt?.toDate()?.time ?: 0L,
+ userData = entity.userData?.let { UserDataMapper.fromUserDataEntity(it) }
+ )
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/data/mapper/UserDataMapper.kt b/app/src/main/java/com/min/dnapp/data/mapper/UserDataMapper.kt
new file mode 100644
index 0000000..536a1bd
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/mapper/UserDataMapper.kt
@@ -0,0 +1,40 @@
+package com.min.dnapp.data.mapper
+
+import com.min.dnapp.data.remote.dto.UserDataEntity
+import com.min.dnapp.domain.model.User
+import com.min.dnapp.domain.model.UserData
+
+object UserDataMapper {
+ /**
+ * User -> UserData 변환
+ */
+ fun fromUser(user: User): UserData {
+ return UserData(
+ badgeLv = user.badgeLv,
+ nickname = user.nickname,
+ profileImageName = user.profileImageName
+ )
+ }
+
+ /**
+ * UserData -> UserDataEntity 변환
+ */
+ fun fromUserData(userData: UserData): UserDataEntity {
+ return UserDataEntity(
+ badgeLv = userData.badgeLv,
+ nickname = userData.nickname,
+ profileImageName = userData.profileImageName
+ )
+ }
+
+ /**
+ * UserDataEntity -> UserData 변환
+ */
+ fun fromUserDataEntity(userDataEntity: UserDataEntity): UserData {
+ return UserData(
+ badgeLv = userDataEntity.badgeLv ?: 1,
+ nickname = userDataEntity.nickname ?: "",
+ profileImageName = userDataEntity.profileImageName ?: "01_boat"
+ )
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/data/mapper/UserMapper.kt b/app/src/main/java/com/min/dnapp/data/mapper/UserMapper.kt
new file mode 100644
index 0000000..4375e9d
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/mapper/UserMapper.kt
@@ -0,0 +1,40 @@
+package com.min.dnapp.data.mapper
+
+import com.min.dnapp.data.remote.dto.UserEntity
+import com.min.dnapp.domain.model.User
+
+object UserMapper {
+ /**
+ * Entity -> Domain 변환
+ */
+ fun toDomain(entity: UserEntity): User {
+ return User(
+ userId = entity.userId ?: "",
+ nickname = entity.nickname ?: "",
+ profileImageName = entity.profileImageName ?: "01_boat",
+ badgeLv = entity.badgeLv ?: 1,
+ badgeName = entity.badgeName ?: "새내기",
+ recordCnt = entity.recordCnt ?: 0,
+ stampCnt = entity.stampCnt ?: 0,
+ // Timestamp -> Long 변환
+ createdAt = entity.createdAt?.toDate()?.time ?: 0L
+ )
+ }
+
+ /**
+ * Domain -> Entity 변환
+ */
+ fun toEntity(domain: User): UserEntity {
+ return UserEntity(
+ userId = domain.userId,
+ nickname = domain.nickname,
+ profileImageName = domain.profileImageName,
+ badgeLv = domain.badgeLv,
+ badgeName = domain.badgeName,
+ recordCnt = domain.recordCnt,
+ stampCnt = domain.stampCnt,
+ // firestore 서버에서 시간 기록
+ createdAt = null
+ )
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/data/remote/LocalSearchResponse.kt b/app/src/main/java/com/min/dnapp/data/remote/LocalSearchResponse.kt
new file mode 100644
index 0000000..d12d4c8
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/remote/LocalSearchResponse.kt
@@ -0,0 +1,16 @@
+package com.min.dnapp.data.remote
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class LocalSearchResponse(
+ @SerialName("items") val items: List
+)
+
+@Serializable
+data class SearchItem(
+ @SerialName("title") val title: String,
+ @SerialName("category") val category: String,
+ @SerialName("roadAddress") val roadAddress: String
+)
diff --git a/app/src/main/java/com/min/dnapp/data/remote/LocalSearchService.kt b/app/src/main/java/com/min/dnapp/data/remote/LocalSearchService.kt
new file mode 100644
index 0000000..ac01086
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/remote/LocalSearchService.kt
@@ -0,0 +1,16 @@
+package com.min.dnapp.data.remote
+
+import retrofit2.http.GET
+import retrofit2.http.Header
+import retrofit2.http.Query
+
+interface LocalSearchService {
+ @GET("v1/search/local.json")
+ suspend fun search(
+// @Header("X-Naver-Client-Id") clientId: String,
+// @Header("X-Naver-Client-Secret") clientSecret: String,
+ @Query("query") query: String,
+ @Query("display") display: Int = 5,
+ @Query("sort") sort: String = "random"
+ ): LocalSearchResponse
+}
diff --git a/app/src/main/java/com/min/dnapp/data/remote/dto/RecordEntity.kt b/app/src/main/java/com/min/dnapp/data/remote/dto/RecordEntity.kt
new file mode 100644
index 0000000..65c3a6b
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/remote/dto/RecordEntity.kt
@@ -0,0 +1,46 @@
+package com.min.dnapp.data.remote.dto
+
+import com.google.firebase.Timestamp
+import com.google.firebase.firestore.Exclude
+import com.google.firebase.firestore.ServerTimestamp
+
+data class RecordEntity(
+ // firestore 문서 ID 값
+ @get:Exclude
+ var recordId: String? = null,
+
+ val userId: String? = null,
+ val userData: UserDataEntity? = null,
+
+ val title: String? = null,
+ val content: String? = null,
+
+ val startDateMillis: Long? = null,
+ val endDateMillis: Long? = null,
+
+ val emotionKey: String? = null,
+ val weatherKey: String? = null,
+
+ val selectedPlace: PlaceEntity? = null,
+ val overseasPlace: String? = null,
+
+ val isShareChecked: Boolean? = null,
+
+ val imageUrl: String? = null,
+
+ // 서버 타임스탬프
+ @ServerTimestamp
+ val createdAt: Timestamp? = null
+)
+
+data class PlaceEntity(
+ val title: String? = null,
+ val category: String? = null,
+ val roadAddress: String? = null
+)
+
+data class UserDataEntity(
+ val badgeLv: Int? = 1,
+ val nickname: String? = null,
+ val profileImageName: String? = "01_boat",
+)
diff --git a/app/src/main/java/com/min/dnapp/data/remote/dto/UserEntity.kt b/app/src/main/java/com/min/dnapp/data/remote/dto/UserEntity.kt
new file mode 100644
index 0000000..55bc463
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/remote/dto/UserEntity.kt
@@ -0,0 +1,14 @@
+package com.min.dnapp.data.remote.dto
+
+import com.google.firebase.Timestamp
+
+data class UserEntity(
+ val userId: String? = null,
+ val nickname: String? = null,
+ val profileImageName: String? = "01_boat",
+ val badgeLv: Int? = 1,
+ val badgeName: String? = "새내기",
+ val recordCnt: Int? = 0,
+ val stampCnt: Int? = 0,
+ val createdAt: Timestamp? = null
+)
diff --git a/app/src/main/java/com/min/dnapp/data/remote/dto/UserEntityExt.kt b/app/src/main/java/com/min/dnapp/data/remote/dto/UserEntityExt.kt
new file mode 100644
index 0000000..02a4f76
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/remote/dto/UserEntityExt.kt
@@ -0,0 +1,14 @@
+package com.min.dnapp.data.remote.dto
+
+fun UserEntity.toSaveMap(): MutableMap {
+ return mutableMapOf(
+ "userId" to this.userId,
+ "nickname" to this.nickname,
+ "profileImageName" to this.profileImageName,
+ "badgeLv" to this.badgeLv,
+ "badgeName" to this.badgeName,
+ "recordCnt" to this.recordCnt,
+ "stampCnt" to this.stampCnt,
+ "createdAt" to this.createdAt
+ )
+}
diff --git a/app/src/main/java/com/min/dnapp/data/repository/AppInitRepositoryImpl.kt b/app/src/main/java/com/min/dnapp/data/repository/AppInitRepositoryImpl.kt
new file mode 100644
index 0000000..c28cfd9
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/repository/AppInitRepositoryImpl.kt
@@ -0,0 +1,23 @@
+package com.min.dnapp.data.repository
+
+import com.min.dnapp.data.datasource.AppPreferencesDataStore
+import com.min.dnapp.domain.repository.AppInitRepository
+import kotlinx.coroutines.flow.Flow
+import javax.inject.Inject
+
+class AppInitRepositoryImpl @Inject constructor(
+ private val preferencesDataStore: AppPreferencesDataStore
+) : AppInitRepository {
+
+ override fun getInitStatus(): Flow> {
+ return preferencesDataStore.initStatusFlow
+ }
+
+ override suspend fun setOnboardingCompleted() {
+ preferencesDataStore.setOnboardingCompleted(true)
+ }
+
+ override suspend fun setProfileSetupCompleted() {
+ preferencesDataStore.setProfileSetupCompleted(true)
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/data/repository/AuthRepositoryImpl.kt b/app/src/main/java/com/min/dnapp/data/repository/AuthRepositoryImpl.kt
new file mode 100644
index 0000000..f8dba15
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/repository/AuthRepositoryImpl.kt
@@ -0,0 +1,263 @@
+package com.min.dnapp.data.repository
+
+import android.content.Context
+import android.util.Log
+import com.google.firebase.auth.FirebaseAuth
+import com.google.firebase.firestore.FieldValue
+import com.google.firebase.firestore.FirebaseFirestore
+import com.kakao.sdk.auth.model.OAuthToken
+import com.kakao.sdk.user.UserApiClient
+import com.min.dnapp.data.mapper.UserMapper
+import com.min.dnapp.data.remote.dto.toSaveMap
+import com.min.dnapp.domain.model.User
+import com.min.dnapp.domain.repository.AuthRepository
+import kotlinx.coroutines.CancellableContinuation
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.tasks.await
+import javax.inject.Inject
+
+class AuthRepositoryImpl @Inject constructor(
+ private val firebaseAuth: FirebaseAuth,
+ private val firestore: FirebaseFirestore
+) : AuthRepository {
+ override suspend fun signInWithKakao(context: Context): Result =
+ suspendCancellableCoroutine { continuation ->
+
+ // 카카오톡 앱 설치 여부에 따라 로그인 방식 결정
+ if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) {
+ // 카카오톡 앱을 통한 로그인
+ UserApiClient.instance.loginWithKakaoTalk(context) { token, error ->
+ // 코루틴이 이미 취소된 경우 처리하지 않음
+ if (continuation.isCancelled) return@loginWithKakaoTalk
+
+ handleKakaoLoginResult(token, error, continuation)
+ }
+ } else {
+ // 카카오 계정을 통한 웹 로그인
+ UserApiClient.instance.loginWithKakaoAccount(context) { token, error ->
+ // 코루틴이 이미 취소된 경우 처리하지 않음
+ if (continuation.isCancelled) return@loginWithKakaoAccount
+
+ handleKakaoLoginResult(token, error, continuation)
+ }
+ }
+ }
+
+ // 카카오 로그인 콜백 결과 처리
+ private fun handleKakaoLoginResult(
+ token: OAuthToken?,
+ error: Throwable?,
+ continuation: CancellableContinuation>
+ ) {
+ if (error != null) {
+ // 카카오 로그인 실패 시
+ continuation.resumeWith(Result.failure(error))
+ } else if (token != null) {
+ // 카카오 로그인 성공 시, 사용자 정보 조회
+ getUserInfoAndReadyAuth(continuation)
+ } else {
+ // 토큰이 null인 경우
+ continuation.resumeWith(Result.failure(Exception("카카오 로그인 토큰을 받을 수 없습니다")))
+ }
+ }
+
+ // 카카오 사용자 정보 조회 및 인증 준비
+ private fun getUserInfoAndReadyAuth(
+ continuation: CancellableContinuation>
+ ) {
+ UserApiClient.instance.me { user, error ->
+ // 코루틴이 취소된 경우 처리 중단
+ if (continuation.isCancelled) return@me
+
+ if (error != null) {
+ // 사용자 정보 조회 실패
+ continuation.resumeWith(Result.failure(error))
+ return@me
+ }
+
+ if (user == null) {
+ continuation.resumeWith(Result.failure(Exception("카카오 사용자 정보를 가져올 수 없습니다")))
+ return@me
+ }
+
+ // 카카오 사용자 id를 firebase auth용 이메일/비밀번호로 변환
+ val userId = user.id.toString()
+ val email = "$userId@kakao.com"
+ val password = userId
+
+ // 카카오 닉네임
+ val nickname = user.kakaoAccount?.profile?.nickname
+
+ // firebase auth에 로그인/회원가입
+ signInToFirebaseAuth(email, password, nickname, continuation)
+ }
+ }
+
+ // firebase auth 기존 사용자 로그인 처리
+ private fun signInToFirebaseAuth(
+ email: String,
+ password: String,
+ nickname: String?,
+ continuation: CancellableContinuation>
+ ) {
+ firebaseAuth.signInWithEmailAndPassword(email, password)
+ .addOnCompleteListener { task ->
+ if (continuation.isCancelled) return@addOnCompleteListener
+
+ if (task.isSuccessful) {
+ // 기존 사용자 로그인 성공
+ Log.d("auth", "signInToFirebaseAuth - 기존 사용자 로그인 성공")
+ continuation.resumeWith(Result.success(Result.success(Unit)))
+ } else {
+ // 기존 사용자가 아닌 경우, 새로 회원가입 진행
+ Log.d("auth", "signInToFirebaseAuth - 새로운 사용자! 회원가입 진행")
+ createFirebaseUser(email, password, nickname, continuation)
+ }
+ }
+ }
+
+ // firebase auth 회원가입 및 사용자 정보 저장
+ private fun createFirebaseUser(
+ email: String,
+ password: String,
+ nickname: String?,
+ continuation: CancellableContinuation>
+ ) {
+ firebaseAuth.createUserWithEmailAndPassword(email, password)
+ .addOnCompleteListener { createTask ->
+ if (continuation.isCancelled) return@addOnCompleteListener
+
+ if (createTask.isSuccessful) {
+ // firebase auth 회원가입 성공
+ val firebaseUser = firebaseAuth.currentUser
+
+ if (firebaseUser == null) {
+ continuation.resumeWith(Result.failure(Exception("firebase 사용자 정보를 찾을 수 없습니다")))
+ return@addOnCompleteListener
+ }
+
+ // 카카오 닉네임 사용하여 User Domain Model 생성
+ val newUser = createUserDomainModel(firebaseUser.uid, nickname)
+
+ // 콜백 내에서 suspend 함수를 호출하기 위해 코루틴 스코프 시작
+ CoroutineScope(Dispatchers.IO).launch {
+ val saveResult = runCatching {
+ // firestore에 저장
+ saveNewUser(newUser)
+ }
+
+ if (saveResult.isSuccess) {
+ // firestore 저장 성공
+ Log.d("auth", "createFirebaseUser - firestore 저장 성공. 닉네임 : $nickname")
+ continuation.resumeWith(Result.success(Result.success(Unit)))
+ } else {
+ // firestore 저장 실패
+ continuation.resumeWith(Result.failure(saveResult.exceptionOrNull() ?: Exception("사용자 정보 저장 실패")))
+ }
+ }
+ } else {
+ // 회원가입 실패
+ val exception = createTask.exception ?: Exception("firebase 회원가입 실패")
+ continuation.resumeWith(Result.failure(exception))
+
+ }
+ }
+ }
+
+ private fun createUserDomainModel(
+ firebaseUid: String,
+ nickname: String?
+ ): User {
+ return User(
+ userId = firebaseUid,
+ nickname = nickname ?: "카카오 사용자",
+ profileImageName = "01_boat",
+ badgeLv = 1,
+ badgeName = "새내기",
+ recordCnt = 0,
+ stampCnt = 0,
+ createdAt = 0L
+ )
+ }
+
+ /**
+ * firestore에 사용자 정보 저장
+ */
+ private suspend fun saveNewUser(user: User) {
+ val userEntity = UserMapper.toEntity(user)
+ val userId = userEntity.userId ?: throw IllegalArgumentException("saveNewUser - user ID null")
+
+ // Entity를 Map으로 변환
+ val dataMap = userEntity.toSaveMap().apply {
+ this["createdAt"] = FieldValue.serverTimestamp()
+ }
+
+ // Map 사용하여 firestore에 저장
+ firestore.collection("users").document(userId).set(dataMap).await()
+ }
+
+ override suspend fun getUser(): User {
+ TODO("Not yet implemented")
+ }
+
+ override suspend fun logout(): Result = suspendCancellableCoroutine { continuation ->
+ // 카카오 SDK 로그아웃
+ UserApiClient.instance.logout { error ->
+ if (error != null) {
+ Log.e("auth", "logout - 카카오 로그아웃 실패", error)
+ continuation.resumeWith(Result.failure(error))
+ return@logout
+ }
+
+ // firebase auth 로그아웃
+ firebaseAuth.signOut()
+ continuation.resumeWith(Result.success(Result.success(Unit)))
+ Log.d("auth", "logout - 카카오 & firebase 로그아웃 성공")
+ }
+ }
+
+ override suspend fun unlinkUser(): Result = suspendCancellableCoroutine { continuation ->
+ val firebaseUser = firebaseAuth.currentUser
+ if (firebaseUser == null) {
+ continuation.resumeWith(Result.failure(Exception("로그인된 사용자가 없습니다")))
+ return@suspendCancellableCoroutine
+ }
+
+ // firebase auth 사용자 삭제
+ firebaseUser.delete()
+ .addOnCompleteListener { authTask ->
+ Log.d("auth", "unlinkUser - firebase 계정 삭제 성공")
+ if (authTask.isSuccessful) {
+ // firestore 문서 삭제
+ firestore.collection("users").document(firebaseUser.uid).delete()
+ .addOnSuccessListener {
+ Log.d("auth", "unlinkUser - firestore 문서 삭제 성공")
+ // 카카오 연결 끊기
+ UserApiClient.instance.unlink { unlinkError ->
+ if (unlinkError != null) {
+ continuation.resumeWith(Result.failure(unlinkError))
+ } else {
+ continuation.resumeWith(Result.success(Result.success(Unit)))
+ Log.d("auth", "unlinkUser - 카카오 연결 끊기 성공")
+ }
+ }
+ }
+ .addOnFailureListener { firestoreException ->
+ continuation.resumeWith(Result.failure(firestoreException))
+ }
+ } else {
+ continuation.resumeWith(Result.failure(authTask.exception ?: Exception("firebase 계정 삭제 실패")))
+ }
+ }
+ }
+
+ /**
+ * 현재 로그인된 사용자의 UID를 반환
+ */
+ override suspend fun getCurrentUserId(): String? {
+ return firebaseAuth.currentUser?.uid
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/data/repository/LocalSearchRepositoryImpl.kt b/app/src/main/java/com/min/dnapp/data/repository/LocalSearchRepositoryImpl.kt
new file mode 100644
index 0000000..6dc6d19
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/repository/LocalSearchRepositoryImpl.kt
@@ -0,0 +1,49 @@
+package com.min.dnapp.data.repository
+
+import android.net.http.HttpEngine
+import android.util.Log
+import com.min.dnapp.data.remote.LocalSearchResponse
+import com.min.dnapp.data.remote.LocalSearchService
+import com.min.dnapp.domain.model.LocalPlace
+import com.min.dnapp.domain.repository.LocalSearchRepository
+import com.min.dnapp.util.Resource
+import com.min.dnapp.util.extractLastCategory
+import com.min.dnapp.util.removeTag
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flow
+import retrofit2.HttpException
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class LocalSearchRepositoryImpl @Inject constructor(
+ private val api: LocalSearchService
+) : LocalSearchRepository {
+ override fun searchPlaces(query: String): Flow>> = flow {
+ // 로딩 상태 방출
+ emit(Resource.Loading())
+
+ try {
+ val response: LocalSearchResponse = api.search(
+ query = query
+ )
+
+ val places = response.items.map { searchItem ->
+ LocalPlace(
+ title = searchItem.title.removeTag(),
+ category = searchItem.category.extractLastCategory(),
+ roadAddress = searchItem.roadAddress
+ )
+ }
+
+ // 성공 상태 방출
+ emit(Resource.Success(places))
+ } catch (e: HttpException) {
+ emit(Resource.Error("서버 오류 발생"))
+ } catch (e: java.io.IOException) {
+ emit(Resource.Error("네트워크 연결 문제"))
+ } catch (e: Exception) {
+ emit(Resource.Error("알 수 없는 오류 발생"))
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/data/repository/RecordRepositoryImpl.kt b/app/src/main/java/com/min/dnapp/data/repository/RecordRepositoryImpl.kt
new file mode 100644
index 0000000..525e717
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/repository/RecordRepositoryImpl.kt
@@ -0,0 +1,154 @@
+package com.min.dnapp.data.repository
+
+import android.net.Uri
+import android.util.Log
+import com.google.firebase.auth.FirebaseAuth
+import com.google.firebase.firestore.FieldValue
+import com.google.firebase.firestore.FirebaseFirestore
+import com.google.firebase.firestore.Query
+import com.google.firebase.firestore.toObjects
+import com.google.firebase.storage.FirebaseStorage
+import com.min.dnapp.data.mapper.RecordMapper
+import com.min.dnapp.data.remote.dto.RecordEntity
+import com.min.dnapp.domain.model.TripRecord
+import com.min.dnapp.domain.repository.RecordRepository
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.tasks.await
+import kotlinx.coroutines.withContext
+import javax.inject.Inject
+
+class RecordRepositoryImpl @Inject constructor(
+ private val firestore: FirebaseFirestore,
+ private val storage: FirebaseStorage,
+ private val firebaseAuth: FirebaseAuth
+) : RecordRepository {
+
+ // 현재 로그인된 사용자 ID 가져오기
+ private val currentUserId: String
+ get() = firebaseAuth.currentUser?.uid ?: throw IllegalStateException("user not authenticated")
+
+ /**
+ * Storage에 이미지 업로드 및 URL 반환
+ */
+ override suspend fun uploadImageAndGetUrl(imageUri: Uri): String {
+ // 사용자 ID 가져오기
+ val userId = currentUserId
+
+ // firebase storage 경로는 사용자별로 분리
+ val storageRef = storage.reference
+ .child("images")
+ .child(userId)
+ .child("${System.currentTimeMillis()}_${imageUri.lastPathSegment}")
+
+ // 이미지 업로드 및 URL 가져오기
+ storageRef.putFile(imageUri).await()
+ return storageRef.downloadUrl.await().toString()
+ }
+
+ /**
+ * 개인 기록 컬렉션에 저장
+ */
+ override suspend fun savePrivateRecord(record: RecordEntity): RecordEntity {
+ // 사용자 ID 가져오기
+ val userId = currentUserId
+
+ val recordCollection = firestore
+ .collection("records")
+ .document(userId)
+ .collection("private_records")
+
+ // firestore에서 새로운 문서 ID 생성
+ val newDoc = recordCollection.document()
+ val recordId = newDoc.id
+
+ // RecordEntity의 ID 관련 필드 채우기
+ val recordWithId = record.copy(
+ recordId = recordId,
+ userId = userId
+ )
+
+ // set()을 사용하여 해당 ID로 저장
+ newDoc.set(recordWithId).await()
+
+ return recordWithId
+ }
+
+ /**
+ * 전체공유 컬렉션에 저장
+ */
+ override suspend fun saveSharedRecord(record: RecordEntity) {
+ // 사용자 ID 가져오기
+ val userId = currentUserId
+
+ val sharedCollection = firestore.collection("shared_records")
+
+ val recordWithId = record.copy(userId = userId)
+
+ // set()을 사용하여 개인 기록과 동일한 ID로 저장
+ recordWithId.recordId?.let { recordId ->
+ sharedCollection.document(recordId).set(recordWithId).await()
+ }
+ }
+
+ override suspend fun getUserRecord(): List {
+ // 사용자 ID 가져오기
+ val userId = currentUserId
+
+ return withContext(Dispatchers.IO) {
+ try {
+ // querySnapshot 객체 가져오기 (사용자의 전체 문서)
+ val querySnapshot = firestore
+ .collection("records")
+ .document(userId)
+ .collection("private_records")
+ .orderBy("startDateMillis", Query.Direction.DESCENDING)
+ .get()
+ .await()
+
+ val entityList = querySnapshot.toObjects()
+ val domainList = entityList.map { RecordMapper.fromEntity(it) }
+
+ return@withContext domainList
+ } catch (e: Exception) {
+ Log.e("record", "getUserRecord error", e)
+ return@withContext emptyList()
+ }
+ }
+ }
+
+ /**
+ * 기록/스탬프 수 1씩 증가
+ */
+ override suspend fun increaseRecordAndStamp() {
+ val userId = currentUserId
+ val userDoc = firestore.collection("users").document(userId)
+
+ // Map으로 업데이트할 필드와 값을 지정
+ val updateCnt = mapOf(
+ "recordCnt" to FieldValue.increment(1),
+ "stampCnt" to FieldValue.increment(1)
+ )
+
+ userDoc.update(updateCnt).await()
+ }
+
+ override suspend fun getSharedRecord(): List {
+ return withContext(Dispatchers.IO) {
+ try {
+ val querySnapshot = firestore
+ .collection("shared_records")
+ .orderBy("createdAt", Query.Direction.DESCENDING)
+ .get()
+ .await()
+
+ val entityList = querySnapshot.toObjects()
+ val domainList = entityList.map { RecordMapper.fromEntity(it) }
+
+ return@withContext domainList
+ } catch (e: Exception) {
+ Log.e("record", "getSharedRecord error", e)
+ return@withContext emptyList()
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/data/repository/UserRepositoryImpl.kt b/app/src/main/java/com/min/dnapp/data/repository/UserRepositoryImpl.kt
new file mode 100644
index 0000000..44813c2
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/data/repository/UserRepositoryImpl.kt
@@ -0,0 +1,100 @@
+package com.min.dnapp.data.repository
+
+import android.util.Log
+import com.google.firebase.auth.FirebaseAuth
+import com.google.firebase.firestore.FirebaseFirestore
+import com.min.dnapp.data.remote.dto.UserEntity
+import com.min.dnapp.domain.model.Badge
+import com.min.dnapp.domain.repository.UserRepository
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.tasks.await
+import kotlinx.coroutines.withContext
+import javax.inject.Inject
+
+class UserRepositoryImpl @Inject constructor(
+ private val firestore: FirebaseFirestore,
+ private val firebaseAuth: FirebaseAuth
+) : UserRepository {
+
+ // 사용자 ID 가져오기
+ private val currentUserId: String
+ get() = firebaseAuth.currentUser?.uid ?: throw IllegalStateException("user not authenticated")
+
+ override suspend fun getUserData(uid: String): UserEntity {
+ // I/O 작업을 위해 Dispatchers.IO로 스레드 전환
+ return withContext(Dispatchers.IO) {
+ try {
+ val document = firestore
+ .collection("users")
+ .document(uid)
+ .get()
+ .await()
+
+ val userEntity = document.toObject(UserEntity::class.java)
+
+ return@withContext userEntity ?: throw Exception("user document problem")
+ } catch (e: Exception) {
+ throw Exception("failed getUserData: ${e.message}", e)
+ }
+ }
+ }
+
+ override suspend fun updateProfileImage(profileImageName: String): Result {
+ val userID = currentUserId
+ val userDoc = firestore.collection("users").document(userID)
+
+ val updateImage = mapOf(
+ "profileImageName" to profileImageName
+ )
+
+ return withContext(Dispatchers.IO) {
+ try {
+ userDoc.update(updateImage).await()
+ Result.success(Unit)
+ } catch (e: Exception) {
+ Log.e("record", "updateProfileImage error", e)
+ // 실패 정보 상위 레이어로 전달
+ Result.failure(e)
+ }
+ }
+ }
+
+ override suspend fun updateNickname(nickname: String): Result {
+ val userId = currentUserId
+ val userDoc = firestore.collection("users").document(userId)
+
+ val newNickname = mapOf(
+ "nickname" to nickname
+ )
+
+ return withContext(Dispatchers.IO) {
+ try {
+ userDoc.update(newNickname).await()
+ Result.success(Unit)
+ } catch (e: Exception) {
+ Log.e("record", "updateNickname error", e)
+ Result.failure(e)
+ }
+ }
+ }
+
+ override suspend fun updateBadge(badge: Badge): Result {
+ val userId = currentUserId
+ val userDoc = firestore.collection("users").document(userId)
+
+ val newBadge = mapOf(
+ "badgeLv" to badge.level,
+ "badgeName" to badge.name
+ )
+
+ return withContext(Dispatchers.IO) {
+ try {
+ userDoc.update(newBadge).await()
+ Result.success(Unit)
+ } catch (e: Exception) {
+ Log.e("user", "updateBadge error", e)
+ Result.failure(e)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/BadgeUtil.kt b/app/src/main/java/com/min/dnapp/domain/BadgeUtil.kt
new file mode 100644
index 0000000..e3f4a33
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/BadgeUtil.kt
@@ -0,0 +1,43 @@
+package com.min.dnapp.domain
+
+import com.min.dnapp.R
+import com.min.dnapp.domain.model.Badge
+
+val AllBadges = listOf(
+ Badge(
+ level = 1,
+ name = "새내기",
+ description = "다음 뱃지까지 5개 남았어요",
+ minStamp = 0,
+ nextBadgeRemainStamp = 5,
+ resId = R.drawable.badge_new,
+ ),
+ Badge(
+ level = 2,
+ name = "여린이",
+ description = "다음 뱃지까지 15개 남았어요",
+ minStamp = 5,
+ nextBadgeRemainStamp = 15,
+ resId = R.drawable.badge_bronze
+ ),
+ Badge(
+ level = 3,
+ name = "여행자",
+ description = "다음 뱃지까지 10개 남았어요",
+ minStamp = 20,
+ nextBadgeRemainStamp = 10,
+ resId = R.drawable.badge_silver
+ ),
+ Badge(
+ level = 4,
+ name = "마스터",
+ description = "최고 뱃지에 도달하셨어요",
+ minStamp = 30,
+ nextBadgeRemainStamp = 0,
+ resId = R.drawable.badge_gold
+ )
+)
+
+fun getNewBadgeData(totalStamp: Int): Badge? {
+ return AllBadges.firstOrNull { it.minStamp == totalStamp }
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/model/Badge.kt b/app/src/main/java/com/min/dnapp/domain/model/Badge.kt
new file mode 100644
index 0000000..3389226
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/model/Badge.kt
@@ -0,0 +1,12 @@
+package com.min.dnapp.domain.model
+
+import androidx.annotation.DrawableRes
+
+data class Badge(
+ val level: Int,
+ val name: String,
+ val description: String,
+ val minStamp: Int,
+ val nextBadgeRemainStamp: Int,
+ @DrawableRes val resId: Int,
+)
diff --git a/app/src/main/java/com/min/dnapp/domain/model/EmotionType.kt b/app/src/main/java/com/min/dnapp/domain/model/EmotionType.kt
new file mode 100644
index 0000000..bda48e1
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/model/EmotionType.kt
@@ -0,0 +1,17 @@
+package com.min.dnapp.domain.model
+
+import androidx.annotation.DrawableRes
+import com.min.dnapp.R
+
+enum class EmotionType(
+ val key: String,
+ @DrawableRes val resId: Int
+) {
+ HAPPY("happy", R.drawable.emotion_happy),
+ LOVE("love", R.drawable.emotion_love),
+ SURPRISE("surprise", R.drawable.emotion_surprise),
+ ANGRY("angry", R.drawable.emotion_angry),
+ FEEL("feel", R.drawable.emotion_feel),
+ SAD("sad", R.drawable.emotion_sad),
+ SHINE("shine", R.drawable.emotion_shine)
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/model/LocalPlace.kt b/app/src/main/java/com/min/dnapp/domain/model/LocalPlace.kt
new file mode 100644
index 0000000..b8f92b4
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/model/LocalPlace.kt
@@ -0,0 +1,7 @@
+package com.min.dnapp.domain.model
+
+data class LocalPlace(
+ val title: String,
+ val category: String,
+ val roadAddress: String
+)
diff --git a/app/src/main/java/com/min/dnapp/domain/model/TripRecord.kt b/app/src/main/java/com/min/dnapp/domain/model/TripRecord.kt
new file mode 100644
index 0000000..92a713d
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/model/TripRecord.kt
@@ -0,0 +1,25 @@
+package com.min.dnapp.domain.model
+
+data class TripRecord(
+// var recordId: String,
+ val userId: String,
+ val userData: UserData?,
+ val title: String,
+ val content: String,
+ val startDateMillis: Long,
+ val endDateMillis: Long,
+ val emotionKey: String,
+ val weatherKey: String,
+ // 복합 객체여서 nullable일 수 있음
+ val selectedPlace: LocalPlace?,
+ val overseasPlace: String,
+ val isShareChecked: Boolean,
+ val imageUrl: String,
+ val createdAt: Long
+)
+
+data class UserData(
+ val badgeLv: Int,
+ val nickname: String,
+ val profileImageName: String,
+)
diff --git a/app/src/main/java/com/min/dnapp/domain/model/User.kt b/app/src/main/java/com/min/dnapp/domain/model/User.kt
new file mode 100644
index 0000000..b338020
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/model/User.kt
@@ -0,0 +1,12 @@
+package com.min.dnapp.domain.model
+
+data class User(
+ val userId: String,
+ val nickname: String,
+ val profileImageName: String,
+ val badgeLv: Int,
+ val badgeName: String,
+ val recordCnt: Int,
+ val stampCnt: Int,
+ val createdAt: Long
+)
diff --git a/app/src/main/java/com/min/dnapp/domain/model/WeatherType.kt b/app/src/main/java/com/min/dnapp/domain/model/WeatherType.kt
new file mode 100644
index 0000000..74e6d81
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/model/WeatherType.kt
@@ -0,0 +1,17 @@
+package com.min.dnapp.domain.model
+
+import androidx.annotation.DrawableRes
+import com.min.dnapp.R
+
+enum class WeatherType(
+ val key: String,
+ @DrawableRes val resId: Int
+) {
+ SUN("sun", R.drawable.weather_sun),
+ WIND("wind", R.drawable.weather_wind),
+ MOON("moon", R.drawable.weather_moon),
+ THUNDER("thunder", R.drawable.weather_thunder),
+ RAIN("rain", R.drawable.weather_rain),
+ CLOUD("cloud", R.drawable.weather_cloud),
+ SNOW("snow", R.drawable.weather_snow)
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/repository/AppInitRepository.kt b/app/src/main/java/com/min/dnapp/domain/repository/AppInitRepository.kt
new file mode 100644
index 0000000..b4a7b87
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/repository/AppInitRepository.kt
@@ -0,0 +1,9 @@
+package com.min.dnapp.domain.repository
+
+import kotlinx.coroutines.flow.Flow
+
+interface AppInitRepository {
+ fun getInitStatus(): Flow>
+ suspend fun setOnboardingCompleted()
+ suspend fun setProfileSetupCompleted()
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/repository/AuthRepository.kt b/app/src/main/java/com/min/dnapp/domain/repository/AuthRepository.kt
new file mode 100644
index 0000000..99f5855
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/repository/AuthRepository.kt
@@ -0,0 +1,12 @@
+package com.min.dnapp.domain.repository
+
+import android.content.Context
+import com.min.dnapp.domain.model.User
+
+interface AuthRepository {
+ suspend fun signInWithKakao(context: Context): Result
+ suspend fun logout(): Result
+ suspend fun unlinkUser(): Result
+ suspend fun getCurrentUserId(): String?
+ suspend fun getUser(): User
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/repository/LocalSearchRepository.kt b/app/src/main/java/com/min/dnapp/domain/repository/LocalSearchRepository.kt
new file mode 100644
index 0000000..a04d76a
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/repository/LocalSearchRepository.kt
@@ -0,0 +1,9 @@
+package com.min.dnapp.domain.repository
+
+import com.min.dnapp.domain.model.LocalPlace
+import com.min.dnapp.util.Resource
+import kotlinx.coroutines.flow.Flow
+
+interface LocalSearchRepository {
+ fun searchPlaces(query: String): Flow>>
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/repository/RecordRepository.kt b/app/src/main/java/com/min/dnapp/domain/repository/RecordRepository.kt
new file mode 100644
index 0000000..4ac3ca5
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/repository/RecordRepository.kt
@@ -0,0 +1,14 @@
+package com.min.dnapp.domain.repository
+
+import android.net.Uri
+import com.min.dnapp.data.remote.dto.RecordEntity
+import com.min.dnapp.domain.model.TripRecord
+
+interface RecordRepository {
+ suspend fun uploadImageAndGetUrl(imageUri: Uri): String
+ suspend fun savePrivateRecord(record: RecordEntity): RecordEntity
+ suspend fun saveSharedRecord(record: RecordEntity)
+ suspend fun getUserRecord(): List
+ suspend fun increaseRecordAndStamp()
+ suspend fun getSharedRecord(): List
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/repository/UserRepository.kt b/app/src/main/java/com/min/dnapp/domain/repository/UserRepository.kt
new file mode 100644
index 0000000..b06c3d9
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/repository/UserRepository.kt
@@ -0,0 +1,11 @@
+package com.min.dnapp.domain.repository
+
+import com.min.dnapp.data.remote.dto.UserEntity
+import com.min.dnapp.domain.model.Badge
+
+interface UserRepository {
+ suspend fun getUserData(uid: String): UserEntity
+ suspend fun updateProfileImage(profileImageName: String): Result
+ suspend fun updateNickname(nickname: String): Result
+ suspend fun updateBadge(badge: Badge): Result
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/usecase/AuthWithKakaoUseCase.kt b/app/src/main/java/com/min/dnapp/domain/usecase/AuthWithKakaoUseCase.kt
new file mode 100644
index 0000000..ef5edef
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/usecase/AuthWithKakaoUseCase.kt
@@ -0,0 +1,13 @@
+package com.min.dnapp.domain.usecase
+
+import android.content.Context
+import com.min.dnapp.domain.repository.AuthRepository
+import javax.inject.Inject
+
+class AuthWithKakaoUseCase @Inject constructor(
+ private val authRepository: AuthRepository
+) {
+ suspend operator fun invoke(context: Context): Result {
+ return authRepository.signInWithKakao(context)
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/usecase/GetBadgeDialogDataUseCase.kt b/app/src/main/java/com/min/dnapp/domain/usecase/GetBadgeDialogDataUseCase.kt
new file mode 100644
index 0000000..e9b1f63
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/usecase/GetBadgeDialogDataUseCase.kt
@@ -0,0 +1,11 @@
+package com.min.dnapp.domain.usecase
+
+import com.min.dnapp.domain.getNewBadgeData
+import com.min.dnapp.domain.model.Badge
+import javax.inject.Inject
+
+class GetBadgeDialogDataUseCase @Inject constructor() {
+ operator fun invoke(totalStamp: Int): Badge? {
+ return getNewBadgeData(totalStamp)
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/usecase/GetCurrentUserIdUseCase.kt b/app/src/main/java/com/min/dnapp/domain/usecase/GetCurrentUserIdUseCase.kt
new file mode 100644
index 0000000..dc89bad
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/usecase/GetCurrentUserIdUseCase.kt
@@ -0,0 +1,12 @@
+package com.min.dnapp.domain.usecase
+
+import com.min.dnapp.domain.repository.AuthRepository
+import javax.inject.Inject
+
+class GetCurrentUserIdUseCase @Inject constructor(
+ private val authRepository: AuthRepository
+) {
+ suspend operator fun invoke(): String? {
+ return authRepository.getCurrentUserId()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/usecase/GetInitStatusUseCase.kt b/app/src/main/java/com/min/dnapp/domain/usecase/GetInitStatusUseCase.kt
new file mode 100644
index 0000000..a3e8360
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/usecase/GetInitStatusUseCase.kt
@@ -0,0 +1,13 @@
+package com.min.dnapp.domain.usecase
+
+import com.min.dnapp.domain.repository.AppInitRepository
+import kotlinx.coroutines.flow.Flow
+import javax.inject.Inject
+
+class GetInitStatusUseCase @Inject constructor(
+ private val appInitRepository: AppInitRepository
+) {
+ operator fun invoke(): Flow> {
+ return appInitRepository.getInitStatus()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/usecase/GetSharedRecordUseCase.kt b/app/src/main/java/com/min/dnapp/domain/usecase/GetSharedRecordUseCase.kt
new file mode 100644
index 0000000..b901321
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/usecase/GetSharedRecordUseCase.kt
@@ -0,0 +1,13 @@
+package com.min.dnapp.domain.usecase
+
+import com.min.dnapp.domain.model.TripRecord
+import com.min.dnapp.domain.repository.RecordRepository
+import javax.inject.Inject
+
+class GetSharedRecordUseCase @Inject constructor(
+ private val recordRepository: RecordRepository
+) {
+ suspend operator fun invoke(): List {
+ return recordRepository.getSharedRecord()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/usecase/GetUserDataUseCase.kt b/app/src/main/java/com/min/dnapp/domain/usecase/GetUserDataUseCase.kt
new file mode 100644
index 0000000..c593298
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/usecase/GetUserDataUseCase.kt
@@ -0,0 +1,17 @@
+package com.min.dnapp.domain.usecase
+
+import com.min.dnapp.data.mapper.UserMapper
+import com.min.dnapp.domain.model.User
+import com.min.dnapp.domain.repository.UserRepository
+import javax.inject.Inject
+
+class GetUserDataUseCase @Inject constructor(
+ private val userRepository: UserRepository
+) {
+ suspend operator fun invoke(uid: String): User {
+ val userEntity = userRepository.getUserData(uid)
+
+ // UserEntity -> User 변환
+ return UserMapper.toDomain(userEntity)
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/usecase/GetUserRecordUseCase.kt b/app/src/main/java/com/min/dnapp/domain/usecase/GetUserRecordUseCase.kt
new file mode 100644
index 0000000..2d98262
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/usecase/GetUserRecordUseCase.kt
@@ -0,0 +1,13 @@
+package com.min.dnapp.domain.usecase
+
+import com.min.dnapp.domain.model.TripRecord
+import com.min.dnapp.domain.repository.RecordRepository
+import javax.inject.Inject
+
+class GetUserRecordUseCase @Inject constructor(
+ private val recordRepository: RecordRepository
+) {
+ suspend operator fun invoke(): List {
+ return recordRepository.getUserRecord()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/usecase/LocalSearchUseCase.kt b/app/src/main/java/com/min/dnapp/domain/usecase/LocalSearchUseCase.kt
new file mode 100644
index 0000000..e33e229
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/usecase/LocalSearchUseCase.kt
@@ -0,0 +1,15 @@
+package com.min.dnapp.domain.usecase
+
+import com.min.dnapp.domain.model.LocalPlace
+import com.min.dnapp.domain.repository.LocalSearchRepository
+import com.min.dnapp.util.Resource
+import kotlinx.coroutines.flow.Flow
+import javax.inject.Inject
+
+class LocalSearchUseCase @Inject constructor(
+ private val repository: LocalSearchRepository
+) {
+ operator fun invoke(query: String): Flow>> {
+ return repository.searchPlaces(query)
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/usecase/LogoutUseCase.kt b/app/src/main/java/com/min/dnapp/domain/usecase/LogoutUseCase.kt
new file mode 100644
index 0000000..fa8600b
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/usecase/LogoutUseCase.kt
@@ -0,0 +1,12 @@
+package com.min.dnapp.domain.usecase
+
+import com.min.dnapp.domain.repository.AuthRepository
+import javax.inject.Inject
+
+class LogoutUseCase @Inject constructor(
+ private val authRepository: AuthRepository
+) {
+ suspend operator fun invoke(): Result {
+ return authRepository.logout()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/usecase/SaveRecordUseCase.kt b/app/src/main/java/com/min/dnapp/domain/usecase/SaveRecordUseCase.kt
new file mode 100644
index 0000000..c1d5951
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/usecase/SaveRecordUseCase.kt
@@ -0,0 +1,62 @@
+package com.min.dnapp.domain.usecase
+
+import android.net.Uri
+import com.google.firebase.auth.FirebaseAuth
+import com.min.dnapp.data.mapper.RecordMapper
+import com.min.dnapp.data.mapper.UserDataMapper
+import com.min.dnapp.domain.repository.RecordRepository
+import com.min.dnapp.presentation.write.RecordWriteUiState
+import javax.inject.Inject
+
+class SaveRecordUseCase @Inject constructor(
+ private val recordRepository: RecordRepository,
+ private val getUserDataUseCase: GetUserDataUseCase,
+ private val firebaseAuth: FirebaseAuth
+) {
+ suspend operator fun invoke(
+ uiState: RecordWriteUiState,
+ imageUri: Uri?
+ ): Result {
+ return try {
+ // 이미지 업로드 (URL 획득)
+ val imageUrl = if (imageUri != null) {
+ recordRepository.uploadImageAndGetUrl(imageUri)
+ } else {
+ null
+ }
+
+ // domain 모델 생성 (UiState -> TripRecord)
+ val tripRecord = RecordMapper.fromUiState(uiState, imageUrl)
+
+ // 현재 사용자 ID
+ val userId = firebaseAuth.currentUser?.uid ?: return Result.failure(IllegalStateException("user not login"))
+
+ // 사용자 데이터 가져오기
+ val user = getUserDataUseCase(userId)
+
+ // User -> UserData 변환
+ val userData = UserDataMapper.fromUser(user)
+
+ // TripRecord + UserData -> RecordEntity
+ val recordEntity = RecordMapper.fromDomain(tripRecord, userData)
+ .copy(userId = userId)
+
+ // 개인 기록 컬렉션에 저장 (필수)
+ // recordId가 포함된 RecordEntity를 받아 새 변수에 저장
+ val savedRecord = recordRepository.savePrivateRecord(recordEntity)
+
+ // 공유 여부에 따라 전체공유 컬렉션에도 저장 (선택)
+ if (uiState.isShareChecked) {
+ // 동일한 recordId 포함된 savedRecord 사용
+ recordRepository.saveSharedRecord(savedRecord)
+ }
+
+ // 기록/스탬프 수 1씩 증가
+ recordRepository.increaseRecordAndStamp()
+
+ Result.success(Unit)
+ } catch (e: Exception) {
+ Result.failure(e)
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/usecase/SetOnboardingCompletedUseCase.kt b/app/src/main/java/com/min/dnapp/domain/usecase/SetOnboardingCompletedUseCase.kt
new file mode 100644
index 0000000..0193d5d
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/usecase/SetOnboardingCompletedUseCase.kt
@@ -0,0 +1,12 @@
+package com.min.dnapp.domain.usecase
+
+import com.min.dnapp.domain.repository.AppInitRepository
+import javax.inject.Inject
+
+class SetOnboardingCompletedUseCase @Inject constructor(
+ private val appInitRepository: AppInitRepository
+) {
+ suspend operator fun invoke() {
+ appInitRepository.setOnboardingCompleted()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/usecase/SetProfileSetupCompletedUseCase.kt b/app/src/main/java/com/min/dnapp/domain/usecase/SetProfileSetupCompletedUseCase.kt
new file mode 100644
index 0000000..9864a1e
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/usecase/SetProfileSetupCompletedUseCase.kt
@@ -0,0 +1,12 @@
+package com.min.dnapp.domain.usecase
+
+import com.min.dnapp.domain.repository.AppInitRepository
+import javax.inject.Inject
+
+class SetProfileSetupCompletedUseCase @Inject constructor(
+ private val appInitRepository: AppInitRepository
+){
+ suspend operator fun invoke() {
+ appInitRepository.setProfileSetupCompleted()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/usecase/UnlinkUseCase.kt b/app/src/main/java/com/min/dnapp/domain/usecase/UnlinkUseCase.kt
new file mode 100644
index 0000000..e4e35fe
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/usecase/UnlinkUseCase.kt
@@ -0,0 +1,12 @@
+package com.min.dnapp.domain.usecase
+
+import com.min.dnapp.domain.repository.AuthRepository
+import javax.inject.Inject
+
+class UnlinkUseCase @Inject constructor(
+ private val authRepository: AuthRepository
+) {
+ suspend operator fun invoke(): Result {
+ return authRepository.unlinkUser()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/usecase/UpdateNicknameUseCase.kt b/app/src/main/java/com/min/dnapp/domain/usecase/UpdateNicknameUseCase.kt
new file mode 100644
index 0000000..5d35e1c
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/usecase/UpdateNicknameUseCase.kt
@@ -0,0 +1,12 @@
+package com.min.dnapp.domain.usecase
+
+import com.min.dnapp.domain.repository.UserRepository
+import javax.inject.Inject
+
+class UpdateNicknameUseCase @Inject constructor(
+ private val userRepository: UserRepository
+) {
+ suspend operator fun invoke(nickname: String): Result {
+ return userRepository.updateNickname(nickname)
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/usecase/UpdateProfileImageUseCase.kt b/app/src/main/java/com/min/dnapp/domain/usecase/UpdateProfileImageUseCase.kt
new file mode 100644
index 0000000..87b8b72
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/usecase/UpdateProfileImageUseCase.kt
@@ -0,0 +1,12 @@
+package com.min.dnapp.domain.usecase
+
+import com.min.dnapp.domain.repository.UserRepository
+import javax.inject.Inject
+
+class UpdateProfileImageUseCase @Inject constructor(
+ private val userRepository: UserRepository
+) {
+ suspend operator fun invoke(profileImageName: String): Result {
+ return userRepository.updateProfileImage(profileImageName)
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/domain/usecase/UpdateUserBadgeUseCase.kt b/app/src/main/java/com/min/dnapp/domain/usecase/UpdateUserBadgeUseCase.kt
new file mode 100644
index 0000000..e00417a
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/domain/usecase/UpdateUserBadgeUseCase.kt
@@ -0,0 +1,13 @@
+package com.min.dnapp.domain.usecase
+
+import com.min.dnapp.domain.model.Badge
+import com.min.dnapp.domain.repository.UserRepository
+import javax.inject.Inject
+
+class UpdateUserBadgeUseCase @Inject constructor(
+ private val userRepository: UserRepository
+) {
+ suspend operator fun invoke(badge: Badge): Result {
+ return userRepository.updateBadge(badge)
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/AppStartViewModel.kt b/app/src/main/java/com/min/dnapp/presentation/AppStartViewModel.kt
new file mode 100644
index 0000000..f5bb575
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/AppStartViewModel.kt
@@ -0,0 +1,19 @@
+package com.min.dnapp.presentation
+
+import androidx.lifecycle.ViewModel
+import com.google.firebase.auth.FirebaseAuth
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import javax.inject.Inject
+
+@HiltViewModel
+class AppStartViewModel @Inject constructor(
+ private val firebaseAuth: FirebaseAuth
+) : ViewModel() {
+
+ // 앱의 시작 상태(로그인 여부)를 저장
+ private val _isLogin = MutableStateFlow(firebaseAuth.currentUser != null)
+ val isLogin: StateFlow = _isLogin.asStateFlow()
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/bell/BellScreen.kt b/app/src/main/java/com/min/dnapp/presentation/bell/BellScreen.kt
new file mode 100644
index 0000000..4f0a48a
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/bell/BellScreen.kt
@@ -0,0 +1,148 @@
+package com.min.dnapp.presentation.bell
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.CenterAlignedTopAppBar
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavHostController
+import com.min.dnapp.R
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import com.min.dnapp.presentation.ui.icon.appicons.Back
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun BellScreen(navController: NavHostController) {
+ Scaffold(
+ containerColor = MomentoTheme.colors.brownBg,
+ topBar = {
+ CenterAlignedTopAppBar(
+ title = {
+ Text(
+ text = "알림",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW20
+ )
+ },
+ colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
+ MomentoTheme.colors.brownBg
+ ),
+ navigationIcon = {
+ Icon(
+ modifier = Modifier
+ .clickable { navController.popBackStack() }
+ .padding(16.dp),
+ imageVector = AppIcons.Back,
+ contentDescription = null
+ )
+ }
+ )
+ }
+ ) { paddingValues ->
+ Column(
+ modifier = Modifier
+ .padding(paddingValues)
+ .fillMaxSize()
+ ) {
+ HorizontalDivider(thickness = 1.dp, color = MomentoTheme.colors.grayW80)
+
+// BellCommentItem(
+// image = painterResource(R.drawable.trip),
+// nickname = "안녕하세요안녕하세요"
+// )
+//
+// for (i in 0 until 3) {
+// BellCommentItem(
+// image = painterResource(R.drawable.trip3),
+// nickname = "박사과"
+// )
+// }
+ }
+ }
+}
+
+@Composable
+fun BellCommentItem(
+ image: Painter,
+ nickname: String
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp, vertical = 12.dp ),
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Row {
+ Image(
+ painter = painterResource(R.drawable.bell_comment),
+ contentDescription = null
+ )
+
+ Spacer(Modifier.width(8.dp))
+
+ Column(
+ verticalArrangement = Arrangement.spacedBy(4.dp)
+ ) {
+ Text(
+ text = "2분 전",
+ style = MomentoTheme.typography.label,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ Text(
+ text = "$nickname 님이",
+ style = MomentoTheme.typography.body02 ,
+ color = MomentoTheme.colors.grayW20
+ )
+ Text(
+ text = "내 여행기록에 댓글을 달았어요.",
+ style = MomentoTheme.typography.body02 ,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ Text(
+ text = "제주도 동쪽 투어!",
+ style = MomentoTheme.typography.caption,
+ color = MomentoTheme.colors.grayW40
+ )
+ }
+ }
+
+ Image(
+ modifier = Modifier.size(56.dp),
+ painter = image,
+ contentDescription = null
+ )
+ }
+
+ HorizontalDivider(thickness = 1.dp, color = MomentoTheme.colors.grayW80)
+}
+
+@Preview
+@Composable
+fun BellScreenPreview() {
+ DngoTheme {
+// BellScreen()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/common/BadgeMapper.kt b/app/src/main/java/com/min/dnapp/presentation/common/BadgeMapper.kt
new file mode 100644
index 0000000..f8780de
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/common/BadgeMapper.kt
@@ -0,0 +1,15 @@
+package com.min.dnapp.presentation.common
+
+import com.min.dnapp.R
+
+object BadgeMapper {
+ fun getBadgeImageResId(badgeLv: Int): Int {
+ return when (badgeLv) {
+ 1 -> R.drawable.badge_new
+ 2 -> R.drawable.badge_bronze
+ 3 -> R.drawable.badge_silver
+ 4 -> R.drawable.badge_gold
+ else -> R.drawable.badge_new
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/common/EmotionMapper.kt b/app/src/main/java/com/min/dnapp/presentation/common/EmotionMapper.kt
new file mode 100644
index 0000000..06693fd
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/common/EmotionMapper.kt
@@ -0,0 +1,18 @@
+package com.min.dnapp.presentation.common
+
+import com.min.dnapp.R
+
+object EmotionMapper {
+ fun getEmotionImageResId(emotionName: String): Int {
+ return when (emotionName) {
+ "happy" -> R.drawable.emotion_happy
+ "love" -> R.drawable.emotion_love
+ "surprise" -> R.drawable.emotion_surprise
+ "angry" -> R.drawable.emotion_angry
+ "feel" -> R.drawable.emotion_feel
+ "sad" -> R.drawable.emotion_sad
+ "shine" -> R.drawable.emotion_shine
+ else -> R.drawable.emotion_happy
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/common/ProfileMapper.kt b/app/src/main/java/com/min/dnapp/presentation/common/ProfileMapper.kt
new file mode 100644
index 0000000..ee925c4
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/common/ProfileMapper.kt
@@ -0,0 +1,19 @@
+package com.min.dnapp.presentation.common
+
+import com.min.dnapp.R
+
+object ProfileMapper {
+ fun getProfileImageResId(profileName: String): Int {
+ return when (profileName) {
+ "01_boat" -> R.drawable.logo_profile
+ "02_tent" -> R.drawable.logo_profile2
+ "03_sea" -> R.drawable.logo_profile3
+ "04_bag" -> R.drawable.logo_profile4
+ "05_plane" -> R.drawable.logo_profile5
+ "06_telescope" -> R.drawable.logo_profile6
+ "07_map" -> R.drawable.logo_profile7
+ "08_mount" -> R.drawable.logo_profile8
+ else -> R.drawable.logo_profile
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/common/SnackbarMessage.kt b/app/src/main/java/com/min/dnapp/presentation/common/SnackbarMessage.kt
new file mode 100644
index 0000000..0640995
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/common/SnackbarMessage.kt
@@ -0,0 +1,5 @@
+package com.min.dnapp.presentation.common
+
+data class SnackbarMessage(
+ val message: String
+)
diff --git a/app/src/main/java/com/min/dnapp/presentation/common/WeatherMapper.kt b/app/src/main/java/com/min/dnapp/presentation/common/WeatherMapper.kt
new file mode 100644
index 0000000..81c397c
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/common/WeatherMapper.kt
@@ -0,0 +1,18 @@
+package com.min.dnapp.presentation.common
+
+import com.min.dnapp.R
+
+object WeatherMapper {
+ fun getWeatherImageResId(weatherName: String): Int {
+ return when (weatherName) {
+ "sun" -> R.drawable.weather_sun
+ "wind" -> R.drawable.weather_wind
+ "moon" -> R.drawable.weather_moon
+ "thunder" -> R.drawable.weather_thunder
+ "rain" -> R.drawable.weather_rain
+ "cloud" -> R.drawable.weather_cloud
+ "snow" -> R.drawable.weather_snow
+ else -> R.drawable.weather_sun
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/find/FindDetailScreen.kt b/app/src/main/java/com/min/dnapp/presentation/find/FindDetailScreen.kt
new file mode 100644
index 0000000..c27d226
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/find/FindDetailScreen.kt
@@ -0,0 +1,324 @@
+package com.min.dnapp.presentation.find
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material3.CenterAlignedTopAppBar
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+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.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavHostController
+import com.min.dnapp.R
+import com.min.dnapp.presentation.find.component.SharedRecordContentSection
+import com.min.dnapp.presentation.find.component.SharedRecordTimeSection
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import com.min.dnapp.presentation.ui.icon.appicons.Back
+import com.min.dnapp.presentation.ui.icon.appicons.More
+import com.min.dnapp.presentation.ui.icon.appicons.RecordBest
+import com.min.dnapp.presentation.ui.icon.appicons.RecordBookmark
+import com.min.dnapp.presentation.ui.icon.appicons.RecordComment
+import com.min.dnapp.presentation.ui.icon.appicons.RecordLike
+import com.min.dnapp.presentation.ui.icon.appicons.RecordSurprise
+import com.min.dnapp.presentation.ui.profile.ProfileImageCircle
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun FindDetailScreen(navController: NavHostController) {
+ Scaffold(
+ containerColor = MomentoTheme.colors.brownBg,
+ topBar = {
+ CenterAlignedTopAppBar(
+ title = {
+ Text(
+ text = "상세 보기",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW20
+ )
+ },
+ colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
+ containerColor = MomentoTheme.colors.brownBg
+ ),
+ navigationIcon = {
+ Icon(
+ modifier = Modifier
+ .clickable { navController.popBackStack() }
+ .padding(16.dp),
+ imageVector = AppIcons.Back,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW20
+ )
+ }
+ )
+ }
+ ) { paddingValues ->
+ LazyColumn(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(paddingValues)
+ ) {
+ // 기록 내용 영역
+ item {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(20.dp)
+ ) {
+ // 프로필 이미지
+// ProfileImageCircle(modifier = Modifier.size(36.dp))
+
+ Spacer(Modifier.width(8.dp))
+
+ Column(
+ modifier = Modifier.weight(1f)
+ ) {
+ // 공유 경과시간 + 더보기 아이콘 영역
+ SharedRecordMoreSection()
+
+ Spacer(Modifier.height(8.dp))
+
+ Text(
+ text = "시골청년님이 여행 기록을 공유했어요.",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ Spacer(Modifier.height(8.dp))
+
+ // 기록 내용
+// SharedRecordContentSection()
+ }
+ }
+ }
+
+ // 반응 이모지 영역
+ item {
+ HorizontalDivider(thickness = 1.dp, color = MomentoTheme.colors.grayW60)
+ FindDetailReactionSection(
+ bestNum = 1,
+ likeNum = 2,
+ surpriseNum = 12,
+ commentNum = 2
+ )
+ HorizontalDivider(thickness = 1.dp, color = MomentoTheme.colors.grayW60)
+ }
+
+ // 댓글 목록 영역
+ items(count = 5) {
+ CommentItem()
+ }
+ }
+ }
+}
+
+@Composable
+fun SharedRecordMoreSection() {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+// SharedRecordTimeSection()
+
+ Icon(
+ modifier = Modifier
+ .clickable { }
+ .padding(start = 16.dp),
+ imageVector = AppIcons.More,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW20
+ )
+ }
+}
+
+@Composable
+fun FindDetailReactionSection(
+ bestNum: Int,
+ likeNum: Int,
+ surpriseNum: Int,
+ commentNum: Int
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(start = 16.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = AppIcons.RecordComment,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW60
+ )
+ Spacer(Modifier.width(2.dp))
+ Text(
+ text = commentNum.toString(),
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+
+ Spacer(Modifier.width(20.dp))
+
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = AppIcons.RecordBest,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW60
+ )
+ Spacer(Modifier.width(2.dp))
+ Text(
+ text = bestNum.toString(),
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+
+ Spacer(Modifier.width(4.dp))
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = AppIcons.RecordLike,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW60
+ )
+ Spacer(Modifier.width(2.dp))
+ Text(
+ text = likeNum.toString(),
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+
+ Spacer(Modifier.width(4.dp))
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = AppIcons.RecordSurprise,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW60
+ )
+ Spacer(Modifier.width(2.dp))
+ Text(
+ text = surpriseNum.toString(),
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+ }
+
+ Icon(
+ modifier = Modifier
+ .clickable { }
+ .padding(16.dp),
+ imageVector = AppIcons.RecordBookmark,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW60
+ )
+ }
+}
+
+@Composable
+fun CommentItem() {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp, vertical = 12.dp)
+ ) {
+ // 프로필 이미지
+// ProfileImageCircle(modifier = Modifier.size(24.dp))
+
+ Spacer(Modifier.width(8.dp))
+
+ Column {
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = "성민",
+ style = MomentoTheme.typography.body03,
+ color = MomentoTheme.colors.grayW20
+ )
+ Spacer(Modifier.width(4.dp))
+ Image(
+ modifier = Modifier.size(20.dp),
+ painter = painterResource(R.drawable.badge_bronze),
+ contentDescription = null
+ )
+ }
+
+ Spacer(Modifier.height(8.dp))
+
+ Text(
+ text = "너무 예쁜데요??",
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ Spacer(Modifier.height(6.dp))
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = "1시간 전",
+ style = MomentoTheme.typography.label,
+ color = MomentoTheme.colors.grayW60
+ )
+ Spacer(Modifier.width(4.dp))
+ Text(
+ text = "・",
+ style = MomentoTheme.typography.label,
+ color = MomentoTheme.colors.grayW60
+ )
+ Spacer(Modifier.width(4.dp))
+ Text(
+ text = "신고",
+ style = MomentoTheme.typography.label,
+ color = MomentoTheme.colors.grayW60
+ )
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+fun FindDetailScreenPreview() {
+ DngoTheme {
+// FindDetailScreen()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/find/FindScreen.kt b/app/src/main/java/com/min/dnapp/presentation/find/FindScreen.kt
new file mode 100644
index 0000000..f47f9dc
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/find/FindScreen.kt
@@ -0,0 +1,134 @@
+package com.min.dnapp.presentation.find
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.material3.CenterAlignedTopAppBar
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.NavHostController
+import com.min.dnapp.R
+import com.min.dnapp.presentation.find.component.SharedRecordItem
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import com.min.dnapp.presentation.ui.icon.appicons.Bell
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun FindScreen(
+ navController: NavHostController,
+ findViewModel: FindViewModel = hiltViewModel()
+) {
+ val uiState by findViewModel.uiState.collectAsStateWithLifecycle()
+
+ Scaffold(
+ contentWindowInsets = WindowInsets(0, 0, 0, 0),
+ containerColor = MomentoTheme.colors.brownBg,
+ topBar = {
+ CenterAlignedTopAppBar(
+ title = { Text("") },
+ colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
+ containerColor = MomentoTheme.colors.brownBg
+ ),
+ navigationIcon = {
+ Image(
+ modifier = Modifier
+// .clickable { }
+// .padding(16.dp),
+ .padding(start = 16.dp),
+ painter = painterResource(R.drawable.logo_momento),
+ contentDescription = null
+ )
+ },
+ actions = {
+ Icon(
+ modifier = Modifier
+ .clickable { navController.navigate("bell") }
+ .padding(16.dp),
+ imageVector = AppIcons.Bell,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW60
+ )
+ }
+ )
+ }
+ ) { paddingValues ->
+
+ when (uiState) {
+ is FindUiState.Loading -> {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ CircularProgressIndicator(
+ modifier = Modifier.size(40.dp),
+ color = MomentoTheme.colors.brownW20,
+ strokeWidth = 4.dp
+ )
+ }
+ }
+ is FindUiState.Error -> {}
+ is FindUiState.Success -> {
+ // Success 데이터 추출
+ val data = uiState as FindUiState.Success
+
+ LazyColumn(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(paddingValues)
+ ) {
+ item {
+ HorizontalDivider(thickness = 1.dp, color = MomentoTheme.colors.grayW80)
+ }
+
+ itemsIndexed(data.records) { idx, record ->
+ Box(
+ modifier = Modifier.padding(20.dp)
+ ) {
+ // 발견 탭에 공유된 여행기록 아이템
+ SharedRecordItem(
+ record = record,
+ onClick = {
+// navController.navigate("explore_detail")
+ }
+ )
+ }
+
+ if (idx < data.records.size - 1) {
+ HorizontalDivider(thickness = 1.dp, color = MomentoTheme.colors.grayW80)
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+fun FindScreenPreview() {
+ DngoTheme {
+// FindScreen()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/find/FindUiState.kt b/app/src/main/java/com/min/dnapp/presentation/find/FindUiState.kt
new file mode 100644
index 0000000..adc5987
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/find/FindUiState.kt
@@ -0,0 +1,11 @@
+package com.min.dnapp.presentation.find
+
+import com.min.dnapp.domain.model.TripRecord
+
+sealed class FindUiState {
+ data object Loading: FindUiState()
+ data class Success(
+ val records: List = emptyList()
+ ) : FindUiState()
+ data class Error(val message: String): FindUiState()
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/find/FindViewModel.kt b/app/src/main/java/com/min/dnapp/presentation/find/FindViewModel.kt
new file mode 100644
index 0000000..1ff7b39
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/find/FindViewModel.kt
@@ -0,0 +1,47 @@
+package com.min.dnapp.presentation.find
+
+import android.util.Log
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.min.dnapp.domain.usecase.GetSharedRecordUseCase
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class FindViewModel @Inject constructor(
+ private val getSharedRecordUseCase: GetSharedRecordUseCase
+) : ViewModel() {
+
+ private val _uiState = MutableStateFlow(FindUiState.Loading)
+ val uiState: StateFlow = _uiState.asStateFlow()
+
+ init {
+ loaFindData()
+ }
+
+ private fun loaFindData() {
+ viewModelScope.launch {
+ // 로딩 시작
+ _uiState.value = FindUiState.Loading
+
+ // 공유된 기록 목록 가져오기
+ try {
+ val sharedRecords = getSharedRecordUseCase()
+ Log.d("record", "loaFindData - sharedRecords: $sharedRecords")
+ val successState = FindUiState.Success(
+ records = sharedRecords
+ )
+ _uiState.value = successState
+ } catch (e: Exception) {
+ _uiState.value = FindUiState.Success(
+ records = emptyList()
+ )
+ Log.e("record", "기록 목록 조회 실패", e)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/find/component/FindReactionIconSection.kt b/app/src/main/java/com/min/dnapp/presentation/find/component/FindReactionIconSection.kt
new file mode 100644
index 0000000..e578575
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/find/component/FindReactionIconSection.kt
@@ -0,0 +1,121 @@
+package com.min.dnapp.presentation.find.component
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import com.min.dnapp.presentation.ui.icon.appicons.RecordBest
+import com.min.dnapp.presentation.ui.icon.appicons.RecordBookmark
+import com.min.dnapp.presentation.ui.icon.appicons.RecordComment
+import com.min.dnapp.presentation.ui.icon.appicons.RecordLike
+import com.min.dnapp.presentation.ui.icon.appicons.RecordSurprise
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun FindReactionIconSection(
+ bestNum: Int,
+ likeNum: Int,
+ surpriseNum: Int,
+ commentNum: Int
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = AppIcons.RecordBest,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW60
+ )
+ Spacer(Modifier.width(2.dp))
+ Text(
+ text = bestNum.toString(),
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+
+ Spacer(Modifier.width(4.dp))
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = AppIcons.RecordLike,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW60
+ )
+ Spacer(Modifier.width(2.dp))
+ Text(
+ text = likeNum.toString(),
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+
+ Spacer(Modifier.width(4.dp))
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = AppIcons.RecordSurprise,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW60
+ )
+ Spacer(Modifier.width(2.dp))
+ Text(
+ text = surpriseNum.toString(),
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+ }
+
+ Row(
+ modifier = Modifier.clickable { },
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = AppIcons.RecordComment,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW60
+ )
+ Spacer(Modifier.width(2.dp))
+ Text(
+ text = commentNum.toString(),
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+
+ Spacer(Modifier.width(12.dp))
+
+ Icon(
+ imageVector = AppIcons.RecordBookmark,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW60
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/find/component/SharedRecordContentSection.kt b/app/src/main/java/com/min/dnapp/presentation/find/component/SharedRecordContentSection.kt
new file mode 100644
index 0000000..d16671b
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/find/component/SharedRecordContentSection.kt
@@ -0,0 +1,149 @@
+package com.min.dnapp.presentation.find.component
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import coil3.compose.AsyncImage
+import com.min.dnapp.R
+import com.min.dnapp.presentation.common.EmotionMapper
+import com.min.dnapp.presentation.common.WeatherMapper
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+import com.min.dnapp.util.toDateString
+
+@Composable
+fun SharedRecordContentSection(
+ title: String,
+ content: String,
+ startDateMillis: Long,
+ endDateMillis: Long?,
+ weatherName: String,
+ emotionName: String,
+ placeName: String?,
+ imageUrl: String?
+) {
+ val startDate = startDateMillis.toDateString("yy.MM.dd")
+ val endDate = endDateMillis?.toDateString("yy.MM.dd")
+
+ // 여행 날짜 텍스트 계산
+ val dateText = endDate?.let { "$startDate ~ $it" } ?: startDate
+
+ // 날씨 & 감정 이미지 리소스로 변환
+ val weatherImageResId = WeatherMapper.getWeatherImageResId(weatherName)
+ val emotionImageResId = EmotionMapper.getEmotionImageResId(emotionName)
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(color = MomentoTheme.colors.brownW90)
+ .padding(horizontal = 16.dp)
+ ) {
+ Spacer(Modifier.height(16.dp))
+
+ // 여행 제목
+ Text(
+ text = title,
+// style = MomentoTheme.typography.label,
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ Spacer(Modifier.height(6.dp))
+
+ HorizontalDivider(thickness = 1.dp, color = MomentoTheme.colors.pinkBase)
+
+ Spacer(Modifier.height(4.dp))
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ // 여행 날짜
+ Text(
+ text = dateText,
+ style = MomentoTheme.typography.body03,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ // 날씨 & 감정
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Image(
+ modifier = Modifier.size(28.dp),
+ painter = painterResource(weatherImageResId),
+ contentDescription = null
+ )
+ Spacer(Modifier.width(4.dp))
+ Image(
+ modifier = Modifier.size(28.dp),
+ painter = painterResource(emotionImageResId),
+ contentDescription = null
+ )
+ }
+ }
+
+ Spacer(Modifier.height(4.dp))
+
+ // 여행 장소
+ placeName?.let { place ->
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Image(
+ painter = painterResource(R.drawable.write_place),
+ contentDescription = null
+ )
+ Spacer(Modifier.width(6.dp))
+ Text(
+ text = place,
+ style = MomentoTheme.typography.body03 ,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+ }
+
+ Spacer(Modifier.height(6.dp))
+
+ // 여행 내용
+ Text(
+ text = content,
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ Spacer(Modifier.height(6.dp))
+
+ // 여행 이미지
+ imageUrl?.let { image ->
+ AsyncImage(
+ modifier = Modifier
+ .fillMaxWidth()
+ // 가로:세로 비율 1:1
+ .aspectRatio(1f),
+ model = image,
+ contentDescription = null,
+ contentScale = ContentScale.Crop
+ )
+ }
+
+ Spacer(Modifier.height(12.dp))
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/find/component/SharedRecordItem.kt b/app/src/main/java/com/min/dnapp/presentation/find/component/SharedRecordItem.kt
new file mode 100644
index 0000000..8d213b1
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/find/component/SharedRecordItem.kt
@@ -0,0 +1,90 @@
+package com.min.dnapp.presentation.find.component
+
+import androidx.compose.foundation.clickable
+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.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.domain.model.TripRecord
+import com.min.dnapp.presentation.ui.profile.ProfileImageCircle
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun SharedRecordItem(
+ record: TripRecord,
+ onClick: () -> Unit
+) {
+ Row(
+ modifier = Modifier
+// .clickable { onClick() }
+ .fillMaxWidth()
+ ) {
+ // 프로필 이미지
+ ProfileImageCircle(
+ profileImageName = record.userData?.profileImageName,
+ modifier = Modifier.size(36.dp)
+ )
+
+ Spacer(Modifier.width(8.dp))
+
+ Column(
+ modifier = Modifier.weight(1f)
+ ) {
+ // 공유 경과시간 영역
+ SharedRecordTimeSection(
+ nickname = record.userData?.nickname ?: "",
+ badgeLv = record.userData?.badgeLv ?: 1,
+ createdAtMillis = record.createdAt
+ )
+
+ Spacer(Modifier.height(8.dp))
+
+ Text(
+ text = "${record.userData?.nickname}님이 여행 기록을 공유했어요.",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ Spacer(Modifier.height(8.dp))
+
+ // 기록 전체내용
+ SharedRecordContentSection(
+ title = record.title,
+ content = record.content,
+ startDateMillis = record.startDateMillis,
+ endDateMillis = if (record.endDateMillis == 0L) null else record.endDateMillis,
+ weatherName = record.weatherKey,
+ emotionName = record.emotionKey,
+ placeName = record.selectedPlace?.title,
+ imageUrl = if (record.imageUrl.isEmpty()) null else record.imageUrl
+ )
+
+// Spacer(Modifier.height(12.dp))
+//
+// // 반응 이모지 영역
+// FindReactionIconSection(
+// bestNum = 1,
+// likeNum = 2,
+// surpriseNum = 12,
+// commentNum = 2
+// )
+ }
+ }
+}
+
+@Preview
+@Composable
+fun RecordItemPreview() {
+ DngoTheme {
+// SharedRecordItem()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/find/component/SharedRecordTimeSection.kt b/app/src/main/java/com/min/dnapp/presentation/find/component/SharedRecordTimeSection.kt
new file mode 100644
index 0000000..101d777
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/find/component/SharedRecordTimeSection.kt
@@ -0,0 +1,51 @@
+package com.min.dnapp.presentation.find.component
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.R
+import com.min.dnapp.presentation.common.BadgeMapper
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+import com.min.dnapp.util.toTimeAgoString
+
+@Composable
+fun SharedRecordTimeSection(
+ nickname: String,
+ badgeLv: Int,
+ createdAtMillis: Long
+) {
+ val imageResId = BadgeMapper.getBadgeImageResId(badgeLv)
+
+ // 경과 시간 텍스트 (현재 시간 기준으로 공유된 시간 계산)
+ val timeAgoText = createdAtMillis.toTimeAgoString()
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = nickname,
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ Image(
+ modifier = Modifier.size(20.dp),
+// painter = painterResource(R.drawable.badge_bronze),
+ painter = painterResource(imageResId),
+ contentDescription = null
+ )
+ Spacer(Modifier.width(4.dp))
+ Text(
+ text = timeAgoText,
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/home/HomeScreen.kt b/app/src/main/java/com/min/dnapp/presentation/home/HomeScreen.kt
new file mode 100644
index 0000000..4d7282f
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/home/HomeScreen.kt
@@ -0,0 +1,85 @@
+package com.min.dnapp.presentation.home
+
+import android.widget.Toast
+import androidx.compose.foundation.layout.Arrangement
+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.material3.Button
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.SnackbarHost
+import androidx.compose.material3.SnackbarHostState
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.navigation.NavController
+import com.min.dnapp.presentation.login.LoginViewModel
+
+@Composable
+fun HomeScreen(
+ loginViewModel: LoginViewModel = hiltViewModel(),
+ navController: NavController
+) {
+ val context = LocalContext.current
+ val snackbarHostState = remember { SnackbarHostState() }
+
+ Scaffold (
+ snackbarHost = { SnackbarHost(snackbarHostState) }
+ ) { paddingValues ->
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(paddingValues),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text("홈 화면")
+
+ Spacer(modifier = Modifier.height(32.dp))
+
+ Button(
+ onClick = {
+ loginViewModel.onLogoutClicked(
+ onSuccess = {
+ navController.navigate("login") {
+ popUpTo("login") { inclusive = true }
+ }
+ },
+ onFailure = {
+ Toast.makeText(context, "로그아웃 실패 : ${it.message}", Toast.LENGTH_SHORT).show()
+ }
+ )
+ }
+ ) {
+ Text("로그아웃")
+ }
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ Button(
+ onClick = {
+// loginViewModel.onUnlinkClicked(
+// onSuccess = {
+// Toast.makeText(context, "회원탈퇴 성공", Toast.LENGTH_SHORT).show()
+// navController.navigate("login") {
+// popUpTo("login") { inclusive = true }
+// }
+// },
+// onFailure = {
+// Toast.makeText(context, "회원탈퇴 실패 : ${it.message}", Toast.LENGTH_SHORT).show()
+// }
+// )
+ }
+ ) {
+ Text("회원탈퇴")
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/home/HomeScreen2.kt b/app/src/main/java/com/min/dnapp/presentation/home/HomeScreen2.kt
new file mode 100644
index 0000000..e90f30b
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/home/HomeScreen2.kt
@@ -0,0 +1,524 @@
+package com.min.dnapp.presentation.home
+
+import android.util.Log
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.CenterAlignedTopAppBar
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.VerticalDivider
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.NavHostController
+import androidx.navigation.compose.rememberNavController
+import coil3.compose.AsyncImage
+import com.min.dnapp.R
+import com.min.dnapp.domain.model.TripRecord
+import com.min.dnapp.presentation.ui.component.CustomFloatingActionButton
+import com.min.dnapp.presentation.ui.component.UserBadge
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import com.min.dnapp.presentation.ui.icon.appicons.ArrowRight
+import com.min.dnapp.presentation.ui.icon.appicons.Bell
+import com.min.dnapp.presentation.ui.icon.appicons.Year
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+import com.min.dnapp.util.toDateString
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun HomeScreen2(
+ navController: NavHostController,
+ homeViewModel: HomeViewModel = hiltViewModel()
+) {
+ val uiState by homeViewModel.uiState.collectAsStateWithLifecycle()
+
+ Scaffold(
+ contentWindowInsets = WindowInsets(0, 0, 0, 0),
+ containerColor = MomentoTheme.colors.brownW90,
+ topBar = {
+ CenterAlignedTopAppBar(
+ title = { Text("") },
+ colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
+ MomentoTheme.colors.brownW90
+ ),
+ navigationIcon = {
+ Image(
+ modifier = Modifier
+// .clickable { }
+// .padding(16.dp),
+ .padding(start = 16.dp),
+ painter = painterResource(R.drawable.logo_momento),
+ contentDescription = null
+ )
+ },
+ actions = {
+ Icon(
+ modifier = Modifier
+ .clickable { navController.navigate("bell") }
+ .padding(16.dp),
+ imageVector = AppIcons.Bell,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW60
+ )
+ }
+ )
+ },
+ floatingActionButton = {
+ CustomFloatingActionButton(
+ onClick = {
+ navController.navigate("record_write")
+ }
+ )
+ }
+ ) { paddingValues ->
+
+ when (uiState) {
+ is HomeUiState.Loading -> {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ CircularProgressIndicator(
+ modifier = Modifier.size(40.dp),
+ color = MomentoTheme.colors.brownW20,
+ strokeWidth = 4.dp
+ )
+ }
+ }
+ is HomeUiState.Error -> {
+ Log.e("home", "home 데이터 로드 실패")
+ }
+ is HomeUiState.Success -> {
+ // Success 데이터 추출
+ val data = uiState as HomeUiState.Success
+
+ Column(
+ modifier = Modifier
+ .padding(paddingValues)
+ .fillMaxSize()
+ ) {
+ Spacer(Modifier.height(20.dp))
+
+ HomeHeaderSection(
+ nickname = data.nickname,
+ badgeLv = data.badgeLv,
+ badgeName = data.badgeName,
+ recordCnt = data.recordCnt,
+ stampCnt = data.stampCnt
+ )
+
+ Spacer(Modifier.height(20.dp))
+
+ // 여행 기록 영역 (카드형 + 타임라인형)
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f)
+ .background(color = MomentoTheme.colors.brownBg, shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp))
+ .padding(horizontal = 20.dp)
+ ) {
+ Spacer(Modifier.height(20.dp))
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = "내 여행 기록",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW20
+ )
+ Icon(
+ modifier = Modifier.clickable {
+ navController.navigate("my_record")
+ },
+ imageVector = AppIcons.ArrowRight,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW20
+ )
+ }
+
+ Spacer(Modifier.height(12.dp))
+
+ if (data.records.isEmpty()) {
+ // 기록 없는 경우
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Spacer(Modifier.height(20.dp))
+ Image(
+ painter = painterResource(R.drawable.record_empty),
+ contentDescription = null
+ )
+ Spacer(Modifier.height(12.dp))
+ Text(
+ text = "아직 기록이 없네요. \n첫 여행을 기록해보세요!",
+ style = MomentoTheme.typography.title01,
+ color = MomentoTheme.colors.grayW20,
+ textAlign = TextAlign.Center
+ )
+ }
+ } else {
+ // 카드형 영역
+ HomeCardSection(records = data.records)
+
+ Spacer(Modifier.height(20.dp))
+
+ // 타임라인형 영역
+ TimelineSection(records = data.records)
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun HomeHeaderSection(
+ nickname: String,
+ recordCnt: Int,
+ stampCnt: Int,
+ badgeLv: Int,
+ badgeName: String,
+// badgeImageResId: Int
+) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp)
+ ) {
+ UserBadge(
+ badgeLv = badgeLv,
+ badgeName = badgeName,
+// badgeImageResId = badgeImageResId
+ )
+
+ Spacer(Modifier.height(12.dp))
+
+ Text(
+ text = "$nickname 님,",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW20
+ )
+ Spacer(Modifier.height(4.dp))
+ Text(
+// text = "이번 달엔 총0번 여행을 다녀왔어요!",
+ text = "지금까지 총 ${recordCnt}번 여행을 다녀왔어요!",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ Spacer(Modifier.height(12.dp))
+
+ // 스탬프 영역
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Image(
+ painter = painterResource(R.drawable.my_stamp),
+ contentDescription = null
+ )
+ Spacer(Modifier.width(4.dp))
+ Text(
+ text = "모은 스탬프 ${stampCnt}개",
+ style = MomentoTheme.typography.label,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+ }
+}
+
+@Composable
+fun HomeCardSection(
+ records: List
+) {
+ LazyRow(
+ horizontalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ items(records) { record ->
+ if (record.imageUrl.isEmpty()) {
+ HomeCardNoImage(record = record)
+ } else {
+ HomeCardImage(record = record)
+ }
+ }
+ }
+}
+
+@Composable
+fun HomeCardNoImage(
+ record: TripRecord
+) {
+ Box(
+ modifier = Modifier
+ .size(100.dp)
+ .background(color = MomentoTheme.colors.greenW80)
+ .padding(horizontal = 12.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Image(
+ painter = painterResource(R.drawable.image_basic),
+ contentDescription = null
+ )
+ record.selectedPlace?.let { selectedPlace ->
+ Text(
+ text = selectedPlace.title,
+ style = MomentoTheme.typography.label,
+ color = MomentoTheme.colors.grayW20,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ }
+ }
+}
+
+@Composable
+fun HomeCardImage(
+ record: TripRecord
+) {
+ Box(
+ modifier = Modifier,
+ contentAlignment = Alignment.Center
+ ) {
+ AsyncImage(
+ modifier = Modifier.size(100.dp),
+ model = record.imageUrl,
+ contentDescription = null,
+ contentScale = ContentScale.Crop
+ )
+
+ // 이미지 어둡게 처리
+ Box(
+ modifier = Modifier
+ .matchParentSize()
+ .background(color = Color.Black.copy(alpha = 0.3f))
+ )
+
+ record.selectedPlace?.let { selectedPlace ->
+ Text(
+ modifier = Modifier
+ .width(88.dp)
+ .padding(start = 12.dp),
+ text = selectedPlace.title,
+ style = MomentoTheme.typography.label,
+ color = MomentoTheme.colors.white,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ }
+ }
+}
+
+@Composable
+fun TimelineSection(
+ records: List
+) {
+ LazyColumn(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = 20.dp),
+ ) {
+ itemsIndexed(records) { idx, record ->
+ // 현재 항목의 년도
+ val currentYear = record.startDateMillis.toDateString("yyyy")
+ Log.d("home", "toDateString - currentYear: $currentYear")
+
+ // 직전 항목의 년도와 비교
+ val previousYear = if (idx > 0) {
+ records[idx-1].startDateMillis.toDateString("yyyy")
+ } else {
+ null
+ }
+
+ // 년도별 첫번째 항목 여부
+ val isYearStart = (idx == 0) || (currentYear != previousYear)
+
+ // 1번째 항목인 경우 or 년도가 바뀌는 시점에 년도 표시
+ if (idx == 0 || currentYear != previousYear) {
+ if (idx != 0) {
+ Spacer(Modifier.height(20.dp))
+ }
+ YearHeader(year = currentYear)
+ Spacer(Modifier.height(12.dp))
+ }
+
+ TimelineItem(
+ isYearStart = isYearStart,
+ startDateMillis = record.startDateMillis,
+ endDateMillis = if (record.endDateMillis == 0L) null else record.endDateMillis,
+ imageUrl = if (record.imageUrl.isEmpty()) null else record.imageUrl,
+ title = record.title,
+ placeName = record.selectedPlace?.title
+ )
+ }
+
+ item {
+ Spacer(Modifier.height(20.dp))
+ }
+ }
+}
+
+@Composable
+fun YearHeader(
+ year: String
+) {
+ Row {
+ Icon(
+ imageVector = AppIcons.Year,
+ contentDescription = null,
+ tint = MomentoTheme.colors.brownBase
+ )
+ Spacer(Modifier.width(6.dp))
+ Text(
+ text = "${year}년",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+}
+
+@Composable
+fun TimelineItem(
+ isYearStart: Boolean,
+ startDateMillis: Long,
+ endDateMillis: Long?,
+ title: String,
+ placeName: String?,
+ imageUrl: String?
+) {
+ // 월.일 추출 (예: 07.25)
+ val startDate = startDateMillis.toDateString("MM.dd")
+ val endDate = endDateMillis?.toDateString("MM.dd")
+
+ // 여행 날짜 텍스트 계산
+ val dateText = endDate?.let { "$startDate ~ $endDate" } ?: startDate
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Row(
+ modifier = Modifier.weight(1f)
+ ) {
+ HomeGrayLine(isYearStart = isYearStart)
+
+ Spacer(Modifier.width(12.dp))
+
+ Column(
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ Spacer(Modifier.height(4.dp))
+ Text(
+ text = dateText,
+ style = MomentoTheme.typography.body03,
+ color = MomentoTheme.colors.grayW20
+ )
+ Text(
+ text = title,
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Image(
+ painter = painterResource(R.drawable.write_place),
+ contentDescription = null
+ )
+ Spacer(Modifier.width(6.dp))
+ placeName?.let { place ->
+ Text(
+ text = place,
+ style = MomentoTheme.typography.body03,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+ }
+ }
+ }
+
+ imageUrl?.let { image ->
+ AsyncImage(
+ modifier = Modifier.size(90.dp),
+ model = image,
+ contentDescription = null,
+ contentScale = ContentScale.Crop
+ )
+ }
+ }
+}
+
+@Composable
+fun HomeGrayLine(
+ isYearStart: Boolean
+) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ if (isYearStart) {
+ Box(
+ modifier = Modifier
+ .size(8.dp)
+ .background(color = MomentoTheme.colors.grayW60, shape = CircleShape)
+ )
+ }
+ VerticalDivider(modifier = Modifier.height(100.dp), thickness = 1.dp, color = MomentoTheme.colors.grayW60)
+ Box(
+ modifier = Modifier
+ .size(8.dp)
+ .background(color = MomentoTheme.colors.grayW60, shape = CircleShape)
+ )
+ }
+}
+
+@Preview
+@Composable
+fun HomeScreen2Preview() {
+ DngoTheme {
+ HomeScreen2(
+ navController = rememberNavController()
+ )
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/home/HomeUiState.kt b/app/src/main/java/com/min/dnapp/presentation/home/HomeUiState.kt
new file mode 100644
index 0000000..e49a643
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/home/HomeUiState.kt
@@ -0,0 +1,17 @@
+package com.min.dnapp.presentation.home
+
+import com.min.dnapp.domain.model.TripRecord
+
+sealed class HomeUiState {
+ data object Loading : HomeUiState()
+ data class Success(
+ val nickname: String,
+ val badgeLv: Int,
+ val badgeName: String,
+ val recordCnt: Int,
+ val stampCnt: Int,
+ // 기록 목록
+ val records: List = emptyList()
+ ) : HomeUiState()
+ data class Error(val message: String) : HomeUiState()
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/home/HomeViewModel.kt b/app/src/main/java/com/min/dnapp/presentation/home/HomeViewModel.kt
new file mode 100644
index 0000000..20c6597
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/home/HomeViewModel.kt
@@ -0,0 +1,84 @@
+package com.min.dnapp.presentation.home
+
+import android.util.Log
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.min.dnapp.domain.model.User
+import com.min.dnapp.domain.usecase.GetCurrentUserIdUseCase
+import com.min.dnapp.domain.usecase.GetUserDataUseCase
+import com.min.dnapp.domain.usecase.GetUserRecordUseCase
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class HomeViewModel @Inject constructor(
+ private val getUserDataUseCase: GetUserDataUseCase,
+ private val getCurrentUserIdUseCase: GetCurrentUserIdUseCase,
+ private val getUserRecordUseCase: GetUserRecordUseCase
+) : ViewModel() {
+
+ private val _uiState = MutableStateFlow(HomeUiState.Loading)
+ val uiState: StateFlow = _uiState.asStateFlow()
+
+ init {
+ loadHomeData()
+ }
+
+ private fun loadHomeData() {
+ viewModelScope.launch {
+ // 로딩 시작
+ _uiState.value = HomeUiState.Loading
+
+ // 인증 정보 요청
+ val uid = try {
+ getCurrentUserIdUseCase() ?: throw Exception("사용자 인증 정보 없음")
+ } catch (e: Exception) {
+ _uiState.value = HomeUiState.Error("인증 정보 로드 실패: ${e.message}")
+ return@launch
+ }
+
+ // 사용자 정보 로드 및 Success 상태 초기화
+ val successState: HomeUiState.Success
+ try {
+ val user = getUserDataUseCase(uid)
+ Log.d("home", "loadHomeData - user: $user")
+
+ successState = mapUserToHomeUiState(user)
+
+ _uiState.value = successState
+ } catch (e: Exception) {
+ _uiState.value = HomeUiState.Error("사용자 정보 로드 실패: ${e.message}")
+ return@launch
+ }
+
+ // 여행기록 정보 로드 및 상태 업데이트
+ try {
+ val userRecords = getUserRecordUseCase()
+ Log.d("home", "loadHomeData - userRecords: $userRecords")
+ val finalSuccessState = successState.copy(
+ records = userRecords
+ )
+ _uiState.value = finalSuccessState
+ } catch (e: Exception) {
+ _uiState.value = successState.copy(
+ records = emptyList()
+ )
+ Log.e("home", "기록 정보 로드 실패", e)
+ }
+ }
+ }
+
+ private fun mapUserToHomeUiState(user: User): HomeUiState.Success {
+ return HomeUiState.Success(
+ nickname = user.nickname,
+ badgeLv = user.badgeLv,
+ badgeName = user.badgeName,
+ recordCnt = user.recordCnt,
+ stampCnt = user.stampCnt
+ )
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/init/AppInitViewModel.kt b/app/src/main/java/com/min/dnapp/presentation/init/AppInitViewModel.kt
new file mode 100644
index 0000000..0273343
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/init/AppInitViewModel.kt
@@ -0,0 +1,113 @@
+package com.min.dnapp.presentation.init
+
+import android.util.Log
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.min.dnapp.domain.usecase.GetInitStatusUseCase
+import com.min.dnapp.domain.usecase.SetOnboardingCompletedUseCase
+import com.min.dnapp.domain.usecase.SetProfileSetupCompletedUseCase
+import com.min.dnapp.domain.usecase.UpdateProfileImageUseCase
+import com.min.dnapp.presentation.mypage.ProfileImageType
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+enum class InitRoute {
+ LOADING,
+ ONBOARDING,
+ PROFILE_SETUP,
+ MAIN
+}
+
+data class InitUiState(
+ val route: InitRoute = InitRoute.LOADING
+)
+
+@HiltViewModel
+class AppInitViewModel @Inject constructor(
+ getInitStatusUseCase: GetInitStatusUseCase,
+ private val setOnboardingCompletedUseCase: SetOnboardingCompletedUseCase,
+ private val setProfileSetupCompletedUseCase: SetProfileSetupCompletedUseCase,
+ private val updateProfileImageUseCase: UpdateProfileImageUseCase
+) : ViewModel() {
+
+ private val _uiState = MutableStateFlow(InitUiState())
+ val uiState: StateFlow = _uiState.asStateFlow()
+
+ // 선택된 프로필 이미지
+ private val _selectedImage = MutableStateFlow(null)
+ val selectedImage: StateFlow = _selectedImage.asStateFlow()
+
+ // 프로필 저장 상태
+ private val _saveImageState = MutableStateFlow(SaveImageState.Init)
+ val saveImageState: StateFlow = _saveImageState.asStateFlow()
+
+ init {
+ viewModelScope.launch {
+ getInitStatusUseCase().collect { (isOnboardingCompleted, isProfileSetupCompleted) ->
+ val nextRoute = when {
+ // 2단계 완료
+ isProfileSetupCompleted -> InitRoute.MAIN
+ // 1단계 완료
+ isOnboardingCompleted -> InitRoute.PROFILE_SETUP
+ // 둘다 완료되지 않음
+ else -> InitRoute.ONBOARDING
+ }
+ _uiState.update { it.copy(route = nextRoute) }
+ }
+ }
+ }
+
+ /**
+ * 온보딩 3개 완료 후 호출
+ */
+ fun onOnboardingFinished() {
+ viewModelScope.launch {
+ setOnboardingCompletedUseCase()
+ }
+ }
+
+ /**
+ * 프로필 선택 완료 후 호출
+ */
+ private fun onProfileSetupFinished() {
+ viewModelScope.launch {
+ setProfileSetupCompletedUseCase()
+ }
+ }
+
+ /**
+ * 프로필 이미지 선택
+ */
+ fun selectImage(image: ProfileImageType) {
+ // 선택된 이미지 다시 선택하면 해제
+ _selectedImage.value = if (_selectedImage.value == image) null else image
+ }
+
+ /**
+ * 프로필 이미지 저장
+ */
+ fun saveProfileImage() {
+ viewModelScope.launch {
+ _selectedImage.value?.let { image ->
+
+ // 로딩 시작
+ _saveImageState.value = SaveImageState.Loading
+
+ val result = updateProfileImageUseCase(image.key)
+
+ result.onSuccess {
+ onProfileSetupFinished()
+ _saveImageState.value = SaveImageState.Success
+ }.onFailure { exception ->
+ Log.e("init", "saveProfileImage 실패", exception)
+ _saveImageState.value = SaveImageState.Error("이미지 저장 실패")
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/init/LoadingScreen.kt b/app/src/main/java/com/min/dnapp/presentation/init/LoadingScreen.kt
new file mode 100644
index 0000000..92efd05
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/init/LoadingScreen.kt
@@ -0,0 +1,33 @@
+package com.min.dnapp.presentation.init
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun LoadingScreen() {
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MomentoTheme.colors.brownBg
+ ) {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ CircularProgressIndicator(
+ modifier = Modifier.size(40.dp),
+ color = MomentoTheme.colors.brownW20,
+ strokeWidth = 4.dp
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/init/OnboardingScreen.kt b/app/src/main/java/com/min/dnapp/presentation/init/OnboardingScreen.kt
new file mode 100644
index 0000000..d41a875
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/init/OnboardingScreen.kt
@@ -0,0 +1,63 @@
+package com.min.dnapp.presentation.init
+
+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.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.systemBarsPadding
+import androidx.compose.material3.Surface
+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.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.R
+import com.min.dnapp.presentation.init.component.OnboardingButtonSection
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun OnboardingScreen(
+ onFinish: () -> Unit
+) {
+ Surface(
+ modifier = Modifier
+ .systemBarsPadding()
+ .fillMaxSize(),
+ color = MomentoTheme.colors.brownW90
+ ) {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ verticalArrangement = Arrangement.SpaceBetween
+ ) {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.End
+ ) {
+ Spacer(Modifier.height(144.dp))
+ Image(
+ painter = painterResource(R.drawable.logo_onboarding),
+ contentDescription = null
+ )
+ }
+
+ OnboardingButtonSection(
+ onboardingNum = 1,
+ title = "모멘토와 함께하는 따뜻하고 진솔한 공간. \n제목, 날짜, 장소, 감정을 \n입력하여 내 기록을 남겨보세요.",
+ onClick = { onFinish() }
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+fun OnboardingScreenPreview() {
+ DngoTheme {
+// OnboardingScreen()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/init/OnboardingScreen2.kt b/app/src/main/java/com/min/dnapp/presentation/init/OnboardingScreen2.kt
new file mode 100644
index 0000000..c63a77d
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/init/OnboardingScreen2.kt
@@ -0,0 +1,90 @@
+package com.min.dnapp.presentation.init
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.CenterAlignedTopAppBar
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+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.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavHostController
+import com.min.dnapp.R
+import com.min.dnapp.presentation.init.component.OnboardingButtonSection
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import com.min.dnapp.presentation.ui.icon.appicons.Back
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun OnboardingScreen2(
+ navController: NavHostController,
+ onFinish: () -> Unit
+) {
+ Scaffold(
+ containerColor = MomentoTheme.colors.brownW90,
+ topBar = {
+ CenterAlignedTopAppBar(
+ title = { Text("") },
+ navigationIcon = {
+ Icon(
+ modifier = Modifier
+ .clickable { navController.popBackStack() }
+ .padding(16.dp),
+ imageVector = AppIcons.Back,
+ contentDescription = null
+ )
+ },
+ colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
+ containerColor = MomentoTheme.colors.brownW90
+ )
+ )
+ }
+ ) { paddingValues ->
+ Column(
+ modifier = Modifier
+ .padding(paddingValues)
+ .fillMaxSize(),
+ verticalArrangement = Arrangement.SpaceBetween
+ ) {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.End
+ ) {
+ Spacer(Modifier.height(76.dp))
+ Image(
+ painter = painterResource(R.drawable.logo_onboarding2),
+ contentDescription = null
+ )
+ }
+
+ OnboardingButtonSection(
+ onboardingNum = 2,
+ title = "여행을 기록할 때마다 스탬프가 적립돼요. \n하나의 기록이 모여 나만의 컬렉션이 완성됩니다. \n",
+ onClick = { onFinish() }
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+fun OnboardingScreen2Preview() {
+ DngoTheme {
+// OnboardingScreen2()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/init/OnboardingScreen3.kt b/app/src/main/java/com/min/dnapp/presentation/init/OnboardingScreen3.kt
new file mode 100644
index 0000000..5296fa8
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/init/OnboardingScreen3.kt
@@ -0,0 +1,90 @@
+package com.min.dnapp.presentation.init
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.CenterAlignedTopAppBar
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+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.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavHostController
+import com.min.dnapp.R
+import com.min.dnapp.presentation.init.component.OnboardingButtonSection
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import com.min.dnapp.presentation.ui.icon.appicons.Back
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun OnboardingScreen3(
+ navController: NavHostController,
+ onFinish: () -> Unit
+) {
+ Scaffold(
+ containerColor = MomentoTheme.colors.brownW90,
+ topBar = {
+ CenterAlignedTopAppBar(
+ title = { Text("") },
+ navigationIcon = {
+ Icon(
+ modifier = Modifier
+ .clickable { navController.popBackStack() }
+ .padding(16.dp),
+ imageVector = AppIcons.Back,
+ contentDescription = null
+ )
+ },
+ colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
+ containerColor = MomentoTheme.colors.brownW90
+ )
+ )
+ }
+ ) { paddingValues ->
+ Column(
+ modifier = Modifier
+ .padding(paddingValues)
+ .fillMaxSize(),
+ verticalArrangement = Arrangement.SpaceBetween
+ ) {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Spacer(Modifier.height(40.dp))
+ Image(
+ painter = painterResource(R.drawable.logo_onboarding3),
+ contentDescription = null
+ )
+ }
+
+ OnboardingButtonSection(
+ onboardingNum = 3,
+ title = "솔직한 기록을 다른 사람과 가볍게 나누기 \n공감과 영감을 주고받는 공간입니다. \n",
+ onClick = { onFinish() }
+ )
+ }
+ }
+}
+
+@Preview
+@Composable
+fun OnboardingScreen3Preview() {
+ DngoTheme {
+// OnboardingScreen3()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/init/ProfileSetupScreen.kt b/app/src/main/java/com/min/dnapp/presentation/init/ProfileSetupScreen.kt
new file mode 100644
index 0000000..3796175
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/init/ProfileSetupScreen.kt
@@ -0,0 +1,144 @@
+package com.min.dnapp.presentation.init
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.CenterAlignedTopAppBar
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.min.dnapp.presentation.mypage.ProfileImageType
+import com.min.dnapp.presentation.mypage.component.ProfileImageItem
+import com.min.dnapp.presentation.ui.component.SelectButton
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun ProfileSetupScreen(
+ viewModel: AppInitViewModel = hiltViewModel(),
+ onFinish: () -> Unit
+) {
+ val selectedImage by viewModel.selectedImage.collectAsStateWithLifecycle()
+ val saveImageState by viewModel.saveImageState.collectAsStateWithLifecycle()
+
+ Scaffold(
+ containerColor = MomentoTheme.colors.brownBg,
+ topBar = {
+ CenterAlignedTopAppBar(
+ title = {
+ Text(
+ text = "프로필 설정",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW20
+ )
+ },
+ colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
+ containerColor = MomentoTheme.colors.brownBg
+ )
+ )
+ }
+ ) { paddingValues ->
+ Column(
+ modifier = Modifier
+ .padding(paddingValues)
+ .fillMaxSize(),
+ verticalArrangement = Arrangement.SpaceBetween
+ ) {
+ // 프로필 이미지 목록 (8개)
+ val allImages = ProfileImageType.entries.toList()
+
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ for (idx in 0 until allImages.size step 2) {
+
+ // 처음 Row가 아니면, Row 위에 간격 추가
+ if (idx > 0) {
+ Spacer(Modifier.height(16.dp))
+ }
+
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(20.dp)
+ ) {
+ ProfileImageItem(
+ profileImageType = allImages[idx],
+ isSelected = selectedImage == allImages[idx],
+ onClick = { image -> viewModel.selectImage(image) }
+ )
+ ProfileImageItem(
+ profileImageType = allImages[idx+1],
+ isSelected = selectedImage == allImages[idx+1],
+ onClick = { image -> viewModel.selectImage(image) }
+ )
+ }
+ }
+
+ Spacer(Modifier.height(20.dp))
+
+ Text(
+ text = "원하는 프로필을 선택해주세요!",
+ style = MomentoTheme.typography.title01,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+
+ // 확인 버튼
+ Column(
+ modifier = Modifier.padding(horizontal = 20.dp)
+ ) {
+ SelectButton(
+ enabled = selectedImage != null,
+ onConfirm = { viewModel.saveProfileImage() }
+ )
+ Spacer(Modifier.height(20.dp))
+ }
+ }
+ }
+
+ when (saveImageState) {
+ is SaveImageState.Loading -> {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ CircularProgressIndicator(
+ modifier = Modifier.size(40.dp),
+ color = MomentoTheme.colors.brownW20,
+ strokeWidth = 4.dp
+ )
+ }
+ }
+ is SaveImageState.Success -> { onFinish() }
+ is SaveImageState.Error -> {}
+ else -> {
+ // Init 또는 Success
+ }
+ }
+}
+
+@Preview
+@Composable
+fun ProfileSelectScreenPreview() {
+ DngoTheme {
+// ProfileSetupScreen()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/init/SaveImageState.kt b/app/src/main/java/com/min/dnapp/presentation/init/SaveImageState.kt
new file mode 100644
index 0000000..a8e4ff0
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/init/SaveImageState.kt
@@ -0,0 +1,8 @@
+package com.min.dnapp.presentation.init
+
+sealed class SaveImageState {
+ data object Init : SaveImageState()
+ data object Loading : SaveImageState()
+ data object Success : SaveImageState()
+ data class Error(val message: String) : SaveImageState()
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/init/component/OnboardingButtonSection.kt b/app/src/main/java/com/min/dnapp/presentation/init/component/OnboardingButtonSection.kt
new file mode 100644
index 0000000..03dc659
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/init/component/OnboardingButtonSection.kt
@@ -0,0 +1,99 @@
+package com.min.dnapp.presentation.init.component
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun OnboardingButtonSection(
+ onboardingNum: Int,
+ title: String,
+ onClick: () -> Unit
+) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(color = MomentoTheme.colors.white, shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp)),
+ ) {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Spacer(Modifier.height(40.dp))
+ Text(
+ text = title,
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.black,
+ textAlign = TextAlign.Center
+ )
+ Spacer(Modifier.height(20.dp))
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Row {
+ OnboardingIndicator(
+ isActive = onboardingNum == 1
+ )
+ Spacer(Modifier.width(4.dp))
+ OnboardingIndicator(
+ isActive = onboardingNum == 2
+ )
+ Spacer(Modifier.width(4.dp))
+ OnboardingIndicator(
+ isActive = onboardingNum == 3
+ )
+ }
+ Box(
+ modifier = Modifier
+ .clickable { onClick() }
+ .background(color = MomentoTheme.colors.pinkW80, shape = RoundedCornerShape(10.dp))
+ .padding(horizontal = 20.dp, vertical = 12.dp)
+ ) {
+ Text(
+ text = "Next",
+ style = MomentoTheme.typography.label,
+ color = MomentoTheme.colors.black,
+ textAlign = TextAlign.Center
+ )
+ }
+ }
+ Spacer(Modifier.height(20.dp))
+ }
+ }
+}
+
+@Composable
+private fun OnboardingIndicator(isActive: Boolean) {
+ val indicatorColor = if (isActive) MomentoTheme.colors.pinkBase else MomentoTheme.colors.pinkW40
+ val indicatorShape = if (isActive) RoundedCornerShape(20.dp) else CircleShape
+ val indicatorWidth = if (isActive) 16.dp else 8.dp
+ val indicatorHeight = 8.dp
+
+ Box(
+ modifier = Modifier
+ .width(indicatorWidth)
+ .height(indicatorHeight)
+ .background(color = indicatorColor, shape = indicatorShape)
+ )
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/login/LoginScreen.kt b/app/src/main/java/com/min/dnapp/presentation/login/LoginScreen.kt
new file mode 100644
index 0000000..68659c4
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/login/LoginScreen.kt
@@ -0,0 +1,79 @@
+package com.min.dnapp.presentation.login
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.SnackbarHost
+import androidx.compose.material3.SnackbarHostState
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.NavController
+
+@Composable
+fun LoginScreen(
+ loginViewModel: LoginViewModel = hiltViewModel(),
+ navController: NavController
+) {
+ // viewModel의 상태를 구독
+ val isLoading by loginViewModel.isLoading.collectAsStateWithLifecycle()
+ val context = LocalContext.current
+
+ // 스택바 상태 관리
+ val snackbarHostState = remember { SnackbarHostState() }
+
+ // 로그인 결과에 따른 처리
+// LaunchedEffect(loginResult) {
+// loginResult?.let { result ->
+// result.onSuccess {
+// navController.navigate("home") {
+// popUpTo("login") { inclusive = true }
+// }
+// loginViewModel.clearLoginResult()
+// }.onFailure { exception ->
+// val errorMessage = when {
+// exception.message?.contains("network", ignoreCase = true) == true ->
+// "네트워크 연결을 확인해주세요"
+// exception.message?.contains("cancelled", ignoreCase = true ) == true ->
+// "로그인이 취소되었습니다"
+// else -> "로그인에 실패했습니다"
+// }
+// snackbarHostState.showSnackbar(errorMessage)
+// }
+// }
+// }
+
+ Scaffold(
+ snackbarHost = { SnackbarHost(snackbarHostState) }
+ ) { paddingValues ->
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(paddingValues)
+ ) {
+ if (isLoading) {
+ CircularProgressIndicator()
+ Text(
+ text = "로그인 중..."
+ )
+ } else {
+ Button(
+ onClick = {
+// loginViewModel.onKakaoLoginClicked(context)
+ }
+ ) {
+ Text("카카오 로그인")
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/login/LoginScreen2.kt b/app/src/main/java/com/min/dnapp/presentation/login/LoginScreen2.kt
new file mode 100644
index 0000000..f12372f
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/login/LoginScreen2.kt
@@ -0,0 +1,183 @@
+package com.min.dnapp.presentation.login
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.systemBarsPadding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.NavHostController
+import com.min.dnapp.R
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import com.min.dnapp.presentation.ui.icon.appicons.Kakao
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.KakaoYellow
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun LoginScreen2(
+ navController: NavHostController,
+ viewModel: LoginViewModel = hiltViewModel(),
+ onLoginSuccess: () -> Unit
+) {
+ val context = LocalContext.current
+ val isLoading by viewModel.isLoading.collectAsStateWithLifecycle()
+
+ Surface(
+ modifier = Modifier
+ // 시스템바 영역 피하기 (status bar, navigation bar 모두 적용)
+ .systemBarsPadding()
+ .fillMaxSize(),
+ color = MomentoTheme.colors.brownW80
+ ) {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ verticalArrangement = Arrangement.SpaceBetween
+ ) {
+ LoginHeaderSection()
+ LoginButtonSection(
+ onClick = {
+ viewModel.onKakaoLoginClicked(
+ context = context,
+ onSuccess = { onLoginSuccess() },
+ onFailure = {
+
+ }
+ )
+ }
+ )
+ }
+ }
+
+ if (isLoading) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(MomentoTheme.colors.black.copy(alpha = 0.5f)),
+ contentAlignment = Alignment.Center
+ ) {
+ CircularProgressIndicator(
+ modifier = Modifier.size(40.dp),
+ color = MomentoTheme.colors.white,
+ strokeWidth = 4.dp
+ )
+ }
+ }
+}
+
+@Composable
+fun LoginHeaderSection() {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Spacer(Modifier.height(60.dp))
+
+ Box(
+ modifier = Modifier.fillMaxWidth(),
+ contentAlignment = Alignment.BottomCenter
+ ) {
+ Image(
+ painter = painterResource(R.drawable.login_cloud),
+ contentDescription = null,
+ contentScale = ContentScale.Crop
+ )
+ Image(
+ modifier = Modifier.offset(y = 28.dp),
+ painter = painterResource(R.drawable.login_momento),
+ contentDescription = null
+ )
+ }
+
+ Spacer(Modifier.height(48.dp))
+
+ Text(
+ text = "모멘토에 오신 것을 환영합니다!",
+ style = MomentoTheme.typography.title01,
+ color = MomentoTheme.colors.brownB60
+ )
+ }
+}
+
+@Composable
+fun LoginButtonSection(
+ onClick: () -> Unit
+) {
+ Box(
+ modifier = Modifier.fillMaxWidth(),
+ contentAlignment = Alignment.BottomCenter
+ ) {
+ Image(
+ modifier = Modifier.offset(y = 20.dp),
+ painter = painterResource(R.drawable.login_mount),
+ contentDescription = null,
+ contentScale = ContentScale.Crop
+ )
+
+ Column(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp)
+ .background(color = KakaoYellow, shape = RoundedCornerShape(10.dp))
+ ) {
+ Row(
+ modifier = Modifier
+ .clickable { onClick() }
+ .fillMaxWidth()
+ .padding(16.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = AppIcons.Kakao,
+ contentDescription = null
+ )
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ text = "카카오로 로그인하기",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW20,
+ textAlign = TextAlign.Center
+ )
+ }
+ }
+ Spacer(Modifier.height(60.dp))
+ }
+ }
+}
+
+@Preview
+@Composable
+fun LoginScreen2Preview() {
+ DngoTheme {
+// LoginScreen2()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/login/LoginViewModel.kt b/app/src/main/java/com/min/dnapp/presentation/login/LoginViewModel.kt
new file mode 100644
index 0000000..904e2ed
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/login/LoginViewModel.kt
@@ -0,0 +1,122 @@
+package com.min.dnapp.presentation.login
+
+import android.content.Context
+import android.util.Log
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.min.dnapp.domain.usecase.LogoutUseCase
+import com.min.dnapp.domain.usecase.AuthWithKakaoUseCase
+import com.min.dnapp.domain.usecase.UnlinkUseCase
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class LoginViewModel @Inject constructor(
+ private val authWithKakaoUseCase: AuthWithKakaoUseCase,
+ private val logoutUseCase: LogoutUseCase,
+ private val unlinkUseCase: UnlinkUseCase
+) : ViewModel() {
+
+ // 로딩 상태
+ private val _isLoading = MutableStateFlow(false)
+ val isLoading: StateFlow = _isLoading.asStateFlow()
+
+ // 회원탈퇴 완료 모달창 상태
+ private val _showUserRemoveSuccessDialog = MutableStateFlow(false)
+ val showUserRemoveSuccessDialog: StateFlow = _showUserRemoveSuccessDialog.asStateFlow()
+
+ /**
+ * 카카오 로그인
+ */
+ fun onKakaoLoginClicked(
+ context: Context,
+ onSuccess: () -> Unit,
+ onFailure: (Throwable) -> Unit
+ ) {
+ viewModelScope.launch {
+ _isLoading.value = true
+
+ try {
+ val result = authWithKakaoUseCase(context)
+ Log.d("auth", "onKakaoLoginClicked - result : $result")
+
+ result.onSuccess {
+ onSuccess()
+ }.onFailure { exception ->
+ onFailure(exception)
+ }
+ } catch (e: Exception) {
+ Log.e("auth", "onKakaoLoginClicked - unexpected error", e)
+ onFailure(e)
+ } finally {
+ _isLoading.value = false
+ }
+ }
+ }
+
+ /**
+ * 로그아웃 처리
+ * - 카카오 SDK 로그아웃
+ * - firebase auth 로그아웃
+ */
+ fun onLogoutClicked(
+ onSuccess: () -> Unit,
+ onFailure: (Throwable) -> Unit
+ ) {
+ viewModelScope.launch {
+ try {
+ val result = logoutUseCase()
+ Log.d("auth", "onLogoutClicked - result : $result")
+
+ result.onSuccess {
+ onSuccess()
+ }.onFailure { exception ->
+ onFailure(exception)
+ }
+ } catch (e: Exception) {
+ Log.e("auth", "onLogoutClicked - unexpected error", e)
+ onFailure(e)
+ }
+ }
+ }
+
+ /**
+ * 회원탈퇴 완료 모달창 열기
+ */
+ fun openDialog() {
+ _showUserRemoveSuccessDialog.value = true
+ }
+
+ /**
+ * 회원탈퇴 처리
+ * - firebase auth 사용자 삭제
+ * - firestore "users" 문서 삭제
+ * - 카카오 연결 끊기
+ */
+ fun onUnlinkClicked(
+// onSuccess: () -> Unit,
+// onFailure: (Throwable) -> Unit
+ ) {
+ viewModelScope.launch {
+ try {
+ val result = unlinkUseCase()
+ Log.d("auth", "onUnlinkClicked - result : $result")
+
+ result.onSuccess {
+// onSuccess()
+ openDialog()
+ }.onFailure { exception ->
+// onFailure(exception)
+ Log.e("auth", "회원탈퇴 처리 실패", exception)
+ }
+ } catch (e: Exception) {
+ Log.e("auth", "onUnlinkClicked - unexpected error", e)
+// onFailure(e)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/mypage/MyRecordScreen.kt b/app/src/main/java/com/min/dnapp/presentation/mypage/MyRecordScreen.kt
new file mode 100644
index 0000000..0fcb3a9
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/mypage/MyRecordScreen.kt
@@ -0,0 +1,293 @@
+package com.min.dnapp.presentation.mypage
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.material3.CenterAlignedTopAppBar
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.NavHostController
+import coil3.compose.AsyncImage
+import com.min.dnapp.R
+import com.min.dnapp.presentation.common.EmotionMapper
+import com.min.dnapp.presentation.common.WeatherMapper
+import com.min.dnapp.presentation.home.YearHeader
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import com.min.dnapp.presentation.ui.icon.appicons.Back
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+import com.min.dnapp.util.toDateString
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun MyRecordScreen(
+ navController: NavHostController,
+ myRecordViewModel: MyRecordViewModel = hiltViewModel()
+) {
+ val uiState by myRecordViewModel.uiState.collectAsStateWithLifecycle()
+
+ Scaffold(
+ containerColor = MomentoTheme.colors.brownBg,
+ topBar = {
+ CenterAlignedTopAppBar(
+ title = {
+ Text(
+ text = "내 기록 모아보기",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW20
+ )
+ },
+ colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
+ MomentoTheme.colors.brownBg
+ ),
+ navigationIcon = {
+ Icon(
+ modifier = Modifier
+ .clickable { navController.popBackStack() }
+ .padding(16.dp),
+ imageVector = AppIcons.Back,
+ contentDescription = null
+ )
+ }
+ )
+ }
+ ) { paddingValues ->
+
+ when (uiState) {
+ is MyRecordUiState.Loading -> {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ CircularProgressIndicator(
+ modifier = Modifier.size(40.dp),
+ color = MomentoTheme.colors.brownW20,
+ strokeWidth = 4.dp
+ )
+ }
+ }
+ is MyRecordUiState.Error -> {}
+ is MyRecordUiState.Success -> {
+ // Success 데이터 추출
+ val data = uiState as MyRecordUiState.Success
+
+ LazyColumn(
+ modifier = Modifier
+ .padding(paddingValues)
+ .padding(horizontal = 20.dp)
+ .fillMaxSize()
+ ) {
+ itemsIndexed(data.records) { idx, record ->
+
+ // 현재 항목의 년도
+ val currentYear = record.startDateMillis.toDateString("yyyy")
+
+ // 직전 항목의 년도와 비교
+ val previousYear = if (idx > 0) {
+ data.records[idx-1].startDateMillis.toDateString("yyyy")
+ } else {
+ null
+ }
+
+ // 년도별 첫번째 항목 여부 (1번째 항목인 경우 or 년도가 바뀌는 시점)
+ val isYearStart = (idx == 0) || (currentYear != previousYear)
+
+ // 년도 표시
+ if (isYearStart) {
+ Spacer(Modifier.height(20.dp))
+ YearHeader(year = currentYear)
+ Spacer(Modifier.height(12.dp))
+ }
+
+ // 날짜, 날씨+감정
+ MyRecordItemHeaderSection(
+ startDateMillis = record.startDateMillis,
+ endDateMillis = if (record.endDateMillis == 0L) null else record.endDateMillis,
+ weatherName = record.weatherKey,
+ emotionName = record.emotionKey,
+ )
+
+ Spacer(Modifier.height(8.dp))
+
+ // 내 기록 아이템
+ MyRecordItem(
+ title = record.title,
+ content = record.content,
+ placeName = record.selectedPlace?.title,
+ imageUrl = if (record.imageUrl.isEmpty()) null else record.imageUrl
+ )
+
+ Spacer(Modifier.height(20.dp))
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun MyRecordItemHeaderSection(
+ startDateMillis: Long,
+ endDateMillis: Long?,
+ weatherName: String,
+ emotionName: String,
+) {
+ val startDate = startDateMillis.toDateString("MM.dd")
+ val endDate = endDateMillis?.toDateString("MM.dd")
+
+ // 여행 날짜 텍스트 계산
+ val dateText = endDate?.let { "$startDate ~ $it" } ?: startDate
+
+ // 날씨 & 감정 이미지 리소스로 변환
+ val weatherImageResId = WeatherMapper.getWeatherImageResId(weatherName)
+ val emotionImageResId = EmotionMapper.getEmotionImageResId(emotionName)
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ // 날짜 (08.20 ~ 08.22)
+ Text(
+ text = dateText,
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ // 날씨 & 감정
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = "날씨",
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20
+ )
+ Spacer(Modifier.width(4.dp))
+ Image(
+ modifier = Modifier.size(28.dp),
+ painter = painterResource(weatherImageResId),
+ contentDescription = null
+ )
+ }
+
+ Spacer(Modifier.width(8.dp))
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = "감정",
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20
+ )
+ Spacer(Modifier.width(4.dp))
+ Image(
+ modifier = Modifier.size(28.dp),
+ painter = painterResource(emotionImageResId),
+ contentDescription = null
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun MyRecordItem(
+ title: String,
+ content: String,
+ placeName: String?,
+ imageUrl: String?
+) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(color = MomentoTheme.colors.brownW90)
+ .padding(16.dp)
+ ) {
+ // 여행 제목
+ Text(
+ text = title,
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ Spacer(Modifier.height(8.dp))
+
+ HorizontalDivider(thickness = 1.dp, color = MomentoTheme.colors.pinkBase)
+
+ Spacer(Modifier.height(8.dp))
+
+ // 여행 장소
+ placeName?.let { place ->
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Image(
+ painter = painterResource(R.drawable.write_place),
+ contentDescription = null
+ )
+ Spacer(Modifier.width(6.dp))
+ Text(
+ text = place,
+ style = MomentoTheme.typography.body03 ,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+ }
+
+ Spacer(Modifier.height(8.dp))
+
+ // 여행 내용
+ Text(
+ text = content,
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ // 여행 이미지
+ imageUrl?.let { image ->
+ Spacer(Modifier.height(8.dp))
+
+ AsyncImage(
+ modifier = Modifier
+ .fillMaxWidth()
+ // 가로:세로 비율 1:1
+ .aspectRatio(1f),
+ model = image,
+ contentDescription = null,
+ contentScale = ContentScale.Crop
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/mypage/MyRecordUiState.kt b/app/src/main/java/com/min/dnapp/presentation/mypage/MyRecordUiState.kt
new file mode 100644
index 0000000..4be113b
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/mypage/MyRecordUiState.kt
@@ -0,0 +1,11 @@
+package com.min.dnapp.presentation.mypage
+
+import com.min.dnapp.domain.model.TripRecord
+
+sealed class MyRecordUiState {
+ data object Loading: MyRecordUiState()
+ data class Success(
+ val records: List = emptyList()
+ ) : MyRecordUiState()
+ data class Error(val message: String): MyRecordUiState()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/min/dnapp/presentation/mypage/MyRecordViewModel.kt b/app/src/main/java/com/min/dnapp/presentation/mypage/MyRecordViewModel.kt
new file mode 100644
index 0000000..a95a207
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/mypage/MyRecordViewModel.kt
@@ -0,0 +1,47 @@
+package com.min.dnapp.presentation.mypage
+
+import android.util.Log
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.min.dnapp.domain.usecase.GetUserRecordUseCase
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class MyRecordViewModel @Inject constructor(
+ private val getUserRecordUseCase: GetUserRecordUseCase
+) : ViewModel() {
+
+ private val _uiState = MutableStateFlow(MyRecordUiState.Loading)
+ val uiState: StateFlow = _uiState.asStateFlow()
+
+ init {
+ loadMyRecordData()
+ }
+
+ private fun loadMyRecordData() {
+ viewModelScope.launch {
+ // 로딩 시작
+ _uiState.value = MyRecordUiState.Loading
+
+ // 내 여행기록 목록 가져오기
+ try {
+ val myRecords = getUserRecordUseCase()
+ Log.d("my", "loadMyRecordData - myRecords: $myRecords")
+ val successState = MyRecordUiState.Success(
+ records = myRecords
+ )
+ _uiState.value = successState
+ } catch (e: Exception) {
+ _uiState.value = MyRecordUiState.Success(
+ records = emptyList()
+ )
+ Log.e("my", "내 여행기록 조회 실패", e)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/mypage/MypageScreen.kt b/app/src/main/java/com/min/dnapp/presentation/mypage/MypageScreen.kt
new file mode 100644
index 0000000..41171cf
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/mypage/MypageScreen.kt
@@ -0,0 +1,455 @@
+package com.min.dnapp.presentation.mypage
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.CenterAlignedTopAppBar
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.NavHostController
+import com.min.dnapp.R
+import com.min.dnapp.presentation.common.ProfileMapper
+import com.min.dnapp.presentation.mypage.component.NicknameUpdateDialog
+import com.min.dnapp.presentation.mypage.component.ProfileImageDialog
+import com.min.dnapp.presentation.ui.component.UserBadge
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import com.min.dnapp.presentation.ui.icon.appicons.Bell
+import com.min.dnapp.presentation.ui.icon.appicons.PenSmall
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun MypageScreen(
+ navController: NavHostController,
+ mypageViewModel: MypageViewModel = hiltViewModel()
+) {
+ val uiState by mypageViewModel.uiState.collectAsStateWithLifecycle()
+ val showImageUpdateDialog by mypageViewModel.showImageUpdateDialog.collectAsStateWithLifecycle()
+ val selectedImage by mypageViewModel.selectedImage.collectAsStateWithLifecycle()
+ val nicknameState by mypageViewModel.nicknameState.collectAsStateWithLifecycle()
+
+ var showNicknameUpdateDialog by remember { mutableStateOf(false) }
+
+ Scaffold(
+ containerColor = MomentoTheme.colors.brownW90,
+ topBar = {
+ CenterAlignedTopAppBar(
+ title = { Text("") },
+ colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
+ containerColor = MomentoTheme.colors.brownW90
+ ),
+ navigationIcon = {
+ Image(
+ modifier = Modifier
+// .clickable { }
+// .padding(16.dp),
+ .padding(start = 16.dp),
+ painter = painterResource(R.drawable.logo_momento),
+ contentDescription = null
+ )
+ },
+ actions = {
+ Icon(
+ modifier = Modifier
+ .clickable { navController.navigate("bell") }
+ .padding(16.dp),
+ imageVector = AppIcons.Bell,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW60
+ )
+ }
+ )
+ }
+ ) { paddingValues ->
+
+ when (uiState) {
+ is MypageUiState.Loading -> {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ CircularProgressIndicator(
+ modifier = Modifier.size(40.dp),
+ color = MomentoTheme.colors.brownW20,
+ strokeWidth = 4.dp
+ )
+ }
+ }
+ is MypageUiState.Error -> {}
+ is MypageUiState.Success -> {
+ // Success 데이터 추출
+ val data = uiState as MypageUiState.Success
+
+ Column(
+ modifier = Modifier
+ .padding(paddingValues)
+ .fillMaxSize()
+ ) {
+ Spacer(Modifier.height(20.dp))
+
+ MypageProfileSection(
+ profileImageName = data.user.profileImageName,
+ badgeLv = data.user.badgeLv,
+ badgeName = data.user.badgeName,
+ nickname = data.user.nickname,
+ recordCnt = data.user.recordCnt,
+ stampCnt = data.user.stampCnt,
+ onImageClick = { mypageViewModel.openDialog() },
+ onNicknameClick = { showNicknameUpdateDialog = true }
+ )
+
+ Spacer(Modifier.height(20.dp))
+
+ HorizontalDivider(thickness = 1.dp, color = MomentoTheme.colors.brownW60)
+
+ Spacer(Modifier.height(20.dp))
+
+ MypageMenuSection(
+ onRecordClick = {
+ navController.navigate("my_record")
+ },
+ onSettingClick = {
+ navController.navigate("setting")
+ }
+ )
+ }
+ }
+ }
+ }
+
+ // 프로필 이미지 변경 모달창
+ if (showImageUpdateDialog) {
+ ProfileImageDialog(
+ selectedImage = selectedImage,
+ onImageClick = { profileImageType ->
+ mypageViewModel.selectImage(profileImageType)
+ },
+ onDismiss = { mypageViewModel.closeDialog() },
+ onConfirm = { mypageViewModel.updateProfileImage() }
+ )
+ }
+
+ // 닉네임 변경 모달창
+ if (showNicknameUpdateDialog) {
+ NicknameUpdateDialog(
+ state = nicknameState,
+ onValueChange = { newValue ->
+ mypageViewModel.onNicknameChange(newValue)
+ },
+ onDismiss = { showNicknameUpdateDialog = false },
+ onCancel = { showNicknameUpdateDialog = false },
+ onConfirm = {
+ mypageViewModel.updateNickname(
+ onSuccess = { showNicknameUpdateDialog = false }
+ )
+ }
+ )
+ }
+}
+
+@Composable
+fun MypageProfileSection(
+ profileImageName: String,
+ badgeLv: Int,
+ badgeName: String,
+ nickname: String,
+ recordCnt: Int,
+ stampCnt: Int,
+ onImageClick: () -> Unit,
+ onNicknameClick: () -> Unit
+) {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ // 프로필 이미지 (수정 가능)
+ UserProfileImage(
+ profileImageName = profileImageName,
+ onClick = { onImageClick() }
+ )
+
+ Spacer(Modifier.height(12.dp))
+
+ // 뱃지
+ UserBadge(
+ badgeLv = badgeLv,
+ badgeName = badgeName
+ )
+
+ Spacer(Modifier.height(16.dp))
+
+ // 닉네임
+ UserNickname(
+ nickname = nickname,
+ onClick = { onNicknameClick() }
+ )
+
+ Spacer(Modifier.height(28.dp))
+
+ // 기록/스탬프 개수
+ RecordAndStampNum(
+ recordCnt = recordCnt,
+ stampCnt = stampCnt
+ )
+ }
+}
+
+@Composable
+fun UserProfileImage(
+ profileImageName: String,
+ onClick: () -> Unit
+) {
+ val profileImageResId = ProfileMapper.getProfileImageResId(profileImageName)
+
+ Box(
+ modifier = Modifier
+ .clickable { onClick() }
+ .size(120.dp)
+ ) {
+ Box(
+ modifier = Modifier
+ .size(120.dp)
+ .border(width = 2.dp, color = MomentoTheme.colors.grayW80, shape = RoundedCornerShape(10.dp))
+ ) {
+ Image(
+ painter = painterResource(profileImageResId),
+ contentDescription = null
+ )
+ }
+
+ Box(
+ modifier = Modifier
+ .align(Alignment.BottomEnd)
+ .offset(x = 12.dp, y = 12.dp)
+ .size(28.dp)
+ .background(color = MomentoTheme.colors.grayW90, shape = CircleShape),
+ contentAlignment = Alignment.Center
+ ) {
+ Icon(
+ imageVector = AppIcons.PenSmall,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW60
+ )
+ }
+ }
+}
+
+@Composable
+fun UserNickname(
+ nickname: String,
+ onClick: () -> Unit
+) {
+ Column(
+ modifier = Modifier
+ .clickable { onClick() }
+ .width(120.dp)
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Spacer(Modifier.width(16.dp))
+ Text(
+ text = nickname,
+ style = MomentoTheme.typography.title01,
+ color = MomentoTheme.colors.grayW20
+ )
+ Icon(
+ modifier = Modifier.size(16.dp),
+ imageVector = AppIcons.PenSmall,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW60
+ )
+ }
+
+ Spacer(Modifier.height(4.dp))
+
+ HorizontalDivider(thickness = 1.dp, color = MomentoTheme.colors.grayW60)
+ }
+}
+
+@Composable
+fun RecordAndStampNum(
+ recordCnt: Int,
+ stampCnt: Int
+) {
+ Row(
+ modifier = Modifier
+ .padding(horizontal = 20.dp)
+ .fillMaxWidth()
+ ) {
+ // 여행 기록 영역
+ Box(
+ modifier = Modifier
+ .weight(1f)
+ .background(color = MomentoTheme.colors.pinkW60, shape = RoundedCornerShape(10.dp))
+ ) {
+ Column(
+ modifier = Modifier
+ .padding(horizontal = 16.dp)
+ .fillMaxWidth()
+ ) {
+ Spacer(Modifier.height(12.dp))
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Image(
+ painter = painterResource(R.drawable.my_record),
+ contentDescription = null
+ )
+ Spacer(Modifier.width(8.dp))
+ Text(
+ text = "여행 기록",
+ style = MomentoTheme.typography.label,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+
+ Spacer(Modifier.height(12.dp))
+
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ text = "${recordCnt}개",
+ style = MomentoTheme.typography.label,
+ color = MomentoTheme.colors.grayW20,
+ textAlign = TextAlign.End
+ )
+
+ Spacer(Modifier.height(12.dp))
+ }
+ }
+
+ Spacer(Modifier.width(16.dp))
+
+ // 수집 스탬프 영역
+ Box(
+ modifier = Modifier
+ .weight(1f)
+ .background(color = MomentoTheme.colors.pinkW60, shape = RoundedCornerShape(10.dp))
+ ) {
+ Column(
+ modifier = Modifier
+ .padding(horizontal = 16.dp)
+ .fillMaxWidth()
+ ) {
+ Spacer(Modifier.height(12.dp))
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Image(
+ painter = painterResource(R.drawable.my_stamp),
+ contentDescription = null
+ )
+ Spacer(Modifier.width(8.dp))
+ Text(
+ text = "수집 스탬프",
+ style = MomentoTheme.typography.label,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+
+ Spacer(Modifier.height(12.dp))
+
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ text = "${stampCnt}개",
+ style = MomentoTheme.typography.label,
+ color = MomentoTheme.colors.grayW20,
+ textAlign = TextAlign.End
+ )
+
+ Spacer(Modifier.height(12.dp))
+ }
+ }
+ }
+}
+
+@Composable
+fun MypageMenuSection(
+ onRecordClick: () -> Unit,
+ onSettingClick: () -> Unit
+) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp)
+ ) {
+ MypageMenuItem(
+ text = "내 기록 모아보기",
+ onClick = { onRecordClick() }
+ )
+ Spacer(Modifier.height(28.dp))
+ MypageMenuItem(
+ text = "북마크",
+ onClick = {}
+ )
+ Spacer(Modifier.height(28.dp))
+ MypageMenuItem(
+ text = "설정",
+ onClick = { onSettingClick() }
+ )
+ }
+}
+
+@Composable
+fun MypageMenuItem(
+ text: String,
+ onClick: () -> Unit
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable { onClick() }
+ ) {
+ Text(
+ text = text,
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+}
+
+@Preview
+@Composable
+fun MypageScreenPreview() {
+ DngoTheme {
+// MypageScreen()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/mypage/MypageUiState.kt b/app/src/main/java/com/min/dnapp/presentation/mypage/MypageUiState.kt
new file mode 100644
index 0000000..c7740a2
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/mypage/MypageUiState.kt
@@ -0,0 +1,11 @@
+package com.min.dnapp.presentation.mypage
+
+import com.min.dnapp.domain.model.User
+
+sealed class MypageUiState {
+ data object Loading : MypageUiState()
+ data class Success(
+ val user: User
+ ) : MypageUiState()
+ data class Error(val message: String) : MypageUiState()
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/mypage/MypageViewModel.kt b/app/src/main/java/com/min/dnapp/presentation/mypage/MypageViewModel.kt
new file mode 100644
index 0000000..be60c9a
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/mypage/MypageViewModel.kt
@@ -0,0 +1,193 @@
+package com.min.dnapp.presentation.mypage
+
+import android.util.Log
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.min.dnapp.domain.usecase.GetCurrentUserIdUseCase
+import com.min.dnapp.domain.usecase.GetUserDataUseCase
+import com.min.dnapp.domain.usecase.UpdateNicknameUseCase
+import com.min.dnapp.domain.usecase.UpdateProfileImageUseCase
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class MypageViewModel @Inject constructor(
+ private val getCurrentUserIdUseCase: GetCurrentUserIdUseCase,
+ private val getUserDataUseCase: GetUserDataUseCase,
+ private val updateProfileImageUseCase: UpdateProfileImageUseCase,
+ private val saveNicknameUseCase: UpdateNicknameUseCase
+) : ViewModel() {
+
+ private val _uiState = MutableStateFlow(MypageUiState.Loading)
+ val uiState: StateFlow = _uiState.asStateFlow()
+
+ // 프로필 이미지 변경 모달창 상태
+ private val _showImageUpdateDialog = MutableStateFlow(false)
+ val showImageUpdateDialog: StateFlow = _showImageUpdateDialog.asStateFlow()
+
+ // 현재 선택된 프로필 이미지
+ private val _selectedImage = MutableStateFlow(null)
+ val selectedImage: StateFlow = _selectedImage.asStateFlow()
+
+ private val _nicknameState = MutableStateFlow(NicknameValidationState())
+ val nicknameState: StateFlow = _nicknameState
+
+ private val MIN_LENGTH = 2
+ private val MAX_LENGTH = 10
+
+ // 한글, 영문, 숫자, .(마침표), _(언더만)만 허용
+ private val ALLOWED_CHARS_REGEX = Regex("^[가-힣a-zA-Z0-9._]*$")
+
+ init {
+ loadMyData()
+ }
+
+ private fun loadMyData() {
+ viewModelScope.launch {
+ // 로딩 시작
+ _uiState.value = MypageUiState.Loading
+
+ // 사용자 ID 가져오기
+ val uid = try {
+ getCurrentUserIdUseCase() ?: throw Exception("사용자 인증 정보 없음")
+ } catch (e: Exception) {
+ _uiState.value = MypageUiState.Error("인증 정보 조회 실패: ${e.message}")
+ return@launch
+ }
+
+ // 사용자 정보 가져오기
+ try {
+ val user = getUserDataUseCase(uid)
+ Log.d("my", "loadMyData - user: $user")
+ val successState = MypageUiState.Success(
+ user = user
+ )
+ _uiState.value = successState
+ } catch (e: Exception) {
+ Log.e("my", "사용자 정보 조회 실패", e)
+ }
+ }
+ }
+
+ /**
+ * 프로필 이미지 선택
+ */
+ fun selectImage(image: ProfileImageType) {
+ // 선택된 이미지 다시 선택하면 해제
+ _selectedImage.value = if (_selectedImage.value == image) null else image
+ }
+
+ /**
+ * 프로필 이미지 변경 모달창 열기
+ */
+ fun openDialog() {
+ _showImageUpdateDialog.value = true
+ }
+
+ /**
+ * 프로필 이미지 변경 모달창 닫기
+ */
+ fun closeDialog() {
+ _showImageUpdateDialog.value = false
+ }
+
+ /**
+ * 프로필 이미지 변경
+ */
+ fun updateProfileImage() {
+ viewModelScope.launch {
+ selectedImage.value?.let { image ->
+ val result = updateProfileImageUseCase(image.key)
+
+ result.onSuccess {
+ closeDialog()
+ loadMyData()
+ Log.d("my", "updateProfileImage 성공")
+ }.onFailure { exception ->
+ Log.e("my", "updateProfileImage 실패", exception)
+ }
+ }
+ }
+ }
+
+ /**
+ * 사용자의 입력에 따라 닉네임 검사 및 업데이트
+ */
+ fun onNicknameChange(newInput: String) {
+
+ // 10자 초과 입력 차단
+ val finalInput = if (newInput.length > MAX_LENGTH) {
+ newInput.substring(0, MAX_LENGTH)
+ } else {
+ newInput
+ }
+ Log.d("my", "onNicknameChange finalInput: $finalInput")
+
+ val (isValid, errorMessage) = validateNickname(finalInput)
+
+ _nicknameState.update {
+ it.copy(
+ currentNickname = finalInput,
+ errorMessage = errorMessage,
+ isValid = isValid
+ )
+ }
+ }
+
+ private fun validateNickname(nickname: String): Pair {
+
+ // 빈 문자열인 경우
+ if (nickname.isEmpty()) {
+ return Pair(false, null)
+ }
+
+ // 문자 제한 검사
+ if (!nickname.matches(ALLOWED_CHARS_REGEX)) {
+ return Pair(false, "한글, 영문, 숫자, .(마침표), _(언더바)만 가능합니다")
+ }
+
+ // 글자수 제한 검사
+ if (nickname.length < MIN_LENGTH) {
+ return Pair(false, "2~10자로 입력해주세요")
+ }
+
+ // 모든 조건 통과
+ return Pair(true, null)
+ }
+
+ /**
+ * 새로운 닉네임 저장
+ */
+ fun updateNickname(onSuccess: () -> Unit) {
+ viewModelScope.launch {
+ val nicknameState = _nicknameState.value
+
+ if (!nicknameState.isValid || nicknameState.isSaving) {
+ return@launch
+ }
+
+ // 저장 시작
+ _nicknameState.update { it.copy(isSaving = true) }
+
+ val newNickname = nicknameState.currentNickname
+ val result = saveNicknameUseCase(newNickname)
+
+ result.onSuccess {
+ // 프로필 데이터 갱신
+ loadMyData()
+ // Dialog 닫기 신호 전달
+ onSuccess()
+ Log.d("my", "updateNickname 성공")
+ }.onFailure { exception ->
+ Log.e("my", "updateNickname 실패", exception)
+ }
+
+ _nicknameState.update { it.copy(isSaving = false) }
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/mypage/NicknameValidationState.kt b/app/src/main/java/com/min/dnapp/presentation/mypage/NicknameValidationState.kt
new file mode 100644
index 0000000..27cf7f9
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/mypage/NicknameValidationState.kt
@@ -0,0 +1,8 @@
+package com.min.dnapp.presentation.mypage
+
+data class NicknameValidationState(
+ val currentNickname: String = "",
+ val errorMessage: String? = null,
+ val isValid: Boolean = false,
+ val isSaving: Boolean = false
+)
diff --git a/app/src/main/java/com/min/dnapp/presentation/mypage/ProfileImageType.kt b/app/src/main/java/com/min/dnapp/presentation/mypage/ProfileImageType.kt
new file mode 100644
index 0000000..239cef3
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/mypage/ProfileImageType.kt
@@ -0,0 +1,18 @@
+package com.min.dnapp.presentation.mypage
+
+import androidx.annotation.DrawableRes
+import com.min.dnapp.R
+
+enum class ProfileImageType(
+ val key: String,
+ @DrawableRes val resId: Int
+) {
+ BOAT("01_boat", R.drawable.logo_profile),
+ TENT("02_tent", R.drawable.logo_profile2),
+ SEA("03_sea", R.drawable.logo_profile3),
+ BAG("04_bag", R.drawable.logo_profile4),
+ PLANE("05_plane", R.drawable.logo_profile5),
+ TELESCOPE("06_telescope", R.drawable.logo_profile6),
+ MAP("07_map", R.drawable.logo_profile7),
+ MOUNT("08_mount", R.drawable.logo_profile8)
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/mypage/SettingScreen.kt b/app/src/main/java/com/min/dnapp/presentation/mypage/SettingScreen.kt
new file mode 100644
index 0000000..1046a53
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/mypage/SettingScreen.kt
@@ -0,0 +1,154 @@
+package com.min.dnapp.presentation.mypage
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.CenterAlignedTopAppBar
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.NavHostController
+import com.min.dnapp.presentation.login.LoginViewModel
+import com.min.dnapp.presentation.mypage.component.UserRemoveDialog
+import com.min.dnapp.presentation.mypage.component.UserRemoveSuccessDialog
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import com.min.dnapp.presentation.ui.icon.appicons.Back
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun SettingScreen(
+ navController: NavHostController,
+ loginViewModel: LoginViewModel = hiltViewModel()
+) {
+ val showUserRemoveSuccessDialog by loginViewModel.showUserRemoveSuccessDialog.collectAsStateWithLifecycle()
+
+ var showUserRemoveDialog by remember { mutableStateOf(false) }
+
+ Scaffold(
+ containerColor = MomentoTheme.colors.brownBg,
+ topBar = {
+ CenterAlignedTopAppBar(
+ title = {
+ Text(
+ text = "설정",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW20
+ )
+ },
+ colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
+ containerColor = MomentoTheme.colors.brownBg
+ ),
+ navigationIcon = {
+ Icon(
+ modifier = Modifier
+ .clickable { navController.popBackStack() }
+ .padding(16.dp),
+ imageVector = AppIcons.Back,
+ contentDescription = null
+ )
+ }
+ )
+ }
+ ) { paddingValues ->
+ Column(
+ modifier = Modifier
+ .padding(paddingValues)
+ .padding(horizontal = 20.dp)
+ .fillMaxSize()
+ ) {
+ Spacer(Modifier.height(20.dp))
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable {
+ loginViewModel.onLogoutClicked(
+ onSuccess = {
+ // 로그아웃 성공 후 이동
+ navController.navigate("login") {
+ // 최상위 그래프의 ID까지 스택 모두 제거
+ popUpTo(navController.graph.id) { inclusive = true }
+ }
+ },
+ onFailure = {}
+ )
+ }
+ ) {
+ Text(
+ text = "로그아웃",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+
+ Spacer(Modifier.height(28.dp))
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable { showUserRemoveDialog = true }
+ ) {
+ Text(
+ text = "탈퇴하기",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+ }
+ }
+
+ // 회원탈퇴 확인 모달창
+ if (showUserRemoveDialog) {
+ UserRemoveDialog(
+ onDismiss = { showUserRemoveDialog = false },
+ onCancel = { showUserRemoveDialog = false },
+ onConfirm = {
+ showUserRemoveDialog = false
+ loginViewModel.onUnlinkClicked()
+ }
+ )
+ }
+
+ // 회원탈퇴 완료 모달창
+ if (showUserRemoveSuccessDialog) {
+ UserRemoveSuccessDialog(
+ onDismiss = {
+ navController.navigate("login") {
+ popUpTo(navController.graph.id) { inclusive = true }
+ }
+ },
+ onConfirm = {
+ navController.navigate("login") {
+ popUpTo(navController.graph.id) { inclusive = true }
+ }
+ }
+ )
+ }
+}
+
+@Preview
+@Composable
+fun SettingScreenPreview() {
+ DngoTheme {
+// SettingScreen()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/mypage/component/NicknameUpdateDialog.kt b/app/src/main/java/com/min/dnapp/presentation/mypage/component/NicknameUpdateDialog.kt
new file mode 100644
index 0000000..0ef9266
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/mypage/component/NicknameUpdateDialog.kt
@@ -0,0 +1,155 @@
+package com.min.dnapp.presentation.mypage.component
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import com.min.dnapp.presentation.mypage.NicknameValidationState
+import com.min.dnapp.presentation.ui.theme.ErrorRed
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun NicknameUpdateDialog(
+ state: NicknameValidationState,
+ onValueChange: (String) -> Unit,
+ onDismiss: () -> Unit,
+ onCancel: () -> Unit,
+ onConfirm: () -> Unit
+) {
+ Dialog(
+ onDismissRequest = onDismiss
+ ) {
+ Surface(
+ modifier = Modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(16.dp),
+ color = MomentoTheme.colors.brownBg
+ ) {
+ Box(
+ contentAlignment = Alignment.Center
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(20.dp),
+ ) {
+ Text(
+ text = "닉네임",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ Spacer(Modifier.height(12.dp))
+
+ // 닉네임 입력 영역
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(40.dp)
+ .border(width = 1.dp, color = MomentoTheme.colors.grayW90, shape = RoundedCornerShape(5.dp))
+ .background(color = MomentoTheme.colors.white)
+ .padding(start = 12.dp),
+ contentAlignment = Alignment.CenterStart
+ ) {
+ if (state.currentNickname.isEmpty()) {
+ Text(
+ text = "새 닉네임을 입력하세요.",
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW60
+ )
+ }
+ BasicTextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = state.currentNickname,
+ onValueChange = { newValue ->
+ onValueChange(newValue)
+ },
+ textStyle = MomentoTheme.typography.body02,
+ singleLine = true
+ )
+ }
+
+ // 에러메시지 표시
+ if (state.errorMessage != null) {
+ Spacer(Modifier.height(4.dp))
+ Text(
+ text = state.errorMessage,
+ style = MomentoTheme.typography.body03,
+ color = ErrorRed
+ )
+ }
+
+ Spacer(Modifier.height(20.dp))
+
+ // 버튼 영역
+ Row(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Box(
+ modifier = Modifier
+ .clickable { onCancel() }
+ .weight(1f)
+ .background(color = MomentoTheme.colors.white)
+ .border(width = 1.dp, color = MomentoTheme.colors.grayW60, shape = RoundedCornerShape(5.dp))
+ .padding(vertical = 16.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = "취소",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+
+ Spacer(Modifier.width(12.dp))
+
+ Box(
+ modifier = Modifier
+ .clickable {
+ if (state.isValid) { onConfirm() }
+ }
+ .weight(1f)
+ .background(
+ color = if (state.isValid) MomentoTheme.colors.pinkBase else MomentoTheme.colors.grayW80,
+ shape = RoundedCornerShape(5.dp)
+ )
+ .padding(vertical = 16.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = "저장",
+ style = MomentoTheme.typography.body01,
+ color = if (state.isValid) MomentoTheme.colors.grayW20 else MomentoTheme.colors.white
+ )
+ }
+ }
+ }
+
+ if (state.isSaving) {
+ CircularProgressIndicator(
+ modifier = Modifier.size(40.dp),
+ color = MomentoTheme.colors.brownW20,
+ strokeWidth = 4.dp
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/mypage/component/ProfileImageDialog.kt b/app/src/main/java/com/min/dnapp/presentation/mypage/component/ProfileImageDialog.kt
new file mode 100644
index 0000000..47a9b24
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/mypage/component/ProfileImageDialog.kt
@@ -0,0 +1,113 @@
+package com.min.dnapp.presentation.mypage.component
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import com.min.dnapp.presentation.mypage.ProfileImageType
+import com.min.dnapp.presentation.ui.component.SelectButton
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun ProfileImageDialog(
+ selectedImage: ProfileImageType?,
+ onImageClick: (ProfileImageType) -> Unit,
+ onDismiss: () -> Unit,
+ onConfirm: () -> Unit
+) {
+ // 프로필 이미지 목록 (8개)
+ val allImages = ProfileImageType.entries.toList()
+
+ Dialog(
+ onDismissRequest = onDismiss
+ ) {
+ Surface(
+ modifier = Modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(16.dp),
+ color = MomentoTheme.colors.white
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp, vertical = 20.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text(
+ text = "프로필을 선택해주세요!",
+ style = MomentoTheme.typography.title01,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ Spacer(Modifier.height(20.dp))
+
+ for (idx in 0 until allImages.size step 2) {
+
+ // 처음 Row가 아니면, Row 위에 간격 추가
+ if (idx > 0) {
+ Spacer(Modifier.height(16.dp))
+ }
+
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(20.dp)
+ ) {
+ ProfileImageItem(
+ profileImageType = allImages[idx],
+ isSelected = selectedImage == allImages[idx],
+ onClick = onImageClick
+ )
+ ProfileImageItem(
+ profileImageType = allImages[idx+1],
+ isSelected = selectedImage == allImages[idx+1],
+ onClick = onImageClick
+ )
+ }
+ }
+
+ Spacer(Modifier.height(24.dp))
+
+ // 확인 버튼
+ SelectButton(
+ enabled = selectedImage != null,
+ onConfirm = onConfirm
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun ProfileImageItem(
+ profileImageType: ProfileImageType,
+ isSelected: Boolean,
+ onClick: (ProfileImageType) -> Unit
+) {
+ val borderColor = if (isSelected) MomentoTheme.colors.pinkBase else Color.Transparent
+
+ Box(
+ modifier = Modifier
+ .clickable { onClick(profileImageType) }
+ .border(width = 3.dp, color = borderColor, shape = RoundedCornerShape(10.dp))
+ ) {
+ Image(
+ painter = painterResource(profileImageType.resId),
+ contentDescription = null
+ )
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/mypage/component/UserRemoveDialog.kt b/app/src/main/java/com/min/dnapp/presentation/mypage/component/UserRemoveDialog.kt
new file mode 100644
index 0000000..f6f24b7
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/mypage/component/UserRemoveDialog.kt
@@ -0,0 +1,112 @@
+package com.min.dnapp.presentation.mypage.component
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun UserRemoveDialog(
+ onDismiss: () -> Unit,
+ onCancel: () -> Unit,
+ onConfirm: () -> Unit
+) {
+ Dialog(
+ onDismissRequest = onDismiss
+ ) {
+ Surface(
+ modifier = Modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(16.dp),
+ color = MomentoTheme.colors.brownBg
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(20.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text(
+ text = "정말 탈퇴하시겠어요?",
+ style = MomentoTheme.typography.title01,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ Spacer(Modifier.height(8.dp))
+
+ Text(
+ text = "회원탈퇴 후 계정 복구가 불가능합니다. \n탈퇴하시겠습니까?",
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20,
+ textAlign = TextAlign.Center,
+ )
+
+ Spacer(Modifier.height(20.dp))
+
+ // 버튼 영역
+ Row(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Box(
+ modifier = Modifier
+ .clickable { onCancel() }
+ .weight(1f)
+ .background(color = MomentoTheme.colors.white)
+ .border(width = 1.dp, color = MomentoTheme.colors.grayW60, shape = RoundedCornerShape(5.dp))
+ .padding(vertical = 16.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = "취소",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+
+ Spacer(Modifier.width(12.dp))
+
+ Box(
+ modifier = Modifier
+ .clickable { onConfirm() }
+ .weight(1f)
+ .background(color = MomentoTheme.colors.brownBase, shape = RoundedCornerShape(5.dp))
+ .padding(vertical = 16.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = "탈퇴",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.white
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+fun UserRemoveDialogPreview() {
+ DngoTheme {
+// UserRemoveDialog()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/mypage/component/UserRemoveSuccessDialog.kt b/app/src/main/java/com/min/dnapp/presentation/mypage/component/UserRemoveSuccessDialog.kt
new file mode 100644
index 0000000..3e35271
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/mypage/component/UserRemoveSuccessDialog.kt
@@ -0,0 +1,85 @@
+package com.min.dnapp.presentation.mypage.component
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+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.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun UserRemoveSuccessDialog(
+ onDismiss: () -> Unit,
+ onConfirm: () -> Unit
+) {
+ Dialog(
+ onDismissRequest = onDismiss
+ ) {
+ Surface(
+ modifier = Modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(16.dp),
+ color = MomentoTheme.colors.brownBg
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(20.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text(
+ text = "탈퇴가 완료되었습니다.",
+ style = MomentoTheme.typography.title01,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ Spacer(Modifier.height(8.dp))
+
+ Text(
+ text = "그동안 모멘토를 이용해주셔서 감사합니다.",
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20,
+ textAlign = TextAlign.Center,
+ )
+
+ Spacer(Modifier.height(20.dp))
+
+ Box(
+ modifier = Modifier
+ .clickable { onConfirm() }
+ .fillMaxWidth()
+ .background(color = MomentoTheme.colors.brownBase, shape = RoundedCornerShape(5.dp))
+ .padding(vertical = 16.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = "확인",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.white
+ )
+ }
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+fun UserRemoveSuccessDialogPreview() {
+ DngoTheme {
+// UserRemoveSuccessDialog()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/navigation/AppInitHost.kt b/app/src/main/java/com/min/dnapp/presentation/navigation/AppInitHost.kt
new file mode 100644
index 0000000..eaa6e44
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/navigation/AppInitHost.kt
@@ -0,0 +1,93 @@
+package com.min.dnapp.presentation.navigation
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.compose.NavHost
+import androidx.navigation.compose.composable
+import androidx.navigation.compose.navigation
+import androidx.navigation.compose.rememberNavController
+import com.min.dnapp.presentation.init.AppInitViewModel
+import com.min.dnapp.presentation.init.InitRoute
+import com.min.dnapp.presentation.init.LoadingScreen
+import com.min.dnapp.presentation.init.OnboardingScreen
+import com.min.dnapp.presentation.init.OnboardingScreen2
+import com.min.dnapp.presentation.init.OnboardingScreen3
+import com.min.dnapp.presentation.init.ProfileSetupScreen
+
+@Composable
+fun AppInitHost(
+ appInitViewModel: AppInitViewModel = hiltViewModel(),
+ onInitComplete: () -> Unit
+) {
+ val uiState by appInitViewModel.uiState.collectAsStateWithLifecycle()
+
+ // nested navigation 담당 (인증 후)
+ val navController = rememberNavController()
+
+ // 초기 상태에 따라 시작 경로 결정
+ val startDestination = when (uiState.route) {
+ InitRoute.LOADING -> "loading"
+ InitRoute.ONBOARDING -> "onboarding_flow"
+ InitRoute.PROFILE_SETUP -> "profile_setup"
+ InitRoute.MAIN -> {
+ // 1번만 실행
+ LaunchedEffect(Unit) {
+ onInitComplete()
+ }
+ // NavHost 렌더링 중단
+ return
+ }
+ }
+
+ NavHost(
+ navController = navController,
+ startDestination = startDestination
+ ) {
+ composable("loading") {
+ LoadingScreen()
+ }
+
+ navigation(
+ startDestination = "onboarding",
+ route = "onboarding_flow"
+ ) {
+ composable("onboarding") {
+ OnboardingScreen(
+ onFinish = {
+ navController.navigate("onboarding2")
+ }
+ )
+ }
+ composable("onboarding2") {
+ OnboardingScreen2(
+ navController = navController,
+ onFinish = {
+ navController.navigate("onboarding3")
+ }
+ )
+ }
+ composable("onboarding3") {
+ OnboardingScreen3(
+ navController = navController,
+ onFinish = {
+ appInitViewModel.onOnboardingFinished()
+ navController.navigate("profile_setup") {
+ popUpTo("onboarding_flow") { inclusive = true }
+ }
+ }
+ )
+ }
+ }
+
+ composable("profile_setup") {
+ ProfileSetupScreen(
+ onFinish = {
+ onInitComplete()
+ }
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/profile/ProfileSetupScreen.kt b/app/src/main/java/com/min/dnapp/presentation/profile/ProfileSetupScreen.kt
new file mode 100644
index 0000000..cc89bdb
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/profile/ProfileSetupScreen.kt
@@ -0,0 +1,171 @@
+package com.min.dnapp.presentation.profile
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.R
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun ProfileSetupScreen() {
+ Surface(
+ modifier = Modifier.fillMaxSize(),
+ color = MomentoTheme.colors.white
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(horizontal = 20.dp),
+ verticalArrangement = Arrangement.SpaceBetween
+ ) {
+ ProfileSelectSection()
+ ProfileButtonSection(
+ onClick = {}
+ )
+ }
+ }
+}
+
+@Composable
+fun ProfileSelectSection() {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Spacer(Modifier.height(20.dp))
+
+ Text(
+ text = "프로필 설정",
+ style = MomentoTheme.typography.title01,
+ color = MomentoTheme.colors.black
+ )
+
+ Spacer(Modifier.height(34.dp))
+
+ // 기본 프로필이미지 목록 (8개)
+ Column(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.Center
+ ) {
+ Image(
+ painter = painterResource(R.drawable.logo_profile),
+ contentDescription = null
+ )
+ Spacer(Modifier.width(20.dp))
+ Image(
+ painter = painterResource(R.drawable.logo_profile2),
+ contentDescription = null
+ )
+ }
+ Spacer(Modifier.height(16.dp))
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.Center
+ ) {
+ Image(
+ painter = painterResource(R.drawable.logo_profile3),
+ contentDescription = null
+ )
+ Spacer(Modifier.width(20.dp))
+ Image(
+ painter = painterResource(R.drawable.logo_profile4),
+ contentDescription = null
+ )
+ }
+ Spacer(Modifier.height(16.dp))
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.Center
+ ) {
+ Image(
+ painter = painterResource(R.drawable.logo_profile5),
+ contentDescription = null
+ )
+ Spacer(Modifier.width(20.dp))
+ Image(
+ painter = painterResource(R.drawable.logo_profile6),
+ contentDescription = null
+ )
+ }
+ Spacer(Modifier.height(16.dp))
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.Center
+ ) {
+ Image(
+ painter = painterResource(R.drawable.logo_profile7),
+ contentDescription = null
+ )
+ Spacer(Modifier.width(20.dp))
+ Image(
+ painter = painterResource(R.drawable.logo_profile8),
+ contentDescription = null
+ )
+ }
+ }
+
+ Spacer(Modifier.height(20.dp))
+
+ Text(
+ text = "프로필을 설정해주세요!",
+ style = MomentoTheme.typography.title01,
+ color = MomentoTheme.colors.black
+ )
+ }
+}
+
+@Composable
+fun ProfileButtonSection(
+ onClick: () -> Unit
+) {
+ Column(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Box(
+ modifier = Modifier
+ .clickable { onClick() }
+ .fillMaxWidth()
+ .background(color = MomentoTheme.colors.brownBase, shape = RoundedCornerShape(10.dp))
+ .padding(vertical = 16.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = "선택했어요",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.white
+ )
+ }
+ Spacer(Modifier.height(16.dp))
+ }
+}
+
+@Preview
+@Composable
+fun ProfileSetupScreenPreview() {
+ DngoTheme {
+ ProfileSetupScreen()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/component/CustomFloatingActionButton.kt b/app/src/main/java/com/min/dnapp/presentation/ui/component/CustomFloatingActionButton.kt
new file mode 100644
index 0000000..fade4ca
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/component/CustomFloatingActionButton.kt
@@ -0,0 +1,45 @@
+package com.min.dnapp.presentation.ui.component
+
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import com.min.dnapp.presentation.ui.icon.appicons.Pen
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun CustomFloatingActionButton(
+ onClick: () -> Unit
+) {
+ Surface(
+ onClick = { onClick() },
+ color = MomentoTheme.colors.pinkB20,
+ shape = RoundedCornerShape(50.dp)
+ ) {
+ Row(
+ modifier = Modifier.padding(horizontal = 12.dp, vertical = 10.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = AppIcons.Pen,
+ contentDescription = null,
+ tint = MomentoTheme.colors.white
+ )
+ Spacer(Modifier.width(4.dp))
+ Text(
+ text = "기록 남기기",
+ style = MomentoTheme.typography.label,
+ color = MomentoTheme.colors.white
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/component/CustomSnackbar.kt b/app/src/main/java/com/min/dnapp/presentation/ui/component/CustomSnackbar.kt
new file mode 100644
index 0000000..323ed7c
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/component/CustomSnackbar.kt
@@ -0,0 +1,54 @@
+package com.min.dnapp.presentation.ui.component
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.SnackbarData
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.R
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun CustomSnackbar(
+ snackbarData: SnackbarData
+) {
+ Row(
+ modifier = Modifier
+ .clip(RoundedCornerShape(100.dp))
+ .background(color = MomentoTheme.colors.grayW20)
+ .padding(horizontal = 12.dp, vertical = 8.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Image(
+ painter = painterResource(R.drawable.logo_snackbar),
+ contentDescription = null
+ )
+ Spacer(Modifier.width(10.dp))
+ Text(
+ text = snackbarData.visuals.message,
+// style = MomentoTheme.typography.label,
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.white
+ )
+ }
+}
+
+@Preview
+@Composable
+fun CustomSnackbarPreview() {
+ DngoTheme {
+// CustomSnackbar()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/component/MomentoBottomNav.kt b/app/src/main/java/com/min/dnapp/presentation/ui/component/MomentoBottomNav.kt
new file mode 100644
index 0000000..57e75e5
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/component/MomentoBottomNav.kt
@@ -0,0 +1,119 @@
+package com.min.dnapp.presentation.ui.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.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.navigationBarsPadding
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+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.vector.ImageVector
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.R
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import com.min.dnapp.presentation.ui.icon.appicons.Explore
+import com.min.dnapp.presentation.ui.icon.appicons.Home
+import com.min.dnapp.presentation.ui.icon.appicons.My
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+data class BottonNavItem(
+ val icon: ImageVector,
+ val selectedIcon: Int,
+ val label: String,
+ val route: String
+)
+
+@Composable
+fun MomentoBottomNav(
+ currentRoute: String,
+ onNavItemClick: (String) -> Unit
+) {
+ val items = listOf(
+ BottonNavItem(AppIcons.Home, R.drawable.ic_home, "Home", "home"),
+ BottonNavItem(AppIcons.Explore, R.drawable.ic_explore, "Discover", "find"),
+ BottonNavItem(AppIcons.My, R.drawable.ic_my, "My", "my")
+ )
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ // bottomNav가 navigationBar 위에 올라가도록 padding 처리
+ .navigationBarsPadding()
+ ) {
+ HorizontalDivider(thickness = 1.dp, color = MomentoTheme.colors.grayW90)
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+// .height(56.dp)
+ .height(58.dp)
+ .background(color = MomentoTheme.colors.white),
+ horizontalArrangement = Arrangement.SpaceAround
+ ) {
+ items.forEach { item ->
+ val selected = currentRoute == item.route
+
+ Column(
+ modifier = Modifier
+ .weight(1f)
+ .fillMaxHeight()
+ .clickable(
+ // 리플효과 제거
+ indication = null,
+ interactionSource = remember { MutableInteractionSource() }
+ ) {
+ onNavItemClick(item.route)
+ },
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Spacer(Modifier.height(6.dp))
+
+ // bottomBar 아이콘
+ if (selected) {
+ Image(
+ painter = painterResource(item.selectedIcon),
+ contentDescription = null
+ )
+ } else {
+ Icon(
+ imageVector = item.icon,
+ contentDescription = null,
+ tint = if (selected) MomentoTheme.colors.brownBase else MomentoTheme.colors.grayW60
+ )
+ }
+
+ Text(
+ text = item.label,
+ style = MomentoTheme.typography.label,
+ color = if (selected) MomentoTheme.colors.brownB40 else MomentoTheme.colors.grayW60
+ )
+ }
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+fun MomentoBottomNavPreview() {
+ DngoTheme {
+ MomentoBottomNav(
+ currentRoute = "home"
+ ) { }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/component/SelectButton.kt b/app/src/main/java/com/min/dnapp/presentation/ui/component/SelectButton.kt
new file mode 100644
index 0000000..de34924
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/component/SelectButton.kt
@@ -0,0 +1,41 @@
+package com.min.dnapp.presentation.ui.component
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun SelectButton(
+ enabled: Boolean,
+ onConfirm: () -> Unit
+) {
+ val buttonColor = if (enabled) {
+ MomentoTheme.colors.brownBase
+ } else {
+ MomentoTheme.colors.grayW80
+ }
+
+ Box(
+ modifier = Modifier
+ .clickable(enabled = enabled, onClick = onConfirm)
+ .fillMaxWidth()
+ .background(color = buttonColor, shape = RoundedCornerShape(10.dp))
+ .padding(vertical = 16.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = "선택했어요",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.white
+ )
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/component/UserBadge.kt b/app/src/main/java/com/min/dnapp/presentation/ui/component/UserBadge.kt
new file mode 100644
index 0000000..d6d5d7a
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/component/UserBadge.kt
@@ -0,0 +1,47 @@
+package com.min.dnapp.presentation.ui.component
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.common.BadgeMapper
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun UserBadge(
+ badgeLv: Int,
+ badgeName: String
+) {
+ // badgeLv을 통해 매핑된 이미지 리소스 ID
+ val badgeImageResId = BadgeMapper.getBadgeImageResId(badgeLv)
+
+ Row(
+ modifier = Modifier
+ .background(color = MomentoTheme.colors.white, shape = RoundedCornerShape(20.dp))
+ .padding(horizontal = 12.dp, vertical = 6.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Image(
+ modifier = Modifier.size(24.dp),
+ painter = painterResource(badgeImageResId),
+ contentDescription = null
+ )
+ Spacer(Modifier.width(4.dp))
+ Text(
+ text = badgeName,
+// style = MomentoTheme.typography.caption,
+ style = MomentoTheme.typography.body03,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/__AppIcons.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/__AppIcons.kt
new file mode 100644
index 0000000..5767381
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/__AppIcons.kt
@@ -0,0 +1,40 @@
+package com.min.dnapp.presentation.ui.icon
+
+import androidx.compose.ui.graphics.vector.ImageVector
+import com.min.dnapp.presentation.ui.icon.appicons.ArrowRight
+import com.min.dnapp.presentation.ui.icon.appicons.Back
+import com.min.dnapp.presentation.ui.icon.appicons.Bell
+import com.min.dnapp.presentation.ui.icon.appicons.Calendar
+import com.min.dnapp.presentation.ui.icon.appicons.Delete
+import com.min.dnapp.presentation.ui.icon.appicons.DeleteLine
+import com.min.dnapp.presentation.ui.icon.appicons.Explore
+import com.min.dnapp.presentation.ui.icon.appicons.Gallery
+import com.min.dnapp.presentation.ui.icon.appicons.Home
+import com.min.dnapp.presentation.ui.icon.appicons.Kakao
+import com.min.dnapp.presentation.ui.icon.appicons.More
+import com.min.dnapp.presentation.ui.icon.appicons.My
+import com.min.dnapp.presentation.ui.icon.appicons.Pen
+import com.min.dnapp.presentation.ui.icon.appicons.PenSmall
+import com.min.dnapp.presentation.ui.icon.appicons.RecordBest
+import com.min.dnapp.presentation.ui.icon.appicons.RecordBookmark
+import com.min.dnapp.presentation.ui.icon.appicons.RecordComment
+import com.min.dnapp.presentation.ui.icon.appicons.RecordLike
+import com.min.dnapp.presentation.ui.icon.appicons.RecordSurprise
+import com.min.dnapp.presentation.ui.icon.appicons.ShareTriangle
+import com.min.dnapp.presentation.ui.icon.appicons.Year
+import kotlin.collections.List as ____KtList
+
+public object AppIcons
+
+private var __AllIcons: ____KtList? = null
+
+public val AppIcons.AllIcons: ____KtList
+ get() {
+ if (__AllIcons != null) {
+ return __AllIcons!!
+ }
+ __AllIcons= listOf(ArrowRight, Back, Bell, Calendar, Delete, DeleteLine, Explore, Gallery, Home,
+ Kakao, More, My, Pen, PenSmall, RecordBest, RecordBookmark, RecordComment, RecordLike,
+ RecordSurprise, ShareTriangle, Year)
+ return __AllIcons!!
+ }
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/ArrowRight.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/ArrowRight.kt
new file mode 100644
index 0000000..ee0192f
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/ArrowRight.kt
@@ -0,0 +1,114 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.ArrowRight: ImageVector
+ get() {
+ if (_arrowRight != null) {
+ return _arrowRight!!
+ }
+ _arrowRight = Builder(name = "ArrowRight", defaultWidth = 25.0.dp, defaultHeight = 24.0.dp,
+ viewportWidth = 25.0f, viewportHeight = 24.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(18.546f, 12.0f)
+ lineTo(18.9f, 11.646f)
+ lineTo(19.253f, 12.0f)
+ lineTo(18.9f, 12.354f)
+ lineTo(18.546f, 12.0f)
+ close()
+ moveTo(6.547f, 12.5f)
+ curveTo(6.414f, 12.5f, 6.287f, 12.447f, 6.193f, 12.354f)
+ curveTo(6.099f, 12.26f, 6.047f, 12.133f, 6.047f, 12.0f)
+ curveTo(6.047f, 11.867f, 6.099f, 11.74f, 6.193f, 11.646f)
+ curveTo(6.287f, 11.553f, 6.414f, 11.5f, 6.547f, 11.5f)
+ verticalLineTo(12.5f)
+ close()
+ moveTo(14.901f, 7.646f)
+ lineTo(18.9f, 11.646f)
+ lineTo(18.192f, 12.354f)
+ lineTo(14.193f, 8.354f)
+ lineTo(14.901f, 7.646f)
+ close()
+ moveTo(18.9f, 12.354f)
+ lineTo(14.901f, 16.354f)
+ lineTo(14.193f, 15.646f)
+ lineTo(18.192f, 11.646f)
+ lineTo(18.9f, 12.354f)
+ close()
+ moveTo(18.546f, 12.5f)
+ horizontalLineTo(6.547f)
+ verticalLineTo(11.5f)
+ horizontalLineTo(18.546f)
+ verticalLineTo(12.5f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.2f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(18.546f, 12.0f)
+ lineTo(18.9f, 11.646f)
+ lineTo(19.253f, 12.0f)
+ lineTo(18.9f, 12.354f)
+ lineTo(18.546f, 12.0f)
+ close()
+ moveTo(6.547f, 12.5f)
+ curveTo(6.414f, 12.5f, 6.287f, 12.447f, 6.193f, 12.354f)
+ curveTo(6.099f, 12.26f, 6.047f, 12.133f, 6.047f, 12.0f)
+ curveTo(6.047f, 11.867f, 6.099f, 11.74f, 6.193f, 11.646f)
+ curveTo(6.287f, 11.553f, 6.414f, 11.5f, 6.547f, 11.5f)
+ verticalLineTo(12.5f)
+ close()
+ moveTo(14.901f, 7.646f)
+ lineTo(18.9f, 11.646f)
+ lineTo(18.192f, 12.354f)
+ lineTo(14.193f, 8.354f)
+ lineTo(14.901f, 7.646f)
+ close()
+ moveTo(18.9f, 12.354f)
+ lineTo(14.901f, 16.354f)
+ lineTo(14.193f, 15.646f)
+ lineTo(18.192f, 11.646f)
+ lineTo(18.9f, 12.354f)
+ close()
+ moveTo(18.546f, 12.5f)
+ horizontalLineTo(6.547f)
+ verticalLineTo(11.5f)
+ horizontalLineTo(18.546f)
+ verticalLineTo(12.5f)
+ close()
+ }
+ }
+ .build()
+ return _arrowRight!!
+ }
+
+private var _arrowRight: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.ArrowRight, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Back.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Back.kt
new file mode 100644
index 0000000..6aa3841
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Back.kt
@@ -0,0 +1,114 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.Back: ImageVector
+ get() {
+ if (_back != null) {
+ return _back!!
+ }
+ _back = Builder(name = "Back", defaultWidth = 16.0.dp, defaultHeight = 12.0.dp,
+ viewportWidth = 16.0f, viewportHeight = 12.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(1.121f, 6.0f)
+ lineTo(0.708f, 5.587f)
+ lineTo(0.296f, 6.0f)
+ lineTo(0.708f, 6.413f)
+ lineTo(1.121f, 6.0f)
+ close()
+ moveTo(15.121f, 6.583f)
+ curveTo(15.276f, 6.583f, 15.424f, 6.522f, 15.533f, 6.413f)
+ curveTo(15.643f, 6.303f, 15.704f, 6.155f, 15.704f, 6.0f)
+ curveTo(15.704f, 5.845f, 15.643f, 5.697f, 15.533f, 5.588f)
+ curveTo(15.424f, 5.478f, 15.276f, 5.417f, 15.121f, 5.417f)
+ verticalLineTo(6.583f)
+ close()
+ moveTo(5.374f, 0.92f)
+ lineTo(0.708f, 5.587f)
+ lineTo(1.534f, 6.413f)
+ lineTo(6.2f, 1.746f)
+ lineTo(5.374f, 0.92f)
+ close()
+ moveTo(0.708f, 6.413f)
+ lineTo(5.374f, 11.08f)
+ lineTo(6.2f, 10.254f)
+ lineTo(1.534f, 5.587f)
+ lineTo(0.708f, 6.413f)
+ close()
+ moveTo(1.121f, 6.583f)
+ horizontalLineTo(15.121f)
+ verticalLineTo(5.417f)
+ horizontalLineTo(1.121f)
+ verticalLineTo(6.583f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.2f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(1.121f, 6.0f)
+ lineTo(0.708f, 5.587f)
+ lineTo(0.296f, 6.0f)
+ lineTo(0.708f, 6.413f)
+ lineTo(1.121f, 6.0f)
+ close()
+ moveTo(15.121f, 6.583f)
+ curveTo(15.276f, 6.583f, 15.424f, 6.522f, 15.533f, 6.413f)
+ curveTo(15.643f, 6.303f, 15.704f, 6.155f, 15.704f, 6.0f)
+ curveTo(15.704f, 5.845f, 15.643f, 5.697f, 15.533f, 5.588f)
+ curveTo(15.424f, 5.478f, 15.276f, 5.417f, 15.121f, 5.417f)
+ verticalLineTo(6.583f)
+ close()
+ moveTo(5.374f, 0.92f)
+ lineTo(0.708f, 5.587f)
+ lineTo(1.534f, 6.413f)
+ lineTo(6.2f, 1.746f)
+ lineTo(5.374f, 0.92f)
+ close()
+ moveTo(0.708f, 6.413f)
+ lineTo(5.374f, 11.08f)
+ lineTo(6.2f, 10.254f)
+ lineTo(1.534f, 5.587f)
+ lineTo(0.708f, 6.413f)
+ close()
+ moveTo(1.121f, 6.583f)
+ horizontalLineTo(15.121f)
+ verticalLineTo(5.417f)
+ horizontalLineTo(1.121f)
+ verticalLineTo(6.583f)
+ close()
+ }
+ }
+ .build()
+ return _back!!
+ }
+
+private var _back: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.Back, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Bell.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Bell.kt
new file mode 100644
index 0000000..222a2e0
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Bell.kt
@@ -0,0 +1,218 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.Bell: ImageVector
+ get() {
+ if (_bell != null) {
+ return _bell!!
+ }
+ _bell = Builder(name = "Bell", defaultWidth = 16.0.dp, defaultHeight = 20.0.dp,
+ viewportWidth = 16.0f, viewportHeight = 20.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(10.546f, 16.734f)
+ curveTo(10.812f, 16.806f, 10.971f, 17.083f, 10.899f, 17.352f)
+ curveTo(10.729f, 17.997f, 10.352f, 18.567f, 9.828f, 18.974f)
+ curveTo(9.304f, 19.38f, 8.661f, 19.6f, 8.001f, 19.6f)
+ curveTo(7.341f, 19.6f, 6.699f, 19.38f, 6.175f, 18.974f)
+ curveTo(5.651f, 18.567f, 5.274f, 17.997f, 5.103f, 17.352f)
+ curveTo(5.032f, 17.083f, 5.19f, 16.806f, 5.457f, 16.734f)
+ curveTo(5.724f, 16.662f, 5.998f, 16.821f, 6.069f, 17.091f)
+ curveTo(6.183f, 17.521f, 6.434f, 17.9f, 6.783f, 18.171f)
+ curveTo(7.132f, 18.442f, 7.561f, 18.59f, 8.001f, 18.59f)
+ curveTo(8.441f, 18.59f, 8.869f, 18.442f, 9.219f, 18.171f)
+ curveTo(9.568f, 17.9f, 9.82f, 17.521f, 9.934f, 17.091f)
+ curveTo(10.005f, 16.821f, 10.279f, 16.661f, 10.546f, 16.734f)
+ close()
+ moveTo(13.001f, 6.463f)
+ curveTo(13.001f, 5.123f, 12.474f, 3.838f, 11.537f, 2.891f)
+ curveTo(10.599f, 1.943f, 9.327f, 1.41f, 8.001f, 1.41f)
+ curveTo(6.675f, 1.41f, 5.403f, 1.943f, 4.466f, 2.891f)
+ curveTo(3.528f, 3.838f, 3.001f, 5.123f, 3.001f, 6.463f)
+ verticalLineTo(8.484f)
+ curveTo(3.001f, 10.233f, 2.44f, 11.935f, 1.401f, 13.334f)
+ lineTo(1.4f, 13.335f)
+ lineTo(1.188f, 13.617f)
+ curveTo(1.15f, 13.67f, 1.127f, 13.704f, 1.106f, 13.733f)
+ curveTo(1.097f, 13.746f, 1.09f, 13.755f, 1.085f, 13.762f)
+ curveTo(1.081f, 13.769f, 1.078f, 13.772f, 1.078f, 13.772f)
+ lineTo(1.077f, 13.771f)
+ curveTo(1.031f, 13.844f, 1.006f, 13.929f, 1.002f, 14.016f)
+ curveTo(0.998f, 14.103f, 1.015f, 14.19f, 1.054f, 14.267f)
+ curveTo(1.092f, 14.345f, 1.15f, 14.412f, 1.222f, 14.461f)
+ curveTo(1.293f, 14.509f, 1.375f, 14.538f, 1.46f, 14.545f)
+ curveTo(1.46f, 14.545f, 1.461f, 14.546f, 1.463f, 14.546f)
+ curveTo(1.465f, 14.546f, 1.469f, 14.546f, 1.473f, 14.546f)
+ curveTo(1.481f, 14.546f, 1.492f, 14.547f, 1.508f, 14.547f)
+ horizontalLineTo(14.493f)
+ curveTo(14.518f, 14.546f, 14.528f, 14.545f, 14.53f, 14.545f)
+ lineTo(14.54f, 14.544f)
+ curveTo(14.625f, 14.537f, 14.708f, 14.509f, 14.779f, 14.46f)
+ curveTo(14.851f, 14.411f, 14.908f, 14.344f, 14.946f, 14.267f)
+ curveTo(14.985f, 14.189f, 15.003f, 14.103f, 14.999f, 14.016f)
+ curveTo(14.995f, 13.931f, 14.968f, 13.848f, 14.924f, 13.775f)
+ curveTo(14.89f, 13.725f, 14.855f, 13.675f, 14.818f, 13.627f)
+ lineTo(14.814f, 13.623f)
+ lineTo(14.601f, 13.334f)
+ verticalLineTo(13.333f)
+ curveTo(13.563f, 11.934f, 13.001f, 10.233f, 13.001f, 8.484f)
+ verticalLineTo(6.463f)
+ close()
+ moveTo(14.001f, 8.484f)
+ curveTo(14.001f, 9.919f, 14.433f, 11.317f, 15.235f, 12.495f)
+ lineTo(15.401f, 12.728f)
+ lineTo(15.612f, 13.013f)
+ lineTo(15.759f, 13.218f)
+ lineTo(15.764f, 13.226f)
+ lineTo(15.769f, 13.232f)
+ curveTo(15.906f, 13.453f, 15.984f, 13.705f, 15.997f, 13.966f)
+ curveTo(16.01f, 14.226f, 15.956f, 14.485f, 15.841f, 14.719f)
+ curveTo(15.725f, 14.952f, 15.553f, 15.151f, 15.339f, 15.297f)
+ curveTo(15.127f, 15.441f, 14.883f, 15.527f, 14.629f, 15.549f)
+ lineTo(14.63f, 15.55f)
+ horizontalLineTo(14.627f)
+ curveTo(14.625f, 15.55f, 14.623f, 15.551f, 14.621f, 15.551f)
+ lineTo(14.62f, 15.55f)
+ curveTo(14.55f, 15.557f, 14.46f, 15.557f, 14.358f, 15.557f)
+ horizontalLineTo(1.644f)
+ curveTo(1.535f, 15.557f, 1.447f, 15.557f, 1.384f, 15.552f)
+ horizontalLineTo(1.383f)
+ curveTo(1.125f, 15.532f, 0.877f, 15.445f, 0.663f, 15.299f)
+ curveTo(0.449f, 15.153f, 0.275f, 14.953f, 0.159f, 14.719f)
+ curveTo(0.044f, 14.486f, -0.01f, 14.226f, 0.003f, 13.966f)
+ curveTo(0.016f, 13.705f, 0.095f, 13.451f, 0.233f, 13.23f)
+ lineTo(0.234f, 13.229f)
+ curveTo(0.267f, 13.177f, 0.321f, 13.104f, 0.383f, 13.02f)
+ lineTo(0.387f, 13.014f)
+ lineTo(0.602f, 12.727f)
+ curveTo(1.51f, 11.503f, 2.001f, 10.014f, 2.001f, 8.484f)
+ verticalLineTo(6.463f)
+ curveTo(2.001f, 4.855f, 2.634f, 3.313f, 3.759f, 2.176f)
+ curveTo(4.884f, 1.039f, 6.41f, 0.4f, 8.001f, 0.4f)
+ curveTo(9.592f, 0.4f, 11.119f, 1.039f, 12.244f, 2.176f)
+ curveTo(13.369f, 3.313f, 14.001f, 4.855f, 14.001f, 6.463f)
+ verticalLineTo(8.484f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(10.546f, 16.734f)
+ curveTo(10.812f, 16.806f, 10.971f, 17.083f, 10.899f, 17.352f)
+ curveTo(10.729f, 17.997f, 10.352f, 18.567f, 9.828f, 18.974f)
+ curveTo(9.304f, 19.38f, 8.661f, 19.6f, 8.001f, 19.6f)
+ curveTo(7.341f, 19.6f, 6.699f, 19.38f, 6.175f, 18.974f)
+ curveTo(5.651f, 18.567f, 5.274f, 17.997f, 5.103f, 17.352f)
+ curveTo(5.032f, 17.083f, 5.19f, 16.806f, 5.457f, 16.734f)
+ curveTo(5.724f, 16.662f, 5.998f, 16.821f, 6.069f, 17.091f)
+ curveTo(6.183f, 17.521f, 6.434f, 17.9f, 6.783f, 18.171f)
+ curveTo(7.132f, 18.442f, 7.561f, 18.59f, 8.001f, 18.59f)
+ curveTo(8.441f, 18.59f, 8.869f, 18.442f, 9.219f, 18.171f)
+ curveTo(9.568f, 17.9f, 9.82f, 17.521f, 9.934f, 17.091f)
+ curveTo(10.005f, 16.821f, 10.279f, 16.661f, 10.546f, 16.734f)
+ close()
+ moveTo(13.001f, 6.463f)
+ curveTo(13.001f, 5.123f, 12.474f, 3.838f, 11.537f, 2.891f)
+ curveTo(10.599f, 1.943f, 9.327f, 1.41f, 8.001f, 1.41f)
+ curveTo(6.675f, 1.41f, 5.403f, 1.943f, 4.466f, 2.891f)
+ curveTo(3.528f, 3.838f, 3.001f, 5.123f, 3.001f, 6.463f)
+ verticalLineTo(8.484f)
+ curveTo(3.001f, 10.233f, 2.44f, 11.935f, 1.401f, 13.334f)
+ lineTo(1.4f, 13.335f)
+ lineTo(1.188f, 13.617f)
+ curveTo(1.15f, 13.67f, 1.127f, 13.704f, 1.106f, 13.733f)
+ curveTo(1.097f, 13.746f, 1.09f, 13.755f, 1.085f, 13.762f)
+ curveTo(1.081f, 13.769f, 1.078f, 13.772f, 1.078f, 13.772f)
+ lineTo(1.077f, 13.771f)
+ curveTo(1.031f, 13.844f, 1.006f, 13.929f, 1.002f, 14.016f)
+ curveTo(0.998f, 14.103f, 1.015f, 14.19f, 1.054f, 14.267f)
+ curveTo(1.092f, 14.345f, 1.15f, 14.412f, 1.222f, 14.461f)
+ curveTo(1.293f, 14.509f, 1.375f, 14.538f, 1.46f, 14.545f)
+ curveTo(1.46f, 14.545f, 1.461f, 14.546f, 1.463f, 14.546f)
+ curveTo(1.465f, 14.546f, 1.469f, 14.546f, 1.473f, 14.546f)
+ curveTo(1.481f, 14.546f, 1.492f, 14.547f, 1.508f, 14.547f)
+ horizontalLineTo(14.493f)
+ curveTo(14.518f, 14.546f, 14.528f, 14.545f, 14.53f, 14.545f)
+ lineTo(14.54f, 14.544f)
+ curveTo(14.625f, 14.537f, 14.708f, 14.509f, 14.779f, 14.46f)
+ curveTo(14.851f, 14.411f, 14.908f, 14.344f, 14.946f, 14.267f)
+ curveTo(14.985f, 14.189f, 15.003f, 14.103f, 14.999f, 14.016f)
+ curveTo(14.995f, 13.931f, 14.968f, 13.848f, 14.924f, 13.775f)
+ curveTo(14.89f, 13.725f, 14.855f, 13.675f, 14.818f, 13.627f)
+ lineTo(14.814f, 13.623f)
+ lineTo(14.601f, 13.334f)
+ verticalLineTo(13.333f)
+ curveTo(13.563f, 11.934f, 13.001f, 10.233f, 13.001f, 8.484f)
+ verticalLineTo(6.463f)
+ close()
+ moveTo(14.001f, 8.484f)
+ curveTo(14.001f, 9.919f, 14.433f, 11.317f, 15.235f, 12.495f)
+ lineTo(15.401f, 12.728f)
+ lineTo(15.612f, 13.013f)
+ lineTo(15.759f, 13.218f)
+ lineTo(15.764f, 13.226f)
+ lineTo(15.769f, 13.232f)
+ curveTo(15.906f, 13.453f, 15.984f, 13.705f, 15.997f, 13.966f)
+ curveTo(16.01f, 14.226f, 15.956f, 14.485f, 15.841f, 14.719f)
+ curveTo(15.725f, 14.952f, 15.553f, 15.151f, 15.339f, 15.297f)
+ curveTo(15.127f, 15.441f, 14.883f, 15.527f, 14.629f, 15.549f)
+ lineTo(14.63f, 15.55f)
+ horizontalLineTo(14.627f)
+ curveTo(14.625f, 15.55f, 14.623f, 15.551f, 14.621f, 15.551f)
+ lineTo(14.62f, 15.55f)
+ curveTo(14.55f, 15.557f, 14.46f, 15.557f, 14.358f, 15.557f)
+ horizontalLineTo(1.644f)
+ curveTo(1.535f, 15.557f, 1.447f, 15.557f, 1.384f, 15.552f)
+ horizontalLineTo(1.383f)
+ curveTo(1.125f, 15.532f, 0.877f, 15.445f, 0.663f, 15.299f)
+ curveTo(0.449f, 15.153f, 0.275f, 14.953f, 0.159f, 14.719f)
+ curveTo(0.044f, 14.486f, -0.01f, 14.226f, 0.003f, 13.966f)
+ curveTo(0.016f, 13.705f, 0.095f, 13.451f, 0.233f, 13.23f)
+ lineTo(0.234f, 13.229f)
+ curveTo(0.267f, 13.177f, 0.321f, 13.104f, 0.383f, 13.02f)
+ lineTo(0.387f, 13.014f)
+ lineTo(0.602f, 12.727f)
+ curveTo(1.51f, 11.503f, 2.001f, 10.014f, 2.001f, 8.484f)
+ verticalLineTo(6.463f)
+ curveTo(2.001f, 4.855f, 2.634f, 3.313f, 3.759f, 2.176f)
+ curveTo(4.884f, 1.039f, 6.41f, 0.4f, 8.001f, 0.4f)
+ curveTo(9.592f, 0.4f, 11.119f, 1.039f, 12.244f, 2.176f)
+ curveTo(13.369f, 3.313f, 14.001f, 4.855f, 14.001f, 6.463f)
+ verticalLineTo(8.484f)
+ close()
+ }
+ }
+ .build()
+ return _bell!!
+ }
+
+private var _bell: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.Bell, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Calendar.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Calendar.kt
new file mode 100644
index 0000000..9a59e01
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Calendar.kt
@@ -0,0 +1,100 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.Calendar: ImageVector
+ get() {
+ if (_calendar != null) {
+ return _calendar!!
+ }
+ _calendar = Builder(name = "Calendar", defaultWidth = 20.0.dp, defaultHeight = 20.0.dp,
+ viewportWidth = 20.0f, viewportHeight = 20.0f).apply {
+ path(fill = SolidColor(Color(0xFFA56C48)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(18.0f, 6.0f)
+ curveTo(18.0f, 5.448f, 17.552f, 5.0f, 17.0f, 5.0f)
+ horizontalLineTo(3.0f)
+ curveTo(2.448f, 5.0f, 2.0f, 5.448f, 2.0f, 6.0f)
+ verticalLineTo(17.0f)
+ curveTo(2.0f, 17.552f, 2.448f, 18.0f, 3.0f, 18.0f)
+ horizontalLineTo(17.0f)
+ curveTo(17.552f, 18.0f, 18.0f, 17.552f, 18.0f, 17.0f)
+ verticalLineTo(6.0f)
+ close()
+ moveTo(20.0f, 17.0f)
+ curveTo(20.0f, 18.657f, 18.657f, 20.0f, 17.0f, 20.0f)
+ horizontalLineTo(3.0f)
+ curveTo(1.343f, 20.0f, 0.0f, 18.657f, 0.0f, 17.0f)
+ verticalLineTo(6.0f)
+ curveTo(0.0f, 4.343f, 1.343f, 3.0f, 3.0f, 3.0f)
+ horizontalLineTo(17.0f)
+ curveTo(18.657f, 3.0f, 20.0f, 4.343f, 20.0f, 6.0f)
+ verticalLineTo(17.0f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFA56C48)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(1.0f, 8.0f)
+ curveTo(1.0f, 6.114f, 1.0f, 5.172f, 1.586f, 4.586f)
+ curveTo(2.172f, 4.0f, 3.114f, 4.0f, 5.0f, 4.0f)
+ horizontalLineTo(15.0f)
+ curveTo(16.886f, 4.0f, 17.828f, 4.0f, 18.414f, 4.586f)
+ curveTo(19.0f, 5.172f, 19.0f, 6.114f, 19.0f, 8.0f)
+ horizontalLineTo(1.0f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFA56C48)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(4.0f, 4.0f)
+ verticalLineTo(1.0f)
+ curveTo(4.0f, 0.448f, 4.448f, 0.0f, 5.0f, 0.0f)
+ curveTo(5.552f, 0.0f, 6.0f, 0.448f, 6.0f, 1.0f)
+ verticalLineTo(4.0f)
+ curveTo(6.0f, 4.552f, 5.552f, 5.0f, 5.0f, 5.0f)
+ curveTo(4.448f, 5.0f, 4.0f, 4.552f, 4.0f, 4.0f)
+ close()
+ moveTo(14.0f, 4.0f)
+ verticalLineTo(1.0f)
+ curveTo(14.0f, 0.448f, 14.448f, 0.0f, 15.0f, 0.0f)
+ curveTo(15.552f, 0.0f, 16.0f, 0.448f, 16.0f, 1.0f)
+ verticalLineTo(4.0f)
+ curveTo(16.0f, 4.552f, 15.552f, 5.0f, 15.0f, 5.0f)
+ curveTo(14.448f, 5.0f, 14.0f, 4.552f, 14.0f, 4.0f)
+ close()
+ }
+ }
+ .build()
+ return _calendar!!
+ }
+
+private var _calendar: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.Calendar, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Delete.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Delete.kt
new file mode 100644
index 0000000..1835935
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Delete.kt
@@ -0,0 +1,92 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.EvenOdd
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.Delete: ImageVector
+ get() {
+ if (_delete != null) {
+ return _delete!!
+ }
+ _delete = Builder(name = "Delete", defaultWidth = 18.0.dp, defaultHeight = 18.0.dp,
+ viewportWidth = 18.0f, viewportHeight = 18.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = EvenOdd) {
+ moveTo(9.0f, 0.0f)
+ curveTo(13.971f, 0.0f, 18.0f, 4.029f, 18.0f, 9.0f)
+ curveTo(18.0f, 13.971f, 13.971f, 18.0f, 9.0f, 18.0f)
+ curveTo(4.029f, 18.0f, 0.0f, 13.971f, 0.0f, 9.0f)
+ curveTo(0.0f, 4.029f, 4.029f, 0.0f, 9.0f, 0.0f)
+ close()
+ moveTo(9.0f, 8.293f)
+ lineTo(6.0f, 5.293f)
+ lineTo(5.293f, 6.0f)
+ lineTo(8.293f, 9.0f)
+ lineTo(5.293f, 12.0f)
+ lineTo(6.0f, 12.707f)
+ lineTo(9.0f, 9.707f)
+ lineTo(12.0f, 12.707f)
+ lineTo(12.707f, 12.0f)
+ lineTo(9.707f, 9.0f)
+ lineTo(12.707f, 6.0f)
+ lineTo(12.0f, 5.293f)
+ lineTo(9.0f, 8.293f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = EvenOdd) {
+ moveTo(9.0f, 0.0f)
+ curveTo(13.971f, 0.0f, 18.0f, 4.029f, 18.0f, 9.0f)
+ curveTo(18.0f, 13.971f, 13.971f, 18.0f, 9.0f, 18.0f)
+ curveTo(4.029f, 18.0f, 0.0f, 13.971f, 0.0f, 9.0f)
+ curveTo(0.0f, 4.029f, 4.029f, 0.0f, 9.0f, 0.0f)
+ close()
+ moveTo(9.0f, 8.293f)
+ lineTo(6.0f, 5.293f)
+ lineTo(5.293f, 6.0f)
+ lineTo(8.293f, 9.0f)
+ lineTo(5.293f, 12.0f)
+ lineTo(6.0f, 12.707f)
+ lineTo(9.0f, 9.707f)
+ lineTo(12.0f, 12.707f)
+ lineTo(12.707f, 12.0f)
+ lineTo(9.707f, 9.0f)
+ lineTo(12.707f, 6.0f)
+ lineTo(12.0f, 5.293f)
+ lineTo(9.0f, 8.293f)
+ close()
+ }
+ }
+ .build()
+ return _delete!!
+ }
+
+private var _delete: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.Delete, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/DeleteLine.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/DeleteLine.kt
new file mode 100644
index 0000000..5a27d43
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/DeleteLine.kt
@@ -0,0 +1,88 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.DeleteLine: ImageVector
+ get() {
+ if (_deleteLine != null) {
+ return _deleteLine!!
+ }
+ _deleteLine = Builder(name = "DeleteLine", defaultWidth = 16.0.dp, defaultHeight = 16.0.dp,
+ viewportWidth = 16.0f, viewportHeight = 16.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(11.764f, 3.764f)
+ curveTo(11.895f, 3.634f, 12.106f, 3.634f, 12.236f, 3.764f)
+ curveTo(12.366f, 3.894f, 12.366f, 4.105f, 12.236f, 4.235f)
+ lineTo(8.471f, 8.0f)
+ lineTo(12.236f, 11.764f)
+ curveTo(12.366f, 11.894f, 12.366f, 12.105f, 12.236f, 12.236f)
+ curveTo(12.106f, 12.366f, 11.895f, 12.366f, 11.764f, 12.236f)
+ lineTo(8.0f, 8.471f)
+ lineTo(4.236f, 12.236f)
+ curveTo(4.106f, 12.366f, 3.895f, 12.366f, 3.764f, 12.236f)
+ curveTo(3.634f, 12.105f, 3.634f, 11.894f, 3.764f, 11.764f)
+ lineTo(7.529f, 8.0f)
+ lineTo(3.764f, 4.235f)
+ curveTo(3.634f, 4.105f, 3.634f, 3.894f, 3.764f, 3.764f)
+ curveTo(3.895f, 3.634f, 4.106f, 3.634f, 4.236f, 3.764f)
+ lineTo(8.0f, 7.528f)
+ lineTo(11.764f, 3.764f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.2f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(11.764f, 3.764f)
+ curveTo(11.895f, 3.634f, 12.106f, 3.634f, 12.236f, 3.764f)
+ curveTo(12.366f, 3.894f, 12.366f, 4.105f, 12.236f, 4.235f)
+ lineTo(8.471f, 8.0f)
+ lineTo(12.236f, 11.764f)
+ curveTo(12.366f, 11.894f, 12.366f, 12.105f, 12.236f, 12.236f)
+ curveTo(12.106f, 12.366f, 11.895f, 12.366f, 11.764f, 12.236f)
+ lineTo(8.0f, 8.471f)
+ lineTo(4.236f, 12.236f)
+ curveTo(4.106f, 12.366f, 3.895f, 12.366f, 3.764f, 12.236f)
+ curveTo(3.634f, 12.105f, 3.634f, 11.894f, 3.764f, 11.764f)
+ lineTo(7.529f, 8.0f)
+ lineTo(3.764f, 4.235f)
+ curveTo(3.634f, 4.105f, 3.634f, 3.894f, 3.764f, 3.764f)
+ curveTo(3.895f, 3.634f, 4.106f, 3.634f, 4.236f, 3.764f)
+ lineTo(8.0f, 7.528f)
+ lineTo(11.764f, 3.764f)
+ close()
+ }
+ }
+ .build()
+ return _deleteLine!!
+ }
+
+private var _deleteLine: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.DeleteLine, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Explore.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Explore.kt
new file mode 100644
index 0000000..1f15b2a
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Explore.kt
@@ -0,0 +1,350 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.Explore: ImageVector
+ get() {
+ if (_explore != null) {
+ return _explore!!
+ }
+ _explore = Builder(name = "Explore", defaultWidth = 28.0.dp, defaultHeight = 29.0.dp,
+ viewportWidth = 28.0f, viewportHeight = 29.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(16.941f, 9.833f)
+ curveTo(16.941f, 8.222f, 15.635f, 6.917f, 14.024f, 6.917f)
+ curveTo(12.413f, 6.917f, 11.107f, 8.222f, 11.107f, 9.833f)
+ curveTo(11.107f, 11.444f, 12.413f, 12.75f, 14.024f, 12.75f)
+ curveTo(15.635f, 12.75f, 16.941f, 11.444f, 16.941f, 9.833f)
+ close()
+ moveTo(18.107f, 9.833f)
+ curveTo(18.107f, 12.089f, 16.279f, 13.917f, 14.024f, 13.917f)
+ curveTo(11.769f, 13.917f, 9.941f, 12.089f, 9.941f, 9.833f)
+ curveTo(9.941f, 7.578f, 11.769f, 5.75f, 14.024f, 5.75f)
+ curveTo(16.279f, 5.75f, 18.107f, 7.578f, 18.107f, 9.833f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(16.941f, 9.833f)
+ curveTo(16.941f, 8.222f, 15.635f, 6.917f, 14.024f, 6.917f)
+ curveTo(12.413f, 6.917f, 11.107f, 8.222f, 11.107f, 9.833f)
+ curveTo(11.107f, 11.444f, 12.413f, 12.75f, 14.024f, 12.75f)
+ curveTo(15.635f, 12.75f, 16.941f, 11.444f, 16.941f, 9.833f)
+ close()
+ moveTo(18.107f, 9.833f)
+ curveTo(18.107f, 12.089f, 16.279f, 13.917f, 14.024f, 13.917f)
+ curveTo(11.769f, 13.917f, 9.941f, 12.089f, 9.941f, 9.833f)
+ curveTo(9.941f, 7.578f, 11.769f, 5.75f, 14.024f, 5.75f)
+ curveTo(16.279f, 5.75f, 18.107f, 7.578f, 18.107f, 9.833f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(8.573f, 8.094f)
+ curveTo(8.955f, 8.143f, 9.322f, 8.268f, 9.656f, 8.461f)
+ curveTo(9.989f, 8.653f, 10.281f, 8.909f, 10.514f, 9.215f)
+ curveTo(10.748f, 9.52f, 10.919f, 9.87f, 11.017f, 10.241f)
+ curveTo(11.115f, 10.613f, 11.139f, 11.001f, 11.087f, 11.382f)
+ curveTo(11.034f, 11.763f, 10.907f, 12.13f, 10.713f, 12.462f)
+ curveTo(10.323f, 13.126f, 9.687f, 13.609f, 8.942f, 13.806f)
+ curveTo(8.198f, 14.003f, 7.406f, 13.897f, 6.739f, 13.512f)
+ curveTo(6.072f, 13.127f, 5.584f, 12.494f, 5.382f, 11.751f)
+ curveTo(5.18f, 11.008f, 5.282f, 10.215f, 5.662f, 9.545f)
+ curveTo(5.852f, 9.211f, 6.107f, 8.917f, 6.411f, 8.682f)
+ curveTo(6.715f, 8.446f, 7.062f, 8.272f, 7.433f, 8.171f)
+ curveTo(7.804f, 8.07f, 8.192f, 8.044f, 8.573f, 8.094f)
+ close()
+ moveTo(20.24f, 8.094f)
+ curveTo(20.621f, 8.143f, 20.989f, 8.268f, 21.322f, 8.461f)
+ curveTo(21.655f, 8.653f, 21.948f, 8.909f, 22.181f, 9.215f)
+ curveTo(22.415f, 9.52f, 22.585f, 9.87f, 22.684f, 10.241f)
+ curveTo(22.782f, 10.613f, 22.805f, 11.001f, 22.753f, 11.382f)
+ curveTo(22.701f, 11.763f, 22.574f, 12.13f, 22.379f, 12.462f)
+ curveTo(21.99f, 13.126f, 21.353f, 13.609f, 20.609f, 13.806f)
+ curveTo(19.865f, 14.003f, 19.072f, 13.897f, 18.406f, 13.512f)
+ curveTo(17.739f, 13.127f, 17.25f, 12.494f, 17.049f, 11.751f)
+ curveTo(16.847f, 11.008f, 16.948f, 10.215f, 17.329f, 9.545f)
+ curveTo(17.519f, 9.211f, 17.773f, 8.917f, 18.077f, 8.682f)
+ curveTo(18.381f, 8.446f, 18.728f, 8.272f, 19.099f, 8.171f)
+ curveTo(19.471f, 8.07f, 19.858f, 8.044f, 20.24f, 8.094f)
+ close()
+ moveTo(8.423f, 9.251f)
+ curveTo(8.194f, 9.222f, 7.962f, 9.238f, 7.739f, 9.298f)
+ curveTo(7.516f, 9.358f, 7.307f, 9.462f, 7.125f, 9.603f)
+ curveTo(6.943f, 9.745f, 6.79f, 9.921f, 6.676f, 10.122f)
+ curveTo(6.448f, 10.523f, 6.388f, 10.999f, 6.509f, 11.444f)
+ curveTo(6.63f, 11.89f, 6.922f, 12.271f, 7.322f, 12.502f)
+ curveTo(7.722f, 12.733f, 8.198f, 12.796f, 8.645f, 12.678f)
+ curveTo(9.091f, 12.56f, 9.473f, 12.27f, 9.707f, 11.872f)
+ curveTo(9.823f, 11.673f, 9.9f, 11.452f, 9.931f, 11.223f)
+ curveTo(9.962f, 10.995f, 9.948f, 10.762f, 9.889f, 10.539f)
+ curveTo(9.83f, 10.316f, 9.727f, 10.107f, 9.587f, 9.923f)
+ curveTo(9.447f, 9.74f, 9.272f, 9.587f, 9.072f, 9.471f)
+ curveTo(8.872f, 9.356f, 8.652f, 9.281f, 8.423f, 9.251f)
+ close()
+ moveTo(20.089f, 9.251f)
+ curveTo(19.861f, 9.222f, 19.628f, 9.238f, 19.406f, 9.298f)
+ curveTo(19.183f, 9.358f, 18.974f, 9.462f, 18.792f, 9.603f)
+ curveTo(18.609f, 9.745f, 18.457f, 9.921f, 18.343f, 10.122f)
+ curveTo(18.115f, 10.523f, 18.054f, 10.999f, 18.175f, 11.444f)
+ curveTo(18.296f, 11.89f, 18.589f, 12.271f, 18.989f, 12.502f)
+ curveTo(19.389f, 12.733f, 19.865f, 12.796f, 20.312f, 12.678f)
+ curveTo(20.758f, 12.56f, 21.14f, 12.27f, 21.373f, 11.872f)
+ curveTo(21.49f, 11.673f, 21.567f, 11.452f, 21.598f, 11.223f)
+ curveTo(21.629f, 10.995f, 21.615f, 10.762f, 21.556f, 10.539f)
+ curveTo(21.497f, 10.316f, 21.394f, 10.107f, 21.254f, 9.923f)
+ curveTo(21.114f, 9.74f, 20.939f, 9.587f, 20.739f, 9.471f)
+ curveTo(20.539f, 9.356f, 20.318f, 9.281f, 20.089f, 9.251f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(8.573f, 8.094f)
+ curveTo(8.955f, 8.143f, 9.322f, 8.268f, 9.656f, 8.461f)
+ curveTo(9.989f, 8.653f, 10.281f, 8.909f, 10.514f, 9.215f)
+ curveTo(10.748f, 9.52f, 10.919f, 9.87f, 11.017f, 10.241f)
+ curveTo(11.115f, 10.613f, 11.139f, 11.001f, 11.087f, 11.382f)
+ curveTo(11.034f, 11.763f, 10.907f, 12.13f, 10.713f, 12.462f)
+ curveTo(10.323f, 13.126f, 9.687f, 13.609f, 8.942f, 13.806f)
+ curveTo(8.198f, 14.003f, 7.406f, 13.897f, 6.739f, 13.512f)
+ curveTo(6.072f, 13.127f, 5.584f, 12.494f, 5.382f, 11.751f)
+ curveTo(5.18f, 11.008f, 5.282f, 10.215f, 5.662f, 9.545f)
+ curveTo(5.852f, 9.211f, 6.107f, 8.917f, 6.411f, 8.682f)
+ curveTo(6.715f, 8.446f, 7.062f, 8.272f, 7.433f, 8.171f)
+ curveTo(7.804f, 8.07f, 8.192f, 8.044f, 8.573f, 8.094f)
+ close()
+ moveTo(20.24f, 8.094f)
+ curveTo(20.621f, 8.143f, 20.989f, 8.268f, 21.322f, 8.461f)
+ curveTo(21.655f, 8.653f, 21.948f, 8.909f, 22.181f, 9.215f)
+ curveTo(22.415f, 9.52f, 22.585f, 9.87f, 22.684f, 10.241f)
+ curveTo(22.782f, 10.613f, 22.805f, 11.001f, 22.753f, 11.382f)
+ curveTo(22.701f, 11.763f, 22.574f, 12.13f, 22.379f, 12.462f)
+ curveTo(21.99f, 13.126f, 21.353f, 13.609f, 20.609f, 13.806f)
+ curveTo(19.865f, 14.003f, 19.072f, 13.897f, 18.406f, 13.512f)
+ curveTo(17.739f, 13.127f, 17.25f, 12.494f, 17.049f, 11.751f)
+ curveTo(16.847f, 11.008f, 16.948f, 10.215f, 17.329f, 9.545f)
+ curveTo(17.519f, 9.211f, 17.773f, 8.917f, 18.077f, 8.682f)
+ curveTo(18.381f, 8.446f, 18.728f, 8.272f, 19.099f, 8.171f)
+ curveTo(19.471f, 8.07f, 19.858f, 8.044f, 20.24f, 8.094f)
+ close()
+ moveTo(8.423f, 9.251f)
+ curveTo(8.194f, 9.222f, 7.962f, 9.238f, 7.739f, 9.298f)
+ curveTo(7.516f, 9.358f, 7.307f, 9.462f, 7.125f, 9.603f)
+ curveTo(6.943f, 9.745f, 6.79f, 9.921f, 6.676f, 10.122f)
+ curveTo(6.448f, 10.523f, 6.388f, 10.999f, 6.509f, 11.444f)
+ curveTo(6.63f, 11.89f, 6.922f, 12.271f, 7.322f, 12.502f)
+ curveTo(7.722f, 12.733f, 8.198f, 12.796f, 8.645f, 12.678f)
+ curveTo(9.091f, 12.56f, 9.473f, 12.27f, 9.707f, 11.872f)
+ curveTo(9.823f, 11.673f, 9.9f, 11.452f, 9.931f, 11.223f)
+ curveTo(9.962f, 10.995f, 9.948f, 10.762f, 9.889f, 10.539f)
+ curveTo(9.83f, 10.316f, 9.727f, 10.107f, 9.587f, 9.923f)
+ curveTo(9.447f, 9.74f, 9.272f, 9.587f, 9.072f, 9.471f)
+ curveTo(8.872f, 9.356f, 8.652f, 9.281f, 8.423f, 9.251f)
+ close()
+ moveTo(20.089f, 9.251f)
+ curveTo(19.861f, 9.222f, 19.628f, 9.238f, 19.406f, 9.298f)
+ curveTo(19.183f, 9.358f, 18.974f, 9.462f, 18.792f, 9.603f)
+ curveTo(18.609f, 9.745f, 18.457f, 9.921f, 18.343f, 10.122f)
+ curveTo(18.115f, 10.523f, 18.054f, 10.999f, 18.175f, 11.444f)
+ curveTo(18.296f, 11.89f, 18.589f, 12.271f, 18.989f, 12.502f)
+ curveTo(19.389f, 12.733f, 19.865f, 12.796f, 20.312f, 12.678f)
+ curveTo(20.758f, 12.56f, 21.14f, 12.27f, 21.373f, 11.872f)
+ curveTo(21.49f, 11.673f, 21.567f, 11.452f, 21.598f, 11.223f)
+ curveTo(21.629f, 10.995f, 21.615f, 10.762f, 21.556f, 10.539f)
+ curveTo(21.497f, 10.316f, 21.394f, 10.107f, 21.254f, 9.923f)
+ curveTo(21.114f, 9.74f, 20.939f, 9.587f, 20.739f, 9.471f)
+ curveTo(20.539f, 9.356f, 20.318f, 9.281f, 20.089f, 9.251f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(14.024f, 16.25f)
+ curveTo(16.265f, 16.25f, 17.765f, 17.058f, 18.742f, 18.144f)
+ curveTo(19.7f, 19.208f, 20.113f, 20.492f, 20.293f, 21.396f)
+ horizontalLineTo(20.292f)
+ curveTo(20.505f, 22.455f, 19.633f, 23.25f, 18.69f, 23.25f)
+ horizontalLineTo(9.357f)
+ curveTo(8.416f, 23.25f, 7.542f, 22.454f, 7.755f, 21.395f)
+ curveTo(7.936f, 20.49f, 8.349f, 19.208f, 9.307f, 18.144f)
+ curveTo(10.283f, 17.059f, 11.783f, 16.25f, 14.024f, 16.25f)
+ close()
+ moveTo(14.024f, 17.417f)
+ curveTo(12.098f, 17.417f, 10.919f, 18.096f, 10.174f, 18.924f)
+ curveTo(9.41f, 19.772f, 9.058f, 20.827f, 8.899f, 21.624f)
+ verticalLineTo(21.625f)
+ curveTo(8.874f, 21.748f, 8.909f, 21.85f, 8.983f, 21.931f)
+ curveTo(9.062f, 22.017f, 9.193f, 22.083f, 9.357f, 22.083f)
+ horizontalLineTo(18.69f)
+ curveTo(18.855f, 22.083f, 18.986f, 22.016f, 19.065f, 21.931f)
+ curveTo(19.139f, 21.85f, 19.174f, 21.748f, 19.149f, 21.625f)
+ lineTo(19.148f, 21.624f)
+ curveTo(18.989f, 20.828f, 18.638f, 19.772f, 17.875f, 18.924f)
+ curveTo(17.129f, 18.096f, 15.95f, 17.417f, 14.024f, 17.417f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(14.024f, 16.25f)
+ curveTo(16.265f, 16.25f, 17.765f, 17.058f, 18.742f, 18.144f)
+ curveTo(19.7f, 19.208f, 20.113f, 20.492f, 20.293f, 21.396f)
+ horizontalLineTo(20.292f)
+ curveTo(20.505f, 22.455f, 19.633f, 23.25f, 18.69f, 23.25f)
+ horizontalLineTo(9.357f)
+ curveTo(8.416f, 23.25f, 7.542f, 22.454f, 7.755f, 21.395f)
+ curveTo(7.936f, 20.49f, 8.349f, 19.208f, 9.307f, 18.144f)
+ curveTo(10.283f, 17.059f, 11.783f, 16.25f, 14.024f, 16.25f)
+ close()
+ moveTo(14.024f, 17.417f)
+ curveTo(12.098f, 17.417f, 10.919f, 18.096f, 10.174f, 18.924f)
+ curveTo(9.41f, 19.772f, 9.058f, 20.827f, 8.899f, 21.624f)
+ verticalLineTo(21.625f)
+ curveTo(8.874f, 21.748f, 8.909f, 21.85f, 8.983f, 21.931f)
+ curveTo(9.062f, 22.017f, 9.193f, 22.083f, 9.357f, 22.083f)
+ horizontalLineTo(18.69f)
+ curveTo(18.855f, 22.083f, 18.986f, 22.016f, 19.065f, 21.931f)
+ curveTo(19.139f, 21.85f, 19.174f, 21.748f, 19.149f, 21.625f)
+ lineTo(19.148f, 21.624f)
+ curveTo(18.989f, 20.828f, 18.638f, 19.772f, 17.875f, 18.924f)
+ curveTo(17.129f, 18.096f, 15.95f, 17.417f, 14.024f, 17.417f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(8.166f, 15.551f)
+ curveTo(9.224f, 15.538f, 10.249f, 15.925f, 11.034f, 16.634f)
+ lineTo(11.597f, 17.144f)
+ lineTo(10.96f, 17.556f)
+ curveTo(9.382f, 18.579f, 8.524f, 20.406f, 8.067f, 22.117f)
+ lineTo(7.952f, 22.55f)
+ horizontalLineTo(4.789f)
+ curveTo(3.772f, 22.55f, 2.9f, 21.649f, 3.17f, 20.562f)
+ lineTo(3.172f, 20.56f)
+ curveTo(3.395f, 19.67f, 3.812f, 18.441f, 4.576f, 17.424f)
+ curveTo(5.351f, 16.392f, 6.506f, 15.554f, 8.158f, 15.551f)
+ curveTo(8.16f, 15.551f, 8.162f, 15.551f, 8.164f, 15.551f)
+ lineTo(8.166f, 15.55f)
+ verticalLineTo(15.551f)
+ close()
+ moveTo(19.833f, 15.55f)
+ curveTo(21.49f, 15.55f, 22.648f, 16.39f, 23.424f, 17.424f)
+ curveTo(24.093f, 18.314f, 24.496f, 19.367f, 24.736f, 20.212f)
+ lineTo(24.829f, 20.56f)
+ verticalLineTo(20.562f)
+ curveTo(25.1f, 21.649f, 24.228f, 22.55f, 23.211f, 22.55f)
+ horizontalLineTo(20.049f)
+ lineTo(19.933f, 22.117f)
+ curveTo(19.477f, 20.407f, 18.619f, 18.579f, 17.041f, 17.556f)
+ lineTo(16.397f, 17.139f)
+ lineTo(16.971f, 16.63f)
+ curveTo(17.7f, 15.985f, 18.644f, 15.55f, 19.833f, 15.55f)
+ close()
+ moveTo(8.166f, 16.716f)
+ curveTo(6.967f, 16.717f, 6.125f, 17.303f, 5.508f, 18.125f)
+ curveTo(4.879f, 18.963f, 4.51f, 20.019f, 4.303f, 20.844f)
+ curveTo(4.241f, 21.096f, 4.426f, 21.383f, 4.789f, 21.383f)
+ horizontalLineTo(7.065f)
+ curveTo(7.513f, 19.898f, 8.29f, 18.249f, 9.652f, 17.081f)
+ curveTo(9.201f, 16.839f, 8.694f, 16.709f, 8.174f, 16.716f)
+ horizontalLineTo(8.166f)
+ close()
+ moveTo(19.833f, 16.716f)
+ curveTo(19.256f, 16.716f, 18.767f, 16.854f, 18.347f, 17.081f)
+ curveTo(19.71f, 18.249f, 20.488f, 19.897f, 20.937f, 21.383f)
+ horizontalLineTo(23.211f)
+ curveTo(23.575f, 21.383f, 23.76f, 21.096f, 23.698f, 20.844f)
+ lineTo(23.612f, 20.525f)
+ curveTo(23.394f, 19.759f, 23.042f, 18.858f, 22.491f, 18.125f)
+ curveTo(21.874f, 17.303f, 21.032f, 16.716f, 19.833f, 16.716f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(8.166f, 15.551f)
+ curveTo(9.224f, 15.538f, 10.249f, 15.925f, 11.034f, 16.634f)
+ lineTo(11.597f, 17.144f)
+ lineTo(10.96f, 17.556f)
+ curveTo(9.382f, 18.579f, 8.524f, 20.406f, 8.067f, 22.117f)
+ lineTo(7.952f, 22.55f)
+ horizontalLineTo(4.789f)
+ curveTo(3.772f, 22.55f, 2.9f, 21.649f, 3.17f, 20.562f)
+ lineTo(3.172f, 20.56f)
+ curveTo(3.395f, 19.67f, 3.812f, 18.441f, 4.576f, 17.424f)
+ curveTo(5.351f, 16.392f, 6.506f, 15.554f, 8.158f, 15.551f)
+ curveTo(8.16f, 15.551f, 8.162f, 15.551f, 8.164f, 15.551f)
+ lineTo(8.166f, 15.55f)
+ verticalLineTo(15.551f)
+ close()
+ moveTo(19.833f, 15.55f)
+ curveTo(21.49f, 15.55f, 22.648f, 16.39f, 23.424f, 17.424f)
+ curveTo(24.093f, 18.314f, 24.496f, 19.367f, 24.736f, 20.212f)
+ lineTo(24.829f, 20.56f)
+ verticalLineTo(20.562f)
+ curveTo(25.1f, 21.649f, 24.228f, 22.55f, 23.211f, 22.55f)
+ horizontalLineTo(20.049f)
+ lineTo(19.933f, 22.117f)
+ curveTo(19.477f, 20.407f, 18.619f, 18.579f, 17.041f, 17.556f)
+ lineTo(16.397f, 17.139f)
+ lineTo(16.971f, 16.63f)
+ curveTo(17.7f, 15.985f, 18.644f, 15.55f, 19.833f, 15.55f)
+ close()
+ moveTo(8.166f, 16.716f)
+ curveTo(6.967f, 16.717f, 6.125f, 17.303f, 5.508f, 18.125f)
+ curveTo(4.879f, 18.963f, 4.51f, 20.019f, 4.303f, 20.844f)
+ curveTo(4.241f, 21.096f, 4.426f, 21.383f, 4.789f, 21.383f)
+ horizontalLineTo(7.065f)
+ curveTo(7.513f, 19.898f, 8.29f, 18.249f, 9.652f, 17.081f)
+ curveTo(9.201f, 16.839f, 8.694f, 16.709f, 8.174f, 16.716f)
+ horizontalLineTo(8.166f)
+ close()
+ moveTo(19.833f, 16.716f)
+ curveTo(19.256f, 16.716f, 18.767f, 16.854f, 18.347f, 17.081f)
+ curveTo(19.71f, 18.249f, 20.488f, 19.897f, 20.937f, 21.383f)
+ horizontalLineTo(23.211f)
+ curveTo(23.575f, 21.383f, 23.76f, 21.096f, 23.698f, 20.844f)
+ lineTo(23.612f, 20.525f)
+ curveTo(23.394f, 19.759f, 23.042f, 18.858f, 22.491f, 18.125f)
+ curveTo(21.874f, 17.303f, 21.032f, 16.716f, 19.833f, 16.716f)
+ close()
+ }
+ }
+ .build()
+ return _explore!!
+ }
+
+private var _explore: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.Explore, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Gallery.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Gallery.kt
new file mode 100644
index 0000000..c11e911
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Gallery.kt
@@ -0,0 +1,305 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.EvenOdd
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.Gallery: ImageVector
+ get() {
+ if (_gallery != null) {
+ return _gallery!!
+ }
+ _gallery = Builder(name = "Gallery", defaultWidth = 22.0.dp, defaultHeight = 22.0.dp,
+ viewportWidth = 22.0f, viewportHeight = 22.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(11.25f, 0.75f)
+ curveTo(13.121f, 0.75f, 14.576f, 0.749f, 15.706f, 0.901f)
+ curveTo(16.851f, 1.055f, 17.738f, 1.374f, 18.432f, 2.068f)
+ curveTo(19.125f, 2.763f, 19.444f, 3.65f, 19.599f, 4.795f)
+ curveTo(19.751f, 5.925f, 19.75f, 7.379f, 19.75f, 9.25f)
+ verticalLineTo(11.25f)
+ curveTo(19.75f, 11.293f, 19.749f, 11.336f, 19.749f, 11.379f)
+ curveTo(19.434f, 11.153f, 19.1f, 10.958f, 18.75f, 10.797f)
+ verticalLineTo(9.25f)
+ curveTo(18.75f, 7.35f, 18.749f, 5.976f, 18.607f, 4.928f)
+ curveTo(18.468f, 3.894f, 18.202f, 3.253f, 17.725f, 2.775f)
+ curveTo(17.247f, 2.298f, 16.606f, 2.031f, 15.572f, 1.892f)
+ curveTo(14.524f, 1.751f, 13.15f, 1.75f, 11.25f, 1.75f)
+ horizontalLineTo(9.25f)
+ curveTo(7.35f, 1.75f, 5.976f, 1.751f, 4.928f, 1.893f)
+ curveTo(3.894f, 2.032f, 3.253f, 2.298f, 2.775f, 2.775f)
+ curveTo(2.298f, 3.252f, 2.031f, 3.894f, 1.892f, 4.928f)
+ curveTo(1.751f, 5.976f, 1.75f, 7.35f, 1.75f, 9.25f)
+ verticalLineTo(11.25f)
+ curveTo(1.75f, 13.15f, 1.751f, 14.524f, 1.893f, 15.572f)
+ curveTo(2.032f, 16.606f, 2.298f, 17.247f, 2.775f, 17.725f)
+ curveTo(3.252f, 18.202f, 3.894f, 18.469f, 4.928f, 18.608f)
+ curveTo(5.976f, 18.749f, 7.35f, 18.75f, 9.25f, 18.75f)
+ horizontalLineTo(10.797f)
+ curveTo(10.958f, 19.1f, 11.153f, 19.434f, 11.379f, 19.749f)
+ curveTo(11.336f, 19.749f, 11.293f, 19.75f, 11.25f, 19.75f)
+ horizontalLineTo(9.25f)
+ curveTo(7.379f, 19.75f, 5.924f, 19.751f, 4.794f, 19.599f)
+ curveTo(3.65f, 19.445f, 2.762f, 19.126f, 2.068f, 18.432f)
+ curveTo(1.375f, 17.737f, 1.055f, 16.85f, 0.901f, 15.705f)
+ curveTo(0.749f, 14.575f, 0.75f, 13.121f, 0.75f, 11.25f)
+ verticalLineTo(9.25f)
+ curveTo(0.75f, 7.379f, 0.749f, 5.924f, 0.901f, 4.794f)
+ curveTo(1.055f, 3.65f, 1.374f, 2.762f, 2.068f, 2.068f)
+ curveTo(2.763f, 1.375f, 3.65f, 1.055f, 4.795f, 0.901f)
+ curveTo(5.925f, 0.749f, 7.379f, 0.75f, 9.25f, 0.75f)
+ horizontalLineTo(11.25f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(11.25f, 0.75f)
+ curveTo(13.121f, 0.75f, 14.576f, 0.749f, 15.706f, 0.901f)
+ curveTo(16.851f, 1.055f, 17.738f, 1.374f, 18.432f, 2.068f)
+ curveTo(19.125f, 2.763f, 19.444f, 3.65f, 19.599f, 4.795f)
+ curveTo(19.751f, 5.925f, 19.75f, 7.379f, 19.75f, 9.25f)
+ verticalLineTo(11.25f)
+ curveTo(19.75f, 11.293f, 19.749f, 11.336f, 19.749f, 11.379f)
+ curveTo(19.434f, 11.153f, 19.1f, 10.958f, 18.75f, 10.797f)
+ verticalLineTo(9.25f)
+ curveTo(18.75f, 7.35f, 18.749f, 5.976f, 18.607f, 4.928f)
+ curveTo(18.468f, 3.894f, 18.202f, 3.253f, 17.725f, 2.775f)
+ curveTo(17.247f, 2.298f, 16.606f, 2.031f, 15.572f, 1.892f)
+ curveTo(14.524f, 1.751f, 13.15f, 1.75f, 11.25f, 1.75f)
+ horizontalLineTo(9.25f)
+ curveTo(7.35f, 1.75f, 5.976f, 1.751f, 4.928f, 1.893f)
+ curveTo(3.894f, 2.032f, 3.253f, 2.298f, 2.775f, 2.775f)
+ curveTo(2.298f, 3.252f, 2.031f, 3.894f, 1.892f, 4.928f)
+ curveTo(1.751f, 5.976f, 1.75f, 7.35f, 1.75f, 9.25f)
+ verticalLineTo(11.25f)
+ curveTo(1.75f, 13.15f, 1.751f, 14.524f, 1.893f, 15.572f)
+ curveTo(2.032f, 16.606f, 2.298f, 17.247f, 2.775f, 17.725f)
+ curveTo(3.252f, 18.202f, 3.894f, 18.469f, 4.928f, 18.608f)
+ curveTo(5.976f, 18.749f, 7.35f, 18.75f, 9.25f, 18.75f)
+ horizontalLineTo(10.797f)
+ curveTo(10.958f, 19.1f, 11.153f, 19.434f, 11.379f, 19.749f)
+ curveTo(11.336f, 19.749f, 11.293f, 19.75f, 11.25f, 19.75f)
+ horizontalLineTo(9.25f)
+ curveTo(7.379f, 19.75f, 5.924f, 19.751f, 4.794f, 19.599f)
+ curveTo(3.65f, 19.445f, 2.762f, 19.126f, 2.068f, 18.432f)
+ curveTo(1.375f, 17.737f, 1.055f, 16.85f, 0.901f, 15.705f)
+ curveTo(0.749f, 14.575f, 0.75f, 13.121f, 0.75f, 11.25f)
+ verticalLineTo(9.25f)
+ curveTo(0.75f, 7.379f, 0.749f, 5.924f, 0.901f, 4.794f)
+ curveTo(1.055f, 3.65f, 1.374f, 2.762f, 2.068f, 2.068f)
+ curveTo(2.763f, 1.375f, 3.65f, 1.055f, 4.795f, 0.901f)
+ curveTo(5.925f, 0.749f, 7.379f, 0.75f, 9.25f, 0.75f)
+ horizontalLineTo(11.25f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(6.366f, 8.262f)
+ curveTo(6.825f, 8.223f, 7.285f, 8.312f, 7.696f, 8.518f)
+ curveTo(8.155f, 8.748f, 8.474f, 9.153f, 8.761f, 9.663f)
+ curveTo(9.043f, 10.164f, 9.333f, 10.841f, 9.697f, 11.691f)
+ lineTo(9.71f, 11.72f)
+ lineTo(9.763f, 11.844f)
+ curveTo(9.878f, 12.127f, 10.004f, 12.406f, 10.143f, 12.679f)
+ curveTo(10.243f, 12.862f, 10.3f, 12.908f, 10.324f, 12.924f)
+ curveTo(10.428f, 12.988f, 10.552f, 13.012f, 10.673f, 12.991f)
+ curveTo(10.702f, 12.986f, 10.772f, 12.964f, 10.933f, 12.831f)
+ curveTo(11.096f, 12.697f, 11.296f, 12.497f, 11.596f, 12.197f)
+ lineTo(11.611f, 12.181f)
+ curveTo(12.012f, 11.781f, 12.335f, 11.458f, 12.618f, 11.219f)
+ curveTo(12.911f, 10.973f, 13.195f, 10.785f, 13.528f, 10.685f)
+ curveTo(13.778f, 10.609f, 14.035f, 10.575f, 14.293f, 10.579f)
+ curveTo(13.441f, 10.873f, 12.658f, 11.358f, 12.008f, 12.008f)
+ curveTo(11.435f, 12.58f, 10.99f, 13.257f, 10.691f, 13.993f)
+ curveTo(10.379f, 14.015f, 10.066f, 13.941f, 9.798f, 13.774f)
+ curveTo(9.554f, 13.623f, 9.394f, 13.396f, 9.264f, 13.157f)
+ curveTo(9.138f, 12.925f, 9.008f, 12.621f, 8.854f, 12.261f)
+ lineTo(8.844f, 12.238f)
+ lineTo(8.79f, 12.113f)
+ curveTo(8.411f, 11.228f, 8.142f, 10.603f, 7.889f, 10.153f)
+ curveTo(7.638f, 9.706f, 7.442f, 9.509f, 7.248f, 9.412f)
+ curveTo(7.001f, 9.288f, 6.725f, 9.235f, 6.449f, 9.258f)
+ curveTo(6.233f, 9.277f, 5.978f, 9.387f, 5.578f, 9.708f)
+ curveTo(5.177f, 10.031f, 4.694f, 10.513f, 4.013f, 11.193f)
+ lineTo(3.25f, 11.957f)
+ verticalLineTo(10.543f)
+ lineTo(3.306f, 10.486f)
+ lineTo(3.328f, 10.465f)
+ curveTo(3.982f, 9.81f, 4.503f, 9.29f, 4.951f, 8.929f)
+ curveTo(5.407f, 8.562f, 5.853f, 8.305f, 6.366f, 8.262f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(6.366f, 8.262f)
+ curveTo(6.825f, 8.223f, 7.285f, 8.312f, 7.696f, 8.518f)
+ curveTo(8.155f, 8.748f, 8.474f, 9.153f, 8.761f, 9.663f)
+ curveTo(9.043f, 10.164f, 9.333f, 10.841f, 9.697f, 11.691f)
+ lineTo(9.71f, 11.72f)
+ lineTo(9.763f, 11.844f)
+ curveTo(9.878f, 12.127f, 10.004f, 12.406f, 10.143f, 12.679f)
+ curveTo(10.243f, 12.862f, 10.3f, 12.908f, 10.324f, 12.924f)
+ curveTo(10.428f, 12.988f, 10.552f, 13.012f, 10.673f, 12.991f)
+ curveTo(10.702f, 12.986f, 10.772f, 12.964f, 10.933f, 12.831f)
+ curveTo(11.096f, 12.697f, 11.296f, 12.497f, 11.596f, 12.197f)
+ lineTo(11.611f, 12.181f)
+ curveTo(12.012f, 11.781f, 12.335f, 11.458f, 12.618f, 11.219f)
+ curveTo(12.911f, 10.973f, 13.195f, 10.785f, 13.528f, 10.685f)
+ curveTo(13.778f, 10.609f, 14.035f, 10.575f, 14.293f, 10.579f)
+ curveTo(13.441f, 10.873f, 12.658f, 11.358f, 12.008f, 12.008f)
+ curveTo(11.435f, 12.58f, 10.99f, 13.257f, 10.691f, 13.993f)
+ curveTo(10.379f, 14.015f, 10.066f, 13.941f, 9.798f, 13.774f)
+ curveTo(9.554f, 13.623f, 9.394f, 13.396f, 9.264f, 13.157f)
+ curveTo(9.138f, 12.925f, 9.008f, 12.621f, 8.854f, 12.261f)
+ lineTo(8.844f, 12.238f)
+ lineTo(8.79f, 12.113f)
+ curveTo(8.411f, 11.228f, 8.142f, 10.603f, 7.889f, 10.153f)
+ curveTo(7.638f, 9.706f, 7.442f, 9.509f, 7.248f, 9.412f)
+ curveTo(7.001f, 9.288f, 6.725f, 9.235f, 6.449f, 9.258f)
+ curveTo(6.233f, 9.277f, 5.978f, 9.387f, 5.578f, 9.708f)
+ curveTo(5.177f, 10.031f, 4.694f, 10.513f, 4.013f, 11.193f)
+ lineTo(3.25f, 11.957f)
+ verticalLineTo(10.543f)
+ lineTo(3.306f, 10.486f)
+ lineTo(3.328f, 10.465f)
+ curveTo(3.982f, 9.81f, 4.503f, 9.29f, 4.951f, 8.929f)
+ curveTo(5.407f, 8.562f, 5.853f, 8.305f, 6.366f, 8.262f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(14.75f, 4.25f)
+ curveTo(15.578f, 4.25f, 16.25f, 4.922f, 16.25f, 5.75f)
+ curveTo(16.25f, 6.578f, 15.578f, 7.25f, 14.75f, 7.25f)
+ curveTo(13.922f, 7.25f, 13.25f, 6.578f, 13.25f, 5.75f)
+ curveTo(13.25f, 4.922f, 13.922f, 4.25f, 14.75f, 4.25f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(14.75f, 4.25f)
+ curveTo(15.578f, 4.25f, 16.25f, 4.922f, 16.25f, 5.75f)
+ curveTo(16.25f, 6.578f, 15.578f, 7.25f, 14.75f, 7.25f)
+ curveTo(13.922f, 7.25f, 13.25f, 6.578f, 13.25f, 5.75f)
+ curveTo(13.25f, 4.922f, 13.922f, 4.25f, 14.75f, 4.25f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = EvenOdd) {
+ moveTo(16.25f, 21.25f)
+ curveTo(17.576f, 21.25f, 18.848f, 20.723f, 19.785f, 19.785f)
+ curveTo(20.723f, 18.848f, 21.25f, 17.576f, 21.25f, 16.25f)
+ curveTo(21.25f, 14.924f, 20.723f, 13.652f, 19.785f, 12.715f)
+ curveTo(18.848f, 11.777f, 17.576f, 11.25f, 16.25f, 11.25f)
+ curveTo(14.924f, 11.25f, 13.652f, 11.777f, 12.715f, 12.715f)
+ curveTo(11.777f, 13.652f, 11.25f, 14.924f, 11.25f, 16.25f)
+ curveTo(11.25f, 17.576f, 11.777f, 18.848f, 12.715f, 19.785f)
+ curveTo(13.652f, 20.723f, 14.924f, 21.25f, 16.25f, 21.25f)
+ close()
+ moveTo(16.25f, 13.075f)
+ curveTo(16.371f, 13.075f, 16.486f, 13.122f, 16.571f, 13.208f)
+ curveTo(16.657f, 13.293f, 16.705f, 13.408f, 16.705f, 13.529f)
+ verticalLineTo(15.795f)
+ horizontalLineTo(18.971f)
+ curveTo(19.091f, 15.795f, 19.207f, 15.843f, 19.292f, 15.929f)
+ curveTo(19.378f, 16.014f, 19.426f, 16.129f, 19.426f, 16.25f)
+ curveTo(19.426f, 16.371f, 19.378f, 16.486f, 19.292f, 16.571f)
+ curveTo(19.207f, 16.657f, 19.091f, 16.705f, 18.971f, 16.705f)
+ horizontalLineTo(16.705f)
+ verticalLineTo(18.972f)
+ curveTo(16.705f, 19.092f, 16.657f, 19.208f, 16.571f, 19.293f)
+ curveTo(16.486f, 19.378f, 16.371f, 19.426f, 16.25f, 19.426f)
+ curveTo(16.129f, 19.426f, 16.014f, 19.378f, 15.929f, 19.293f)
+ curveTo(15.843f, 19.208f, 15.795f, 19.092f, 15.795f, 18.972f)
+ verticalLineTo(16.705f)
+ horizontalLineTo(13.529f)
+ curveTo(13.408f, 16.705f, 13.293f, 16.657f, 13.208f, 16.571f)
+ curveTo(13.122f, 16.486f, 13.075f, 16.371f, 13.075f, 16.25f)
+ curveTo(13.075f, 16.129f, 13.122f, 16.014f, 13.208f, 15.929f)
+ curveTo(13.293f, 15.843f, 13.408f, 15.795f, 13.529f, 15.795f)
+ horizontalLineTo(15.795f)
+ verticalLineTo(13.529f)
+ curveTo(15.795f, 13.408f, 15.843f, 13.293f, 15.929f, 13.208f)
+ curveTo(16.014f, 13.122f, 16.129f, 13.075f, 16.25f, 13.075f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = EvenOdd) {
+ moveTo(16.25f, 21.25f)
+ curveTo(17.576f, 21.25f, 18.848f, 20.723f, 19.785f, 19.785f)
+ curveTo(20.723f, 18.848f, 21.25f, 17.576f, 21.25f, 16.25f)
+ curveTo(21.25f, 14.924f, 20.723f, 13.652f, 19.785f, 12.715f)
+ curveTo(18.848f, 11.777f, 17.576f, 11.25f, 16.25f, 11.25f)
+ curveTo(14.924f, 11.25f, 13.652f, 11.777f, 12.715f, 12.715f)
+ curveTo(11.777f, 13.652f, 11.25f, 14.924f, 11.25f, 16.25f)
+ curveTo(11.25f, 17.576f, 11.777f, 18.848f, 12.715f, 19.785f)
+ curveTo(13.652f, 20.723f, 14.924f, 21.25f, 16.25f, 21.25f)
+ close()
+ moveTo(16.25f, 13.075f)
+ curveTo(16.371f, 13.075f, 16.486f, 13.122f, 16.571f, 13.208f)
+ curveTo(16.657f, 13.293f, 16.705f, 13.408f, 16.705f, 13.529f)
+ verticalLineTo(15.795f)
+ horizontalLineTo(18.971f)
+ curveTo(19.091f, 15.795f, 19.207f, 15.843f, 19.292f, 15.929f)
+ curveTo(19.378f, 16.014f, 19.426f, 16.129f, 19.426f, 16.25f)
+ curveTo(19.426f, 16.371f, 19.378f, 16.486f, 19.292f, 16.571f)
+ curveTo(19.207f, 16.657f, 19.091f, 16.705f, 18.971f, 16.705f)
+ horizontalLineTo(16.705f)
+ verticalLineTo(18.972f)
+ curveTo(16.705f, 19.092f, 16.657f, 19.208f, 16.571f, 19.293f)
+ curveTo(16.486f, 19.378f, 16.371f, 19.426f, 16.25f, 19.426f)
+ curveTo(16.129f, 19.426f, 16.014f, 19.378f, 15.929f, 19.293f)
+ curveTo(15.843f, 19.208f, 15.795f, 19.092f, 15.795f, 18.972f)
+ verticalLineTo(16.705f)
+ horizontalLineTo(13.529f)
+ curveTo(13.408f, 16.705f, 13.293f, 16.657f, 13.208f, 16.571f)
+ curveTo(13.122f, 16.486f, 13.075f, 16.371f, 13.075f, 16.25f)
+ curveTo(13.075f, 16.129f, 13.122f, 16.014f, 13.208f, 15.929f)
+ curveTo(13.293f, 15.843f, 13.408f, 15.795f, 13.529f, 15.795f)
+ horizontalLineTo(15.795f)
+ verticalLineTo(13.529f)
+ curveTo(15.795f, 13.408f, 15.843f, 13.293f, 15.929f, 13.208f)
+ curveTo(16.014f, 13.122f, 16.129f, 13.075f, 16.25f, 13.075f)
+ close()
+ }
+ }
+ .build()
+ return _gallery!!
+ }
+
+private var _gallery: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.Gallery, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Home.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Home.kt
new file mode 100644
index 0000000..c4779e5
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Home.kt
@@ -0,0 +1,216 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.Home: ImageVector
+ get() {
+ if (_home != null) {
+ return _home!!
+ }
+ _home = Builder(name = "Home", defaultWidth = 29.0.dp, defaultHeight = 29.0.dp,
+ viewportWidth = 29.0f, viewportHeight = 29.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(23.25f, 12.167f)
+ curveTo(23.25f, 12.012f, 23.189f, 11.864f, 23.079f, 11.755f)
+ curveTo(22.97f, 11.645f, 22.822f, 11.584f, 22.667f, 11.584f)
+ curveTo(22.512f, 11.584f, 22.364f, 11.645f, 22.254f, 11.755f)
+ curveTo(22.145f, 11.864f, 22.083f, 12.012f, 22.083f, 12.167f)
+ horizontalLineTo(23.25f)
+ close()
+ moveTo(6.917f, 12.167f)
+ curveTo(6.917f, 12.012f, 6.855f, 11.864f, 6.746f, 11.755f)
+ curveTo(6.636f, 11.645f, 6.488f, 11.584f, 6.333f, 11.584f)
+ curveTo(6.179f, 11.584f, 6.03f, 11.645f, 5.921f, 11.755f)
+ curveTo(5.812f, 11.864f, 5.75f, 12.012f, 5.75f, 12.167f)
+ horizontalLineTo(6.917f)
+ close()
+ moveTo(24.587f, 14.913f)
+ curveTo(24.697f, 15.023f, 24.845f, 15.085f, 25.0f, 15.085f)
+ curveTo(25.155f, 15.085f, 25.304f, 15.023f, 25.413f, 14.913f)
+ curveTo(25.523f, 14.804f, 25.584f, 14.655f, 25.584f, 14.5f)
+ curveTo(25.584f, 14.345f, 25.523f, 14.197f, 25.413f, 14.087f)
+ lineTo(24.587f, 14.913f)
+ close()
+ moveTo(14.5f, 4.0f)
+ lineTo(14.913f, 3.587f)
+ curveTo(14.859f, 3.533f, 14.795f, 3.49f, 14.724f, 3.461f)
+ curveTo(14.653f, 3.431f, 14.577f, 3.416f, 14.5f, 3.416f)
+ curveTo(14.423f, 3.416f, 14.347f, 3.431f, 14.276f, 3.461f)
+ curveTo(14.206f, 3.49f, 14.141f, 3.533f, 14.087f, 3.587f)
+ lineTo(14.5f, 4.0f)
+ close()
+ moveTo(3.587f, 14.087f)
+ curveTo(3.478f, 14.197f, 3.416f, 14.345f, 3.416f, 14.5f)
+ curveTo(3.416f, 14.655f, 3.478f, 14.804f, 3.587f, 14.913f)
+ curveTo(3.697f, 15.023f, 3.845f, 15.085f, 4.0f, 15.085f)
+ curveTo(4.155f, 15.085f, 4.304f, 15.023f, 4.413f, 14.913f)
+ lineTo(3.587f, 14.087f)
+ close()
+ moveTo(8.667f, 25.584f)
+ horizontalLineTo(20.333f)
+ verticalLineTo(24.417f)
+ horizontalLineTo(8.667f)
+ verticalLineTo(25.584f)
+ close()
+ moveTo(23.25f, 22.667f)
+ verticalLineTo(12.167f)
+ horizontalLineTo(22.083f)
+ verticalLineTo(22.667f)
+ horizontalLineTo(23.25f)
+ close()
+ moveTo(6.917f, 22.667f)
+ verticalLineTo(12.167f)
+ horizontalLineTo(5.75f)
+ verticalLineTo(22.667f)
+ horizontalLineTo(6.917f)
+ close()
+ moveTo(25.413f, 14.087f)
+ lineTo(14.913f, 3.587f)
+ lineTo(14.087f, 4.413f)
+ lineTo(24.587f, 14.913f)
+ lineTo(25.413f, 14.087f)
+ close()
+ moveTo(14.087f, 3.587f)
+ lineTo(3.587f, 14.087f)
+ lineTo(4.413f, 14.913f)
+ lineTo(14.913f, 4.413f)
+ lineTo(14.087f, 3.587f)
+ close()
+ moveTo(20.333f, 25.584f)
+ curveTo(21.107f, 25.584f, 21.849f, 25.276f, 22.396f, 24.729f)
+ curveTo(22.943f, 24.183f, 23.25f, 23.441f, 23.25f, 22.667f)
+ horizontalLineTo(22.083f)
+ curveTo(22.083f, 23.131f, 21.899f, 23.576f, 21.571f, 23.904f)
+ curveTo(21.243f, 24.233f, 20.798f, 24.417f, 20.333f, 24.417f)
+ verticalLineTo(25.584f)
+ close()
+ moveTo(8.667f, 24.417f)
+ curveTo(8.203f, 24.417f, 7.758f, 24.233f, 7.429f, 23.904f)
+ curveTo(7.101f, 23.576f, 6.917f, 23.131f, 6.917f, 22.667f)
+ horizontalLineTo(5.75f)
+ curveTo(5.75f, 23.441f, 6.057f, 24.183f, 6.604f, 24.729f)
+ curveTo(7.151f, 25.276f, 7.893f, 25.584f, 8.667f, 25.584f)
+ verticalLineTo(24.417f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(23.25f, 12.167f)
+ curveTo(23.25f, 12.012f, 23.189f, 11.864f, 23.079f, 11.755f)
+ curveTo(22.97f, 11.645f, 22.822f, 11.584f, 22.667f, 11.584f)
+ curveTo(22.512f, 11.584f, 22.364f, 11.645f, 22.254f, 11.755f)
+ curveTo(22.145f, 11.864f, 22.083f, 12.012f, 22.083f, 12.167f)
+ horizontalLineTo(23.25f)
+ close()
+ moveTo(6.917f, 12.167f)
+ curveTo(6.917f, 12.012f, 6.855f, 11.864f, 6.746f, 11.755f)
+ curveTo(6.636f, 11.645f, 6.488f, 11.584f, 6.333f, 11.584f)
+ curveTo(6.179f, 11.584f, 6.03f, 11.645f, 5.921f, 11.755f)
+ curveTo(5.812f, 11.864f, 5.75f, 12.012f, 5.75f, 12.167f)
+ horizontalLineTo(6.917f)
+ close()
+ moveTo(24.587f, 14.913f)
+ curveTo(24.697f, 15.023f, 24.845f, 15.085f, 25.0f, 15.085f)
+ curveTo(25.155f, 15.085f, 25.304f, 15.023f, 25.413f, 14.913f)
+ curveTo(25.523f, 14.804f, 25.584f, 14.655f, 25.584f, 14.5f)
+ curveTo(25.584f, 14.345f, 25.523f, 14.197f, 25.413f, 14.087f)
+ lineTo(24.587f, 14.913f)
+ close()
+ moveTo(14.5f, 4.0f)
+ lineTo(14.913f, 3.587f)
+ curveTo(14.859f, 3.533f, 14.795f, 3.49f, 14.724f, 3.461f)
+ curveTo(14.653f, 3.431f, 14.577f, 3.416f, 14.5f, 3.416f)
+ curveTo(14.423f, 3.416f, 14.347f, 3.431f, 14.276f, 3.461f)
+ curveTo(14.206f, 3.49f, 14.141f, 3.533f, 14.087f, 3.587f)
+ lineTo(14.5f, 4.0f)
+ close()
+ moveTo(3.587f, 14.087f)
+ curveTo(3.478f, 14.197f, 3.416f, 14.345f, 3.416f, 14.5f)
+ curveTo(3.416f, 14.655f, 3.478f, 14.804f, 3.587f, 14.913f)
+ curveTo(3.697f, 15.023f, 3.845f, 15.085f, 4.0f, 15.085f)
+ curveTo(4.155f, 15.085f, 4.304f, 15.023f, 4.413f, 14.913f)
+ lineTo(3.587f, 14.087f)
+ close()
+ moveTo(8.667f, 25.584f)
+ horizontalLineTo(20.333f)
+ verticalLineTo(24.417f)
+ horizontalLineTo(8.667f)
+ verticalLineTo(25.584f)
+ close()
+ moveTo(23.25f, 22.667f)
+ verticalLineTo(12.167f)
+ horizontalLineTo(22.083f)
+ verticalLineTo(22.667f)
+ horizontalLineTo(23.25f)
+ close()
+ moveTo(6.917f, 22.667f)
+ verticalLineTo(12.167f)
+ horizontalLineTo(5.75f)
+ verticalLineTo(22.667f)
+ horizontalLineTo(6.917f)
+ close()
+ moveTo(25.413f, 14.087f)
+ lineTo(14.913f, 3.587f)
+ lineTo(14.087f, 4.413f)
+ lineTo(24.587f, 14.913f)
+ lineTo(25.413f, 14.087f)
+ close()
+ moveTo(14.087f, 3.587f)
+ lineTo(3.587f, 14.087f)
+ lineTo(4.413f, 14.913f)
+ lineTo(14.913f, 4.413f)
+ lineTo(14.087f, 3.587f)
+ close()
+ moveTo(20.333f, 25.584f)
+ curveTo(21.107f, 25.584f, 21.849f, 25.276f, 22.396f, 24.729f)
+ curveTo(22.943f, 24.183f, 23.25f, 23.441f, 23.25f, 22.667f)
+ horizontalLineTo(22.083f)
+ curveTo(22.083f, 23.131f, 21.899f, 23.576f, 21.571f, 23.904f)
+ curveTo(21.243f, 24.233f, 20.798f, 24.417f, 20.333f, 24.417f)
+ verticalLineTo(25.584f)
+ close()
+ moveTo(8.667f, 24.417f)
+ curveTo(8.203f, 24.417f, 7.758f, 24.233f, 7.429f, 23.904f)
+ curveTo(7.101f, 23.576f, 6.917f, 23.131f, 6.917f, 22.667f)
+ horizontalLineTo(5.75f)
+ curveTo(5.75f, 23.441f, 6.057f, 24.183f, 6.604f, 24.729f)
+ curveTo(7.151f, 25.276f, 7.893f, 25.584f, 8.667f, 25.584f)
+ verticalLineTo(24.417f)
+ close()
+ }
+ }
+ .build()
+ return _home!!
+ }
+
+private var _home: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.Home, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Kakao.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Kakao.kt
new file mode 100644
index 0000000..93ab59c
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Kakao.kt
@@ -0,0 +1,55 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType.Companion.EvenOdd
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.Kakao: ImageVector
+ get() {
+ if (_kakao != null) {
+ return _kakao!!
+ }
+ _kakao = Builder(name = "Kakao", defaultWidth = 20.0.dp, defaultHeight = 20.0.dp,
+ viewportWidth = 20.0f, viewportHeight = 20.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = EvenOdd) {
+ moveTo(10.0f, 0.667f)
+ curveTo(4.477f, 0.667f, 0.0f, 4.125f, 0.0f, 8.391f)
+ curveTo(0.0f, 11.044f, 1.732f, 13.383f, 4.369f, 14.774f)
+ lineTo(3.259f, 18.827f)
+ curveTo(3.161f, 19.185f, 3.571f, 19.471f, 3.885f, 19.263f)
+ lineTo(8.749f, 16.053f)
+ curveTo(9.159f, 16.093f, 9.576f, 16.116f, 10.0f, 16.116f)
+ curveTo(15.523f, 16.116f, 20.0f, 12.658f, 20.0f, 8.391f)
+ curveTo(20.0f, 4.125f, 15.523f, 0.667f, 10.0f, 0.667f)
+ close()
+ }
+ }
+ .build()
+ return _kakao!!
+ }
+
+private var _kakao: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.Kakao, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/More.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/More.kt
new file mode 100644
index 0000000..3af04d5
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/More.kt
@@ -0,0 +1,104 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.More: ImageVector
+ get() {
+ if (_more != null) {
+ return _more!!
+ }
+ _more = Builder(name = "More", defaultWidth = 21.0.dp, defaultHeight = 20.0.dp,
+ viewportWidth = 21.0f, viewportHeight = 20.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(5.11f, 10.0f)
+ curveTo(5.11f, 10.506f, 4.7f, 10.917f, 4.193f, 10.917f)
+ curveTo(3.687f, 10.917f, 3.277f, 10.506f, 3.277f, 10.0f)
+ curveTo(3.277f, 9.494f, 3.687f, 9.083f, 4.193f, 9.083f)
+ curveTo(4.7f, 9.083f, 5.11f, 9.494f, 5.11f, 10.0f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.2f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(5.11f, 10.0f)
+ curveTo(5.11f, 10.506f, 4.7f, 10.917f, 4.193f, 10.917f)
+ curveTo(3.687f, 10.917f, 3.277f, 10.506f, 3.277f, 10.0f)
+ curveTo(3.277f, 9.494f, 3.687f, 9.083f, 4.193f, 9.083f)
+ curveTo(4.7f, 9.083f, 5.11f, 9.494f, 5.11f, 10.0f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(11.693f, 10.0f)
+ curveTo(11.693f, 10.506f, 11.283f, 10.917f, 10.777f, 10.917f)
+ curveTo(10.27f, 10.917f, 9.86f, 10.506f, 9.86f, 10.0f)
+ curveTo(9.86f, 9.494f, 10.27f, 9.083f, 10.777f, 9.083f)
+ curveTo(11.283f, 9.083f, 11.693f, 9.494f, 11.693f, 10.0f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.2f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(11.693f, 10.0f)
+ curveTo(11.693f, 10.506f, 11.283f, 10.917f, 10.777f, 10.917f)
+ curveTo(10.27f, 10.917f, 9.86f, 10.506f, 9.86f, 10.0f)
+ curveTo(9.86f, 9.494f, 10.27f, 9.083f, 10.777f, 9.083f)
+ curveTo(11.283f, 9.083f, 11.693f, 9.494f, 11.693f, 10.0f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(18.277f, 10.0f)
+ curveTo(18.277f, 10.506f, 17.866f, 10.917f, 17.36f, 10.917f)
+ curveTo(16.854f, 10.917f, 16.443f, 10.506f, 16.443f, 10.0f)
+ curveTo(16.443f, 9.494f, 16.854f, 9.083f, 17.36f, 9.083f)
+ curveTo(17.866f, 9.083f, 18.277f, 9.494f, 18.277f, 10.0f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.2f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(18.277f, 10.0f)
+ curveTo(18.277f, 10.506f, 17.866f, 10.917f, 17.36f, 10.917f)
+ curveTo(16.854f, 10.917f, 16.443f, 10.506f, 16.443f, 10.0f)
+ curveTo(16.443f, 9.494f, 16.854f, 9.083f, 17.36f, 9.083f)
+ curveTo(17.866f, 9.083f, 18.277f, 9.494f, 18.277f, 10.0f)
+ close()
+ }
+ }
+ .build()
+ return _more!!
+ }
+
+private var _more: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.More, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/My.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/My.kt
new file mode 100644
index 0000000..f5fb11f
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/My.kt
@@ -0,0 +1,144 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.My: ImageVector
+ get() {
+ if (_my != null) {
+ return _my!!
+ }
+ _my = Builder(name = "My", defaultWidth = 29.0.dp, defaultHeight = 29.0.dp, viewportWidth =
+ 29.0f, viewportHeight = 29.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(17.417f, 12.166f)
+ curveTo(17.417f, 10.556f, 16.111f, 9.25f, 14.5f, 9.25f)
+ curveTo(12.889f, 9.25f, 11.583f, 10.556f, 11.583f, 12.166f)
+ curveTo(11.583f, 13.777f, 12.889f, 15.083f, 14.5f, 15.083f)
+ curveTo(16.111f, 15.083f, 17.417f, 13.777f, 17.417f, 12.166f)
+ close()
+ moveTo(18.583f, 12.166f)
+ curveTo(18.583f, 14.422f, 16.755f, 16.25f, 14.5f, 16.25f)
+ curveTo(12.245f, 16.25f, 10.417f, 14.422f, 10.417f, 12.166f)
+ curveTo(10.417f, 9.911f, 12.245f, 8.083f, 14.5f, 8.083f)
+ curveTo(16.755f, 8.083f, 18.583f, 9.911f, 18.583f, 12.166f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(17.417f, 12.166f)
+ curveTo(17.417f, 10.556f, 16.111f, 9.25f, 14.5f, 9.25f)
+ curveTo(12.889f, 9.25f, 11.583f, 10.556f, 11.583f, 12.166f)
+ curveTo(11.583f, 13.777f, 12.889f, 15.083f, 14.5f, 15.083f)
+ curveTo(16.111f, 15.083f, 17.417f, 13.777f, 17.417f, 12.166f)
+ close()
+ moveTo(18.583f, 12.166f)
+ curveTo(18.583f, 14.422f, 16.755f, 16.25f, 14.5f, 16.25f)
+ curveTo(12.245f, 16.25f, 10.417f, 14.422f, 10.417f, 12.166f)
+ curveTo(10.417f, 9.911f, 12.245f, 8.083f, 14.5f, 8.083f)
+ curveTo(16.755f, 8.083f, 18.583f, 9.911f, 18.583f, 12.166f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(24.417f, 14.5f)
+ curveTo(24.417f, 9.023f, 19.977f, 4.583f, 14.5f, 4.583f)
+ curveTo(9.023f, 4.583f, 4.583f, 9.023f, 4.583f, 14.5f)
+ curveTo(4.583f, 19.977f, 9.023f, 24.417f, 14.5f, 24.417f)
+ curveTo(19.977f, 24.417f, 24.417f, 19.977f, 24.417f, 14.5f)
+ close()
+ moveTo(25.583f, 14.5f)
+ curveTo(25.583f, 20.621f, 20.621f, 25.583f, 14.5f, 25.583f)
+ curveTo(8.379f, 25.583f, 3.417f, 20.621f, 3.417f, 14.5f)
+ curveTo(3.417f, 8.379f, 8.379f, 3.417f, 14.5f, 3.417f)
+ curveTo(20.621f, 3.417f, 25.583f, 8.379f, 25.583f, 14.5f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(24.417f, 14.5f)
+ curveTo(24.417f, 9.023f, 19.977f, 4.583f, 14.5f, 4.583f)
+ curveTo(9.023f, 4.583f, 4.583f, 9.023f, 4.583f, 14.5f)
+ curveTo(4.583f, 19.977f, 9.023f, 24.417f, 14.5f, 24.417f)
+ curveTo(19.977f, 24.417f, 24.417f, 19.977f, 24.417f, 14.5f)
+ close()
+ moveTo(25.583f, 14.5f)
+ curveTo(25.583f, 20.621f, 20.621f, 25.583f, 14.5f, 25.583f)
+ curveTo(8.379f, 25.583f, 3.417f, 20.621f, 3.417f, 14.5f)
+ curveTo(3.417f, 8.379f, 8.379f, 3.417f, 14.5f, 3.417f)
+ curveTo(20.621f, 3.417f, 25.583f, 8.379f, 25.583f, 14.5f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(14.5f, 17.417f)
+ curveTo(16.195f, 17.417f, 17.853f, 17.866f, 19.218f, 18.708f)
+ curveTo(20.583f, 19.552f, 21.592f, 20.751f, 22.054f, 22.139f)
+ curveTo(22.156f, 22.445f, 21.99f, 22.775f, 21.685f, 22.877f)
+ curveTo(21.379f, 22.979f, 21.048f, 22.814f, 20.946f, 22.508f)
+ curveTo(20.582f, 21.415f, 19.771f, 20.421f, 18.605f, 19.701f)
+ curveTo(17.438f, 18.98f, 15.995f, 18.583f, 14.5f, 18.583f)
+ curveTo(13.005f, 18.583f, 11.561f, 18.98f, 10.395f, 19.701f)
+ curveTo(9.23f, 20.421f, 8.418f, 21.416f, 8.054f, 22.508f)
+ curveTo(7.952f, 22.814f, 7.621f, 22.979f, 7.315f, 22.877f)
+ curveTo(7.01f, 22.775f, 6.845f, 22.445f, 6.946f, 22.139f)
+ curveTo(7.408f, 20.751f, 8.418f, 19.551f, 9.782f, 18.708f)
+ curveTo(11.146f, 17.866f, 12.805f, 17.417f, 14.5f, 17.417f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(14.5f, 17.417f)
+ curveTo(16.195f, 17.417f, 17.853f, 17.866f, 19.218f, 18.708f)
+ curveTo(20.583f, 19.552f, 21.592f, 20.751f, 22.054f, 22.139f)
+ curveTo(22.156f, 22.445f, 21.99f, 22.775f, 21.685f, 22.877f)
+ curveTo(21.379f, 22.979f, 21.048f, 22.814f, 20.946f, 22.508f)
+ curveTo(20.582f, 21.415f, 19.771f, 20.421f, 18.605f, 19.701f)
+ curveTo(17.438f, 18.98f, 15.995f, 18.583f, 14.5f, 18.583f)
+ curveTo(13.005f, 18.583f, 11.561f, 18.98f, 10.395f, 19.701f)
+ curveTo(9.23f, 20.421f, 8.418f, 21.416f, 8.054f, 22.508f)
+ curveTo(7.952f, 22.814f, 7.621f, 22.979f, 7.315f, 22.877f)
+ curveTo(7.01f, 22.775f, 6.845f, 22.445f, 6.946f, 22.139f)
+ curveTo(7.408f, 20.751f, 8.418f, 19.551f, 9.782f, 18.708f)
+ curveTo(11.146f, 17.866f, 12.805f, 17.417f, 14.5f, 17.417f)
+ close()
+ }
+ }
+ .build()
+ return _my!!
+ }
+
+private var _my: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.My, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Pen.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Pen.kt
new file mode 100644
index 0000000..8299864
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Pen.kt
@@ -0,0 +1,132 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.Pen: ImageVector
+ get() {
+ if (_pen != null) {
+ return _pen!!
+ }
+ _pen = Builder(name = "Pen", defaultWidth = 20.0.dp, defaultHeight = 20.0.dp, viewportWidth
+ = 20.0f, viewportHeight = 20.0f).apply {
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(15.026f, 7.164f)
+ curveTo(15.026f, 6.992f, 14.956f, 6.835f, 14.817f, 6.653f)
+ curveTo(14.682f, 6.476f, 14.48f, 6.273f, 14.212f, 6.005f)
+ lineTo(14.192f, 5.985f)
+ lineTo(14.029f, 5.822f)
+ curveTo(13.762f, 5.555f, 13.559f, 5.353f, 13.382f, 5.218f)
+ lineTo(13.249f, 5.127f)
+ curveTo(13.12f, 5.048f, 13.0f, 5.009f, 12.871f, 5.009f)
+ curveTo(12.699f, 5.009f, 12.543f, 5.078f, 12.361f, 5.217f)
+ curveTo(12.184f, 5.352f, 11.98f, 5.555f, 11.712f, 5.822f)
+ lineTo(5.693f, 11.842f)
+ lineTo(5.665f, 11.869f)
+ curveTo(5.524f, 12.011f, 5.45f, 12.089f, 5.399f, 12.178f)
+ lineTo(5.398f, 12.179f)
+ curveTo(5.346f, 12.271f, 5.318f, 12.376f, 5.269f, 12.575f)
+ lineTo(5.259f, 12.611f)
+ lineTo(4.698f, 14.852f)
+ curveTo(4.67f, 14.959f, 4.647f, 15.068f, 4.631f, 15.177f)
+ curveTo(4.627f, 15.215f, 4.629f, 15.251f, 4.636f, 15.282f)
+ curveTo(4.642f, 15.311f, 4.654f, 15.335f, 4.677f, 15.359f)
+ curveTo(4.7f, 15.382f, 4.724f, 15.393f, 4.751f, 15.399f)
+ curveTo(4.782f, 15.406f, 4.818f, 15.406f, 4.857f, 15.403f)
+ curveTo(4.972f, 15.387f, 5.087f, 15.363f, 5.199f, 15.332f)
+ lineTo(5.216f, 15.327f)
+ lineTo(7.423f, 14.775f)
+ lineTo(7.434f, 14.773f)
+ lineTo(7.459f, 14.767f)
+ curveTo(7.657f, 14.717f, 7.764f, 14.688f, 7.855f, 14.637f)
+ curveTo(7.947f, 14.584f, 8.027f, 14.507f, 8.17f, 14.365f)
+ lineTo(8.197f, 14.337f)
+ lineTo(14.192f, 8.342f)
+ lineTo(14.212f, 8.322f)
+ curveTo(14.48f, 8.055f, 14.682f, 7.851f, 14.817f, 7.674f)
+ curveTo(14.956f, 7.491f, 15.026f, 7.335f, 15.026f, 7.164f)
+ close()
+ moveTo(15.859f, 7.164f)
+ curveTo(15.859f, 7.592f, 15.674f, 7.924f, 15.48f, 8.179f)
+ lineTo(15.479f, 8.18f)
+ curveTo(15.303f, 8.411f, 15.055f, 8.658f, 14.802f, 8.912f)
+ lineTo(8.759f, 14.954f)
+ curveTo(8.632f, 15.081f, 8.474f, 15.243f, 8.268f, 15.361f)
+ curveTo(8.06f, 15.479f, 7.837f, 15.531f, 7.663f, 15.575f)
+ lineTo(7.652f, 15.578f)
+ lineTo(7.625f, 15.583f)
+ lineTo(5.444f, 16.128f)
+ lineTo(5.427f, 16.133f)
+ curveTo(5.273f, 16.177f, 5.115f, 16.21f, 4.956f, 16.23f)
+ lineTo(4.946f, 16.232f)
+ curveTo(4.774f, 16.25f, 4.395f, 16.257f, 4.086f, 15.947f)
+ curveTo(3.779f, 15.639f, 3.786f, 15.262f, 3.802f, 15.091f)
+ lineTo(3.803f, 15.081f)
+ lineTo(3.805f, 15.072f)
+ curveTo(3.826f, 14.925f, 3.855f, 14.78f, 3.893f, 14.637f)
+ lineTo(4.451f, 12.409f)
+ lineTo(4.46f, 12.373f)
+ curveTo(4.503f, 12.198f, 4.555f, 11.976f, 4.673f, 11.768f)
+ curveTo(4.79f, 11.561f, 4.955f, 11.402f, 5.08f, 11.276f)
+ lineTo(5.085f, 11.27f)
+ lineTo(5.108f, 11.248f)
+ lineTo(11.103f, 5.253f)
+ lineTo(11.123f, 5.233f)
+ curveTo(11.377f, 4.98f, 11.624f, 4.731f, 11.855f, 4.555f)
+ curveTo(12.111f, 4.359f, 12.443f, 4.175f, 12.871f, 4.175f)
+ curveTo(13.246f, 4.175f, 13.547f, 4.317f, 13.788f, 4.483f)
+ lineTo(13.887f, 4.555f)
+ lineTo(14.064f, 4.699f)
+ curveTo(14.242f, 4.855f, 14.428f, 5.043f, 14.618f, 5.233f)
+ lineTo(14.639f, 5.253f)
+ lineTo(14.781f, 5.396f)
+ lineTo(14.802f, 5.416f)
+ curveTo(15.055f, 5.669f, 15.303f, 5.916f, 15.479f, 6.147f)
+ lineTo(15.552f, 6.247f)
+ curveTo(15.718f, 6.488f, 15.859f, 6.789f, 15.859f, 7.164f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(10.788f, 5.914f)
+ lineTo(13.288f, 4.247f)
+ lineTo(15.788f, 6.747f)
+ lineTo(14.121f, 9.247f)
+ lineTo(10.788f, 5.914f)
+ close()
+ }
+ }
+ .build()
+ return _pen!!
+ }
+
+private var _pen: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.Pen, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/PenSmall.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/PenSmall.kt
new file mode 100644
index 0000000..b7acab6
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/PenSmall.kt
@@ -0,0 +1,220 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.PenSmall: ImageVector
+ get() {
+ if (_penSmall != null) {
+ return _penSmall!!
+ }
+ _penSmall = Builder(name = "PenSmall", defaultWidth = 10.0.dp, defaultHeight = 11.0.dp,
+ viewportWidth = 10.0f, viewportHeight = 11.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(9.021f, 2.772f)
+ curveTo(9.021f, 2.635f, 8.965f, 2.51f, 8.853f, 2.364f)
+ curveTo(8.745f, 2.223f, 8.584f, 2.06f, 8.37f, 1.845f)
+ lineTo(8.353f, 1.83f)
+ lineTo(8.223f, 1.7f)
+ curveTo(8.009f, 1.486f, 7.847f, 1.324f, 7.706f, 1.216f)
+ lineTo(7.599f, 1.143f)
+ curveTo(7.496f, 1.08f, 7.4f, 1.049f, 7.297f, 1.048f)
+ curveTo(7.159f, 1.048f, 7.034f, 1.104f, 6.889f, 1.215f)
+ curveTo(6.747f, 1.323f, 6.584f, 1.485f, 6.37f, 1.7f)
+ lineTo(1.554f, 6.515f)
+ lineTo(1.532f, 6.537f)
+ curveTo(1.419f, 6.65f, 1.359f, 6.713f, 1.319f, 6.784f)
+ lineTo(1.318f, 6.785f)
+ curveTo(1.277f, 6.858f, 1.255f, 6.942f, 1.215f, 7.101f)
+ lineTo(1.207f, 7.131f)
+ lineTo(0.758f, 8.924f)
+ curveTo(0.736f, 9.009f, 0.717f, 9.096f, 0.704f, 9.183f)
+ curveTo(0.702f, 9.214f, 0.703f, 9.242f, 0.708f, 9.267f)
+ curveTo(0.713f, 9.29f, 0.723f, 9.31f, 0.741f, 9.328f)
+ curveTo(0.76f, 9.347f, 0.779f, 9.356f, 0.801f, 9.361f)
+ curveTo(0.826f, 9.367f, 0.854f, 9.367f, 0.885f, 9.364f)
+ curveTo(0.978f, 9.351f, 1.069f, 9.332f, 1.159f, 9.307f)
+ lineTo(1.172f, 9.303f)
+ lineTo(2.938f, 8.862f)
+ lineTo(2.947f, 8.86f)
+ lineTo(2.967f, 8.855f)
+ curveTo(3.125f, 8.815f, 3.211f, 8.792f, 3.284f, 8.751f)
+ curveTo(3.357f, 8.709f, 3.422f, 8.648f, 3.536f, 8.534f)
+ lineTo(3.557f, 8.511f)
+ lineTo(8.353f, 3.715f)
+ lineTo(8.37f, 3.7f)
+ curveTo(8.584f, 3.485f, 8.745f, 3.322f, 8.853f, 3.181f)
+ curveTo(8.965f, 3.034f, 9.021f, 2.91f, 9.021f, 2.772f)
+ close()
+ moveTo(9.687f, 2.772f)
+ curveTo(9.687f, 3.115f, 9.539f, 3.381f, 9.384f, 3.585f)
+ lineTo(9.383f, 3.586f)
+ curveTo(9.242f, 3.771f, 9.044f, 3.968f, 8.841f, 4.171f)
+ lineTo(4.007f, 9.005f)
+ curveTo(3.905f, 9.107f, 3.779f, 9.236f, 3.614f, 9.33f)
+ curveTo(3.448f, 9.425f, 3.269f, 9.467f, 3.13f, 9.502f)
+ lineTo(3.122f, 9.504f)
+ lineTo(3.1f, 9.508f)
+ lineTo(1.355f, 9.944f)
+ lineTo(1.342f, 9.948f)
+ curveTo(1.218f, 9.983f, 1.092f, 10.009f, 0.965f, 10.026f)
+ lineTo(0.956f, 10.027f)
+ curveTo(0.819f, 10.041f, 0.516f, 10.047f, 0.269f, 9.799f)
+ curveTo(0.023f, 9.553f, 0.028f, 9.251f, 0.042f, 9.114f)
+ lineTo(0.042f, 9.106f)
+ lineTo(0.044f, 9.099f)
+ curveTo(0.06f, 8.982f, 0.084f, 8.865f, 0.115f, 8.751f)
+ lineTo(0.561f, 6.969f)
+ lineTo(0.568f, 6.94f)
+ curveTo(0.603f, 6.8f, 0.644f, 6.623f, 0.738f, 6.456f)
+ curveTo(0.832f, 6.29f, 0.964f, 6.163f, 1.064f, 6.062f)
+ lineTo(1.068f, 6.058f)
+ lineTo(1.086f, 6.04f)
+ lineTo(5.882f, 1.244f)
+ lineTo(5.898f, 1.228f)
+ curveTo(6.101f, 1.025f, 6.299f, 0.826f, 6.484f, 0.685f)
+ curveTo(6.689f, 0.529f, 6.955f, 0.382f, 7.297f, 0.382f)
+ curveTo(7.597f, 0.382f, 7.838f, 0.496f, 8.03f, 0.628f)
+ lineTo(8.109f, 0.685f)
+ lineTo(8.251f, 0.801f)
+ curveTo(8.394f, 0.926f, 8.542f, 1.076f, 8.695f, 1.228f)
+ lineTo(8.711f, 1.244f)
+ lineTo(8.825f, 1.358f)
+ lineTo(8.841f, 1.374f)
+ curveTo(9.044f, 1.577f, 9.242f, 1.774f, 9.383f, 1.959f)
+ lineTo(9.441f, 2.039f)
+ curveTo(9.574f, 2.232f, 9.687f, 2.473f, 9.687f, 2.772f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(9.021f, 2.772f)
+ curveTo(9.021f, 2.635f, 8.965f, 2.51f, 8.853f, 2.364f)
+ curveTo(8.745f, 2.223f, 8.584f, 2.06f, 8.37f, 1.845f)
+ lineTo(8.353f, 1.83f)
+ lineTo(8.223f, 1.7f)
+ curveTo(8.009f, 1.486f, 7.847f, 1.324f, 7.706f, 1.216f)
+ lineTo(7.599f, 1.143f)
+ curveTo(7.496f, 1.08f, 7.4f, 1.049f, 7.297f, 1.048f)
+ curveTo(7.159f, 1.048f, 7.034f, 1.104f, 6.889f, 1.215f)
+ curveTo(6.747f, 1.323f, 6.584f, 1.485f, 6.37f, 1.7f)
+ lineTo(1.554f, 6.515f)
+ lineTo(1.532f, 6.537f)
+ curveTo(1.419f, 6.65f, 1.359f, 6.713f, 1.319f, 6.784f)
+ lineTo(1.318f, 6.785f)
+ curveTo(1.277f, 6.858f, 1.255f, 6.942f, 1.215f, 7.101f)
+ lineTo(1.207f, 7.131f)
+ lineTo(0.758f, 8.924f)
+ curveTo(0.736f, 9.009f, 0.717f, 9.096f, 0.704f, 9.183f)
+ curveTo(0.702f, 9.214f, 0.703f, 9.242f, 0.708f, 9.267f)
+ curveTo(0.713f, 9.29f, 0.723f, 9.31f, 0.741f, 9.328f)
+ curveTo(0.76f, 9.347f, 0.779f, 9.356f, 0.801f, 9.361f)
+ curveTo(0.826f, 9.367f, 0.854f, 9.367f, 0.885f, 9.364f)
+ curveTo(0.978f, 9.351f, 1.069f, 9.332f, 1.159f, 9.307f)
+ lineTo(1.172f, 9.303f)
+ lineTo(2.938f, 8.862f)
+ lineTo(2.947f, 8.86f)
+ lineTo(2.967f, 8.855f)
+ curveTo(3.125f, 8.815f, 3.211f, 8.792f, 3.284f, 8.751f)
+ curveTo(3.357f, 8.709f, 3.422f, 8.648f, 3.536f, 8.534f)
+ lineTo(3.557f, 8.511f)
+ lineTo(8.353f, 3.715f)
+ lineTo(8.37f, 3.7f)
+ curveTo(8.584f, 3.485f, 8.745f, 3.322f, 8.853f, 3.181f)
+ curveTo(8.965f, 3.034f, 9.021f, 2.91f, 9.021f, 2.772f)
+ close()
+ moveTo(9.687f, 2.772f)
+ curveTo(9.687f, 3.115f, 9.539f, 3.381f, 9.384f, 3.585f)
+ lineTo(9.383f, 3.586f)
+ curveTo(9.242f, 3.771f, 9.044f, 3.968f, 8.841f, 4.171f)
+ lineTo(4.007f, 9.005f)
+ curveTo(3.905f, 9.107f, 3.779f, 9.236f, 3.614f, 9.33f)
+ curveTo(3.448f, 9.425f, 3.269f, 9.467f, 3.13f, 9.502f)
+ lineTo(3.122f, 9.504f)
+ lineTo(3.1f, 9.508f)
+ lineTo(1.355f, 9.944f)
+ lineTo(1.342f, 9.948f)
+ curveTo(1.218f, 9.983f, 1.092f, 10.009f, 0.965f, 10.026f)
+ lineTo(0.956f, 10.027f)
+ curveTo(0.819f, 10.041f, 0.516f, 10.047f, 0.269f, 9.799f)
+ curveTo(0.023f, 9.553f, 0.028f, 9.251f, 0.042f, 9.114f)
+ lineTo(0.042f, 9.106f)
+ lineTo(0.044f, 9.099f)
+ curveTo(0.06f, 8.982f, 0.084f, 8.865f, 0.115f, 8.751f)
+ lineTo(0.561f, 6.969f)
+ lineTo(0.568f, 6.94f)
+ curveTo(0.603f, 6.8f, 0.644f, 6.623f, 0.738f, 6.456f)
+ curveTo(0.832f, 6.29f, 0.964f, 6.163f, 1.064f, 6.062f)
+ lineTo(1.068f, 6.058f)
+ lineTo(1.086f, 6.04f)
+ lineTo(5.882f, 1.244f)
+ lineTo(5.898f, 1.228f)
+ curveTo(6.101f, 1.025f, 6.299f, 0.826f, 6.484f, 0.685f)
+ curveTo(6.689f, 0.529f, 6.955f, 0.382f, 7.297f, 0.382f)
+ curveTo(7.597f, 0.382f, 7.838f, 0.496f, 8.03f, 0.628f)
+ lineTo(8.109f, 0.685f)
+ lineTo(8.251f, 0.801f)
+ curveTo(8.394f, 0.926f, 8.542f, 1.076f, 8.695f, 1.228f)
+ lineTo(8.711f, 1.244f)
+ lineTo(8.825f, 1.358f)
+ lineTo(8.841f, 1.374f)
+ curveTo(9.044f, 1.577f, 9.242f, 1.774f, 9.383f, 1.959f)
+ lineTo(9.441f, 2.039f)
+ curveTo(9.574f, 2.232f, 9.687f, 2.473f, 9.687f, 2.772f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(5.63f, 1.773f)
+ lineTo(7.63f, 0.439f)
+ lineTo(9.63f, 2.439f)
+ lineTo(8.297f, 4.439f)
+ lineTo(5.63f, 1.773f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(5.63f, 1.773f)
+ lineTo(7.63f, 0.439f)
+ lineTo(9.63f, 2.439f)
+ lineTo(8.297f, 4.439f)
+ lineTo(5.63f, 1.773f)
+ close()
+ }
+ }
+ .build()
+ return _penSmall!!
+ }
+
+private var _penSmall: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.PenSmall, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/RecordBest.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/RecordBest.kt
new file mode 100644
index 0000000..5cf69f7
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/RecordBest.kt
@@ -0,0 +1,184 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.RecordBest: ImageVector
+ get() {
+ if (_recordBest != null) {
+ return _recordBest!!
+ }
+ _recordBest = Builder(name = "RecordBest", defaultWidth = 20.0.dp, defaultHeight = 20.0.dp,
+ viewportWidth = 20.0f, viewportHeight = 20.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(13.661f, 17.5f)
+ horizontalLineTo(4.128f)
+ curveTo(3.702f, 17.499f, 3.293f, 17.33f, 2.992f, 17.029f)
+ curveTo(2.69f, 16.728f, 2.521f, 16.319f, 2.52f, 15.893f)
+ verticalLineTo(10.536f)
+ curveTo(2.52f, 9.65f, 3.241f, 8.929f, 4.127f, 8.929f)
+ horizontalLineTo(6.0f)
+ curveTo(6.959f, 7.607f, 7.728f, 6.157f, 8.283f, 4.621f)
+ lineTo(8.78f, 3.242f)
+ curveTo(8.9f, 2.819f, 9.324f, 2.5f, 9.799f, 2.5f)
+ lineTo(9.935f, 2.504f)
+ curveTo(10.605f, 2.541f, 11.227f, 2.856f, 11.656f, 3.379f)
+ curveTo(11.853f, 3.619f, 12.001f, 3.894f, 12.093f, 4.19f)
+ lineTo(12.129f, 4.319f)
+ curveTo(12.215f, 4.663f, 12.223f, 5.022f, 12.153f, 5.37f)
+ lineTo(11.656f, 7.856f)
+ horizontalLineTo(15.336f)
+ lineTo(15.465f, 7.861f)
+ curveTo(16.101f, 7.898f, 16.68f, 8.209f, 17.061f, 8.73f)
+ curveTo(17.237f, 8.966f, 17.361f, 9.236f, 17.427f, 9.521f)
+ lineTo(17.451f, 9.645f)
+ curveTo(17.507f, 9.977f, 17.483f, 10.317f, 17.381f, 10.638f)
+ lineTo(15.708f, 15.995f)
+ curveTo(15.443f, 16.839f, 14.705f, 17.423f, 13.837f, 17.493f)
+ lineTo(13.661f, 17.5f)
+ close()
+ moveTo(13.664f, 16.428f)
+ curveTo(13.892f, 16.428f, 14.115f, 16.355f, 14.298f, 16.22f)
+ lineTo(14.365f, 16.166f)
+ curveTo(14.515f, 16.036f, 14.626f, 15.867f, 14.686f, 15.676f)
+ lineTo(16.36f, 10.319f)
+ curveTo(16.41f, 10.159f, 16.421f, 9.988f, 16.394f, 9.823f)
+ curveTo(16.366f, 9.657f, 16.3f, 9.5f, 16.201f, 9.364f)
+ curveTo(16.102f, 9.228f, 15.972f, 9.117f, 15.822f, 9.041f)
+ curveTo(15.672f, 8.965f, 15.506f, 8.926f, 15.337f, 8.927f)
+ horizontalLineTo(11.003f)
+ lineTo(10.944f, 8.923f)
+ curveTo(10.885f, 8.917f, 10.827f, 8.901f, 10.774f, 8.875f)
+ curveTo(10.702f, 8.841f, 10.638f, 8.793f, 10.588f, 8.731f)
+ curveTo(10.538f, 8.67f, 10.502f, 8.598f, 10.482f, 8.521f)
+ curveTo(10.463f, 8.444f, 10.461f, 8.364f, 10.477f, 8.286f)
+ lineTo(11.102f, 5.16f)
+ lineTo(11.124f, 5.014f)
+ curveTo(11.137f, 4.868f, 11.126f, 4.72f, 11.09f, 4.577f)
+ curveTo(11.043f, 4.386f, 10.953f, 4.207f, 10.828f, 4.055f)
+ curveTo(10.703f, 3.903f, 10.546f, 3.781f, 10.368f, 3.697f)
+ curveTo(10.19f, 3.613f, 9.996f, 3.57f, 9.799f, 3.57f)
+ lineTo(9.292f, 4.983f)
+ horizontalLineTo(9.296f)
+ curveTo(8.695f, 6.646f, 7.858f, 8.215f, 6.81f, 9.639f)
+ verticalLineTo(16.428f)
+ horizontalLineTo(13.664f)
+ close()
+ moveTo(5.735f, 16.428f)
+ verticalLineTo(9.999f)
+ horizontalLineTo(4.128f)
+ lineTo(4.023f, 10.01f)
+ curveTo(3.954f, 10.024f, 3.889f, 10.051f, 3.831f, 10.09f)
+ lineTo(3.75f, 10.156f)
+ curveTo(3.649f, 10.257f, 3.592f, 10.393f, 3.592f, 10.535f)
+ verticalLineTo(15.893f)
+ curveTo(3.592f, 16.035f, 3.649f, 16.171f, 3.75f, 16.272f)
+ curveTo(3.825f, 16.347f, 3.92f, 16.398f, 4.023f, 16.419f)
+ lineTo(4.128f, 16.428f)
+ horizontalLineTo(5.735f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(13.661f, 17.5f)
+ horizontalLineTo(4.128f)
+ curveTo(3.702f, 17.499f, 3.293f, 17.33f, 2.992f, 17.029f)
+ curveTo(2.69f, 16.728f, 2.521f, 16.319f, 2.52f, 15.893f)
+ verticalLineTo(10.536f)
+ curveTo(2.52f, 9.65f, 3.241f, 8.929f, 4.127f, 8.929f)
+ horizontalLineTo(6.0f)
+ curveTo(6.959f, 7.607f, 7.728f, 6.157f, 8.283f, 4.621f)
+ lineTo(8.78f, 3.242f)
+ curveTo(8.9f, 2.819f, 9.324f, 2.5f, 9.799f, 2.5f)
+ lineTo(9.935f, 2.504f)
+ curveTo(10.605f, 2.541f, 11.227f, 2.856f, 11.656f, 3.379f)
+ curveTo(11.853f, 3.619f, 12.001f, 3.894f, 12.093f, 4.19f)
+ lineTo(12.129f, 4.319f)
+ curveTo(12.215f, 4.663f, 12.223f, 5.022f, 12.153f, 5.37f)
+ lineTo(11.656f, 7.856f)
+ horizontalLineTo(15.336f)
+ lineTo(15.465f, 7.861f)
+ curveTo(16.101f, 7.898f, 16.68f, 8.209f, 17.061f, 8.73f)
+ curveTo(17.237f, 8.966f, 17.361f, 9.236f, 17.427f, 9.521f)
+ lineTo(17.451f, 9.645f)
+ curveTo(17.507f, 9.977f, 17.483f, 10.317f, 17.381f, 10.638f)
+ lineTo(15.708f, 15.995f)
+ curveTo(15.443f, 16.839f, 14.705f, 17.423f, 13.837f, 17.493f)
+ lineTo(13.661f, 17.5f)
+ close()
+ moveTo(13.664f, 16.428f)
+ curveTo(13.892f, 16.428f, 14.115f, 16.355f, 14.298f, 16.22f)
+ lineTo(14.365f, 16.166f)
+ curveTo(14.515f, 16.036f, 14.626f, 15.867f, 14.686f, 15.676f)
+ lineTo(16.36f, 10.319f)
+ curveTo(16.41f, 10.159f, 16.421f, 9.988f, 16.394f, 9.823f)
+ curveTo(16.366f, 9.657f, 16.3f, 9.5f, 16.201f, 9.364f)
+ curveTo(16.102f, 9.228f, 15.972f, 9.117f, 15.822f, 9.041f)
+ curveTo(15.672f, 8.965f, 15.506f, 8.926f, 15.337f, 8.927f)
+ horizontalLineTo(11.003f)
+ lineTo(10.944f, 8.923f)
+ curveTo(10.885f, 8.917f, 10.827f, 8.901f, 10.774f, 8.875f)
+ curveTo(10.702f, 8.841f, 10.638f, 8.793f, 10.588f, 8.731f)
+ curveTo(10.538f, 8.67f, 10.502f, 8.598f, 10.482f, 8.521f)
+ curveTo(10.463f, 8.444f, 10.461f, 8.364f, 10.477f, 8.286f)
+ lineTo(11.102f, 5.16f)
+ lineTo(11.124f, 5.014f)
+ curveTo(11.137f, 4.868f, 11.126f, 4.72f, 11.09f, 4.577f)
+ curveTo(11.043f, 4.386f, 10.953f, 4.207f, 10.828f, 4.055f)
+ curveTo(10.703f, 3.903f, 10.546f, 3.781f, 10.368f, 3.697f)
+ curveTo(10.19f, 3.613f, 9.996f, 3.57f, 9.799f, 3.57f)
+ lineTo(9.292f, 4.983f)
+ horizontalLineTo(9.296f)
+ curveTo(8.695f, 6.646f, 7.858f, 8.215f, 6.81f, 9.639f)
+ verticalLineTo(16.428f)
+ horizontalLineTo(13.664f)
+ close()
+ moveTo(5.735f, 16.428f)
+ verticalLineTo(9.999f)
+ horizontalLineTo(4.128f)
+ lineTo(4.023f, 10.01f)
+ curveTo(3.954f, 10.024f, 3.889f, 10.051f, 3.831f, 10.09f)
+ lineTo(3.75f, 10.156f)
+ curveTo(3.649f, 10.257f, 3.592f, 10.393f, 3.592f, 10.535f)
+ verticalLineTo(15.893f)
+ curveTo(3.592f, 16.035f, 3.649f, 16.171f, 3.75f, 16.272f)
+ curveTo(3.825f, 16.347f, 3.92f, 16.398f, 4.023f, 16.419f)
+ lineTo(4.128f, 16.428f)
+ horizontalLineTo(5.735f)
+ close()
+ }
+ }
+ .build()
+ return _recordBest!!
+ }
+
+private var _recordBest: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.RecordBest, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/RecordBookmark.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/RecordBookmark.kt
new file mode 100644
index 0000000..aafb923
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/RecordBookmark.kt
@@ -0,0 +1,178 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.RecordBookmark: ImageVector
+ get() {
+ if (_recordBookmark != null) {
+ return _recordBookmark!!
+ }
+ _recordBookmark = Builder(name = "RecordBookmark", defaultWidth = 20.0.dp, defaultHeight =
+ 20.0.dp, viewportWidth = 20.0f, viewportHeight = 20.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(10.0f, 13.395f)
+ curveTo(10.391f, 13.395f, 10.733f, 13.532f, 11.086f, 13.748f)
+ curveTo(11.43f, 13.958f, 11.822f, 14.267f, 12.306f, 14.649f)
+ lineTo(12.866f, 15.092f)
+ lineTo(13.484f, 15.578f)
+ curveTo(14.057f, 16.025f, 14.503f, 16.356f, 14.872f, 16.569f)
+ curveTo(15.369f, 16.855f, 15.598f, 16.851f, 15.755f, 16.775f)
+ lineTo(15.812f, 16.741f)
+ curveTo(15.945f, 16.648f, 16.065f, 16.449f, 16.138f, 15.951f)
+ curveTo(16.22f, 15.387f, 16.221f, 14.586f, 16.221f, 13.454f)
+ verticalLineTo(7.764f)
+ curveTo(16.221f, 6.574f, 16.22f, 5.722f, 16.134f, 5.076f)
+ curveTo(16.059f, 4.521f, 15.927f, 4.164f, 15.712f, 3.899f)
+ lineTo(15.614f, 3.791f)
+ curveTo(15.34f, 3.515f, 14.966f, 3.354f, 14.335f, 3.268f)
+ curveTo(13.691f, 3.181f, 12.844f, 3.181f, 11.659f, 3.181f)
+ horizontalLineTo(8.341f)
+ curveTo(7.156f, 3.181f, 6.309f, 3.181f, 5.665f, 3.268f)
+ curveTo(5.113f, 3.343f, 4.758f, 3.476f, 4.494f, 3.692f)
+ lineTo(4.386f, 3.791f)
+ curveTo(4.112f, 4.066f, 3.951f, 4.441f, 3.866f, 5.076f)
+ curveTo(3.78f, 5.722f, 3.779f, 6.574f, 3.779f, 7.764f)
+ verticalLineTo(13.454f)
+ curveTo(3.779f, 14.585f, 3.78f, 15.387f, 3.862f, 15.951f)
+ curveTo(3.945f, 16.521f, 4.09f, 16.699f, 4.245f, 16.775f)
+ curveTo(4.401f, 16.851f, 4.631f, 16.855f, 5.127f, 16.569f)
+ curveTo(5.619f, 16.285f, 6.248f, 15.791f, 7.134f, 15.091f)
+ lineTo(7.694f, 14.649f)
+ curveTo(8.178f, 14.267f, 8.57f, 13.958f, 8.914f, 13.748f)
+ curveTo(9.267f, 13.532f, 9.609f, 13.395f, 10.0f, 13.395f)
+ close()
+ moveTo(17.051f, 13.454f)
+ curveTo(17.051f, 14.558f, 17.052f, 15.433f, 16.959f, 16.072f)
+ curveTo(16.867f, 16.706f, 16.662f, 17.258f, 16.118f, 17.525f)
+ horizontalLineTo(16.117f)
+ curveTo(15.572f, 17.79f, 15.014f, 17.611f, 14.46f, 17.292f)
+ curveTo(14.042f, 17.051f, 13.552f, 16.686f, 12.969f, 16.231f)
+ lineTo(12.353f, 15.747f)
+ lineTo(11.793f, 15.304f)
+ curveTo(11.294f, 14.91f, 10.947f, 14.638f, 10.655f, 14.46f)
+ curveTo(10.373f, 14.287f, 10.182f, 14.229f, 10.0f, 14.229f)
+ curveTo(9.818f, 14.229f, 9.627f, 14.287f, 9.345f, 14.46f)
+ curveTo(9.053f, 14.638f, 8.706f, 14.91f, 8.206f, 15.304f)
+ verticalLineTo(15.305f)
+ lineTo(7.646f, 15.746f)
+ curveTo(6.782f, 16.428f, 6.097f, 16.97f, 5.54f, 17.292f)
+ curveTo(4.986f, 17.611f, 4.428f, 17.79f, 3.883f, 17.525f)
+ curveTo(3.339f, 17.259f, 3.133f, 16.706f, 3.041f, 16.072f)
+ curveTo(2.948f, 15.433f, 2.949f, 14.557f, 2.949f, 13.454f)
+ verticalLineTo(7.764f)
+ curveTo(2.949f, 6.597f, 2.948f, 5.68f, 3.044f, 4.965f)
+ curveTo(3.141f, 4.238f, 3.345f, 3.658f, 3.8f, 3.202f)
+ lineTo(3.976f, 3.042f)
+ curveTo(4.4f, 2.696f, 4.922f, 2.528f, 5.555f, 2.442f)
+ curveTo(6.267f, 2.346f, 7.18f, 2.347f, 8.341f, 2.347f)
+ horizontalLineTo(11.659f)
+ curveTo(12.82f, 2.347f, 13.733f, 2.346f, 14.445f, 2.442f)
+ curveTo(15.168f, 2.54f, 15.746f, 2.745f, 16.2f, 3.202f)
+ lineTo(16.359f, 3.378f)
+ curveTo(16.704f, 3.805f, 16.871f, 4.329f, 16.956f, 4.965f)
+ curveTo(17.052f, 5.68f, 17.051f, 6.597f, 17.051f, 7.764f)
+ verticalLineTo(13.454f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(10.0f, 13.395f)
+ curveTo(10.391f, 13.395f, 10.733f, 13.532f, 11.086f, 13.748f)
+ curveTo(11.43f, 13.958f, 11.822f, 14.267f, 12.306f, 14.649f)
+ lineTo(12.866f, 15.092f)
+ lineTo(13.484f, 15.578f)
+ curveTo(14.057f, 16.025f, 14.503f, 16.356f, 14.872f, 16.569f)
+ curveTo(15.369f, 16.855f, 15.598f, 16.851f, 15.755f, 16.775f)
+ lineTo(15.812f, 16.741f)
+ curveTo(15.945f, 16.648f, 16.065f, 16.449f, 16.138f, 15.951f)
+ curveTo(16.22f, 15.387f, 16.221f, 14.586f, 16.221f, 13.454f)
+ verticalLineTo(7.764f)
+ curveTo(16.221f, 6.574f, 16.22f, 5.722f, 16.134f, 5.076f)
+ curveTo(16.059f, 4.521f, 15.927f, 4.164f, 15.712f, 3.899f)
+ lineTo(15.614f, 3.791f)
+ curveTo(15.34f, 3.515f, 14.966f, 3.354f, 14.335f, 3.268f)
+ curveTo(13.691f, 3.181f, 12.844f, 3.181f, 11.659f, 3.181f)
+ horizontalLineTo(8.341f)
+ curveTo(7.156f, 3.181f, 6.309f, 3.181f, 5.665f, 3.268f)
+ curveTo(5.113f, 3.343f, 4.758f, 3.476f, 4.494f, 3.692f)
+ lineTo(4.386f, 3.791f)
+ curveTo(4.112f, 4.066f, 3.951f, 4.441f, 3.866f, 5.076f)
+ curveTo(3.78f, 5.722f, 3.779f, 6.574f, 3.779f, 7.764f)
+ verticalLineTo(13.454f)
+ curveTo(3.779f, 14.585f, 3.78f, 15.387f, 3.862f, 15.951f)
+ curveTo(3.945f, 16.521f, 4.09f, 16.699f, 4.245f, 16.775f)
+ curveTo(4.401f, 16.851f, 4.631f, 16.855f, 5.127f, 16.569f)
+ curveTo(5.619f, 16.285f, 6.248f, 15.791f, 7.134f, 15.091f)
+ lineTo(7.694f, 14.649f)
+ curveTo(8.178f, 14.267f, 8.57f, 13.958f, 8.914f, 13.748f)
+ curveTo(9.267f, 13.532f, 9.609f, 13.395f, 10.0f, 13.395f)
+ close()
+ moveTo(17.051f, 13.454f)
+ curveTo(17.051f, 14.558f, 17.052f, 15.433f, 16.959f, 16.072f)
+ curveTo(16.867f, 16.706f, 16.662f, 17.258f, 16.118f, 17.525f)
+ horizontalLineTo(16.117f)
+ curveTo(15.572f, 17.79f, 15.014f, 17.611f, 14.46f, 17.292f)
+ curveTo(14.042f, 17.051f, 13.552f, 16.686f, 12.969f, 16.231f)
+ lineTo(12.353f, 15.747f)
+ lineTo(11.793f, 15.304f)
+ curveTo(11.294f, 14.91f, 10.947f, 14.638f, 10.655f, 14.46f)
+ curveTo(10.373f, 14.287f, 10.182f, 14.229f, 10.0f, 14.229f)
+ curveTo(9.818f, 14.229f, 9.627f, 14.287f, 9.345f, 14.46f)
+ curveTo(9.053f, 14.638f, 8.706f, 14.91f, 8.206f, 15.304f)
+ verticalLineTo(15.305f)
+ lineTo(7.646f, 15.746f)
+ curveTo(6.782f, 16.428f, 6.097f, 16.97f, 5.54f, 17.292f)
+ curveTo(4.986f, 17.611f, 4.428f, 17.79f, 3.883f, 17.525f)
+ curveTo(3.339f, 17.259f, 3.133f, 16.706f, 3.041f, 16.072f)
+ curveTo(2.948f, 15.433f, 2.949f, 14.557f, 2.949f, 13.454f)
+ verticalLineTo(7.764f)
+ curveTo(2.949f, 6.597f, 2.948f, 5.68f, 3.044f, 4.965f)
+ curveTo(3.141f, 4.238f, 3.345f, 3.658f, 3.8f, 3.202f)
+ lineTo(3.976f, 3.042f)
+ curveTo(4.4f, 2.696f, 4.922f, 2.528f, 5.555f, 2.442f)
+ curveTo(6.267f, 2.346f, 7.18f, 2.347f, 8.341f, 2.347f)
+ horizontalLineTo(11.659f)
+ curveTo(12.82f, 2.347f, 13.733f, 2.346f, 14.445f, 2.442f)
+ curveTo(15.168f, 2.54f, 15.746f, 2.745f, 16.2f, 3.202f)
+ lineTo(16.359f, 3.378f)
+ curveTo(16.704f, 3.805f, 16.871f, 4.329f, 16.956f, 4.965f)
+ curveTo(17.052f, 5.68f, 17.051f, 6.597f, 17.051f, 7.764f)
+ verticalLineTo(13.454f)
+ close()
+ }
+ }
+ .build()
+ return _recordBookmark!!
+ }
+
+private var _recordBookmark: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.RecordBookmark, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/RecordComment.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/RecordComment.kt
new file mode 100644
index 0000000..bbf3388
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/RecordComment.kt
@@ -0,0 +1,210 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.RecordComment: ImageVector
+ get() {
+ if (_recordComment != null) {
+ return _recordComment!!
+ }
+ _recordComment = Builder(name = "RecordComment", defaultWidth = 20.0.dp, defaultHeight =
+ 20.0.dp, viewportWidth = 20.0f, viewportHeight = 20.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(16.25f, 9.23f)
+ curveTo(16.25f, 8.051f, 16.249f, 7.198f, 16.182f, 6.534f)
+ curveTo(16.115f, 5.877f, 15.986f, 5.45f, 15.759f, 5.11f)
+ curveTo(15.546f, 4.791f, 15.272f, 4.518f, 14.954f, 4.305f)
+ curveTo(14.613f, 4.077f, 14.187f, 3.949f, 13.53f, 3.882f)
+ curveTo(12.865f, 3.814f, 12.012f, 3.813f, 10.833f, 3.813f)
+ horizontalLineTo(9.167f)
+ curveTo(7.987f, 3.813f, 7.135f, 3.814f, 6.471f, 3.882f)
+ curveTo(5.814f, 3.949f, 5.387f, 4.077f, 5.046f, 4.305f)
+ curveTo(4.728f, 4.518f, 4.454f, 4.791f, 4.242f, 5.11f)
+ curveTo(4.014f, 5.45f, 3.885f, 5.877f, 3.818f, 6.534f)
+ curveTo(3.751f, 7.198f, 3.75f, 8.051f, 3.75f, 9.23f)
+ curveTo(3.75f, 10.409f, 3.751f, 11.262f, 3.818f, 11.926f)
+ curveTo(3.885f, 12.583f, 4.014f, 13.01f, 4.242f, 13.35f)
+ curveTo(4.454f, 13.669f, 4.728f, 13.942f, 5.046f, 14.155f)
+ curveTo(5.344f, 14.354f, 5.708f, 14.478f, 6.233f, 14.551f)
+ curveTo(6.765f, 14.624f, 7.434f, 14.642f, 8.335f, 14.647f)
+ curveTo(8.492f, 14.647f, 8.636f, 14.736f, 8.706f, 14.877f)
+ lineTo(9.627f, 16.719f)
+ lineTo(9.657f, 16.769f)
+ curveTo(9.689f, 16.816f, 9.732f, 16.857f, 9.781f, 16.887f)
+ curveTo(9.847f, 16.928f, 9.923f, 16.949f, 10.0f, 16.949f)
+ curveTo(10.077f, 16.949f, 10.153f, 16.928f, 10.219f, 16.887f)
+ curveTo(10.285f, 16.847f, 10.338f, 16.788f, 10.373f, 16.719f)
+ lineTo(11.294f, 14.876f)
+ curveTo(11.364f, 14.736f, 11.508f, 14.647f, 11.665f, 14.646f)
+ curveTo(12.566f, 14.642f, 13.236f, 14.624f, 13.768f, 14.551f)
+ curveTo(14.293f, 14.478f, 14.656f, 14.354f, 14.954f, 14.155f)
+ lineTo(15.071f, 14.073f)
+ curveTo(15.34f, 13.874f, 15.572f, 13.629f, 15.759f, 13.35f)
+ curveTo(15.986f, 13.01f, 16.115f, 12.583f, 16.182f, 11.926f)
+ curveTo(16.249f, 11.262f, 16.25f, 10.409f, 16.25f, 9.23f)
+ close()
+ moveTo(10.0f, 10.48f)
+ curveTo(10.23f, 10.48f, 10.417f, 10.667f, 10.417f, 10.897f)
+ curveTo(10.417f, 11.127f, 10.23f, 11.313f, 10.0f, 11.313f)
+ horizontalLineTo(7.5f)
+ curveTo(7.27f, 11.313f, 7.083f, 11.127f, 7.083f, 10.897f)
+ curveTo(7.083f, 10.667f, 7.27f, 10.48f, 7.5f, 10.48f)
+ horizontalLineTo(10.0f)
+ close()
+ moveTo(12.5f, 7.147f)
+ curveTo(12.73f, 7.147f, 12.917f, 7.333f, 12.917f, 7.563f)
+ curveTo(12.917f, 7.793f, 12.73f, 7.98f, 12.5f, 7.98f)
+ horizontalLineTo(7.5f)
+ curveTo(7.27f, 7.98f, 7.083f, 7.793f, 7.083f, 7.563f)
+ curveTo(7.083f, 7.333f, 7.27f, 7.147f, 7.5f, 7.147f)
+ horizontalLineTo(12.5f)
+ close()
+ moveTo(17.083f, 9.23f)
+ curveTo(17.083f, 10.392f, 17.084f, 11.295f, 17.011f, 12.011f)
+ curveTo(16.937f, 12.734f, 16.786f, 13.313f, 16.452f, 13.813f)
+ curveTo(16.178f, 14.223f, 15.826f, 14.575f, 15.417f, 14.849f)
+ lineTo(15.416f, 14.848f)
+ curveTo(14.979f, 15.14f, 14.483f, 15.293f, 13.882f, 15.376f)
+ curveTo(13.346f, 15.45f, 12.705f, 15.469f, 11.925f, 15.476f)
+ lineTo(11.117f, 17.092f)
+ curveTo(11.014f, 17.299f, 10.854f, 17.474f, 10.657f, 17.596f)
+ curveTo(10.46f, 17.718f, 10.232f, 17.782f, 10.0f, 17.782f)
+ curveTo(9.768f, 17.782f, 9.541f, 17.718f, 9.343f, 17.596f)
+ curveTo(9.146f, 17.474f, 8.987f, 17.299f, 8.883f, 17.092f)
+ lineTo(8.075f, 15.477f)
+ curveTo(7.295f, 15.47f, 6.655f, 15.45f, 6.118f, 15.376f)
+ curveTo(5.517f, 15.292f, 5.02f, 15.14f, 4.583f, 14.849f)
+ curveTo(4.174f, 14.575f, 3.822f, 14.223f, 3.548f, 13.813f)
+ curveTo(3.214f, 13.313f, 3.063f, 12.734f, 2.989f, 12.011f)
+ curveTo(2.916f, 11.295f, 2.917f, 10.392f, 2.917f, 9.23f)
+ curveTo(2.917f, 8.068f, 2.916f, 7.165f, 2.989f, 6.449f)
+ curveTo(3.063f, 5.726f, 3.214f, 5.147f, 3.548f, 4.647f)
+ curveTo(3.822f, 4.237f, 4.174f, 3.885f, 4.583f, 3.611f)
+ curveTo(5.084f, 3.277f, 5.663f, 3.126f, 6.386f, 3.052f)
+ curveTo(7.102f, 2.98f, 8.005f, 2.98f, 9.167f, 2.98f)
+ horizontalLineTo(10.833f)
+ curveTo(11.995f, 2.98f, 12.898f, 2.98f, 13.614f, 3.052f)
+ curveTo(14.337f, 3.126f, 14.917f, 3.277f, 15.417f, 3.611f)
+ curveTo(15.826f, 3.885f, 16.178f, 4.237f, 16.452f, 4.647f)
+ curveTo(16.786f, 5.147f, 16.937f, 5.726f, 17.011f, 6.449f)
+ curveTo(17.084f, 7.165f, 17.083f, 8.068f, 17.083f, 9.23f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(16.25f, 9.23f)
+ curveTo(16.25f, 8.051f, 16.249f, 7.198f, 16.182f, 6.534f)
+ curveTo(16.115f, 5.877f, 15.986f, 5.45f, 15.759f, 5.11f)
+ curveTo(15.546f, 4.791f, 15.272f, 4.518f, 14.954f, 4.305f)
+ curveTo(14.613f, 4.077f, 14.187f, 3.949f, 13.53f, 3.882f)
+ curveTo(12.865f, 3.814f, 12.012f, 3.813f, 10.833f, 3.813f)
+ horizontalLineTo(9.167f)
+ curveTo(7.987f, 3.813f, 7.135f, 3.814f, 6.471f, 3.882f)
+ curveTo(5.814f, 3.949f, 5.387f, 4.077f, 5.046f, 4.305f)
+ curveTo(4.728f, 4.518f, 4.454f, 4.791f, 4.242f, 5.11f)
+ curveTo(4.014f, 5.45f, 3.885f, 5.877f, 3.818f, 6.534f)
+ curveTo(3.751f, 7.198f, 3.75f, 8.051f, 3.75f, 9.23f)
+ curveTo(3.75f, 10.409f, 3.751f, 11.262f, 3.818f, 11.926f)
+ curveTo(3.885f, 12.583f, 4.014f, 13.01f, 4.242f, 13.35f)
+ curveTo(4.454f, 13.669f, 4.728f, 13.942f, 5.046f, 14.155f)
+ curveTo(5.344f, 14.354f, 5.708f, 14.478f, 6.233f, 14.551f)
+ curveTo(6.765f, 14.624f, 7.434f, 14.642f, 8.335f, 14.647f)
+ curveTo(8.492f, 14.647f, 8.636f, 14.736f, 8.706f, 14.877f)
+ lineTo(9.627f, 16.719f)
+ lineTo(9.657f, 16.769f)
+ curveTo(9.689f, 16.816f, 9.732f, 16.857f, 9.781f, 16.887f)
+ curveTo(9.847f, 16.928f, 9.923f, 16.949f, 10.0f, 16.949f)
+ curveTo(10.077f, 16.949f, 10.153f, 16.928f, 10.219f, 16.887f)
+ curveTo(10.285f, 16.847f, 10.338f, 16.788f, 10.373f, 16.719f)
+ lineTo(11.294f, 14.876f)
+ curveTo(11.364f, 14.736f, 11.508f, 14.647f, 11.665f, 14.646f)
+ curveTo(12.566f, 14.642f, 13.236f, 14.624f, 13.768f, 14.551f)
+ curveTo(14.293f, 14.478f, 14.656f, 14.354f, 14.954f, 14.155f)
+ lineTo(15.071f, 14.073f)
+ curveTo(15.34f, 13.874f, 15.572f, 13.629f, 15.759f, 13.35f)
+ curveTo(15.986f, 13.01f, 16.115f, 12.583f, 16.182f, 11.926f)
+ curveTo(16.249f, 11.262f, 16.25f, 10.409f, 16.25f, 9.23f)
+ close()
+ moveTo(10.0f, 10.48f)
+ curveTo(10.23f, 10.48f, 10.417f, 10.667f, 10.417f, 10.897f)
+ curveTo(10.417f, 11.127f, 10.23f, 11.313f, 10.0f, 11.313f)
+ horizontalLineTo(7.5f)
+ curveTo(7.27f, 11.313f, 7.083f, 11.127f, 7.083f, 10.897f)
+ curveTo(7.083f, 10.667f, 7.27f, 10.48f, 7.5f, 10.48f)
+ horizontalLineTo(10.0f)
+ close()
+ moveTo(12.5f, 7.147f)
+ curveTo(12.73f, 7.147f, 12.917f, 7.333f, 12.917f, 7.563f)
+ curveTo(12.917f, 7.793f, 12.73f, 7.98f, 12.5f, 7.98f)
+ horizontalLineTo(7.5f)
+ curveTo(7.27f, 7.98f, 7.083f, 7.793f, 7.083f, 7.563f)
+ curveTo(7.083f, 7.333f, 7.27f, 7.147f, 7.5f, 7.147f)
+ horizontalLineTo(12.5f)
+ close()
+ moveTo(17.083f, 9.23f)
+ curveTo(17.083f, 10.392f, 17.084f, 11.295f, 17.011f, 12.011f)
+ curveTo(16.937f, 12.734f, 16.786f, 13.313f, 16.452f, 13.813f)
+ curveTo(16.178f, 14.223f, 15.826f, 14.575f, 15.417f, 14.849f)
+ lineTo(15.416f, 14.848f)
+ curveTo(14.979f, 15.14f, 14.483f, 15.293f, 13.882f, 15.376f)
+ curveTo(13.346f, 15.45f, 12.705f, 15.469f, 11.925f, 15.476f)
+ lineTo(11.117f, 17.092f)
+ curveTo(11.014f, 17.299f, 10.854f, 17.474f, 10.657f, 17.596f)
+ curveTo(10.46f, 17.718f, 10.232f, 17.782f, 10.0f, 17.782f)
+ curveTo(9.768f, 17.782f, 9.541f, 17.718f, 9.343f, 17.596f)
+ curveTo(9.146f, 17.474f, 8.987f, 17.299f, 8.883f, 17.092f)
+ lineTo(8.075f, 15.477f)
+ curveTo(7.295f, 15.47f, 6.655f, 15.45f, 6.118f, 15.376f)
+ curveTo(5.517f, 15.292f, 5.02f, 15.14f, 4.583f, 14.849f)
+ curveTo(4.174f, 14.575f, 3.822f, 14.223f, 3.548f, 13.813f)
+ curveTo(3.214f, 13.313f, 3.063f, 12.734f, 2.989f, 12.011f)
+ curveTo(2.916f, 11.295f, 2.917f, 10.392f, 2.917f, 9.23f)
+ curveTo(2.917f, 8.068f, 2.916f, 7.165f, 2.989f, 6.449f)
+ curveTo(3.063f, 5.726f, 3.214f, 5.147f, 3.548f, 4.647f)
+ curveTo(3.822f, 4.237f, 4.174f, 3.885f, 4.583f, 3.611f)
+ curveTo(5.084f, 3.277f, 5.663f, 3.126f, 6.386f, 3.052f)
+ curveTo(7.102f, 2.98f, 8.005f, 2.98f, 9.167f, 2.98f)
+ horizontalLineTo(10.833f)
+ curveTo(11.995f, 2.98f, 12.898f, 2.98f, 13.614f, 3.052f)
+ curveTo(14.337f, 3.126f, 14.917f, 3.277f, 15.417f, 3.611f)
+ curveTo(15.826f, 3.885f, 16.178f, 4.237f, 16.452f, 4.647f)
+ curveTo(16.786f, 5.147f, 16.937f, 5.726f, 17.011f, 6.449f)
+ curveTo(17.084f, 7.165f, 17.083f, 8.068f, 17.083f, 9.23f)
+ close()
+ }
+ }
+ .build()
+ return _recordComment!!
+ }
+
+private var _recordComment: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.RecordComment, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/RecordLike.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/RecordLike.kt
new file mode 100644
index 0000000..46408e5
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/RecordLike.kt
@@ -0,0 +1,148 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.RecordLike: ImageVector
+ get() {
+ if (_recordLike != null) {
+ return _recordLike!!
+ }
+ _recordLike = Builder(name = "RecordLike", defaultWidth = 20.0.dp, defaultHeight = 20.0.dp,
+ viewportWidth = 20.0f, viewportHeight = 20.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(3.127f, 4.487f)
+ curveTo(4.971f, 2.128f, 8.674f, 2.524f, 9.97f, 5.219f)
+ curveTo(9.973f, 5.225f, 9.976f, 5.23f, 9.981f, 5.233f)
+ curveTo(9.987f, 5.236f, 9.993f, 5.238f, 9.999f, 5.238f)
+ curveTo(10.006f, 5.238f, 10.012f, 5.236f, 10.017f, 5.233f)
+ curveTo(10.023f, 5.23f, 10.027f, 5.225f, 10.03f, 5.219f)
+ lineTo(10.158f, 4.973f)
+ curveTo(11.555f, 2.502f, 15.086f, 2.203f, 16.873f, 4.487f)
+ lineTo(17.133f, 4.819f)
+ curveTo(17.864f, 5.754f, 18.217f, 6.927f, 18.123f, 8.106f)
+ curveTo(18.03f, 9.286f, 17.497f, 10.388f, 16.627f, 11.198f)
+ lineTo(10.788f, 16.64f)
+ curveTo(10.693f, 16.728f, 10.601f, 16.816f, 10.516f, 16.881f)
+ curveTo(10.425f, 16.951f, 10.31f, 17.021f, 10.162f, 17.05f)
+ curveTo(10.055f, 17.071f, 9.944f, 17.071f, 9.837f, 17.05f)
+ horizontalLineTo(9.835f)
+ curveTo(9.687f, 17.021f, 9.572f, 16.949f, 9.483f, 16.88f)
+ curveTo(9.398f, 16.815f, 9.306f, 16.728f, 9.211f, 16.64f)
+ lineTo(2.598f, 10.477f)
+ horizontalLineTo(2.741f)
+ curveTo(2.247f, 9.783f, 1.944f, 8.965f, 1.876f, 8.106f)
+ curveTo(1.783f, 6.927f, 2.136f, 5.756f, 2.866f, 4.821f)
+ lineTo(3.127f, 4.487f)
+ close()
+ moveTo(9.211f, 5.578f)
+ curveTo(8.216f, 3.51f, 5.433f, 3.151f, 3.931f, 4.829f)
+ lineTo(3.79f, 4.998f)
+ lineTo(3.53f, 5.331f)
+ curveTo(2.928f, 6.102f, 2.637f, 7.068f, 2.714f, 8.04f)
+ curveTo(2.791f, 9.013f, 3.231f, 9.922f, 3.947f, 10.59f)
+ lineTo(4.72f, 11.31f)
+ lineTo(9.784f, 16.032f)
+ curveTo(9.891f, 16.132f, 9.951f, 16.186f, 9.996f, 16.221f)
+ curveTo(9.997f, 16.222f, 9.998f, 16.222f, 9.999f, 16.222f)
+ curveTo(10.0f, 16.222f, 10.002f, 16.222f, 10.003f, 16.221f)
+ curveTo(10.047f, 16.187f, 10.105f, 16.133f, 10.212f, 16.034f)
+ lineTo(10.213f, 16.033f)
+ lineTo(16.052f, 10.591f)
+ curveTo(16.769f, 9.923f, 17.21f, 9.014f, 17.287f, 8.041f)
+ curveTo(17.364f, 7.069f, 17.072f, 6.101f, 16.469f, 5.331f)
+ lineTo(16.209f, 4.998f)
+ curveTo(14.748f, 3.13f, 11.815f, 3.443f, 10.788f, 5.578f)
+ curveTo(10.716f, 5.725f, 10.604f, 5.851f, 10.465f, 5.938f)
+ curveTo(10.326f, 6.025f, 10.164f, 6.071f, 9.999f, 6.071f)
+ curveTo(9.835f, 6.071f, 9.674f, 6.025f, 9.534f, 5.938f)
+ curveTo(9.395f, 5.851f, 9.283f, 5.726f, 9.212f, 5.579f)
+ lineTo(9.211f, 5.578f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(3.127f, 4.487f)
+ curveTo(4.971f, 2.128f, 8.674f, 2.524f, 9.97f, 5.219f)
+ curveTo(9.973f, 5.225f, 9.976f, 5.23f, 9.981f, 5.233f)
+ curveTo(9.987f, 5.236f, 9.993f, 5.238f, 9.999f, 5.238f)
+ curveTo(10.006f, 5.238f, 10.012f, 5.236f, 10.017f, 5.233f)
+ curveTo(10.023f, 5.23f, 10.027f, 5.225f, 10.03f, 5.219f)
+ lineTo(10.158f, 4.973f)
+ curveTo(11.555f, 2.502f, 15.086f, 2.203f, 16.873f, 4.487f)
+ lineTo(17.133f, 4.819f)
+ curveTo(17.864f, 5.754f, 18.217f, 6.927f, 18.123f, 8.106f)
+ curveTo(18.03f, 9.286f, 17.497f, 10.388f, 16.627f, 11.198f)
+ lineTo(10.788f, 16.64f)
+ curveTo(10.693f, 16.728f, 10.601f, 16.816f, 10.516f, 16.881f)
+ curveTo(10.425f, 16.951f, 10.31f, 17.021f, 10.162f, 17.05f)
+ curveTo(10.055f, 17.071f, 9.944f, 17.071f, 9.837f, 17.05f)
+ horizontalLineTo(9.835f)
+ curveTo(9.687f, 17.021f, 9.572f, 16.949f, 9.483f, 16.88f)
+ curveTo(9.398f, 16.815f, 9.306f, 16.728f, 9.211f, 16.64f)
+ lineTo(2.598f, 10.477f)
+ horizontalLineTo(2.741f)
+ curveTo(2.247f, 9.783f, 1.944f, 8.965f, 1.876f, 8.106f)
+ curveTo(1.783f, 6.927f, 2.136f, 5.756f, 2.866f, 4.821f)
+ lineTo(3.127f, 4.487f)
+ close()
+ moveTo(9.211f, 5.578f)
+ curveTo(8.216f, 3.51f, 5.433f, 3.151f, 3.931f, 4.829f)
+ lineTo(3.79f, 4.998f)
+ lineTo(3.53f, 5.331f)
+ curveTo(2.928f, 6.102f, 2.637f, 7.068f, 2.714f, 8.04f)
+ curveTo(2.791f, 9.013f, 3.231f, 9.922f, 3.947f, 10.59f)
+ lineTo(4.72f, 11.31f)
+ lineTo(9.784f, 16.032f)
+ curveTo(9.891f, 16.132f, 9.951f, 16.186f, 9.996f, 16.221f)
+ curveTo(9.997f, 16.222f, 9.998f, 16.222f, 9.999f, 16.222f)
+ curveTo(10.0f, 16.222f, 10.002f, 16.222f, 10.003f, 16.221f)
+ curveTo(10.047f, 16.187f, 10.105f, 16.133f, 10.212f, 16.034f)
+ lineTo(10.213f, 16.033f)
+ lineTo(16.052f, 10.591f)
+ curveTo(16.769f, 9.923f, 17.21f, 9.014f, 17.287f, 8.041f)
+ curveTo(17.364f, 7.069f, 17.072f, 6.101f, 16.469f, 5.331f)
+ lineTo(16.209f, 4.998f)
+ curveTo(14.748f, 3.13f, 11.815f, 3.443f, 10.788f, 5.578f)
+ curveTo(10.716f, 5.725f, 10.604f, 5.851f, 10.465f, 5.938f)
+ curveTo(10.326f, 6.025f, 10.164f, 6.071f, 9.999f, 6.071f)
+ curveTo(9.835f, 6.071f, 9.674f, 6.025f, 9.534f, 5.938f)
+ curveTo(9.395f, 5.851f, 9.283f, 5.726f, 9.212f, 5.579f)
+ lineTo(9.211f, 5.578f)
+ close()
+ }
+ }
+ .build()
+ return _recordLike!!
+ }
+
+private var _recordLike: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.RecordLike, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/RecordSurprise.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/RecordSurprise.kt
new file mode 100644
index 0000000..1564609
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/RecordSurprise.kt
@@ -0,0 +1,148 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.RecordSurprise: ImageVector
+ get() {
+ if (_recordSurprise != null) {
+ return _recordSurprise!!
+ }
+ _recordSurprise = Builder(name = "RecordSurprise", defaultWidth = 20.0.dp, defaultHeight =
+ 20.0.dp, viewportWidth = 20.0f, viewportHeight = 20.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(17.2f, 10.0f)
+ curveTo(17.2f, 6.024f, 13.976f, 2.8f, 10.0f, 2.8f)
+ curveTo(6.024f, 2.8f, 2.8f, 6.024f, 2.8f, 10.0f)
+ curveTo(2.8f, 13.976f, 6.024f, 17.2f, 10.0f, 17.2f)
+ curveTo(13.976f, 17.2f, 17.2f, 13.976f, 17.2f, 10.0f)
+ close()
+ moveTo(18.0f, 10.0f)
+ curveTo(18.0f, 14.418f, 14.418f, 18.0f, 10.0f, 18.0f)
+ curveTo(5.582f, 18.0f, 2.0f, 14.418f, 2.0f, 10.0f)
+ curveTo(2.0f, 5.582f, 5.582f, 2.0f, 10.0f, 2.0f)
+ curveTo(14.418f, 2.0f, 18.0f, 5.582f, 18.0f, 10.0f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(17.2f, 10.0f)
+ curveTo(17.2f, 6.024f, 13.976f, 2.8f, 10.0f, 2.8f)
+ curveTo(6.024f, 2.8f, 2.8f, 6.024f, 2.8f, 10.0f)
+ curveTo(2.8f, 13.976f, 6.024f, 17.2f, 10.0f, 17.2f)
+ curveTo(13.976f, 17.2f, 17.2f, 13.976f, 17.2f, 10.0f)
+ close()
+ moveTo(18.0f, 10.0f)
+ curveTo(18.0f, 14.418f, 14.418f, 18.0f, 10.0f, 18.0f)
+ curveTo(5.582f, 18.0f, 2.0f, 14.418f, 2.0f, 10.0f)
+ curveTo(2.0f, 5.582f, 5.582f, 2.0f, 10.0f, 2.0f)
+ curveTo(14.418f, 2.0f, 18.0f, 5.582f, 18.0f, 10.0f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(7.12f, 8.4f)
+ curveTo(7.562f, 8.4f, 7.92f, 8.042f, 7.92f, 7.6f)
+ curveTo(7.92f, 7.158f, 7.562f, 6.8f, 7.12f, 6.8f)
+ curveTo(6.678f, 6.8f, 6.32f, 7.158f, 6.32f, 7.6f)
+ curveTo(6.32f, 8.042f, 6.678f, 8.4f, 7.12f, 8.4f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(7.12f, 8.4f)
+ curveTo(7.562f, 8.4f, 7.92f, 8.042f, 7.92f, 7.6f)
+ curveTo(7.92f, 7.158f, 7.562f, 6.8f, 7.12f, 6.8f)
+ curveTo(6.678f, 6.8f, 6.32f, 7.158f, 6.32f, 7.6f)
+ curveTo(6.32f, 8.042f, 6.678f, 8.4f, 7.12f, 8.4f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(12.88f, 8.4f)
+ curveTo(13.322f, 8.4f, 13.68f, 8.042f, 13.68f, 7.6f)
+ curveTo(13.68f, 7.158f, 13.322f, 6.8f, 12.88f, 6.8f)
+ curveTo(12.438f, 6.8f, 12.08f, 7.158f, 12.08f, 7.6f)
+ curveTo(12.08f, 8.042f, 12.438f, 8.4f, 12.88f, 8.4f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(12.88f, 8.4f)
+ curveTo(13.322f, 8.4f, 13.68f, 8.042f, 13.68f, 7.6f)
+ curveTo(13.68f, 7.158f, 13.322f, 6.8f, 12.88f, 6.8f)
+ curveTo(12.438f, 6.8f, 12.08f, 7.158f, 12.08f, 7.6f)
+ curveTo(12.08f, 8.042f, 12.438f, 8.4f, 12.88f, 8.4f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(11.6f, 12.8f)
+ curveTo(11.6f, 12.268f, 11.026f, 11.6f, 10.0f, 11.6f)
+ curveTo(8.974f, 11.6f, 8.4f, 12.268f, 8.4f, 12.8f)
+ curveTo(8.4f, 13.332f, 8.974f, 14.0f, 10.0f, 14.0f)
+ verticalLineTo(14.8f)
+ curveTo(8.674f, 14.8f, 7.6f, 13.904f, 7.6f, 12.8f)
+ curveTo(7.6f, 11.696f, 8.674f, 10.8f, 10.0f, 10.8f)
+ curveTo(11.325f, 10.8f, 12.4f, 11.696f, 12.4f, 12.8f)
+ curveTo(12.4f, 13.904f, 11.325f, 14.8f, 10.0f, 14.8f)
+ verticalLineTo(14.0f)
+ curveTo(11.026f, 14.0f, 11.6f, 13.332f, 11.6f, 12.8f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(11.6f, 12.8f)
+ curveTo(11.6f, 12.268f, 11.026f, 11.6f, 10.0f, 11.6f)
+ curveTo(8.974f, 11.6f, 8.4f, 12.268f, 8.4f, 12.8f)
+ curveTo(8.4f, 13.332f, 8.974f, 14.0f, 10.0f, 14.0f)
+ verticalLineTo(14.8f)
+ curveTo(8.674f, 14.8f, 7.6f, 13.904f, 7.6f, 12.8f)
+ curveTo(7.6f, 11.696f, 8.674f, 10.8f, 10.0f, 10.8f)
+ curveTo(11.325f, 10.8f, 12.4f, 11.696f, 12.4f, 12.8f)
+ curveTo(12.4f, 13.904f, 11.325f, 14.8f, 10.0f, 14.8f)
+ verticalLineTo(14.0f)
+ curveTo(11.026f, 14.0f, 11.6f, 13.332f, 11.6f, 12.8f)
+ close()
+ }
+ }
+ .build()
+ return _recordSurprise!!
+ }
+
+private var _recordSurprise: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.RecordSurprise, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/ShareTriangle.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/ShareTriangle.kt
new file mode 100644
index 0000000..caf6778
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/ShareTriangle.kt
@@ -0,0 +1,62 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.ShareTriangle: ImageVector
+ get() {
+ if (_shareTriangle != null) {
+ return _shareTriangle!!
+ }
+ _shareTriangle = Builder(name = "ShareTriangle", defaultWidth = 12.0.dp, defaultHeight =
+ 8.0.dp, viewportWidth = 12.0f, viewportHeight = 8.0f).apply {
+ path(fill = SolidColor(Color(0xFF6E8B74)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(6.0f, 8.0f)
+ lineTo(12.0f, 0.0f)
+ horizontalLineTo(0.0f)
+ lineTo(6.0f, 8.0f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFffffff)), stroke = null, fillAlpha = 0.6f,
+ strokeLineWidth = 0.0f, strokeLineCap = Butt, strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f, pathFillType = NonZero) {
+ moveTo(6.0f, 8.0f)
+ lineTo(12.0f, 0.0f)
+ horizontalLineTo(0.0f)
+ lineTo(6.0f, 8.0f)
+ close()
+ }
+ }
+ .build()
+ return _shareTriangle!!
+ }
+
+private var _shareTriangle: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.ShareTriangle, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Year.kt b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Year.kt
new file mode 100644
index 0000000..7908cf6
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/icon/appicons/Year.kt
@@ -0,0 +1,100 @@
+package com.min.dnapp.presentation.ui.icon.appicons
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import kotlin.Unit
+
+public val AppIcons.Year: ImageVector
+ get() {
+ if (_year != null) {
+ return _year!!
+ }
+ _year = Builder(name = "Year", defaultWidth = 15.0.dp, defaultHeight = 18.0.dp,
+ viewportWidth = 15.0f, viewportHeight = 18.0f).apply {
+ path(fill = SolidColor(Color(0xFFA56C48)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(12.983f, 7.833f)
+ curveTo(12.983f, 7.465f, 12.685f, 7.167f, 12.317f, 7.167f)
+ horizontalLineTo(2.983f)
+ curveTo(2.615f, 7.167f, 2.317f, 7.465f, 2.317f, 7.833f)
+ verticalLineTo(15.167f)
+ curveTo(2.317f, 15.535f, 2.615f, 15.833f, 2.983f, 15.833f)
+ horizontalLineTo(12.317f)
+ curveTo(12.685f, 15.833f, 12.983f, 15.535f, 12.983f, 15.167f)
+ verticalLineTo(7.833f)
+ close()
+ moveTo(14.317f, 15.167f)
+ curveTo(14.317f, 16.271f, 13.421f, 17.167f, 12.317f, 17.167f)
+ horizontalLineTo(2.983f)
+ curveTo(1.879f, 17.167f, 0.983f, 16.271f, 0.983f, 15.167f)
+ verticalLineTo(7.833f)
+ curveTo(0.983f, 6.729f, 1.879f, 5.833f, 2.983f, 5.833f)
+ horizontalLineTo(12.317f)
+ curveTo(13.421f, 5.833f, 14.317f, 6.729f, 14.317f, 7.833f)
+ verticalLineTo(15.167f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFA56C48)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(1.65f, 7.333f)
+ curveTo(1.65f, 6.076f, 1.65f, 5.448f, 2.041f, 5.057f)
+ curveTo(2.431f, 4.667f, 3.059f, 4.667f, 4.317f, 4.667f)
+ horizontalLineTo(10.983f)
+ curveTo(12.241f, 4.667f, 12.869f, 4.667f, 13.259f, 5.057f)
+ curveTo(13.65f, 5.448f, 13.65f, 6.076f, 13.65f, 7.333f)
+ horizontalLineTo(1.65f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFFA56C48)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(3.65f, 3.5f)
+ verticalLineTo(1.5f)
+ curveTo(3.65f, 1.132f, 3.948f, 0.833f, 4.317f, 0.833f)
+ curveTo(4.685f, 0.833f, 4.983f, 1.132f, 4.983f, 1.5f)
+ verticalLineTo(3.5f)
+ curveTo(4.983f, 3.868f, 4.685f, 4.167f, 4.317f, 4.167f)
+ curveTo(3.948f, 4.167f, 3.65f, 3.868f, 3.65f, 3.5f)
+ close()
+ moveTo(10.317f, 3.5f)
+ verticalLineTo(1.5f)
+ curveTo(10.317f, 1.132f, 10.615f, 0.833f, 10.983f, 0.833f)
+ curveTo(11.351f, 0.833f, 11.65f, 1.132f, 11.65f, 1.5f)
+ verticalLineTo(3.5f)
+ curveTo(11.65f, 3.868f, 11.351f, 4.167f, 10.983f, 4.167f)
+ curveTo(10.615f, 4.167f, 10.317f, 3.868f, 10.317f, 3.5f)
+ close()
+ }
+ }
+ .build()
+ return _year!!
+ }
+
+private var _year: ImageVector? = null
+
+@Preview
+@Composable
+private fun Preview(): Unit {
+ Box(modifier = Modifier.padding(12.dp)) {
+ Image(imageVector = AppIcons.Year, contentDescription = "")
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/profile/ProfileImageCircle.kt b/app/src/main/java/com/min/dnapp/presentation/ui/profile/ProfileImageCircle.kt
new file mode 100644
index 0000000..de0c349
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/profile/ProfileImageCircle.kt
@@ -0,0 +1,34 @@
+package com.min.dnapp.presentation.ui.profile
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.common.ProfileMapper
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun ProfileImageCircle(
+ profileImageName: String?,
+ modifier: Modifier = Modifier
+) {
+ profileImageName?.let { name ->
+ val imageResId = ProfileMapper.getProfileImageResId(name)
+
+ Box(
+ modifier = modifier
+ .clip(CircleShape)
+ .border(width = 2.dp, color = MomentoTheme.colors.grayW90, shape = CircleShape)
+ ) {
+ Image(
+ painter = painterResource(imageResId),
+ contentDescription = null
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/theme/Color.kt b/app/src/main/java/com/min/dnapp/presentation/ui/theme/Color.kt
new file mode 100644
index 0000000..01ed4d2
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/theme/Color.kt
@@ -0,0 +1,49 @@
+package com.min.dnapp.presentation.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+// primary palette
+val Brown_Bg = Color(0xFFFDFCFB)
+val Brown_W90 = Color(0xFFF6F0ED)
+val Brown_W80 = Color(0xFFEDE2DA)
+val Brown_W70 = Color(0xFFE4D3C8)
+val Brown_W60 = Color(0xFFE4D3C8)
+val Brown_W50 = Color(0xFFD2B5A3)
+val Brown_W40 = Color(0xFFC9A791)
+val Brown_W20 = Color(0xFFB7896D)
+val Brown_Base = Color(0xFFA56C48)
+val Brown_B20 = Color(0xFF84563A)
+val Brown_B40 = Color(0xFF63412B)
+val Brown_B60 = Color(0xFF422B1D)
+val Brown_B80 = Color(0xFF21160E)
+
+// secondary palette
+val Pink_W80 = Color(0xFFFBF0ED)
+val Pink_W60 = Color(0xFFF6E2DB)
+val Pink_W40 = Color(0xFFF2D3CA)
+val Pink_W20 = Color(0xFFEDC5B8)
+val Pink_Base = Color(0xFFE9B6A6)
+val Pink_B20 = Color(0xFFBA9285)
+val Pink_B40 = Color(0xFF8C6D64)
+val Pink_B60 = Color(0xFF5D4942)
+val Pink_B80 = Color(0xFF2F2421)
+
+val Green_W80 = Color(0xFFE2E8E3)
+val Green_W60 = Color(0xFFC5D1C7)
+val Green_W40 = Color(0xFFA8B9AC)
+val Green_W20 = Color(0xFF8BA290)
+val Green_Base = Color(0xFF6E8B74)
+
+// gray
+val Gray_W10 = Color(0xFF1B1D1F)
+val Gray_W20 = Color(0xFF333333)
+val Gray_W40 = Color(0xFF666666)
+val Gray_W60 = Color(0xFF999999)
+val Gray_W80 = Color(0xFFCCCCCC)
+val Gray_W90 = Color(0xFFE5E5E5)
+val Black = Color(0xFF000000)
+val White = Color(0xFFFFFFFF)
+
+// 기타
+val KakaoYellow = Color(0xFFFEE500)
+val ErrorRed = Color(0xFFEF5350)
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/theme/Theme.kt b/app/src/main/java/com/min/dnapp/presentation/ui/theme/Theme.kt
new file mode 100644
index 0000000..3422df4
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/theme/Theme.kt
@@ -0,0 +1,154 @@
+package com.min.dnapp.presentation.ui.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.staticCompositionLocalOf
+import androidx.compose.ui.graphics.Color
+
+@Immutable
+data class MomentoColors(
+ // primary (brown)
+ val brownBg: Color,
+ val brownW90: Color,
+ val brownW80: Color,
+ val brownW70: Color,
+ val brownW60: Color,
+ val brownW50: Color,
+ val brownW40: Color,
+ val brownW20: Color,
+ val brownBase: Color,
+ val brownB20: Color,
+ val brownB40: Color,
+ val brownB60: Color,
+ val brownB80: Color,
+
+ // secondary (pink)
+ val pinkW80: Color,
+ val pinkW60: Color,
+ val pinkW40: Color,
+ val pinkW20: Color,
+ val pinkBase: Color,
+ val pinkB20: Color,
+ val pinkB40: Color,
+ val pinkB60: Color,
+ val pinkB80: Color,
+
+ // secondary (green)
+ val greenW80: Color,
+ val greenW60: Color,
+ val greenW40: Color,
+ val greenW20: Color,
+ val greenBase: Color,
+
+ // gray
+ val grayW10: Color,
+ val grayW20: Color,
+ val grayW40: Color,
+ val grayW60: Color,
+ val grayW80: Color,
+ val grayW90: Color,
+
+ val black: Color,
+ val white: Color,
+
+ val isDark: Boolean
+)
+
+private val LightColorPalette = MomentoColors(
+ brownBg = Brown_Bg,
+ brownW90 = Brown_W90,
+ brownW80 = Brown_W80,
+ brownW70 = Brown_W70,
+ brownW60 = Brown_W60,
+ brownW50 = Brown_W50,
+ brownW40 = Brown_W40,
+ brownW20 = Brown_W20,
+ brownBase = Brown_Base,
+ brownB20 = Brown_B20,
+ brownB40 = Brown_B40,
+ brownB60 = Brown_B60,
+ brownB80 = Brown_B80,
+ pinkW80 = Pink_W80,
+ pinkW60 = Pink_W60,
+ pinkW40 = Pink_W40,
+ pinkW20 = Pink_W20,
+ pinkBase = Pink_Base,
+ pinkB20 = Pink_B20,
+ pinkB40 = Pink_B40,
+ pinkB60 = Pink_B60,
+ pinkB80 = Pink_B80,
+ greenW80 = Green_W80,
+ greenW60 = Green_W60,
+ greenW40 = Green_W40,
+ greenW20 = Green_W20,
+ greenBase = Green_Base,
+ grayW10 = Gray_W10,
+ grayW20 = Gray_W20,
+ grayW40 = Gray_W40,
+ grayW60 = Gray_W60,
+ grayW80 = Gray_W80,
+ grayW90 = Gray_W90,
+ black = Black,
+ white = White,
+ isDark = false
+)
+
+private val DarkColorPalette = LightColorPalette.copy(
+ isDark = true
+)
+
+/**
+ * CompositionLocal을 통해 커스텀 color palette 제공
+ */
+private val LocalMomentoColors = staticCompositionLocalOf {
+ error("No MomentoColorPalette provided")
+}
+
+// Material3 기본 ColorScheme에 커스텀 color 매핑
+private fun momentoLightColorScheme(colors: MomentoColors) = lightColorScheme(
+ primary = colors.brownBase,
+ onPrimary = colors.white,
+ background = colors.brownBg,
+ onBackground = colors.black,
+ surface = colors.white,
+ onSurface = colors.grayW10
+)
+
+@Composable
+fun DngoTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ content: @Composable () -> Unit
+) {
+ val appTypography = getAppTypography()
+ val colors = if (darkTheme) DarkColorPalette else LightColorPalette
+
+ CompositionLocalProvider(
+ LocalAppTypography provides appTypography,
+ LocalMomentoColors provides colors
+ ) {
+ MaterialTheme(
+ colorScheme = momentoLightColorScheme(colors),
+ content = content
+ )
+ }
+}
+
+/**
+ * Momento 디자인 속성에 접근하기 위한 객체
+ */
+object MomentoTheme {
+ val typography: AppTypography
+ @Composable
+ @ReadOnlyComposable
+ get() = LocalAppTypography.current
+
+ val colors: MomentoColors
+ @Composable
+ @ReadOnlyComposable
+ get() = LocalMomentoColors.current
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/ui/theme/Type.kt b/app/src/main/java/com/min/dnapp/presentation/ui/theme/Type.kt
new file mode 100644
index 0000000..0c073d4
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/ui/theme/Type.kt
@@ -0,0 +1,101 @@
+package com.min.dnapp.presentation.ui.theme
+
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.staticCompositionLocalOf
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.Font
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.em
+import androidx.compose.ui.unit.sp
+import com.min.dnapp.R
+
+private val pretendard = FontFamily(
+ Font(resId = R.font.pretendard_regular, weight = FontWeight.Normal),
+ Font(resId = R.font.pretendard_medium, weight = FontWeight.Medium),
+ Font(resId = R.font.pretendard_semibold, weight = FontWeight.SemiBold),
+ Font(resId = R.font.pretendard_bold, weight = FontWeight.Bold)
+)
+
+/**
+ * 커스텀 폰트 스타일 정의
+ */
+@Immutable
+data class AppTypography(
+ val display: TextStyle,
+ val title01: TextStyle,
+ val title02: TextStyle,
+ val body01: TextStyle,
+ val body02: TextStyle,
+ val body03: TextStyle,
+ val label: TextStyle,
+ val caption: TextStyle
+)
+
+/**
+ * CompositionLocal을 통해 커스텀 typography 제공
+ */
+val LocalAppTypography = staticCompositionLocalOf {
+ error("No AppTypography provided")
+}
+
+fun getAppTypography(): AppTypography {
+ return AppTypography(
+ display = TextStyle(
+ fontFamily = pretendard,
+ fontWeight = FontWeight.Bold,
+ fontSize = 24.sp,
+ lineHeight = (24 * 1.3).sp, // 130%
+ letterSpacing = (-0.01).em // -1%
+ ),
+ title01 = TextStyle(
+ fontFamily = pretendard,
+ fontWeight = FontWeight.Medium,
+ fontSize = 20.sp,
+ lineHeight = (20 * 1.3).sp,
+ letterSpacing = (-0.01).em
+ ),
+ title02 = TextStyle(
+ fontFamily = pretendard,
+ fontWeight = FontWeight.SemiBold,
+ fontSize = 16.sp,
+ lineHeight = (16 * 1.4).sp,
+ letterSpacing = (-0.01).em
+ ),
+ body01 = TextStyle(
+ fontFamily = pretendard,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = (16 * 1.4).sp,
+ letterSpacing = (-0.01).em
+ ),
+ body02 = TextStyle(
+ fontFamily = pretendard,
+ fontWeight = FontWeight.Normal,
+ fontSize = 14.sp,
+ lineHeight = (14 * 1.4).sp,
+ letterSpacing = (-0.01).em
+ ),
+ body03 = TextStyle(
+ fontFamily = pretendard,
+ fontWeight = FontWeight.Normal,
+ fontSize = 12.sp,
+ lineHeight = (12 * 1.4).sp,
+ letterSpacing = (-0.01).em
+ ),
+ label = TextStyle(
+ fontFamily = pretendard,
+ fontWeight = FontWeight.Medium,
+ fontSize = 12.sp,
+ lineHeight = (12 * 1.3).sp,
+ letterSpacing = (-0.01).em
+ ),
+ caption = TextStyle(
+ fontFamily = pretendard,
+ fontWeight = FontWeight.Normal,
+ fontSize = 10.sp,
+ lineHeight = (10 * 1.3).sp,
+ letterSpacing = (-0.01).em
+ )
+ )
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/write/CheckBadgeViewModel.kt b/app/src/main/java/com/min/dnapp/presentation/write/CheckBadgeViewModel.kt
new file mode 100644
index 0000000..3c17c45
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/write/CheckBadgeViewModel.kt
@@ -0,0 +1,80 @@
+package com.min.dnapp.presentation.write
+
+import android.util.Log
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.min.dnapp.domain.usecase.GetBadgeDialogDataUseCase
+import com.min.dnapp.domain.usecase.GetCurrentUserIdUseCase
+import com.min.dnapp.domain.usecase.GetUserDataUseCase
+import com.min.dnapp.domain.usecase.UpdateUserBadgeUseCase
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class CheckBadgeViewModel @Inject constructor(
+ private val getCurrentUserIdUseCase: GetCurrentUserIdUseCase,
+ private val getUserDataUseCase: GetUserDataUseCase,
+ private val getBadgeDialogDataUseCase: GetBadgeDialogDataUseCase,
+ private val updateUserBadgeUseCase: UpdateUserBadgeUseCase
+) : ViewModel() {
+
+ private val _dialogState = MutableStateFlow(WriteFinishDialogState.Hidden)
+ val dialogState: StateFlow = _dialogState.asStateFlow()
+
+ init {
+ checkNewBadge()
+ }
+
+ /**
+ * 새로운 뱃지 달성 여부 체크
+ */
+ fun checkNewBadge() {
+ viewModelScope.launch {
+
+ // 사용자 ID 가져오기
+ val uid = try {
+ getCurrentUserIdUseCase() ?: throw Exception("사용자 인증 정보 없음")
+ } catch (e: Exception) {
+ Log.e("write", "사용자 정보 조회 실패", e)
+ return@launch
+ }
+
+ val useData = getUserDataUseCase(uid)
+ val userStamp = useData.stampCnt
+ val newBadge = getBadgeDialogDataUseCase(userStamp)
+
+ // 뱃지 업데이트 DB에 반영
+ if (newBadge != null) {
+ val result = updateUserBadgeUseCase(newBadge)
+
+ // DB 업데이트 실패 시
+ if (result.isFailure) {
+ Log.e("user", "뱃지 DB 업데이트 실패: ${result.exceptionOrNull()}")
+
+ _dialogState.value = WriteFinishDialogState.StampDialog
+ return@launch
+ }
+ }
+
+ // 1초 지연 후 모달 표시
+ delay(1000)
+
+ _dialogState.value = if (newBadge != null) {
+ // 뱃지 모달 표시
+ WriteFinishDialogState.BadgeDialog(newBadge)
+ } else {
+ // 스탬프 모달 표시
+ WriteFinishDialogState.StampDialog
+ }
+ }
+ }
+
+ fun closeDialog() {
+ _dialogState.value = WriteFinishDialogState.Hidden
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/write/RecordWriteScreen.kt b/app/src/main/java/com/min/dnapp/presentation/write/RecordWriteScreen.kt
new file mode 100644
index 0000000..ce564f0
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/write/RecordWriteScreen.kt
@@ -0,0 +1,925 @@
+package com.min.dnapp.presentation.write
+
+import android.content.Intent
+import android.net.Uri
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.navigationBarsPadding
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.CenterAlignedTopAppBar
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.DatePickerDefaults
+import androidx.compose.material3.DatePickerDialog
+import androidx.compose.material3.DateRangePicker
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.ModalBottomSheet
+import androidx.compose.material3.RadioButton
+import androidx.compose.material3.RadioButtonDefaults
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.SnackbarDuration
+import androidx.compose.material3.SnackbarHost
+import androidx.compose.material3.SnackbarHostState
+import androidx.compose.material3.Switch
+import androidx.compose.material3.SwitchDefaults
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.rememberDateRangePickerState
+import androidx.compose.material3.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.draw.scale
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.NavHostController
+import coil3.compose.AsyncImage
+import com.min.dnapp.R
+import com.min.dnapp.domain.model.EmotionType
+import com.min.dnapp.domain.model.LocalPlace
+import com.min.dnapp.domain.model.WeatherType
+import com.min.dnapp.presentation.ui.component.CustomSnackbar
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import com.min.dnapp.presentation.ui.icon.appicons.Back
+import com.min.dnapp.presentation.ui.icon.appicons.Calendar
+import com.min.dnapp.presentation.ui.icon.appicons.Delete
+import com.min.dnapp.presentation.ui.icon.appicons.Gallery
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+import com.min.dnapp.presentation.write.component.EmotionBottomSheetContent
+import com.min.dnapp.presentation.write.component.PlaceBottomSheetContent
+import com.min.dnapp.presentation.write.component.PlaceWarningDialog
+import com.min.dnapp.presentation.write.component.ShareGuide
+import com.min.dnapp.presentation.write.component.WeatherBottomSheetContent
+import com.min.dnapp.util.toLocalDate
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun RecordWriteScreen(
+ navController: NavHostController,
+ viewModel: RecordWriteViewModel = hiltViewModel()
+) {
+ val uiState by viewModel.uiState.collectAsStateWithLifecycle()
+ val context = LocalContext.current
+
+ val snackbarHostState = remember { SnackbarHostState() }
+
+ // 공유 안내 말풍선 표시상태
+ var isShareGuideVisible by remember { mutableStateOf(true) }
+
+ // 캘린더 모달 표시상태
+ var showDatePicker by remember { mutableStateOf(false) }
+
+ var showEmotionBottomSheet by remember { mutableStateOf(false) }
+ var showWeatherBottomSheet by remember { mutableStateOf(false) }
+
+ // 여행지 추가 모달 표시상태
+ var showPlaceBottomSheet by remember { mutableStateOf(false) }
+ val sheetState = rememberModalBottomSheetState(
+ // halfExpanded 상태 건너뛰기
+ skipPartiallyExpanded = true
+ )
+ // 저장된 여행지 삭제 경고 모달
+ var showPlaceWarning by remember { mutableStateOf(false) }
+
+ // Photo Picker 런처 등록
+ val singlePhotoPickerLauncher = rememberLauncherForActivityResult(
+ contract = ActivityResultContracts.PickVisualMedia()
+ ) { uri ->
+ // URI 결과 전달
+ viewModel.onPhotoSelected(uri)
+
+ // URI 접근권한 지속적으로 요청
+ if (uri != null) {
+ val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
+ context.contentResolver.takePersistableUriPermission(uri, flags)
+ }
+ }
+
+ // Photo Picker 실행 요청
+ LaunchedEffect(viewModel.imageEvent) {
+ viewModel.imageEvent.collect { shouldLaunch ->
+ if (shouldLaunch) {
+ // Photo Picker 실행
+ singlePhotoPickerLauncher.launch(
+ input = PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
+ )
+ // 이벤트 처리 후 viewModel에 알림
+ viewModel.photoPickerEventHandled()
+ }
+ }
+ }
+
+ // 1회성 이벤트 Flow 수집 및 처리
+ LaunchedEffect(Unit) {
+ // 구독
+ viewModel.completeSaveRecordFlow.collect {
+ // 기록저장 성공 후 화면 이동
+ navController.navigate("write_finish") {
+ popUpTo("record_write") {
+ inclusive = true
+ }
+ }
+ }
+ }
+
+ // 메시지 발행을 수집하여 스낵바 표시
+ LaunchedEffect(Unit) {
+ viewModel.snackbarMessage.collect { message ->
+ snackbarHostState.showSnackbar(
+ message = message.message,
+ duration = SnackbarDuration.Short
+ )
+ }
+ }
+
+ Scaffold(
+ containerColor = MomentoTheme.colors.brownW90,
+ topBar = {
+ CenterAlignedTopAppBar(
+ title = {
+ Text(
+ text = "기록 작성하기",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW20
+ )
+ },
+ colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
+ containerColor = MomentoTheme.colors.brownW90
+ ),
+ navigationIcon = {
+ Icon(
+ modifier = Modifier
+ .clickable { navController.popBackStack() }
+ .padding(16.dp),
+ imageVector = AppIcons.Back,
+ contentDescription = null
+ )
+ },
+ actions = {
+ Text(
+ modifier = Modifier
+ .clickable {
+ viewModel.saveRecord()
+ }
+ .padding(16.dp),
+ text = "완료",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+ )
+ },
+ bottomBar = {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .navigationBarsPadding(),
+ horizontalAlignment = Alignment.End
+ ) {
+ // 공유 안내 말풍선
+ if (isShareGuideVisible) {
+ ShareGuide(
+ onClick = { isShareGuideVisible = false }
+ )
+ }
+
+ // 이미지 아이콘 & 공유여부 스위치 영역
+ ImageAndShareSection(
+ isChecked = uiState.isShareChecked,
+ onCheckedChange = { newChecked ->
+ viewModel.updateShare(newChecked)
+ },
+ onGalleryClick = { viewModel.onGalleryIconClicked() }
+ )
+ }
+ },
+ snackbarHost = {
+ SnackbarHost(hostState = snackbarHostState) { data ->
+ CustomSnackbar(
+ snackbarData = data
+ )
+ }
+ }
+ ) { paddingValues ->
+ Column(
+ modifier = Modifier
+ .padding(paddingValues)
+ .fillMaxSize()
+ .verticalScroll(rememberScrollState()),
+ verticalArrangement = Arrangement.SpaceBetween
+ ) {
+ Column(
+ modifier = Modifier
+ .padding(horizontal = 20.dp)
+ .fillMaxWidth()
+ ) {
+ Spacer(Modifier.height(20.dp))
+
+ // 날짜 영역
+ WriteDateSection(
+ startMillis = uiState.selectedStartDateMillis,
+ endMillis = uiState.selectedEndDateMillis,
+ onClick = { showDatePicker = true }
+ )
+
+ Spacer(Modifier.height(20.dp))
+
+ // 감정 & 날씨 영역
+ EmotionAndWeatherSection(
+ selectedEmotion = uiState.selectedEmotion,
+ selectedWeather = uiState.selectedWeather,
+ onClickEmotion = { showEmotionBottomSheet = true },
+ onClickWeather = { showWeatherBottomSheet = true }
+ )
+
+ Spacer(Modifier.height(20.dp))
+
+ // 제목 영역
+ WriteTitleSection(
+ recordTitle = uiState.recordTitle,
+ onValueChange = { newValue ->
+ viewModel.updateTitle(newValue)
+ }
+ )
+
+ Spacer(Modifier.height(20.dp))
+
+ // 여행지 영역
+ WritePlaceSection(
+ selectedPlace = uiState.selectedPlace,
+ overseasPlace = uiState.overseasPlace,
+ onValueChange = { newValue ->
+ // 해외 여행지 입력 시, 국내 여행지 저장된 경우
+ if (uiState.selectedPlace != null) { viewModel.clearSelectedPlace() }
+ viewModel.updateOverseas(newValue)
+ },
+ onAddClick = {
+ // 국내 여행지 추가 클릭 시, 해외 여행지 저장된 경우
+ if (uiState.overseasPlace.isNotEmpty()) { viewModel.clearOverseasPlace() }
+ showPlaceBottomSheet = true
+ },
+ onWarningClick = { showPlaceWarning = true }
+ )
+
+ Spacer(Modifier.height(40.dp))
+
+ // 내용 영역
+ WriteContentSection(
+ selectedImageUri = uiState.selectedImageUri,
+ recordContent = uiState.recordContent,
+ onValueChange = { newValue ->
+ viewModel.updateContent(newValue)
+ },
+ onClick = { viewModel.clearSelectedImageUri() }
+ )
+ }
+ }
+ }
+
+ // 기록 등록 진행 시 로딩 인디케이터 표시
+ if (uiState.isSaving) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(MomentoTheme.colors.black.copy(alpha = 0.5f)),
+ contentAlignment = Alignment.Center
+ ) {
+ CircularProgressIndicator(
+ modifier = Modifier.size(40.dp),
+ color = MomentoTheme.colors.white,
+ strokeWidth = 4.dp
+ )
+ }
+ }
+
+ // 캘린더 모달
+ if (showDatePicker) {
+ val dateRangePickerState = rememberDateRangePickerState(
+ initialSelectedStartDateMillis = uiState.selectedStartDateMillis,
+ initialSelectedEndDateMillis = uiState.selectedEndDateMillis
+ )
+
+ DatePickerDialog(
+ onDismissRequest = { showDatePicker = false },
+ confirmButton = {
+ TextButton(
+ onClick = {
+ val startMillis = dateRangePickerState.selectedStartDateMillis
+ val endMillis = dateRangePickerState.selectedEndDateMillis
+
+ viewModel.updateDateRange(
+ startDateMillis = startMillis,
+ endDateMillis = endMillis
+ )
+
+ showDatePicker = false
+ }
+ ) {
+ Text(
+ text = "확인",
+ )
+ }
+ },
+ dismissButton = {
+ TextButton(
+ onClick = { showDatePicker = false }
+ ) {
+ Text(
+ text = "취소"
+ )
+ }
+ },
+ colors = DatePickerDefaults.colors(
+ containerColor = MomentoTheme.colors.white
+ )
+ ) {
+ DateRangePicker(
+ state = dateRangePickerState,
+ colors = DatePickerDefaults.colors(
+ containerColor = MomentoTheme.colors.white,
+ // 기간(시작일과 종료일 사이 공간)의 배경색
+ dayInSelectionRangeContainerColor = MomentoTheme.colors.brownW80
+ ),
+ // 펜 아이콘 숨기기 (캘린더 뷰와 텍스트 입력 뷰 전환 역할)
+ showModeToggle = false,
+ title = {
+ Box(
+ modifier = Modifier.padding(start = 16.dp, top = 16.dp)
+ ) {
+ Text(
+ text = "여행 날짜 선택",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+ },
+ headline = {
+ Box(
+ modifier = Modifier.padding(16.dp)
+ ) {
+ CustomDateRangeHeadline(
+ startDateMillis = dateRangePickerState.selectedStartDateMillis,
+ endDateMillis = dateRangePickerState.selectedEndDateMillis
+ )
+ }
+ }
+ )
+ }
+ }
+
+ // 감정 선택 바텀시트
+ if (showEmotionBottomSheet) {
+ ModalBottomSheet(
+ onDismissRequest = { showEmotionBottomSheet = false },
+ dragHandle = null,
+ containerColor = MomentoTheme.colors.white,
+ shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp)
+ ) {
+ EmotionBottomSheetContent(
+ onConfirm = { emotionType ->
+ viewModel.updateEmotion(emotionType)
+ showEmotionBottomSheet = false
+ }
+ )
+ }
+ }
+
+ // 날씨 선택 바텀시트
+ if (showWeatherBottomSheet) {
+ ModalBottomSheet(
+ onDismissRequest = { showWeatherBottomSheet = false },
+ dragHandle = null,
+ containerColor = MomentoTheme.colors.white,
+ shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp)
+ ) {
+ WeatherBottomSheetContent (
+ onConfirm = { weatherType ->
+ viewModel.updateWeather(weatherType)
+ showWeatherBottomSheet = false
+ }
+ )
+ }
+ }
+
+ // 위치 추가 바텀시트
+ if (showPlaceBottomSheet) {
+ ModalBottomSheet(
+ onDismissRequest = { showPlaceBottomSheet = false },
+ sheetState = sheetState,
+ dragHandle = null,
+ containerColor = MomentoTheme.colors.white,
+ shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp)
+ ) {
+ PlaceBottomSheetContent(
+ searchState = uiState.searchState,
+ onValueChange = { newValue ->
+ viewModel.updateQuery(newValue)
+ },
+ onSearch = { viewModel.searchPlace() },
+ onClear = { viewModel.clearSearchResult() },
+ onConfirm = { place ->
+ viewModel.updatePlace(place)
+ showPlaceBottomSheet = false
+ }
+ )
+ }
+ }
+
+ // 해외 버튼 클릭 시 경고 모달 표시 (국내 여행지 저장된 경우)
+ if (showPlaceWarning) {
+ PlaceWarningDialog(
+ onDismiss = { showPlaceWarning = false },
+ onCancel = { showPlaceWarning = false },
+ onConfirm = {
+ viewModel.clearSelectedPlace()
+ showPlaceWarning = false
+ }
+ )
+ }
+}
+
+@Composable
+fun WriteDateSection(
+ startMillis: Long?,
+ endMillis: Long?,
+ onClick: () -> Unit
+) {
+ Row(
+ modifier = Modifier.clickable { onClick() },
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = AppIcons.Calendar,
+ contentDescription = null,
+ tint = MomentoTheme.colors.brownBase
+ )
+ Spacer(Modifier.width(10.dp))
+
+ when {
+ // 날짜 선택되지 않은 경우
+ startMillis == null -> {
+ Text(
+ text = "날짜 선택",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW40
+ )
+ }
+
+ // 시작일만 선택된 경우 or 시작일과 종료일이 같은 날짜에 선택된 경우
+ endMillis == null || startMillis == endMillis -> {
+ val startDate = startMillis.toLocalDate()
+
+ Text(
+ text = "${startDate?.year}년 ${startDate?.monthValue}월 ${startDate?.dayOfMonth}일",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+
+ // 시작일과 종료일 모두 선택된 경우 (다른 날짜)
+ else -> {
+ val startDate = startMillis.toLocalDate()
+ val endDate = endMillis.toLocalDate()
+
+ Text(
+ text = "${startDate?.year}년 ${startDate?.monthValue}월 ${startDate?.dayOfMonth}일 ~ ${endDate?.year}년 ${endDate?.monthValue}월 ${endDate?.dayOfMonth}일",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun CustomDateRangeHeadline(
+ startDateMillis: Long?,
+ endDateMillis: Long?
+) {
+ if (startDateMillis == null) {
+ Text(
+ text = "날짜를 선택해주세요",
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW40
+ )
+ } else {
+ val startDate = startDateMillis.toLocalDate()
+
+ if (endDateMillis == null || startDateMillis == endDateMillis) {
+ // 시작일만 선택된 경우 or 시작일과 종료일이 같은 날짜에 선택된 경우
+ Text(
+ text = "${startDate?.year}년 ${startDate?.monthValue}월 ${startDate?.dayOfMonth}일",
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20
+ )
+ } else {
+ // 시작일과 종료일 모두 선택된 경우 (다른 날짜)
+ val endDate = endDateMillis.toLocalDate()
+
+ Row {
+ Text(
+ text = "${startDate?.year}년 ${startDate?.monthValue}월 ${startDate?.dayOfMonth}일",
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20
+ )
+ Text(
+ text = " ~ ",
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20
+ )
+ Text(
+ text = "${endDate?.year}년 ${endDate?.monthValue}월 ${endDate?.dayOfMonth}일",
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun EmotionAndWeatherSection(
+ selectedEmotion: EmotionType?,
+ selectedWeather: WeatherType?,
+ onClickEmotion: () -> Unit,
+ onClickWeather: () -> Unit
+) {
+ Row {
+ Row(
+ modifier = Modifier.clickable { onClickEmotion() },
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = "감정 태그",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ Spacer(Modifier.width(8.dp))
+ Box(
+ modifier = Modifier
+ .size(32.dp)
+ .background(color = MomentoTheme.colors.white, shape = RoundedCornerShape(5.dp))
+ .border(width = 1.dp, color = MomentoTheme.colors.pinkW60, shape = RoundedCornerShape(5.dp)),
+ contentAlignment = Alignment.Center
+ ) {
+ selectedEmotion?.let { emotionType ->
+ Image(
+ painter = painterResource(emotionType.resId),
+ contentDescription = null
+ )
+ }
+ }
+ }
+ Spacer(Modifier.width(20.dp))
+ Row(
+ modifier = Modifier.clickable { onClickWeather() },
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = "날씨",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ Spacer(Modifier.width(8.dp))
+ Box(
+ modifier = Modifier
+ .size(32.dp)
+ .background(color = MomentoTheme.colors.white, shape = RoundedCornerShape(5.dp))
+ .border(width = 1.dp, color = MomentoTheme.colors.pinkW60, shape = RoundedCornerShape(5.dp)),
+ contentAlignment = Alignment.Center
+ ) {
+ selectedWeather?.let { weatherType ->
+ Image(
+ painter = painterResource(weatherType.resId),
+ contentDescription = null
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun WritePlaceSection(
+ selectedPlace: LocalPlace?,
+ overseasPlace: String,
+ onValueChange: (String) -> Unit,
+ onAddClick: () -> Unit,
+ onWarningClick: () -> Unit,
+) {
+ val radioOptions = listOf("국내", "해외 (직접 입력)")
+ var selectedCountry by remember { mutableStateOf("국내") }
+
+ Column(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = "어디로 다녀오셨나요?",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ // 라디오 버튼
+ Row(
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ radioOptions.forEachIndexed { idx, text ->
+ RadioButton(
+ modifier = Modifier.size(24.dp),
+ selected = (text == selectedCountry),
+ onClick = {
+ // 해외 버튼 클릭 & 저장된 국내 여행지 있는 경우
+ if (idx == 1 && selectedPlace != null) { onWarningClick() }
+ selectedCountry = text
+ },
+ colors = RadioButtonDefaults.colors(
+ selectedColor = MomentoTheme.colors.greenW20,
+ unselectedColor = MomentoTheme.colors.grayW80,
+ disabledSelectedColor = MomentoTheme.colors.white
+ )
+ )
+ Spacer(Modifier.width(4.dp))
+ Text(
+ text = text,
+ style = MomentoTheme.typography.body03,
+ color = MomentoTheme.colors.grayW20
+ )
+ if (idx == 0) {
+ Spacer(Modifier.width(12.dp))
+ }
+ }
+ }
+ }
+
+ Spacer(Modifier.height(12.dp))
+
+ if (selectedCountry == "국내") {
+ Row(
+ modifier = Modifier.clickable { onAddClick() },
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Image(
+ painter = painterResource(R.drawable.write_place),
+ contentDescription = null
+ )
+ Spacer(Modifier.width(6.dp))
+ if (selectedPlace == null) {
+ Text(
+ text = "위치 추가",
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW40
+ )
+ } else {
+ Text(
+ text = selectedPlace.title,
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+ }
+ } else {
+ // 해외
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(40.dp)
+ .background(color = MomentoTheme.colors.brownBg, shape = RoundedCornerShape(5.dp))
+ .padding(start = 12.dp),
+ contentAlignment = Alignment.CenterStart
+ ) {
+ if (overseasPlace.isEmpty()) {
+ Text(
+ text = "예) 도쿄, 발리",
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW60
+ )
+ }
+ BasicTextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = overseasPlace,
+ onValueChange = onValueChange,
+ textStyle = MomentoTheme.typography.body02,
+ singleLine = true
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun WriteTitleSection(
+ recordTitle: String,
+ onValueChange: (String) -> Unit
+) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(40.dp)
+ .background(color = MomentoTheme.colors.brownBg, shape = RoundedCornerShape(5.dp))
+ .padding(start = 12.dp),
+ contentAlignment = Alignment.CenterStart
+ ) {
+ if (recordTitle.isEmpty()) {
+ Text(
+ text = "제목",
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW60
+ )
+ }
+ BasicTextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = recordTitle,
+ onValueChange = onValueChange,
+ textStyle = MomentoTheme.typography.body02,
+ singleLine = true
+ )
+ }
+}
+
+@Composable
+fun WriteContentSection(
+ selectedImageUri: Uri?,
+ recordContent: String,
+ onValueChange: (String) -> Unit,
+ onClick: () -> Unit
+) {
+ Column(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text(
+ text = "이번 여행에 대해 소개해주세요.",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ Spacer(Modifier.height(12.dp))
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+// .height(300.dp)
+ .height(280.dp)
+ .background(color = MomentoTheme.colors.brownBg)
+ .padding(16.dp)
+ ) {
+ if (recordContent.isEmpty()) {
+ Text(
+ text = "내용",
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW60
+ )
+ }
+ BasicTextField(
+ modifier = Modifier.fillMaxSize(),
+ value = recordContent,
+ onValueChange = onValueChange,
+ textStyle = MomentoTheme.typography.body02,
+ singleLine = false
+ )
+ }
+ // 선택된 이미지
+ selectedImageUri?.let { uri ->
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(color = MomentoTheme.colors.brownBg)
+ .padding(start = 16.dp, bottom = 16.dp),
+ ) {
+ Box(
+ contentAlignment = Alignment.TopEnd
+ ) {
+ AsyncImage(
+ modifier = Modifier
+ .size(72.dp)
+ .clip(RoundedCornerShape(5.dp)),
+ model = uri,
+ contentDescription = null,
+ contentScale = ContentScale.Crop
+ )
+
+ Box(
+ modifier = Modifier
+ .offset(x = 8.dp, y = (-8).dp)
+ .clickable { onClick() }
+ .background(color = MomentoTheme.colors.white, shape = CircleShape)
+ ) {
+ Icon(
+ modifier = Modifier.size(20.dp),
+ imageVector = AppIcons.Delete,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW60
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+fun ImageAndShareSection(
+ isChecked: Boolean,
+ onCheckedChange: (Boolean) -> Unit,
+ onGalleryClick: () -> Unit
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(color = MomentoTheme.colors.white),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ modifier = Modifier
+ .clickable { onGalleryClick() }
+ .padding(horizontal = 20.dp, vertical = 10.dp),
+ imageVector = AppIcons.Gallery,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW60
+ )
+
+ Row(
+ modifier = Modifier.padding(end = 20.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = "공유 여부",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ Spacer(Modifier.width(8.dp))
+ Switch(
+ // 기본 크기의 80%로 축소
+ modifier = Modifier.scale(0.8f),
+ checked = isChecked,
+ onCheckedChange = onCheckedChange,
+ thumbContent = { FixedSizeThumbBox() },
+ colors = SwitchDefaults.colors(
+ // on - switch 배경 색
+ checkedTrackColor = MomentoTheme.colors.greenW20,
+ // off - switch 배경 색
+ uncheckedTrackColor = MomentoTheme.colors.grayW80,
+ // off - switch 테두리 색
+ uncheckedBorderColor = Color.Transparent,
+ // off - thumb 테두리 색
+ uncheckedThumbColor = MomentoTheme.colors.white
+ )
+ )
+ }
+ }
+}
+
+@Composable
+fun FixedSizeThumbBox() {
+ Box(
+ modifier = Modifier
+ .size(SwitchDefaults.IconSize)
+ .background(color = MomentoTheme.colors.white, shape = CircleShape)
+ )
+}
+
+@Preview
+@Composable
+fun RecordWriteScreenPreview() {
+ DngoTheme {
+// RecordWriteScreen()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/write/RecordWriteUiState.kt b/app/src/main/java/com/min/dnapp/presentation/write/RecordWriteUiState.kt
new file mode 100644
index 0000000..e08d4a9
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/write/RecordWriteUiState.kt
@@ -0,0 +1,28 @@
+package com.min.dnapp.presentation.write
+
+import android.net.Uri
+import com.min.dnapp.domain.model.EmotionType
+import com.min.dnapp.domain.model.LocalPlace
+import com.min.dnapp.domain.model.WeatherType
+
+data class SearchState(
+ val isLoading: Boolean = false,
+ val places: List = emptyList(),
+ val error: String? = null,
+ val query: String = ""
+)
+
+data class RecordWriteUiState(
+ val isSaving: Boolean = false,
+ val recordTitle: String = "",
+ val recordContent: String = "",
+ val selectedStartDateMillis: Long? = null,
+ val selectedEndDateMillis: Long? = null,
+ val selectedEmotion: EmotionType? = null,
+ val selectedWeather : WeatherType? = null,
+ val searchState: SearchState = SearchState(),
+ val selectedPlace: LocalPlace? = null,
+ val overseasPlace: String = "",
+ val selectedImageUri: Uri? = null,
+ val isShareChecked: Boolean = true
+)
diff --git a/app/src/main/java/com/min/dnapp/presentation/write/RecordWriteViewModel.kt b/app/src/main/java/com/min/dnapp/presentation/write/RecordWriteViewModel.kt
new file mode 100644
index 0000000..d625acb
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/write/RecordWriteViewModel.kt
@@ -0,0 +1,314 @@
+package com.min.dnapp.presentation.write
+
+import android.net.Uri
+import android.util.Log
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.min.dnapp.domain.model.EmotionType
+import com.min.dnapp.domain.model.LocalPlace
+import com.min.dnapp.domain.model.WeatherType
+import com.min.dnapp.domain.usecase.LocalSearchUseCase
+import com.min.dnapp.domain.usecase.SaveRecordUseCase
+import com.min.dnapp.presentation.common.SnackbarMessage
+import com.min.dnapp.util.Resource
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.receiveAsFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class RecordWriteViewModel @Inject constructor(
+ private val localSearchUseCase: LocalSearchUseCase,
+ private val saveRecordUseCase: SaveRecordUseCase
+) : ViewModel() {
+
+ private val _uiState = MutableStateFlow(RecordWriteUiState())
+ val uiState: StateFlow = _uiState.asStateFlow()
+
+ // Photo Picker를 실행하도록 요청하는 이벤트
+ private val _imageEvent = MutableStateFlow(false)
+ val imageEvent: StateFlow = _imageEvent.asStateFlow()
+
+ private val _completeSaveRecord = Channel()
+ val completeSaveRecordFlow = _completeSaveRecord.receiveAsFlow()
+
+ private val _snackbarMessage = MutableSharedFlow()
+ val snackbarMessage = _snackbarMessage.asSharedFlow()
+
+ // 이전 검색 작업을 취소하기 위한 Job
+ private var searchJob: Job? = null
+
+// init {
+// searchPlace("광안리해수욕장")
+// }
+
+ /**
+ * 제목 - textField의 입력 값 업데이트
+ */
+ fun updateTitle(newText: String) {
+ _uiState.value = _uiState.value.copy(recordTitle = newText)
+ Log.d("write", "updateTitle - newText : $newText")
+ }
+
+ /**
+ * 내용 - textField의 입력 값 업데이트
+ */
+ fun updateContent(newText: String) {
+ _uiState.value = _uiState.value.copy(recordContent = newText)
+ Log.d("write", "updateContent - newText : $newText")
+ }
+
+ /**
+ * 선택된 출발일과 도착일을 동시에 업데이트
+ */
+ fun updateDateRange(startDateMillis: Long?, endDateMillis: Long?) {
+ _uiState.value = _uiState.value.copy(
+ selectedStartDateMillis = startDateMillis,
+ selectedEndDateMillis = endDateMillis
+ )
+ }
+
+ /**
+ * 선택된 감정 업데이트
+ */
+ fun updateEmotion(emotionType: EmotionType) {
+ _uiState.value = _uiState.value.copy(selectedEmotion = emotionType)
+ Log.d("write", "selectEmotion - emotionType : $emotionType")
+ }
+
+ /**
+ * 선택된 날씨 업데이트
+ */
+ fun updateWeather(weatherType: WeatherType) {
+ _uiState.value = _uiState.value.copy(selectedWeather = weatherType)
+ Log.d("write", "selectWeather - weatherType : $weatherType")
+ }
+
+ /**
+ * textField의 입력 값만 업데이트하고, 검색 API 호출은 생략
+ */
+ fun updateQuery(newQuery: String) {
+ _uiState.value = _uiState.value.copy(searchState = _uiState.value.searchState.copy(query = newQuery))
+
+ // 검색어가 빈 경우 목록 지우기
+ if (newQuery.isBlank()) {
+ // 진행중인 검색 작업 취소
+ searchJob?.cancel()
+ Log.d("naver", "updateQuery - newQuery blank")
+ }
+ }
+
+ /**
+ * 검색 버튼 클릭 시, 검색 API 호출
+ */
+ fun searchPlace() {
+ val currentQuery = _uiState.value.searchState.query
+
+ // 검색어가 빈 경우
+ if (currentQuery.isBlank()) {
+ Log.d("naver", "searchPlace - newQuery blank")
+ return
+ }
+
+ // 진행중인(이전 검색) 작업이 있으면 취소
+ searchJob?.cancel()
+
+ // Flow 수집 및 상태 업데이트 시작
+ searchJob = localSearchUseCase(currentQuery)
+ .onEach { result ->
+ when (result) {
+ is Resource.Loading -> {
+ // 로딩 시작
+ _uiState.value = _uiState.value.copy(searchState = _uiState.value.searchState.copy(
+ isLoading = true,
+ places = result.data ?: emptyList(),
+ error = null
+ ))
+ Log.d("naver", "search for $currentQuery : Loading...")
+ }
+ is Resource.Success -> {
+ // 성공
+ _uiState.value = _uiState.value.copy(searchState = _uiState.value.searchState.copy(
+ isLoading = false,
+ places = result.data ?: emptyList(),
+ error = null
+ ))
+ Log.d("naver", "search success : ${result.data?.size} 개")
+ }
+ is Resource.Error -> {
+ // 에러
+ _uiState.value = _uiState.value.copy(searchState = _uiState.value.searchState.copy(
+ isLoading = false,
+ places = result.data ?: emptyList(),
+ error = result.message ?: "알 수 없는 에러 발생"
+ ))
+ Log.e("naver", "search error : ${result.message}")
+ }
+ }
+ }
+ // viewModelScope에서 flow를 실행
+ .launchIn(viewModelScope)
+ }
+
+ /**
+ * 검색 결과 목록 초기화
+ * (textField에 포커스가 잡히거나, 새 검색 시작할 때 사용)
+ */
+ fun clearSearchResult() {
+ searchJob?.cancel()
+
+ // query는 그대로 유지, 결과 목록만 초기화
+ _uiState.value = _uiState.value.copy(searchState = _uiState.value.searchState.copy(
+ isLoading = false,
+ places = emptyList(),
+ error = null
+ ))
+ Log.d("naver", "result cleared")
+ }
+
+ /**
+ * 국내 - 선택된 장소 업데이트
+ */
+ fun updatePlace(place: LocalPlace) {
+ _uiState.value = _uiState.value.copy(selectedPlace = place)
+ }
+
+ /**
+ * 해외 - textField의 입력 값 업데이트
+ */
+ fun updateOverseas(newText: String) {
+ _uiState.value = _uiState.value.copy(overseasPlace = newText)
+ Log.d("write", "updateOverseas - newText : $newText")
+ }
+
+ /**
+ * 공유여부 설정 상태 업데이트
+ */
+ fun updateShare(newChecked: Boolean) {
+ _uiState.value = _uiState.value.copy(isShareChecked = newChecked)
+ Log.d("write", "updateShare - newChecked : $newChecked")
+ }
+
+ /**
+ * 갤러리 아이콘 클릭 - Photo Picker 실행 요청
+ */
+ fun onGalleryIconClicked() {
+ _imageEvent.value = true
+ }
+
+ /**
+ * Photo Picker 실행 이벤트가 처리되었음을 알림
+ */
+ fun photoPickerEventHandled() {
+ _imageEvent.value = false
+ }
+
+ /**
+ * Photo Picker에서 선택된 URI를 받아 상태 업데이트
+ */
+ fun onPhotoSelected(uri: Uri?) {
+ _uiState.value = _uiState.value.copy(selectedImageUri = uri)
+ Log.d("write", "onPhotoSelected - uri : $uri")
+ }
+
+ /**
+ * Firebase에 기록 저장
+ */
+ fun saveRecord() {
+ // 필수 항목 미입력 시, 메시지 발행 후 종료
+ getSnackbarMessage(uiState.value)?.let { message ->
+ viewModelScope.launch {
+ _snackbarMessage.emit(message)
+ }
+ return
+ }
+
+ // 중복 저장 방지
+ if (uiState.value.isSaving) return
+
+ val currentUiState = uiState.value
+ val imageUrl = currentUiState.selectedImageUri
+
+ _uiState.update { it.copy(isSaving = true) }
+
+ viewModelScope.launch {
+ val result = saveRecordUseCase(currentUiState, imageUrl)
+
+ result.onSuccess {
+ // 저장 성공
+ _completeSaveRecord.send(Unit)
+ }.onFailure { exception ->
+ // 저장 실패
+ Log.e("write", "saveRecord - exception : $exception")
+ }
+
+ _uiState.update { it.copy(isSaving = false) }
+ }
+ }
+
+ private fun getSnackbarMessage(uiState: RecordWriteUiState): SnackbarMessage? {
+ if (uiState.recordTitle.isBlank()) {
+ return SnackbarMessage(
+ message = WriteMessage.TITLE_EMPTY
+ )
+ }
+ if (uiState.recordContent.isBlank()) {
+ return SnackbarMessage(
+ message = WriteMessage.CONTENT_EMPTY
+ )
+ }
+ if (uiState.selectedStartDateMillis == null) {
+ return SnackbarMessage(
+ message = WriteMessage.DATE_EMPTY
+ )
+ }
+ if (uiState.selectedEmotion == null) {
+ return SnackbarMessage(
+ message = WriteMessage.EMOTION_EMPTY
+ )
+ }
+ if (uiState.selectedWeather == null) {
+ return SnackbarMessage(
+ message = WriteMessage.WEATHER_EMPTY
+ )
+ }
+ if (uiState.selectedPlace == null && uiState.overseasPlace.isBlank()) {
+ return SnackbarMessage(
+ message = WriteMessage.PLACE_EMPTY
+ )
+ }
+
+ return null
+ }
+
+ /**
+ * 저장된 국내 여행지 삭제
+ */
+ fun clearSelectedPlace() {
+ _uiState.update { it.copy(selectedPlace = null) }
+ }
+
+ /**
+ * 저장된 해외 여행지 삭제
+ */
+ fun clearOverseasPlace() {
+ _uiState.update { it.copy(overseasPlace = "") }
+ }
+
+ /**
+ * 선택된 이미지 삭제
+ */
+ fun clearSelectedImageUri() {
+ _uiState.update { it.copy(selectedImageUri = null) }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/write/WriteFinishDialogState.kt b/app/src/main/java/com/min/dnapp/presentation/write/WriteFinishDialogState.kt
new file mode 100644
index 0000000..25ab12f
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/write/WriteFinishDialogState.kt
@@ -0,0 +1,9 @@
+package com.min.dnapp.presentation.write
+
+import com.min.dnapp.domain.model.Badge
+
+sealed class WriteFinishDialogState {
+ data object Hidden: WriteFinishDialogState()
+ data object StampDialog: WriteFinishDialogState()
+ data class BadgeDialog(val badge: Badge): WriteFinishDialogState()
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/write/WriteFinishScreen.kt b/app/src/main/java/com/min/dnapp/presentation/write/WriteFinishScreen.kt
new file mode 100644
index 0000000..e24efe3
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/write/WriteFinishScreen.kt
@@ -0,0 +1,124 @@
+package com.min.dnapp.presentation.write
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+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.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.systemBarsPadding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.NavHostController
+import com.min.dnapp.R
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+import com.min.dnapp.presentation.write.component.LevelUpDialog
+import com.min.dnapp.presentation.write.component.WriteStampDialog
+
+@Composable
+fun WriteFinishScreen(
+ navController: NavHostController,
+ viewModel: CheckBadgeViewModel = hiltViewModel()
+) {
+ val currentDialogState by viewModel.dialogState.collectAsStateWithLifecycle()
+
+ Surface(
+ modifier = Modifier
+ .systemBarsPadding()
+ .fillMaxSize(),
+ color = MomentoTheme.colors.white
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(horizontal = 20.dp),
+ verticalArrangement = Arrangement.SpaceBetween
+ ) {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Spacer(Modifier.height(100.dp))
+ Image(
+ painter = painterResource(R.drawable.write_finish),
+ contentDescription = null
+ )
+ Spacer(Modifier.height(40.dp))
+ Text(
+ text = "여행 기록 작성이 완료되었어요.",
+ style = MomentoTheme.typography.title01,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+
+ // 버튼 영역
+ Column(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Box(
+ modifier = Modifier
+ .clickable {
+ navController.navigate("home") {
+ // 스택 모두 제거
+ popUpTo(navController.graph.id) { inclusive = true }
+ }
+ }
+ .fillMaxWidth()
+ .background(color = MomentoTheme.colors.brownBase, shape = RoundedCornerShape(10.dp))
+ .padding(vertical = 16.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = "홈으로",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.white
+ )
+ }
+ Spacer(Modifier.height(16.dp))
+ }
+ }
+ }
+
+ when (currentDialogState) {
+ is WriteFinishDialogState.BadgeDialog -> {
+ val data = currentDialogState as WriteFinishDialogState.BadgeDialog
+
+ LevelUpDialog(
+ badge = data.badge,
+ onDismiss = { viewModel.closeDialog() },
+ onConfirm = { viewModel.closeDialog() },
+ )
+ }
+ is WriteFinishDialogState.StampDialog -> {
+ WriteStampDialog(
+ onDismiss = { viewModel.closeDialog() },
+ onConfirm = { viewModel.closeDialog() }
+ )
+ }
+ is WriteFinishDialogState.Hidden -> {}
+ }
+}
+
+@Preview
+@Composable
+fun WriteFinishScreenPreview() {
+ DngoTheme {
+// WriteFinishScreen()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/write/WriteMessage.kt b/app/src/main/java/com/min/dnapp/presentation/write/WriteMessage.kt
new file mode 100644
index 0000000..8cec782
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/write/WriteMessage.kt
@@ -0,0 +1,10 @@
+package com.min.dnapp.presentation.write
+
+object WriteMessage {
+ const val TITLE_EMPTY = "제목을 입력해주세요"
+ const val CONTENT_EMPTY = "내용을 입력해주세요"
+ const val DATE_EMPTY = "날짜를 선택해주세요"
+ const val EMOTION_EMPTY = "감정 태그를 선택해주세요"
+ const val WEATHER_EMPTY = "날씨를 선택해주세요"
+ const val PLACE_EMPTY = "여행지를 입력해주세요"
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/write/component/EmotionBottomSheetContent.kt b/app/src/main/java/com/min/dnapp/presentation/write/component/EmotionBottomSheetContent.kt
new file mode 100644
index 0000000..8ed1817
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/write/component/EmotionBottomSheetContent.kt
@@ -0,0 +1,161 @@
+package com.min.dnapp.presentation.write.component
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.R
+import com.min.dnapp.domain.model.EmotionType
+import com.min.dnapp.presentation.ui.component.SelectButton
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun EmotionBottomSheetContent(
+ onConfirm: (EmotionType) -> Unit
+) {
+ // 선택된 EmotionType 객체 저장
+ var selectedEmotion by remember { mutableStateOf(null) }
+ // emotion 선택 시 버튼 활성화
+ val isButtonEnabled = selectedEmotion != null
+
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Spacer(Modifier.height(20.dp))
+
+ Text(
+ text = "기분은 어땠나요?",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ Spacer(Modifier.height(20.dp))
+
+ HorizontalDivider(thickness = 1.dp, color = MomentoTheme.colors.grayW90)
+
+ Spacer(Modifier.height(12.dp))
+
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ verticalArrangement = Arrangement.spacedBy(20.dp)
+ ) {
+ // happy / love / surprise
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceEvenly
+ ) {
+ EmotionItem(
+ emotionType = EmotionType.HAPPY,
+ isSelected = selectedEmotion == EmotionType.HAPPY,
+ onSelect = { emotionType -> selectedEmotion = emotionType }
+ )
+ EmotionItem(
+ emotionType = EmotionType.LOVE,
+ isSelected = selectedEmotion == EmotionType.LOVE,
+ onSelect = { emotionType -> selectedEmotion = emotionType }
+ )
+ EmotionItem(
+ emotionType = EmotionType.SURPRISE,
+ isSelected = selectedEmotion == EmotionType.SURPRISE,
+ onSelect = { emotionType -> selectedEmotion = emotionType }
+ )
+ }
+
+ // angry / feel / sad
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceEvenly
+ ) {
+ EmotionItem(
+ emotionType = EmotionType.ANGRY,
+ isSelected = selectedEmotion == EmotionType.ANGRY,
+ onSelect = { emotionType -> selectedEmotion = emotionType }
+ )
+ EmotionItem(
+ emotionType = EmotionType.FEEL,
+ isSelected = selectedEmotion == EmotionType.FEEL,
+ onSelect = { emotionType -> selectedEmotion = emotionType }
+ )
+ EmotionItem(
+ emotionType = EmotionType.SAD,
+ isSelected = selectedEmotion == EmotionType.SAD,
+ onSelect = { emotionType -> selectedEmotion = emotionType }
+ )
+ }
+
+ // shine
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.Center
+ ) {
+ EmotionItem(
+ emotionType = EmotionType.SHINE,
+ isSelected = selectedEmotion == EmotionType.SHINE,
+ onSelect = { emotionType -> selectedEmotion = emotionType }
+ )
+ }
+ }
+
+ Spacer(Modifier.height(32.dp))
+
+ // 선택 버튼
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp)
+ ) {
+ SelectButton(
+ enabled = isButtonEnabled,
+ onConfirm = {
+ selectedEmotion?.let(onConfirm)
+ }
+ )
+ Spacer(Modifier.height(16.dp))
+ }
+ }
+}
+
+@Composable
+fun EmotionItem(
+ emotionType: EmotionType,
+ isSelected: Boolean,
+ onSelect: (EmotionType) -> Unit
+) {
+ Box(
+ contentAlignment = Alignment.Center
+ ) {
+ Image(
+ modifier = Modifier.clickable {
+ onSelect(emotionType)
+ },
+ painter = painterResource(emotionType.resId),
+ contentDescription = null
+ )
+ if (isSelected) {
+ Image(
+ modifier = Modifier.size(52.dp),
+ painter = painterResource(R.drawable.ew_check),
+ contentDescription = null
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/write/component/LevelUpDialog.kt b/app/src/main/java/com/min/dnapp/presentation/write/component/LevelUpDialog.kt
new file mode 100644
index 0000000..43d4341
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/write/component/LevelUpDialog.kt
@@ -0,0 +1,134 @@
+package com.min.dnapp.presentation.write.component
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+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.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import com.airbnb.lottie.compose.LottieAnimation
+import com.airbnb.lottie.compose.LottieCompositionSpec
+import com.airbnb.lottie.compose.animateLottieCompositionAsState
+import com.airbnb.lottie.compose.rememberLottieComposition
+import com.min.dnapp.domain.model.Badge
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun LevelUpDialog(
+ badge: Badge,
+ onDismiss: () -> Unit,
+ onConfirm: () -> Unit
+) {
+ Dialog(
+ onDismissRequest = onDismiss
+ ) {
+ Surface(
+ modifier = Modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(16.dp),
+ color = MomentoTheme.colors.white
+ ) {
+ Box(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Column(
+ modifier = Modifier.padding(horizontal = 20.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Spacer(Modifier.height(20.dp))
+ Text(
+ text = "${badge.name}가 된 것을 축하해요!",
+ style = MomentoTheme.typography.title01,
+ color = MomentoTheme.colors.grayW20
+ )
+ Spacer(Modifier.height(8.dp))
+ Text(
+ text = "여행 기록 ${badge.minStamp}개 달성",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW60
+ )
+ Spacer(Modifier.height(20.dp))
+ Image(
+ modifier = Modifier.size(160.dp),
+ painter = painterResource(badge.resId),
+ contentDescription = null
+ )
+ Spacer(Modifier.height(20.dp))
+ Text(
+ text = badge.description,
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ Spacer(Modifier.height(20.dp))
+ Box(
+ modifier = Modifier
+ .clickable { onConfirm() }
+ .fillMaxWidth()
+ .background(
+ color = MomentoTheme.colors.brownBase,
+ shape = RoundedCornerShape(10.dp)
+ )
+ .padding(vertical = 16.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = "확인했어요",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.white
+ )
+ }
+ Spacer(Modifier.height(20.dp))
+ }
+
+ // Box 영역 전체 덮도록 설정
+ LottieFireworksAnimation(
+ modifier = Modifier.matchParentSize()
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun LottieFireworksAnimation(
+ modifier: Modifier = Modifier
+) {
+ val composition by rememberLottieComposition(spec = LottieCompositionSpec.Asset("fireworks.json"))
+
+ // 애니메이션 1번만 재생
+ val progress by animateLottieCompositionAsState(
+ composition = composition,
+ iterations = 1,
+ isPlaying = true,
+ restartOnPlay = false
+ )
+
+ LottieAnimation(
+ modifier = modifier,
+ composition = composition,
+ progress = progress
+ )
+}
+
+@Preview
+@Composable
+fun LevelUpDialogPreview() {
+ DngoTheme {
+// LevelUpDialog()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/write/component/PlaceBottomSheetContent.kt b/app/src/main/java/com/min/dnapp/presentation/write/component/PlaceBottomSheetContent.kt
new file mode 100644
index 0000000..aed456f
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/write/component/PlaceBottomSheetContent.kt
@@ -0,0 +1,203 @@
+package com.min.dnapp.presentation.write.component
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.platform.LocalSoftwareKeyboardController
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.domain.model.LocalPlace
+import com.min.dnapp.presentation.ui.component.SelectButton
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+import com.min.dnapp.presentation.write.SearchState
+
+@Composable
+fun PlaceBottomSheetContent(
+ searchState: SearchState,
+ onValueChange: (String) -> Unit,
+ onSearch: () -> Unit,
+ onClear: () -> Unit,
+ onConfirm: (LocalPlace) -> Unit,
+) {
+ // 검색 결과 목록에서 선택된 아이템
+ var selectedPlace by remember { mutableStateOf(null) }
+
+ // UI 컨트롤러
+ val keyboardController = LocalSoftwareKeyboardController.current
+ val focusManager = LocalFocusManager.current
+
+ val onSearchAction = {
+ // 여행지 검색 실행
+ onSearch()
+ // 키보드 숨기기
+ keyboardController?.hide()
+ // 포커스 해제
+ focusManager.clearFocus()
+ }
+
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Spacer(Modifier.height(20.dp))
+
+ Box(
+ modifier = Modifier
+ .padding(horizontal = 20.dp)
+ .fillMaxWidth()
+ .border(width = 1.dp, color = MomentoTheme.colors.grayW80, shape = RoundedCornerShape(6.dp))
+ .padding(horizontal = 16.dp, vertical = 12.dp)
+ ) {
+ if (searchState.query.isEmpty()) {
+ Text(
+ text = "여행지를 입력해주세요",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW60
+ )
+ }
+ BasicTextField(
+ modifier = Modifier
+ .fillMaxWidth()
+ .onFocusChanged { focusState ->
+ if (focusState.isFocused) {
+ selectedPlace = null
+ onClear()
+ }
+ },
+ value = searchState.query,
+ onValueChange = onValueChange,
+ textStyle = MomentoTheme.typography.body01,
+ singleLine = true,
+ keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
+ keyboardActions = KeyboardActions(
+ onSearch = { onSearchAction() }
+ )
+ )
+ }
+
+ Spacer(Modifier.height(20.dp))
+
+ // 검색 진행 시 로딩 인디케이터 표시
+ if (searchState.isLoading) {
+ CircularProgressIndicator(
+ modifier = Modifier.size(40.dp),
+ color = MomentoTheme.colors.brownW20,
+ strokeWidth = 4.dp
+ )
+ }
+
+ // 검색 결과 목록
+ Column(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ searchState.places.forEach { item ->
+ PlaceItem(
+ placeName = item.title,
+ placeCategory = item.category,
+ placeRoadAddress = item.roadAddress,
+ isSelected = selectedPlace == item,
+ onClick = { selectedPlace = item }
+ )
+ }
+ }
+
+ Spacer(Modifier.height(20.dp))
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp)
+ ) {
+ SelectButton(
+ enabled = selectedPlace != null,
+ onConfirm = {
+ // 콜백으로 이벤트 전달
+ selectedPlace?.let(onConfirm)
+ }
+ )
+ Spacer(Modifier.height(16.dp))
+ }
+ }
+}
+
+@Composable
+fun PlaceItem(
+ placeName: String,
+ placeCategory: String,
+ placeRoadAddress: String,
+ isSelected: Boolean = false,
+ onClick: () -> Unit
+) {
+ Column(
+ modifier = Modifier
+ .clickable { onClick() }
+ .fillMaxWidth()
+ .background(
+ if (isSelected) MomentoTheme.colors.brownW80 else Color.Transparent
+ )
+ .padding(20.dp),
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween
+ ) {
+ Text(
+ text = placeName,
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ Text(
+ text = placeCategory,
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW40
+ )
+ }
+ Spacer(Modifier.height(4.dp))
+ Text(
+ text = placeRoadAddress,
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW60
+ )
+ }
+ HorizontalDivider(thickness = 1.dp, color = MomentoTheme.colors.grayW80)
+}
+
+@Preview(showBackground = true)
+@Composable
+fun PlaceBottomSheetContentPreview() {
+ DngoTheme {
+// PlaceBottomSheetContent(
+// value = "",
+// onValueChange = {},
+// onConfirm = {}
+// )
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/write/component/PlaceWarningDialog.kt b/app/src/main/java/com/min/dnapp/presentation/write/component/PlaceWarningDialog.kt
new file mode 100644
index 0000000..4fd666a
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/write/component/PlaceWarningDialog.kt
@@ -0,0 +1,115 @@
+package com.min.dnapp.presentation.write.component
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import com.min.dnapp.presentation.ui.theme.DngoTheme
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun PlaceWarningDialog(
+ onDismiss: () -> Unit,
+ onCancel: () -> Unit,
+ onConfirm: () -> Unit
+) {
+ Dialog(
+ onDismissRequest = onDismiss
+ ) {
+ Surface(
+ modifier = Modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(16.dp),
+ color = MomentoTheme.colors.white
+ ) {
+ Column(
+ modifier = Modifier.padding(horizontal = 20.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Spacer(Modifier.height(20.dp))
+
+ Text(
+ text = "여행지를 변경하시겠어요?",
+ style = MomentoTheme.typography.title01,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ Spacer(Modifier.height(8.dp))
+
+ Text(
+ text = "해외 여행지를 입력하면 \n기존에 저장한 국내 여행지가 삭제됩니다.",
+ style = MomentoTheme.typography.body02,
+ color = MomentoTheme.colors.grayW20,
+ textAlign = TextAlign.Center,
+ )
+
+ Spacer(Modifier.height(20.dp))
+
+ // 취소/변경 버튼
+ Row(
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Box(
+ modifier = Modifier
+ .clickable { onCancel() }
+ .weight(1f)
+ .background(color = MomentoTheme.colors.white)
+ .border(width = 1.dp, color = MomentoTheme.colors.grayW80, shape = RoundedCornerShape(5.dp))
+ .padding(vertical = 16.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = "취소",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.grayW20
+ )
+ }
+ Spacer(Modifier.width(12.dp))
+ Box(
+ modifier = Modifier
+ .clickable { onConfirm() }
+ .weight(1f)
+ .background(
+ color = MomentoTheme.colors.brownBase,
+ shape = RoundedCornerShape(5.dp)
+ )
+ .padding(vertical = 16.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = "변경",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.white
+ )
+ }
+ }
+
+ Spacer(Modifier.height(20.dp))
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+fun PlaceWarningDialogPreview() {
+ DngoTheme {
+// PlaceWarningDialog()
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/write/component/ShareGuide.kt b/app/src/main/java/com/min/dnapp/presentation/write/component/ShareGuide.kt
new file mode 100644
index 0000000..bb5bc8c
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/write/component/ShareGuide.kt
@@ -0,0 +1,64 @@
+package com.min.dnapp.presentation.write.component
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.presentation.ui.icon.AppIcons
+import com.min.dnapp.presentation.ui.icon.appicons.DeleteLine
+import com.min.dnapp.presentation.ui.icon.appicons.ShareTriangle
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun ShareGuide(
+ onClick: () -> Unit
+) {
+ Box(
+ modifier = Modifier.padding(end = 20.dp)
+ ) {
+ Column(
+ horizontalAlignment = Alignment.End
+ ) {
+ Row(
+ modifier = Modifier
+ .background(color = MomentoTheme.colors.greenW60, shape = RoundedCornerShape(5.dp))
+ .padding(start = 12.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = "발견 탭에 나의 여행 기록을 공유합니다",
+ style = MomentoTheme.typography.label,
+ color = MomentoTheme.colors.grayW20
+ )
+ Icon(
+ modifier = Modifier
+ .clickable { onClick() }
+ .padding(12.dp),
+ imageVector = AppIcons.DeleteLine,
+ contentDescription = null,
+ tint = MomentoTheme.colors.grayW20
+ )
+ }
+
+ Row {
+ Icon(
+ imageVector = AppIcons.ShareTriangle,
+ contentDescription = null,
+ tint = MomentoTheme.colors.greenW60
+ )
+ Spacer(Modifier.width(16.dp))
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/write/component/WeatherBottomSheetContent.kt b/app/src/main/java/com/min/dnapp/presentation/write/component/WeatherBottomSheetContent.kt
new file mode 100644
index 0000000..e85502f
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/write/component/WeatherBottomSheetContent.kt
@@ -0,0 +1,161 @@
+package com.min.dnapp.presentation.write.component
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import com.min.dnapp.R
+import com.min.dnapp.domain.model.WeatherType
+import com.min.dnapp.presentation.ui.component.SelectButton
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun WeatherBottomSheetContent(
+ onConfirm: (WeatherType) -> Unit
+) {
+ // 선택된 WeatherType 객체 저장
+ var selectedWeather by remember { mutableStateOf(null) }
+ // weather 선택 시 버튼 활성화
+ val isButtonEnabled = selectedWeather != null
+
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Spacer(Modifier.height(20.dp))
+
+ Text(
+ text = "날씨는 어땠나요?",
+ style = MomentoTheme.typography.title02,
+ color = MomentoTheme.colors.grayW20
+ )
+
+ Spacer(Modifier.height(20.dp))
+
+ HorizontalDivider(thickness = 1.dp, color = MomentoTheme.colors.grayW90)
+
+ Spacer(Modifier.height(12.dp))
+
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ verticalArrangement = Arrangement.spacedBy(20.dp)
+ ) {
+ // sun / wind / moon
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceEvenly
+ ) {
+ WeatherItem(
+ weatherType = WeatherType.SUN,
+ isSelected = selectedWeather == WeatherType.SUN,
+ onSelect = { weatherType -> selectedWeather = weatherType }
+ )
+ WeatherItem(
+ weatherType = WeatherType.WIND,
+ isSelected = selectedWeather == WeatherType.WIND,
+ onSelect = { weatherType -> selectedWeather = weatherType }
+ )
+ WeatherItem(
+ weatherType = WeatherType.MOON,
+ isSelected = selectedWeather == WeatherType.MOON,
+ onSelect = { weatherType -> selectedWeather = weatherType }
+ )
+ }
+
+ // thunder / rain / cloud
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceEvenly
+ ) {
+ WeatherItem(
+ weatherType = WeatherType.THUNDER,
+ isSelected = selectedWeather == WeatherType.THUNDER,
+ onSelect = { weatherType -> selectedWeather = weatherType }
+ )
+ WeatherItem(
+ weatherType = WeatherType.RAIN,
+ isSelected = selectedWeather == WeatherType.RAIN,
+ onSelect = { weatherType -> selectedWeather = weatherType }
+ )
+ WeatherItem(
+ weatherType = WeatherType.CLOUD,
+ isSelected = selectedWeather == WeatherType.CLOUD,
+ onSelect = { weatherType -> selectedWeather = weatherType }
+ )
+ }
+
+ // snow
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.Center
+ ) {
+ WeatherItem(
+ weatherType = WeatherType.SNOW,
+ isSelected = selectedWeather == WeatherType.SNOW,
+ onSelect = { weatherType -> selectedWeather = weatherType }
+ )
+ }
+ }
+
+ Spacer(Modifier.height(32.dp))
+
+ // 선택 버튼
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 20.dp)
+ ) {
+ SelectButton(
+ enabled = isButtonEnabled,
+ onConfirm = {
+ selectedWeather?.let(onConfirm)
+ }
+ )
+ Spacer(Modifier.height(16.dp))
+ }
+ }
+}
+
+@Composable
+fun WeatherItem(
+ weatherType: WeatherType,
+ isSelected: Boolean,
+ onSelect: (WeatherType) -> Unit
+) {
+ Box(
+ contentAlignment = Alignment.Center
+ ) {
+ Image(
+ modifier = Modifier.clickable {
+ onSelect(weatherType)
+ },
+ painter = painterResource(weatherType.resId),
+ contentDescription = null
+ )
+ if (isSelected) {
+ Image(
+ modifier = Modifier.size(48.dp),
+ painter = painterResource(R.drawable.ew_check),
+ contentDescription = null
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/presentation/write/component/WriteStampDialog.kt b/app/src/main/java/com/min/dnapp/presentation/write/component/WriteStampDialog.kt
new file mode 100644
index 0000000..cd9b680
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/presentation/write/component/WriteStampDialog.kt
@@ -0,0 +1,71 @@
+package com.min.dnapp.presentation.write.component
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+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.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import com.min.dnapp.R
+import com.min.dnapp.presentation.ui.theme.MomentoTheme
+
+@Composable
+fun WriteStampDialog(
+ onDismiss: () -> Unit,
+ onConfirm: () -> Unit
+) {
+ Dialog(
+ onDismissRequest = onDismiss
+ ) {
+ Surface(
+ modifier = Modifier.fillMaxWidth(),
+ shape = RoundedCornerShape(16.dp),
+ color = MomentoTheme.colors.white
+ ) {
+ Column(
+ modifier = Modifier.padding(horizontal = 20.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Spacer(Modifier.height(20.dp))
+ Image(
+ painter = painterResource(R.drawable.write_stamp),
+ contentDescription = null
+ )
+ Spacer(Modifier.height(20.dp))
+ Text(
+ text = "스탬프 1개가 지급되었어요!",
+ style = MomentoTheme.typography.title01,
+ color = MomentoTheme.colors.grayW20
+ )
+ Spacer(Modifier.height(20.dp))
+ Box(
+ modifier = Modifier
+ .clickable { onConfirm() }
+ .fillMaxWidth()
+ .background(color = MomentoTheme.colors.brownBase, shape = RoundedCornerShape(10.dp))
+ .padding(vertical = 16.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = "확인했어요",
+ style = MomentoTheme.typography.body01,
+ color = MomentoTheme.colors.white
+ )
+ }
+ Spacer(Modifier.height(20.dp))
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/util/DateTimeExt.kt b/app/src/main/java/com/min/dnapp/util/DateTimeExt.kt
new file mode 100644
index 0000000..09493bc
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/util/DateTimeExt.kt
@@ -0,0 +1,78 @@
+package com.min.dnapp.util
+
+import android.util.Log
+import java.text.SimpleDateFormat
+import java.time.Instant
+import java.time.LocalDate
+import java.time.ZoneId
+import java.util.Date
+import java.util.Locale
+import java.util.concurrent.TimeUnit
+
+/**
+ * Long(밀리초) 값을 시스템 기본 시간대를 기준으로 LocalDate로 변환
+ */
+fun Long?.toLocalDate(): LocalDate? {
+ if (this == null || this == 0L) {
+ return null
+ }
+
+ return Instant.ofEpochMilli(this)
+ .atZone(ZoneId.systemDefault())
+ .toLocalDate()
+}
+
+/**
+ * 원하는 날짜 포맷으로 변환
+ */
+fun Long.toDateString(format: String): String {
+ val date = Date(this)
+ Log.d("record", "toDateString - date: $date")
+
+ val formatter = SimpleDateFormat(format, Locale.getDefault())
+ Log.d("record", "toDateString - formatter: $formatter")
+
+ return formatter.format(date)
+}
+
+/**
+ * 현재 시간 기준, 경과 시간으로 변환
+ *
+ * @return (예: "5분 전, "2시간 전")
+ */
+fun Long?.toTimeAgoString(): String {
+ if (this == null || this == 0L) {
+ return "날짜 없음"
+ }
+
+ // 현재 시간
+ val nowMillis = System.currentTimeMillis()
+ // 입력된 시간
+ val inputMillis = this
+
+ // 시간 차이
+ val diffMillis = Math.abs(nowMillis - inputMillis)
+
+ // 시간 차이를 단위별로 변환
+ val seconds = TimeUnit.MILLISECONDS.toSeconds(diffMillis)
+ val minutes = TimeUnit.MILLISECONDS.toMinutes(diffMillis)
+ val hours = TimeUnit.MILLISECONDS.toHours(diffMillis)
+ val days = TimeUnit.MILLISECONDS.toDays(diffMillis)
+
+ return when {
+ // 1분 미만
+ seconds < 60 -> "방금 전"
+ // 1시간 미만
+ minutes < 60 -> "${minutes}분 전"
+ // 24시간 미만
+ hours < 24 -> "${hours}시간 전"
+ // 7일 미만
+ days < 7 -> "${days}일 전"
+ // 30일 미만
+ days < 30 -> "${days / 7}주 전"
+ // 1년 미만
+ days < 365 -> "${days / 30}달 전"
+ // 1년 이상
+ else -> "${days / 365}년 전"
+ }
+}
diff --git a/app/src/main/java/com/min/dnapp/util/Resource.kt b/app/src/main/java/com/min/dnapp/util/Resource.kt
new file mode 100644
index 0000000..6404418
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/util/Resource.kt
@@ -0,0 +1,7 @@
+package com.min.dnapp.util
+
+sealed class Resource(val data: T? = null, val message: String? = null) {
+ class Success(data: T) : Resource(data)
+ class Error(message: String, data: T? = null) : Resource(data, message)
+ class Loading(data: T? = null) : Resource(data)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/min/dnapp/util/StringExt.kt b/app/src/main/java/com/min/dnapp/util/StringExt.kt
new file mode 100644
index 0000000..1cb89b7
--- /dev/null
+++ b/app/src/main/java/com/min/dnapp/util/StringExt.kt
@@ -0,0 +1,17 @@
+package com.min.dnapp.util
+
+/**
+ * 문자열에서 태그를 제거하는 확장함수
+ * 네이버 검색 API의 title 필드에 포함된 태그를 제거하는데 사용
+ */
+fun String.removeTag(): String {
+ return this.replace(Regex("<(/)?b>"), "")
+}
+
+/**
+ * 카테고리 문자열에서 가장 마지막 카테고리를 추출하는 확장함수
+ * 예: "음식점>일식>돈가스" -> "돈가스"
+ */
+fun String.extractLastCategory(): String {
+ return this.split(">").lastOrNull() ?: this
+}
diff --git a/app/src/main/res/drawable/badge_bronze.xml b/app/src/main/res/drawable/badge_bronze.xml
new file mode 100644
index 0000000..e55da67
--- /dev/null
+++ b/app/src/main/res/drawable/badge_bronze.xml
@@ -0,0 +1,250 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/badge_gold.xml b/app/src/main/res/drawable/badge_gold.xml
new file mode 100644
index 0000000..f95f729
--- /dev/null
+++ b/app/src/main/res/drawable/badge_gold.xml
@@ -0,0 +1,250 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/badge_new.xml b/app/src/main/res/drawable/badge_new.xml
new file mode 100644
index 0000000..027c806
--- /dev/null
+++ b/app/src/main/res/drawable/badge_new.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/badge_silver.xml b/app/src/main/res/drawable/badge_silver.xml
new file mode 100644
index 0000000..95c20e1
--- /dev/null
+++ b/app/src/main/res/drawable/badge_silver.xml
@@ -0,0 +1,370 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/beach.png b/app/src/main/res/drawable/beach.png
new file mode 100644
index 0000000..52c9aa0
Binary files /dev/null and b/app/src/main/res/drawable/beach.png differ
diff --git a/app/src/main/res/drawable/bell_comment.xml b/app/src/main/res/drawable/bell_comment.xml
new file mode 100644
index 0000000..9c3e3ea
--- /dev/null
+++ b/app/src/main/res/drawable/bell_comment.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/emotion_angry.xml b/app/src/main/res/drawable/emotion_angry.xml
new file mode 100644
index 0000000..ccabfb6
--- /dev/null
+++ b/app/src/main/res/drawable/emotion_angry.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/emotion_feel.xml b/app/src/main/res/drawable/emotion_feel.xml
new file mode 100644
index 0000000..cee3641
--- /dev/null
+++ b/app/src/main/res/drawable/emotion_feel.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/emotion_happy.xml b/app/src/main/res/drawable/emotion_happy.xml
new file mode 100644
index 0000000..9883e53
--- /dev/null
+++ b/app/src/main/res/drawable/emotion_happy.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/emotion_love.xml b/app/src/main/res/drawable/emotion_love.xml
new file mode 100644
index 0000000..a5a35a8
--- /dev/null
+++ b/app/src/main/res/drawable/emotion_love.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/emotion_sad.xml b/app/src/main/res/drawable/emotion_sad.xml
new file mode 100644
index 0000000..e3a6ef7
--- /dev/null
+++ b/app/src/main/res/drawable/emotion_sad.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/emotion_shine.xml b/app/src/main/res/drawable/emotion_shine.xml
new file mode 100644
index 0000000..86c8197
--- /dev/null
+++ b/app/src/main/res/drawable/emotion_shine.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/emotion_surprise.xml b/app/src/main/res/drawable/emotion_surprise.xml
new file mode 100644
index 0000000..0672f65
--- /dev/null
+++ b/app/src/main/res/drawable/emotion_surprise.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ew_check.xml b/app/src/main/res/drawable/ew_check.xml
new file mode 100644
index 0000000..a0fb31b
--- /dev/null
+++ b/app/src/main/res/drawable/ew_check.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_explore.xml b/app/src/main/res/drawable/ic_explore.xml
new file mode 100644
index 0000000..4c647e3
--- /dev/null
+++ b/app/src/main/res/drawable/ic_explore.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_home.xml b/app/src/main/res/drawable/ic_home.xml
new file mode 100644
index 0000000..bb80386
--- /dev/null
+++ b/app/src/main/res/drawable/ic_home.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_momento.xml b/app/src/main/res/drawable/ic_momento.xml
new file mode 100644
index 0000000..b181568
--- /dev/null
+++ b/app/src/main/res/drawable/ic_momento.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_momento_foreground.xml b/app/src/main/res/drawable/ic_momento_foreground.xml
new file mode 100644
index 0000000..b181568
--- /dev/null
+++ b/app/src/main/res/drawable/ic_momento_foreground.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_my.xml b/app/src/main/res/drawable/ic_my.xml
new file mode 100644
index 0000000..0691659
--- /dev/null
+++ b/app/src/main/res/drawable/ic_my.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/image_basic.xml b/app/src/main/res/drawable/image_basic.xml
new file mode 100644
index 0000000..600ee09
--- /dev/null
+++ b/app/src/main/res/drawable/image_basic.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/login_cloud.xml b/app/src/main/res/drawable/login_cloud.xml
new file mode 100644
index 0000000..2ae5c65
--- /dev/null
+++ b/app/src/main/res/drawable/login_cloud.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/login_momento.xml b/app/src/main/res/drawable/login_momento.xml
new file mode 100644
index 0000000..841b2d4
--- /dev/null
+++ b/app/src/main/res/drawable/login_momento.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/login_mount.xml b/app/src/main/res/drawable/login_mount.xml
new file mode 100644
index 0000000..d8a7779
--- /dev/null
+++ b/app/src/main/res/drawable/login_mount.xml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/logo_momento.xml b/app/src/main/res/drawable/logo_momento.xml
new file mode 100644
index 0000000..0f08d1a
--- /dev/null
+++ b/app/src/main/res/drawable/logo_momento.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/logo_onboarding.xml b/app/src/main/res/drawable/logo_onboarding.xml
new file mode 100644
index 0000000..00ffcb3
--- /dev/null
+++ b/app/src/main/res/drawable/logo_onboarding.xml
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/logo_onboarding2.xml b/app/src/main/res/drawable/logo_onboarding2.xml
new file mode 100644
index 0000000..a3d99bc
--- /dev/null
+++ b/app/src/main/res/drawable/logo_onboarding2.xml
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/logo_onboarding3.xml b/app/src/main/res/drawable/logo_onboarding3.xml
new file mode 100644
index 0000000..af4c493
--- /dev/null
+++ b/app/src/main/res/drawable/logo_onboarding3.xml
@@ -0,0 +1,185 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/logo_profile.xml b/app/src/main/res/drawable/logo_profile.xml
new file mode 100644
index 0000000..2294644
--- /dev/null
+++ b/app/src/main/res/drawable/logo_profile.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/logo_profile2.xml b/app/src/main/res/drawable/logo_profile2.xml
new file mode 100644
index 0000000..f1fd730
--- /dev/null
+++ b/app/src/main/res/drawable/logo_profile2.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/logo_profile3.xml b/app/src/main/res/drawable/logo_profile3.xml
new file mode 100644
index 0000000..eb43256
--- /dev/null
+++ b/app/src/main/res/drawable/logo_profile3.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/logo_profile4.xml b/app/src/main/res/drawable/logo_profile4.xml
new file mode 100644
index 0000000..08be0b3
--- /dev/null
+++ b/app/src/main/res/drawable/logo_profile4.xml
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/logo_profile5.xml b/app/src/main/res/drawable/logo_profile5.xml
new file mode 100644
index 0000000..d42ecc8
--- /dev/null
+++ b/app/src/main/res/drawable/logo_profile5.xml
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/logo_profile6.xml b/app/src/main/res/drawable/logo_profile6.xml
new file mode 100644
index 0000000..e5307b7
--- /dev/null
+++ b/app/src/main/res/drawable/logo_profile6.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/logo_profile7.xml b/app/src/main/res/drawable/logo_profile7.xml
new file mode 100644
index 0000000..fc59bbc
--- /dev/null
+++ b/app/src/main/res/drawable/logo_profile7.xml
@@ -0,0 +1,241 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/logo_profile8.xml b/app/src/main/res/drawable/logo_profile8.xml
new file mode 100644
index 0000000..d774ddd
--- /dev/null
+++ b/app/src/main/res/drawable/logo_profile8.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/logo_snackbar.xml b/app/src/main/res/drawable/logo_snackbar.xml
new file mode 100644
index 0000000..0846c94
--- /dev/null
+++ b/app/src/main/res/drawable/logo_snackbar.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/logo_splash.xml b/app/src/main/res/drawable/logo_splash.xml
new file mode 100644
index 0000000..2afcf51
--- /dev/null
+++ b/app/src/main/res/drawable/logo_splash.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/my_record.xml b/app/src/main/res/drawable/my_record.xml
new file mode 100644
index 0000000..57b8b4b
--- /dev/null
+++ b/app/src/main/res/drawable/my_record.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/my_stamp.xml b/app/src/main/res/drawable/my_stamp.xml
new file mode 100644
index 0000000..d5d158b
--- /dev/null
+++ b/app/src/main/res/drawable/my_stamp.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/record_empty.xml b/app/src/main/res/drawable/record_empty.xml
new file mode 100644
index 0000000..8f693ed
--- /dev/null
+++ b/app/src/main/res/drawable/record_empty.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/splash_layer_list.xml b/app/src/main/res/drawable/splash_layer_list.xml
new file mode 100644
index 0000000..019027e
--- /dev/null
+++ b/app/src/main/res/drawable/splash_layer_list.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/trip.png b/app/src/main/res/drawable/trip.png
new file mode 100644
index 0000000..f75814c
Binary files /dev/null and b/app/src/main/res/drawable/trip.png differ
diff --git a/app/src/main/res/drawable/trip2.png b/app/src/main/res/drawable/trip2.png
new file mode 100644
index 0000000..f218399
Binary files /dev/null and b/app/src/main/res/drawable/trip2.png differ
diff --git a/app/src/main/res/drawable/trip3.png b/app/src/main/res/drawable/trip3.png
new file mode 100644
index 0000000..f5253ff
Binary files /dev/null and b/app/src/main/res/drawable/trip3.png differ
diff --git a/app/src/main/res/drawable/weather_cloud.xml b/app/src/main/res/drawable/weather_cloud.xml
new file mode 100644
index 0000000..dc5f758
--- /dev/null
+++ b/app/src/main/res/drawable/weather_cloud.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/weather_moon.xml b/app/src/main/res/drawable/weather_moon.xml
new file mode 100644
index 0000000..d637baa
--- /dev/null
+++ b/app/src/main/res/drawable/weather_moon.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/weather_rain.xml b/app/src/main/res/drawable/weather_rain.xml
new file mode 100644
index 0000000..524aa23
--- /dev/null
+++ b/app/src/main/res/drawable/weather_rain.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/weather_snow.xml b/app/src/main/res/drawable/weather_snow.xml
new file mode 100644
index 0000000..208b6ac
--- /dev/null
+++ b/app/src/main/res/drawable/weather_snow.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/weather_sun.xml b/app/src/main/res/drawable/weather_sun.xml
new file mode 100644
index 0000000..07c0feb
--- /dev/null
+++ b/app/src/main/res/drawable/weather_sun.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/weather_thunder.xml b/app/src/main/res/drawable/weather_thunder.xml
new file mode 100644
index 0000000..591c1f5
--- /dev/null
+++ b/app/src/main/res/drawable/weather_thunder.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/weather_wind.xml b/app/src/main/res/drawable/weather_wind.xml
new file mode 100644
index 0000000..eb1bf98
--- /dev/null
+++ b/app/src/main/res/drawable/weather_wind.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/write_finish.xml b/app/src/main/res/drawable/write_finish.xml
new file mode 100644
index 0000000..88b33a8
--- /dev/null
+++ b/app/src/main/res/drawable/write_finish.xml
@@ -0,0 +1,200 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/write_place.xml b/app/src/main/res/drawable/write_place.xml
new file mode 100644
index 0000000..f1a5275
--- /dev/null
+++ b/app/src/main/res/drawable/write_place.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/write_stamp.xml b/app/src/main/res/drawable/write_stamp.xml
new file mode 100644
index 0000000..fa61a9c
--- /dev/null
+++ b/app/src/main/res/drawable/write_stamp.xml
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/font/pretendard_bold.otf b/app/src/main/res/font/pretendard_bold.otf
new file mode 100644
index 0000000..8e5e30a
Binary files /dev/null and b/app/src/main/res/font/pretendard_bold.otf differ
diff --git a/app/src/main/res/font/pretendard_medium.otf b/app/src/main/res/font/pretendard_medium.otf
new file mode 100644
index 0000000..0575069
Binary files /dev/null and b/app/src/main/res/font/pretendard_medium.otf differ
diff --git a/app/src/main/res/font/pretendard_regular.otf b/app/src/main/res/font/pretendard_regular.otf
new file mode 100644
index 0000000..08bf4cf
Binary files /dev/null and b/app/src/main/res/font/pretendard_regular.otf differ
diff --git a/app/src/main/res/font/pretendard_semibold.otf b/app/src/main/res/font/pretendard_semibold.otf
new file mode 100644
index 0000000..e7e36ab
Binary files /dev/null and b/app/src/main/res/font/pretendard_semibold.otf differ
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_momento.xml b/app/src/main/res/mipmap-anydpi-v26/ic_momento.xml
new file mode 100644
index 0000000..7f9f2eb
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_momento.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_momento_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_momento_round.xml
new file mode 100644
index 0000000..7f9f2eb
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_momento_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_momento.webp b/app/src/main/res/mipmap-hdpi/ic_momento.webp
new file mode 100644
index 0000000..14206db
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_momento.webp differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_momento_round.webp b/app/src/main/res/mipmap-hdpi/ic_momento_round.webp
new file mode 100644
index 0000000..c2337e9
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_momento_round.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_momento.webp b/app/src/main/res/mipmap-mdpi/ic_momento.webp
new file mode 100644
index 0000000..df539ec
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_momento.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_momento_round.webp b/app/src/main/res/mipmap-mdpi/ic_momento_round.webp
new file mode 100644
index 0000000..12f0f58
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_momento_round.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_momento.webp b/app/src/main/res/mipmap-xhdpi/ic_momento.webp
new file mode 100644
index 0000000..9dd5fd3
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_momento.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_momento_round.webp b/app/src/main/res/mipmap-xhdpi/ic_momento_round.webp
new file mode 100644
index 0000000..673c329
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_momento_round.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_momento.webp b/app/src/main/res/mipmap-xxhdpi/ic_momento.webp
new file mode 100644
index 0000000..ade4bcf
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_momento.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_momento_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_momento_round.webp
new file mode 100644
index 0000000..c3213c3
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_momento_round.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_momento.webp b/app/src/main/res/mipmap-xxxhdpi/ic_momento.webp
new file mode 100644
index 0000000..4c74cbb
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_momento.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_momento_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_momento_round.webp
new file mode 100644
index 0000000..cec8ca1
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_momento_round.webp differ
diff --git a/app/src/main/res/values-v31/themes.xml b/app/src/main/res/values-v31/themes.xml
new file mode 100644
index 0000000..f255e1e
--- /dev/null
+++ b/app/src/main/res/values-v31/themes.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3f4a0dc
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,11 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+ #FFEDE2DA
+
\ No newline at end of file
diff --git a/app/src/main/res/values/ic_momento_background.xml b/app/src/main/res/values/ic_momento_background.xml
new file mode 100644
index 0000000..d08fd9e
--- /dev/null
+++ b/app/src/main/res/values/ic_momento_background.xml
@@ -0,0 +1,4 @@
+
+
+ #FFFFFF
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..60fb829
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ 모멘토
+
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..0cba15e
--- /dev/null
+++ b/app/src/main/res/values/themes.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000..4df9255
--- /dev/null
+++ b/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000..9ee9997
--- /dev/null
+++ b/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/test/java/com/min/dnapp/ExampleUnitTest.kt b/app/src/test/java/com/min/dnapp/ExampleUnitTest.kt
new file mode 100644
index 0000000..047d7f7
--- /dev/null
+++ b/app/src/test/java/com/min/dnapp/ExampleUnitTest.kt
@@ -0,0 +1,17 @@
+package com.min.dnapp
+
+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/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 0000000..0bee734
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,10 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ alias(libs.plugins.android.application) apply false
+ alias(libs.plugins.kotlin.android) apply false
+ alias(libs.plugins.kotlin.compose) apply false
+ alias(libs.plugins.google.services) apply false
+ alias(libs.plugins.ksp) apply false
+ alias(libs.plugins.hilt) apply false
+ alias(libs.plugins.firebase.crashlytics) apply false
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..20e2a01
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. For more details, visit
+# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 0000000..c44dc68
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,73 @@
+[versions]
+agp = "8.11.1"
+kotlin = "2.2.0"
+coreKtx = "1.17.0"
+junit = "4.13.2"
+junitVersion = "1.3.0"
+espressoCore = "3.7.0"
+lifecycleRuntimeKtx = "2.9.2"
+activityCompose = "1.10.1"
+#composeBom = "2024.09.00"
+composeBom = "2025.05.00"
+google = "4.4.3"
+firebaseBom = "34.1.0"
+crashlytics = "3.0.6"
+kakao = "2.21.6"
+nav = "2.9.3"
+hilt = "2.56.2"
+ksp = "2.2.0-2.0.2"
+hiltNavigationCompose = "1.2.0"
+splash = "1.0.1"
+retrofit = "3.0.0"
+kotlinSerialization = "1.9.0"
+okhttp = "5.0.0"
+coil = "3.1.0"
+lottie = "6.6.9"
+datastore = "1.1.7"
+
+[libraries]
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
+androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
+androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
+androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
+androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
+androidx-ui = { group = "androidx.compose.ui", name = "ui" }
+androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
+androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
+androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
+androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
+androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
+androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
+
+firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" }
+firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics-ndk" }
+firebase-analytics = { module = "com.google.firebase:firebase-analytics" }
+firebase-auth = { module = "com.google.firebase:firebase-auth" }
+firebase-firestore = { module = "com.google.firebase:firebase-firestore" }
+firebase-storage = { module = "com.google.firebase:firebase-storage" }
+kakao-sdk = { module = "com.kakao.sdk:v2-user", version.ref = "kakao" }
+androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "nav" }
+hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
+hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" }
+hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
+splash = { module = "androidx.core:core-splashscreen", version.ref = "splash" }
+retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
+retrofit-converter-kotlinx-serialization = { module = "com.squareup.retrofit2:converter-kotlinx-serialization", version.ref = "retrofit" }
+kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinSerialization" }
+okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
+coil = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" }
+coil-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coil" }
+lottie-compose = { module = "com.airbnb.android:lottie-compose", version.ref = "lottie" }
+datastore-preferences = { module = "androidx.datastore:datastore-preferences" , version.ref = "datastore" }
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "agp" }
+kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
+google-services = { id = "com.google.gms.google-services", version.ref = "google" }
+hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
+ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
+kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
+firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "crashlytics" }
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..d12884d
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sun Aug 24 15:58:59 KST 2025
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..ac1b06f
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100644
index 0000000..18526ed
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1,25 @@
+pluginManagement {
+ repositories {
+ google {
+ content {
+ includeGroupByRegex("com\\.android.*")
+ includeGroupByRegex("com\\.google.*")
+ includeGroupByRegex("androidx.*")
+ }
+ }
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ maven { url = java.net.URI("https://devrepo.kakao.com/nexus/content/groups/public/") }
+ }
+}
+
+rootProject.name = "Dngo"
+include(":app")
+
\ No newline at end of file