From 86838b03b7a1d09fc6894e4bd86e68b6e1fb15e0 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Mon, 2 Sep 2024 18:11:37 +0900 Subject: [PATCH 01/24] chore: Update Coil, Ktor Version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coil의 CMM 호환이 3.0.0부터 지원되어 모두 업데이트 하였습니다. --- .../all/src/main/java/com/b1nd/dodam/member/AllScreen.kt | 2 +- .../com/b1nd/dodam/askwakeupsong/AskWakeupSongScreen.kt | 2 +- .../java/com/b1nd/dodam/student/home/card/BannerCard.kt | 2 +- .../java/com/b1nd/dodam/student/home/card/WakeupSongCard.kt | 2 +- .../main/java/com/b1nd/dodam/wakeupsong/WakeupSongScreen.kt | 2 +- .../src/main/java/com/b1nd/dodam/setting/SettingScreen.kt | 2 +- gradle/libs.versions.toml | 6 +++--- settings.gradle.kts | 1 + 8 files changed, 10 insertions(+), 9 deletions(-) diff --git a/feature-student/all/src/main/java/com/b1nd/dodam/member/AllScreen.kt b/feature-student/all/src/main/java/com/b1nd/dodam/member/AllScreen.kt index c9a479bb..2bc605a5 100644 --- a/feature-student/all/src/main/java/com/b1nd/dodam/member/AllScreen.kt +++ b/feature-student/all/src/main/java/com/b1nd/dodam/member/AllScreen.kt @@ -31,7 +31,7 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage +import coil3.compose.AsyncImage import com.b1nd.dodam.dds.animation.bounceClick import com.b1nd.dodam.dds.component.DodamTopAppBar import com.b1nd.dodam.dds.component.button.DodamIconButton diff --git a/feature-student/ask-wakeup-song/src/main/java/com/b1nd/dodam/askwakeupsong/AskWakeupSongScreen.kt b/feature-student/ask-wakeup-song/src/main/java/com/b1nd/dodam/askwakeupsong/AskWakeupSongScreen.kt index 3af0c538..abb7b16e 100644 --- a/feature-student/ask-wakeup-song/src/main/java/com/b1nd/dodam/askwakeupsong/AskWakeupSongScreen.kt +++ b/feature-student/ask-wakeup-song/src/main/java/com/b1nd/dodam/askwakeupsong/AskWakeupSongScreen.kt @@ -40,7 +40,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import coil.compose.AsyncImage +import coil3.compose.AsyncImage import com.b1nd.dodam.dds.animation.bounceClick import com.b1nd.dodam.dds.component.DodamDialog import com.b1nd.dodam.dds.component.DodamLargeTopAppBar diff --git a/feature-student/home/src/main/java/com/b1nd/dodam/student/home/card/BannerCard.kt b/feature-student/home/src/main/java/com/b1nd/dodam/student/home/card/BannerCard.kt index 87211ae0..6ca1d66d 100644 --- a/feature-student/home/src/main/java/com/b1nd/dodam/student/home/card/BannerCard.kt +++ b/feature-student/home/src/main/java/com/b1nd/dodam/student/home/card/BannerCard.kt @@ -17,7 +17,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage +import coil3.compose.AsyncImage import com.b1nd.dodam.dds.foundation.DodamShape import com.b1nd.dodam.student.home.PagerIndicator import com.b1nd.dodam.student.home.model.BannerUiState diff --git a/feature-student/home/src/main/java/com/b1nd/dodam/student/home/card/WakeupSongCard.kt b/feature-student/home/src/main/java/com/b1nd/dodam/student/home/card/WakeupSongCard.kt index d9c49929..00724f3f 100644 --- a/feature-student/home/src/main/java/com/b1nd/dodam/student/home/card/WakeupSongCard.kt +++ b/feature-student/home/src/main/java/com/b1nd/dodam/student/home/card/WakeupSongCard.kt @@ -28,7 +28,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage +import coil3.compose.AsyncImage import com.b1nd.dodam.dds.animation.LoadingDotsIndicator import com.b1nd.dodam.dds.animation.bounceClick import com.b1nd.dodam.dds.foundation.DodamIcons diff --git a/feature-student/wakeup-song/src/main/java/com/b1nd/dodam/wakeupsong/WakeupSongScreen.kt b/feature-student/wakeup-song/src/main/java/com/b1nd/dodam/wakeupsong/WakeupSongScreen.kt index 6ab25ec0..e63694ba 100644 --- a/feature-student/wakeup-song/src/main/java/com/b1nd/dodam/wakeupsong/WakeupSongScreen.kt +++ b/feature-student/wakeup-song/src/main/java/com/b1nd/dodam/wakeupsong/WakeupSongScreen.kt @@ -43,7 +43,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import coil.compose.AsyncImage +import coil3.compose.AsyncImage import com.b1nd.dodam.data.core.model.Status import com.b1nd.dodam.dds.animation.bounceClick import com.b1nd.dodam.dds.animation.bounceCombinedClick diff --git a/feature/setting/src/main/java/com/b1nd/dodam/setting/SettingScreen.kt b/feature/setting/src/main/java/com/b1nd/dodam/setting/SettingScreen.kt index 7ec16fdf..95fc74f6 100644 --- a/feature/setting/src/main/java/com/b1nd/dodam/setting/SettingScreen.kt +++ b/feature/setting/src/main/java/com/b1nd/dodam/setting/SettingScreen.kt @@ -37,7 +37,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import coil.compose.AsyncImage +import coil3.compose.AsyncImage import com.b1nd.dodam.dds.component.DodamDialog import com.b1nd.dodam.dds.component.DodamSmallTopAppBar import com.b1nd.dodam.dds.component.button.DodamLargeFilledButton diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 13f94de8..e9dedaa5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -40,8 +40,8 @@ app-update = "2.1.0" spotless = "6.23.3" material = "1.11.0" bottomsheetdialog = "1.3.1" -ktor = "2.3.8" -coil = "2.6.0" +ktor = "3.0.0-wasm2" +coil = "3.0.0-alpha10" dds = "0.1.18" dds-cmm = "1.0.3" composeinvestigator = "1.5.10-0.2.1" @@ -116,7 +116,7 @@ ktor-client-content-negotiation = { group = "io.ktor", name = "ktor-client-conte ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktor" } ktor-client-logging = { group = "io.ktor", name = "ktor-client-logging", version.ref = "ktor" } ktor-client-auth = { group = "io.ktor", name = "ktor-client-auth", version.ref = "ktor" } -coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" } +coil-compose = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" } dodam-design-system = { group = "com.github.Team-B1ND", name = "dds-android", version.ref = "dds" } wheel-picker-compose = { group = "com.github.commandiron", name = "WheelPickerCompose", version.ref = "wheel-picker" } koin-android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 65545c57..35e7fe77 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,6 +14,7 @@ dependencyResolutionManagement { google() mavenCentral() maven("https://jitpack.io") + maven("https://maven.pkg.jetbrains.space/kotlin/p/wasm/experimental") } } From b8abe04db0e2132cc763f307c374776ea0f4ef93 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Tue, 3 Sep 2024 15:35:25 +0900 Subject: [PATCH 02/24] feat: Create Home Banner Component And Fix Coil --- .../primitive/KotlinSerializationPlugin.kt | 2 + .../MultiplatformKotlinSerializationPlugin.kt | 2 + dodam-teacher-android/build.gradle.kts | 24 +++- .../src/androidMain/AndroidManifest.xml | 1 + .../teacher/utiles/DodamTeacherApplication.kt | 13 +++ .../com/b1nd/dodam/teacher/DodamTeacherApp.kt | 42 ++++++- dodam-teacher-ios/iosApp/Info.plist | 4 + feature-teacher/home/build.gradle.kts | 86 ++++++++++++++ feature-teacher/home/consumer-rules.pro | 0 feature-teacher/home/proguard-rules.pro | 21 ++++ .../dodam/home/preview/HomeScreenPreview.kt | 14 +++ .../kotlin/com/b1nd/dodam/home/HomeScreen.kt | 109 ++++++++++++++++++ .../com/b1nd/dodam/home/card/BannerCard.kt | 78 +++++++++++++ .../com/b1nd/dodam/home/model/HomeUiState.kt | 46 ++++++++ .../dodam/home/navigation/HomeNavigation.kt | 26 +++++ gradle/libs.versions.toml | 16 ++- settings.gradle.kts | 3 +- 17 files changed, 475 insertions(+), 12 deletions(-) create mode 100644 dodam-teacher-android/src/androidMain/kotlin/com/b1nd/dodam/teacher/utiles/DodamTeacherApplication.kt create mode 100644 feature-teacher/home/build.gradle.kts create mode 100644 feature-teacher/home/consumer-rules.pro create mode 100644 feature-teacher/home/proguard-rules.pro create mode 100644 feature-teacher/home/src/androidMain/kotlin/com/b1nd/dodam/home/preview/HomeScreenPreview.kt create mode 100644 feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt create mode 100644 feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/BannerCard.kt create mode 100644 feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt create mode 100644 feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/navigation/HomeNavigation.kt diff --git a/build-logic/src/main/kotlin/com/b1nd/dodam/primitive/KotlinSerializationPlugin.kt b/build-logic/src/main/kotlin/com/b1nd/dodam/primitive/KotlinSerializationPlugin.kt index ef2bcbab..2682b5f1 100644 --- a/build-logic/src/main/kotlin/com/b1nd/dodam/primitive/KotlinSerializationPlugin.kt +++ b/build-logic/src/main/kotlin/com/b1nd/dodam/primitive/KotlinSerializationPlugin.kt @@ -15,6 +15,8 @@ class KotlinSerializationPlugin : Plugin { } dependencies { implementation(libs.library("kotlinx-serialization-json")) + implementation(libs.library("kotlinx-io-core")) + implementation(libs.library("kotlinx-io-bytestring")) } } } diff --git a/build-logic/src/main/kotlin/com/b1nd/dodam/primitive/MultiplatformKotlinSerializationPlugin.kt b/build-logic/src/main/kotlin/com/b1nd/dodam/primitive/MultiplatformKotlinSerializationPlugin.kt index 02e7d289..d24fde86 100644 --- a/build-logic/src/main/kotlin/com/b1nd/dodam/primitive/MultiplatformKotlinSerializationPlugin.kt +++ b/build-logic/src/main/kotlin/com/b1nd/dodam/primitive/MultiplatformKotlinSerializationPlugin.kt @@ -18,6 +18,8 @@ class MultiplatformKotlinSerializationPlugin : Plugin { sourceSets.commonMain { dependencies { implementation(libs.library("kotlinx-serialization-json")) + implementation(libs.library("kotlinx-io-core")) + implementation(libs.library("kotlinx-io-bytestring")) } } } diff --git a/dodam-teacher-android/build.gradle.kts b/dodam-teacher-android/build.gradle.kts index c0d8dda1..84a1cbc1 100644 --- a/dodam-teacher-android/build.gradle.kts +++ b/dodam-teacher-android/build.gradle.kts @@ -6,21 +6,17 @@ plugins { alias(libs.plugins.dodam.multiplatform.application) alias(libs.plugins.dodam.multiplatform.compose) alias(libs.plugins.dodam.multiplatform.kotlin) + alias(libs.plugins.dodam.multiplatform.kotlin.serialization) alias(libs.plugins.dodam.multiplatform.koin) } kotlin { setIOS("DodamTeacher", "com.b1nd.dodam.teacher") sourceSets { - androidMain.dependencies { - implementation(compose.preview) - implementation(libs.androidx.compose.activity) - implementation(libs.koin.android) - implementation(projects.keystore) - } commonMain.dependencies { implementation(libs.dodam.design.system.cmm) implementation(projects.common) + implementation(projects.ui) implementation(projects.network.login) implementation(projects.feature.onboarding) @@ -32,6 +28,22 @@ kotlin { implementation(projects.network.register) implementation(projects.common) implementation(projects.network.core) + implementation(projects.featureTeacher.home) + + implementation(libs.coil) + implementation(libs.coil.compose) + implementation(libs.coil.network.ktor) + implementation(libs.ktor.client.core) + } + androidMain.dependencies { + implementation(compose.preview) + implementation(libs.androidx.compose.activity) + implementation(libs.koin.android) + implementation(projects.keystore) + implementation(libs.coil.network.okhttp) + } + iosMain.dependencies { + implementation(libs.ktor.client.darwin) } } } diff --git a/dodam-teacher-android/src/androidMain/AndroidManifest.xml b/dodam-teacher-android/src/androidMain/AndroidManifest.xml index 61c139f3..49133636 100644 --- a/dodam-teacher-android/src/androidMain/AndroidManifest.xml +++ b/dodam-teacher-android/src/androidMain/AndroidManifest.xml @@ -7,6 +7,7 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" + android:name=".utiles.DodamTeacherApplication" android:theme="@android:style/Theme.Material.Light.NoActionBar"> + getAsyncImageLoader(context) + } val navHostController = rememberNavController() DodamTheme { NavHost( navController = navHostController, - startDestination = ONBOARDING_ROUTE, + startDestination = HOME_ROUTE, ) { onboardingScreen( onRegisterClick = navHostController::navigateToInfo, @@ -49,9 +68,26 @@ fun DodamTeacherApp() { loginScreen( onBackClick = navHostController::popBackStack, - navigateToMain = {}, + navigateToMain = navHostController::navigateToHome, role = "TEACHER", ) + + homeScreen() } } } + +@OptIn(ExperimentalCoilApi::class) +internal fun getAsyncImageLoader(context: PlatformContext) = + ImageLoader.Builder(context) + .crossfade(true) + .memoryCache { + MemoryCache.Builder() + .maxSizePercent(context, percent = 0.25) + .build() + } + .logger(DebugLogger()) + .components { + add(KtorNetworkFetcherFactory()) + } + .build() \ No newline at end of file diff --git a/dodam-teacher-ios/iosApp/Info.plist b/dodam-teacher-ios/iosApp/Info.plist index aaf5e182..23aa0f00 100644 --- a/dodam-teacher-ios/iosApp/Info.plist +++ b/dodam-teacher-ios/iosApp/Info.plist @@ -4,6 +4,10 @@ CADisableMinimumFrameDurationOnPhone + NSAppTransportSecurity + + NSAllowsArbitraryLoads + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable diff --git a/feature-teacher/home/build.gradle.kts b/feature-teacher/home/build.gradle.kts new file mode 100644 index 00000000..f884dc27 --- /dev/null +++ b/feature-teacher/home/build.gradle.kts @@ -0,0 +1,86 @@ +import com.b1nd.dodam.dsl.androidLibrary +import com.b1nd.dodam.dsl.debugImplementation +import com.b1nd.dodam.dsl.implementation +import com.b1nd.dodam.dsl.implementationPlatform +import com.b1nd.dodam.dsl.kotlin +import com.b1nd.dodam.dsl.setIOS +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + alias(libs.plugins.dodam.multiplatform.feature) + alias(libs.plugins.dodam.multiplatform.koin) + alias(libs.plugins.dodam.multiplatform.kotlin.serialization) +} + +kotlin { + + androidTarget { + @OptIn(ExperimentalKotlinGradlePluginApi::class) + compilerOptions { + jvmTarget.set(JvmTarget.JVM_21) + } + } + + setIOS( + name = "Home", + bundleId = "com.b1nd.dodam.home" + ) + + sourceSets { + commonMain.dependencies { + implementation(libs.dodam.design.system.cmm) + implementation(libs.kotlinx.datetime) + implementation(libs.kotlinx.coroutines.core) + implementation(projects.common) + implementation(projects.ui) + implementation(projects.logging) + + + implementation(projects.data.login) + implementation(projects.data.banner) + implementation(projects.data.meal) + implementation(projects.data.outing) + implementation(projects.data.nightStudy) + implementation(projects.data.schedule) + implementation(libs.kotlinx.io.bytestring) + implementation(libs.coil) + implementation(libs.coil.compose) + implementation(libs.coil.network.ktor) + implementation(libs.ktor.client.core) + } + + androidMain.dependencies { + implementation(libs.coil.network.okhttp) + } + iosMain.dependencies { + implementation(libs.ktor.client.darwin) + } +// iosMain.dependencies { +// implementation("io.ktor:ktor-client-ios:3.0.0-wasm2") +// } + } +} + +androidLibrary { + namespace = "com.b1nd.dodam.home" + compileSdk = libs.versions.compileSdk.get().toInt() + + buildFeatures { + compose = true + } + + defaultConfig { + minSdk = libs.versions.minSdk.get().toInt() + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + composeCompiler { + enableStrongSkippingMode = true + + reportsDestination = layout.buildDirectory.dir("compose_compiler") + stabilityConfigurationFile = rootProject.layout.projectDirectory.file("stability_config.conf") + } +} \ No newline at end of file diff --git a/feature-teacher/home/consumer-rules.pro b/feature-teacher/home/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/feature-teacher/home/proguard-rules.pro b/feature-teacher/home/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature-teacher/home/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature-teacher/home/src/androidMain/kotlin/com/b1nd/dodam/home/preview/HomeScreenPreview.kt b/feature-teacher/home/src/androidMain/kotlin/com/b1nd/dodam/home/preview/HomeScreenPreview.kt new file mode 100644 index 00000000..8e6f8a09 --- /dev/null +++ b/feature-teacher/home/src/androidMain/kotlin/com/b1nd/dodam/home/preview/HomeScreenPreview.kt @@ -0,0 +1,14 @@ +package com.b1nd.dodam.home.preview + +import androidx.compose.runtime.Composable +import androidx.compose.ui.tooling.preview.Preview +import com.b1nd.dodam.designsystem.DodamTheme +import com.b1nd.dodam.home.HomeScreen + +@Composable +@Preview +fun HomeScreenPreview() { + DodamTheme { + HomeScreen() + } +} \ No newline at end of file diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt new file mode 100644 index 00000000..ae8f5e56 --- /dev/null +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt @@ -0,0 +1,109 @@ +package com.b1nd.dodam.home + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.unit.dp +import coil3.compose.AsyncImage +import com.b1nd.dodam.data.banner.model.Banner +import com.b1nd.dodam.data.banner.model.BannerStatus +import com.b1nd.dodam.designsystem.DodamTheme +import com.b1nd.dodam.designsystem.component.ActionIcon +import com.b1nd.dodam.designsystem.component.DodamContentTopAppBar +import com.b1nd.dodam.designsystem.foundation.DodamIcons +import com.b1nd.dodam.designsystem.resources.Res +import com.b1nd.dodam.home.card.BannerCard +import com.b1nd.dodam.home.model.BannerUiState +import com.b1nd.dodam.ui.icons.DodamLogo +import io.ktor.utils.io.ByteReadChannel +import kotlinx.collections.immutable.persistentListOf +import org.jetbrains.compose.resources.painterResource + + +@Composable +internal fun HomeScreen( + +) { + Scaffold( + modifier = Modifier.background(DodamTheme.colors.backgroundNeutral), + topBar = { + DodamContentTopAppBar( + modifier = Modifier + .background(DodamTheme.colors.backgroundNeutral) + .statusBarsPadding(), + content = { + Column { + Row { + Spacer(Modifier.width(16.dp)) + Icon( + modifier = Modifier.width(90.dp), + imageVector = DodamLogo, + contentDescription = null, + tint = DodamTheme.colors.primaryNormal, + ) + } + } + }, + actionIcons = persistentListOf( + ActionIcon( + icon = DodamIcons.Bell, + onClick = {} + ) + ) + ) + } + ) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(it), + contentAlignment = Alignment.TopCenter, + ) { + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .background(DodamTheme.colors.backgroundNeutral) + .padding(horizontal = 16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + item { + BannerCard( + state = BannerUiState.Success( + persistentListOf( + Banner( + id = 0, + imageUrl = "https://dodam.kr.object.ncloudstorage.com/dodam/6634113f-951b-430c-81c9-957de0e8abddalimo.png", + redirectUrl = "https://dodam.b1nd.com", + title = "test", + status = BannerStatus.ACTIVE, + expireAt = kotlinx.datetime.LocalDateTime.parse("2024-01-26T13:48:25.623088") + ) + ) + ) + ) + } + } + } + } +} \ No newline at end of file diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/BannerCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/BannerCard.kt new file mode 100644 index 00000000..acb05c7b --- /dev/null +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/BannerCard.kt @@ -0,0 +1,78 @@ +package com.b1nd.dodam.home.card + +import androidx.compose.foundation.ExperimentalFoundationApi +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.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.text.input.KeyboardType.Companion.Uri +import androidx.compose.ui.unit.dp +import coil3.ImageLoader +import coil3.PlatformContext +import coil3.annotation.ExperimentalCoilApi +import coil3.compose.AsyncImage +import coil3.compose.LocalPlatformContext +import coil3.compose.rememberAsyncImagePainter +import coil3.memory.MemoryCache +import coil3.request.ImageRequest +import coil3.request.crossfade +import coil3.size.Size +import coil3.util.DebugLogger +import com.b1nd.dodam.designsystem.DodamTheme +import com.b1nd.dodam.designsystem.foundation.DodamIcons +import com.b1nd.dodam.home.model.BannerUiState +import com.b1nd.dodam.logging.KmLogging + +@OptIn(ExperimentalFoundationApi::class) +@Composable +internal fun BannerCard( + state: BannerUiState +) { + when (state) { + is BannerUiState.None -> {} + is BannerUiState.Success -> { + val banners = remember { state.data } + val bannerPagerState = rememberPagerState { state.data.size } + val urlHandler = LocalUriHandler.current + Box( + modifier = Modifier + .background(DodamTheme.colors.backgroundNeutral) + ) { + + HorizontalPager( + state = bannerPagerState + ) { page -> + AsyncImage( + modifier = Modifier + .fillMaxWidth() + .clip(DodamTheme.shapes.large) + .clickable { + urlHandler.openUri(banners[page].redirectUrl) + }, + model = banners[page].imageUrl, + placeholder = rememberVectorPainter(DodamIcons.Note.value), + error = rememberVectorPainter(DodamIcons.XMarkCircle.value), + onError = { + it.result.throwable.printStackTrace() + }, + contentDescription = null, + contentScale = ContentScale.FillWidth, + ) + } + } + } + } +} \ No newline at end of file diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt new file mode 100644 index 00000000..d6946d92 --- /dev/null +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt @@ -0,0 +1,46 @@ +package com.b1nd.dodam.home.model + +import com.b1nd.dodam.data.banner.model.Banner +import com.b1nd.dodam.data.nightstudy.model.NightStudy +import com.b1nd.dodam.data.outing.model.Outing +import com.b1nd.dodam.data.schedule.model.Schedule +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.ImmutableMap + +data class HomeUiState( + val showShimmer: Boolean = true, + val bannerUiState: BannerUiState = BannerUiState.None, + val mealUiState: MealUiState = MealUiState.Loading, + val outUiState: OutUiState = OutUiState.Loading, + val nightStudyUiState: NightStudyUiState = NightStudyUiState.Loading, + val scheduleUiState: ScheduleUiState = ScheduleUiState.Loading, +) + +sealed interface BannerUiState { + data object None : BannerUiState + data class Success(val data: ImmutableList) : BannerUiState +} + +sealed interface MealUiState { + data class Success(val data: ImmutableMap) : MealUiState + data object Loading : MealUiState + data object Error : MealUiState +} + +sealed interface OutUiState { + data class Success(val data: Outing?) : OutUiState + data object Loading : OutUiState + data object Error : OutUiState +} + +sealed interface NightStudyUiState { + data class Success(val data: NightStudy?) : NightStudyUiState + data object Loading : NightStudyUiState + data object Error : NightStudyUiState +} + +sealed interface ScheduleUiState { + data class Success(val data: ImmutableList) : ScheduleUiState + data object Loading : ScheduleUiState + data object Error : ScheduleUiState +} diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/navigation/HomeNavigation.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/navigation/HomeNavigation.kt new file mode 100644 index 00000000..89059066 --- /dev/null +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/navigation/HomeNavigation.kt @@ -0,0 +1,26 @@ +package com.b1nd.dodam.home.navigation + +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import com.b1nd.dodam.home.HomeScreen + +const val HOME_ROUTE = "home" + +fun NavController.navigateToHome(navOptions: NavOptions? = null) = + this.navigate(HOME_ROUTE) + +fun NavGraphBuilder.homeScreen() { + composable( + route = HOME_ROUTE, + enterTransition = { EnterTransition.None }, + exitTransition = { ExitTransition.None }, + popEnterTransition = { EnterTransition.None }, + popExitTransition = { ExitTransition.None }, + ) { + HomeScreen() + } +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e9dedaa5..7cbf5505 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,6 +11,7 @@ kotlinx-coroutines = "1.7.3" kotlinx-collections-immutable = "0.3.7" kotlinx-serialization-json = "1.6.2" kotlinx-datetime = "0.5.0" +kotlinx-io = "0.5.3" swift-klib = "0.6.3" # android @@ -41,7 +42,7 @@ spotless = "6.23.3" material = "1.11.0" bottomsheetdialog = "1.3.1" ktor = "3.0.0-wasm2" -coil = "3.0.0-alpha10" +coil = "3.0.0-alpha06" dds = "0.1.18" dds-cmm = "1.0.3" composeinvestigator = "1.5.10-0.2.1" @@ -72,7 +73,8 @@ kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx- kotlinx-collections-immutable = { group = "org.jetbrains.kotlinx", name = "kotlinx-collections-immutable", version.ref = "kotlinx-collections-immutable" } kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" } kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinx-datetime" } - +kotlinx-io-bytestring = {group = "org.jetbrains.kotlinx", name = "kotlinx-io-bytestring", version.ref = "kotlinx-io" } +kotlinx-io-core = {group = "org.jetbrains.kotlinx", name = "kotlinx-io-core", version.ref = "kotlinx-io" } # android android-gradle-plugin = { group = "com.android.tools.build", name = "gradle", version.ref = "agp" } androidx-compose-activity = { group = "androidx.activity", name = "activity-compose", version.ref = "androidx-activity-compose" } @@ -116,7 +118,15 @@ ktor-client-content-negotiation = { group = "io.ktor", name = "ktor-client-conte ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktor" } ktor-client-logging = { group = "io.ktor", name = "ktor-client-logging", version.ref = "ktor" } ktor-client-auth = { group = "io.ktor", name = "ktor-client-auth", version.ref = "ktor" } +ktor-client-json = { group = "io.ktor", name = "ktor-client-json", version.ref = "ktor" } +ktor-client-okhttp = { group = "io.ktor", name = "ktor-client-okhttp", version.ref = "ktor" } +ktor-client-darwin = { group = "io.ktor", name = "ktor-client-darwin", version.ref = "ktor" } coil-compose = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" } +coil-compose-core = { group = "io.coil-kt.coil3", name = "coil-compose-core" , version.ref = "coil"} +coil-network-ktor = { group = "io.coil-kt.coil3", name = "coil-network-ktor" , version.ref = "coil"} +coil-network-okhttp = { group = "io.coil-kt.coil3", name = "coil-network-okhttp" , version.ref = "coil"} +coil = { group = "io.coil-kt.coil3", name = "coil" , version.ref = "coil"} + dodam-design-system = { group = "com.github.Team-B1ND", name = "dds-android", version.ref = "dds" } wheel-picker-compose = { group = "com.github.commandiron", name = "WheelPickerCompose", version.ref = "wheel-picker" } koin-android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" } @@ -173,3 +183,5 @@ dodam-multiplatform-application = { id = "b1nd.dodam.primitive.multiplatform.app [bundles] compose-implementation = ["androidx-compose-ui", "androidx-compose-ui-foundation", "androidx-compose-ui-graphics", "androidx-compose-lifecycle", "androidx-compose-ui-tooling", "androidx-compose-ui-tooling-preview", "androidx-compose-material3"] +ktor-implementation = ["ktor-client-json", "ktor-client-logging", "ktor-client-core", "ktor-serialization-kotlinx-json", "ktor-client-content-negotiation"] +coil-implementaion = ["coil", "coil-compose", "coil-compose-core", "coil-network-ktor"] \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 35e7fe77..8f2fae26 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -70,5 +70,6 @@ include( ":feature-student:ask-wakeup-song", ":logging", ":dodam-teacher-android", - ":feature-teacher:register" + ":feature-teacher:register", + ":feature-teacher:home", ) From 5bbc1c46b56dbd59f0dc386ca4dcc5ac04bedbc9 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Tue, 3 Sep 2024 16:11:09 +0900 Subject: [PATCH 03/24] refactor: Create Multiplatform Coil Plugin --- build-logic/build.gradle.kts | 4 ++ .../primitive/MultiplatformCoilPlugin.kt | 37 +++++++++++++++++++ dodam-teacher-android/build.gradle.kts | 12 +----- feature-teacher/home/build.gradle.kts | 14 +------ gradle/libs.versions.toml | 6 +-- 5 files changed, 46 insertions(+), 27 deletions(-) create mode 100644 build-logic/src/main/kotlin/com/b1nd/dodam/primitive/MultiplatformCoilPlugin.kt diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index 15f291d5..a87bbf90 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -80,6 +80,10 @@ gradlePlugin { id = "b1nd.dodam.primitive.multiplatform.application" implementationClass = "com.b1nd.dodam.primitive.MultiplatformApplicationPlugin" } + register("multiplatformCoilPlugin") { + id = "b1nd.dodam.primitive.multiplatform.coil" + implementationClass = "com.b1nd.dodam.primitive.MultiplatformCoilPlugin" + } // convention register("androidFeature") { id = "b1nd.dodam.convention.android.feature" diff --git a/build-logic/src/main/kotlin/com/b1nd/dodam/primitive/MultiplatformCoilPlugin.kt b/build-logic/src/main/kotlin/com/b1nd/dodam/primitive/MultiplatformCoilPlugin.kt new file mode 100644 index 00000000..d346c93e --- /dev/null +++ b/build-logic/src/main/kotlin/com/b1nd/dodam/primitive/MultiplatformCoilPlugin.kt @@ -0,0 +1,37 @@ +package com.b1nd.dodam.primitive + +import com.b1nd.dodam.dsl.kotlin +import com.b1nd.dodam.dsl.library +import com.b1nd.dodam.dsl.libs +import com.b1nd.dodam.dsl.setupMultiplatform +import org.gradle.api.Plugin +import org.gradle.api.Project + +class MultiplatformCoilPlugin: Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("org.jetbrains.kotlin.plugin.serialization") + } + + kotlin { + sourceSets.commonMain.dependencies { + implementation(libs.library("kotlinx-serialization-json")) + implementation(libs.library("kotlinx-io-core")) + implementation(libs.library("kotlinx-io-bytestring")) + implementation(libs.library("coil")) + implementation(libs.library("coil.compose")) + implementation(libs.library("coil.network.ktor")) + implementation(libs.library("ktor.client.core")) + } + + sourceSets.androidMain.dependencies { + implementation(libs.library("coil.network.okhttp")) + } + sourceSets.iosMain.dependencies { + implementation(libs.library("ktor.client.darwin")) + } + } + } + } +} \ No newline at end of file diff --git a/dodam-teacher-android/build.gradle.kts b/dodam-teacher-android/build.gradle.kts index 84a1cbc1..ddab7e30 100644 --- a/dodam-teacher-android/build.gradle.kts +++ b/dodam-teacher-android/build.gradle.kts @@ -6,7 +6,7 @@ plugins { alias(libs.plugins.dodam.multiplatform.application) alias(libs.plugins.dodam.multiplatform.compose) alias(libs.plugins.dodam.multiplatform.kotlin) - alias(libs.plugins.dodam.multiplatform.kotlin.serialization) + alias(libs.plugins.dodam.multiplatform.coil) alias(libs.plugins.dodam.multiplatform.koin) } kotlin { @@ -29,21 +29,13 @@ kotlin { implementation(projects.common) implementation(projects.network.core) implementation(projects.featureTeacher.home) - - implementation(libs.coil) - implementation(libs.coil.compose) - implementation(libs.coil.network.ktor) - implementation(libs.ktor.client.core) } + androidMain.dependencies { implementation(compose.preview) implementation(libs.androidx.compose.activity) implementation(libs.koin.android) implementation(projects.keystore) - implementation(libs.coil.network.okhttp) - } - iosMain.dependencies { - implementation(libs.ktor.client.darwin) } } } diff --git a/feature-teacher/home/build.gradle.kts b/feature-teacher/home/build.gradle.kts index f884dc27..c80d1f36 100644 --- a/feature-teacher/home/build.gradle.kts +++ b/feature-teacher/home/build.gradle.kts @@ -10,7 +10,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { alias(libs.plugins.dodam.multiplatform.feature) alias(libs.plugins.dodam.multiplatform.koin) - alias(libs.plugins.dodam.multiplatform.kotlin.serialization) + alias(libs.plugins.dodam.multiplatform.coil) } kotlin { @@ -43,18 +43,6 @@ kotlin { implementation(projects.data.outing) implementation(projects.data.nightStudy) implementation(projects.data.schedule) - implementation(libs.kotlinx.io.bytestring) - implementation(libs.coil) - implementation(libs.coil.compose) - implementation(libs.coil.network.ktor) - implementation(libs.ktor.client.core) - } - - androidMain.dependencies { - implementation(libs.coil.network.okhttp) - } - iosMain.dependencies { - implementation(libs.ktor.client.darwin) } // iosMain.dependencies { // implementation("io.ktor:ktor-client-ios:3.0.0-wasm2") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7cbf5505..c44d2061 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -180,8 +180,6 @@ dodam-multiplatform-kotlin = { id = "b1nd.dodam.primitive.multiplatform.kotlin", dodam-multiplatform-kotlin-serialization = { id = "b1nd.dodam.primitive.multiplatform.kotlin.serialization", version = "unspecified"} dodam-multiplatform-koin = { id = "b1nd.dodam.primitive.multiplatform.koin", version = "unspecified"} dodam-multiplatform-application = { id = "b1nd.dodam.primitive.multiplatform.application", version = "unspecified"} +dodam-multiplatform-coil = { id = "b1nd.dodam.primitive.multiplatform.coil", version = "unspecified"} -[bundles] -compose-implementation = ["androidx-compose-ui", "androidx-compose-ui-foundation", "androidx-compose-ui-graphics", "androidx-compose-lifecycle", "androidx-compose-ui-tooling", "androidx-compose-ui-tooling-preview", "androidx-compose-material3"] -ktor-implementation = ["ktor-client-json", "ktor-client-logging", "ktor-client-core", "ktor-serialization-kotlinx-json", "ktor-client-content-negotiation"] -coil-implementaion = ["coil", "coil-compose", "coil-compose-core", "coil-network-ktor"] \ No newline at end of file +[bundles] \ No newline at end of file From a0697f6f461c9be669b546ee7dae4f7137477213 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Thu, 5 Sep 2024 11:30:22 +0900 Subject: [PATCH 04/24] feat: Create Home Meal Card --- feature-teacher/home/build.gradle.kts | 3 - .../kotlin/com/b1nd/dodam/home/HomeScreen.kt | 77 ++++++++++ .../com/b1nd/dodam/home/card/MealCard.kt | 131 ++++++++++++++++++ .../com/b1nd/dodam/home/model/HomeUiState.kt | 3 +- gradle/libs.versions.toml | 2 +- .../b1nd/dodam/ui/component/DodamContainer.kt | 110 +++++++++++++++ 6 files changed, 321 insertions(+), 5 deletions(-) create mode 100644 feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt create mode 100644 ui/src/commonMain/kotlin/com/b1nd/dodam/ui/component/DodamContainer.kt diff --git a/feature-teacher/home/build.gradle.kts b/feature-teacher/home/build.gradle.kts index c80d1f36..984683fd 100644 --- a/feature-teacher/home/build.gradle.kts +++ b/feature-teacher/home/build.gradle.kts @@ -44,9 +44,6 @@ kotlin { implementation(projects.data.nightStudy) implementation(projects.data.schedule) } -// iosMain.dependencies { -// implementation("io.ktor:ktor-client-ios:3.0.0-wasm2") -// } } } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt index ae8f5e56..b39d159a 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt @@ -1,7 +1,10 @@ package com.b1nd.dodam.home +import androidx.compose.animation.animateContentSize 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.Box import androidx.compose.foundation.layout.Column @@ -11,33 +14,49 @@ 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.statusBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +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.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import coil3.compose.AsyncImage import com.b1nd.dodam.data.banner.model.Banner import com.b1nd.dodam.data.banner.model.BannerStatus +import com.b1nd.dodam.data.meal.model.Meal +import com.b1nd.dodam.data.meal.model.MealDetail +import com.b1nd.dodam.data.meal.model.Menu import com.b1nd.dodam.designsystem.DodamTheme +import com.b1nd.dodam.designsystem.animation.rememberBounceIndication import com.b1nd.dodam.designsystem.component.ActionIcon import com.b1nd.dodam.designsystem.component.DodamContentTopAppBar import com.b1nd.dodam.designsystem.foundation.DodamIcons +import com.b1nd.dodam.designsystem.foundation.DodamShapes import com.b1nd.dodam.designsystem.resources.Res import com.b1nd.dodam.home.card.BannerCard +import com.b1nd.dodam.home.card.MealCard import com.b1nd.dodam.home.model.BannerUiState +import com.b1nd.dodam.home.model.MealUiState +import com.b1nd.dodam.ui.component.DodamContainer import com.b1nd.dodam.ui.icons.DodamLogo import io.ktor.utils.io.ByteReadChannel import kotlinx.collections.immutable.persistentListOf +import kotlinx.datetime.LocalDate import org.jetbrains.compose.resources.painterResource @@ -103,7 +122,65 @@ internal fun HomeScreen( ) ) } + item { + + MealCard( + state = MealUiState.Success( + data = Meal( + exists = false, + date = LocalDate.fromEpochDays(150000), + breakfast = MealDetail( + details = listOf( + Menu(name = "쇠고기우엉볶음밥", allergies = listOf()), + Menu(name = "오이생채", allergies = listOf()), + Menu(name = "불고기치즈파니니", allergies = listOf()), + Menu(name = "배추김치", allergies = listOf()), + Menu(name = "계란실파국", allergies = listOf()) + ), + calorie = 30f + ), + lunch = null, + dinner = null + ) + ), + onClickContent = {}, + onClickRefresh = {} + ) + } } } } +} + +@Composable +internal fun DefaultText(onClick: () -> Unit, label: String, body: String) { + Box( + modifier = Modifier + .fillMaxWidth() + .clickable( + onClick = onClick, + interactionSource = remember { MutableInteractionSource() }, + indication = rememberBounceIndication(), + ), + contentAlignment = Alignment.Center + + ) { + Column( + modifier = Modifier + .padding(6.dp) + .align(Alignment.CenterStart) + ) { + Text( + text = label, + style = DodamTheme.typography.caption1Medium(), + color = DodamTheme.colors.labelAlternative + ) + Spacer(modifier = Modifier.height(2.dp)) + Text( + text = body, + style = DodamTheme.typography.body1Bold().copy(fontWeight = FontWeight.Bold), + color = DodamTheme.colors.primaryNormal + ) + } + } } \ No newline at end of file diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt new file mode 100644 index 00000000..5b3d7c81 --- /dev/null +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt @@ -0,0 +1,131 @@ +package com.b1nd.dodam.home.card + +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.material3.Text +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.unit.dp +import com.b1nd.dodam.data.meal.model.Meal +import com.b1nd.dodam.data.meal.model.MealDetail +import com.b1nd.dodam.designsystem.DodamTheme +import com.b1nd.dodam.designsystem.animation.rememberBounceIndication +import com.b1nd.dodam.designsystem.component.DodamLoadingDots +import com.b1nd.dodam.designsystem.component.DodamPageIndicator +import com.b1nd.dodam.designsystem.foundation.DodamIcons +import com.b1nd.dodam.home.DefaultText +import com.b1nd.dodam.home.model.MealUiState +import com.b1nd.dodam.ui.component.DodamContainer + +@OptIn(ExperimentalFoundationApi::class) +@Composable +internal fun MealCard( + state: MealUiState, + onClickContent: () -> Unit, + onClickRefresh: () -> Unit +) { + // 급식 리스트로 빠바박 3개옴 + DodamContainer( + icon = DodamIcons.ForkAndKnife, + title = "오늘의 급식" + ) { + when (state) { + is MealUiState.Success -> { + val mealState = rememberPagerState { 3 } + Column { + HorizontalPager( + state = mealState, + modifier = Modifier + .padding( + top = 6.dp, + start = 6.dp, + end = 6.dp, + ) + .animateContentSize() + .clickable( + onClick = onClickContent, + interactionSource = remember { MutableInteractionSource() }, + indication = rememberBounceIndication() + ) + ) { index -> + val meals = index.getMeal(state.data)?.details + if (meals != null) { + Column { + for (i in meals.indices step 2) { + Row( + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = meals[i].name, + style = DodamTheme.typography.body1Medium(), + color = DodamTheme.colors.labelNormal + ) + Spacer(Modifier.weight(1f)) + Text( + text = meals.getOrNull(i)?.name ?: "", + style = DodamTheme.typography.body1Medium(), + color = DodamTheme.colors.labelNormal + ) + Spacer(Modifier.weight(1f)) + } + } + } + } else { + DefaultText( + onClick = onClickContent, + label = "오늘은 급식이 없어요", + body = "내일 급식 보러가기", + ) + } + } + DodamPageIndicator( + modifier = Modifier.align(Alignment.End), + pagerState = mealState + ) + } + } + is MealUiState.Loading -> { + Box( + modifier = Modifier + .fillMaxWidth() + .height(50.dp), + contentAlignment = Alignment.Center, + ) { + DodamLoadingDots() + } + } + is MealUiState.Error -> { + DefaultText( + onClick = onClickRefresh, + label = "급식을 불러올 수 없어요", + body = "다시 불러오기" + ) + } + } + } +} + +private fun Int.getMeal(meal: Meal): MealDetail? = + when (this) { + 0 -> meal.breakfast + 1 -> meal.lunch + else -> meal.dinner + } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt index d6946d92..3c69148b 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt @@ -1,6 +1,7 @@ package com.b1nd.dodam.home.model import com.b1nd.dodam.data.banner.model.Banner +import com.b1nd.dodam.data.meal.model.Meal import com.b1nd.dodam.data.nightstudy.model.NightStudy import com.b1nd.dodam.data.outing.model.Outing import com.b1nd.dodam.data.schedule.model.Schedule @@ -22,7 +23,7 @@ sealed interface BannerUiState { } sealed interface MealUiState { - data class Success(val data: ImmutableMap) : MealUiState + data class Success(val data: Meal) : MealUiState data object Loading : MealUiState data object Error : MealUiState } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c44d2061..91c19e74 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -44,7 +44,7 @@ bottomsheetdialog = "1.3.1" ktor = "3.0.0-wasm2" coil = "3.0.0-alpha06" dds = "0.1.18" -dds-cmm = "1.0.3" +dds-cmm = "1.0.4" composeinvestigator = "1.5.10-0.2.1" wheel-picker = "1.1.11" koin = "3.5.6" diff --git a/ui/src/commonMain/kotlin/com/b1nd/dodam/ui/component/DodamContainer.kt b/ui/src/commonMain/kotlin/com/b1nd/dodam/ui/component/DodamContainer.kt new file mode 100644 index 00000000..a5501d0f --- /dev/null +++ b/ui/src/commonMain/kotlin/com/b1nd/dodam/ui/component/DodamContainer.kt @@ -0,0 +1,110 @@ +package com.b1nd.dodam.ui.component + +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.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.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.unit.dp +import com.b1nd.dodam.designsystem.DodamTheme +import com.b1nd.dodam.designsystem.animation.rememberBounceIndication +import com.b1nd.dodam.designsystem.foundation.DodamIcons + +@Composable +fun DodamContainer( + modifier: Modifier = Modifier, + icon: DodamIcons, + title: String, + showNextButton: Boolean = false, + onNextClick: (() -> Unit)? = null, + content: @Composable () -> Unit, +) { + Column( + modifier = modifier + .fillMaxWidth() + .animateContentSize() + .background( + color = DodamTheme.colors.backgroundNormal, + shape = DodamTheme.shapes.large, + ), + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .then( + if (showNextButton) { + Modifier.clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = rememberBounceIndication(), + onClick = onNextClick?: {} + ) + } else { + Modifier + }, + ) + .padding(start = 10.dp, end = 10.dp, top = 16.dp, bottom = 6.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Spacer(modifier = Modifier.width(6.dp)) + Box( + modifier = Modifier + .background( + color = DodamTheme.colors.primaryNormal.copy(alpha = 0.65f), + shape = RoundedCornerShape(100), + ), + contentAlignment = Alignment.Center, + ) { + Icon( + modifier = Modifier + .padding(8.dp) + .size(16.dp), + imageVector = icon.value, + contentDescription = "icon", + tint = DodamTheme.colors.staticWhite, + ) + } + + Spacer(modifier = Modifier.width(12.dp)) + + Text( + text = title, + color = DodamTheme.colors.labelStrong, + style = DodamTheme.typography.headlineBold(), + ) + + Spacer(modifier = Modifier.weight(1f)) + + if (showNextButton) { + Icon( + modifier = Modifier + .size(16.dp), + imageVector = DodamIcons.ChevronRight.value, + contentDescription = "next", + tint = DodamTheme.colors.lineNormal, + ) + } + } + Box( + modifier = Modifier.padding(horizontal = 6.dp) + ) { + content() + } + + Spacer(modifier = Modifier.height(10.dp)) + } +} From 23452a390332d15f15c0688f00774a96a0e0c071 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Thu, 5 Sep 2024 11:30:53 +0900 Subject: [PATCH 05/24] chore: Remove Banner Placeholder Image --- .../commonMain/kotlin/com/b1nd/dodam/home/card/BannerCard.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/BannerCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/BannerCard.kt index acb05c7b..ca529d5d 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/BannerCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/BannerCard.kt @@ -63,8 +63,6 @@ internal fun BannerCard( urlHandler.openUri(banners[page].redirectUrl) }, model = banners[page].imageUrl, - placeholder = rememberVectorPainter(DodamIcons.Note.value), - error = rememberVectorPainter(DodamIcons.XMarkCircle.value), onError = { it.result.throwable.printStackTrace() }, From c2b68fb4b8d19ace88ea696da6f1246680b883ed Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Thu, 5 Sep 2024 12:03:28 +0900 Subject: [PATCH 06/24] feat: Create Home Out Card --- .../kotlin/com/b1nd/dodam/home/HomeScreen.kt | 44 ++++++++++++++++ .../com/b1nd/dodam/home/card/MealCard.kt | 4 +- .../com/b1nd/dodam/home/card/OutCard.kt | 51 +++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt index b39d159a..1fe845b6 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt @@ -44,12 +44,16 @@ import com.b1nd.dodam.data.meal.model.Menu import com.b1nd.dodam.designsystem.DodamTheme import com.b1nd.dodam.designsystem.animation.rememberBounceIndication import com.b1nd.dodam.designsystem.component.ActionIcon +import com.b1nd.dodam.designsystem.component.ButtonRole +import com.b1nd.dodam.designsystem.component.ButtonSize +import com.b1nd.dodam.designsystem.component.DodamButton import com.b1nd.dodam.designsystem.component.DodamContentTopAppBar import com.b1nd.dodam.designsystem.foundation.DodamIcons import com.b1nd.dodam.designsystem.foundation.DodamShapes import com.b1nd.dodam.designsystem.resources.Res import com.b1nd.dodam.home.card.BannerCard import com.b1nd.dodam.home.card.MealCard +import com.b1nd.dodam.home.card.OutCard import com.b1nd.dodam.home.model.BannerUiState import com.b1nd.dodam.home.model.MealUiState import com.b1nd.dodam.ui.component.DodamContainer @@ -147,6 +151,10 @@ internal fun HomeScreen( onClickRefresh = {} ) } + + item { + OutCard() + } } } } @@ -183,4 +191,40 @@ internal fun DefaultText(onClick: () -> Unit, label: String, body: String) { ) } } +} + +@Composable +internal fun InnerCountCard( + modifier: Modifier = Modifier, + title: String, + content: String, + buttonText: String, + onClick: () -> Unit +) { + Row( + modifier = modifier + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Column { + Text( + text = title, + style = DodamTheme.typography.labelMedium(), + color = DodamTheme.colors.labelAssistive + ) + Text( + text = content, + style = DodamTheme.typography.heading2Medium(), + color = DodamTheme.colors.labelNormal + ) + } + Spacer(modifier = Modifier.weight(1f)) + Spacer(modifier = Modifier.width(8.dp)) + DodamButton( + text = buttonText, + onClick = onClick, + buttonRole = ButtonRole.Assistive, + buttonSize = ButtonSize.Small, + ) + } } \ No newline at end of file diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt index 5b3d7c81..82c8f27f 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt @@ -97,7 +97,9 @@ internal fun MealCard( } } DodamPageIndicator( - modifier = Modifier.align(Alignment.End), + modifier = Modifier + .padding(end = 6.dp) + .align(Alignment.End), pagerState = mealState ) } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt new file mode 100644 index 00000000..efe0ad30 --- /dev/null +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt @@ -0,0 +1,51 @@ +package com.b1nd.dodam.home.card + +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.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.b1nd.dodam.designsystem.DodamTheme +import com.b1nd.dodam.designsystem.foundation.DodamIcons +import com.b1nd.dodam.home.InnerCountCard +import com.b1nd.dodam.ui.component.DodamContainer + +@Composable +fun OutCard( + +) { + DodamContainer( + icon = DodamIcons.DoorOpen, + title = "외출/외박 현황" + ) { + Column( + modifier = Modifier + .padding(6.dp) + .fillMaxWidth() + ) { + InnerCountCard( + title = "현재 외출중인 학생", + content = "13명", + buttonText = "12명 대기중", + onClick = { + + } + ) + Spacer(modifier = Modifier.height(16.dp)) + InnerCountCard( + title = "현재 외박중인 학생", + content = "10명", + buttonText = "11명 대기중", + onClick = { + + } + ) + } + } +} From 2c541199ca1f1f0a0b36c8809ca9ee2519ae5e56 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Thu, 5 Sep 2024 12:05:54 +0900 Subject: [PATCH 07/24] feat: Create Home Night Study Card --- .../kotlin/com/b1nd/dodam/home/HomeScreen.kt | 5 +++ .../b1nd/dodam/home/card/NightStudyCard.kt | 43 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt index 1fe845b6..ea75ad6d 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt @@ -53,6 +53,7 @@ import com.b1nd.dodam.designsystem.foundation.DodamShapes import com.b1nd.dodam.designsystem.resources.Res import com.b1nd.dodam.home.card.BannerCard import com.b1nd.dodam.home.card.MealCard +import com.b1nd.dodam.home.card.NightStudyCard import com.b1nd.dodam.home.card.OutCard import com.b1nd.dodam.home.model.BannerUiState import com.b1nd.dodam.home.model.MealUiState @@ -155,6 +156,10 @@ internal fun HomeScreen( item { OutCard() } + + item { + NightStudyCard() + } } } } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt new file mode 100644 index 00000000..64ace985 --- /dev/null +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt @@ -0,0 +1,43 @@ +package com.b1nd.dodam.home.card + +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.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.b1nd.dodam.data.nightstudy.model.NightStudy +import com.b1nd.dodam.designsystem.DodamTheme +import com.b1nd.dodam.designsystem.foundation.DodamIcons +import com.b1nd.dodam.home.InnerCountCard +import com.b1nd.dodam.ui.component.DodamContainer + +@Composable +fun NightStudyCard( + +) { + DodamContainer( + icon = DodamIcons.DoorOpen, + title = "심야 자습 현황" + ) { + Column( + modifier = Modifier + .padding(6.dp) + .fillMaxWidth() + ) { + InnerCountCard( + title = "현재 자습중인 학생", + content = "13명", + buttonText = "12명 대기중", + onClick = { + + } + ) + } + } +} From 126c71b1bfe9d2f901dc6894d43a56de6adee55f Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Thu, 5 Sep 2024 12:34:29 +0900 Subject: [PATCH 08/24] feat: Create Common Dodam Date --- common/build.gradle.kts | 1 + .../com/b1nd/dodam/common/date/DodamDate.kt | 7 +++++++ .../b1nd/dodam/common/date/DodamDate.ios.kt | 18 ++++++++++++++++++ .../dodam/common/date/DodamDate.android.kt | 9 +++++++++ 4 files changed, 35 insertions(+) create mode 100644 common/src/commonMain/kotlin/com/b1nd/dodam/common/date/DodamDate.kt create mode 100644 common/src/iosMain/kotlin/com/b1nd/dodam/common/date/DodamDate.ios.kt create mode 100644 common/src/main/java/com/b1nd/dodam/common/date/DodamDate.android.kt diff --git a/common/build.gradle.kts b/common/build.gradle.kts index b197fdfd..054bc026 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -11,6 +11,7 @@ kotlin { commonMain.dependencies { implementation(libs.koin.core) implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.datetime) } } } diff --git a/common/src/commonMain/kotlin/com/b1nd/dodam/common/date/DodamDate.kt b/common/src/commonMain/kotlin/com/b1nd/dodam/common/date/DodamDate.kt new file mode 100644 index 00000000..a0034a7f --- /dev/null +++ b/common/src/commonMain/kotlin/com/b1nd/dodam/common/date/DodamDate.kt @@ -0,0 +1,7 @@ +package com.b1nd.dodam.common.date + +import kotlinx.datetime.LocalDateTime + +expect object DodamDate { + fun now(): LocalDateTime +} \ No newline at end of file diff --git a/common/src/iosMain/kotlin/com/b1nd/dodam/common/date/DodamDate.ios.kt b/common/src/iosMain/kotlin/com/b1nd/dodam/common/date/DodamDate.ios.kt new file mode 100644 index 00000000..a7927a03 --- /dev/null +++ b/common/src/iosMain/kotlin/com/b1nd/dodam/common/date/DodamDate.ios.kt @@ -0,0 +1,18 @@ +package com.b1nd.dodam.common.date + +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toKotlinInstant +import kotlinx.datetime.toLocalDateTime +import platform.Foundation.NSDate +import platform.Foundation.now + +actual object DodamDate { + actual fun now(): LocalDateTime = + NSDate + .now() + .toKotlinInstant() + .toLocalDateTime(TimeZone.currentSystemDefault()) + + +} \ No newline at end of file diff --git a/common/src/main/java/com/b1nd/dodam/common/date/DodamDate.android.kt b/common/src/main/java/com/b1nd/dodam/common/date/DodamDate.android.kt new file mode 100644 index 00000000..07289497 --- /dev/null +++ b/common/src/main/java/com/b1nd/dodam/common/date/DodamDate.android.kt @@ -0,0 +1,9 @@ +package com.b1nd.dodam.common.date + +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.toKotlinLocalDateTime + +actual object DodamDate { + actual fun now(): LocalDateTime = java.time.LocalDateTime.now().toKotlinLocalDateTime() + +} \ No newline at end of file From 9c346ab7bff17b441113b8117d0824fcff12a5d1 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Thu, 5 Sep 2024 14:16:26 +0900 Subject: [PATCH 09/24] feat: Create Home Scedule Card --- .../com/b1nd/dodam/common/date/DodamDate.kt | 2 + .../b1nd/dodam/common/date/DodamDate.ios.kt | 5 + .../dodam/common/date/DodamDate.android.kt | 4 +- .../kotlin/com/b1nd/dodam/home/HomeScreen.kt | 46 +++- .../b1nd/dodam/home/card/NightStudyCard.kt | 4 +- .../com/b1nd/dodam/home/card/OutCard.kt | 2 +- .../com/b1nd/dodam/home/card/ScheduleCard.kt | 253 ++++++++++++++++++ 7 files changed, 311 insertions(+), 5 deletions(-) create mode 100644 feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/ScheduleCard.kt diff --git a/common/src/commonMain/kotlin/com/b1nd/dodam/common/date/DodamDate.kt b/common/src/commonMain/kotlin/com/b1nd/dodam/common/date/DodamDate.kt index a0034a7f..4aca9af6 100644 --- a/common/src/commonMain/kotlin/com/b1nd/dodam/common/date/DodamDate.kt +++ b/common/src/commonMain/kotlin/com/b1nd/dodam/common/date/DodamDate.kt @@ -1,7 +1,9 @@ package com.b1nd.dodam.common.date +import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDateTime expect object DodamDate { fun now(): LocalDateTime + fun localDateNow(): LocalDate } \ No newline at end of file diff --git a/common/src/iosMain/kotlin/com/b1nd/dodam/common/date/DodamDate.ios.kt b/common/src/iosMain/kotlin/com/b1nd/dodam/common/date/DodamDate.ios.kt index a7927a03..f65e8e11 100644 --- a/common/src/iosMain/kotlin/com/b1nd/dodam/common/date/DodamDate.ios.kt +++ b/common/src/iosMain/kotlin/com/b1nd/dodam/common/date/DodamDate.ios.kt @@ -1,5 +1,6 @@ package com.b1nd.dodam.common.date +import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDateTime import kotlinx.datetime.TimeZone import kotlinx.datetime.toKotlinInstant @@ -14,5 +15,9 @@ actual object DodamDate { .toKotlinInstant() .toLocalDateTime(TimeZone.currentSystemDefault()) + actual fun localDateNow(): LocalDate = now().date + + + } \ No newline at end of file diff --git a/common/src/main/java/com/b1nd/dodam/common/date/DodamDate.android.kt b/common/src/main/java/com/b1nd/dodam/common/date/DodamDate.android.kt index 07289497..193683a2 100644 --- a/common/src/main/java/com/b1nd/dodam/common/date/DodamDate.android.kt +++ b/common/src/main/java/com/b1nd/dodam/common/date/DodamDate.android.kt @@ -1,9 +1,11 @@ package com.b1nd.dodam.common.date +import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.toKotlinLocalDate import kotlinx.datetime.toKotlinLocalDateTime actual object DodamDate { actual fun now(): LocalDateTime = java.time.LocalDateTime.now().toKotlinLocalDateTime() - + actual fun localDateNow(): LocalDate = java.time.LocalDate.now().toKotlinLocalDate() } \ No newline at end of file diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt index ea75ad6d..61bc8244 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt @@ -36,11 +36,15 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import coil3.compose.AsyncImage +import com.b1nd.dodam.common.date.DodamDate import com.b1nd.dodam.data.banner.model.Banner import com.b1nd.dodam.data.banner.model.BannerStatus import com.b1nd.dodam.data.meal.model.Meal import com.b1nd.dodam.data.meal.model.MealDetail import com.b1nd.dodam.data.meal.model.Menu +import com.b1nd.dodam.data.schedule.model.Grade +import com.b1nd.dodam.data.schedule.model.Schedule +import com.b1nd.dodam.data.schedule.model.ScheduleType import com.b1nd.dodam.designsystem.DodamTheme import com.b1nd.dodam.designsystem.animation.rememberBounceIndication import com.b1nd.dodam.designsystem.component.ActionIcon @@ -55,13 +59,17 @@ import com.b1nd.dodam.home.card.BannerCard import com.b1nd.dodam.home.card.MealCard import com.b1nd.dodam.home.card.NightStudyCard import com.b1nd.dodam.home.card.OutCard +import com.b1nd.dodam.home.card.ScheduleCard import com.b1nd.dodam.home.model.BannerUiState import com.b1nd.dodam.home.model.MealUiState +import com.b1nd.dodam.home.model.ScheduleUiState import com.b1nd.dodam.ui.component.DodamContainer import com.b1nd.dodam.ui.icons.DodamLogo import io.ktor.utils.io.ByteReadChannel import kotlinx.collections.immutable.persistentListOf +import kotlinx.datetime.DatePeriod import kotlinx.datetime.LocalDate +import kotlinx.datetime.plus import org.jetbrains.compose.resources.painterResource @@ -106,7 +114,7 @@ internal fun HomeScreen( ) { LazyColumn( modifier = Modifier - .fillMaxWidth() + .fillMaxSize() .background(DodamTheme.colors.backgroundNeutral) .padding(horizontal = 16.dp), verticalArrangement = Arrangement.spacedBy(12.dp), @@ -160,6 +168,42 @@ internal fun HomeScreen( item { NightStudyCard() } + + item { + ScheduleCard( + uiState = ScheduleUiState.Success( + persistentListOf( + Schedule( + id = 0, + name = "타운홀미팅", + place = "방송실", + type = ScheduleType.ACADEMIC, + date = persistentListOf(DodamDate.localDateNow()), + targetGrades = persistentListOf(Grade.GRADE_ALL) + ), + Schedule( + id = 1, + name = "교내 해커톤", + place = "방송실", + type = ScheduleType.ACADEMIC, + date = persistentListOf(DodamDate.localDateNow().plus(DatePeriod(days = 2))), + targetGrades = persistentListOf(Grade.GRADE_ALL) + ), + Schedule( + id = 2, + name = "직업 교육 박람회", + place = "방송실", + type = ScheduleType.ACADEMIC, + date = persistentListOf(DodamDate.localDateNow().plus(DatePeriod(days = 1))), + targetGrades = persistentListOf(Grade.GRADE_ALL) + ) + ) + ), + showShimmer = false, + fetchSchedule = {}, + onContentClick = {} + ) + } } } } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt index 64ace985..a0dc704c 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt @@ -18,11 +18,11 @@ import com.b1nd.dodam.home.InnerCountCard import com.b1nd.dodam.ui.component.DodamContainer @Composable -fun NightStudyCard( +internal fun NightStudyCard( ) { DodamContainer( - icon = DodamIcons.DoorOpen, + icon = DodamIcons.MoonPlus, title = "심야 자습 현황" ) { Column( diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt index efe0ad30..2ab22766 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt @@ -17,7 +17,7 @@ import com.b1nd.dodam.home.InnerCountCard import com.b1nd.dodam.ui.component.DodamContainer @Composable -fun OutCard( +internal fun OutCard( ) { DodamContainer( diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/ScheduleCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/ScheduleCard.kt new file mode 100644 index 00000000..38019a0c --- /dev/null +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/ScheduleCard.kt @@ -0,0 +1,253 @@ +package com.b1nd.dodam.home.card + +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.basicMarquee +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +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.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +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.Color +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.util.fastForEach +import com.b1nd.dodam.common.date.DodamDate +import com.b1nd.dodam.data.schedule.model.Grade +import com.b1nd.dodam.data.schedule.model.Schedule +import com.b1nd.dodam.designsystem.DodamTheme +import com.b1nd.dodam.designsystem.animation.rememberBounceIndication +import com.b1nd.dodam.designsystem.component.DodamLoadingDots +import com.b1nd.dodam.designsystem.foundation.DodamIcons +import com.b1nd.dodam.home.DefaultText +import com.b1nd.dodam.home.InnerCountCard +import com.b1nd.dodam.home.model.ScheduleUiState +import com.b1nd.dodam.ui.component.DodamContainer +import com.b1nd.dodam.ui.effect.shimmerEffect +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList +import kotlinx.datetime.DatePeriod +import kotlinx.datetime.isoDayNumber +import kotlinx.datetime.plus + +@Composable +internal fun ScheduleCard( + uiState: ScheduleUiState, + showShimmer: Boolean, + fetchSchedule: () -> Unit, + onContentClick: () -> Unit, +) { + val current = DodamDate.now().date + val tomorrow = current.plus(DatePeriod(days = 1)) + + DodamContainer( + icon = DodamIcons.Calendar, + title = "가까운 일정", + ) { + if (!showShimmer) { + when (uiState) { + is ScheduleUiState.Success -> { + val schedules = remember { uiState.data } + if (schedules.isEmpty()) { + DefaultText( + onClick = onContentClick, + label = "다음 달 일정을 기다려주세요", + body = "한 달간 일정이 없어요", + ) + } else { + Column( + modifier = Modifier + .fillMaxWidth() + .animateContentSize() + .padding(horizontal = 10.dp) + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = rememberBounceIndication(), + onClick = onContentClick, + ) + .padding(6.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + val latestSchedule = remember { + if (current in schedules.first().date) { + current + } else { + if (tomorrow in schedules.first().date) { + tomorrow + } else { + schedules.first().date.first() + } + } + } + + ScheduleComponent( + day = "${if (current == latestSchedule) current.dayOfMonth else latestSchedule.dayOfMonth}일", + dayOfWeek = listOf( + "월", + "화", + "수", + "목", + "금", + "토", + "일", + )[if (current == latestSchedule) current.dayOfWeek.isoDayNumber-1 else latestSchedule.dayOfWeek.isoDayNumber-1] + "요일", + body = remember { + schedules.filter { + latestSchedule in it.date + }.toImmutableList() + }, + ) + + val nextSchedule = remember { + if (schedules.first().date.size > 1 && schedules[0].date[0] != schedules[0].date[1]) { + if (tomorrow in schedules.first().date) { + tomorrow + } else { + null + } + } else { + if (schedules.size > 1) { + schedules[1].date.first() + } else { + null + } + } + } + + nextSchedule?.let { + ScheduleComponent( + day = "${it.dayOfMonth}일", + dayOfWeek = listOf( + "월", + "화", + "수", + "목", + "금", + "토", + "일", + )[it.dayOfWeek.isoDayNumber-1] + "요일", + body = remember { + schedules.filter { schedule -> + it in schedule.date + }.toImmutableList() + }, + ) + } + } + } + } + + is ScheduleUiState.Loading -> { + Box( + modifier = Modifier + .fillMaxWidth() + .height(50.dp), + contentAlignment = Alignment.Center, + ) { + DodamLoadingDots() + } + } + + is ScheduleUiState.Error -> { + DefaultText( + onClick = fetchSchedule, + label = "일정을 불러올 수 없어요", + body = "다시 불러오기", + ) + } + } + } else { + Box( + modifier = Modifier + .fillMaxWidth() + .height(50.dp), + contentAlignment = Alignment.Center, + ) { + DodamLoadingDots() + } + } + } +} + + +@OptIn(ExperimentalFoundationApi::class) +@Composable +private fun ScheduleComponent( + modifier: Modifier = Modifier, + day: String, + dayOfWeek: String, + body: ImmutableList +) { + Column( + modifier = modifier + ) { + Row( + verticalAlignment = Alignment.Bottom + ) { + Text( + text = day, + style = DodamTheme.typography.heading2Bold(), + color = DodamTheme.colors.labelNormal + ) + Spacer(modifier = Modifier.width(4.dp)) + Text( + text = dayOfWeek, + style = DodamTheme.typography.labelMedium(), + color = DodamTheme.colors.labelAlternative + ) + } + Spacer(Modifier.height(8.dp)) + body.fastForEach { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Box( + modifier = Modifier + .size(6.dp) + .background( + if (it.targetGrades.isEmpty()) { + Color(0xFF0167BC) + } else { + when (it.targetGrades.first()) { + Grade.GRADE_1 -> Color(0xFFFCA800) + Grade.GRADE_2 -> Color(0xFF3FBDE5) + Grade.GRADE_3 -> Color(0xFFA252E1) + Grade.GRADE_ALL -> Color(0xFFF97E6D) + Grade.GRADE_ETC -> Color(0xFF0167BC) + } + }, + CircleShape, + ), + ) + + Spacer(modifier = Modifier.width(6.dp)) + + Text( + modifier = Modifier.basicMarquee(), + text = it.name, + style = DodamTheme.typography.body1Medium().copy( + lineHeight = 19.sp + ), + color = DodamTheme.colors.labelNeutral, + ) + } + Spacer(modifier = Modifier.height(8.dp)) + } + } +} \ No newline at end of file From e5b7b1e2c36c92ecca5d9e66295bfb2092001b37 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Thu, 5 Sep 2024 14:19:20 +0900 Subject: [PATCH 10/24] feat: Home Add Bottom Spacer --- .../src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt index 61bc8244..d6943bbf 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt @@ -204,6 +204,9 @@ internal fun HomeScreen( onContentClick = {} ) } + item { + Spacer(modifier = Modifier.height(150.dp)) + } } } } From 0f68ea03f3ef9cab278c2dcf54e9d850205e0ba7 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Thu, 5 Sep 2024 15:42:43 +0900 Subject: [PATCH 11/24] feat: Meal Connect Remote --- dodam-teacher-android/build.gradle.kts | 12 ++++ .../kotlin/com/b1nd/dodam/teacher/Koin.kt | 22 ++++++ .../kotlin/com/b1nd/dodam/home/HomeScreen.kt | 33 ++++----- .../com/b1nd/dodam/home/HomeViewModel.kt | 70 +++++++++++++++++++ .../com/b1nd/dodam/home/di/ViewModelModule.kt | 10 +++ .../dodam/network/core/di/NetworkModule.kt | 1 + 6 files changed, 128 insertions(+), 20 deletions(-) create mode 100644 feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt create mode 100644 feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/di/ViewModelModule.kt diff --git a/dodam-teacher-android/build.gradle.kts b/dodam-teacher-android/build.gradle.kts index ddab7e30..e4ea900e 100644 --- a/dodam-teacher-android/build.gradle.kts +++ b/dodam-teacher-android/build.gradle.kts @@ -29,6 +29,18 @@ kotlin { implementation(projects.common) implementation(projects.network.core) implementation(projects.featureTeacher.home) + + implementation(projects.data.login) + implementation(projects.data.banner) + implementation(projects.data.meal) + implementation(projects.data.outing) + implementation(projects.data.nightStudy) + implementation(projects.data.schedule) + implementation(projects.network.banner) + implementation(projects.network.meal) + implementation(projects.network.outing) + implementation(projects.network.nightStudy) + implementation(projects.network.schedule) } androidMain.dependencies { diff --git a/dodam-teacher-android/src/commonMain/kotlin/com/b1nd/dodam/teacher/Koin.kt b/dodam-teacher-android/src/commonMain/kotlin/com/b1nd/dodam/teacher/Koin.kt index 2f4d357b..9f45cf2f 100644 --- a/dodam-teacher-android/src/commonMain/kotlin/com/b1nd/dodam/teacher/Koin.kt +++ b/dodam-teacher-android/src/commonMain/kotlin/com/b1nd/dodam/teacher/Koin.kt @@ -2,11 +2,22 @@ package com.b1nd.dodam.teacher import com.b1nd.dodam.common.network.di.coroutineScopeModule import com.b1nd.dodam.common.network.di.dispatchersModule +import com.b1nd.dodam.data.banner.di.bannerRepositoryModule import com.b1nd.dodam.data.login.di.loginRepositoryModule +import com.b1nd.dodam.data.meal.di.mealRepositoryModule +import com.b1nd.dodam.data.nightstudy.di.nightStudyRepositoryModule +import com.b1nd.dodam.data.outing.di.outingRepositoryModule +import com.b1nd.dodam.data.schedule.di.scheduleRepositoryModule import com.b1nd.dodam.datastore.di.dataStoreModule +import com.b1nd.dodam.home.di.homeViewModelModule import com.b1nd.dodam.login.di.loginViewModelModule +import com.b1nd.dodam.network.banner.di.bannerDataSourceModule import com.b1nd.dodam.network.core.di.networkCoreModule import com.b1nd.dodam.network.login.di.loginDataSourceModule +import com.b1nd.dodam.network.meal.di.mealDataSourceModule +import com.b1nd.dodam.network.nightstudy.di.nightStudyDataSourceModule +import com.b1nd.dodam.network.outing.di.outingDataSourceModule +import com.b1nd.dodam.network.schedule.di.scheduleDatasourceModule import com.b1nd.dodam.register.di.registerDataSourceModule import com.b1nd.dodam.register.di.registerRepositoryModule import com.b1nd.dodam.register.di.registerViewModelModule @@ -26,6 +37,17 @@ fun initKoin(block: KoinApplication.() -> Unit = {}) { loginViewModelModule, loginRepositoryModule, loginDataSourceModule, + homeViewModelModule, + bannerRepositoryModule, + bannerDataSourceModule, + mealRepositoryModule, + mealDataSourceModule, + outingRepositoryModule, + outingDataSourceModule, + nightStudyRepositoryModule, + nightStudyDataSourceModule, + scheduleRepositoryModule, + scheduleDatasourceModule ) block() } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt index d6943bbf..94f92b44 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt @@ -25,6 +25,8 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -71,12 +73,18 @@ import kotlinx.datetime.DatePeriod import kotlinx.datetime.LocalDate import kotlinx.datetime.plus import org.jetbrains.compose.resources.painterResource +import org.koin.compose.viewmodel.koinViewModel +import org.koin.core.annotation.KoinExperimentalAPI +@OptIn(KoinExperimentalAPI::class) @Composable internal fun HomeScreen( - + viewModel: HomeViewModel = koinViewModel() ) { + + val state by viewModel.state.collectAsState() + Scaffold( modifier = Modifier.background(DodamTheme.colors.backgroundNeutral), topBar = { @@ -138,26 +146,11 @@ internal fun HomeScreen( item { MealCard( - state = MealUiState.Success( - data = Meal( - exists = false, - date = LocalDate.fromEpochDays(150000), - breakfast = MealDetail( - details = listOf( - Menu(name = "쇠고기우엉볶음밥", allergies = listOf()), - Menu(name = "오이생채", allergies = listOf()), - Menu(name = "불고기치즈파니니", allergies = listOf()), - Menu(name = "배추김치", allergies = listOf()), - Menu(name = "계란실파국", allergies = listOf()) - ), - calorie = 30f - ), - lunch = null, - dinner = null - ) - ), + state = state.mealUiState, onClickContent = {}, - onClickRefresh = {} + onClickRefresh = { + viewModel.loadMeal(DodamDate.localDateNow()) + } ) } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt new file mode 100644 index 00000000..af3d6c91 --- /dev/null +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt @@ -0,0 +1,70 @@ +package com.b1nd.dodam.home + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.createSavedStateHandle +import androidx.lifecycle.viewModelScope +import com.b1nd.dodam.common.date.DodamDate +import com.b1nd.dodam.common.result.Result +import com.b1nd.dodam.data.meal.MealRepository +import com.b1nd.dodam.home.model.HomeUiState +import com.b1nd.dodam.home.model.MealUiState +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import kotlinx.datetime.LocalDate +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class HomeViewModel: ViewModel(), KoinComponent { + + private val mealRepository: MealRepository by inject() + + private val _state = MutableStateFlow(HomeUiState()) + val state = _state.asStateFlow() + + init { + viewModelScope.launch { + val date = DodamDate.localDateNow() + loadMeal(date) + } + } + + fun loadMeal(date: LocalDate) = viewModelScope.launch { + mealRepository.getMeal( + year = date.year, + month = date.monthNumber, + day = date.dayOfMonth + ).collect { + when (it) { + is Result.Success -> { + _state.update { state -> + state.copy( + mealUiState = MealUiState.Success(it.data) + ) + } + } + is Result.Loading -> { + _state.update { state -> + state.copy( + mealUiState = MealUiState.Loading + ) + } + } + is Result.Error -> { + it.error.printStackTrace() + _state.update { state -> + state.copy( + mealUiState = MealUiState.Error + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/di/ViewModelModule.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/di/ViewModelModule.kt new file mode 100644 index 00000000..900320d4 --- /dev/null +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/di/ViewModelModule.kt @@ -0,0 +1,10 @@ +package com.b1nd.dodam.home.di + +import com.b1nd.dodam.home.HomeViewModel +import org.koin.compose.viewmodel.dsl.viewModel +import org.koin.compose.viewmodel.dsl.viewModelOf +import org.koin.dsl.module + +val homeViewModelModule = module { + viewModel { HomeViewModel() } +} \ No newline at end of file diff --git a/network/core/src/commonMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.kt b/network/core/src/commonMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.kt index e69225c8..25327f4c 100644 --- a/network/core/src/commonMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.kt +++ b/network/core/src/commonMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.kt @@ -36,6 +36,7 @@ val log = logging() val networkCoreModule = module { single { val datastore: DataStoreRepository = get() + HttpClient(CIO) { install(ContentNegotiation) { json( From 517c62cbc08b4b783250a1a0b05f3622c7d21709 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Thu, 5 Sep 2024 15:45:21 +0900 Subject: [PATCH 12/24] feat: Add Shimmer --- .../kotlin/com/b1nd/dodam/home/HomeScreen.kt | 9 +- .../com/b1nd/dodam/home/card/MealCard.kt | 153 ++++++++++-------- .../b1nd/dodam/home/card/NightStudyCard.kt | 44 +++-- .../com/b1nd/dodam/home/card/OutCard.kt | 62 ++++--- 4 files changed, 157 insertions(+), 111 deletions(-) diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt index 94f92b44..0585730e 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt @@ -147,6 +147,7 @@ internal fun HomeScreen( MealCard( state = state.mealUiState, + showShimmer = state.showShimmer, onClickContent = {}, onClickRefresh = { viewModel.loadMeal(DodamDate.localDateNow()) @@ -155,11 +156,15 @@ internal fun HomeScreen( } item { - OutCard() + OutCard( + showShimmer = state.showShimmer, + ) } item { - NightStudyCard() + NightStudyCard( + showShimmer = state.showShimmer, + ) } item { diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt index 82c8f27f..de61df4e 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt @@ -39,88 +39,101 @@ import com.b1nd.dodam.ui.component.DodamContainer @Composable internal fun MealCard( state: MealUiState, + showShimmer: Boolean, onClickContent: () -> Unit, onClickRefresh: () -> Unit ) { - // 급식 리스트로 빠바박 3개옴 - DodamContainer( - icon = DodamIcons.ForkAndKnife, - title = "오늘의 급식" - ) { - when (state) { - is MealUiState.Success -> { - val mealState = rememberPagerState { 3 } - Column { - HorizontalPager( - state = mealState, - modifier = Modifier - .padding( - top = 6.dp, - start = 6.dp, - end = 6.dp, - ) - .animateContentSize() - .clickable( - onClick = onClickContent, - interactionSource = remember { MutableInteractionSource() }, - indication = rememberBounceIndication() - ) - ) { index -> - val meals = index.getMeal(state.data)?.details - if (meals != null) { - Column { - for (i in meals.indices step 2) { - Row( - modifier = Modifier.fillMaxWidth() - ) { - Text( - text = meals[i].name, - style = DodamTheme.typography.body1Medium(), - color = DodamTheme.colors.labelNormal - ) - Spacer(Modifier.weight(1f)) - Text( - text = meals.getOrNull(i)?.name ?: "", - style = DodamTheme.typography.body1Medium(), - color = DodamTheme.colors.labelNormal - ) - Spacer(Modifier.weight(1f)) + if (!showShimmer) { + DodamContainer( + icon = DodamIcons.ForkAndKnife, + title = "오늘의 급식" + ) { + when (state) { + is MealUiState.Success -> { + val mealState = rememberPagerState { 3 } + Column { + HorizontalPager( + state = mealState, + modifier = Modifier + .padding( + top = 6.dp, + start = 6.dp, + end = 6.dp, + ) + .animateContentSize() + .clickable( + onClick = onClickContent, + interactionSource = remember { MutableInteractionSource() }, + indication = rememberBounceIndication() + ) + ) { index -> + val meals = index.getMeal(state.data)?.details + if (meals != null) { + Column { + for (i in meals.indices step 2) { + Row( + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = meals[i].name, + style = DodamTheme.typography.body1Medium(), + color = DodamTheme.colors.labelNormal + ) + Spacer(Modifier.weight(1f)) + Text( + text = meals.getOrNull(i)?.name ?: "", + style = DodamTheme.typography.body1Medium(), + color = DodamTheme.colors.labelNormal + ) + Spacer(Modifier.weight(1f)) + } } } + } else { + DefaultText( + onClick = onClickContent, + label = "오늘은 급식이 없어요", + body = "내일 급식 보러가기", + ) } - } else { - DefaultText( - onClick = onClickContent, - label = "오늘은 급식이 없어요", - body = "내일 급식 보러가기", - ) } + DodamPageIndicator( + modifier = Modifier + .padding(end = 6.dp) + .align(Alignment.End), + pagerState = mealState + ) } - DodamPageIndicator( + } + + is MealUiState.Loading -> { + Box( modifier = Modifier - .padding(end = 6.dp) - .align(Alignment.End), - pagerState = mealState - ) + .fillMaxWidth() + .height(50.dp), + contentAlignment = Alignment.Center, + ) { + DodamLoadingDots() + } } - } - is MealUiState.Loading -> { - Box( - modifier = Modifier - .fillMaxWidth() - .height(50.dp), - contentAlignment = Alignment.Center, - ) { - DodamLoadingDots() + + is MealUiState.Error -> { + DefaultText( + onClick = onClickRefresh, + label = "급식을 불러올 수 없어요", + body = "다시 불러오기" + ) } } - is MealUiState.Error -> { - DefaultText( - onClick = onClickRefresh, - label = "급식을 불러올 수 없어요", - body = "다시 불러오기" - ) - } + } + } else { + Box( + modifier = Modifier + .fillMaxWidth() + .height(50.dp), + contentAlignment = Alignment.Center, + ) { + DodamLoadingDots() } } } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt index a0dc704c..4a083fcc 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt @@ -1,5 +1,6 @@ package com.b1nd.dodam.home.card +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -9,35 +10,48 @@ import androidx.compose.foundation.layout.padding 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.unit.dp import com.b1nd.dodam.data.nightstudy.model.NightStudy import com.b1nd.dodam.designsystem.DodamTheme +import com.b1nd.dodam.designsystem.component.DodamLoadingDots import com.b1nd.dodam.designsystem.foundation.DodamIcons import com.b1nd.dodam.home.InnerCountCard import com.b1nd.dodam.ui.component.DodamContainer @Composable internal fun NightStudyCard( - + showShimmer: Boolean, ) { - DodamContainer( - icon = DodamIcons.MoonPlus, - title = "심야 자습 현황" - ) { - Column( + if (!showShimmer) { + DodamContainer( + icon = DodamIcons.MoonPlus, + title = "심야 자습 현황" + ) { + Column( + modifier = Modifier + .padding(6.dp) + .fillMaxWidth() + ) { + InnerCountCard( + title = "현재 자습중인 학생", + content = "13명", + buttonText = "12명 대기중", + onClick = { + + } + ) + } + } + } else { + Box( modifier = Modifier - .padding(6.dp) .fillMaxWidth() + .height(50.dp), + contentAlignment = Alignment.Center, ) { - InnerCountCard( - title = "현재 자습중인 학생", - content = "13명", - buttonText = "12명 대기중", - onClick = { - - } - ) + DodamLoadingDots() } } } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt index 2ab22766..fbcaf112 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt @@ -1,5 +1,6 @@ package com.b1nd.dodam.home.card +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -9,43 +10,56 @@ import androidx.compose.foundation.layout.padding 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.unit.dp import com.b1nd.dodam.designsystem.DodamTheme +import com.b1nd.dodam.designsystem.component.DodamLoadingDots import com.b1nd.dodam.designsystem.foundation.DodamIcons import com.b1nd.dodam.home.InnerCountCard import com.b1nd.dodam.ui.component.DodamContainer @Composable internal fun OutCard( - + showShimmer: Boolean, ) { - DodamContainer( - icon = DodamIcons.DoorOpen, - title = "외출/외박 현황" - ) { - Column( - modifier = Modifier - .padding(6.dp) - .fillMaxWidth() + if (!showShimmer) { + DodamContainer( + icon = DodamIcons.DoorOpen, + title = "외출/외박 현황" ) { - InnerCountCard( - title = "현재 외출중인 학생", - content = "13명", - buttonText = "12명 대기중", - onClick = { + Column( + modifier = Modifier + .padding(6.dp) + .fillMaxWidth() + ) { + InnerCountCard( + title = "현재 외출중인 학생", + content = "13명", + buttonText = "12명 대기중", + onClick = { - } - ) - Spacer(modifier = Modifier.height(16.dp)) - InnerCountCard( - title = "현재 외박중인 학생", - content = "10명", - buttonText = "11명 대기중", - onClick = { + } + ) + Spacer(modifier = Modifier.height(16.dp)) + InnerCountCard( + title = "현재 외박중인 학생", + content = "10명", + buttonText = "11명 대기중", + onClick = { - } - ) + } + ) + } + } + } else { + Box( + modifier = Modifier + .fillMaxWidth() + .height(50.dp), + contentAlignment = Alignment.Center, + ) { + DodamLoadingDots() } } } From 0656a72db8cbc5b16b8588e9e59af838c8354e8d Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Thu, 5 Sep 2024 15:58:30 +0900 Subject: [PATCH 13/24] feat: Create Home Banner Remote --- .../kotlin/com/b1nd/dodam/home/HomeScreen.kt | 13 +------- .../com/b1nd/dodam/home/HomeViewModel.kt | 33 +++++++++++++++++++ .../com/b1nd/dodam/home/card/MealCard.kt | 28 ++++++++-------- .../b1nd/dodam/home/card/NightStudyCard.kt | 28 ++++++++-------- .../com/b1nd/dodam/home/card/OutCard.kt | 28 ++++++++-------- 5 files changed, 76 insertions(+), 54 deletions(-) diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt index 0585730e..7edf8e14 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt @@ -129,18 +129,7 @@ internal fun HomeScreen( ) { item { BannerCard( - state = BannerUiState.Success( - persistentListOf( - Banner( - id = 0, - imageUrl = "https://dodam.kr.object.ncloudstorage.com/dodam/6634113f-951b-430c-81c9-957de0e8abddalimo.png", - redirectUrl = "https://dodam.b1nd.com", - title = "test", - status = BannerStatus.ACTIVE, - expireAt = kotlinx.datetime.LocalDateTime.parse("2024-01-26T13:48:25.623088") - ) - ) - ) + state = state.bannerUiState ) } item { diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt index af3d6c91..1be882d5 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt @@ -6,6 +6,8 @@ import androidx.lifecycle.viewModelScope import com.b1nd.dodam.common.date.DodamDate import com.b1nd.dodam.common.result.Result import com.b1nd.dodam.data.meal.MealRepository +import com.b1nd.dodam.data.nightstudy.NightStudyRepository +import com.b1nd.dodam.home.model.BannerUiState import com.b1nd.dodam.home.model.HomeUiState import com.b1nd.dodam.home.model.MealUiState import kotlinx.coroutines.flow.MutableStateFlow @@ -23,6 +25,7 @@ import org.koin.core.component.inject class HomeViewModel: ViewModel(), KoinComponent { + private val bannerRepository: BannerRepository by inject() private val mealRepository: MealRepository by inject() private val _state = MutableStateFlow(HomeUiState()) @@ -30,11 +33,36 @@ class HomeViewModel: ViewModel(), KoinComponent { init { viewModelScope.launch { + loadBanner() val date = DodamDate.localDateNow() loadMeal(date) } } + private fun loadBanner() = viewModelScope.launch { + bannerRepository.getActiveBanner().collect { + when (it) { + is Result.Success -> { + _state.update { state -> + state.copy( + showShimmer = false, + bannerUiState = BannerUiState.Success(it.data) + ) + } + } + is Result.Loading -> {} + is Result.Error -> { + _state.update { state -> + state.copy( + bannerUiState = BannerUiState.None + ) + } + } + + } + } + } + fun loadMeal(date: LocalDate) = viewModelScope.launch { mealRepository.getMeal( year = date.year, @@ -45,6 +73,7 @@ class HomeViewModel: ViewModel(), KoinComponent { is Result.Success -> { _state.update { state -> state.copy( + showShimmer = false, mealUiState = MealUiState.Success(it.data) ) } @@ -67,4 +96,8 @@ class HomeViewModel: ViewModel(), KoinComponent { } } } + fun loadNightStudy() = viewModelScope.launch { + val date = DodamDate.localDateNow() + nightStudyRepository + } } \ No newline at end of file diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt index de61df4e..027af183 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt @@ -43,11 +43,11 @@ internal fun MealCard( onClickContent: () -> Unit, onClickRefresh: () -> Unit ) { - if (!showShimmer) { - DodamContainer( - icon = DodamIcons.ForkAndKnife, - title = "오늘의 급식" - ) { + DodamContainer( + icon = DodamIcons.ForkAndKnife, + title = "오늘의 급식" + ) { + if (!showShimmer) { when (state) { is MealUiState.Success -> { val mealState = rememberPagerState { 3 } @@ -125,15 +125,15 @@ internal fun MealCard( ) } } - } - } else { - Box( - modifier = Modifier - .fillMaxWidth() - .height(50.dp), - contentAlignment = Alignment.Center, - ) { - DodamLoadingDots() + } else { + Box( + modifier = Modifier + .fillMaxWidth() + .height(50.dp), + contentAlignment = Alignment.Center, + ) { + DodamLoadingDots() + } } } } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt index 4a083fcc..7abbef11 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt @@ -24,11 +24,11 @@ import com.b1nd.dodam.ui.component.DodamContainer internal fun NightStudyCard( showShimmer: Boolean, ) { - if (!showShimmer) { - DodamContainer( - icon = DodamIcons.MoonPlus, - title = "심야 자습 현황" - ) { + DodamContainer( + icon = DodamIcons.MoonPlus, + title = "심야 자습 현황" + ) { + if (!showShimmer) { Column( modifier = Modifier .padding(6.dp) @@ -43,15 +43,15 @@ internal fun NightStudyCard( } ) } - } - } else { - Box( - modifier = Modifier - .fillMaxWidth() - .height(50.dp), - contentAlignment = Alignment.Center, - ) { - DodamLoadingDots() + } else { + Box( + modifier = Modifier + .fillMaxWidth() + .height(50.dp), + contentAlignment = Alignment.Center, + ) { + DodamLoadingDots() + } } } } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt index fbcaf112..95d7ef3a 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt @@ -23,11 +23,11 @@ import com.b1nd.dodam.ui.component.DodamContainer internal fun OutCard( showShimmer: Boolean, ) { - if (!showShimmer) { - DodamContainer( - icon = DodamIcons.DoorOpen, - title = "외출/외박 현황" - ) { + DodamContainer( + icon = DodamIcons.DoorOpen, + title = "외출/외박 현황" + ) { + if (!showShimmer) { Column( modifier = Modifier .padding(6.dp) @@ -51,15 +51,15 @@ internal fun OutCard( } ) } - } - } else { - Box( - modifier = Modifier - .fillMaxWidth() - .height(50.dp), - contentAlignment = Alignment.Center, - ) { - DodamLoadingDots() + } else { + Box( + modifier = Modifier + .fillMaxWidth() + .height(50.dp), + contentAlignment = Alignment.Center, + ) { + DodamLoadingDots() + } } } } From 9ea49d01f3b5386f013747f18e6a2e1ab974b6f7 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Thu, 5 Sep 2024 16:50:01 +0900 Subject: [PATCH 14/24] feat: Create Night Study Remote --- .../data/nightstudy/NightStudyRepository.kt | 4 ++ .../repository/NightStudyRepositoryImpl.kt | 12 +++++ .../kotlin/com/b1nd/dodam/home/HomeScreen.kt | 5 ++ .../com/b1nd/dodam/home/HomeViewModel.kt | 48 ++++++++++++++++++- .../b1nd/dodam/home/card/NightStudyCard.kt | 48 ++++++++++++++----- .../com/b1nd/dodam/home/model/HomeUiState.kt | 5 +- .../com/b1nd/dodam/network/core/DodamUrl.kt | 1 + .../nightstudy/api/NightStudyService.kt | 14 ++++++ .../datasource/NightStudyDataSource.kt | 4 ++ 9 files changed, 126 insertions(+), 15 deletions(-) diff --git a/data/night-study/src/commonMain/kotlin/com/b1nd/dodam/data/nightstudy/NightStudyRepository.kt b/data/night-study/src/commonMain/kotlin/com/b1nd/dodam/data/nightstudy/NightStudyRepository.kt index 41e2fe47..153b0762 100644 --- a/data/night-study/src/commonMain/kotlin/com/b1nd/dodam/data/nightstudy/NightStudyRepository.kt +++ b/data/night-study/src/commonMain/kotlin/com/b1nd/dodam/data/nightstudy/NightStudyRepository.kt @@ -13,4 +13,8 @@ interface NightStudyRepository { fun askNightStudy(place: Place, content: String, doNeedPhone: Boolean, reasonForPhone: String?, startAt: LocalDate, endAt: LocalDate): Flow> fun deleteNightStudy(id: Long): Flow> + + fun getNightStudy(): Flow>> + + fun getNightStudyPending(): Flow>> } diff --git a/data/night-study/src/commonMain/kotlin/com/b1nd/dodam/data/nightstudy/repository/NightStudyRepositoryImpl.kt b/data/night-study/src/commonMain/kotlin/com/b1nd/dodam/data/nightstudy/repository/NightStudyRepositoryImpl.kt index f1ab7a3e..bb209213 100644 --- a/data/night-study/src/commonMain/kotlin/com/b1nd/dodam/data/nightstudy/repository/NightStudyRepositoryImpl.kt +++ b/data/night-study/src/commonMain/kotlin/com/b1nd/dodam/data/nightstudy/repository/NightStudyRepositoryImpl.kt @@ -55,4 +55,16 @@ internal class NightStudyRepositoryImpl( emit(remote.deleteNightStudy(id)) }.asResult().flowOn(dispatcher) } + + override fun getNightStudy(): Flow>> { + return flow { + emit(remote.getNightStudy().map { it.toModel() }.toImmutableList()) + }.asResult().flowOn(dispatcher) + } + + override fun getNightStudyPending(): Flow>> { + return flow { + emit(remote.getNightStudyPending().map { it.toModel() }.toImmutableList()) + }.asResult().flowOn(dispatcher) + } } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt index 7edf8e14..a94c4401 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt @@ -153,6 +153,11 @@ internal fun HomeScreen( item { NightStudyCard( showShimmer = state.showShimmer, + uiState = state.nightStudyUiState, + onContentClick = {}, + onRefreshClick = { + viewModel.loadNightStudy() + } ) } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt index 1be882d5..372937e4 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt @@ -5,15 +5,18 @@ import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewModelScope import com.b1nd.dodam.common.date.DodamDate import com.b1nd.dodam.common.result.Result +import com.b1nd.dodam.data.banner.BannerRepository import com.b1nd.dodam.data.meal.MealRepository import com.b1nd.dodam.data.nightstudy.NightStudyRepository import com.b1nd.dodam.home.model.BannerUiState import com.b1nd.dodam.home.model.HomeUiState import com.b1nd.dodam.home.model.MealUiState +import com.b1nd.dodam.home.model.NightStudyUiState import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.stateIn @@ -27,6 +30,7 @@ class HomeViewModel: ViewModel(), KoinComponent { private val bannerRepository: BannerRepository by inject() private val mealRepository: MealRepository by inject() + private val nightStudyRepository: NightStudyRepository by inject() private val _state = MutableStateFlow(HomeUiState()) val state = _state.asStateFlow() @@ -36,6 +40,7 @@ class HomeViewModel: ViewModel(), KoinComponent { loadBanner() val date = DodamDate.localDateNow() loadMeal(date) + loadNightStudy() } } @@ -96,8 +101,47 @@ class HomeViewModel: ViewModel(), KoinComponent { } } } + fun loadNightStudy() = viewModelScope.launch { - val date = DodamDate.localDateNow() - nightStudyRepository + val nightStudyFlow = nightStudyRepository.getNightStudy() + val nightStudyPendingFlow = nightStudyRepository.getNightStudyPending() + + nightStudyFlow.combine(nightStudyPendingFlow){ nightStudyFlow, nightStudyPendingFlow -> + var activeCount = 0 + var pendingCount = 0 + + when (nightStudyFlow) { + + is Result.Success -> { + activeCount = nightStudyFlow.data.size + } + is Result.Loading -> {} + is Result.Error -> { + return@combine NightStudyUiState.Error + } + } + + when (nightStudyPendingFlow) { + is Result.Success -> { + pendingCount = nightStudyPendingFlow.data.size + } + is Result.Loading -> {} + is Result.Error -> { + return@combine NightStudyUiState.Error + } + } + + return@combine NightStudyUiState.Success( + active = activeCount, + pending = pendingCount + ) + }.collect { + _state.update { state -> + state.copy( + showShimmer = false, + nightStudyUiState = it + ) + } + } } } \ No newline at end of file diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt index 7abbef11..5e1d48f2 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt @@ -17,31 +17,55 @@ import com.b1nd.dodam.data.nightstudy.model.NightStudy import com.b1nd.dodam.designsystem.DodamTheme import com.b1nd.dodam.designsystem.component.DodamLoadingDots import com.b1nd.dodam.designsystem.foundation.DodamIcons +import com.b1nd.dodam.home.DefaultText import com.b1nd.dodam.home.InnerCountCard +import com.b1nd.dodam.home.model.NightStudyUiState import com.b1nd.dodam.ui.component.DodamContainer @Composable internal fun NightStudyCard( + uiState: NightStudyUiState, showShimmer: Boolean, + onRefreshClick: () -> Unit, + onContentClick: () -> Unit ) { DodamContainer( icon = DodamIcons.MoonPlus, title = "심야 자습 현황" ) { if (!showShimmer) { - Column( - modifier = Modifier - .padding(6.dp) - .fillMaxWidth() - ) { - InnerCountCard( - title = "현재 자습중인 학생", - content = "13명", - buttonText = "12명 대기중", - onClick = { - + when (uiState) { + is NightStudyUiState.Success -> { + Column( + modifier = Modifier + .padding(6.dp) + .fillMaxWidth() + ) { + InnerCountCard( + title = "현재 자습중인 학생", + content = "${uiState.active}명", + buttonText = "${uiState.pending}명 대기중", + onClick = onContentClick + ) + } + } + NightStudyUiState.Error -> { + DefaultText( + onClick = onRefreshClick, + label = "심자 현황을 불러올 수 없어요", + body = "다시 불러오기" + ) + } + NightStudyUiState.Loading -> { + Box( + modifier = Modifier + .fillMaxWidth() + .height(50.dp), + contentAlignment = Alignment.Center, + ) { + DodamLoadingDots() } - ) + } } } else { Box( diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt index 3c69148b..ba766a55 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt @@ -35,7 +35,10 @@ sealed interface OutUiState { } sealed interface NightStudyUiState { - data class Success(val data: NightStudy?) : NightStudyUiState + data class Success( + val active: Int, + val pending: Int + ) : NightStudyUiState data object Loading : NightStudyUiState data object Error : NightStudyUiState } diff --git a/network/core/src/commonMain/kotlin/com/b1nd/dodam/network/core/DodamUrl.kt b/network/core/src/commonMain/kotlin/com/b1nd/dodam/network/core/DodamUrl.kt index 73578a32..7a658544 100644 --- a/network/core/src/commonMain/kotlin/com/b1nd/dodam/network/core/DodamUrl.kt +++ b/network/core/src/commonMain/kotlin/com/b1nd/dodam/network/core/DodamUrl.kt @@ -53,6 +53,7 @@ object DodamUrl { object NightStudy { const val MY = "$NIGHT_STUDY/my" + const val PENDING = "$NIGHT_STUDY/pending" } object Schedule { diff --git a/network/night-study/src/commonMain/kotlin/com/b1nd/dodam/network/nightstudy/api/NightStudyService.kt b/network/night-study/src/commonMain/kotlin/com/b1nd/dodam/network/nightstudy/api/NightStudyService.kt index 7c23e329..492693b7 100644 --- a/network/night-study/src/commonMain/kotlin/com/b1nd/dodam/network/nightstudy/api/NightStudyService.kt +++ b/network/night-study/src/commonMain/kotlin/com/b1nd/dodam/network/nightstudy/api/NightStudyService.kt @@ -54,4 +54,18 @@ internal class NightStudyService( .body() } } + + override suspend fun getNightStudy(): ImmutableList { + return safeRequest { + network.get(DodamUrl.NIGHT_STUDY) + .body>>() + }.toImmutableList() + } + + override suspend fun getNightStudyPending(): ImmutableList { + return safeRequest { + network.get(DodamUrl.NightStudy.PENDING) + .body>>() + }.toImmutableList() + } } diff --git a/network/night-study/src/commonMain/kotlin/com/b1nd/dodam/network/nightstudy/datasource/NightStudyDataSource.kt b/network/night-study/src/commonMain/kotlin/com/b1nd/dodam/network/nightstudy/datasource/NightStudyDataSource.kt index c56e1be9..c666ee87 100644 --- a/network/night-study/src/commonMain/kotlin/com/b1nd/dodam/network/nightstudy/datasource/NightStudyDataSource.kt +++ b/network/night-study/src/commonMain/kotlin/com/b1nd/dodam/network/nightstudy/datasource/NightStudyDataSource.kt @@ -10,4 +10,8 @@ interface NightStudyDataSource { suspend fun askNightStudy(place: String, content: String, doNeedPhone: Boolean, reasonForPhone: String?, startAt: LocalDate, endAt: LocalDate) suspend fun deleteNightStudy(id: Long) + + suspend fun getNightStudy(): ImmutableList + + suspend fun getNightStudyPending(): ImmutableList } From 1ee40bbee0dfc827dc9ecb7d685b7902c9105ae5 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Fri, 6 Sep 2024 16:06:22 +0900 Subject: [PATCH 15/24] feat: Create Home Outing Remote --- .../dodam/data/outing/OutingRepository.kt | 4 ++ .../outing/repository/OutingRepositoryImpl.kt | 12 ++++ .../kotlin/com/b1nd/dodam/home/HomeScreen.kt | 6 ++ .../com/b1nd/dodam/home/HomeViewModel.kt | 54 ++++++++++++++++ .../com/b1nd/dodam/home/card/OutCard.kt | 63 +++++++++++++------ .../com/b1nd/dodam/home/model/HomeUiState.kt | 7 ++- .../com/b1nd/dodam/network/core/DodamUrl.kt | 1 + .../dodam/network/outing/api/OutingService.kt | 17 +++++ .../outing/datasource/OutingDataSource.kt | 5 ++ 9 files changed, 148 insertions(+), 21 deletions(-) diff --git a/data/outing/src/commonMain/kotlin/com/b1nd/dodam/data/outing/OutingRepository.kt b/data/outing/src/commonMain/kotlin/com/b1nd/dodam/data/outing/OutingRepository.kt index 97a72b9d..b51ecfcb 100644 --- a/data/outing/src/commonMain/kotlin/com/b1nd/dodam/data/outing/OutingRepository.kt +++ b/data/outing/src/commonMain/kotlin/com/b1nd/dodam/data/outing/OutingRepository.kt @@ -10,8 +10,12 @@ import kotlinx.datetime.LocalDateTime interface OutingRepository { fun getMyOut(): Flow>> + fun getOutings(date: LocalDate): Flow>> + fun askOuting(reason: String, startAt: LocalDateTime, endAt: LocalDateTime): Flow> + fun getSleepovers(date: LocalDate): Flow>> + fun askSleepover(reason: String, startAt: LocalDate, endAt: LocalDate): Flow> fun deleteOuting(id: Long): Flow> diff --git a/data/outing/src/commonMain/kotlin/com/b1nd/dodam/data/outing/repository/OutingRepositoryImpl.kt b/data/outing/src/commonMain/kotlin/com/b1nd/dodam/data/outing/repository/OutingRepositoryImpl.kt index 9ece6674..459a7bbc 100644 --- a/data/outing/src/commonMain/kotlin/com/b1nd/dodam/data/outing/repository/OutingRepositoryImpl.kt +++ b/data/outing/src/commonMain/kotlin/com/b1nd/dodam/data/outing/repository/OutingRepositoryImpl.kt @@ -35,12 +35,24 @@ internal class OutingRepositoryImpl( }.asResult().flowOn(dispatcher) } + override fun getOutings(date: LocalDate): Flow>> { + return flow { + emit(network.getOutings(date).map { it.toModel() }.toImmutableList()) + }.asResult().flowOn(dispatcher) + } + override fun askOuting(reason: String, startAt: LocalDateTime, endAt: LocalDateTime): Flow> { return flow { emit(network.askOuting(reason, startAt, endAt)) }.asResult().flowOn(dispatcher) } + override fun getSleepovers(date: LocalDate): Flow>> { + return flow { + emit(network.getSleepovers(date).map { it.toModel() }.toImmutableList()) + }.asResult().flowOn(dispatcher) + } + override fun askSleepover(reason: String, startAt: LocalDate, endAt: LocalDate): Flow> { return flow { emit(network.askSleepover(reason, startAt, endAt)) diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt index a94c4401..6baeff7f 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt @@ -147,6 +147,12 @@ internal fun HomeScreen( item { OutCard( showShimmer = state.showShimmer, + uiState = state.outUiState, + onRefreshClick = { + viewModel.loadOuting() + }, + onOutingClick = {}, + onSleepoverClick = {} ) } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt index 372937e4..fd2ee81f 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt @@ -6,12 +6,15 @@ import androidx.lifecycle.viewModelScope import com.b1nd.dodam.common.date.DodamDate import com.b1nd.dodam.common.result.Result import com.b1nd.dodam.data.banner.BannerRepository +import com.b1nd.dodam.data.core.model.Status import com.b1nd.dodam.data.meal.MealRepository import com.b1nd.dodam.data.nightstudy.NightStudyRepository +import com.b1nd.dodam.data.outing.OutingRepository import com.b1nd.dodam.home.model.BannerUiState import com.b1nd.dodam.home.model.HomeUiState import com.b1nd.dodam.home.model.MealUiState import com.b1nd.dodam.home.model.NightStudyUiState +import com.b1nd.dodam.home.model.OutUiState import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -30,6 +33,7 @@ class HomeViewModel: ViewModel(), KoinComponent { private val bannerRepository: BannerRepository by inject() private val mealRepository: MealRepository by inject() + private val outingRepository: OutingRepository by inject() private val nightStudyRepository: NightStudyRepository by inject() private val _state = MutableStateFlow(HomeUiState()) @@ -40,6 +44,7 @@ class HomeViewModel: ViewModel(), KoinComponent { loadBanner() val date = DodamDate.localDateNow() loadMeal(date) + loadOuting() loadNightStudy() } } @@ -102,6 +107,55 @@ class HomeViewModel: ViewModel(), KoinComponent { } } + fun loadOuting() = viewModelScope.launch { + val date = DodamDate.localDateNow() + combine( + outingRepository.getOutings(date), + outingRepository.getSleepovers(date) + ) { outing, sleepover -> + var outAllowCount = 0 + var outPendingCount = 0 + var sleepoverAllowCount = 0 + var sleepoverPendingCount = 0 + + when (outing) { + is Result.Success -> { + outAllowCount = outing.data.filter { it.status == Status.ALLOWED }.size + outPendingCount = outing.data.filter { it.status == Status.PENDING }.size + } + is Result.Loading -> {} + is Result.Error -> { + return@combine OutUiState.Error + } + } + + when (sleepover) { + is Result.Success -> { + sleepoverAllowCount = sleepover.data.filter { it.status == Status.ALLOWED }.size + sleepoverPendingCount = sleepover.data.filter { it.status == Status.PENDING }.size + } + is Result.Loading -> {} + is Result.Error -> { + return@combine OutUiState.Error + } + } + + return@combine OutUiState.Success( + outAllowCount = outAllowCount, + outPendingCount = outPendingCount, + sleepoverAllowCount = sleepoverAllowCount, + sleepoverPendingCount = sleepoverPendingCount + ) + }.collect { + _state.update { state -> + state.copy( + showShimmer = false, + outUiState = it + ) + } + } + } + fun loadNightStudy() = viewModelScope.launch { val nightStudyFlow = nightStudyRepository.getNightStudy() val nightStudyPendingFlow = nightStudyRepository.getNightStudyPending() diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt index 95d7ef3a..d4f7c5bc 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt @@ -16,40 +16,63 @@ import androidx.compose.ui.unit.dp import com.b1nd.dodam.designsystem.DodamTheme import com.b1nd.dodam.designsystem.component.DodamLoadingDots import com.b1nd.dodam.designsystem.foundation.DodamIcons +import com.b1nd.dodam.home.DefaultText import com.b1nd.dodam.home.InnerCountCard +import com.b1nd.dodam.home.model.OutUiState import com.b1nd.dodam.ui.component.DodamContainer @Composable internal fun OutCard( showShimmer: Boolean, + uiState: OutUiState, + onRefreshClick: () -> Unit, + onOutingClick: () -> Unit, + onSleepoverClick: () -> Unit ) { DodamContainer( icon = DodamIcons.DoorOpen, title = "외출/외박 현황" ) { if (!showShimmer) { - Column( - modifier = Modifier - .padding(6.dp) - .fillMaxWidth() - ) { - InnerCountCard( - title = "현재 외출중인 학생", - content = "13명", - buttonText = "12명 대기중", - onClick = { - + when (uiState) { + is OutUiState.Success -> { + Column( + modifier = Modifier + .padding(6.dp) + .fillMaxWidth() + ) { + InnerCountCard( + title = "현재 외출중인 학생", + content = "${uiState.outAllowCount}명", + buttonText = "${uiState.outPendingCount}명 대기중", + onClick = onOutingClick + ) + Spacer(modifier = Modifier.height(16.dp)) + InnerCountCard( + title = "현재 외박중인 학생", + content = "${uiState.sleepoverAllowCount}명", + buttonText = "${uiState.sleepoverPendingCount}명 대기중", + onClick = onSleepoverClick + ) } - ) - Spacer(modifier = Modifier.height(16.dp)) - InnerCountCard( - title = "현재 외박중인 학생", - content = "10명", - buttonText = "11명 대기중", - onClick = { - + } + is OutUiState.Loading -> { + Box( + modifier = Modifier + .fillMaxWidth() + .height(50.dp), + contentAlignment = Alignment.Center, + ) { + DodamLoadingDots() } - ) + } + is OutUiState.Error -> { + DefaultText( + onClick = onRefreshClick, + label = "외출/외박 현황을 불러올 수 없어요", + body = "다시 불러오기", + ) + } } } else { Box( diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt index ba766a55..d0f04f77 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt @@ -29,7 +29,12 @@ sealed interface MealUiState { } sealed interface OutUiState { - data class Success(val data: Outing?) : OutUiState + data class Success( + val outAllowCount: Int, + val outPendingCount: Int, + val sleepoverAllowCount: Int, + val sleepoverPendingCount: Int, + ) : OutUiState data object Loading : OutUiState data object Error : OutUiState } diff --git a/network/core/src/commonMain/kotlin/com/b1nd/dodam/network/core/DodamUrl.kt b/network/core/src/commonMain/kotlin/com/b1nd/dodam/network/core/DodamUrl.kt index 7a658544..294ae41f 100644 --- a/network/core/src/commonMain/kotlin/com/b1nd/dodam/network/core/DodamUrl.kt +++ b/network/core/src/commonMain/kotlin/com/b1nd/dodam/network/core/DodamUrl.kt @@ -45,6 +45,7 @@ object DodamUrl { object Sleepover { const val MY = "$SLEEPOVER/my" + const val VALID = "$SLEEPOVER/valid" } object Outing { diff --git a/network/outing/src/commonMain/kotlin/com/b1nd/dodam/network/outing/api/OutingService.kt b/network/outing/src/commonMain/kotlin/com/b1nd/dodam/network/outing/api/OutingService.kt index 6d238cad..d6b52a56 100644 --- a/network/outing/src/commonMain/kotlin/com/b1nd/dodam/network/outing/api/OutingService.kt +++ b/network/outing/src/commonMain/kotlin/com/b1nd/dodam/network/outing/api/OutingService.kt @@ -14,6 +14,7 @@ import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.request.delete import io.ktor.client.request.get +import io.ktor.client.request.parameter import io.ktor.client.request.post import io.ktor.client.request.setBody import io.ktor.http.ContentType @@ -40,6 +41,22 @@ internal class OutingService( }.toImmutableList() } + override suspend fun getSleepovers(date: LocalDate): ImmutableList { + return safeRequest { + client.get(DodamUrl.Sleepover.VALID) { + parameter("date", date) + }.body>>() + }.toImmutableList() + } + + override suspend fun getOutings(date: LocalDate): ImmutableList { + return safeRequest { + client.get(DodamUrl.OUTING) { + parameter("date", date) + }.body>>() + }.toImmutableList() + } + override suspend fun askOuting(reason: String, startAt: LocalDateTime, endAt: LocalDateTime) { return defaultSafeRequest { client.post(DodamUrl.OUTING) { diff --git a/network/outing/src/commonMain/kotlin/com/b1nd/dodam/network/outing/datasource/OutingDataSource.kt b/network/outing/src/commonMain/kotlin/com/b1nd/dodam/network/outing/datasource/OutingDataSource.kt index 68f459c1..ff515368 100644 --- a/network/outing/src/commonMain/kotlin/com/b1nd/dodam/network/outing/datasource/OutingDataSource.kt +++ b/network/outing/src/commonMain/kotlin/com/b1nd/dodam/network/outing/datasource/OutingDataSource.kt @@ -8,8 +8,13 @@ import kotlinx.datetime.LocalDateTime interface OutingDataSource { suspend fun getMySleepover(): ImmutableList + suspend fun getMyOuting(): ImmutableList + suspend fun getSleepovers(date: LocalDate): ImmutableList + + suspend fun getOutings(date: LocalDate): ImmutableList + suspend fun askOuting(reason: String, startAt: LocalDateTime, endAt: LocalDateTime) suspend fun askSleepover(reason: String, startAt: LocalDate, endAt: LocalDate) From 5267306584156d6d6be849cabb64005550554254 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Fri, 6 Sep 2024 16:10:02 +0900 Subject: [PATCH 16/24] refactor: Improved Home Code Readability --- .../kotlin/com/b1nd/dodam/home/HomeScreen.kt | 12 +++--------- .../com/b1nd/dodam/home/HomeViewModel.kt | 19 ++++++++++++------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt index 6baeff7f..80ea42c5 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt @@ -138,9 +138,7 @@ internal fun HomeScreen( state = state.mealUiState, showShimmer = state.showShimmer, onClickContent = {}, - onClickRefresh = { - viewModel.loadMeal(DodamDate.localDateNow()) - } + onClickRefresh = viewModel::loadMeal ) } @@ -148,9 +146,7 @@ internal fun HomeScreen( OutCard( showShimmer = state.showShimmer, uiState = state.outUiState, - onRefreshClick = { - viewModel.loadOuting() - }, + onRefreshClick = viewModel::loadOuting, onOutingClick = {}, onSleepoverClick = {} ) @@ -161,9 +157,7 @@ internal fun HomeScreen( showShimmer = state.showShimmer, uiState = state.nightStudyUiState, onContentClick = {}, - onRefreshClick = { - viewModel.loadNightStudy() - } + onRefreshClick = viewModel::loadNightStudy ) } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt index fd2ee81f..4017c05c 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt @@ -42,8 +42,7 @@ class HomeViewModel: ViewModel(), KoinComponent { init { viewModelScope.launch { loadBanner() - val date = DodamDate.localDateNow() - loadMeal(date) + loadMeal() loadOuting() loadNightStudy() } @@ -62,6 +61,7 @@ class HomeViewModel: ViewModel(), KoinComponent { } is Result.Loading -> {} is Result.Error -> { + it.error.printStackTrace() _state.update { state -> state.copy( bannerUiState = BannerUiState.None @@ -73,7 +73,8 @@ class HomeViewModel: ViewModel(), KoinComponent { } } - fun loadMeal(date: LocalDate) = viewModelScope.launch { + fun loadMeal() = viewModelScope.launch { + val date = DodamDate.localDateNow() mealRepository.getMeal( year = date.year, month = date.monthNumber, @@ -125,6 +126,7 @@ class HomeViewModel: ViewModel(), KoinComponent { } is Result.Loading -> {} is Result.Error -> { + outing.error.printStackTrace() return@combine OutUiState.Error } } @@ -136,6 +138,7 @@ class HomeViewModel: ViewModel(), KoinComponent { } is Result.Loading -> {} is Result.Error -> { + sleepover.error.printStackTrace() return@combine OutUiState.Error } } @@ -157,10 +160,10 @@ class HomeViewModel: ViewModel(), KoinComponent { } fun loadNightStudy() = viewModelScope.launch { - val nightStudyFlow = nightStudyRepository.getNightStudy() - val nightStudyPendingFlow = nightStudyRepository.getNightStudyPending() - - nightStudyFlow.combine(nightStudyPendingFlow){ nightStudyFlow, nightStudyPendingFlow -> + combine( + nightStudyRepository.getNightStudy(), + nightStudyRepository.getNightStudyPending() + ){ nightStudyFlow, nightStudyPendingFlow -> var activeCount = 0 var pendingCount = 0 @@ -171,6 +174,7 @@ class HomeViewModel: ViewModel(), KoinComponent { } is Result.Loading -> {} is Result.Error -> { + nightStudyFlow.error.printStackTrace() return@combine NightStudyUiState.Error } } @@ -181,6 +185,7 @@ class HomeViewModel: ViewModel(), KoinComponent { } is Result.Loading -> {} is Result.Error -> { + nightStudyPendingFlow.error.printStackTrace() return@combine NightStudyUiState.Error } } From b6a1eaed5fca604598ea84f4e9896b6602499748 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Fri, 6 Sep 2024 16:50:19 +0900 Subject: [PATCH 17/24] feat: Create Home Schedule Remote --- .../dodam/common/utiles/Extension.android.kt | 2 +- .../kotlin/com/b1nd/dodam/home/HomeScreen.kt | 33 ++--------------- .../com/b1nd/dodam/home/HomeViewModel.kt | 36 +++++++++++++++++++ .../network/schedule/api/ScheduleService.kt | 4 +-- 4 files changed, 42 insertions(+), 33 deletions(-) diff --git a/common/src/main/java/com/b1nd/dodam/common/utiles/Extension.android.kt b/common/src/main/java/com/b1nd/dodam/common/utiles/Extension.android.kt index d290037f..186fa11e 100644 --- a/common/src/main/java/com/b1nd/dodam/common/utiles/Extension.android.kt +++ b/common/src/main/java/com/b1nd/dodam/common/utiles/Extension.android.kt @@ -1,3 +1,3 @@ package com.b1nd.dodam.common.utiles -actual fun String.Companion.javaFormat(format: String, vararg args: Any?): String = String.format(format, args) +actual fun String.Companion.javaFormat(format: String, vararg args: Any?): String = String.format(format, *args) diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt index 80ea42c5..f2c806e1 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt @@ -163,36 +163,9 @@ internal fun HomeScreen( item { ScheduleCard( - uiState = ScheduleUiState.Success( - persistentListOf( - Schedule( - id = 0, - name = "타운홀미팅", - place = "방송실", - type = ScheduleType.ACADEMIC, - date = persistentListOf(DodamDate.localDateNow()), - targetGrades = persistentListOf(Grade.GRADE_ALL) - ), - Schedule( - id = 1, - name = "교내 해커톤", - place = "방송실", - type = ScheduleType.ACADEMIC, - date = persistentListOf(DodamDate.localDateNow().plus(DatePeriod(days = 2))), - targetGrades = persistentListOf(Grade.GRADE_ALL) - ), - Schedule( - id = 2, - name = "직업 교육 박람회", - place = "방송실", - type = ScheduleType.ACADEMIC, - date = persistentListOf(DodamDate.localDateNow().plus(DatePeriod(days = 1))), - targetGrades = persistentListOf(Grade.GRADE_ALL) - ) - ) - ), - showShimmer = false, - fetchSchedule = {}, + uiState = state.scheduleUiState, + showShimmer = state.showShimmer, + fetchSchedule = viewModel::loadSchedule, onContentClick = {} ) } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt index 4017c05c..56ee1572 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt @@ -10,11 +10,13 @@ import com.b1nd.dodam.data.core.model.Status import com.b1nd.dodam.data.meal.MealRepository import com.b1nd.dodam.data.nightstudy.NightStudyRepository import com.b1nd.dodam.data.outing.OutingRepository +import com.b1nd.dodam.data.schedule.ScheduleRepository import com.b1nd.dodam.home.model.BannerUiState import com.b1nd.dodam.home.model.HomeUiState import com.b1nd.dodam.home.model.MealUiState import com.b1nd.dodam.home.model.NightStudyUiState import com.b1nd.dodam.home.model.OutUiState +import com.b1nd.dodam.home.model.ScheduleUiState import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -25,7 +27,9 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.datetime.DatePeriod import kotlinx.datetime.LocalDate +import kotlinx.datetime.plus import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -35,6 +39,7 @@ class HomeViewModel: ViewModel(), KoinComponent { private val mealRepository: MealRepository by inject() private val outingRepository: OutingRepository by inject() private val nightStudyRepository: NightStudyRepository by inject() + private val scheduleRepository: ScheduleRepository by inject() private val _state = MutableStateFlow(HomeUiState()) val state = _state.asStateFlow() @@ -45,6 +50,7 @@ class HomeViewModel: ViewModel(), KoinComponent { loadMeal() loadOuting() loadNightStudy() + loadSchedule() } } @@ -203,4 +209,34 @@ class HomeViewModel: ViewModel(), KoinComponent { } } } + + fun loadSchedule() = viewModelScope.launch { + val startDate = DodamDate.localDateNow() + val endDate = startDate.plus(DatePeriod(months = 1)) + scheduleRepository.getScheduleBetweenPeriods( + startDate = startDate, + endDate = endDate + ).collect { + when (it) { + is Result.Success -> { + _state.update { state -> + state.copy( + showShimmer = false, + scheduleUiState = ScheduleUiState.Success(data = it.data) + ) + } + } + is Result.Loading -> {} + is Result.Error -> { + it.error.printStackTrace() + _state.update { state -> + state.copy( + showShimmer = false, + scheduleUiState = ScheduleUiState.Error + ) + } + } + } + } + } } \ No newline at end of file diff --git a/network/schedule/src/commonMain/kotlin/com/b1nd/dodam/network/schedule/api/ScheduleService.kt b/network/schedule/src/commonMain/kotlin/com/b1nd/dodam/network/schedule/api/ScheduleService.kt index b9638cea..3ec8ebb6 100644 --- a/network/schedule/src/commonMain/kotlin/com/b1nd/dodam/network/schedule/api/ScheduleService.kt +++ b/network/schedule/src/commonMain/kotlin/com/b1nd/dodam/network/schedule/api/ScheduleService.kt @@ -18,8 +18,8 @@ internal class ScheduleService( override suspend fun getScheduleBetweenPeriods(startDate: String, endDate: String): ImmutableList { return safeRequest { client.get(DodamUrl.Schedule.SEARCH) { - parameter("startDate", startDate) - parameter("endDate", endDate) + parameter("startAt", startDate) + parameter("endAt", endDate) }.body>>() }.toImmutableList() } From efc12bfda4356d395666cdd96ac308afd026ffc6 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Fri, 6 Sep 2024 17:21:33 +0900 Subject: [PATCH 18/24] fix: Ktor Client iOS TLS Error --- .../kotlin/com/b1nd/dodam/teacher/DodamTeacherApp.kt | 2 +- network/core/build.gradle.kts | 6 ++++-- .../dodam/network/core/di/NetworkModule.android.kt | 12 ++++++++++++ .../com/b1nd/dodam/network/core/di/NetworkModule.kt | 6 ++++-- .../b1nd/dodam/network/core/di/NetworkModule.ios.kt | 12 ++++++++++++ 5 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 network/core/src/androidMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.android.kt create mode 100644 network/core/src/iosMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.ios.kt diff --git a/dodam-teacher-android/src/commonMain/kotlin/com/b1nd/dodam/teacher/DodamTeacherApp.kt b/dodam-teacher-android/src/commonMain/kotlin/com/b1nd/dodam/teacher/DodamTeacherApp.kt index f7973622..a728aeb7 100644 --- a/dodam-teacher-android/src/commonMain/kotlin/com/b1nd/dodam/teacher/DodamTeacherApp.kt +++ b/dodam-teacher-android/src/commonMain/kotlin/com/b1nd/dodam/teacher/DodamTeacherApp.kt @@ -41,7 +41,7 @@ fun DodamTeacherApp() { DodamTheme { NavHost( navController = navHostController, - startDestination = HOME_ROUTE, + startDestination = ONBOARDING_ROUTE, ) { onboardingScreen( onRegisterClick = navHostController::navigateToInfo, diff --git a/network/core/build.gradle.kts b/network/core/build.gradle.kts index 1941f17f..9924a06b 100644 --- a/network/core/build.gradle.kts +++ b/network/core/build.gradle.kts @@ -11,14 +11,16 @@ plugins { kotlin { sourceSets { androidMain.dependencies { - + implementation(libs.ktor.client.cio) + } + iosMain.dependencies { + implementation(libs.ktor.client.darwin) } commonMain.dependencies { implementation(projects.datastore) implementation(projects.common) api(libs.ktor.client.core) implementation(libs.ktor.client.logging) - implementation(libs.ktor.client.cio) implementation(libs.ktor.client.auth) api(libs.ktor.client.content.negotiation) api(libs.ktor.serialization.kotlinx.json) diff --git a/network/core/src/androidMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.android.kt b/network/core/src/androidMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.android.kt new file mode 100644 index 00000000..319cf8d2 --- /dev/null +++ b/network/core/src/androidMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.android.kt @@ -0,0 +1,12 @@ +package com.b1nd.dodam.network.core.di + +import io.ktor.client.HttpClient +import io.ktor.client.HttpClientConfig +import io.ktor.client.engine.HttpClientEngineConfig +import io.ktor.client.engine.cio.CIO + +internal actual fun getHttpClient(block: HttpClientConfig<*>.() -> Unit): HttpClient = + HttpClient( + engine = CIO.create(), + block = block + ) \ No newline at end of file diff --git a/network/core/src/commonMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.kt b/network/core/src/commonMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.kt index 25327f4c..88bf2716 100644 --- a/network/core/src/commonMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.kt +++ b/network/core/src/commonMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.kt @@ -8,8 +8,8 @@ import com.b1nd.dodam.network.core.model.Response import com.b1nd.dodam.network.core.model.TokenRequest import com.b1nd.dodam.network.core.model.TokenResponse import io.ktor.client.HttpClient +import io.ktor.client.HttpClientConfig import io.ktor.client.call.body -import io.ktor.client.engine.cio.CIO import io.ktor.client.plugins.HttpTimeout import io.ktor.client.plugins.auth.Auth import io.ktor.client.plugins.auth.providers.BearerTokens @@ -33,11 +33,13 @@ private const val TIME_OUT = 60_000L val log = logging() +internal expect fun getHttpClient(block: HttpClientConfig<*>.() -> Unit = {}): HttpClient + val networkCoreModule = module { single { val datastore: DataStoreRepository = get() - HttpClient(CIO) { + getHttpClient { install(ContentNegotiation) { json( Json { diff --git a/network/core/src/iosMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.ios.kt b/network/core/src/iosMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.ios.kt new file mode 100644 index 00000000..86376205 --- /dev/null +++ b/network/core/src/iosMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.ios.kt @@ -0,0 +1,12 @@ +package com.b1nd.dodam.network.core.di + +import io.ktor.client.HttpClient +import io.ktor.client.HttpClientConfig +import io.ktor.client.engine.HttpClientEngineConfig +import io.ktor.client.engine.darwin.Darwin + +internal actual fun getHttpClient(block: HttpClientConfig<*>.() -> Unit): HttpClient = + HttpClient( + engine = Darwin.create(), + block = block + ) \ No newline at end of file From 4ddb2054253b5b6cbda956f6807462b3524a21c3 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Fri, 6 Sep 2024 19:28:17 +0900 Subject: [PATCH 19/24] fix: iOS Not Load Schedule --- .../kotlin/com/b1nd/dodam/common/utiles/Extension.kt | 3 --- .../com/b1nd/dodam/common/utiles/Extension.ios.kt | 10 ---------- .../com/b1nd/dodam/common/utiles/Extension.android.kt | 3 --- .../b1nd/dodam/data/schedule/ScheduleRepository.kt | 2 +- .../schedule/repository/ScheduleRepositoryImpl.kt | 7 +++---- .../java/com/b1nd/dodam/student/home/HomeViewModel.kt | 8 ++++---- .../kotlin/com/b1nd/dodam/home/HomeViewModel.kt | 11 ++--------- .../dodam/network/schedule/api/ScheduleService.kt | 7 ++++--- .../network/schedule/datasource/ScheduleDataSource.kt | 3 ++- 9 files changed, 16 insertions(+), 38 deletions(-) delete mode 100644 common/src/commonMain/kotlin/com/b1nd/dodam/common/utiles/Extension.kt delete mode 100644 common/src/iosMain/kotlin/com/b1nd/dodam/common/utiles/Extension.ios.kt delete mode 100644 common/src/main/java/com/b1nd/dodam/common/utiles/Extension.android.kt diff --git a/common/src/commonMain/kotlin/com/b1nd/dodam/common/utiles/Extension.kt b/common/src/commonMain/kotlin/com/b1nd/dodam/common/utiles/Extension.kt deleted file mode 100644 index 32f3c209..00000000 --- a/common/src/commonMain/kotlin/com/b1nd/dodam/common/utiles/Extension.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.b1nd.dodam.common.utiles - -expect fun String.Companion.javaFormat(format: String, vararg args: Any?): String diff --git a/common/src/iosMain/kotlin/com/b1nd/dodam/common/utiles/Extension.ios.kt b/common/src/iosMain/kotlin/com/b1nd/dodam/common/utiles/Extension.ios.kt deleted file mode 100644 index 19f044c4..00000000 --- a/common/src/iosMain/kotlin/com/b1nd/dodam/common/utiles/Extension.ios.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.b1nd.dodam.common.utiles - -import platform.Foundation.NSString -import platform.Foundation.stringWithFormat -import platform.darwin.NSObject - -actual fun String.Companion.javaFormat(format: String, vararg args: Any?): String { -// val nsArray = arrayOf(*args.map { it as? NSObject ?: NSNull() }.toTypedArray())//args.map { it as? NSObject ?: NSNull() } //NSString.stringWithFormat(format, args) - return NSString.stringWithFormat(format, args.map { it as? NSObject }) -} diff --git a/common/src/main/java/com/b1nd/dodam/common/utiles/Extension.android.kt b/common/src/main/java/com/b1nd/dodam/common/utiles/Extension.android.kt deleted file mode 100644 index 186fa11e..00000000 --- a/common/src/main/java/com/b1nd/dodam/common/utiles/Extension.android.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.b1nd.dodam.common.utiles - -actual fun String.Companion.javaFormat(format: String, vararg args: Any?): String = String.format(format, *args) diff --git a/data/schedule/src/commonMain/kotlin/com/b1nd/dodam/data/schedule/ScheduleRepository.kt b/data/schedule/src/commonMain/kotlin/com/b1nd/dodam/data/schedule/ScheduleRepository.kt index baac12df..18170b6a 100644 --- a/data/schedule/src/commonMain/kotlin/com/b1nd/dodam/data/schedule/ScheduleRepository.kt +++ b/data/schedule/src/commonMain/kotlin/com/b1nd/dodam/data/schedule/ScheduleRepository.kt @@ -7,5 +7,5 @@ import kotlinx.coroutines.flow.Flow import kotlinx.datetime.LocalDate interface ScheduleRepository { - fun getScheduleBetweenPeriods(startDate: LocalDate, endDate: LocalDate): Flow>> + fun getScheduleBetweenPeriods(startAt: LocalDate, endAt: LocalDate): Flow>> } diff --git a/data/schedule/src/commonMain/kotlin/com/b1nd/dodam/data/schedule/repository/ScheduleRepositoryImpl.kt b/data/schedule/src/commonMain/kotlin/com/b1nd/dodam/data/schedule/repository/ScheduleRepositoryImpl.kt index 3049cbb4..14c7d7c9 100644 --- a/data/schedule/src/commonMain/kotlin/com/b1nd/dodam/data/schedule/repository/ScheduleRepositoryImpl.kt +++ b/data/schedule/src/commonMain/kotlin/com/b1nd/dodam/data/schedule/repository/ScheduleRepositoryImpl.kt @@ -4,7 +4,6 @@ import com.b1nd.dodam.common.Dispatcher import com.b1nd.dodam.common.DispatcherType import com.b1nd.dodam.common.result.Result import com.b1nd.dodam.common.result.asResult -import com.b1nd.dodam.common.utiles.javaFormat import com.b1nd.dodam.data.schedule.ScheduleRepository import com.b1nd.dodam.data.schedule.model.Schedule import com.b1nd.dodam.data.schedule.model.toModel @@ -21,12 +20,12 @@ internal class ScheduleRepositoryImpl constructor( private val network: ScheduleDataSource, @Dispatcher(DispatcherType.IO) private val dispatcher: CoroutineDispatcher, ) : ScheduleRepository { - override fun getScheduleBetweenPeriods(startDate: LocalDate, endDate: LocalDate): Flow>> { + override fun getScheduleBetweenPeriods(startAt: LocalDate, endAt: LocalDate): Flow>> { return flow { emit( network.getScheduleBetweenPeriods( - String.javaFormat("%02d-%02d-%02d", startDate.year, startDate.monthNumber, startDate.dayOfMonth), - String.javaFormat("%02d-%02d-%02d", endDate.year, endDate.monthNumber, endDate.dayOfMonth), + startAt = startAt, + endAt = endAt, ).map { it.toModel() }.toImmutableList(), diff --git a/feature-student/home/src/main/java/com/b1nd/dodam/student/home/HomeViewModel.kt b/feature-student/home/src/main/java/com/b1nd/dodam/student/home/HomeViewModel.kt index 2998ad79..c8106285 100644 --- a/feature-student/home/src/main/java/com/b1nd/dodam/student/home/HomeViewModel.kt +++ b/feature-student/home/src/main/java/com/b1nd/dodam/student/home/HomeViewModel.kt @@ -186,8 +186,8 @@ class HomeViewModel : ViewModel(), KoinComponent { } launch { scheduleRepository.getScheduleBetweenPeriods( - startDate = LocalDate.of(localDate.year, localDate.monthNumber, localDate.dayOfMonth).toKotlinLocalDate(), - endDate = LocalDate.of(nextDate.year, nextDate.monthNumber, nextDate.dayOfMonth).toKotlinLocalDate(), + startAt = LocalDate.of(localDate.year, localDate.monthNumber, localDate.dayOfMonth).toKotlinLocalDate(), + endAt = LocalDate.of(nextDate.year, nextDate.monthNumber, nextDate.dayOfMonth).toKotlinLocalDate(), ).collect { result -> when (result) { is Result.Success -> { @@ -403,8 +403,8 @@ class HomeViewModel : ViewModel(), KoinComponent { fun fetchSchedule() = viewModelScope.launch { scheduleRepository.getScheduleBetweenPeriods( - startDate = LocalDate.of(localDate.year, localDate.monthNumber, localDate.dayOfMonth).toKotlinLocalDate(), - endDate = LocalDate.of(nextDate.year, nextDate.monthNumber, nextDate.dayOfMonth).toKotlinLocalDate(), + startAt = LocalDate.of(localDate.year, localDate.monthNumber, localDate.dayOfMonth).toKotlinLocalDate(), + endAt = LocalDate.of(nextDate.year, nextDate.monthNumber, nextDate.dayOfMonth).toKotlinLocalDate(), ).collect { result -> when (result) { is Result.Success -> { diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt index 56ee1572..ff3952bd 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt @@ -1,7 +1,6 @@ package com.b1nd.dodam.home import androidx.lifecycle.ViewModel -import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewModelScope import com.b1nd.dodam.common.date.DodamDate import com.b1nd.dodam.common.result.Result @@ -18,17 +17,11 @@ import com.b1nd.dodam.home.model.NightStudyUiState import com.b1nd.dodam.home.model.OutUiState import com.b1nd.dodam.home.model.ScheduleUiState import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.datetime.DatePeriod -import kotlinx.datetime.LocalDate import kotlinx.datetime.plus import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -214,8 +207,8 @@ class HomeViewModel: ViewModel(), KoinComponent { val startDate = DodamDate.localDateNow() val endDate = startDate.plus(DatePeriod(months = 1)) scheduleRepository.getScheduleBetweenPeriods( - startDate = startDate, - endDate = endDate + startAt = startDate, + endAt = endDate ).collect { when (it) { is Result.Success -> { diff --git a/network/schedule/src/commonMain/kotlin/com/b1nd/dodam/network/schedule/api/ScheduleService.kt b/network/schedule/src/commonMain/kotlin/com/b1nd/dodam/network/schedule/api/ScheduleService.kt index 3ec8ebb6..dbec3664 100644 --- a/network/schedule/src/commonMain/kotlin/com/b1nd/dodam/network/schedule/api/ScheduleService.kt +++ b/network/schedule/src/commonMain/kotlin/com/b1nd/dodam/network/schedule/api/ScheduleService.kt @@ -11,15 +11,16 @@ import io.ktor.client.request.get import io.ktor.client.request.parameter import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList +import kotlinx.datetime.LocalDate internal class ScheduleService( private val client: HttpClient, ) : ScheduleDataSource { - override suspend fun getScheduleBetweenPeriods(startDate: String, endDate: String): ImmutableList { + override suspend fun getScheduleBetweenPeriods(startAt: LocalDate, endAt: LocalDate): ImmutableList { return safeRequest { client.get(DodamUrl.Schedule.SEARCH) { - parameter("startAt", startDate) - parameter("endAt", endDate) + parameter("startAt", startAt) + parameter("endAt", endAt) }.body>>() }.toImmutableList() } diff --git a/network/schedule/src/commonMain/kotlin/com/b1nd/dodam/network/schedule/datasource/ScheduleDataSource.kt b/network/schedule/src/commonMain/kotlin/com/b1nd/dodam/network/schedule/datasource/ScheduleDataSource.kt index fe499423..1050579a 100644 --- a/network/schedule/src/commonMain/kotlin/com/b1nd/dodam/network/schedule/datasource/ScheduleDataSource.kt +++ b/network/schedule/src/commonMain/kotlin/com/b1nd/dodam/network/schedule/datasource/ScheduleDataSource.kt @@ -2,7 +2,8 @@ package com.b1nd.dodam.network.schedule.datasource import com.b1nd.dodam.network.schedule.model.ScheduleResponse import kotlinx.collections.immutable.ImmutableList +import kotlinx.datetime.LocalDate interface ScheduleDataSource { - suspend fun getScheduleBetweenPeriods(startDate: String, endDate: String): ImmutableList + suspend fun getScheduleBetweenPeriods(startAt: LocalDate, endAt: LocalDate): ImmutableList } From f0a08a6f3823dfe3c542ee12ad239163cf7dd5bb Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Fri, 6 Sep 2024 19:50:08 +0900 Subject: [PATCH 20/24] feat: Create Home Pull Refresh --- feature-teacher/home/build.gradle.kts | 1 + .../kotlin/com/b1nd/dodam/home/HomeScreen.kt | 35 +++++++++++++++++-- .../com/b1nd/dodam/home/HomeViewModel.kt | 2 +- gradle/libs.versions.toml | 1 + 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/feature-teacher/home/build.gradle.kts b/feature-teacher/home/build.gradle.kts index 984683fd..9e65d360 100644 --- a/feature-teacher/home/build.gradle.kts +++ b/feature-teacher/home/build.gradle.kts @@ -35,6 +35,7 @@ kotlin { implementation(projects.common) implementation(projects.ui) implementation(projects.logging) + implementation(libs.multiplatform.compose.material) implementation(projects.data.login) diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt index f2c806e1..56d99411 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt @@ -20,14 +20,21 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.pullrefresh.PullRefreshIndicator +import androidx.compose.material.pullrefresh.pullRefresh +import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState 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 @@ -77,13 +84,32 @@ import org.koin.compose.viewmodel.koinViewModel import org.koin.core.annotation.KoinExperimentalAPI -@OptIn(KoinExperimentalAPI::class) +@OptIn(KoinExperimentalAPI::class, ExperimentalMaterialApi::class) @Composable internal fun HomeScreen( viewModel: HomeViewModel = koinViewModel() ) { val state by viewModel.state.collectAsState() + var isRefreshing by remember { mutableStateOf(false) } + + val pullRefreshState = rememberPullRefreshState( + refreshing = isRefreshing, + onRefresh = { + isRefreshing = true + viewModel.run { + loadMeal() + loadOuting() + loadNightStudy() + loadSchedule() + loadBanner() + } + } + ) + + LaunchedEffect(state.mealUiState) { + isRefreshing = false + } Scaffold( modifier = Modifier.background(DodamTheme.colors.backgroundNeutral), @@ -117,7 +143,8 @@ internal fun HomeScreen( Box( modifier = Modifier .fillMaxSize() - .padding(it), + .padding(it) + .pullRefresh(pullRefreshState), contentAlignment = Alignment.TopCenter, ) { LazyColumn( @@ -173,6 +200,10 @@ internal fun HomeScreen( Spacer(modifier = Modifier.height(150.dp)) } } + PullRefreshIndicator( + refreshing = isRefreshing, + state = pullRefreshState + ) } } } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt index ff3952bd..0ec09f7c 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt @@ -47,7 +47,7 @@ class HomeViewModel: ViewModel(), KoinComponent { } } - private fun loadBanner() = viewModelScope.launch { + fun loadBanner() = viewModelScope.launch { bannerRepository.getActiveBanner().collect { when (it) { is Result.Success -> { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 91c19e74..4cb0ba4a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -98,6 +98,7 @@ androidx-navigation = { group = "androidx.navigation", name = "navigation-ui-ktx androidx-datastore-core-okio = { group = "androidx.datastore", name = "datastore-core-okio", version.ref = "datastore" } androidx-datastore-preferences-core = { group = "androidx.datastore", name = "datastore-preferences-core", version.ref = "datastore" } multiplatform-compose-material3 = { group = "org.jetbrains.compose.material3", name = "material3", version.ref = "compose-plugin"} +multiplatform-compose-material = { group = "org.jetbrains.compose.material", name = "material", version.ref = "compose-plugin"} multiplatform-compose-ui-tooling-preview = { group = "org.jetbrains.compose.ui", name = "ui-tooling-preview", version.ref = "compose-plugin"} multiplatform-compose-navigation = { group = "org.jetbrains.androidx.navigation", name = "navigation-compose", version.ref = "compose-navigation" } multiplatform-compose-components-resources = { group = "org.jetbrains.compose.components", name = "components-resources", version.ref = "compose-plugin"} From 08c87277078484d7d7f064839764be8135e5457e Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Fri, 6 Sep 2024 19:59:00 +0900 Subject: [PATCH 21/24] fix: Home Meal Text Margin --- .../kotlin/com/b1nd/dodam/home/card/MealCard.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt index 027af183..b42e3894 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt @@ -70,22 +70,22 @@ internal fun MealCard( val meals = index.getMeal(state.data)?.details if (meals != null) { Column { - for (i in meals.indices step 2) { + for (i in meals.indices step 2 ) { Row( modifier = Modifier.fillMaxWidth() ) { Text( + modifier = Modifier.weight(1f), text = meals[i].name, style = DodamTheme.typography.body1Medium(), color = DodamTheme.colors.labelNormal ) - Spacer(Modifier.weight(1f)) Text( - text = meals.getOrNull(i)?.name ?: "", + modifier = Modifier.weight(1f), + text = meals.getOrNull(i+1)?.name ?: "", style = DodamTheme.typography.body1Medium(), color = DodamTheme.colors.labelNormal ) - Spacer(Modifier.weight(1f)) } } } From f9993d3e1432d40f980b7642bb6b015bac6f9ea2 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Fri, 6 Sep 2024 20:56:41 +0900 Subject: [PATCH 22/24] refactor: Not Show Loading UI --- .../com/b1nd/dodam/common/utiles/Utiles.kt | 35 +++++++++++++++ .../com/b1nd/dodam/home/HomeViewModel.kt | 44 ++++++++++++++----- .../b1nd/dodam/home/card/NightStudyCard.kt | 4 +- 3 files changed, 69 insertions(+), 14 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/b1nd/dodam/common/utiles/Utiles.kt diff --git a/common/src/commonMain/kotlin/com/b1nd/dodam/common/utiles/Utiles.kt b/common/src/commonMain/kotlin/com/b1nd/dodam/common/utiles/Utiles.kt new file mode 100644 index 00000000..218e2b10 --- /dev/null +++ b/common/src/commonMain/kotlin/com/b1nd/dodam/common/utiles/Utiles.kt @@ -0,0 +1,35 @@ +package com.b1nd.dodam.common.utiles + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.joinAll + + +fun combineWhenAllComplete( + flow1: Flow, + flow2: Flow, + transform: suspend (T1, T2) -> R +): Flow = flow { + var lastValue1: T1? = null + var lastValue2: T2? = null + + // 첫 번째 Flow 처리 + val job1 = flow1.onEach { value -> + lastValue1 = value + }.launchIn(CoroutineScope(Dispatchers.IO)) + + // 두 번째 Flow 처리 + val job2 = flow2.onEach { value -> + lastValue2 = value + }.launchIn(CoroutineScope(Dispatchers.IO)) + + joinAll(job1, job2) + + // 두 Flow가 모두 종료되었을 때 마지막 값으로 변환 작업 수행 + emit(transform(lastValue1!!, lastValue2!!)) +} \ No newline at end of file diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt index 0ec09f7c..f73febb3 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.b1nd.dodam.common.date.DodamDate import com.b1nd.dodam.common.result.Result +import com.b1nd.dodam.common.utiles.combineWhenAllComplete import com.b1nd.dodam.data.banner.BannerRepository import com.b1nd.dodam.data.core.model.Status import com.b1nd.dodam.data.meal.MealRepository @@ -16,9 +17,11 @@ import com.b1nd.dodam.home.model.MealUiState import com.b1nd.dodam.home.model.NightStudyUiState import com.b1nd.dodam.home.model.OutUiState import com.b1nd.dodam.home.model.ScheduleUiState +import com.b1nd.dodam.logging.KmLogging +import com.b1nd.dodam.logging.logging import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.datetime.DatePeriod @@ -109,7 +112,12 @@ class HomeViewModel: ViewModel(), KoinComponent { fun loadOuting() = viewModelScope.launch { val date = DodamDate.localDateNow() - combine( + _state.update { + it.copy( + outUiState = OutUiState.Loading + ) + } + combineWhenAllComplete( outingRepository.getOutings(date), outingRepository.getSleepovers(date) ) { outing, sleepover -> @@ -126,7 +134,7 @@ class HomeViewModel: ViewModel(), KoinComponent { is Result.Loading -> {} is Result.Error -> { outing.error.printStackTrace() - return@combine OutUiState.Error + return@combineWhenAllComplete OutUiState.Error } } @@ -138,17 +146,17 @@ class HomeViewModel: ViewModel(), KoinComponent { is Result.Loading -> {} is Result.Error -> { sleepover.error.printStackTrace() - return@combine OutUiState.Error + return@combineWhenAllComplete OutUiState.Error } } - return@combine OutUiState.Success( + return@combineWhenAllComplete OutUiState.Success( outAllowCount = outAllowCount, outPendingCount = outPendingCount, sleepoverAllowCount = sleepoverAllowCount, sleepoverPendingCount = sleepoverPendingCount ) - }.collect { + }.collectLatest { _state.update { state -> state.copy( showShimmer = false, @@ -159,7 +167,13 @@ class HomeViewModel: ViewModel(), KoinComponent { } fun loadNightStudy() = viewModelScope.launch { - combine( + _state.update { + it.copy( + nightStudyUiState = NightStudyUiState.Loading + ) + } + + combineWhenAllComplete( nightStudyRepository.getNightStudy(), nightStudyRepository.getNightStudyPending() ){ nightStudyFlow, nightStudyPendingFlow -> @@ -174,7 +188,7 @@ class HomeViewModel: ViewModel(), KoinComponent { is Result.Loading -> {} is Result.Error -> { nightStudyFlow.error.printStackTrace() - return@combine NightStudyUiState.Error + return@combineWhenAllComplete NightStudyUiState.Error } } @@ -185,15 +199,15 @@ class HomeViewModel: ViewModel(), KoinComponent { is Result.Loading -> {} is Result.Error -> { nightStudyPendingFlow.error.printStackTrace() - return@combine NightStudyUiState.Error + return@combineWhenAllComplete NightStudyUiState.Error } } - return@combine NightStudyUiState.Success( + return@combineWhenAllComplete NightStudyUiState.Success( active = activeCount, pending = pendingCount ) - }.collect { + }.collectLatest { _state.update { state -> state.copy( showShimmer = false, @@ -219,7 +233,13 @@ class HomeViewModel: ViewModel(), KoinComponent { ) } } - is Result.Loading -> {} + is Result.Loading -> { + _state.update { + it.copy( + scheduleUiState = ScheduleUiState.Loading + ) + } + } is Result.Error -> { it.error.printStackTrace() _state.update { state -> diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt index 5e1d48f2..aad6e60b 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt @@ -49,14 +49,14 @@ internal fun NightStudyCard( ) } } - NightStudyUiState.Error -> { + is NightStudyUiState.Error -> { DefaultText( onClick = onRefreshClick, label = "심자 현황을 불러올 수 없어요", body = "다시 불러오기" ) } - NightStudyUiState.Loading -> { + is NightStudyUiState.Loading -> { Box( modifier = Modifier .fillMaxWidth() From c3efc634bb67d1b4769e7c6d04a0f7097d2ed99f Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Fri, 6 Sep 2024 20:58:01 +0900 Subject: [PATCH 23/24] style: Apply Spotless Format --- .../com/b1nd/dodam/common/date/DodamDate.kt | 2 +- .../com/b1nd/dodam/common/utiles/Utiles.kt | 9 +- .../b1nd/dodam/common/date/DodamDate.ios.kt | 15 ++-- .../dodam/common/date/DodamDate.android.kt | 2 +- .../teacher/utiles/DodamTeacherApplication.kt | 4 +- .../com/b1nd/dodam/teacher/DodamTeacherApp.kt | 31 +++---- .../kotlin/com/b1nd/dodam/teacher/Koin.kt | 2 +- .../dodam/home/preview/HomeScreenPreview.kt | 2 +- .../kotlin/com/b1nd/dodam/home/HomeScreen.kt | 84 +++++-------------- .../com/b1nd/dodam/home/HomeViewModel.kt | 58 ++++++------- .../com/b1nd/dodam/home/card/BannerCard.kt | 30 +------ .../com/b1nd/dodam/home/card/MealCard.kt | 42 ++++------ .../b1nd/dodam/home/card/NightStudyCard.kt | 21 ++--- .../com/b1nd/dodam/home/card/OutCard.kt | 20 ++--- .../com/b1nd/dodam/home/card/ScheduleCard.kt | 35 +++----- .../com/b1nd/dodam/home/di/ViewModelModule.kt | 3 +- .../com/b1nd/dodam/home/model/HomeUiState.kt | 5 +- .../dodam/home/navigation/HomeNavigation.kt | 5 +- .../network/core/di/NetworkModule.android.kt | 10 +-- .../network/core/di/NetworkModule.ios.kt | 10 +-- .../b1nd/dodam/ui/component/DodamContainer.kt | 4 +- 21 files changed, 127 insertions(+), 267 deletions(-) diff --git a/common/src/commonMain/kotlin/com/b1nd/dodam/common/date/DodamDate.kt b/common/src/commonMain/kotlin/com/b1nd/dodam/common/date/DodamDate.kt index 4aca9af6..9c6cebe3 100644 --- a/common/src/commonMain/kotlin/com/b1nd/dodam/common/date/DodamDate.kt +++ b/common/src/commonMain/kotlin/com/b1nd/dodam/common/date/DodamDate.kt @@ -6,4 +6,4 @@ import kotlinx.datetime.LocalDateTime expect object DodamDate { fun now(): LocalDateTime fun localDateNow(): LocalDate -} \ No newline at end of file +} diff --git a/common/src/commonMain/kotlin/com/b1nd/dodam/common/utiles/Utiles.kt b/common/src/commonMain/kotlin/com/b1nd/dodam/common/utiles/Utiles.kt index 218e2b10..99ffdd07 100644 --- a/common/src/commonMain/kotlin/com/b1nd/dodam/common/utiles/Utiles.kt +++ b/common/src/commonMain/kotlin/com/b1nd/dodam/common/utiles/Utiles.kt @@ -9,12 +9,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.joinAll - -fun combineWhenAllComplete( - flow1: Flow, - flow2: Flow, - transform: suspend (T1, T2) -> R -): Flow = flow { +fun combineWhenAllComplete(flow1: Flow, flow2: Flow, transform: suspend (T1, T2) -> R): Flow = flow { var lastValue1: T1? = null var lastValue2: T2? = null @@ -32,4 +27,4 @@ fun combineWhenAllComplete( // 두 Flow가 모두 종료되었을 때 마지막 값으로 변환 작업 수행 emit(transform(lastValue1!!, lastValue2!!)) -} \ No newline at end of file +} diff --git a/common/src/iosMain/kotlin/com/b1nd/dodam/common/date/DodamDate.ios.kt b/common/src/iosMain/kotlin/com/b1nd/dodam/common/date/DodamDate.ios.kt index f65e8e11..8893093e 100644 --- a/common/src/iosMain/kotlin/com/b1nd/dodam/common/date/DodamDate.ios.kt +++ b/common/src/iosMain/kotlin/com/b1nd/dodam/common/date/DodamDate.ios.kt @@ -9,15 +9,10 @@ import platform.Foundation.NSDate import platform.Foundation.now actual object DodamDate { - actual fun now(): LocalDateTime = - NSDate - .now() - .toKotlinInstant() - .toLocalDateTime(TimeZone.currentSystemDefault()) + actual fun now(): LocalDateTime = NSDate + .now() + .toKotlinInstant() + .toLocalDateTime(TimeZone.currentSystemDefault()) actual fun localDateNow(): LocalDate = now().date - - - - -} \ No newline at end of file +} diff --git a/common/src/main/java/com/b1nd/dodam/common/date/DodamDate.android.kt b/common/src/main/java/com/b1nd/dodam/common/date/DodamDate.android.kt index 193683a2..e51bfa78 100644 --- a/common/src/main/java/com/b1nd/dodam/common/date/DodamDate.android.kt +++ b/common/src/main/java/com/b1nd/dodam/common/date/DodamDate.android.kt @@ -8,4 +8,4 @@ import kotlinx.datetime.toKotlinLocalDateTime actual object DodamDate { actual fun now(): LocalDateTime = java.time.LocalDateTime.now().toKotlinLocalDateTime() actual fun localDateNow(): LocalDate = java.time.LocalDate.now().toKotlinLocalDate() -} \ No newline at end of file +} diff --git a/dodam-teacher-android/src/androidMain/kotlin/com/b1nd/dodam/teacher/utiles/DodamTeacherApplication.kt b/dodam-teacher-android/src/androidMain/kotlin/com/b1nd/dodam/teacher/utiles/DodamTeacherApplication.kt index bd078d3d..0de1c6ac 100644 --- a/dodam-teacher-android/src/androidMain/kotlin/com/b1nd/dodam/teacher/utiles/DodamTeacherApplication.kt +++ b/dodam-teacher-android/src/androidMain/kotlin/com/b1nd/dodam/teacher/utiles/DodamTeacherApplication.kt @@ -6,8 +6,8 @@ import coil3.PlatformContext import coil3.SingletonImageLoader import com.b1nd.dodam.teacher.getAsyncImageLoader -class DodamTeacherApplication: Application(), SingletonImageLoader.Factory { +class DodamTeacherApplication : Application(), SingletonImageLoader.Factory { override fun newImageLoader(context: PlatformContext): ImageLoader { return getAsyncImageLoader(this) } -} \ No newline at end of file +} diff --git a/dodam-teacher-android/src/commonMain/kotlin/com/b1nd/dodam/teacher/DodamTeacherApp.kt b/dodam-teacher-android/src/commonMain/kotlin/com/b1nd/dodam/teacher/DodamTeacherApp.kt index a728aeb7..931b92ec 100644 --- a/dodam-teacher-android/src/commonMain/kotlin/com/b1nd/dodam/teacher/DodamTeacherApp.kt +++ b/dodam-teacher-android/src/commonMain/kotlin/com/b1nd/dodam/teacher/DodamTeacherApp.kt @@ -3,21 +3,16 @@ package com.b1nd.dodam.teacher import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import coil3.ImageLoader import coil3.PlatformContext import coil3.annotation.ExperimentalCoilApi import coil3.compose.setSingletonImageLoaderFactory import coil3.memory.MemoryCache -import coil3.network.CacheStrategy -import coil3.network.NetworkFetcher import coil3.network.ktor.KtorNetworkFetcherFactory -import coil3.network.ktor.asNetworkClient import coil3.request.crossfade import coil3.util.DebugLogger import com.b1nd.dodam.designsystem.DodamTheme -import com.b1nd.dodam.home.navigation.HOME_ROUTE import com.b1nd.dodam.home.navigation.homeScreen import com.b1nd.dodam.home.navigation.navigateToHome import com.b1nd.dodam.login.navigation.loginScreen @@ -29,7 +24,6 @@ import com.b1nd.dodam.register.navigation.authScreen import com.b1nd.dodam.register.navigation.infoScreen import com.b1nd.dodam.register.navigation.navigateToAuth import com.b1nd.dodam.register.navigation.navigateToInfo -import io.ktor.client.HttpClient @OptIn(ExperimentalMaterial3Api::class, ExperimentalCoilApi::class) @Composable @@ -78,16 +72,15 @@ fun DodamTeacherApp() { } @OptIn(ExperimentalCoilApi::class) -internal fun getAsyncImageLoader(context: PlatformContext) = - ImageLoader.Builder(context) - .crossfade(true) - .memoryCache { - MemoryCache.Builder() - .maxSizePercent(context, percent = 0.25) - .build() - } - .logger(DebugLogger()) - .components { - add(KtorNetworkFetcherFactory()) - } - .build() \ No newline at end of file +internal fun getAsyncImageLoader(context: PlatformContext) = ImageLoader.Builder(context) + .crossfade(true) + .memoryCache { + MemoryCache.Builder() + .maxSizePercent(context, percent = 0.25) + .build() + } + .logger(DebugLogger()) + .components { + add(KtorNetworkFetcherFactory()) + } + .build() diff --git a/dodam-teacher-android/src/commonMain/kotlin/com/b1nd/dodam/teacher/Koin.kt b/dodam-teacher-android/src/commonMain/kotlin/com/b1nd/dodam/teacher/Koin.kt index 9f45cf2f..22dbf69a 100644 --- a/dodam-teacher-android/src/commonMain/kotlin/com/b1nd/dodam/teacher/Koin.kt +++ b/dodam-teacher-android/src/commonMain/kotlin/com/b1nd/dodam/teacher/Koin.kt @@ -47,7 +47,7 @@ fun initKoin(block: KoinApplication.() -> Unit = {}) { nightStudyRepositoryModule, nightStudyDataSourceModule, scheduleRepositoryModule, - scheduleDatasourceModule + scheduleDatasourceModule, ) block() } diff --git a/feature-teacher/home/src/androidMain/kotlin/com/b1nd/dodam/home/preview/HomeScreenPreview.kt b/feature-teacher/home/src/androidMain/kotlin/com/b1nd/dodam/home/preview/HomeScreenPreview.kt index 8e6f8a09..06314937 100644 --- a/feature-teacher/home/src/androidMain/kotlin/com/b1nd/dodam/home/preview/HomeScreenPreview.kt +++ b/feature-teacher/home/src/androidMain/kotlin/com/b1nd/dodam/home/preview/HomeScreenPreview.kt @@ -11,4 +11,4 @@ fun HomeScreenPreview() { DodamTheme { HomeScreen() } -} \ No newline at end of file +} diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt index 56d99411..7837262f 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt @@ -1,7 +1,5 @@ package com.b1nd.dodam.home -import androidx.compose.animation.animateContentSize -import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource @@ -14,18 +12,14 @@ 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.statusBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.pullrefresh.PullRefreshIndicator import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -37,23 +31,8 @@ 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.graphics.vector.ImageVector -import androidx.compose.ui.graphics.vector.rememberVectorPainter -import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import coil3.compose.AsyncImage -import com.b1nd.dodam.common.date.DodamDate -import com.b1nd.dodam.data.banner.model.Banner -import com.b1nd.dodam.data.banner.model.BannerStatus -import com.b1nd.dodam.data.meal.model.Meal -import com.b1nd.dodam.data.meal.model.MealDetail -import com.b1nd.dodam.data.meal.model.Menu -import com.b1nd.dodam.data.schedule.model.Grade -import com.b1nd.dodam.data.schedule.model.Schedule -import com.b1nd.dodam.data.schedule.model.ScheduleType import com.b1nd.dodam.designsystem.DodamTheme import com.b1nd.dodam.designsystem.animation.rememberBounceIndication import com.b1nd.dodam.designsystem.component.ActionIcon @@ -62,34 +41,20 @@ import com.b1nd.dodam.designsystem.component.ButtonSize import com.b1nd.dodam.designsystem.component.DodamButton import com.b1nd.dodam.designsystem.component.DodamContentTopAppBar import com.b1nd.dodam.designsystem.foundation.DodamIcons -import com.b1nd.dodam.designsystem.foundation.DodamShapes -import com.b1nd.dodam.designsystem.resources.Res import com.b1nd.dodam.home.card.BannerCard import com.b1nd.dodam.home.card.MealCard import com.b1nd.dodam.home.card.NightStudyCard import com.b1nd.dodam.home.card.OutCard import com.b1nd.dodam.home.card.ScheduleCard -import com.b1nd.dodam.home.model.BannerUiState -import com.b1nd.dodam.home.model.MealUiState -import com.b1nd.dodam.home.model.ScheduleUiState -import com.b1nd.dodam.ui.component.DodamContainer import com.b1nd.dodam.ui.icons.DodamLogo -import io.ktor.utils.io.ByteReadChannel import kotlinx.collections.immutable.persistentListOf -import kotlinx.datetime.DatePeriod -import kotlinx.datetime.LocalDate import kotlinx.datetime.plus -import org.jetbrains.compose.resources.painterResource import org.koin.compose.viewmodel.koinViewModel import org.koin.core.annotation.KoinExperimentalAPI - @OptIn(KoinExperimentalAPI::class, ExperimentalMaterialApi::class) @Composable -internal fun HomeScreen( - viewModel: HomeViewModel = koinViewModel() -) { - +internal fun HomeScreen(viewModel: HomeViewModel = koinViewModel()) { val state by viewModel.state.collectAsState() var isRefreshing by remember { mutableStateOf(false) } @@ -104,7 +69,7 @@ internal fun HomeScreen( loadSchedule() loadBanner() } - } + }, ) LaunchedEffect(state.mealUiState) { @@ -134,11 +99,11 @@ internal fun HomeScreen( actionIcons = persistentListOf( ActionIcon( icon = DodamIcons.Bell, - onClick = {} - ) - ) + onClick = {}, + ), + ), ) - } + }, ) { Box( modifier = Modifier @@ -156,16 +121,15 @@ internal fun HomeScreen( ) { item { BannerCard( - state = state.bannerUiState + state = state.bannerUiState, ) } item { - MealCard( state = state.mealUiState, showShimmer = state.showShimmer, onClickContent = {}, - onClickRefresh = viewModel::loadMeal + onClickRefresh = viewModel::loadMeal, ) } @@ -175,7 +139,7 @@ internal fun HomeScreen( uiState = state.outUiState, onRefreshClick = viewModel::loadOuting, onOutingClick = {}, - onSleepoverClick = {} + onSleepoverClick = {}, ) } @@ -184,7 +148,7 @@ internal fun HomeScreen( showShimmer = state.showShimmer, uiState = state.nightStudyUiState, onContentClick = {}, - onRefreshClick = viewModel::loadNightStudy + onRefreshClick = viewModel::loadNightStudy, ) } @@ -193,7 +157,7 @@ internal fun HomeScreen( uiState = state.scheduleUiState, showShimmer = state.showShimmer, fetchSchedule = viewModel::loadSchedule, - onContentClick = {} + onContentClick = {}, ) } item { @@ -202,7 +166,7 @@ internal fun HomeScreen( } PullRefreshIndicator( refreshing = isRefreshing, - state = pullRefreshState + state = pullRefreshState, ) } } @@ -218,52 +182,46 @@ internal fun DefaultText(onClick: () -> Unit, label: String, body: String) { interactionSource = remember { MutableInteractionSource() }, indication = rememberBounceIndication(), ), - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { Column( modifier = Modifier .padding(6.dp) - .align(Alignment.CenterStart) + .align(Alignment.CenterStart), ) { Text( text = label, style = DodamTheme.typography.caption1Medium(), - color = DodamTheme.colors.labelAlternative + color = DodamTheme.colors.labelAlternative, ) Spacer(modifier = Modifier.height(2.dp)) Text( text = body, style = DodamTheme.typography.body1Bold().copy(fontWeight = FontWeight.Bold), - color = DodamTheme.colors.primaryNormal + color = DodamTheme.colors.primaryNormal, ) } } } @Composable -internal fun InnerCountCard( - modifier: Modifier = Modifier, - title: String, - content: String, - buttonText: String, - onClick: () -> Unit -) { +internal fun InnerCountCard(modifier: Modifier = Modifier, title: String, content: String, buttonText: String, onClick: () -> Unit) { Row( modifier = modifier .fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Column { Text( text = title, style = DodamTheme.typography.labelMedium(), - color = DodamTheme.colors.labelAssistive + color = DodamTheme.colors.labelAssistive, ) Text( text = content, style = DodamTheme.typography.heading2Medium(), - color = DodamTheme.colors.labelNormal + color = DodamTheme.colors.labelNormal, ) } Spacer(modifier = Modifier.weight(1f)) @@ -275,4 +233,4 @@ internal fun InnerCountCard( buttonSize = ButtonSize.Small, ) } -} \ No newline at end of file +} diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt index f73febb3..5f854bbf 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeViewModel.kt @@ -17,8 +17,6 @@ import com.b1nd.dodam.home.model.MealUiState import com.b1nd.dodam.home.model.NightStudyUiState import com.b1nd.dodam.home.model.OutUiState import com.b1nd.dodam.home.model.ScheduleUiState -import com.b1nd.dodam.logging.KmLogging -import com.b1nd.dodam.logging.logging import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collectLatest @@ -29,7 +27,7 @@ import kotlinx.datetime.plus import org.koin.core.component.KoinComponent import org.koin.core.component.inject -class HomeViewModel: ViewModel(), KoinComponent { +class HomeViewModel : ViewModel(), KoinComponent { private val bannerRepository: BannerRepository by inject() private val mealRepository: MealRepository by inject() @@ -57,7 +55,7 @@ class HomeViewModel: ViewModel(), KoinComponent { _state.update { state -> state.copy( showShimmer = false, - bannerUiState = BannerUiState.Success(it.data) + bannerUiState = BannerUiState.Success(it.data), ) } } @@ -66,11 +64,10 @@ class HomeViewModel: ViewModel(), KoinComponent { it.error.printStackTrace() _state.update { state -> state.copy( - bannerUiState = BannerUiState.None + bannerUiState = BannerUiState.None, ) } } - } } } @@ -80,29 +77,29 @@ class HomeViewModel: ViewModel(), KoinComponent { mealRepository.getMeal( year = date.year, month = date.monthNumber, - day = date.dayOfMonth + day = date.dayOfMonth, ).collect { when (it) { is Result.Success -> { - _state.update { state -> + _state.update { state -> state.copy( showShimmer = false, - mealUiState = MealUiState.Success(it.data) + mealUiState = MealUiState.Success(it.data), ) } } is Result.Loading -> { - _state.update { state -> + _state.update { state -> state.copy( - mealUiState = MealUiState.Loading + mealUiState = MealUiState.Loading, ) } } is Result.Error -> { it.error.printStackTrace() - _state.update { state -> + _state.update { state -> state.copy( - mealUiState = MealUiState.Error + mealUiState = MealUiState.Error, ) } } @@ -114,12 +111,12 @@ class HomeViewModel: ViewModel(), KoinComponent { val date = DodamDate.localDateNow() _state.update { it.copy( - outUiState = OutUiState.Loading + outUiState = OutUiState.Loading, ) } combineWhenAllComplete( outingRepository.getOutings(date), - outingRepository.getSleepovers(date) + outingRepository.getSleepovers(date), ) { outing, sleepover -> var outAllowCount = 0 var outPendingCount = 0 @@ -150,17 +147,17 @@ class HomeViewModel: ViewModel(), KoinComponent { } } - return@combineWhenAllComplete OutUiState.Success( + return@combineWhenAllComplete OutUiState.Success( outAllowCount = outAllowCount, outPendingCount = outPendingCount, sleepoverAllowCount = sleepoverAllowCount, - sleepoverPendingCount = sleepoverPendingCount + sleepoverPendingCount = sleepoverPendingCount, ) }.collectLatest { _state.update { state -> state.copy( showShimmer = false, - outUiState = it + outUiState = it, ) } } @@ -169,19 +166,18 @@ class HomeViewModel: ViewModel(), KoinComponent { fun loadNightStudy() = viewModelScope.launch { _state.update { it.copy( - nightStudyUiState = NightStudyUiState.Loading + nightStudyUiState = NightStudyUiState.Loading, ) } combineWhenAllComplete( nightStudyRepository.getNightStudy(), - nightStudyRepository.getNightStudyPending() - ){ nightStudyFlow, nightStudyPendingFlow -> + nightStudyRepository.getNightStudyPending(), + ) { nightStudyFlow, nightStudyPendingFlow -> var activeCount = 0 var pendingCount = 0 when (nightStudyFlow) { - is Result.Success -> { activeCount = nightStudyFlow.data.size } @@ -205,13 +201,13 @@ class HomeViewModel: ViewModel(), KoinComponent { return@combineWhenAllComplete NightStudyUiState.Success( active = activeCount, - pending = pendingCount + pending = pendingCount, ) }.collectLatest { _state.update { state -> state.copy( showShimmer = false, - nightStudyUiState = it + nightStudyUiState = it, ) } } @@ -222,34 +218,34 @@ class HomeViewModel: ViewModel(), KoinComponent { val endDate = startDate.plus(DatePeriod(months = 1)) scheduleRepository.getScheduleBetweenPeriods( startAt = startDate, - endAt = endDate + endAt = endDate, ).collect { when (it) { is Result.Success -> { - _state.update { state -> + _state.update { state -> state.copy( showShimmer = false, - scheduleUiState = ScheduleUiState.Success(data = it.data) + scheduleUiState = ScheduleUiState.Success(data = it.data), ) } } is Result.Loading -> { _state.update { it.copy( - scheduleUiState = ScheduleUiState.Loading + scheduleUiState = ScheduleUiState.Loading, ) } } is Result.Error -> { it.error.printStackTrace() - _state.update { state -> + _state.update { state -> state.copy( showShimmer = false, - scheduleUiState = ScheduleUiState.Error + scheduleUiState = ScheduleUiState.Error, ) } } } } } -} \ No newline at end of file +} diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/BannerCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/BannerCard.kt index ca529d5d..e5c052cd 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/BannerCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/BannerCard.kt @@ -1,46 +1,25 @@ package com.b1nd.dodam.home.card import androidx.compose.foundation.ExperimentalFoundationApi -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.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.runtime.Composable import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.text.input.KeyboardType.Companion.Uri -import androidx.compose.ui.unit.dp -import coil3.ImageLoader -import coil3.PlatformContext -import coil3.annotation.ExperimentalCoilApi import coil3.compose.AsyncImage -import coil3.compose.LocalPlatformContext -import coil3.compose.rememberAsyncImagePainter -import coil3.memory.MemoryCache -import coil3.request.ImageRequest -import coil3.request.crossfade -import coil3.size.Size -import coil3.util.DebugLogger import com.b1nd.dodam.designsystem.DodamTheme -import com.b1nd.dodam.designsystem.foundation.DodamIcons import com.b1nd.dodam.home.model.BannerUiState -import com.b1nd.dodam.logging.KmLogging @OptIn(ExperimentalFoundationApi::class) @Composable -internal fun BannerCard( - state: BannerUiState -) { +internal fun BannerCard(state: BannerUiState) { when (state) { is BannerUiState.None -> {} is BannerUiState.Success -> { @@ -49,11 +28,10 @@ internal fun BannerCard( val urlHandler = LocalUriHandler.current Box( modifier = Modifier - .background(DodamTheme.colors.backgroundNeutral) + .background(DodamTheme.colors.backgroundNeutral), ) { - HorizontalPager( - state = bannerPagerState + state = bannerPagerState, ) { page -> AsyncImage( modifier = Modifier @@ -73,4 +51,4 @@ internal fun BannerCard( } } } -} \ No newline at end of file +} diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt index b42e3894..1be2252f 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/MealCard.kt @@ -7,18 +7,14 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material3.Text 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 @@ -37,15 +33,10 @@ import com.b1nd.dodam.ui.component.DodamContainer @OptIn(ExperimentalFoundationApi::class) @Composable -internal fun MealCard( - state: MealUiState, - showShimmer: Boolean, - onClickContent: () -> Unit, - onClickRefresh: () -> Unit -) { +internal fun MealCard(state: MealUiState, showShimmer: Boolean, onClickContent: () -> Unit, onClickRefresh: () -> Unit) { DodamContainer( icon = DodamIcons.ForkAndKnife, - title = "오늘의 급식" + title = "오늘의 급식", ) { if (!showShimmer) { when (state) { @@ -64,27 +55,27 @@ internal fun MealCard( .clickable( onClick = onClickContent, interactionSource = remember { MutableInteractionSource() }, - indication = rememberBounceIndication() - ) + indication = rememberBounceIndication(), + ), ) { index -> val meals = index.getMeal(state.data)?.details if (meals != null) { Column { - for (i in meals.indices step 2 ) { + for (i in meals.indices step 2) { Row( - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) { Text( modifier = Modifier.weight(1f), text = meals[i].name, style = DodamTheme.typography.body1Medium(), - color = DodamTheme.colors.labelNormal + color = DodamTheme.colors.labelNormal, ) Text( modifier = Modifier.weight(1f), - text = meals.getOrNull(i+1)?.name ?: "", + text = meals.getOrNull(i + 1)?.name ?: "", style = DodamTheme.typography.body1Medium(), - color = DodamTheme.colors.labelNormal + color = DodamTheme.colors.labelNormal, ) } } @@ -101,7 +92,7 @@ internal fun MealCard( modifier = Modifier .padding(end = 6.dp) .align(Alignment.End), - pagerState = mealState + pagerState = mealState, ) } } @@ -121,7 +112,7 @@ internal fun MealCard( DefaultText( onClick = onClickRefresh, label = "급식을 불러올 수 없어요", - body = "다시 불러오기" + body = "다시 불러오기", ) } } @@ -138,9 +129,8 @@ internal fun MealCard( } } -private fun Int.getMeal(meal: Meal): MealDetail? = - when (this) { - 0 -> meal.breakfast - 1 -> meal.lunch - else -> meal.dinner - } +private fun Int.getMeal(meal: Meal): MealDetail? = when (this) { + 0 -> meal.breakfast + 1 -> meal.lunch + else -> meal.dinner +} diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt index aad6e60b..47e4e370 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt @@ -2,19 +2,13 @@ package com.b1nd.dodam.home.card 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.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.b1nd.dodam.data.nightstudy.model.NightStudy -import com.b1nd.dodam.designsystem.DodamTheme import com.b1nd.dodam.designsystem.component.DodamLoadingDots import com.b1nd.dodam.designsystem.foundation.DodamIcons import com.b1nd.dodam.home.DefaultText @@ -23,15 +17,10 @@ import com.b1nd.dodam.home.model.NightStudyUiState import com.b1nd.dodam.ui.component.DodamContainer @Composable -internal fun NightStudyCard( - uiState: NightStudyUiState, - showShimmer: Boolean, - onRefreshClick: () -> Unit, - onContentClick: () -> Unit -) { +internal fun NightStudyCard(uiState: NightStudyUiState, showShimmer: Boolean, onRefreshClick: () -> Unit, onContentClick: () -> Unit) { DodamContainer( icon = DodamIcons.MoonPlus, - title = "심야 자습 현황" + title = "심야 자습 현황", ) { if (!showShimmer) { when (uiState) { @@ -39,13 +28,13 @@ internal fun NightStudyCard( Column( modifier = Modifier .padding(6.dp) - .fillMaxWidth() + .fillMaxWidth(), ) { InnerCountCard( title = "현재 자습중인 학생", content = "${uiState.active}명", buttonText = "${uiState.pending}명 대기중", - onClick = onContentClick + onClick = onContentClick, ) } } @@ -53,7 +42,7 @@ internal fun NightStudyCard( DefaultText( onClick = onRefreshClick, label = "심자 현황을 불러올 수 없어요", - body = "다시 불러오기" + body = "다시 불러오기", ) } is NightStudyUiState.Loading -> { diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt index d4f7c5bc..c5d778cf 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt @@ -2,18 +2,14 @@ package com.b1nd.dodam.home.card 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.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.b1nd.dodam.designsystem.DodamTheme import com.b1nd.dodam.designsystem.component.DodamLoadingDots import com.b1nd.dodam.designsystem.foundation.DodamIcons import com.b1nd.dodam.home.DefaultText @@ -22,16 +18,10 @@ import com.b1nd.dodam.home.model.OutUiState import com.b1nd.dodam.ui.component.DodamContainer @Composable -internal fun OutCard( - showShimmer: Boolean, - uiState: OutUiState, - onRefreshClick: () -> Unit, - onOutingClick: () -> Unit, - onSleepoverClick: () -> Unit -) { +internal fun OutCard(showShimmer: Boolean, uiState: OutUiState, onRefreshClick: () -> Unit, onOutingClick: () -> Unit, onSleepoverClick: () -> Unit) { DodamContainer( icon = DodamIcons.DoorOpen, - title = "외출/외박 현황" + title = "외출/외박 현황", ) { if (!showShimmer) { when (uiState) { @@ -39,20 +29,20 @@ internal fun OutCard( Column( modifier = Modifier .padding(6.dp) - .fillMaxWidth() + .fillMaxWidth(), ) { InnerCountCard( title = "현재 외출중인 학생", content = "${uiState.outAllowCount}명", buttonText = "${uiState.outPendingCount}명 대기중", - onClick = onOutingClick + onClick = onOutingClick, ) Spacer(modifier = Modifier.height(16.dp)) InnerCountCard( title = "현재 외박중인 학생", content = "${uiState.sleepoverAllowCount}명", buttonText = "${uiState.sleepoverPendingCount}명 대기중", - onClick = onSleepoverClick + onClick = onSleepoverClick, ) } } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/ScheduleCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/ScheduleCard.kt index 38019a0c..5d60cb10 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/ScheduleCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/ScheduleCard.kt @@ -17,8 +17,6 @@ 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.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -36,10 +34,8 @@ import com.b1nd.dodam.designsystem.animation.rememberBounceIndication import com.b1nd.dodam.designsystem.component.DodamLoadingDots import com.b1nd.dodam.designsystem.foundation.DodamIcons import com.b1nd.dodam.home.DefaultText -import com.b1nd.dodam.home.InnerCountCard import com.b1nd.dodam.home.model.ScheduleUiState import com.b1nd.dodam.ui.component.DodamContainer -import com.b1nd.dodam.ui.effect.shimmerEffect import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import kotlinx.datetime.DatePeriod @@ -47,12 +43,7 @@ import kotlinx.datetime.isoDayNumber import kotlinx.datetime.plus @Composable -internal fun ScheduleCard( - uiState: ScheduleUiState, - showShimmer: Boolean, - fetchSchedule: () -> Unit, - onContentClick: () -> Unit, -) { +internal fun ScheduleCard(uiState: ScheduleUiState, showShimmer: Boolean, fetchSchedule: () -> Unit, onContentClick: () -> Unit) { val current = DodamDate.now().date val tomorrow = current.plus(DatePeriod(days = 1)) @@ -106,7 +97,7 @@ internal fun ScheduleCard( "금", "토", "일", - )[if (current == latestSchedule) current.dayOfWeek.isoDayNumber-1 else latestSchedule.dayOfWeek.isoDayNumber-1] + "요일", + )[if (current == latestSchedule) current.dayOfWeek.isoDayNumber - 1 else latestSchedule.dayOfWeek.isoDayNumber - 1] + "요일", body = remember { schedules.filter { latestSchedule in it.date @@ -141,7 +132,7 @@ internal fun ScheduleCard( "금", "토", "일", - )[it.dayOfWeek.isoDayNumber-1] + "요일", + )[it.dayOfWeek.isoDayNumber - 1] + "요일", body = remember { schedules.filter { schedule -> it in schedule.date @@ -185,31 +176,25 @@ internal fun ScheduleCard( } } - @OptIn(ExperimentalFoundationApi::class) @Composable -private fun ScheduleComponent( - modifier: Modifier = Modifier, - day: String, - dayOfWeek: String, - body: ImmutableList -) { +private fun ScheduleComponent(modifier: Modifier = Modifier, day: String, dayOfWeek: String, body: ImmutableList) { Column( - modifier = modifier + modifier = modifier, ) { Row( - verticalAlignment = Alignment.Bottom + verticalAlignment = Alignment.Bottom, ) { Text( text = day, style = DodamTheme.typography.heading2Bold(), - color = DodamTheme.colors.labelNormal + color = DodamTheme.colors.labelNormal, ) Spacer(modifier = Modifier.width(4.dp)) Text( text = dayOfWeek, style = DodamTheme.typography.labelMedium(), - color = DodamTheme.colors.labelAlternative + color = DodamTheme.colors.labelAlternative, ) } Spacer(Modifier.height(8.dp)) @@ -242,7 +227,7 @@ private fun ScheduleComponent( modifier = Modifier.basicMarquee(), text = it.name, style = DodamTheme.typography.body1Medium().copy( - lineHeight = 19.sp + lineHeight = 19.sp, ), color = DodamTheme.colors.labelNeutral, ) @@ -250,4 +235,4 @@ private fun ScheduleComponent( Spacer(modifier = Modifier.height(8.dp)) } } -} \ No newline at end of file +} diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/di/ViewModelModule.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/di/ViewModelModule.kt index 900320d4..4b32a89d 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/di/ViewModelModule.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/di/ViewModelModule.kt @@ -2,9 +2,8 @@ package com.b1nd.dodam.home.di import com.b1nd.dodam.home.HomeViewModel import org.koin.compose.viewmodel.dsl.viewModel -import org.koin.compose.viewmodel.dsl.viewModelOf import org.koin.dsl.module val homeViewModelModule = module { viewModel { HomeViewModel() } -} \ No newline at end of file +} diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt index d0f04f77..7cc7d117 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/model/HomeUiState.kt @@ -2,11 +2,8 @@ package com.b1nd.dodam.home.model import com.b1nd.dodam.data.banner.model.Banner import com.b1nd.dodam.data.meal.model.Meal -import com.b1nd.dodam.data.nightstudy.model.NightStudy -import com.b1nd.dodam.data.outing.model.Outing import com.b1nd.dodam.data.schedule.model.Schedule import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.ImmutableMap data class HomeUiState( val showShimmer: Boolean = true, @@ -42,7 +39,7 @@ sealed interface OutUiState { sealed interface NightStudyUiState { data class Success( val active: Int, - val pending: Int + val pending: Int, ) : NightStudyUiState data object Loading : NightStudyUiState data object Error : NightStudyUiState diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/navigation/HomeNavigation.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/navigation/HomeNavigation.kt index 89059066..5646096c 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/navigation/HomeNavigation.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/navigation/HomeNavigation.kt @@ -10,8 +10,7 @@ import com.b1nd.dodam.home.HomeScreen const val HOME_ROUTE = "home" -fun NavController.navigateToHome(navOptions: NavOptions? = null) = - this.navigate(HOME_ROUTE) +fun NavController.navigateToHome(navOptions: NavOptions? = null) = this.navigate(HOME_ROUTE) fun NavGraphBuilder.homeScreen() { composable( @@ -23,4 +22,4 @@ fun NavGraphBuilder.homeScreen() { ) { HomeScreen() } -} \ No newline at end of file +} diff --git a/network/core/src/androidMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.android.kt b/network/core/src/androidMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.android.kt index 319cf8d2..e39e4b78 100644 --- a/network/core/src/androidMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.android.kt +++ b/network/core/src/androidMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.android.kt @@ -2,11 +2,9 @@ package com.b1nd.dodam.network.core.di import io.ktor.client.HttpClient import io.ktor.client.HttpClientConfig -import io.ktor.client.engine.HttpClientEngineConfig import io.ktor.client.engine.cio.CIO -internal actual fun getHttpClient(block: HttpClientConfig<*>.() -> Unit): HttpClient = - HttpClient( - engine = CIO.create(), - block = block - ) \ No newline at end of file +internal actual fun getHttpClient(block: HttpClientConfig<*>.() -> Unit): HttpClient = HttpClient( + engine = CIO.create(), + block = block, +) diff --git a/network/core/src/iosMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.ios.kt b/network/core/src/iosMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.ios.kt index 86376205..c2059944 100644 --- a/network/core/src/iosMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.ios.kt +++ b/network/core/src/iosMain/kotlin/com/b1nd/dodam/network/core/di/NetworkModule.ios.kt @@ -2,11 +2,9 @@ package com.b1nd.dodam.network.core.di import io.ktor.client.HttpClient import io.ktor.client.HttpClientConfig -import io.ktor.client.engine.HttpClientEngineConfig import io.ktor.client.engine.darwin.Darwin -internal actual fun getHttpClient(block: HttpClientConfig<*>.() -> Unit): HttpClient = - HttpClient( - engine = Darwin.create(), - block = block - ) \ No newline at end of file +internal actual fun getHttpClient(block: HttpClientConfig<*>.() -> Unit): HttpClient = HttpClient( + engine = Darwin.create(), + block = block, +) diff --git a/ui/src/commonMain/kotlin/com/b1nd/dodam/ui/component/DodamContainer.kt b/ui/src/commonMain/kotlin/com/b1nd/dodam/ui/component/DodamContainer.kt index a5501d0f..ab8a8d01 100644 --- a/ui/src/commonMain/kotlin/com/b1nd/dodam/ui/component/DodamContainer.kt +++ b/ui/src/commonMain/kotlin/com/b1nd/dodam/ui/component/DodamContainer.kt @@ -51,7 +51,7 @@ fun DodamContainer( Modifier.clickable( interactionSource = remember { MutableInteractionSource() }, indication = rememberBounceIndication(), - onClick = onNextClick?: {} + onClick = onNextClick ?: {}, ) } else { Modifier @@ -100,7 +100,7 @@ fun DodamContainer( } } Box( - modifier = Modifier.padding(horizontal = 6.dp) + modifier = Modifier.padding(horizontal = 6.dp), ) { content() } From b04e69e9f7a70dc63d82bfdf3152dad98e42b1f1 Mon Sep 17 00:00:00 2001 From: 8954sood <8954sood@naver.com> Date: Sat, 7 Sep 2024 18:44:51 +0900 Subject: [PATCH 24/24] feat: If count is zero then Home Inner Button Not Visible --- .../kotlin/com/b1nd/dodam/home/HomeScreen.kt | 20 ++++++++++--------- .../b1nd/dodam/home/card/NightStudyCard.kt | 1 + .../com/b1nd/dodam/home/card/OutCard.kt | 2 ++ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt index 7837262f..273faff2 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/HomeScreen.kt @@ -206,7 +206,7 @@ internal fun DefaultText(onClick: () -> Unit, label: String, body: String) { } @Composable -internal fun InnerCountCard(modifier: Modifier = Modifier, title: String, content: String, buttonText: String, onClick: () -> Unit) { +internal fun InnerCountCard(modifier: Modifier = Modifier, title: String, content: String, buttonText: String, showButton: Boolean, onClick: () -> Unit) { Row( modifier = modifier .fillMaxWidth(), @@ -224,13 +224,15 @@ internal fun InnerCountCard(modifier: Modifier = Modifier, title: String, conten color = DodamTheme.colors.labelNormal, ) } - Spacer(modifier = Modifier.weight(1f)) - Spacer(modifier = Modifier.width(8.dp)) - DodamButton( - text = buttonText, - onClick = onClick, - buttonRole = ButtonRole.Assistive, - buttonSize = ButtonSize.Small, - ) + if (showButton) { + Spacer(modifier = Modifier.weight(1f)) + Spacer(modifier = Modifier.width(8.dp)) + DodamButton( + text = buttonText, + onClick = onClick, + buttonRole = ButtonRole.Assistive, + buttonSize = ButtonSize.Small, + ) + } } } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt index 47e4e370..031441e7 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/NightStudyCard.kt @@ -34,6 +34,7 @@ internal fun NightStudyCard(uiState: NightStudyUiState, showShimmer: Boolean, on title = "현재 자습중인 학생", content = "${uiState.active}명", buttonText = "${uiState.pending}명 대기중", + showButton = uiState.pending != 0, onClick = onContentClick, ) } diff --git a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt index c5d778cf..09852a5d 100644 --- a/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt +++ b/feature-teacher/home/src/commonMain/kotlin/com/b1nd/dodam/home/card/OutCard.kt @@ -35,6 +35,7 @@ internal fun OutCard(showShimmer: Boolean, uiState: OutUiState, onRefreshClick: title = "현재 외출중인 학생", content = "${uiState.outAllowCount}명", buttonText = "${uiState.outPendingCount}명 대기중", + showButton = uiState.outPendingCount != 0, onClick = onOutingClick, ) Spacer(modifier = Modifier.height(16.dp)) @@ -42,6 +43,7 @@ internal fun OutCard(showShimmer: Boolean, uiState: OutUiState, onRefreshClick: title = "현재 외박중인 학생", content = "${uiState.sleepoverAllowCount}명", buttonText = "${uiState.sleepoverPendingCount}명 대기중", + showButton = uiState.sleepoverPendingCount != 0, onClick = onSleepoverClick, ) }