From 60a1160d8be4249066ba6afeb7c56c03c3c0b88e Mon Sep 17 00:00:00 2001 From: easyhz Date: Fri, 19 Jul 2024 16:26:03 +0900 Subject: [PATCH 01/19] =?UTF-8?q?fix:=20step=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/easyhz/noffice/core/common/util/Step.kt | 6 ++++++ .../noffice/feature/sign/contract/signUp/SignUpState.kt | 8 ++------ 2 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 core/common/src/main/java/com/easyhz/noffice/core/common/util/Step.kt diff --git a/core/common/src/main/java/com/easyhz/noffice/core/common/util/Step.kt b/core/common/src/main/java/com/easyhz/noffice/core/common/util/Step.kt new file mode 100644 index 00000000..94ccb12f --- /dev/null +++ b/core/common/src/main/java/com/easyhz/noffice/core/common/util/Step.kt @@ -0,0 +1,6 @@ +package com.easyhz.noffice.core.common.util + +data class Step( + val currentStep: T, + val previousStep: T? +) diff --git a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/signUp/SignUpState.kt b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/signUp/SignUpState.kt index 8f6857e8..829361c9 100644 --- a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/signUp/SignUpState.kt +++ b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/signUp/SignUpState.kt @@ -3,6 +3,7 @@ package com.easyhz.noffice.feature.sign.contract.signUp import androidx.compose.ui.text.input.TextFieldValue import com.easyhz.noffice.core.common.base.UiState import com.easyhz.noffice.core.common.extension.toEnumMap +import com.easyhz.noffice.core.common.util.Step import com.easyhz.noffice.feature.sign.util.signUp.SignUpStep import com.easyhz.noffice.feature.sign.util.signUp.Terms import com.easyhz.noffice.feature.sign.util.signUp.toEnabledStepButton @@ -10,7 +11,7 @@ import com.easyhz.noffice.feature.sign.util.signUp.toTermsMap import java.util.EnumMap data class SignUpState( - val step: Step, + val step: Step, val enabledStepButton: EnumMap, val isCheckedAllTerms: Boolean, val termsStatusMap: EnumMap, @@ -58,11 +59,6 @@ data class SignUpState( } } -data class Step( - val currentStep: SignUpStep, - val previousStep: SignUpStep? -) - internal fun EnumMap.updateStatus(key: SignUpStep, isEnabled: Boolean): EnumMap { this[key] = isEnabled return this From 88ee1cccec0e433b1702645c8d8fda83f20b3e22 Mon Sep 17 00:00:00 2001 From: easyhz Date: Fri, 19 Jul 2024 18:01:51 +0900 Subject: [PATCH 02/19] =?UTF-8?q?fix:=20bottom=20bar=20animation=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20scaffold=20navigation=20bar=20?= =?UTF-8?q?color=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/easyhz/noffice/NofficeApp.kt | 39 ++++++++++++++++--- .../noffice/navigation/NavController.kt | 4 +- .../noffice/navigation/util/Constant.kt | 4 ++ .../component/bottomBar/HomeBottomBar.kt | 2 +- .../component/scaffold/NofficeScaffold.kt | 11 ++++++ 5 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/com/easyhz/noffice/navigation/util/Constant.kt diff --git a/app/src/main/java/com/easyhz/noffice/NofficeApp.kt b/app/src/main/java/com/easyhz/noffice/NofficeApp.kt index 4313addd..964a76ce 100644 --- a/app/src/main/java/com/easyhz/noffice/NofficeApp.kt +++ b/app/src/main/java/com/easyhz/noffice/NofficeApp.kt @@ -1,5 +1,9 @@ package com.easyhz.noffice +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.tween +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.material3.FabPosition @@ -11,10 +15,12 @@ import com.easyhz.noffice.core.design_system.component.bottomBar.HomeBottomBar import com.easyhz.noffice.core.design_system.component.button.HomeAddButton import com.easyhz.noffice.core.design_system.component.scaffold.NofficeScaffold import com.easyhz.noffice.navigation.home.homeScreen +import com.easyhz.noffice.navigation.home.screen.Home +import com.easyhz.noffice.navigation.organization.navigateToOrganizationCreation import com.easyhz.noffice.navigation.organization.organizationScreen import com.easyhz.noffice.navigation.rememberNofficeNavController -import com.easyhz.noffice.navigation.sign.screen.SignUp import com.easyhz.noffice.navigation.sign.signScreen +import com.easyhz.noffice.navigation.util.BOTTOM_BAR_DURATION import com.easyhz.noffice.navigation.util.BottomMenuTabs @Composable @@ -27,7 +33,17 @@ fun NofficeApp() { NofficeScaffold( floatingActionButton = { - if(isVisibleBottomBar) { + AnimatedVisibility( + visible = isVisibleBottomBar, + enter = slideInVertically( + initialOffsetY = { it }, + animationSpec = tween(durationMillis = BOTTOM_BAR_DURATION) + ), + exit = slideOutVertically( + targetOffsetY = { it + 52 }, + animationSpec = tween(durationMillis = BOTTOM_BAR_DURATION) + ) + ) { HomeAddButton( modifier = Modifier.offset(y = 52.dp) ) { } @@ -35,7 +51,17 @@ fun NofficeApp() { }, floatingActionButtonPosition = FabPosition.Center, bottomBar = { - if(isVisibleBottomBar) { + AnimatedVisibility( + visible = isVisibleBottomBar, + enter = slideInVertically( + initialOffsetY = { it}, + animationSpec = tween(durationMillis = BOTTOM_BAR_DURATION) + ), + exit = slideOutVertically( + targetOffsetY = { it}, + animationSpec = tween(durationMillis = BOTTOM_BAR_DURATION) + ) + ) { HomeBottomBar( tabs = enumValues(), current = currentTab, @@ -45,10 +71,13 @@ fun NofficeApp() { } ) { NavHost( - navController = navController, startDestination = SignUp + navController = navController, startDestination = Home ) { homeScreen(modifier = Modifier.padding(it)) - organizationScreen(modifier = Modifier.padding(it)) + organizationScreen( + modifier = Modifier.padding(it), + navigateToCreation = navController::navigateToOrganizationCreation + ) signScreen() } } diff --git a/app/src/main/java/com/easyhz/noffice/navigation/NavController.kt b/app/src/main/java/com/easyhz/noffice/navigation/NavController.kt index 59770e45..39a52470 100644 --- a/app/src/main/java/com/easyhz/noffice/navigation/NavController.kt +++ b/app/src/main/java/com/easyhz/noffice/navigation/NavController.kt @@ -33,12 +33,12 @@ internal class NofficeNavController( fun isInBottomTabs(): Boolean = currentRoute in routes @Composable - fun mapRouteToTab(): BottomMenuTabs { + fun mapRouteToTab(): BottomMenuTabs? { return when (currentRoute) { Home::class.java.name -> BottomMenuTabs.HOME // Add::class.java.name -> BottomMenuTabs.ADD // FIXME Organization::class.java.name -> BottomMenuTabs.ORGANIZATION - else -> BottomMenuTabs.HOME + else -> null } } diff --git a/app/src/main/java/com/easyhz/noffice/navigation/util/Constant.kt b/app/src/main/java/com/easyhz/noffice/navigation/util/Constant.kt new file mode 100644 index 00000000..4e443e80 --- /dev/null +++ b/app/src/main/java/com/easyhz/noffice/navigation/util/Constant.kt @@ -0,0 +1,4 @@ +package com.easyhz.noffice.navigation.util + +const val DURATION = 500 +const val BOTTOM_BAR_DURATION = 300 \ No newline at end of file diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/bottomBar/HomeBottomBar.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/bottomBar/HomeBottomBar.kt index a208d661..5b9f2f6e 100644 --- a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/bottomBar/HomeBottomBar.kt +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/bottomBar/HomeBottomBar.kt @@ -28,7 +28,7 @@ import com.easyhz.noffice.core.design_system.util.bottomBar.BottomMenu @Composable fun HomeBottomBar( modifier: Modifier = Modifier, - current: T, + current: T?, tabs: Array, onClick: (T) -> Unit, ) where T: Enum, T: BottomMenu { diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/scaffold/NofficeScaffold.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/scaffold/NofficeScaffold.kt index 9d35955a..58ed2493 100644 --- a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/scaffold/NofficeScaffold.kt +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/scaffold/NofficeScaffold.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalView +import com.easyhz.noffice.core.design_system.theme.Grey50 import com.easyhz.noffice.core.design_system.theme.White @Composable @@ -28,6 +29,16 @@ fun NofficeScaffold( contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets, content: @Composable (PaddingValues) -> Unit ) { + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + updateWindowColors( + view = view, + statusBarColor = null, + navigationBarColor = Grey50 + ) + } + } Scaffold( modifier = modifier, topBar = topBar, From 3e7d69269c5a37a71c1e89c1c103f929eb98e610 Mon Sep 17 00:00:00 2001 From: easyhz Date: Fri, 19 Jul 2024 18:02:45 +0900 Subject: [PATCH 03/19] =?UTF-8?q?refactor:=20=EB=9D=84=EC=96=B4=EC=93=B0?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/easyhz/noffice/NofficeApp.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/easyhz/noffice/NofficeApp.kt b/app/src/main/java/com/easyhz/noffice/NofficeApp.kt index 964a76ce..1aefb3b5 100644 --- a/app/src/main/java/com/easyhz/noffice/NofficeApp.kt +++ b/app/src/main/java/com/easyhz/noffice/NofficeApp.kt @@ -54,11 +54,11 @@ fun NofficeApp() { AnimatedVisibility( visible = isVisibleBottomBar, enter = slideInVertically( - initialOffsetY = { it}, + initialOffsetY = { it }, animationSpec = tween(durationMillis = BOTTOM_BAR_DURATION) ), exit = slideOutVertically( - targetOffsetY = { it}, + targetOffsetY = { it }, animationSpec = tween(durationMillis = BOTTOM_BAR_DURATION) ) ) { From 4ed65349791c2e82087569389944bd6ffb815a52 Mon Sep 17 00:00:00 2001 From: easyhz Date: Fri, 19 Jul 2024 18:22:27 +0900 Subject: [PATCH 04/19] =?UTF-8?q?fix:=20step=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../easyhz/noffice/core/common/util/Step.kt | 17 +++++++++++++++ .../feature/sign/component/signUp/NameView.kt | 2 +- .../sign/contract/signUp/SignUpState.kt | 12 ++++------- .../sign/screen/signUp/SignUpViewModel.kt | 4 ++-- .../feature/sign/util/signUp/SignUpStep.kt | 21 +++++++++---------- 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/core/common/src/main/java/com/easyhz/noffice/core/common/util/Step.kt b/core/common/src/main/java/com/easyhz/noffice/core/common/util/Step.kt index 94ccb12f..bd83cd12 100644 --- a/core/common/src/main/java/com/easyhz/noffice/core/common/util/Step.kt +++ b/core/common/src/main/java/com/easyhz/noffice/core/common/util/Step.kt @@ -1,6 +1,23 @@ package com.easyhz.noffice.core.common.util +import java.util.EnumMap + data class Step( val currentStep: T, val previousStep: T? ) +interface StepRequired { + val isRequired: Boolean +} +inline fun List.toEnabledStepButton(): EnumMap where T: Enum, T: StepRequired = + EnumMap(T::class.java).apply { + this@toEnabledStepButton.forEach { step -> + this[step] = !step.isRequired + } + } + +fun EnumMap.updateStepButton(key: T, isEnabled: Boolean): EnumMap +where T: Enum { + this[key] = isEnabled + return this +} \ No newline at end of file diff --git a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/component/signUp/NameView.kt b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/component/signUp/NameView.kt index be320a0b..1823c894 100644 --- a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/component/signUp/NameView.kt +++ b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/component/signUp/NameView.kt @@ -47,7 +47,7 @@ internal fun NameView( placeholder = stringResource(id = R.string.sign_up_name_placeholder), isFilled = false, singleLine = true, - icon = null + icon = null // FIXME ) Spacer(modifier = Modifier .fillMaxWidth() diff --git a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/signUp/SignUpState.kt b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/signUp/SignUpState.kt index 829361c9..dd3d3d7e 100644 --- a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/signUp/SignUpState.kt +++ b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/signUp/SignUpState.kt @@ -4,9 +4,10 @@ import androidx.compose.ui.text.input.TextFieldValue import com.easyhz.noffice.core.common.base.UiState import com.easyhz.noffice.core.common.extension.toEnumMap import com.easyhz.noffice.core.common.util.Step +import com.easyhz.noffice.core.common.util.toEnabledStepButton +import com.easyhz.noffice.core.common.util.updateStepButton import com.easyhz.noffice.feature.sign.util.signUp.SignUpStep import com.easyhz.noffice.feature.sign.util.signUp.Terms -import com.easyhz.noffice.feature.sign.util.signUp.toEnabledStepButton import com.easyhz.noffice.feature.sign.util.signUp.toTermsMap import java.util.EnumMap @@ -44,7 +45,7 @@ data class SignUpState( return copy( termsStatusMap = newMap, isCheckedAllTerms = isCheckedAll, - enabledStepButton = enabledStepButton.updateStatus(step.currentStep, isEnabledButton) + enabledStepButton = enabledStepButton.updateStepButton(step.currentStep, isEnabledButton) ) } @@ -54,12 +55,7 @@ data class SignUpState( return copy( termsStatusMap = newMap, isCheckedAllTerms = !isCheckedAllTerms, - enabledStepButton = enabledStepButton.updateStatus(step.currentStep, isEnabledButton) + enabledStepButton = enabledStepButton.updateStepButton(step.currentStep, isEnabledButton) ) } -} - -internal fun EnumMap.updateStatus(key: SignUpStep, isEnabled: Boolean): EnumMap { - this[key] = isEnabled - return this } \ No newline at end of file diff --git a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/screen/signUp/SignUpViewModel.kt b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/screen/signUp/SignUpViewModel.kt index 64a641a4..973a7768 100644 --- a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/screen/signUp/SignUpViewModel.kt +++ b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/screen/signUp/SignUpViewModel.kt @@ -2,10 +2,10 @@ package com.easyhz.noffice.feature.sign.screen.signUp import androidx.compose.ui.text.input.TextFieldValue import com.easyhz.noffice.core.common.base.BaseViewModel +import com.easyhz.noffice.core.common.util.updateStepButton import com.easyhz.noffice.feature.sign.contract.signUp.SignUpIntent import com.easyhz.noffice.feature.sign.contract.signUp.SignUpSideEffect import com.easyhz.noffice.feature.sign.contract.signUp.SignUpState -import com.easyhz.noffice.feature.sign.contract.signUp.updateStatus import com.easyhz.noffice.feature.sign.util.signUp.Terms import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @@ -53,7 +53,7 @@ class SignUpViewModel @Inject constructor( private fun onChangeNameTextValue(newText: TextFieldValue) { val isEnabledButton = newText.text.isNotBlank() - reduce { copy(name = newText, enabledStepButton = enabledStepButton.updateStatus(step.currentStep, isEnabledButton)) } + reduce { copy(name = newText, enabledStepButton = enabledStepButton.updateStepButton(step.currentStep, isEnabledButton)) } } private fun onClearFocus() { diff --git a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/util/signUp/SignUpStep.kt b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/util/signUp/SignUpStep.kt index 5e97d736..9de29a39 100644 --- a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/util/signUp/SignUpStep.kt +++ b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/util/signUp/SignUpStep.kt @@ -1,20 +1,19 @@ package com.easyhz.noffice.feature.sign.util.signUp -import java.util.EnumMap +import com.easyhz.noffice.core.common.util.StepRequired -enum class SignUpStep { - TERMS, NAME; +enum class SignUpStep: StepRequired { + TERMS { + override val isRequired: Boolean + get() = true + }, NAME { + override val isRequired: Boolean + get() = true + }; fun nextStep(): SignUpStep? = entries.getOrNull(this.ordinal + 1) fun beforeStep(): SignUpStep? = entries.getOrNull(this.ordinal - 1) -} - -fun List.toEnabledStepButton(): EnumMap = - EnumMap(SignUpStep::class.java).apply { - this@toEnabledStepButton.forEach { step -> - this[step] = false - } - } \ No newline at end of file +} \ No newline at end of file From eee7a2ec260d7aa74d087c33941ec53a268dfad3 Mon Sep 17 00:00:00 2001 From: easyhz Date: Fri, 19 Jul 2024 18:23:09 +0900 Subject: [PATCH 05/19] =?UTF-8?q?feat:=20groupName=20view=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../organization/OrganizationNavigation.kt | 27 +++++- .../screen/OrganizationCreation.kt | 6 ++ .../noffice/core/design_system/theme/Type.kt | 13 +++ .../src/main/res/values/strings.xml | 3 + .../component/creation/CommonHeader.kt | 59 ++++++++++++ .../component/creation/GroupNameView.kt | 64 +++++++++++++ .../contract/creation/CreationIntent.kt | 12 +++ .../contract/creation/CreationSideEffect.kt | 7 ++ .../contract/creation/CreationState.kt | 31 +++++++ .../organization/OrganizationIntent.kt | 1 + .../creation/OrganizationCreationScreen.kt | 93 +++++++++++++++++++ .../creation/OrganizationCreationViewModel.kt | 64 +++++++++++++ .../util/creation/CreationStep.kt | 31 +++++++ 13 files changed, 409 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/easyhz/noffice/navigation/organization/screen/OrganizationCreation.kt create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/CommonHeader.kt create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/GroupNameView.kt create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationSideEffect.kt create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/util/creation/CreationStep.kt diff --git a/app/src/main/java/com/easyhz/noffice/navigation/organization/OrganizationNavigation.kt b/app/src/main/java/com/easyhz/noffice/navigation/organization/OrganizationNavigation.kt index 96d2911e..46767a6d 100644 --- a/app/src/main/java/com/easyhz/noffice/navigation/organization/OrganizationNavigation.kt +++ b/app/src/main/java/com/easyhz/noffice/navigation/organization/OrganizationNavigation.kt @@ -1,16 +1,35 @@ package com.easyhz.noffice.navigation.organization +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.core.tween import androidx.compose.ui.Modifier import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable +import com.easyhz.noffice.feature.organization.screen.creation.OrganizationCreationScreen import com.easyhz.noffice.feature.organization.screen.organization.OrganizationScreen import com.easyhz.noffice.navigation.organization.screen.Organization +import com.easyhz.noffice.navigation.organization.screen.OrganizationCreation +import com.easyhz.noffice.navigation.util.DURATION -internal fun NavGraphBuilder.organizationScreen(modifier: Modifier) { +internal fun NavGraphBuilder.organizationScreen( + modifier: Modifier, + navigateToCreation: () -> Unit +) { composable { - OrganizationScreen(modifier = modifier) + OrganizationScreen( + modifier = modifier, + navigateToCreation = navigateToCreation + ) + } + composable( + enterTransition = { slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, tween(DURATION)) }, + exitTransition = { slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start, tween(DURATION)) }, + popEnterTransition = { slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.End, tween(DURATION)) }, + popExitTransition = { slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, tween(DURATION)) } + ) { + OrganizationCreationScreen() } } @@ -19,4 +38,8 @@ internal fun NavController.navigateToOrganization(navOptions: NavOptions) { route = Organization, navOptions = navOptions ) +} + +internal fun NavController.navigateToOrganizationCreation() { + navigate(OrganizationCreation) } \ No newline at end of file diff --git a/app/src/main/java/com/easyhz/noffice/navigation/organization/screen/OrganizationCreation.kt b/app/src/main/java/com/easyhz/noffice/navigation/organization/screen/OrganizationCreation.kt new file mode 100644 index 00000000..91ec7969 --- /dev/null +++ b/app/src/main/java/com/easyhz/noffice/navigation/organization/screen/OrganizationCreation.kt @@ -0,0 +1,6 @@ +package com.easyhz.noffice.navigation.organization.screen + +import kotlinx.serialization.Serializable + +@Serializable +object OrganizationCreation \ No newline at end of file diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/theme/Type.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/theme/Type.kt index 9483bf81..27ab89f6 100644 --- a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/theme/Type.kt +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/theme/Type.kt @@ -45,6 +45,19 @@ val Title2 = TextStyle( includeFontPadding = false, ) ) + +val Title3 = TextStyle( + fontFamily = Pretendard, + fontWeight = FontWeight.SemiBold, + color = Grey800, + fontSize = 22.sp, + letterSpacing = LetterSpacing, + textAlign = TextAlign.Justify, + platformStyle = PlatformTextStyle( + includeFontPadding = false, + ) +) + val SubTitle1 = TextStyle( fontFamily = Pretendard, fontWeight = FontWeight.Medium, diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index 1fd7d6be..9990c8b7 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -60,4 +60,7 @@ 새로운 그룹 + 그룹 만들기 + 그룹 이름이 무엇인가요? + 이름을 입력해 주세요. \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/CommonHeader.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/CommonHeader.kt new file mode 100644 index 00000000..f38cf407 --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/CommonHeader.kt @@ -0,0 +1,59 @@ +package com.easyhz.noffice.feature.organization.component.creation + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.easyhz.noffice.core.design_system.R +import com.easyhz.noffice.core.design_system.theme.Green500 +import com.easyhz.noffice.core.design_system.theme.SemiBold16 +import com.easyhz.noffice.core.design_system.theme.Title3 + +@Composable +internal fun CommonHeader( + modifier: Modifier = Modifier, + title: String +) { + Column( + modifier = modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + Icon( + modifier = Modifier.size(18.dp), + painter = painterResource(id = R.drawable.ic_grid), + contentDescription = "grid", + tint = Green500 + ) + Text( + text = stringResource(id = R.string.organization_creation_title), + style = SemiBold16, + color = Green500 + ) + } + Text(text = title, style = Title3) + } +} + +@Preview(showBackground = true) +@Composable +private fun CommonHeaderPrev() { + CommonHeader( + modifier = Modifier.padding(vertical = 16.dp), + title = "그룹 이름 머임?" + ) +} \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/GroupNameView.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/GroupNameView.kt new file mode 100644 index 00000000..326157f5 --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/GroupNameView.kt @@ -0,0 +1,64 @@ +package com.easyhz.noffice.feature.organization.component.creation + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.easyhz.noffice.core.design_system.R +import com.easyhz.noffice.core.design_system.component.button.MediumButton +import com.easyhz.noffice.core.design_system.component.textField.MainTextField +import com.easyhz.noffice.core.design_system.extension.noRippleClickable +import com.easyhz.noffice.feature.organization.contract.creation.CreationIntent +import com.easyhz.noffice.feature.organization.contract.creation.CreationState.Companion.GROUP_NAME_MAX +import com.easyhz.noffice.feature.organization.screen.creation.OrganizationCreationViewModel + +@Composable +internal fun GroupNameView( + modifier: Modifier = Modifier, + viewModel: OrganizationCreationViewModel = hiltViewModel() +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + Column( + modifier = modifier + ) { + CommonHeader( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp, horizontal = 8.dp), + title = stringResource(id = R.string.organization_creation_name_title) + ) + MainTextField( + modifier = Modifier.weight(0.3f), + value = uiState.groupName, + onValueChange = { viewModel.postIntent(CreationIntent.ChangeGroupNameTextValue(it)) }, + title = null, + placeholder = stringResource(id = R.string.organization_creation_name_placeholder), + isFilled = false, + singleLine = true, + maxCount = GROUP_NAME_MAX, + onClickIcon = { viewModel.postIntent(CreationIntent.ClearGroupName) } + ) + Spacer(modifier = Modifier + .fillMaxWidth() + .weight(1f) + .noRippleClickable { viewModel.postIntent(CreationIntent.ClearFocus) } + ) + MediumButton( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp), + text = stringResource(id = R.string.sign_up_button), + enabled = uiState.enabledStepButton[uiState.step.currentStep] ?: false + ) { + viewModel.postIntent(CreationIntent.ClickNextButton) + } + } +} \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt new file mode 100644 index 00000000..0a85e0c7 --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt @@ -0,0 +1,12 @@ +package com.easyhz.noffice.feature.organization.contract.creation + +import androidx.compose.ui.text.input.TextFieldValue +import com.easyhz.noffice.core.common.base.UiIntent + +sealed class CreationIntent: UiIntent() { + data object ClickBackButton: CreationIntent() + data object ClickNextButton: CreationIntent() + data class ChangeGroupNameTextValue(val text: TextFieldValue): CreationIntent() + data object ClearGroupName: CreationIntent() + data object ClearFocus: CreationIntent() +} \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationSideEffect.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationSideEffect.kt new file mode 100644 index 00000000..6c6e3bb5 --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationSideEffect.kt @@ -0,0 +1,7 @@ +package com.easyhz.noffice.feature.organization.contract.creation + +import com.easyhz.noffice.core.common.base.UiSideEffect + +sealed class CreationSideEffect: UiSideEffect() { + data object ClearFocus: CreationSideEffect() +} \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt new file mode 100644 index 00000000..ca0fbcbd --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt @@ -0,0 +1,31 @@ +package com.easyhz.noffice.feature.organization.contract.creation + +import androidx.compose.ui.text.input.TextFieldValue +import com.easyhz.noffice.core.common.base.UiState +import com.easyhz.noffice.core.common.util.Step +import com.easyhz.noffice.core.common.util.toEnabledStepButton +import com.easyhz.noffice.feature.organization.util.creation.CreationStep +import java.util.EnumMap + +data class CreationState( + val step: Step, + val enabledStepButton: EnumMap, + val groupName: TextFieldValue +): UiState() { + companion object { + const val GROUP_NAME_MAX = 10 + fun init() = CreationState( + step = Step(currentStep = CreationStep.GROUP_NAME, previousStep = null), + enabledStepButton = CreationStep.entries.toEnabledStepButton(), + groupName = TextFieldValue("") + ) + } + + fun CreationState.updateStep(currentStep: CreationStep): CreationState = this.copy( + step = step.copy( + previousStep = step.currentStep, + currentStep = currentStep, + ), + ) + +} \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/organization/OrganizationIntent.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/organization/OrganizationIntent.kt index 33d5b3cf..0d7e934d 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/organization/OrganizationIntent.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/organization/OrganizationIntent.kt @@ -3,4 +3,5 @@ package com.easyhz.noffice.feature.organization.contract.organization import com.easyhz.noffice.core.common.base.UiIntent sealed class OrganizationIntent: UiIntent() { + data object ClickOrganizationCreation: OrganizationIntent() } \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt new file mode 100644 index 00000000..faeb810e --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt @@ -0,0 +1,93 @@ +package com.easyhz.noffice.feature.organization.screen.creation + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.SizeTransform +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideOutHorizontally +import androidx.compose.animation.togetherWith +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.easyhz.noffice.core.common.util.collectInSideEffectWithLifecycle +import com.easyhz.noffice.core.design_system.R +import com.easyhz.noffice.core.design_system.component.scaffold.NofficeBasicScaffold +import com.easyhz.noffice.core.design_system.component.scaffold.NofficeScaffold +import com.easyhz.noffice.core.design_system.component.topBar.DetailTopBar +import com.easyhz.noffice.core.design_system.extension.screenHorizonPadding +import com.easyhz.noffice.core.design_system.theme.Grey400 +import com.easyhz.noffice.core.design_system.theme.White +import com.easyhz.noffice.core.design_system.util.topBar.DetailTopBarMenu +import com.easyhz.noffice.feature.organization.component.creation.GroupNameView +import com.easyhz.noffice.feature.organization.contract.creation.CreationSideEffect +import com.easyhz.noffice.feature.organization.util.creation.CreationStep + +@Composable +fun OrganizationCreationScreen( + modifier: Modifier = Modifier, + viewModel: OrganizationCreationViewModel = hiltViewModel() +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val focusManager = LocalFocusManager.current + + NofficeBasicScaffold( + modifier = modifier, + containerColor = White, + topBar = { + DetailTopBar( + leadingItem = DetailTopBarMenu( + content = { + Icon( + modifier = Modifier.size(24.dp), + painter = painterResource(id = R.drawable.ic_chevron_left), + contentDescription = "left", + tint = Grey400 + ) + }, + onClick = { } + ) + ) + } + ) { + AnimatedContent( + modifier = Modifier + .fillMaxSize() + .padding(it), + targetState = uiState.step.currentStep, + transitionSpec = { + if (uiState.step.currentStep.ordinal >= (uiState.step.previousStep?.ordinal ?: 0)) { + (slideInHorizontally { width -> width } + fadeIn()).togetherWith( + slideOutHorizontally { width -> -width } + fadeOut()) + } else { + (slideInHorizontally { width -> -width } + fadeIn()).togetherWith( + slideOutHorizontally { width -> width } + fadeOut()) + }.using(SizeTransform(clip = false)) + }, label = "organizationCreationFlow" + ) { targetScreen -> + when(targetScreen) { + CreationStep.GROUP_NAME -> { GroupNameView(modifier = Modifier.screenHorizonPadding()) } + CreationStep.CATEGORY -> { } + CreationStep.IMAGE -> { } + CreationStep.END_DATE -> { } + CreationStep.PROMOTION -> { } + CreationStep.SUCCESS -> { } + } + } + } + + viewModel.sideEffect.collectInSideEffectWithLifecycle {sideEffect -> + when(sideEffect) { + is CreationSideEffect.ClearFocus -> { focusManager.clearFocus() } + } + } +} \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt new file mode 100644 index 00000000..b3c549b1 --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt @@ -0,0 +1,64 @@ +package com.easyhz.noffice.feature.organization.screen.creation + +import androidx.compose.ui.text.input.TextFieldValue +import com.easyhz.noffice.core.common.base.BaseViewModel +import com.easyhz.noffice.core.common.util.updateStepButton +import com.easyhz.noffice.feature.organization.contract.creation.CreationIntent +import com.easyhz.noffice.feature.organization.contract.creation.CreationSideEffect +import com.easyhz.noffice.feature.organization.contract.creation.CreationState +import com.easyhz.noffice.feature.organization.contract.creation.CreationState.Companion.GROUP_NAME_MAX +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class OrganizationCreationViewModel @Inject constructor( + +) : BaseViewModel( + initialState = CreationState.init() +) { + override fun handleIntent(intent: CreationIntent) { + when (intent) { + is CreationIntent.ClickBackButton -> { onClickBackButton() } + is CreationIntent.ClickNextButton -> { onClickNextButton() } + is CreationIntent.ChangeGroupNameTextValue -> { onChangeGroupNameTextValue(intent.text) } + is CreationIntent.ClearGroupName -> { onClearGroupName() } + is CreationIntent.ClearFocus -> { onClearFocus() } + } + } + + private fun onClickBackButton() { + currentState.step.currentStep.beforeStep()?.let { beforeStep -> + reduce { updateStep(currentStep = beforeStep) } + } // TODO NAVIGATE TO BACK + } + + private fun onClickNextButton() { + currentState.step.currentStep.nextStep()?.let { nextStep -> + reduce { updateStep(currentStep = nextStep) } + } ?: run { + /* TODO NEXT */ + } + } + + private fun onChangeGroupNameTextValue(newText: TextFieldValue) { + if (newText.text.length > GROUP_NAME_MAX) return + val isEnabledButton = newText.text.isNotBlank() + reduce { + copy( + groupName = newText, + enabledStepButton = enabledStepButton.updateStepButton( + step.currentStep, + isEnabledButton + ) + ) + } + } + + private fun onClearGroupName() { + reduce { copy(groupName = TextFieldValue("")) } + } + + private fun onClearFocus() { + postSideEffect { CreationSideEffect.ClearFocus } + } +} \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/util/creation/CreationStep.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/util/creation/CreationStep.kt new file mode 100644 index 00000000..a7a5b6a7 --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/util/creation/CreationStep.kt @@ -0,0 +1,31 @@ +package com.easyhz.noffice.feature.organization.util.creation + +import com.easyhz.noffice.core.common.util.StepRequired + +enum class CreationStep: StepRequired { + GROUP_NAME { + override val isRequired: Boolean + get() = true + }, CATEGORY { + override val isRequired: Boolean + get() = true + }, IMAGE { + override val isRequired: Boolean + get() = false + }, END_DATE { + override val isRequired: Boolean + get() = false + }, PROMOTION { + override val isRequired: Boolean + get() = false + }, SUCCESS { + override val isRequired: Boolean + get() = false + }; + + fun nextStep(): CreationStep? = + entries.getOrNull(this.ordinal + 1) + + fun beforeStep(): CreationStep? = + entries.getOrNull(this.ordinal - 1) +} \ No newline at end of file From 9e56b9b40fe43b094946c0ecdccd52c799e73304 Mon Sep 17 00:00:00 2001 From: easyhz Date: Fri, 19 Jul 2024 18:23:34 +0900 Subject: [PATCH 06/19] =?UTF-8?q?feat:=20creation=20=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../organization/OrganizationSideEffect.kt | 2 ++ .../screen/organization/OrganizationScreen.kt | 17 +++++++++++++---- .../organization/OrganizationViewModel.kt | 8 +++++++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/organization/OrganizationSideEffect.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/organization/OrganizationSideEffect.kt index 21c281b2..5aef5e21 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/organization/OrganizationSideEffect.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/organization/OrganizationSideEffect.kt @@ -3,4 +3,6 @@ package com.easyhz.noffice.feature.organization.contract.organization import com.easyhz.noffice.core.common.base.UiSideEffect sealed class OrganizationSideEffect: UiSideEffect() { + + data object NavigateToCreation: OrganizationSideEffect() } \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/organization/OrganizationScreen.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/organization/OrganizationScreen.kt index e69b9e08..b8e09d20 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/organization/OrganizationScreen.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/organization/OrganizationScreen.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.easyhz.noffice.core.common.util.collectInSideEffectWithLifecycle import com.easyhz.noffice.core.design_system.R import com.easyhz.noffice.core.design_system.component.button.IconMediumButton import com.easyhz.noffice.core.design_system.component.exception.ExceptionView @@ -21,17 +22,19 @@ import com.easyhz.noffice.core.design_system.component.topBar.HomeTopBar import com.easyhz.noffice.core.design_system.extension.screenHorizonPadding import com.easyhz.noffice.core.design_system.util.exception.ExceptionType import com.easyhz.noffice.feature.organization.component.organization.OrganizationItem +import com.easyhz.noffice.feature.organization.contract.organization.OrganizationIntent +import com.easyhz.noffice.feature.organization.contract.organization.OrganizationSideEffect import com.easyhz.noffice.feature.organization.util.OrganizationTopBarMenu @Composable fun OrganizationScreen( modifier: Modifier = Modifier, - viewModel: OrganizationViewModel = hiltViewModel() + viewModel: OrganizationViewModel = hiltViewModel(), + navigateToCreation: () -> Unit ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() NofficeScaffold( - modifier = modifier, topBar = { HomeTopBar( tabs = enumValues(), @@ -48,7 +51,7 @@ fun OrganizationScreen( ) } Column( - modifier = Modifier + modifier = modifier .padding(top = it.calculateTopPadding()) .screenHorizonPadding() ) { @@ -57,7 +60,7 @@ fun OrganizationScreen( text = stringResource(id = R.string.organization_new), iconId = R.drawable.ic_plus ) { - + viewModel.postIntent(OrganizationIntent.ClickOrganizationCreation) } LazyColumn( modifier = Modifier.fillMaxSize() @@ -75,4 +78,10 @@ fun OrganizationScreen( } } + + viewModel.sideEffect.collectInSideEffectWithLifecycle {sideEffect -> + when(sideEffect) { + is OrganizationSideEffect.NavigateToCreation -> { navigateToCreation() } + } + } } \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/organization/OrganizationViewModel.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/organization/OrganizationViewModel.kt index 3737379e..e5cdea6d 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/organization/OrganizationViewModel.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/organization/OrganizationViewModel.kt @@ -14,6 +14,12 @@ class OrganizationViewModel @Inject constructor( initialState = OrganizationState.init() ) { override fun handleIntent(intent: OrganizationIntent) { - TODO("Not yet implemented") + when(intent) { + is OrganizationIntent.ClickOrganizationCreation -> { onClickOrganizationCreation() } + } + } + + private fun onClickOrganizationCreation() { + postSideEffect { OrganizationSideEffect.NavigateToCreation } } } \ No newline at end of file From 27abcadf025169cf0a7bcd9f3e525357e5065e9a Mon Sep 17 00:00:00 2001 From: easyhz Date: Fri, 19 Jul 2024 18:38:14 +0900 Subject: [PATCH 07/19] =?UTF-8?q?fix:=20checkButton=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/button/CheckButton.kt | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/button/CheckButton.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/button/CheckButton.kt index 0d176e31..4ce74ff5 100644 --- a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/button/CheckButton.kt +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/button/CheckButton.kt @@ -52,7 +52,7 @@ fun CheckButton( .heightIn(min = 42.dp) .clip(RoundedCornerShape(8.dp)) .background(if (isComplete) color.completeContainerColor else color.incompleteContainerColor) - .padding(vertical = 10.dp) + .padding(vertical = 14.dp) .screenHorizonPadding(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(10.dp) @@ -63,11 +63,11 @@ fun CheckButton( color = if(isComplete) color.completeContentColor else color.incompleteContentColor, style = Body14 ) - if (isComplete || color.incompleteIconColor != null) { + if ((isComplete && color.completeIconColor != null) || color.incompleteIconColor != null) { Icon( painter = painterResource(id = R.drawable.ic_check), contentDescription = "check", - tint = if (isComplete) color.completeIconColor else color.incompleteIconColor ?: color.completeIconColor + tint = (if (isComplete) color.completeIconColor else color.incompleteIconColor) ?: color.completeContentColor ) } } @@ -79,7 +79,7 @@ fun CheckButton( data class CheckButtonDefaults( val completeContainerColor: Color, val completeContentColor: Color, - val completeIconColor: Color, + val completeIconColor: Color?, val incompleteContainerColor: Color, val incompleteContentColor: Color, val incompleteIconColor: Color?, @@ -187,3 +187,24 @@ private fun CheckButtonOrganizationCompletePrev() { } +@Preview(group = "checkButton", name = "organization - complete") +@Composable +private fun CheckButtonOrganizationCompletePrev2() { + CheckButton( + modifier = Modifier.width(300.dp), + text = "CMC 15th", + isComplete = true, + color = CheckButtonDefaults( + completeContainerColor = Green100, + completeContentColor = Green700, + completeIconColor = null, + incompleteContainerColor = Grey50, + incompleteContentColor = Grey600, + incompleteIconColor = null + ) + ) { + + } +} + + From cc0e90fb81243ac12af97dfaf2614014ecf4b2b2 Mon Sep 17 00:00:00 2001 From: easyhz Date: Fri, 19 Jul 2024 23:32:38 +0900 Subject: [PATCH 08/19] =?UTF-8?q?fix:=20textField=20=ED=8C=8C=EB=9D=BC?= =?UTF-8?q?=EB=AF=B8=ED=84=B0=20=ED=83=80=EC=9E=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/textField/MainTextField.kt | 16 ++++++++-------- .../feature/sign/contract/signUp/SignUpIntent.kt | 2 +- .../feature/sign/contract/signUp/SignUpState.kt | 4 ++-- .../sign/screen/signUp/SignUpViewModel.kt | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/textField/MainTextField.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/textField/MainTextField.kt index 2d8042d3..3dcf542f 100644 --- a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/textField/MainTextField.kt +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/textField/MainTextField.kt @@ -18,8 +18,8 @@ import com.easyhz.noffice.core.design_system.util.textField.getTextFieldState @Composable fun MainTextField( modifier: Modifier = Modifier, - value: TextFieldValue, - onValueChange: (TextFieldValue) -> Unit, + value: String, + onValueChange: (String) -> Unit, title: String?, placeholder: String, isFilled: Boolean, @@ -32,7 +32,7 @@ fun MainTextField( keyboardOptions: KeyboardOptions = KeyboardOptions.Default, keyboardActions: KeyboardActions = KeyboardActions.Default, ) { - val state = getTextFieldState(text = value.text, isFilled = isFilled) + val state = getTextFieldState(text = value, isFilled = isFilled) BasicTextField( value = value, @@ -57,7 +57,7 @@ fun MainTextField( onClickIcon = { onClickIcon() }, - textCount = value.text.length, + textCount = value.length, maxCount = maxCount, innerTextField = innerTextField ) @@ -69,7 +69,7 @@ fun MainTextField( @Composable private fun MainTextFieldPrev() { MainTextField( - value = TextFieldValue("내용이 잇음"), + value = "내용이 잇음", onValueChange = { }, title = null, placeholder = "내용으 입력", @@ -83,7 +83,7 @@ private fun MainTextFieldPrev() { @Composable private fun MainTextFieldPlaceholderPrev() { MainTextField( - value = TextFieldValue(""), + value = "", onValueChange = { }, title = null, placeholder = "내용dmf dlqfur 입력", @@ -97,7 +97,7 @@ private fun MainTextFieldPlaceholderPrev() { @Composable private fun MainTextFieldTitlePrev() { MainTextField( - value = TextFieldValue(""), + value = "", onValueChange = { }, title = "내용", placeholder = "내용dmf dlqfur 입력", @@ -111,7 +111,7 @@ private fun MainTextFieldTitlePrev() { @Composable private fun MainTextFieldMaxCountPrev() { MainTextField( - value = TextFieldValue(""), + value = "", onValueChange = { }, title = null, placeholder = "내용dmf dlqfur 입력", diff --git a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/signUp/SignUpIntent.kt b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/signUp/SignUpIntent.kt index 9f86d5d7..8cf8f990 100644 --- a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/signUp/SignUpIntent.kt +++ b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/signUp/SignUpIntent.kt @@ -11,6 +11,6 @@ sealed class SignUpIntent: UiIntent() { data object ClickTermsAllCheck: SignUpIntent() data class ClickTermsCheck(val terms: Terms): SignUpIntent() data class ClickTermsDetail(val terms: Terms): SignUpIntent() - data class ChangeNameTextValue(val text: TextFieldValue): SignUpIntent() + data class ChangeNameTextValue(val text: String): SignUpIntent() data object ClearFocus: SignUpIntent() } \ No newline at end of file diff --git a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/signUp/SignUpState.kt b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/signUp/SignUpState.kt index dd3d3d7e..631ed6ad 100644 --- a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/signUp/SignUpState.kt +++ b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/contract/signUp/SignUpState.kt @@ -16,7 +16,7 @@ data class SignUpState( val enabledStepButton: EnumMap, val isCheckedAllTerms: Boolean, val termsStatusMap: EnumMap, - val name: TextFieldValue, + val name: String, ) : UiState() { companion object { fun init() = SignUpState( @@ -24,7 +24,7 @@ data class SignUpState( enabledStepButton = SignUpStep.entries.toEnabledStepButton(), isCheckedAllTerms = false, termsStatusMap = Terms.entries.toTermsMap(), - name = TextFieldValue("") + name = "" ) } diff --git a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/screen/signUp/SignUpViewModel.kt b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/screen/signUp/SignUpViewModel.kt index 973a7768..1b11c6f1 100644 --- a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/screen/signUp/SignUpViewModel.kt +++ b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/screen/signUp/SignUpViewModel.kt @@ -51,8 +51,8 @@ class SignUpViewModel @Inject constructor( reduce { updateTermsCheck(terms) } } - private fun onChangeNameTextValue(newText: TextFieldValue) { - val isEnabledButton = newText.text.isNotBlank() + private fun onChangeNameTextValue(newText: String) { + val isEnabledButton = newText.isNotBlank() reduce { copy(name = newText, enabledStepButton = enabledStepButton.updateStepButton(step.currentStep, isEnabledButton)) } } From 4eb45addc0c9d0e693714fe01ef8b9525a8f3cdc Mon Sep 17 00:00:00 2001 From: easyhz Date: Fri, 19 Jul 2024 23:32:51 +0900 Subject: [PATCH 09/19] =?UTF-8?q?fix:=20checkButton=20=ED=8C=8C=EB=9D=BC?= =?UTF-8?q?=EB=AF=B8=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/design_system/component/button/CheckButton.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/button/CheckButton.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/button/CheckButton.kt index 4ce74ff5..6914b54b 100644 --- a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/button/CheckButton.kt +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/button/CheckButton.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.easyhz.noffice.core.design_system.R @@ -38,6 +39,7 @@ import com.easyhz.noffice.core.design_system.theme.Grey600 @Composable fun CheckButton( modifier: Modifier = Modifier, + textAlign: TextAlign = TextAlign.Start, text: String, isComplete: Boolean, color: CheckButtonDefaults = CheckButtonDefaults.default(), @@ -61,7 +63,8 @@ fun CheckButton( modifier = Modifier.weight(1f), text = text, color = if(isComplete) color.completeContentColor else color.incompleteContentColor, - style = Body14 + style = Body14, + textAlign = textAlign ) if ((isComplete && color.completeIconColor != null) || color.incompleteIconColor != null) { Icon( @@ -189,7 +192,7 @@ private fun CheckButtonOrganizationCompletePrev() { @Preview(group = "checkButton", name = "organization - complete") @Composable -private fun CheckButtonOrganizationCompletePrev2() { +private fun CheckButtonOrganizationCompleteNullPrev() { CheckButton( modifier = Modifier.width(300.dp), text = "CMC 15th", From 6716df4e403af7108f470772ec5c8bd6d588075d Mon Sep 17 00:00:00 2001 From: easyhz Date: Fri, 19 Jul 2024 23:49:56 +0900 Subject: [PATCH 10/19] =?UTF-8?q?feat:=20organization=20flow=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=B6=94=EA=B0=80=20#20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/res/values/strings.xml | 1 + .../component/creation/CategoryView.kt | 80 +++++++++++++++++++ .../contract/creation/CreationIntent.kt | 3 +- .../contract/creation/CreationState.kt | 28 ++++++- .../creation/OrganizationCreationScreen.kt | 12 ++- .../creation/OrganizationCreationViewModel.kt | 15 ++-- .../organization/util/creation/Category.kt | 14 ++++ 7 files changed, 140 insertions(+), 13 deletions(-) create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/CategoryView.kt create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/util/creation/Category.kt diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index 9990c8b7..e9f3d67b 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -63,4 +63,5 @@ 그룹 만들기 그룹 이름이 무엇인가요? 이름을 입력해 주세요. + 그룹의 카테고리를 모두 선택해 주세요 \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/CategoryView.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/CategoryView.kt new file mode 100644 index 00000000..3454705a --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/CategoryView.kt @@ -0,0 +1,80 @@ +package com.easyhz.noffice.feature.organization.component.creation + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.itemsIndexed +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.easyhz.noffice.core.design_system.R +import com.easyhz.noffice.core.design_system.component.button.CheckButton +import com.easyhz.noffice.core.design_system.component.button.CheckButtonDefaults +import com.easyhz.noffice.core.design_system.component.button.MediumButton +import com.easyhz.noffice.core.design_system.theme.Green100 +import com.easyhz.noffice.core.design_system.theme.Green700 +import com.easyhz.noffice.core.design_system.theme.Grey50 +import com.easyhz.noffice.core.design_system.theme.Grey600 +import com.easyhz.noffice.feature.organization.contract.creation.CreationIntent +import com.easyhz.noffice.feature.organization.screen.creation.OrganizationCreationViewModel + +@Composable +internal fun CategoryView( + modifier: Modifier = Modifier, + viewModel: OrganizationCreationViewModel = hiltViewModel() +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + Column( + modifier = modifier + ) { + CommonHeader( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp, horizontal = 8.dp), + title = stringResource(id = R.string.organization_creation_category_title) + ) + LazyVerticalGrid( + modifier = Modifier.weight(1f).padding(top = 8.dp), + columns = GridCells.Fixed(2), + verticalArrangement = Arrangement.spacedBy(10.dp), + horizontalArrangement = Arrangement.spacedBy(7.dp) + ) { + itemsIndexed(uiState.category) { index, item -> + CheckButton( + modifier = Modifier.weight(1f), + textAlign = TextAlign.Center, + text = item.title, + isComplete = item.isSelected, + color = CheckButtonDefaults( + completeContainerColor = Green100, + completeContentColor = Green700, + completeIconColor = null, + incompleteContainerColor = Grey50, + incompleteContentColor = Grey600, + incompleteIconColor = null + ) + ) { + viewModel.postIntent(CreationIntent.ClickCategoryItem(index)) + } + } + } + MediumButton( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp), + text = stringResource(id = R.string.sign_up_button), + enabled = uiState.enabledStepButton[uiState.step.currentStep] ?: false + ) { + viewModel.postIntent(CreationIntent.ClickNextButton) + } + } +} \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt index 0a85e0c7..67c8a174 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt @@ -6,7 +6,8 @@ import com.easyhz.noffice.core.common.base.UiIntent sealed class CreationIntent: UiIntent() { data object ClickBackButton: CreationIntent() data object ClickNextButton: CreationIntent() - data class ChangeGroupNameTextValue(val text: TextFieldValue): CreationIntent() + data class ChangeGroupNameTextValue(val text: String): CreationIntent() data object ClearGroupName: CreationIntent() data object ClearFocus: CreationIntent() + data class ClickCategoryItem(val selectedIndex: Int): CreationIntent() } \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt index ca0fbcbd..50b2a902 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt @@ -1,23 +1,27 @@ package com.easyhz.noffice.feature.organization.contract.creation -import androidx.compose.ui.text.input.TextFieldValue import com.easyhz.noffice.core.common.base.UiState +import com.easyhz.noffice.core.common.extension.toEnumMap import com.easyhz.noffice.core.common.util.Step import com.easyhz.noffice.core.common.util.toEnabledStepButton +import com.easyhz.noffice.feature.organization.util.creation.Category import com.easyhz.noffice.feature.organization.util.creation.CreationStep +import com.easyhz.noffice.feature.organization.util.creation.toState import java.util.EnumMap data class CreationState( val step: Step, val enabledStepButton: EnumMap, - val groupName: TextFieldValue + val groupName: String, + val category: List ): UiState() { companion object { const val GROUP_NAME_MAX = 10 fun init() = CreationState( step = Step(currentStep = CreationStep.GROUP_NAME, previousStep = null), enabledStepButton = CreationStep.entries.toEnabledStepButton(), - groupName = TextFieldValue("") + groupName = "", + category = Category.toState() ) } @@ -28,4 +32,20 @@ data class CreationState( ), ) -} \ No newline at end of file + fun CreationState.updateCategoryItem(selectedIndex: Int): CreationState { + val updatedCategory = category.mapIndexed { index, categoryState -> + categoryState.copy(isSelected = categoryState.isSelected.xor(index == selectedIndex)) + } + return copy( + category = updatedCategory, + enabledStepButton = enabledStepButton.toMutableMap().apply { + this[step.currentStep] = updatedCategory.any { it.isSelected } + }.toEnumMap() + ) + } +} + +data class CategoryState( + val title: String, + val isSelected: Boolean +) \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt index faeb810e..51f4253b 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt @@ -1,5 +1,6 @@ package com.easyhz.noffice.feature.organization.screen.creation +import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedContent import androidx.compose.animation.SizeTransform import androidx.compose.animation.fadeIn @@ -22,13 +23,14 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.easyhz.noffice.core.common.util.collectInSideEffectWithLifecycle import com.easyhz.noffice.core.design_system.R import com.easyhz.noffice.core.design_system.component.scaffold.NofficeBasicScaffold -import com.easyhz.noffice.core.design_system.component.scaffold.NofficeScaffold import com.easyhz.noffice.core.design_system.component.topBar.DetailTopBar import com.easyhz.noffice.core.design_system.extension.screenHorizonPadding import com.easyhz.noffice.core.design_system.theme.Grey400 import com.easyhz.noffice.core.design_system.theme.White import com.easyhz.noffice.core.design_system.util.topBar.DetailTopBarMenu +import com.easyhz.noffice.feature.organization.component.creation.CategoryView import com.easyhz.noffice.feature.organization.component.creation.GroupNameView +import com.easyhz.noffice.feature.organization.contract.creation.CreationIntent import com.easyhz.noffice.feature.organization.contract.creation.CreationSideEffect import com.easyhz.noffice.feature.organization.util.creation.CreationStep @@ -40,6 +42,10 @@ fun OrganizationCreationScreen( val uiState by viewModel.uiState.collectAsStateWithLifecycle() val focusManager = LocalFocusManager.current + BackHandler(onBack = { + viewModel.postIntent(CreationIntent.ClickBackButton) + }) + NofficeBasicScaffold( modifier = modifier, containerColor = White, @@ -54,7 +60,7 @@ fun OrganizationCreationScreen( tint = Grey400 ) }, - onClick = { } + onClick = { viewModel.postIntent(CreationIntent.ClickBackButton) } ) ) } @@ -76,7 +82,7 @@ fun OrganizationCreationScreen( ) { targetScreen -> when(targetScreen) { CreationStep.GROUP_NAME -> { GroupNameView(modifier = Modifier.screenHorizonPadding()) } - CreationStep.CATEGORY -> { } + CreationStep.CATEGORY -> { CategoryView(modifier = Modifier.screenHorizonPadding()) } CreationStep.IMAGE -> { } CreationStep.END_DATE -> { } CreationStep.PROMOTION -> { } diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt index b3c549b1..06bda81c 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt @@ -1,6 +1,5 @@ package com.easyhz.noffice.feature.organization.screen.creation -import androidx.compose.ui.text.input.TextFieldValue import com.easyhz.noffice.core.common.base.BaseViewModel import com.easyhz.noffice.core.common.util.updateStepButton import com.easyhz.noffice.feature.organization.contract.creation.CreationIntent @@ -23,6 +22,7 @@ class OrganizationCreationViewModel @Inject constructor( is CreationIntent.ChangeGroupNameTextValue -> { onChangeGroupNameTextValue(intent.text) } is CreationIntent.ClearGroupName -> { onClearGroupName() } is CreationIntent.ClearFocus -> { onClearFocus() } + is CreationIntent.ClickCategoryItem -> { onClickCategoryItem(intent.selectedIndex) } } } @@ -35,14 +35,15 @@ class OrganizationCreationViewModel @Inject constructor( private fun onClickNextButton() { currentState.step.currentStep.nextStep()?.let { nextStep -> reduce { updateStep(currentStep = nextStep) } + postSideEffect { CreationSideEffect.ClearFocus } } ?: run { /* TODO NEXT */ } } - private fun onChangeGroupNameTextValue(newText: TextFieldValue) { - if (newText.text.length > GROUP_NAME_MAX) return - val isEnabledButton = newText.text.isNotBlank() + private fun onChangeGroupNameTextValue(newText: String) { + if (newText.length > GROUP_NAME_MAX) return + val isEnabledButton = newText.isNotBlank() reduce { copy( groupName = newText, @@ -55,10 +56,14 @@ class OrganizationCreationViewModel @Inject constructor( } private fun onClearGroupName() { - reduce { copy(groupName = TextFieldValue("")) } + reduce { copy(groupName = "") } } private fun onClearFocus() { postSideEffect { CreationSideEffect.ClearFocus } } + + private fun onClickCategoryItem(selectedIndex: Int) { + reduce { updateCategoryItem(selectedIndex) } + } } \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/util/creation/Category.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/util/creation/Category.kt new file mode 100644 index 00000000..c55a5ad9 --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/util/creation/Category.kt @@ -0,0 +1,14 @@ +package com.easyhz.noffice.feature.organization.util.creation + +import com.easyhz.noffice.feature.organization.contract.creation.CategoryState + +//enum class Category { +//} + +val Category = listOf("IT 계열", "문화 생활", "어학", "예술", "음악 · 공연", "스터디 · 연구", "스포츠", "창업", "종교", "마케팅 · 홍보", "자연과학", "기타") + +fun List.toState(): List = map { + CategoryState( + it, false + ) +} \ No newline at end of file From 0f935ecbf0ad2bab96bf159f41f027ccefa64907 Mon Sep 17 00:00:00 2001 From: easyhz Date: Sat, 20 Jul 2024 00:28:26 +0900 Subject: [PATCH 11/19] =?UTF-8?q?feat:=20organization=20flow=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20=EC=B6=94=EA=B0=80=20#20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/exception/ExceptionView.kt | 6 +- .../image/OrganizationCreationImageView.kt | 28 +++++ .../component/image/OrganizationImage.kt | 2 +- .../util/exception/ExceptionType.kt | 6 +- .../src/main/res/drawable/ic_upload.xml | 27 +++++ .../src/main/res/values/strings.xml | 5 +- .../component/creation/ImageView.kt | 103 ++++++++++++++++++ ...oupNameView.kt => OrganizationNameView.kt} | 12 +- .../contract/creation/CreationIntent.kt | 8 +- .../contract/creation/CreationSideEffect.kt | 1 + .../contract/creation/CreationState.kt | 15 ++- .../creation/OrganizationCreationScreen.kt | 18 ++- .../creation/OrganizationCreationViewModel.kt | 28 +++-- .../screen/organization/OrganizationScreen.kt | 2 +- .../util/creation/CreationStep.kt | 2 +- 15 files changed, 226 insertions(+), 37 deletions(-) create mode 100644 core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/image/OrganizationCreationImageView.kt create mode 100644 core/design-system/src/main/res/drawable/ic_upload.xml create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/ImageView.kt rename feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/{GroupNameView.kt => OrganizationNameView.kt} (91%) diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/exception/ExceptionView.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/exception/ExceptionView.kt index 2fd47be0..00131425 100644 --- a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/exception/ExceptionView.kt +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/exception/ExceptionView.kt @@ -32,7 +32,7 @@ fun ExceptionView( ) { Image( painter = painterResource(id = type.resId), - contentDescription = "no_group" + contentDescription = "no_organization" ) Column(modifier = Modifier .padding(top = 10.dp) @@ -59,10 +59,10 @@ fun ExceptionView( @Preview @Composable -private fun NoGroupPrev() { +private fun NoOrganizationPrev() { ExceptionView( modifier = Modifier.fillMaxSize(), - type = ExceptionType.NO_GROUP + type = ExceptionType.NO_ORGANIZATION ) } diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/image/OrganizationCreationImageView.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/image/OrganizationCreationImageView.kt new file mode 100644 index 00000000..9c56669a --- /dev/null +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/image/OrganizationCreationImageView.kt @@ -0,0 +1,28 @@ +package com.easyhz.noffice.core.design_system.component.image + +import android.net.Uri +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import com.bumptech.glide.integration.compose.CrossFade +import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi +import com.bumptech.glide.integration.compose.GlideImage +import com.bumptech.glide.integration.compose.placeholder +import com.easyhz.noffice.core.design_system.R + +@OptIn(ExperimentalGlideComposeApi::class) +@Composable +fun OrganizationCreationImageView( + modifier: Modifier = Modifier, + image: Uri +) { + GlideImage( + modifier = modifier, + model = image, + contentDescription = image.toString(), + loading = placeholder(R.drawable.ic_profile_group), + failure = placeholder(R.drawable.ic_profile_group), + contentScale = ContentScale.Crop, + transition = CrossFade, + ) +} \ No newline at end of file diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/image/OrganizationImage.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/image/OrganizationImage.kt index 15af88e7..758029f2 100644 --- a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/image/OrganizationImage.kt +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/image/OrganizationImage.kt @@ -26,7 +26,7 @@ fun OrganizationImage( Image( modifier = modifier.clip(CircleShape), painter = painterResource(id = R.drawable.ic_profile_group), - contentDescription = "group_profile", + contentDescription = "organization_profile", contentScale = ContentScale.Crop ) } else { diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/exception/ExceptionType.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/exception/ExceptionType.kt index 9bf942ff..d78ba4bc 100644 --- a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/exception/ExceptionType.kt +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/exception/ExceptionType.kt @@ -9,10 +9,10 @@ enum class ExceptionType( @StringRes val titleStringId: Int, @StringRes val subTitleStringId: Int ) { - NO_GROUP( + NO_ORGANIZATION( resId = R.drawable.ic_empty_no_group, - titleStringId = R.string.exception_no_group_title, - subTitleStringId = R.string.exception_no_group_sub_title, + titleStringId = R.string.exception_no_organization_title, + subTitleStringId = R.string.exception_no_organization_sub_title, ), NO_TASK( resId = R.drawable.ic_empty_no_task, titleStringId = R.string.exception_no_task_title, diff --git a/core/design-system/src/main/res/drawable/ic_upload.xml b/core/design-system/src/main/res/drawable/ic_upload.xml new file mode 100644 index 00000000..c9f7e82e --- /dev/null +++ b/core/design-system/src/main/res/drawable/ic_upload.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index e9f3d67b..9a20e3b1 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -39,10 +39,10 @@ 앞으로 어떤 즐거운 이벤트가 있을까요? - + 참여한 그룹이 없어요 - + 그룹에 참여해서 공지를 확인해 보세요! @@ -64,4 +64,5 @@ 그룹 이름이 무엇인가요? 이름을 입력해 주세요. 그룹의 카테고리를 모두 선택해 주세요 + 대표 이미지를 설정해 주세요 \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/ImageView.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/ImageView.kt new file mode 100644 index 00000000..8baf2194 --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/ImageView.kt @@ -0,0 +1,103 @@ +package com.easyhz.noffice.feature.organization.component.creation + +import android.net.Uri +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.easyhz.noffice.core.design_system.R +import com.easyhz.noffice.core.design_system.component.button.MediumButton +import com.easyhz.noffice.core.design_system.component.image.OrganizationCreationImageView +import com.easyhz.noffice.core.design_system.extension.noRippleClickable +import com.easyhz.noffice.core.design_system.theme.Grey300 +import com.easyhz.noffice.core.design_system.theme.Grey50 +import com.easyhz.noffice.feature.organization.contract.creation.CreationIntent +import com.easyhz.noffice.feature.organization.screen.creation.OrganizationCreationViewModel + +@Composable +internal fun ImageView( + modifier: Modifier = Modifier, + viewModel: OrganizationCreationViewModel = hiltViewModel() +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + Column( + modifier = modifier + ) { + CommonHeader( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp, horizontal = 8.dp), + title = stringResource(id = R.string.organization_creation_image_title) + ) + Box( + modifier = Modifier.padding(vertical = 28.dp) + .size(280.dp) + .align(Alignment.CenterHorizontally) + .clip(RoundedCornerShape(24.dp)) + .background(Grey50) + .noRippleClickable { viewModel.postIntent(CreationIntent.ClickImageView) } + ) { + when (uiState.organizationImage) { + Uri.EMPTY -> { + EmptyImageView( + modifier = Modifier.align(Alignment.Center) + ) + } + else -> { + OrganizationCreationImageView( + modifier = Modifier.size(280.dp), + image = uiState.organizationImage + ) + } + } + } + Spacer( + modifier = Modifier + .fillMaxWidth() + .weight(1f) + ) + MediumButton( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp), + text = stringResource(id = R.string.sign_up_button), + enabled = uiState.enabledStepButton[uiState.step.currentStep] ?: false + ) { + viewModel.postIntent(CreationIntent.ClickNextButton) + } + } +} + +@Composable +private fun EmptyImageView( + modifier: Modifier = Modifier, +) { + Box( + modifier = modifier + ) { + Icon( + modifier = Modifier + .align(Alignment.Center) + .size(36.dp), + painter = painterResource(id = R.drawable.ic_upload), + contentDescription = "upload", + tint = Grey300 + ) + } +} \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/GroupNameView.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/OrganizationNameView.kt similarity index 91% rename from feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/GroupNameView.kt rename to feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/OrganizationNameView.kt index 326157f5..d5542d24 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/GroupNameView.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/OrganizationNameView.kt @@ -16,11 +16,11 @@ import com.easyhz.noffice.core.design_system.component.button.MediumButton import com.easyhz.noffice.core.design_system.component.textField.MainTextField import com.easyhz.noffice.core.design_system.extension.noRippleClickable import com.easyhz.noffice.feature.organization.contract.creation.CreationIntent -import com.easyhz.noffice.feature.organization.contract.creation.CreationState.Companion.GROUP_NAME_MAX +import com.easyhz.noffice.feature.organization.contract.creation.CreationState.Companion.ORGANIZATION_NAME_MAX import com.easyhz.noffice.feature.organization.screen.creation.OrganizationCreationViewModel @Composable -internal fun GroupNameView( +internal fun OrganizationNameView( modifier: Modifier = Modifier, viewModel: OrganizationCreationViewModel = hiltViewModel() ) { @@ -37,14 +37,14 @@ internal fun GroupNameView( ) MainTextField( modifier = Modifier.weight(0.3f), - value = uiState.groupName, - onValueChange = { viewModel.postIntent(CreationIntent.ChangeGroupNameTextValue(it)) }, + value = uiState.organizationName, + onValueChange = { viewModel.postIntent(CreationIntent.ChangeOrganizationNameTextValue(it)) }, title = null, placeholder = stringResource(id = R.string.organization_creation_name_placeholder), isFilled = false, singleLine = true, - maxCount = GROUP_NAME_MAX, - onClickIcon = { viewModel.postIntent(CreationIntent.ClearGroupName) } + maxCount = ORGANIZATION_NAME_MAX, + onClickIcon = { viewModel.postIntent(CreationIntent.ClearOrganizationName) } ) Spacer(modifier = Modifier .fillMaxWidth() diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt index 67c8a174..78c0e1c8 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt @@ -1,13 +1,15 @@ package com.easyhz.noffice.feature.organization.contract.creation -import androidx.compose.ui.text.input.TextFieldValue +import android.net.Uri import com.easyhz.noffice.core.common.base.UiIntent sealed class CreationIntent: UiIntent() { data object ClickBackButton: CreationIntent() data object ClickNextButton: CreationIntent() - data class ChangeGroupNameTextValue(val text: String): CreationIntent() - data object ClearGroupName: CreationIntent() + data class ChangeOrganizationNameTextValue(val text: String): CreationIntent() + data object ClearOrganizationName: CreationIntent() data object ClearFocus: CreationIntent() data class ClickCategoryItem(val selectedIndex: Int): CreationIntent() + data object ClickImageView : CreationIntent() + data class PickImage(val uri: Uri?) : CreationIntent() } \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationSideEffect.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationSideEffect.kt index 6c6e3bb5..778b9af0 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationSideEffect.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationSideEffect.kt @@ -4,4 +4,5 @@ import com.easyhz.noffice.core.common.base.UiSideEffect sealed class CreationSideEffect: UiSideEffect() { data object ClearFocus: CreationSideEffect() + data object NavigateToGallery: CreationSideEffect() } \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt index 50b2a902..e7391b08 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt @@ -1,5 +1,6 @@ package com.easyhz.noffice.feature.organization.contract.creation +import android.net.Uri import com.easyhz.noffice.core.common.base.UiState import com.easyhz.noffice.core.common.extension.toEnumMap import com.easyhz.noffice.core.common.util.Step @@ -12,16 +13,18 @@ import java.util.EnumMap data class CreationState( val step: Step, val enabledStepButton: EnumMap, - val groupName: String, - val category: List + val organizationName: String, + val category: List, + val organizationImage: Uri ): UiState() { companion object { - const val GROUP_NAME_MAX = 10 + const val ORGANIZATION_NAME_MAX = 10 fun init() = CreationState( - step = Step(currentStep = CreationStep.GROUP_NAME, previousStep = null), + step = Step(currentStep = CreationStep.ORGANIZATION_NAME, previousStep = null), enabledStepButton = CreationStep.entries.toEnabledStepButton(), - groupName = "", - category = Category.toState() + organizationName = "", + category = Category.toState(), + organizationImage = Uri.EMPTY ) } diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt index 51f4253b..3d4bd0f7 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt @@ -1,6 +1,9 @@ package com.easyhz.noffice.feature.organization.screen.creation import androidx.activity.compose.BackHandler +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.PickVisualMediaRequest +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.AnimatedContent import androidx.compose.animation.SizeTransform import androidx.compose.animation.fadeIn @@ -29,7 +32,8 @@ import com.easyhz.noffice.core.design_system.theme.Grey400 import com.easyhz.noffice.core.design_system.theme.White import com.easyhz.noffice.core.design_system.util.topBar.DetailTopBarMenu import com.easyhz.noffice.feature.organization.component.creation.CategoryView -import com.easyhz.noffice.feature.organization.component.creation.GroupNameView +import com.easyhz.noffice.feature.organization.component.creation.OrganizationNameView +import com.easyhz.noffice.feature.organization.component.creation.ImageView import com.easyhz.noffice.feature.organization.contract.creation.CreationIntent import com.easyhz.noffice.feature.organization.contract.creation.CreationSideEffect import com.easyhz.noffice.feature.organization.util.creation.CreationStep @@ -41,6 +45,11 @@ fun OrganizationCreationScreen( ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() val focusManager = LocalFocusManager.current + val galleryLauncher = + rememberLauncherForActivityResult( + contract = ActivityResultContracts.PickVisualMedia(), + onResult = { viewModel.postIntent(CreationIntent.PickImage(it)) } + ) BackHandler(onBack = { viewModel.postIntent(CreationIntent.ClickBackButton) @@ -81,9 +90,9 @@ fun OrganizationCreationScreen( }, label = "organizationCreationFlow" ) { targetScreen -> when(targetScreen) { - CreationStep.GROUP_NAME -> { GroupNameView(modifier = Modifier.screenHorizonPadding()) } + CreationStep.ORGANIZATION_NAME -> { OrganizationNameView(modifier = Modifier.screenHorizonPadding()) } CreationStep.CATEGORY -> { CategoryView(modifier = Modifier.screenHorizonPadding()) } - CreationStep.IMAGE -> { } + CreationStep.IMAGE -> { ImageView(modifier = Modifier.screenHorizonPadding()) } CreationStep.END_DATE -> { } CreationStep.PROMOTION -> { } CreationStep.SUCCESS -> { } @@ -94,6 +103,9 @@ fun OrganizationCreationScreen( viewModel.sideEffect.collectInSideEffectWithLifecycle {sideEffect -> when(sideEffect) { is CreationSideEffect.ClearFocus -> { focusManager.clearFocus() } + is CreationSideEffect.NavigateToGallery -> { + galleryLauncher.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) + } } } } \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt index 06bda81c..d92a28df 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt @@ -1,11 +1,12 @@ package com.easyhz.noffice.feature.organization.screen.creation +import android.net.Uri import com.easyhz.noffice.core.common.base.BaseViewModel import com.easyhz.noffice.core.common.util.updateStepButton import com.easyhz.noffice.feature.organization.contract.creation.CreationIntent import com.easyhz.noffice.feature.organization.contract.creation.CreationSideEffect import com.easyhz.noffice.feature.organization.contract.creation.CreationState -import com.easyhz.noffice.feature.organization.contract.creation.CreationState.Companion.GROUP_NAME_MAX +import com.easyhz.noffice.feature.organization.contract.creation.CreationState.Companion.ORGANIZATION_NAME_MAX import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @@ -19,10 +20,12 @@ class OrganizationCreationViewModel @Inject constructor( when (intent) { is CreationIntent.ClickBackButton -> { onClickBackButton() } is CreationIntent.ClickNextButton -> { onClickNextButton() } - is CreationIntent.ChangeGroupNameTextValue -> { onChangeGroupNameTextValue(intent.text) } - is CreationIntent.ClearGroupName -> { onClearGroupName() } + is CreationIntent.ChangeOrganizationNameTextValue -> { onChangeOrganizationNameTextValue(intent.text) } + is CreationIntent.ClearOrganizationName -> { onClearOrganizationName() } is CreationIntent.ClearFocus -> { onClearFocus() } is CreationIntent.ClickCategoryItem -> { onClickCategoryItem(intent.selectedIndex) } + is CreationIntent.ClickImageView -> { onClickImageView() } + is CreationIntent.PickImage -> { onPickImage(intent.uri) } } } @@ -41,12 +44,12 @@ class OrganizationCreationViewModel @Inject constructor( } } - private fun onChangeGroupNameTextValue(newText: String) { - if (newText.length > GROUP_NAME_MAX) return + private fun onChangeOrganizationNameTextValue(newText: String) { + if (newText.length > ORGANIZATION_NAME_MAX) return val isEnabledButton = newText.isNotBlank() reduce { copy( - groupName = newText, + organizationName = newText, enabledStepButton = enabledStepButton.updateStepButton( step.currentStep, isEnabledButton @@ -55,8 +58,8 @@ class OrganizationCreationViewModel @Inject constructor( } } - private fun onClearGroupName() { - reduce { copy(groupName = "") } + private fun onClearOrganizationName() { + reduce { copy(organizationName = "") } } private fun onClearFocus() { @@ -66,4 +69,13 @@ class OrganizationCreationViewModel @Inject constructor( private fun onClickCategoryItem(selectedIndex: Int) { reduce { updateCategoryItem(selectedIndex) } } + + private fun onClickImageView() { + postSideEffect { CreationSideEffect.NavigateToGallery } + } + private fun onPickImage(uri: Uri?) { + uri?.let { + reduce { copy(organizationImage = it) } + } + } } \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/organization/OrganizationScreen.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/organization/OrganizationScreen.kt index b8e09d20..aea1ce31 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/organization/OrganizationScreen.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/organization/OrganizationScreen.kt @@ -47,7 +47,7 @@ fun OrganizationScreen( if(uiState.organizationList.isEmpty()) { ExceptionView( modifier = Modifier.fillMaxSize(), - type = ExceptionType.NO_GROUP + type = ExceptionType.NO_ORGANIZATION ) } Column( diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/util/creation/CreationStep.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/util/creation/CreationStep.kt index a7a5b6a7..f5614590 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/util/creation/CreationStep.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/util/creation/CreationStep.kt @@ -3,7 +3,7 @@ package com.easyhz.noffice.feature.organization.util.creation import com.easyhz.noffice.core.common.util.StepRequired enum class CreationStep: StepRequired { - GROUP_NAME { + ORGANIZATION_NAME { override val isRequired: Boolean get() = true }, CATEGORY { From 719f24999d63bed565550da0bcb031e2122a6ff8 Mon Sep 17 00:00:00 2001 From: easyhz Date: Sat, 20 Jul 2024 00:41:39 +0900 Subject: [PATCH 12/19] =?UTF-8?q?fix:=20terms=20view=20=EC=95=A0=EB=8B=88?= =?UTF-8?q?=EB=A9=94=EC=9D=B4=EC=85=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sign/component/signUp/TermsView.kt | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/component/signUp/TermsView.kt b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/component/signUp/TermsView.kt index bad2726d..600de8fb 100644 --- a/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/component/signUp/TermsView.kt +++ b/feature/sign/src/main/java/com/easyhz/noffice/feature/sign/component/signUp/TermsView.kt @@ -1,5 +1,6 @@ package com.easyhz.noffice.feature.sign.component.signUp +import androidx.compose.animation.Crossfade import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -114,14 +115,19 @@ private fun TermsCheck( modifier = Modifier.padding(vertical = 16.dp) ) { Box(modifier = Modifier.size(32.dp).noRippleClickable { onClickAllCheck() }) { - Image( - modifier = Modifier - .align(Alignment.TopStart) - .padding(top = 3.dp) - .size(18.dp), - painter = painterResource(id = allCheckIconId), - contentDescription = "check" - ) + Crossfade( + modifier = Modifier.align(Alignment.TopStart), + targetState = allCheckIconId, + label = "Check" + ) { iconId -> + Image( + modifier = Modifier + .padding(top = 3.dp) + .size(18.dp), + painter = painterResource(id = iconId), + contentDescription = "check" + ) + } } Column( verticalArrangement = Arrangement.spacedBy(8.dp) @@ -173,13 +179,18 @@ private fun TermsItem( verticalAlignment = Alignment.CenterVertically ) { Box(modifier = Modifier.size(32.dp).noRippleClickable { onClickCheck() }) { - Image( - modifier = Modifier - .align(Alignment.CenterStart) - .size(18.dp), - painter = painterResource(id = checkedIconId), - contentDescription = "check" - ) + Crossfade( + modifier = Modifier.align(Alignment.CenterStart), + targetState = checkedIconId, + label = "Check" + ) { iconId -> + Image( + modifier = Modifier + .size(18.dp), + painter = painterResource(id = iconId), + contentDescription = "check" + ) + } } Row( modifier = Modifier.noRippleClickable { onClickDetail() }, From fe75fefca89e59ee16024bd042adc1dcf341e597 Mon Sep 17 00:00:00 2001 From: easyhz Date: Sat, 20 Jul 2024 00:45:25 +0900 Subject: [PATCH 13/19] =?UTF-8?q?chore:=20calendar=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/design-system/build.gradle.kts | 3 +++ gradle/libs.versions.toml | 2 ++ 2 files changed, 5 insertions(+) diff --git a/core/design-system/build.gradle.kts b/core/design-system/build.gradle.kts index f4118276..39664e44 100644 --- a/core/design-system/build.gradle.kts +++ b/core/design-system/build.gradle.kts @@ -13,4 +13,7 @@ dependencies { // Glide implementation(libs.glide) + + // Calendar + implementation(libs.calendar.compose) } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d57b5cf4..e2857a08 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,6 +26,7 @@ retrofit = "2.9.0" okhttp = "4.12.0" serialization = "1.6.3" glide = "1.0.0-beta01" +calendar = "2.6.0-beta01" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -65,6 +66,7 @@ retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.r retrofit-gson-converter = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization"} glide = { group = "com.github.bumptech.glide", name = "compose", version.ref = "glide" } +calendar-compose = { group = "com.kizitonwose.calendar", name = "compose", version.ref = "calendar" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } From e287e268878dc2eb6b28c8f78b5d001dfa0f92bb Mon Sep 17 00:00:00 2001 From: easyhz Date: Sat, 20 Jul 2024 13:51:04 +0900 Subject: [PATCH 14/19] =?UTF-8?q?feat:=20=EA=B7=B8=EB=A3=B9=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=ED=94=8C=EB=A1=9C=EC=9A=B0=20=EC=A2=85=EB=A3=8C=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=20#20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/calendar/MonthCalendarView.kt | 132 ++++++++++++++++++ .../component/calendar/MonthDay.kt | 55 ++++++++ .../component/calendar/MonthHeader.kt | 53 +++++++ .../core/design_system/extension/Modifier.kt | 5 +- .../noffice/core/design_system/theme/Color.kt | 5 +- .../design_system/util/calendar/Constant.kt | 8 ++ .../util/calendar/DisplayText.kt | 16 +++ .../util/calendar/MonthCalendar.kt | 77 ++++++++++ .../src/main/res/values/strings.xml | 1 + .../component/creation/EndDateView.kt | 58 ++++++++ .../contract/creation/CreationIntent.kt | 2 + .../contract/creation/CreationState.kt | 7 +- .../creation/OrganizationCreationScreen.kt | 3 +- .../creation/OrganizationCreationViewModel.kt | 7 + 14 files changed, 424 insertions(+), 5 deletions(-) create mode 100644 core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/calendar/MonthCalendarView.kt create mode 100644 core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/calendar/MonthDay.kt create mode 100644 core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/calendar/MonthHeader.kt create mode 100644 core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/calendar/Constant.kt create mode 100644 core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/calendar/DisplayText.kt create mode 100644 core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/calendar/MonthCalendar.kt create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/EndDateView.kt diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/calendar/MonthCalendarView.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/calendar/MonthCalendarView.kt new file mode 100644 index 00000000..4a834bac --- /dev/null +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/calendar/MonthCalendarView.kt @@ -0,0 +1,132 @@ +package com.easyhz.noffice.core.design_system.component.calendar + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.Stable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.easyhz.noffice.core.design_system.theme.Grey700 +import com.easyhz.noffice.core.design_system.theme.SubBody14 +import com.easyhz.noffice.core.design_system.util.calendar.RANGE_MONTH +import com.easyhz.noffice.core.design_system.util.calendar.displayText +import com.easyhz.noffice.core.design_system.util.calendar.rememberFirstMostVisibleMonth +import com.kizitonwose.calendar.compose.CalendarState +import com.kizitonwose.calendar.compose.HorizontalCalendar +import com.kizitonwose.calendar.compose.rememberCalendarState +import com.kizitonwose.calendar.core.OutDateStyle +import com.kizitonwose.calendar.core.daysOfWeek +import com.kizitonwose.calendar.core.firstDayOfWeekFromLocale +import com.kizitonwose.calendar.core.yearMonth +import kotlinx.coroutines.launch +import java.time.LocalDate + +@Composable +fun MonthCalendarView( + modifier: Modifier = Modifier, + selection: LocalDate, + calendarPadding: Dp, + onChangeDate: (LocalDate) -> Unit +) { + val scope = rememberCoroutineScope() + val today = remember { LocalDate.now() } + val startMonth = remember { today.yearMonth } + val endMonth = remember { today.yearMonth.plusMonths(RANGE_MONTH) } + val monthState = rememberCalendarState( + startMonth = startMonth, + endMonth = endMonth, + firstDayOfWeek = firstDayOfWeekFromLocale(), + outDateStyle = OutDateStyle.EndOfRow, + ) + val title = rememberFirstMostVisibleMonth(state = monthState) + + LaunchedEffect(selection) { + if(title.yearMonth == selection.yearMonth) return@LaunchedEffect + monthState.animateScrollToMonth(selection.yearMonth) + } + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(24.dp) + ) { + MonthHeader( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.CenterHorizontally), + title = title.yearMonth.displayText(), + onClickBefore = { + scope.launch { + monthState.animateScrollToMonth(title.yearMonth.minusMonths(1)) + } + }, + onClickNext = { + scope.launch { + monthState.animateScrollToMonth(title.yearMonth.plusMonths(1)) + } + } + ) + MonthCalendarContent( + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(horizontal = calendarPadding), + monthState = monthState, + selection = selection, + today = today, + onChangeDate = onChangeDate + ) + } +} + +@Composable +fun MonthCalendarContent( + modifier: Modifier = Modifier, + monthState: CalendarState, + selection: LocalDate, + today: LocalDate, + onChangeDate: (LocalDate) -> Unit +) { + HorizontalCalendar( + modifier = modifier, + state = monthState, + monthHeader = { CalendarHeader() }, + dayContent = {day -> + Day( + day = day, + isSelected = selection == day.date, + today = today, + ) { clickedDay -> + onChangeDate(clickedDay) + } + }, + ) +} + +@Stable +@Composable +internal fun CalendarHeader() { + val daysOfWeek = remember { daysOfWeek() } + Row( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 8.dp), + ) { + for (dayOfWeek in daysOfWeek) { + Text( + modifier = Modifier.weight(1f), + text = dayOfWeek.displayText(), + style = SubBody14, + color = Grey700, + textAlign = TextAlign.Center + ) + } + } +} \ No newline at end of file diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/calendar/MonthDay.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/calendar/MonthDay.kt new file mode 100644 index 00000000..1baace03 --- /dev/null +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/calendar/MonthDay.kt @@ -0,0 +1,55 @@ +package com.easyhz.noffice.core.design_system.component.calendar + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.easyhz.noffice.core.design_system.extension.noRippleClickable +import com.easyhz.noffice.core.design_system.theme.SemiBold14 +import com.easyhz.noffice.core.design_system.util.calendar.dateFormatter +import com.easyhz.noffice.core.design_system.util.calendar.getSelectionBoxColor +import com.easyhz.noffice.core.design_system.util.calendar.getTextColor +import com.kizitonwose.calendar.core.CalendarDay +import java.time.LocalDate + +@Composable +internal fun Day( + modifier: Modifier = Modifier, + day: CalendarDay, + today: LocalDate, + isSelected: Boolean, + onClick: (LocalDate) -> Unit +) { + val enabled = day.date >= today + val selectionBoxColor = getSelectionBoxColor(isSelected) + val textColor = getTextColor(day, isSelected, enabled) + Box( + modifier = modifier + .fillMaxWidth() + .noRippleClickable(enabled = enabled) { onClick(day.date) }, + contentAlignment = Alignment.TopCenter, + ) { + Box(modifier = Modifier.padding(bottom = 6.dp) + .size(40.dp) + .clip(RoundedCornerShape(8.dp)) + .background(selectionBoxColor), + ) { + Text( + modifier = Modifier.align(Alignment.TopCenter).padding(top = 4.dp), + text = dateFormatter.format(day.date), + style = SemiBold14, + textAlign = TextAlign.Center, + color = textColor + ) + } + } +} \ No newline at end of file diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/calendar/MonthHeader.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/calendar/MonthHeader.kt new file mode 100644 index 00000000..9a150335 --- /dev/null +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/calendar/MonthHeader.kt @@ -0,0 +1,53 @@ +package com.easyhz.noffice.core.design_system.component.calendar + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import com.easyhz.noffice.core.design_system.R +import com.easyhz.noffice.core.design_system.extension.noRippleClickable +import com.easyhz.noffice.core.design_system.theme.Grey500 +import com.easyhz.noffice.core.design_system.theme.InputDialogTitle + +@Composable +internal fun MonthHeader( + modifier: Modifier = Modifier, + title: String, + onClickBefore: () -> Unit, + onClickNext: () -> Unit +) { + Row( + modifier = modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Box(modifier = Modifier.size(32.dp).noRippleClickable { onClickBefore() }) { + Icon( + modifier = Modifier.align(Alignment.CenterStart).size(24.dp), + painter = painterResource(id = R.drawable.ic_chevron_left), + contentDescription = "before", + tint = Grey500 + ) + } + Text( + text = title, + style = InputDialogTitle + ) + Box(modifier = Modifier.size(32.dp).noRippleClickable { onClickNext() }) { + Icon( + modifier = Modifier.align(Alignment.CenterEnd).size(24.dp), + painter = painterResource(id = R.drawable.ic_chevron_right), + contentDescription = "next", + tint = Grey500 + ) + } + } +} \ No newline at end of file diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/extension/Modifier.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/extension/Modifier.kt index ce2a0ae7..49b8af55 100644 --- a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/extension/Modifier.kt +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/extension/Modifier.kt @@ -15,9 +15,12 @@ fun Modifier.screenHorizonPadding(): Modifier = padding(horizontal = 16.dp) inline fun Modifier.noRippleClickable( interactionSource: MutableInteractionSource? = null, + enabled: Boolean = true, crossinline onClick: () -> Unit, ): Modifier = composed { - clickable(indication = null, + clickable( + indication = null, + enabled = enabled, interactionSource = interactionSource ?: remember { MutableInteractionSource() }) { onClick() } diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/theme/Color.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/theme/Color.kt index 159b190b..6856a841 100644 --- a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/theme/Color.kt +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/theme/Color.kt @@ -84,4 +84,7 @@ val Blue600 = Color(0xFF00A0EB) val Blue700 = Color(0xFF007DB8) @Stable -val DimColor = Color(0xFF121212).copy(alpha = 0.5f) \ No newline at end of file +val DimColor = Color(0xFF121212).copy(alpha = 0.5f) + +@Stable +val Red = Color(0xFFFF5757) \ No newline at end of file diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/calendar/Constant.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/calendar/Constant.kt new file mode 100644 index 00000000..be9ea889 --- /dev/null +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/calendar/Constant.kt @@ -0,0 +1,8 @@ +package com.easyhz.noffice.core.design_system.util.calendar + +internal const val CONTENT_SIZE = 40 +internal const val NUM_OF_DAYS_IN_WEEK = 7 +internal const val PADDING_NUM_OF_DAYS_IN_WEEK = NUM_OF_DAYS_IN_WEEK * 2 +internal const val WITHOUT_PADDING_NUM = PADDING_NUM_OF_DAYS_IN_WEEK - 2 +fun getCalendarPadding(target: Int, screenWidth: Int) = + (((PADDING_NUM_OF_DAYS_IN_WEEK * target) + (NUM_OF_DAYS_IN_WEEK * CONTENT_SIZE) - screenWidth) / WITHOUT_PADDING_NUM).coerceAtLeast(0) \ No newline at end of file diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/calendar/DisplayText.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/calendar/DisplayText.kt new file mode 100644 index 00000000..1f83d1ca --- /dev/null +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/calendar/DisplayText.kt @@ -0,0 +1,16 @@ +package com.easyhz.noffice.core.design_system.util.calendar + +import java.time.DayOfWeek +import java.time.YearMonth +import java.time.format.DateTimeFormatter +import java.time.format.TextStyle +import java.util.Locale + +internal val dateFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("d") + +internal fun YearMonth.displayText(): String = + "${year}.${month.value}" +internal fun DayOfWeek.displayText(uppercase: Boolean = false): String = + getDisplayName(TextStyle.SHORT, Locale.KOREA).let { value -> + if (uppercase) value.uppercase(Locale.KOREA) else value + } \ No newline at end of file diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/calendar/MonthCalendar.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/calendar/MonthCalendar.kt new file mode 100644 index 00000000..134cc9b1 --- /dev/null +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/util/calendar/MonthCalendar.kt @@ -0,0 +1,77 @@ +package com.easyhz.noffice.core.design_system.util.calendar + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.snapshotFlow +import androidx.compose.ui.graphics.Color +import com.easyhz.noffice.core.design_system.theme.Green500 +import com.easyhz.noffice.core.design_system.theme.Grey300 +import com.easyhz.noffice.core.design_system.theme.Grey700 +import com.easyhz.noffice.core.design_system.theme.Red +import com.easyhz.noffice.core.design_system.theme.White +import com.kizitonwose.calendar.compose.CalendarLayoutInfo +import com.kizitonwose.calendar.compose.CalendarState +import com.kizitonwose.calendar.core.CalendarDay +import com.kizitonwose.calendar.core.CalendarMonth +import com.kizitonwose.calendar.core.DayPosition +import com.kizitonwose.calendar.core.Week +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filterNotNull +import java.time.DayOfWeek + +const val RANGE_MONTH = 500L + + +/** + * 스크롤 감지하고 해당 월 반환 + * + * @param state HorizontalCalendar 의 상태인 [CalendarState] + * @param viewportPercent 넘어오는 정도 default : 50f + * + * @return 해당 주 [Week] + */ +@Composable +internal fun rememberFirstMostVisibleMonth( + state: CalendarState, + viewportPercent: Float = 50f, +): CalendarMonth { + val visibleMonth = remember(state) { mutableStateOf(state.firstVisibleMonth) } + LaunchedEffect(state) { + snapshotFlow { state.layoutInfo.firstMostVisibleMonth(viewportPercent) } + .distinctUntilChanged() + .filterNotNull() + .collect { month -> visibleMonth.value = month } + } + return visibleMonth.value +} + + +private fun CalendarLayoutInfo.firstMostVisibleMonth(viewportPercent: Float = 50f): CalendarMonth? { + return if (visibleMonthsInfo.isEmpty()) { + null + } else { + val viewportSize = (viewportEndOffset + viewportStartOffset) * viewportPercent / 100f + visibleMonthsInfo.firstOrNull { itemInfo -> + if (itemInfo.offset < 0) { + itemInfo.offset + itemInfo.size >= viewportSize + } else { + itemInfo.size - itemInfo.offset >= viewportSize + } + }?.month + } +} + +internal fun getSelectionBoxColor(isSelected: Boolean): Color { + return if (isSelected) Green500 else White +} + +internal fun getTextColor(day: CalendarDay, isSelected: Boolean, enabled: Boolean): Color { + return when { + isSelected -> White + !enabled || day.position != DayPosition.MonthDate -> Grey300 + day.date.dayOfWeek == DayOfWeek.SUNDAY -> Red + else -> Grey700 + } +} \ No newline at end of file diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index 9a20e3b1..cc0f917d 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -65,4 +65,5 @@ 이름을 입력해 주세요. 그룹의 카테고리를 모두 선택해 주세요 대표 이미지를 설정해 주세요 + 활동 종료 날짜가 언제인가요? \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/EndDateView.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/EndDateView.kt new file mode 100644 index 00000000..6c3b6a74 --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/EndDateView.kt @@ -0,0 +1,58 @@ +package com.easyhz.noffice.feature.organization.component.creation + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.easyhz.noffice.core.design_system.R +import com.easyhz.noffice.core.design_system.component.button.MediumButton +import com.easyhz.noffice.core.design_system.component.calendar.MonthCalendarView +import com.easyhz.noffice.core.design_system.extension.screenHorizonPadding +import com.easyhz.noffice.core.design_system.util.calendar.getCalendarPadding +import com.easyhz.noffice.feature.organization.contract.creation.CreationIntent +import com.easyhz.noffice.feature.organization.screen.creation.OrganizationCreationViewModel + +@Composable +internal fun EndDateView( + modifier: Modifier = Modifier, + viewModel: OrganizationCreationViewModel = hiltViewModel() +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val screenWidth = LocalConfiguration.current.screenWidthDp + val calendarPadding = getCalendarPadding(16, screenWidth).dp + + Column( + modifier = modifier + ) { + CommonHeader( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp, horizontal = 24.dp), + title = stringResource(id = R.string.organization_creation_end_date_title) + ) + MonthCalendarView( + modifier = Modifier.weight(1f), + selection = uiState.endDate, + calendarPadding = calendarPadding + ) { + viewModel.postIntent(CreationIntent.ChangeEndDate(it)) + } + MediumButton( + modifier = Modifier + .screenHorizonPadding() + .fillMaxWidth() + .padding(bottom = 16.dp), + text = stringResource(id = R.string.sign_up_button), + enabled = uiState.enabledStepButton[uiState.step.currentStep] ?: false + ) { + viewModel.postIntent(CreationIntent.ClickNextButton) + } + } +} \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt index 78c0e1c8..8f5335ec 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt @@ -2,6 +2,7 @@ package com.easyhz.noffice.feature.organization.contract.creation import android.net.Uri import com.easyhz.noffice.core.common.base.UiIntent +import java.time.LocalDate sealed class CreationIntent: UiIntent() { data object ClickBackButton: CreationIntent() @@ -12,4 +13,5 @@ sealed class CreationIntent: UiIntent() { data class ClickCategoryItem(val selectedIndex: Int): CreationIntent() data object ClickImageView : CreationIntent() data class PickImage(val uri: Uri?) : CreationIntent() + data class ChangeEndDate(val date: LocalDate): CreationIntent() } \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt index e7391b08..3a48cae3 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt @@ -8,6 +8,7 @@ import com.easyhz.noffice.core.common.util.toEnabledStepButton import com.easyhz.noffice.feature.organization.util.creation.Category import com.easyhz.noffice.feature.organization.util.creation.CreationStep import com.easyhz.noffice.feature.organization.util.creation.toState +import java.time.LocalDate import java.util.EnumMap data class CreationState( @@ -15,7 +16,8 @@ data class CreationState( val enabledStepButton: EnumMap, val organizationName: String, val category: List, - val organizationImage: Uri + val organizationImage: Uri, + val endDate: LocalDate, ): UiState() { companion object { const val ORGANIZATION_NAME_MAX = 10 @@ -24,7 +26,8 @@ data class CreationState( enabledStepButton = CreationStep.entries.toEnabledStepButton(), organizationName = "", category = Category.toState(), - organizationImage = Uri.EMPTY + organizationImage = Uri.EMPTY, + endDate = LocalDate.now() ) } diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt index 3d4bd0f7..ba6f6d65 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt @@ -32,6 +32,7 @@ import com.easyhz.noffice.core.design_system.theme.Grey400 import com.easyhz.noffice.core.design_system.theme.White import com.easyhz.noffice.core.design_system.util.topBar.DetailTopBarMenu import com.easyhz.noffice.feature.organization.component.creation.CategoryView +import com.easyhz.noffice.feature.organization.component.creation.EndDateView import com.easyhz.noffice.feature.organization.component.creation.OrganizationNameView import com.easyhz.noffice.feature.organization.component.creation.ImageView import com.easyhz.noffice.feature.organization.contract.creation.CreationIntent @@ -93,7 +94,7 @@ fun OrganizationCreationScreen( CreationStep.ORGANIZATION_NAME -> { OrganizationNameView(modifier = Modifier.screenHorizonPadding()) } CreationStep.CATEGORY -> { CategoryView(modifier = Modifier.screenHorizonPadding()) } CreationStep.IMAGE -> { ImageView(modifier = Modifier.screenHorizonPadding()) } - CreationStep.END_DATE -> { } + CreationStep.END_DATE -> { EndDateView() } CreationStep.PROMOTION -> { } CreationStep.SUCCESS -> { } } diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt index d92a28df..55598c87 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt @@ -8,6 +8,7 @@ import com.easyhz.noffice.feature.organization.contract.creation.CreationSideEff import com.easyhz.noffice.feature.organization.contract.creation.CreationState import com.easyhz.noffice.feature.organization.contract.creation.CreationState.Companion.ORGANIZATION_NAME_MAX import dagger.hilt.android.lifecycle.HiltViewModel +import java.time.LocalDate import javax.inject.Inject @HiltViewModel @@ -26,6 +27,7 @@ class OrganizationCreationViewModel @Inject constructor( is CreationIntent.ClickCategoryItem -> { onClickCategoryItem(intent.selectedIndex) } is CreationIntent.ClickImageView -> { onClickImageView() } is CreationIntent.PickImage -> { onPickImage(intent.uri) } + is CreationIntent.ChangeEndDate -> { onChangeEndDate(intent.date) } } } @@ -73,9 +75,14 @@ class OrganizationCreationViewModel @Inject constructor( private fun onClickImageView() { postSideEffect { CreationSideEffect.NavigateToGallery } } + private fun onPickImage(uri: Uri?) { uri?.let { reduce { copy(organizationImage = it) } } } + + private fun onChangeEndDate(date: LocalDate) { + reduce { copy(endDate = date) } + } } \ No newline at end of file From db2dcbbab539eba4e2cf9a6f32f00075460033ee Mon Sep 17 00:00:00 2001 From: easyhz Date: Sat, 20 Jul 2024 14:08:54 +0900 Subject: [PATCH 15/19] =?UTF-8?q?feat:=20=EA=B7=B8=EB=A3=B9=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=ED=94=8C=EB=A1=9C=EC=9A=B0=20=EC=A2=85=EB=A3=8C=20?= =?UTF-8?q?=EB=82=A0=EC=A7=9C=20=EC=BA=A1=EC=85=98=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?#20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../noffice/core/common/util/DateFormat.kt | 9 +++++++ .../noffice/core/design_system/theme/Type.kt | 26 +++++++++++++++++- .../src/main/res/values/strings.xml | 1 + .../component/creation/EndDateView.kt | 27 ++++++++++++++++++- 4 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 core/common/src/main/java/com/easyhz/noffice/core/common/util/DateFormat.kt diff --git a/core/common/src/main/java/com/easyhz/noffice/core/common/util/DateFormat.kt b/core/common/src/main/java/com/easyhz/noffice/core/common/util/DateFormat.kt new file mode 100644 index 00000000..b38b7ede --- /dev/null +++ b/core/common/src/main/java/com/easyhz/noffice/core/common/util/DateFormat.kt @@ -0,0 +1,9 @@ + package com.easyhz.noffice.core.common.util + +import java.time.LocalDate +import java.time.format.DateTimeFormatter + +object DateFormat { + fun fullText(date: LocalDate): String = + DateTimeFormatter.ofPattern("yyyy년 MM월 d일").format(date) +} \ No newline at end of file diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/theme/Type.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/theme/Type.kt index 27ab89f6..59e21437 100644 --- a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/theme/Type.kt +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/theme/Type.kt @@ -232,4 +232,28 @@ val CardExceptionSubTitle = TextStyle( platformStyle = PlatformTextStyle( includeFontPadding = false, ) -) \ No newline at end of file +) + +val CalendarCaption1 = TextStyle( + fontFamily = Pretendard, + fontWeight = FontWeight.SemiBold, + color = Green800, + fontSize = 16.sp, + letterSpacing = LetterSpacing, + textAlign = TextAlign.Justify, + platformStyle = PlatformTextStyle( + includeFontPadding = false, + ) +) + +val CalendarCaption2 = TextStyle( + fontFamily = Pretendard, + fontWeight = FontWeight.SemiBold, + color = Grey400, + fontSize = 16.sp, + letterSpacing = LetterSpacing, + textAlign = TextAlign.Justify, + platformStyle = PlatformTextStyle( + includeFontPadding = false, + ) +) diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index cc0f917d..4d107cff 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -66,4 +66,5 @@ 그룹의 카테고리를 모두 선택해 주세요 대표 이미지를 설정해 주세요 활동 종료 날짜가 언제인가요? + 에 활동을 종료할 예정이에요! \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/EndDateView.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/EndDateView.kt index 6c3b6a74..56d0ca39 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/EndDateView.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/EndDateView.kt @@ -3,18 +3,26 @@ package com.easyhz.noffice.feature.organization.component.creation import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.easyhz.noffice.core.common.util.DateFormat import com.easyhz.noffice.core.design_system.R import com.easyhz.noffice.core.design_system.component.button.MediumButton import com.easyhz.noffice.core.design_system.component.calendar.MonthCalendarView import com.easyhz.noffice.core.design_system.extension.screenHorizonPadding +import com.easyhz.noffice.core.design_system.theme.CalendarCaption1 +import com.easyhz.noffice.core.design_system.theme.CalendarCaption2 import com.easyhz.noffice.core.design_system.util.calendar.getCalendarPadding import com.easyhz.noffice.feature.organization.contract.creation.CreationIntent import com.easyhz.noffice.feature.organization.screen.creation.OrganizationCreationViewModel @@ -27,6 +35,15 @@ internal fun EndDateView( val uiState by viewModel.uiState.collectAsStateWithLifecycle() val screenWidth = LocalConfiguration.current.screenWidthDp val calendarPadding = getCalendarPadding(16, screenWidth).dp + val caption = stringResource(id = R.string.organization_creation_end_date_caption) + val annotatedString = buildAnnotatedString { + withStyle(style = CalendarCaption1.toSpanStyle()) { + append(DateFormat.fullText(uiState.endDate)) + } + withStyle(style = CalendarCaption2.toSpanStyle()) { + append(caption) + } + } Column( modifier = modifier @@ -44,11 +61,19 @@ internal fun EndDateView( ) { viewModel.postIntent(CreationIntent.ChangeEndDate(it)) } + Text( + modifier = Modifier + .screenHorizonPadding() + .fillMaxWidth() + .align(Alignment.CenterHorizontally), + text = annotatedString, + textAlign = TextAlign.Center + ) MediumButton( modifier = Modifier .screenHorizonPadding() .fillMaxWidth() - .padding(bottom = 16.dp), + .padding(vertical = 16.dp), text = stringResource(id = R.string.sign_up_button), enabled = uiState.enabledStepButton[uiState.step.currentStep] ?: false ) { From d0bb1e06bd5d6ba21c335b6ef5e56d513cfb50ca Mon Sep 17 00:00:00 2001 From: easyhz Date: Sat, 20 Jul 2024 14:16:45 +0900 Subject: [PATCH 16/19] =?UTF-8?q?feat:=20=EA=B7=B8=EB=A3=B9=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=ED=94=8C=EB=A1=9C=EC=9A=B0=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EB=AA=A8=EC=85=98=20=EC=B6=94=EA=B0=80=20#20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/res/values/strings.xml | 2 + .../component/creation/PromotionView.kt | 62 +++++++++++++++++++ .../contract/creation/CreationIntent.kt | 2 + .../contract/creation/CreationState.kt | 4 +- .../creation/OrganizationCreationScreen.kt | 4 +- .../creation/OrganizationCreationViewModel.kt | 10 +++ .../util/creation/CreationStep.kt | 3 - 7 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/PromotionView.kt diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index 4d107cff..1c0ce331 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -67,4 +67,6 @@ 대표 이미지를 설정해 주세요 활동 종료 날짜가 언제인가요? 에 활동을 종료할 예정이에요! + 프로모션 코드를 입력해 주세요 + 코드를 입력해 주세요. \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/PromotionView.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/PromotionView.kt new file mode 100644 index 00000000..14cf4dc8 --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/PromotionView.kt @@ -0,0 +1,62 @@ +package com.easyhz.noffice.feature.organization.component.creation + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.easyhz.noffice.core.design_system.R +import com.easyhz.noffice.core.design_system.component.button.MediumButton +import com.easyhz.noffice.core.design_system.component.textField.MainTextField +import com.easyhz.noffice.core.design_system.extension.noRippleClickable +import com.easyhz.noffice.feature.organization.contract.creation.CreationIntent +import com.easyhz.noffice.feature.organization.screen.creation.OrganizationCreationViewModel + +@Composable +internal fun PromotionView( + modifier: Modifier = Modifier, + viewModel: OrganizationCreationViewModel = hiltViewModel() +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + Column( + modifier = modifier + ) { + CommonHeader( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp, horizontal = 8.dp), + title = stringResource(id = R.string.organization_creation_promotion_title) + ) + MainTextField( + modifier = Modifier.weight(0.3f), + value = uiState.promotionCode, + onValueChange = { viewModel.postIntent(CreationIntent.ChangePromotionTextValue(it)) }, + title = null, + placeholder = stringResource(id = R.string.organization_creation_promotion_placeholder), + isFilled = false, + singleLine = true, + onClickIcon = { viewModel.postIntent(CreationIntent.ClearPromotionCode) } + ) + Spacer(modifier = Modifier + .fillMaxWidth() + .weight(1f) + .noRippleClickable { viewModel.postIntent(CreationIntent.ClearFocus) } + ) + MediumButton( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 16.dp), + text = stringResource(id = R.string.sign_up_button), + enabled = uiState.enabledStepButton[uiState.step.currentStep] ?: false + ) { + viewModel.postIntent(CreationIntent.ClickNextButton) + } + } +} \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt index 8f5335ec..cf5dee80 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationIntent.kt @@ -14,4 +14,6 @@ sealed class CreationIntent: UiIntent() { data object ClickImageView : CreationIntent() data class PickImage(val uri: Uri?) : CreationIntent() data class ChangeEndDate(val date: LocalDate): CreationIntent() + data class ChangePromotionTextValue(val text: String): CreationIntent() + data object ClearPromotionCode: CreationIntent() } \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt index 3a48cae3..539cf457 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt @@ -18,6 +18,7 @@ data class CreationState( val category: List, val organizationImage: Uri, val endDate: LocalDate, + val promotionCode: String, ): UiState() { companion object { const val ORGANIZATION_NAME_MAX = 10 @@ -27,7 +28,8 @@ data class CreationState( organizationName = "", category = Category.toState(), organizationImage = Uri.EMPTY, - endDate = LocalDate.now() + endDate = LocalDate.now(), + promotionCode = "" ) } diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt index ba6f6d65..bb279fb6 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt @@ -35,6 +35,7 @@ import com.easyhz.noffice.feature.organization.component.creation.CategoryView import com.easyhz.noffice.feature.organization.component.creation.EndDateView import com.easyhz.noffice.feature.organization.component.creation.OrganizationNameView import com.easyhz.noffice.feature.organization.component.creation.ImageView +import com.easyhz.noffice.feature.organization.component.creation.PromotionView import com.easyhz.noffice.feature.organization.contract.creation.CreationIntent import com.easyhz.noffice.feature.organization.contract.creation.CreationSideEffect import com.easyhz.noffice.feature.organization.util.creation.CreationStep @@ -95,8 +96,7 @@ fun OrganizationCreationScreen( CreationStep.CATEGORY -> { CategoryView(modifier = Modifier.screenHorizonPadding()) } CreationStep.IMAGE -> { ImageView(modifier = Modifier.screenHorizonPadding()) } CreationStep.END_DATE -> { EndDateView() } - CreationStep.PROMOTION -> { } - CreationStep.SUCCESS -> { } + CreationStep.PROMOTION -> { PromotionView(modifier = Modifier.screenHorizonPadding()) } } } } diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt index 55598c87..22ff5b6e 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt @@ -28,6 +28,8 @@ class OrganizationCreationViewModel @Inject constructor( is CreationIntent.ClickImageView -> { onClickImageView() } is CreationIntent.PickImage -> { onPickImage(intent.uri) } is CreationIntent.ChangeEndDate -> { onChangeEndDate(intent.date) } + is CreationIntent.ChangePromotionTextValue -> { onChangePromotionTextValue(intent.text) } + is CreationIntent.ClearPromotionCode -> { onClearPromotionCode() } } } @@ -85,4 +87,12 @@ class OrganizationCreationViewModel @Inject constructor( private fun onChangeEndDate(date: LocalDate) { reduce { copy(endDate = date) } } + + private fun onChangePromotionTextValue(newText: String) { + reduce { copy(promotionCode = newText) } + } + + private fun onClearPromotionCode() { + reduce { copy(promotionCode = "") } + } } \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/util/creation/CreationStep.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/util/creation/CreationStep.kt index f5614590..cb0c87f7 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/util/creation/CreationStep.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/util/creation/CreationStep.kt @@ -18,9 +18,6 @@ enum class CreationStep: StepRequired { }, PROMOTION { override val isRequired: Boolean get() = false - }, SUCCESS { - override val isRequired: Boolean - get() = false }; fun nextStep(): CreationStep? = From 8c4ce4e20d2cab70ae7255a93f8ab5556272283a Mon Sep 17 00:00:00 2001 From: easyhz Date: Sat, 20 Jul 2024 16:47:47 +0900 Subject: [PATCH 17/19] =?UTF-8?q?feat:=20=EA=B7=B8=EB=A3=B9=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=ED=94=8C=EB=A1=9C=EC=9A=B0=20=EC=99=84=EB=A3=8C=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EC=B6=94=EA=B0=80=20#20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/easyhz/noffice/NofficeApp.kt | 18 ++- .../organization/OrganizationNavigation.kt | 33 ++++- .../screen/OrganizationInvitation.kt | 9 ++ .../component/calendar/MonthHeader.kt | 27 +++- .../component/textField/MainTextField.kt | 2 + .../noffice/core/design_system/theme/Type.kt | 14 ++- .../src/main/res/values/strings.xml | 5 + .../component/invitation/UrlView.kt | 41 ++++++ .../contract/creation/CreationSideEffect.kt | 1 + .../contract/invitation/InvitationIntent.kt | 9 ++ .../invitation/InvitationSideEffect.kt | 8 ++ .../contract/invitation/InvitationState.kt | 15 +++ .../creation/OrganizationCreationScreen.kt | 6 +- .../creation/OrganizationCreationViewModel.kt | 14 ++- .../screen/invitation/InvitationScreen.kt | 118 ++++++++++++++++++ .../screen/invitation/InvitationViewModel.kt | 35 ++++++ 16 files changed, 342 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/com/easyhz/noffice/navigation/organization/screen/OrganizationInvitation.kt create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/invitation/UrlView.kt create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/invitation/InvitationIntent.kt create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/invitation/InvitationSideEffect.kt create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/invitation/InvitationState.kt create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/invitation/InvitationScreen.kt create mode 100644 feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/invitation/InvitationViewModel.kt diff --git a/app/src/main/java/com/easyhz/noffice/NofficeApp.kt b/app/src/main/java/com/easyhz/noffice/NofficeApp.kt index 1aefb3b5..b1663a01 100644 --- a/app/src/main/java/com/easyhz/noffice/NofficeApp.kt +++ b/app/src/main/java/com/easyhz/noffice/NofficeApp.kt @@ -11,13 +11,17 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.navigation.compose.NavHost +import androidx.navigation.navOptions import com.easyhz.noffice.core.design_system.component.bottomBar.HomeBottomBar import com.easyhz.noffice.core.design_system.component.button.HomeAddButton import com.easyhz.noffice.core.design_system.component.scaffold.NofficeScaffold import com.easyhz.noffice.navigation.home.homeScreen +import com.easyhz.noffice.navigation.home.navigateToHome import com.easyhz.noffice.navigation.home.screen.Home import com.easyhz.noffice.navigation.organization.navigateToOrganizationCreation +import com.easyhz.noffice.navigation.organization.navigateToOrganizationInvitation import com.easyhz.noffice.navigation.organization.organizationScreen +import com.easyhz.noffice.navigation.organization.screen.OrganizationInvitation import com.easyhz.noffice.navigation.rememberNofficeNavController import com.easyhz.noffice.navigation.sign.signScreen import com.easyhz.noffice.navigation.util.BOTTOM_BAR_DURATION @@ -76,7 +80,19 @@ fun NofficeApp() { homeScreen(modifier = Modifier.padding(it)) organizationScreen( modifier = Modifier.padding(it), - navigateToCreation = navController::navigateToOrganizationCreation + navigateToCreation = navController::navigateToOrganizationCreation, + navigateToInvitation = navController::navigateToOrganizationInvitation, + navigateToHome = { + val navOptions = navOptions { + popUpTo(navController.graph.id) { + saveState = true + inclusive = false + } + launchSingleTop = true + restoreState = true + } + navController.navigateToHome(navOptions) + } ) signScreen() } diff --git a/app/src/main/java/com/easyhz/noffice/navigation/organization/OrganizationNavigation.kt b/app/src/main/java/com/easyhz/noffice/navigation/organization/OrganizationNavigation.kt index 46767a6d..5d9be9d0 100644 --- a/app/src/main/java/com/easyhz/noffice/navigation/organization/OrganizationNavigation.kt +++ b/app/src/main/java/com/easyhz/noffice/navigation/organization/OrganizationNavigation.kt @@ -7,15 +7,21 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable +import androidx.navigation.navOptions +import androidx.navigation.toRoute import com.easyhz.noffice.feature.organization.screen.creation.OrganizationCreationScreen +import com.easyhz.noffice.feature.organization.screen.invitation.OrganizationInvitationScreen import com.easyhz.noffice.feature.organization.screen.organization.OrganizationScreen import com.easyhz.noffice.navigation.organization.screen.Organization import com.easyhz.noffice.navigation.organization.screen.OrganizationCreation +import com.easyhz.noffice.navigation.organization.screen.OrganizationInvitation import com.easyhz.noffice.navigation.util.DURATION internal fun NavGraphBuilder.organizationScreen( modifier: Modifier, - navigateToCreation: () -> Unit + navigateToCreation: () -> Unit, + navigateToInvitation: (String, String)-> Unit, + navigateToHome: () -> Unit ) { composable { OrganizationScreen( @@ -29,8 +35,24 @@ internal fun NavGraphBuilder.organizationScreen( popEnterTransition = { slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.End, tween(DURATION)) }, popExitTransition = { slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, tween(DURATION)) } ) { - OrganizationCreationScreen() + OrganizationCreationScreen( + navigateToInvitation = navigateToInvitation + ) + } + composable( + enterTransition = { slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Start, tween(DURATION)) }, + exitTransition = { slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Start, tween(DURATION)) }, + popEnterTransition = { slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.End, tween(DURATION)) }, + popExitTransition = { slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, tween(DURATION)) } + ) { + val args = it.toRoute() + OrganizationInvitationScreen( + invitationUrl = args.invitationUrl, + imageUrl = args.imageUrl, + navigateToHome = navigateToHome + ) } + } internal fun NavController.navigateToOrganization(navOptions: NavOptions) { @@ -42,4 +64,11 @@ internal fun NavController.navigateToOrganization(navOptions: NavOptions) { internal fun NavController.navigateToOrganizationCreation() { navigate(OrganizationCreation) +} + +internal fun NavController.navigateToOrganizationInvitation(invitationUrl: String, imageUrl: String) { + val navOptions = navOptions { + popUpTo(OrganizationCreation) { inclusive = true } + } + navigate(OrganizationInvitation(invitationUrl = invitationUrl, imageUrl = imageUrl), navOptions) } \ No newline at end of file diff --git a/app/src/main/java/com/easyhz/noffice/navigation/organization/screen/OrganizationInvitation.kt b/app/src/main/java/com/easyhz/noffice/navigation/organization/screen/OrganizationInvitation.kt new file mode 100644 index 00000000..2d3f204c --- /dev/null +++ b/app/src/main/java/com/easyhz/noffice/navigation/organization/screen/OrganizationInvitation.kt @@ -0,0 +1,9 @@ +package com.easyhz.noffice.navigation.organization.screen + +import kotlinx.serialization.Serializable + +@Serializable +data class OrganizationInvitation( + val invitationUrl: String, + val imageUrl: String = "", +) diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/calendar/MonthHeader.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/calendar/MonthHeader.kt index 9a150335..a011c4bc 100644 --- a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/calendar/MonthHeader.kt +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/calendar/MonthHeader.kt @@ -5,17 +5,21 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.scale import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import com.easyhz.noffice.core.design_system.R import com.easyhz.noffice.core.design_system.extension.noRippleClickable import com.easyhz.noffice.core.design_system.theme.Grey500 import com.easyhz.noffice.core.design_system.theme.InputDialogTitle +import com.easyhz.noffice.core.design_system.util.interaction.useInteraction @Composable internal fun MonthHeader( @@ -24,26 +28,39 @@ internal fun MonthHeader( onClickBefore: () -> Unit, onClickNext: () -> Unit ) { + val (interactionSource, scale) = useInteraction() Row( modifier = modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center ) { - Box(modifier = Modifier.size(32.dp).noRippleClickable { onClickBefore() }) { + Box(modifier = Modifier + .size(32.dp) + .scale(scale) + .noRippleClickable(interactionSource) { onClickBefore() }) { Icon( - modifier = Modifier.align(Alignment.CenterStart).size(24.dp), + modifier = Modifier + .align(Alignment.CenterStart) + .size(24.dp), painter = painterResource(id = R.drawable.ic_chevron_left), contentDescription = "before", tint = Grey500 ) } Text( + modifier = Modifier.width(76.dp), text = title, - style = InputDialogTitle + style = InputDialogTitle, + textAlign = TextAlign.Center ) - Box(modifier = Modifier.size(32.dp).noRippleClickable { onClickNext() }) { + Box(modifier = Modifier + .size(32.dp) + .scale(scale) + .noRippleClickable(interactionSource) { onClickNext() }) { Icon( - modifier = Modifier.align(Alignment.CenterEnd).size(24.dp), + modifier = Modifier + .align(Alignment.CenterEnd) + .size(24.dp), painter = painterResource(id = R.drawable.ic_chevron_right), contentDescription = "next", tint = Grey500 diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/textField/MainTextField.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/textField/MainTextField.kt index 3dcf542f..f8b327d3 100644 --- a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/textField/MainTextField.kt +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/component/textField/MainTextField.kt @@ -24,6 +24,7 @@ fun MainTextField( placeholder: String, isFilled: Boolean, maxCount: Int? = null, + readOnly: Boolean = false, singleLine: Boolean, minLines: Int = 1, icon: TextFieldIcon? = TextFieldIcon.CLEAR, @@ -41,6 +42,7 @@ fun MainTextField( textStyle = SubBody16.copy( color = Grey800 ), + readOnly = readOnly, keyboardOptions = keyboardOptions, keyboardActions = keyboardActions, singleLine = singleLine, diff --git a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/theme/Type.kt b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/theme/Type.kt index 59e21437..28ff0b96 100644 --- a/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/theme/Type.kt +++ b/core/design-system/src/main/java/com/easyhz/noffice/core/design_system/theme/Type.kt @@ -46,7 +46,7 @@ val Title2 = TextStyle( ) ) -val Title3 = TextStyle( +val Title3 = TextStyle( fontFamily = Pretendard, fontWeight = FontWeight.SemiBold, color = Grey800, @@ -58,6 +58,18 @@ val Title3 = TextStyle( ) ) +val Title4 = TextStyle( + fontFamily = Pretendard, + fontWeight = FontWeight.Medium, + color = Grey800, + fontSize = 20.sp, + letterSpacing = LetterSpacing, + textAlign = TextAlign.Justify, + platformStyle = PlatformTextStyle( + includeFontPadding = false, + ) +) + val SubTitle1 = TextStyle( fontFamily = Pretendard, fontWeight = FontWeight.Medium, diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index 1c0ce331..83002739 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -69,4 +69,9 @@ 에 활동을 종료할 예정이에요! 프로모션 코드를 입력해 주세요 코드를 입력해 주세요. + + 그룹을 만들었어요! + 링크를 통해 멤버들을 초대해 보세요. + 그룹 메인으로 + 초대 링크 복사 \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/invitation/UrlView.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/invitation/UrlView.kt new file mode 100644 index 00000000..a4cddfeb --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/invitation/UrlView.kt @@ -0,0 +1,41 @@ +package com.easyhz.noffice.feature.organization.component.invitation + +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import com.easyhz.noffice.core.design_system.extension.noRippleClickable +import com.easyhz.noffice.core.design_system.theme.Grey100 +import com.easyhz.noffice.core.design_system.theme.Grey600 +import com.easyhz.noffice.core.design_system.theme.SubBody14 + +@Composable +internal fun UrlView( + modifier: Modifier = Modifier, + text: String, + onClick: () -> Unit +) { + Box( + modifier = modifier + .height(48.dp) + .border(width = 1.dp, color = Grey100, shape = RoundedCornerShape(8.dp)) + .padding(horizontal = 12.dp) + .noRippleClickable { onClick() } + ) { + Text( + text = text, + modifier = Modifier.align(Alignment.CenterStart), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = SubBody14, + color = Grey600 + ) + } +} \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationSideEffect.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationSideEffect.kt index 778b9af0..500b750a 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationSideEffect.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationSideEffect.kt @@ -5,4 +5,5 @@ import com.easyhz.noffice.core.common.base.UiSideEffect sealed class CreationSideEffect: UiSideEffect() { data object ClearFocus: CreationSideEffect() data object NavigateToGallery: CreationSideEffect() + data class NavigateToInvitation(val invitationUrl: String, val imageUrl: String): CreationSideEffect() } \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/invitation/InvitationIntent.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/invitation/InvitationIntent.kt new file mode 100644 index 00000000..8e0b6365 --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/invitation/InvitationIntent.kt @@ -0,0 +1,9 @@ +package com.easyhz.noffice.feature.organization.contract.invitation + +import com.easyhz.noffice.core.common.base.UiIntent + +sealed class InvitationIntent: UiIntent() { + data class InitScreen(val invitationUrl: String, val imageUrl: String): InvitationIntent() + data object ClickHomeButton: InvitationIntent() + data object ClickCopyUrl: InvitationIntent() +} \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/invitation/InvitationSideEffect.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/invitation/InvitationSideEffect.kt new file mode 100644 index 00000000..0bbfd2c1 --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/invitation/InvitationSideEffect.kt @@ -0,0 +1,8 @@ +package com.easyhz.noffice.feature.organization.contract.invitation + +import com.easyhz.noffice.core.common.base.UiSideEffect + +sealed class InvitationSideEffect: UiSideEffect() { + data object NavigateToHome: InvitationSideEffect() + data class CopyUrl(val url: String): InvitationSideEffect() +} \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/invitation/InvitationState.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/invitation/InvitationState.kt new file mode 100644 index 00000000..aced2148 --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/invitation/InvitationState.kt @@ -0,0 +1,15 @@ +package com.easyhz.noffice.feature.organization.contract.invitation + +import com.easyhz.noffice.core.common.base.UiState + +data class InvitationState( + val invitationUrl: String, + val imageUrl: String, +): UiState() { + companion object { + fun init() = InvitationState( + invitationUrl = "", + imageUrl = "" + ) + } +} diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt index bb279fb6..1a88d100 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt @@ -43,7 +43,8 @@ import com.easyhz.noffice.feature.organization.util.creation.CreationStep @Composable fun OrganizationCreationScreen( modifier: Modifier = Modifier, - viewModel: OrganizationCreationViewModel = hiltViewModel() + viewModel: OrganizationCreationViewModel = hiltViewModel(), + navigateToInvitation: (String, String) -> Unit ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() val focusManager = LocalFocusManager.current @@ -107,6 +108,9 @@ fun OrganizationCreationScreen( is CreationSideEffect.NavigateToGallery -> { galleryLauncher.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) } + is CreationSideEffect.NavigateToInvitation -> { + navigateToInvitation(sideEffect.invitationUrl, sideEffect.imageUrl) + } } } } \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt index 22ff5b6e..7439eb00 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt @@ -43,9 +43,7 @@ class OrganizationCreationViewModel @Inject constructor( currentState.step.currentStep.nextStep()?.let { nextStep -> reduce { updateStep(currentStep = nextStep) } postSideEffect { CreationSideEffect.ClearFocus } - } ?: run { - /* TODO NEXT */ - } + } ?: onNavigateToInvitation() } private fun onChangeOrganizationNameTextValue(newText: String) { @@ -95,4 +93,14 @@ class OrganizationCreationViewModel @Inject constructor( private fun onClearPromotionCode() { reduce { copy(promotionCode = "") } } + + // TODO: 서버 통신 로직 추가 + private fun onNavigateToInvitation() { + postSideEffect { + CreationSideEffect.NavigateToInvitation( + "www.noffice/dhedgyeqi3e83", + currentState.organizationImage.toString() + ) + } + } } \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/invitation/InvitationScreen.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/invitation/InvitationScreen.kt new file mode 100644 index 00000000..9d9a4771 --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/invitation/InvitationScreen.kt @@ -0,0 +1,118 @@ +package com.easyhz.noffice.feature.organization.screen.invitation + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalClipboardManager +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.easyhz.noffice.core.common.util.collectInSideEffectWithLifecycle +import com.easyhz.noffice.core.design_system.R +import com.easyhz.noffice.core.design_system.component.button.MediumButton +import com.easyhz.noffice.core.design_system.component.image.OrganizationImage +import com.easyhz.noffice.core.design_system.component.scaffold.NofficeBasicScaffold +import com.easyhz.noffice.core.design_system.extension.screenHorizonPadding +import com.easyhz.noffice.core.design_system.theme.Grey100 +import com.easyhz.noffice.core.design_system.theme.Grey600 +import com.easyhz.noffice.core.design_system.theme.SubTitle1 +import com.easyhz.noffice.core.design_system.theme.Title4 +import com.easyhz.noffice.feature.organization.component.invitation.UrlView +import com.easyhz.noffice.feature.organization.contract.invitation.InvitationIntent +import com.easyhz.noffice.feature.organization.contract.invitation.InvitationSideEffect + +@Composable +fun OrganizationInvitationScreen( + modifier: Modifier = Modifier, + viewModel: InvitationViewModel = hiltViewModel(), + invitationUrl: String, + imageUrl: String, + navigateToHome: () -> Unit, +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val clipboardManager = LocalClipboardManager.current + + LaunchedEffect(Unit) { + viewModel.postIntent(InvitationIntent.InitScreen(invitationUrl, imageUrl)) + } + + NofficeBasicScaffold { + Column( + modifier = modifier + .screenHorizonPadding() + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Column( + modifier = Modifier.weight(1f), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Spacer(modifier = Modifier.weight(0.8f)) + OrganizationImage( + modifier = Modifier.size(120.dp), + imageUrl = uiState.imageUrl + ) + Spacer(modifier = Modifier.height(18.dp)) + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text(text = stringResource(id = R.string.organization_creation_success_title), style = Title4) + Text(text = stringResource(id = R.string.organization_creation_success_sub_title), style = SubTitle1) + } + Spacer(modifier = Modifier.height(18.dp)) + UrlView( + modifier = Modifier + .padding(horizontal = 32.dp) + .fillMaxWidth(), + text = uiState.invitationUrl + ) { + viewModel.postIntent(InvitationIntent.ClickCopyUrl) + } + Spacer(modifier = Modifier.weight(1f)) + } + Row( + modifier = Modifier + .padding(bottom = 16.dp), + horizontalArrangement = Arrangement.spacedBy(10.dp) + ) { + MediumButton( + modifier = Modifier.weight(1f), + text = stringResource(id = R.string.organization_creation_success_home_button), + contentColor = Grey600, + containerColor = Grey100 + ) { + viewModel.postIntent(InvitationIntent.ClickHomeButton) + } + MediumButton( + modifier = Modifier.weight(1f), + text = stringResource(id = R.string.organization_creation_success_copy_button) + ) { + viewModel.postIntent(InvitationIntent.ClickCopyUrl) + } + } + } + } + + viewModel.sideEffect.collectInSideEffectWithLifecycle {sideEffect -> + when(sideEffect) { + is InvitationSideEffect.NavigateToHome -> { navigateToHome() } + is InvitationSideEffect.CopyUrl -> { clipboardManager.setText(AnnotatedString(sideEffect.url)) } + } + } +} \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/invitation/InvitationViewModel.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/invitation/InvitationViewModel.kt new file mode 100644 index 00000000..c2466023 --- /dev/null +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/invitation/InvitationViewModel.kt @@ -0,0 +1,35 @@ +package com.easyhz.noffice.feature.organization.screen.invitation + +import com.easyhz.noffice.core.common.base.BaseViewModel +import com.easyhz.noffice.feature.organization.contract.invitation.InvitationIntent +import com.easyhz.noffice.feature.organization.contract.invitation.InvitationSideEffect +import com.easyhz.noffice.feature.organization.contract.invitation.InvitationState +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class InvitationViewModel @Inject constructor( + +): BaseViewModel( + initialState = InvitationState.init() +) { + override fun handleIntent(intent: InvitationIntent) { + when(intent) { + is InvitationIntent.InitScreen -> { initScreen(intent.invitationUrl, intent.imageUrl) } + is InvitationIntent.ClickHomeButton -> { onClickHomeButton() } + is InvitationIntent.ClickCopyUrl -> { onClickCopyUrl() } + } + } + + private fun initScreen(invitationUrl: String, imageUrl: String) { + reduce { copy(invitationUrl = invitationUrl, imageUrl = imageUrl) } + } + + private fun onClickHomeButton() { + postSideEffect { InvitationSideEffect.NavigateToHome } + } + + private fun onClickCopyUrl() { + postSideEffect { InvitationSideEffect.CopyUrl(currentState.invitationUrl) } + } +} \ No newline at end of file From 8e957028518dcf356b602e5cab8c006611306832 Mon Sep 17 00:00:00 2001 From: easyhz Date: Sat, 20 Jul 2024 17:06:14 +0900 Subject: [PATCH 18/19] =?UTF-8?q?feat:=20navigateToHome=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20#20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/easyhz/noffice/NofficeApp.kt | 6 +----- .../organization/OrganizationNavigation.kt | 3 ++- .../screen/creation/OrganizationCreationScreen.kt | 4 +++- .../screen/creation/OrganizationCreationViewModel.kt | 12 ++++++++---- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/easyhz/noffice/NofficeApp.kt b/app/src/main/java/com/easyhz/noffice/NofficeApp.kt index b1663a01..a0ff9ba6 100644 --- a/app/src/main/java/com/easyhz/noffice/NofficeApp.kt +++ b/app/src/main/java/com/easyhz/noffice/NofficeApp.kt @@ -21,7 +21,6 @@ import com.easyhz.noffice.navigation.home.screen.Home import com.easyhz.noffice.navigation.organization.navigateToOrganizationCreation import com.easyhz.noffice.navigation.organization.navigateToOrganizationInvitation import com.easyhz.noffice.navigation.organization.organizationScreen -import com.easyhz.noffice.navigation.organization.screen.OrganizationInvitation import com.easyhz.noffice.navigation.rememberNofficeNavController import com.easyhz.noffice.navigation.sign.signScreen import com.easyhz.noffice.navigation.util.BOTTOM_BAR_DURATION @@ -85,11 +84,8 @@ fun NofficeApp() { navigateToHome = { val navOptions = navOptions { popUpTo(navController.graph.id) { - saveState = true - inclusive = false + inclusive = true } - launchSingleTop = true - restoreState = true } navController.navigateToHome(navOptions) } diff --git a/app/src/main/java/com/easyhz/noffice/navigation/organization/OrganizationNavigation.kt b/app/src/main/java/com/easyhz/noffice/navigation/organization/OrganizationNavigation.kt index 5d9be9d0..0a949556 100644 --- a/app/src/main/java/com/easyhz/noffice/navigation/organization/OrganizationNavigation.kt +++ b/app/src/main/java/com/easyhz/noffice/navigation/organization/OrganizationNavigation.kt @@ -36,7 +36,8 @@ internal fun NavGraphBuilder.organizationScreen( popExitTransition = { slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.End, tween(DURATION)) } ) { OrganizationCreationScreen( - navigateToInvitation = navigateToInvitation + navigateToInvitation = navigateToInvitation, + navigateToHome = navigateToHome ) } composable( diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt index 1a88d100..6f56de1b 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationScreen.kt @@ -44,7 +44,8 @@ import com.easyhz.noffice.feature.organization.util.creation.CreationStep fun OrganizationCreationScreen( modifier: Modifier = Modifier, viewModel: OrganizationCreationViewModel = hiltViewModel(), - navigateToInvitation: (String, String) -> Unit + navigateToInvitation: (String, String) -> Unit, + navigateToHome: () -> Unit ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() val focusManager = LocalFocusManager.current @@ -108,6 +109,7 @@ fun OrganizationCreationScreen( is CreationSideEffect.NavigateToGallery -> { galleryLauncher.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) } + is CreationSideEffect.NavigateToHome -> { navigateToHome() } is CreationSideEffect.NavigateToInvitation -> { navigateToInvitation(sideEffect.invitationUrl, sideEffect.imageUrl) } diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt index 7439eb00..1beddefd 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/screen/creation/OrganizationCreationViewModel.kt @@ -36,7 +36,7 @@ class OrganizationCreationViewModel @Inject constructor( private fun onClickBackButton() { currentState.step.currentStep.beforeStep()?.let { beforeStep -> reduce { updateStep(currentStep = beforeStep) } - } // TODO NAVIGATE TO BACK + } ?: onNavigateToHome() } private fun onClickNextButton() { @@ -73,13 +73,13 @@ class OrganizationCreationViewModel @Inject constructor( } private fun onClickImageView() { + if (!currentState.isEnabledGallery) return postSideEffect { CreationSideEffect.NavigateToGallery } + reduce { copy(isEnabledGallery = false) } } private fun onPickImage(uri: Uri?) { - uri?.let { - reduce { copy(organizationImage = it) } - } + reduce { copy(organizationImage = uri ?: Uri.EMPTY, isEnabledGallery = true) } } private fun onChangeEndDate(date: LocalDate) { @@ -94,6 +94,10 @@ class OrganizationCreationViewModel @Inject constructor( reduce { copy(promotionCode = "") } } + private fun onNavigateToHome() { + postSideEffect { CreationSideEffect.NavigateToHome } + } + // TODO: 서버 통신 로직 추가 private fun onNavigateToInvitation() { postSideEffect { From c92766d7bf356ed0aa39f42c4dc8f32ed281ce0d Mon Sep 17 00:00:00 2001 From: easyhz Date: Sat, 20 Jul 2024 17:07:43 +0900 Subject: [PATCH 19/19] =?UTF-8?q?fix:=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=EB=B7=B0=20=EB=B9=A0=EB=A5=B4=EA=B2=8C=20=ED=84=B0=EC=B9=98=20?= =?UTF-8?q?=EC=8B=9C=20=EA=B0=A4=EB=9F=AC=EB=A6=AC=20=EC=97=AC=EB=9F=AC?= =?UTF-8?q?=EA=B0=9C=20=EC=97=B4=EC=96=B4=EC=A7=80=EB=8A=94=20=EC=9D=B4?= =?UTF-8?q?=EC=8A=88=20=EC=88=98=EC=A0=95=20#20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/organization/component/creation/ImageView.kt | 4 +++- .../organization/contract/creation/CreationSideEffect.kt | 1 + .../feature/organization/contract/creation/CreationState.kt | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/ImageView.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/ImageView.kt index 8baf2194..28f26308 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/ImageView.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/component/creation/ImageView.kt @@ -51,7 +51,9 @@ internal fun ImageView( .align(Alignment.CenterHorizontally) .clip(RoundedCornerShape(24.dp)) .background(Grey50) - .noRippleClickable { viewModel.postIntent(CreationIntent.ClickImageView) } + .noRippleClickable { + viewModel.postIntent(CreationIntent.ClickImageView) + } ) { when (uiState.organizationImage) { Uri.EMPTY -> { diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationSideEffect.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationSideEffect.kt index 500b750a..ba8ae291 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationSideEffect.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationSideEffect.kt @@ -5,5 +5,6 @@ import com.easyhz.noffice.core.common.base.UiSideEffect sealed class CreationSideEffect: UiSideEffect() { data object ClearFocus: CreationSideEffect() data object NavigateToGallery: CreationSideEffect() + data object NavigateToHome: CreationSideEffect() data class NavigateToInvitation(val invitationUrl: String, val imageUrl: String): CreationSideEffect() } \ No newline at end of file diff --git a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt index 539cf457..a61f5984 100644 --- a/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt +++ b/feature/organization/src/main/java/com/easyhz/noffice/feature/organization/contract/creation/CreationState.kt @@ -17,6 +17,7 @@ data class CreationState( val organizationName: String, val category: List, val organizationImage: Uri, + val isEnabledGallery: Boolean, val endDate: LocalDate, val promotionCode: String, ): UiState() { @@ -28,6 +29,7 @@ data class CreationState( organizationName = "", category = Category.toState(), organizationImage = Uri.EMPTY, + isEnabledGallery = true, endDate = LocalDate.now(), promotionCode = "" )