From e01be0ae07d46d3abc0ad89f4564d8738939f135 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Fri, 13 Feb 2026 21:43:20 +0900 Subject: [PATCH 01/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20Photolog?= =?UTF-8?q?Card=20=EA=B8=B0=EB=B3=B8=20=EC=83=89=EC=83=81=20=ED=8C=8C?= =?UTF-8?q?=EB=9D=BC=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 --- .../task_certification/detail/component/PhotologCard.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/PhotologCard.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/PhotologCard.kt index fd1348fc..f7348629 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/PhotologCard.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/PhotologCard.kt @@ -16,13 +16,15 @@ import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.twix.designsystem.theme.CommonColor +import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme @Composable internal fun PhotologCard( - background: Color, - borderColor: Color, modifier: Modifier = Modifier, + borderColor: Color = GrayColor.C500, + background: Color = CommonColor.White, rotation: Float = 0f, content: @Composable BoxScope.() -> Unit = {}, ) { From cbdeb6a48480b6a732c50496088c267298f15e70 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Fri, 13 Feb 2026 21:43:35 +0900 Subject: [PATCH 02/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EC=83=81=EC=84=B8=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?TopBar=20=EA=B5=AC=EC=A1=B0=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/design-system/src/main/res/values/strings.xml | 1 + .../task_certification/detail/TaskCertificationDetail.kt | 7 +++---- .../detail/component/TaskCertificationDetailTopBar.kt | 7 ++----- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index 21c2cdf0..ae854d14 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -15,6 +15,7 @@ 취소 삭제 수정 + 저장 설정 계정 정보 diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt index 9b72b566..9f32a7b4 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt @@ -60,7 +60,6 @@ import com.twix.designsystem.R as DesR fun TaskCertificationDetailRoute( navigateToBack: () -> Unit, navigateToUpload: (Long) -> Unit, - navigateToEditor: () -> Unit, toastManager: ToastManager = koinInject(), viewModel: TaskCertificationDetailViewModel = koinViewModel(), ) { @@ -113,7 +112,7 @@ fun TaskCertificationDetailRoute( TaskCertificationDetailScreen( uiState = uiState, onBack = navigateToBack, - onClickModify = { navigateToEditor() }, + onClickModify = { }, onClickReaction = { viewModel.dispatch(TaskCertificationDetailIntent.Reaction(it)) }, onClickUpload = { if (currentContext.hasCameraPermission()) { @@ -140,10 +139,10 @@ fun TaskCertificationDetailScreen( Scaffold( topBar = { TaskCertificationDetailTopBar( + actionTitle = stringResource(DesR.string.word_modify), goalTitle = uiState.currentGoal.goalName, onBack = onBack, - actionTitle = if (uiState.canModify) stringResource(DesR.string.word_modify) else null, - onClickModify = if (uiState.canModify) onClickModify else null, + onClickModify = onClickModify, modifier = Modifier .background(color = CommonColor.White), diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt index 6cccbc62..2ce38b74 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt @@ -33,9 +33,9 @@ import com.twix.designsystem.R as DesR fun TaskCertificationDetailTopBar( goalTitle: String, onBack: () -> Unit, - actionTitle: String?, - onClickModify: (() -> Unit)?, modifier: Modifier = Modifier, + actionTitle: String? = null, + onClickModify: (() -> Unit)? = null, ) { Column( modifier = @@ -114,14 +114,11 @@ fun TaskCertificationDetailTopBarPreview() { actionTitle = "수정", goalTitle = "목표 타이틀", onBack = {}, - onClickModify = {}, ) TaskCertificationDetailTopBar( goalTitle = "목표 타이틀", onBack = {}, - actionTitle = null, - onClickModify = null, ) } } From 4838aa8fd591ca5f6e17f142823be03e8a08206a Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Fri, 13 Feb 2026 22:16:39 +0900 Subject: [PATCH 03/52] =?UTF-8?q?=E2=9C=A8=20Refactor:=20AppRoundButton=20?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=88=9C=EC=84=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/designsystem/components/button/AppRoundButton.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/button/AppRoundButton.kt b/core/design-system/src/main/java/com/twix/designsystem/components/button/AppRoundButton.kt index d2df0a12..b2622c9a 100644 --- a/core/design-system/src/main/java/com/twix/designsystem/components/button/AppRoundButton.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/button/AppRoundButton.kt @@ -26,9 +26,9 @@ import com.twix.domain.model.enums.AppTextStyle fun AppRoundButton( text: String, textColor: Color, + backgroundColor: Color, modifier: Modifier = Modifier, textStyle: AppTextStyle = AppTextStyle.T2, - backgroundColor: Color, borderColor: Color = GrayColor.C500, hasBorder: Boolean = true, ) { From 19415a338dede70c3c21d609e0ec96a6095c153a Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Fri, 13 Feb 2026 22:17:26 +0900 Subject: [PATCH 04/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=9D=B8=EC=A6=9D?= =?UTF-8?q?=EC=83=B7=20=EC=88=98=EC=A0=95=20=ED=99=94=EB=A9=B4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=EC=9E=AC=EC=B4=AC=EC=98=81=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/twix/navigation/NavRoutes.kt | 3 + .../detail/TaskCertificationDetail.kt | 6 +- .../editor/TaskCertificationEditorRoute.kt | 172 ++++++++++++++++++ .../navigation/TaskCertificationGraph.kt | 40 +++- .../src/main/res/values/strings.xml | 3 + 5 files changed, 213 insertions(+), 11 deletions(-) create mode 100644 feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt diff --git a/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt b/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt index 4432d461..d6e31e82 100644 --- a/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt +++ b/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt @@ -51,6 +51,7 @@ sealed class NavRoutes( enum class From { HOME, DETAIL, + EDITOR, } fun createRoute( @@ -59,6 +60,8 @@ sealed class NavRoutes( ) = "task_certification/$goalId/${from.name}" } + object TaskCertificationEditorRoute : NavRoutes("task_certification_editor") + /** * OnboardingGraph * */ diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt index 9f32a7b4..a3e0cabd 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt @@ -52,16 +52,16 @@ import com.twix.ui.base.ObserveAsEvents import com.twix.ui.extension.findActivity import com.twix.ui.extension.hasCameraPermission import kotlinx.coroutines.launch -import org.koin.androidx.compose.koinViewModel import org.koin.compose.koinInject import com.twix.designsystem.R as DesR @Composable fun TaskCertificationDetailRoute( + viewModel: TaskCertificationDetailViewModel, navigateToBack: () -> Unit, navigateToUpload: (Long) -> Unit, + navigateToEditor: () -> Unit, toastManager: ToastManager = koinInject(), - viewModel: TaskCertificationDetailViewModel = koinViewModel(), ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() val context = LocalContext.current @@ -112,7 +112,7 @@ fun TaskCertificationDetailRoute( TaskCertificationDetailScreen( uiState = uiState, onBack = navigateToBack, - onClickModify = { }, + onClickModify = navigateToEditor, onClickReaction = { viewModel.dispatch(TaskCertificationDetailIntent.Reaction(it)) }, onClickUpload = { if (currentContext.hasCameraPermission()) { diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt new file mode 100644 index 00000000..9cbdeba4 --- /dev/null +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt @@ -0,0 +1,172 @@ +package com.twix.task_certification.editor + +import android.Manifest +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import androidx.core.app.ActivityCompat +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.twix.designsystem.components.button.AppRoundButton +import com.twix.designsystem.components.toast.ToastManager +import com.twix.designsystem.components.toast.model.ToastData +import com.twix.designsystem.components.toast.model.ToastType +import com.twix.designsystem.extension.showCameraPermissionToastWithNavigateToSettingAction +import com.twix.designsystem.theme.CommonColor +import com.twix.designsystem.theme.GrayColor +import com.twix.designsystem.theme.TwixTheme +import com.twix.task_certification.R +import com.twix.task_certification.detail.TaskCertificationDetailViewModel +import com.twix.task_certification.detail.component.CertificatedCard +import com.twix.task_certification.detail.component.PhotologCard +import com.twix.task_certification.detail.component.TaskCertificationDetailTopBar +import com.twix.task_certification.detail.model.TaskCertificationDetailUiState +import com.twix.task_certification.detail.preview.TaskCertificationDetailPreviewProvider +import com.twix.ui.extension.findActivity +import com.twix.ui.extension.hasCameraPermission +import com.twix.ui.extension.noRippleClickable +import kotlinx.coroutines.launch +import org.koin.androidx.compose.koinViewModel +import org.koin.compose.koinInject +import com.twix.designsystem.R as DesR + +@Composable +fun TaskCertificationEditorRoute( + navigateToBack: () -> Unit, + navigateToCertification: (Long) -> Unit, + toastManager: ToastManager = koinInject(), + viewModel: TaskCertificationDetailViewModel = koinViewModel(), +) { + val context = LocalContext.current + val currentContext by rememberUpdatedState(context) + val coroutineScope = rememberCoroutineScope() + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + val permissionLauncher = + rememberLauncherForActivityResult( + ActivityResultContracts.RequestPermission(), + ) { granted -> + + if (granted) { + navigateToCertification(uiState.currentGoalId) + return@rememberLauncherForActivityResult + } + val activity = currentContext.findActivity() ?: return@rememberLauncherForActivityResult + val shouldShowRationale = + ActivityCompat.shouldShowRequestPermissionRationale( + activity, + Manifest.permission.CAMERA, + ) + coroutineScope.launch { + if (!shouldShowRationale) { + toastManager.showCameraPermissionToastWithNavigateToSettingAction(currentContext) + } else { + toastManager.show( + ToastData( + currentContext.getString( + DesR.string.toast_camera_permission_request, + ), + ToastType.ERROR, + ), + ) + } + } + } + + TaskCertificationEditorScreen( + uiState = uiState, + onBack = navigateToBack, + onClickSave = { }, + onClickRetake = { + if (currentContext.hasCameraPermission()) { + navigateToCertification(uiState.currentGoalId) + } else { + permissionLauncher.launch(Manifest.permission.CAMERA) + } + }, + ) +} + +@Composable +fun TaskCertificationEditorScreen( + uiState: TaskCertificationDetailUiState, + onBack: () -> Unit, + onClickSave: () -> Unit, + onClickRetake: (Long) -> Unit, +) { + Scaffold( + topBar = { + TaskCertificationDetailTopBar( + actionTitle = stringResource(DesR.string.word_save), + goalTitle = uiState.currentGoal.goalName, + onBack = onBack, + onClickModify = onClickSave, + modifier = + Modifier + .background(color = CommonColor.White), + ) + }, + ) { innerPadding -> + Column( + Modifier + .padding(innerPadding) + .fillMaxSize() + .background(color = CommonColor.White), + ) { + Spacer(Modifier.height(103.dp)) + + PhotologCard { + CertificatedCard( + imageUrl = uiState.displayedGoalImageUrl, + comment = uiState.displayedGoalComment, + ) + } + + Spacer(Modifier.height(101.dp)) + + AppRoundButton( + text = stringResource(R.string.task_certification_editor_retake), + textColor = GrayColor.C500, + backgroundColor = CommonColor.White, + modifier = + Modifier + .fillMaxWidth() + .height(68.dp) + .padding(horizontal = 30.dp) + .noRippleClickable { onClickRetake(uiState.currentGoalId) }, + ) + } + } +} + +@Preview(showBackground = true) +@Composable +private fun TaskCertificationEditorScreenPreview( + @PreviewParameter(TaskCertificationDetailPreviewProvider::class) + uiState: TaskCertificationDetailUiState, +) { + TwixTheme { + TaskCertificationEditorScreen( + uiState = uiState, + onBack = {}, + onClickSave = {}, + onClickRetake = {}, + ) + } +} diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt b/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt index c80db656..071dba5a 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt @@ -8,8 +8,11 @@ import androidx.navigation.navArgument import androidx.navigation.navigation import com.twix.navigation.NavRoutes import com.twix.navigation.base.NavGraphContributor +import com.twix.navigation.graphViewModel import com.twix.task_certification.certification.TaskCertificationRoute import com.twix.task_certification.detail.TaskCertificationDetailRoute +import com.twix.task_certification.detail.TaskCertificationDetailViewModel +import com.twix.task_certification.editor.TaskCertificationEditorRoute object TaskCertificationGraph : NavGraphContributor { override val graphRoute: NavRoutes @@ -35,11 +38,11 @@ object TaskCertificationGraph : NavGraphContributor { navArgument(NavRoutes.TaskCertificationDetailRoute.ARG_BETWEEN_US) { type = NavType.StringType }, - navArgument(NavRoutes.TaskCertificationDetailRoute.ARG_DATE) { - type = NavType.StringType - }, ), - ) { + ) { backStackEntry -> + val vm: TaskCertificationDetailViewModel = + backStackEntry.graphViewModel(navController, graphRoute.route) + TaskCertificationDetailRoute( navigateToBack = navController::popBackStack, navigateToUpload = { @@ -50,7 +53,30 @@ object TaskCertificationGraph : NavGraphContributor { ) navController.navigate(destination) }, - navigateToEditor = { }, + navigateToEditor = { + navController.navigate(NavRoutes.TaskCertificationEditorRoute.route) + }, + viewModel = vm, + ) + } + + composable( + route = NavRoutes.TaskCertificationEditorRoute.route, + ) { backStackEntry -> + val vm: TaskCertificationDetailViewModel = + backStackEntry.graphViewModel(navController, graphRoute.route) + + TaskCertificationEditorRoute( + viewModel = vm, + navigateToBack = navController::popBackStack, + navigateToCertification = { goalId -> + navController.navigate( + NavRoutes.TaskCertificationRoute.createRoute( + goalId = goalId, + from = NavRoutes.TaskCertificationRoute.From.EDITOR, + ), + ) + }, ) } @@ -67,9 +93,7 @@ object TaskCertificationGraph : NavGraphContributor { ), ) { TaskCertificationRoute( - navigateToBack = { - navController.popBackStack() - }, + navigateToBack = navController::popBackStack, ) } } diff --git a/feature/task-certification/src/main/res/values/strings.xml b/feature/task-certification/src/main/res/values/strings.xml index 8532e3ff..937a8ca3 100644 --- a/feature/task-certification/src/main/res/values/strings.xml +++ b/feature/task-certification/src/main/res/values/strings.xml @@ -12,4 +12,7 @@ %s님은\n아직… 인증샷 조회에 실패했습니다. 찌르기 + + + 다시 찍기 From 4819febd47793441f2946d2356c957dcbd75daf2 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Fri, 13 Feb 2026 22:46:41 +0900 Subject: [PATCH 05/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=ED=8C=8C=ED=8A=B8=EB=84=88=20=EC=9D=B8=EC=A6=9D=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=97=90=EC=84=9C=EB=8A=94=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EC=88=A8=EA=B8=B0=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/TaskCertificationDetail.kt | 12 ++++++++++-- .../component/TaskCertificationDetailTopBar.kt | 10 +++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt index a3e0cabd..25a7be57 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt @@ -139,10 +139,18 @@ fun TaskCertificationDetailScreen( Scaffold( topBar = { TaskCertificationDetailTopBar( - actionTitle = stringResource(DesR.string.word_modify), goalTitle = uiState.currentGoal.goalName, onBack = onBack, - onClickModify = onClickModify, + actionTitle = + when (uiState.currentShow) { + BetweenUs.ME -> stringResource(DesR.string.word_modify) + BetweenUs.PARTNER -> null + }, + onClickModify = + when (uiState.currentShow) { + BetweenUs.ME -> onClickModify + BetweenUs.PARTNER -> null + }, modifier = Modifier .background(color = CommonColor.White), diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt index 2ce38b74..24c81e12 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt @@ -33,9 +33,9 @@ import com.twix.designsystem.R as DesR fun TaskCertificationDetailTopBar( goalTitle: String, onBack: () -> Unit, + actionTitle: String?, + onClickModify: (() -> Unit)?, modifier: Modifier = Modifier, - actionTitle: String? = null, - onClickModify: (() -> Unit)? = null, ) { Column( modifier = @@ -89,7 +89,8 @@ fun TaskCertificationDetailTopBar( } else { GrayColor.C100 }, - ).noRippleClickable { onClickModify?.invoke() }, + ) + .noRippleClickable { onClickModify?.invoke() }, contentAlignment = Alignment.Center, ) { actionTitle?.let { @@ -114,11 +115,14 @@ fun TaskCertificationDetailTopBarPreview() { actionTitle = "수정", goalTitle = "목표 타이틀", onBack = {}, + onClickModify = {}, ) TaskCertificationDetailTopBar( goalTitle = "목표 타이틀", onBack = {}, + actionTitle = null, + onClickModify = null, ) } } From 975d45d4512ac03e69640d80110010ede766e2b9 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Fri, 13 Feb 2026 23:40:30 +0900 Subject: [PATCH 06/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EC=83=81=EC=84=B8=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B2=84=ED=8A=BC=20=EB=85=B8=EC=B6=9C=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/TaskCertificationDetail.kt | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt index 25a7be57..f4379883 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt @@ -141,16 +141,8 @@ fun TaskCertificationDetailScreen( TaskCertificationDetailTopBar( goalTitle = uiState.currentGoal.goalName, onBack = onBack, - actionTitle = - when (uiState.currentShow) { - BetweenUs.ME -> stringResource(DesR.string.word_modify) - BetweenUs.PARTNER -> null - }, - onClickModify = - when (uiState.currentShow) { - BetweenUs.ME -> onClickModify - BetweenUs.PARTNER -> null - }, + actionTitle = if (uiState.canModify) stringResource(DesR.string.word_modify) else null, + onClickModify = if (uiState.canModify) onClickModify else null, modifier = Modifier .background(color = CommonColor.White), From 2ef044cac42be73a6036cf94ca6998ff6d2e276e Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 14 Feb 2026 01:28:40 +0900 Subject: [PATCH 07/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20kotlinx.serialization?= =?UTF-8?q?.json=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/navigation/build.gradle.kts | 4 ++++ feature/task-certification/build.gradle.kts | 1 + 2 files changed, 5 insertions(+) diff --git a/core/navigation/build.gradle.kts b/core/navigation/build.gradle.kts index 9f20a596..b8ccc2de 100644 --- a/core/navigation/build.gradle.kts +++ b/core/navigation/build.gradle.kts @@ -7,3 +7,7 @@ plugins { android { namespace = "com.twix.navigation" } + +dependencies { + implementation(libs.kotlinx.serialization.json) +} diff --git a/feature/task-certification/build.gradle.kts b/feature/task-certification/build.gradle.kts index 4689af48..eb6da430 100644 --- a/feature/task-certification/build.gradle.kts +++ b/feature/task-certification/build.gradle.kts @@ -10,4 +10,5 @@ dependencies { implementation(libs.bundles.cameraX) implementation(libs.guava) + implementation(libs.kotlinx.serialization.json) } From 2d394b4f5c189e6a11aa4b8e28ac9a9c5ebb6327 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 14 Feb 2026 01:29:44 +0900 Subject: [PATCH 08/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=9D=B8=EC=A6=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=ED=99=94=EB=A9=B4=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EC=8B=9C=20=EC=9D=B8=EC=A6=9D=20=EC=A0=95=EB=B3=B4=20=EC=A0=84?= =?UTF-8?q?=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/twix/navigation/NavRoutes.kt | 14 ++++++++++- .../serializer/TaskCertificationSerializer.kt | 12 +++++++++ .../detail/TaskCertificationDetail.kt | 7 +++--- .../model/TaskCertificationDetailUiState.kt | 10 ++++++++ .../navigation/TaskCertificationGraph.kt | 25 ++++++++----------- 5 files changed, 50 insertions(+), 18 deletions(-) create mode 100644 core/navigation/src/main/java/com/twix/navigation/serializer/TaskCertificationSerializer.kt diff --git a/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt b/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt index d6e31e82..ac032da1 100644 --- a/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt +++ b/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt @@ -1,5 +1,8 @@ package com.twix.navigation +import android.net.Uri +import com.twix.navigation.serializer.TaskCertificationSerializer +import kotlinx.serialization.json.Json import java.time.LocalDate /** @@ -60,7 +63,16 @@ sealed class NavRoutes( ) = "task_certification/$goalId/${from.name}" } - object TaskCertificationEditorRoute : NavRoutes("task_certification_editor") + object TaskCertificationEditorRoute : + NavRoutes("task_certification_editor/{data}") { + const val ARG_DATA = "data" + + fun createRoute(data: TaskCertificationSerializer): String { + val json = Json.encodeToString(data) + val encoded = Uri.encode(json) + return "task_certification_editor/$encoded" + } + } /** * OnboardingGraph diff --git a/core/navigation/src/main/java/com/twix/navigation/serializer/TaskCertificationSerializer.kt b/core/navigation/src/main/java/com/twix/navigation/serializer/TaskCertificationSerializer.kt new file mode 100644 index 00000000..bfb16a5b --- /dev/null +++ b/core/navigation/src/main/java/com/twix/navigation/serializer/TaskCertificationSerializer.kt @@ -0,0 +1,12 @@ +package com.twix.navigation.serializer + +import kotlinx.serialization.Serializable + +@Serializable +data class TaskCertificationSerializer( + val nickname: String, + val goalName: String, + val photologId: Long, + val imageUrl: String, + val comment: String?, +) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt index f4379883..e1768bd6 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt @@ -52,16 +52,17 @@ import com.twix.ui.base.ObserveAsEvents import com.twix.ui.extension.findActivity import com.twix.ui.extension.hasCameraPermission import kotlinx.coroutines.launch +import org.koin.androidx.compose.koinViewModel import org.koin.compose.koinInject import com.twix.designsystem.R as DesR @Composable fun TaskCertificationDetailRoute( - viewModel: TaskCertificationDetailViewModel, navigateToBack: () -> Unit, navigateToUpload: (Long) -> Unit, - navigateToEditor: () -> Unit, + navigateToEditor: (TaskCertificationDetailUiState) -> Unit, toastManager: ToastManager = koinInject(), + viewModel: TaskCertificationDetailViewModel = koinViewModel(), ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() val context = LocalContext.current @@ -112,7 +113,7 @@ fun TaskCertificationDetailRoute( TaskCertificationDetailScreen( uiState = uiState, onBack = navigateToBack, - onClickModify = navigateToEditor, + onClickModify = { navigateToEditor(uiState) }, onClickReaction = { viewModel.dispatch(TaskCertificationDetailIntent.Reaction(it)) }, onClickUpload = { if (currentContext.hasCameraPermission()) { diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt index b0326e81..2a277b8d 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt @@ -3,6 +3,7 @@ package com.twix.task_certification.detail.model import androidx.compose.runtime.Immutable import com.twix.domain.model.enums.BetweenUs import com.twix.domain.model.enums.GoalReactionType +import com.twix.navigation.serializer.TaskCertificationSerializer import com.twix.ui.base.State @Immutable @@ -60,4 +61,13 @@ data class TaskCertificationDetailUiState( ) fun updatePartnerReaction(type: GoalReactionType) = copy(photoLogs = photoLogs.updatePartnerReaction(currentGoalId, type)) + + fun toSerializer() = + TaskCertificationSerializer( + nickname = photoLogs.myNickname, + goalName = currentGoal.goalName, + photologId = currentGoal.myPhotolog?.photologId ?: -1, + imageUrl = currentGoal.myPhotolog?.imageUrl ?: "", + comment = currentGoal.myPhotolog?.comment, + ) } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt b/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt index 071dba5a..187d744b 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt @@ -8,10 +8,8 @@ import androidx.navigation.navArgument import androidx.navigation.navigation import com.twix.navigation.NavRoutes import com.twix.navigation.base.NavGraphContributor -import com.twix.navigation.graphViewModel import com.twix.task_certification.certification.TaskCertificationRoute import com.twix.task_certification.detail.TaskCertificationDetailRoute -import com.twix.task_certification.detail.TaskCertificationDetailViewModel import com.twix.task_certification.editor.TaskCertificationEditorRoute object TaskCertificationGraph : NavGraphContributor { @@ -39,10 +37,7 @@ object TaskCertificationGraph : NavGraphContributor { type = NavType.StringType }, ), - ) { backStackEntry -> - val vm: TaskCertificationDetailViewModel = - backStackEntry.graphViewModel(navController, graphRoute.route) - + ) { TaskCertificationDetailRoute( navigateToBack = navController::popBackStack, navigateToUpload = { @@ -53,21 +48,23 @@ object TaskCertificationGraph : NavGraphContributor { ) navController.navigate(destination) }, - navigateToEditor = { - navController.navigate(NavRoutes.TaskCertificationEditorRoute.route) + navigateToEditor = { uiState -> + val serializer = uiState.toSerializer() + navController.navigate(NavRoutes.TaskCertificationEditorRoute.createRoute(serializer)) }, - viewModel = vm, ) } composable( route = NavRoutes.TaskCertificationEditorRoute.route, - ) { backStackEntry -> - val vm: TaskCertificationDetailViewModel = - backStackEntry.graphViewModel(navController, graphRoute.route) - + arguments = + listOf( + navArgument(NavRoutes.TaskCertificationEditorRoute.ARG_DATA) { + type = NavType.StringType + }, + ), + ) { TaskCertificationEditorRoute( - viewModel = vm, navigateToBack = navController::popBackStack, navigateToCertification = { goalId -> navController.navigate( From e6f37ac56fe1043bd00d10787f5621a1df2b0a55 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 14 Feb 2026 01:33:12 +0900 Subject: [PATCH 09/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=9D=B8=EC=A6=9D?= =?UTF-8?q?=EC=83=B7=20=ED=8E=B8=EC=A7=91=20=ED=99=94=EB=A9=B4=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 - 인증샷 편집 화면 및 ViewModel을 추가했습니다. - 촬영된 인증샷 이미지와 코멘트를 표시합니다. - '다시찍기' 버튼을 통해 카메라 화면으로 이동할 수 있습니다. - DI 설정에 `TaskCertificationEditorViewModel`을 추가했습니다. --- .../java/com/twix/navigation/NavRoutes.kt | 4 +- .../TaskCertificationDetailTopBar.kt | 3 +- .../di/TaskCertificationModule.kt | 2 + .../editor/TaskCertificationEditorRoute.kt | 75 +++++++++++++------ .../TaskCertificationEditorViewModel.kt | 42 +++++++++++ .../model/TaskCertificationEditorIntent.kt | 5 ++ .../TaskCertificationEditorSideEffect.kt | 5 ++ .../model/TaskCertificationEditorUiState.kt | 24 ++++++ .../navigation/TaskCertificationGraph.kt | 3 +- 9 files changed, 135 insertions(+), 28 deletions(-) create mode 100644 feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt create mode 100644 feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorIntent.kt create mode 100644 feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorSideEffect.kt create mode 100644 feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt diff --git a/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt b/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt index ac032da1..6b3ef1dd 100644 --- a/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt +++ b/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt @@ -51,6 +51,8 @@ sealed class NavRoutes( const val ARG_GOAL_ID = "goalId" const val ARG_FROM = "from" + private const val NOT_NEED_GOAL_ID = -1L + enum class From { HOME, DETAIL, @@ -58,7 +60,7 @@ sealed class NavRoutes( } fun createRoute( - goalId: Long, + goalId: Long = NOT_NEED_GOAL_ID, from: From, ) = "task_certification/$goalId/${from.name}" } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt index 24c81e12..6cccbc62 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt @@ -89,8 +89,7 @@ fun TaskCertificationDetailTopBar( } else { GrayColor.C100 }, - ) - .noRippleClickable { onClickModify?.invoke() }, + ).noRippleClickable { onClickModify?.invoke() }, contentAlignment = Alignment.Center, ) { actionTitle?.let { diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/di/TaskCertificationModule.kt b/feature/task-certification/src/main/java/com/twix/task_certification/di/TaskCertificationModule.kt index 6d221630..367dba25 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/di/TaskCertificationModule.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/di/TaskCertificationModule.kt @@ -6,6 +6,7 @@ import com.twix.task_certification.certification.TaskCertificationViewModel import com.twix.task_certification.certification.camera.Camera import com.twix.task_certification.certification.camera.CaptureCamera import com.twix.task_certification.detail.TaskCertificationDetailViewModel +import com.twix.task_certification.editor.TaskCertificationEditorViewModel import com.twix.task_certification.navigation.TaskCertificationGraph import org.koin.core.module.dsl.viewModelOf import org.koin.core.qualifier.named @@ -15,6 +16,7 @@ val taskCertificationModule = module { viewModelOf(::TaskCertificationDetailViewModel) viewModelOf(::TaskCertificationViewModel) + viewModelOf(::TaskCertificationEditorViewModel) factory { CaptureCamera(get()) } single(named(NavRoutes.TaskCertificationRoute.route)) { TaskCertificationGraph } } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt index 9cbdeba4..1b79a245 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt @@ -4,26 +4,33 @@ import android.Manifest import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts 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.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.core.app.ActivityCompat import androidx.lifecycle.compose.collectAsStateWithLifecycle +import coil3.compose.AsyncImage +import coil3.request.ImageRequest +import coil3.request.crossfade import com.twix.designsystem.components.button.AppRoundButton +import com.twix.designsystem.components.comment.CommentTextField import com.twix.designsystem.components.toast.ToastManager import com.twix.designsystem.components.toast.model.ToastData import com.twix.designsystem.components.toast.model.ToastType @@ -32,12 +39,9 @@ import com.twix.designsystem.theme.CommonColor import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme import com.twix.task_certification.R -import com.twix.task_certification.detail.TaskCertificationDetailViewModel -import com.twix.task_certification.detail.component.CertificatedCard import com.twix.task_certification.detail.component.PhotologCard import com.twix.task_certification.detail.component.TaskCertificationDetailTopBar -import com.twix.task_certification.detail.model.TaskCertificationDetailUiState -import com.twix.task_certification.detail.preview.TaskCertificationDetailPreviewProvider +import com.twix.task_certification.editor.model.TaskCertificationEditorUiState import com.twix.ui.extension.findActivity import com.twix.ui.extension.hasCameraPermission import com.twix.ui.extension.noRippleClickable @@ -49,9 +53,9 @@ import com.twix.designsystem.R as DesR @Composable fun TaskCertificationEditorRoute( navigateToBack: () -> Unit, - navigateToCertification: (Long) -> Unit, + navigateToCertification: () -> Unit, toastManager: ToastManager = koinInject(), - viewModel: TaskCertificationDetailViewModel = koinViewModel(), + viewModel: TaskCertificationEditorViewModel = koinViewModel(), ) { val context = LocalContext.current val currentContext by rememberUpdatedState(context) @@ -64,7 +68,7 @@ fun TaskCertificationEditorRoute( ) { granted -> if (granted) { - navigateToCertification(uiState.currentGoalId) + navigateToCertification() return@rememberLauncherForActivityResult } val activity = currentContext.findActivity() ?: return@rememberLauncherForActivityResult @@ -92,10 +96,11 @@ fun TaskCertificationEditorRoute( TaskCertificationEditorScreen( uiState = uiState, onBack = navigateToBack, + onClickComment = {}, onClickSave = { }, onClickRetake = { if (currentContext.hasCameraPermission()) { - navigateToCertification(uiState.currentGoalId) + navigateToCertification() } else { permissionLauncher.launch(Manifest.permission.CAMERA) } @@ -105,16 +110,17 @@ fun TaskCertificationEditorRoute( @Composable fun TaskCertificationEditorScreen( - uiState: TaskCertificationDetailUiState, + uiState: TaskCertificationEditorUiState, onBack: () -> Unit, + onClickComment: () -> Unit, onClickSave: () -> Unit, - onClickRetake: (Long) -> Unit, + onClickRetake: () -> Unit, ) { Scaffold( topBar = { TaskCertificationDetailTopBar( actionTitle = stringResource(DesR.string.word_save), - goalTitle = uiState.currentGoal.goalName, + goalTitle = uiState.goalName, onBack = onBack, onClickModify = onClickSave, modifier = @@ -127,15 +133,36 @@ fun TaskCertificationEditorScreen( Modifier .padding(innerPadding) .fillMaxSize() - .background(color = CommonColor.White), + .background(color = CommonColor.White) + .imePadding(), ) { Spacer(Modifier.height(103.dp)) PhotologCard { - CertificatedCard( - imageUrl = uiState.displayedGoalImageUrl, - comment = uiState.displayedGoalComment, - ) + PhotologCard { + Box(Modifier.fillMaxSize()) { + AsyncImage( + model = + ImageRequest + .Builder(LocalContext.current) + .data(uiState.imageUrl) + .crossfade(true) + .build(), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize(), + ) + CommentTextField( + uiModel = uiState.comment, + enabled = false, + modifier = + Modifier + .align(Alignment.BottomCenter) + .padding(bottom = 28.dp) + .noRippleClickable { onClickComment() }, + ) + } + } } Spacer(Modifier.height(101.dp)) @@ -149,7 +176,7 @@ fun TaskCertificationEditorScreen( .fillMaxWidth() .height(68.dp) .padding(horizontal = 30.dp) - .noRippleClickable { onClickRetake(uiState.currentGoalId) }, + .noRippleClickable { onClickRetake() }, ) } } @@ -157,14 +184,16 @@ fun TaskCertificationEditorScreen( @Preview(showBackground = true) @Composable -private fun TaskCertificationEditorScreenPreview( - @PreviewParameter(TaskCertificationDetailPreviewProvider::class) - uiState: TaskCertificationDetailUiState, -) { +private fun TaskCertificationEditorScreenPreview() { TwixTheme { TaskCertificationEditorScreen( - uiState = uiState, + uiState = + TaskCertificationEditorUiState( + nickname = "페토", + goalName = "아이스크림 먹기", + ), onBack = {}, + onClickComment = {}, onClickSave = {}, onClickRetake = {}, ) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt new file mode 100644 index 00000000..0772bbd6 --- /dev/null +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt @@ -0,0 +1,42 @@ +package com.twix.task_certification.editor + +import android.net.Uri +import androidx.lifecycle.SavedStateHandle +import com.twix.domain.repository.PhotoLogRepository +import com.twix.navigation.NavRoutes +import com.twix.navigation.serializer.TaskCertificationSerializer +import com.twix.task_certification.editor.model.TaskCertificationEditorIntent +import com.twix.task_certification.editor.model.TaskCertificationEditorSideEffect +import com.twix.task_certification.editor.model.TaskCertificationEditorUiState +import com.twix.ui.base.BaseViewModel +import kotlinx.serialization.json.Json + +class TaskCertificationEditorViewModel( + private val photologRepository: PhotoLogRepository, + savedStateHandle: SavedStateHandle, +) : BaseViewModel( + TaskCertificationEditorUiState(), + ) { + val serializer = + requireNotNull( + savedStateHandle.get(NavRoutes.TaskCertificationEditorRoute.ARG_DATA)?.let { encoded -> + val json = Uri.decode(encoded) + Json.decodeFromString(json) + }, + ) { SERIALIZER_NOT_FOUND } + + init { + reduceInitialState() + } + + private fun reduceInitialState() { + reduce { updateInitialState(serializer) } + } + + override suspend fun handleIntent(intent: TaskCertificationEditorIntent) { + } + + companion object { + private const val SERIALIZER_NOT_FOUND = "Serializer Not Found" + } +} diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorIntent.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorIntent.kt new file mode 100644 index 00000000..dbaaf9a2 --- /dev/null +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorIntent.kt @@ -0,0 +1,5 @@ +package com.twix.task_certification.editor.model + +import com.twix.ui.base.Intent + +sealed interface TaskCertificationEditorIntent : Intent diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorSideEffect.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorSideEffect.kt new file mode 100644 index 00000000..94b298ba --- /dev/null +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorSideEffect.kt @@ -0,0 +1,5 @@ +package com.twix.task_certification.editor.model + +import com.twix.ui.base.SideEffect + +sealed interface TaskCertificationEditorSideEffect : SideEffect diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt new file mode 100644 index 00000000..923be7e4 --- /dev/null +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt @@ -0,0 +1,24 @@ +package com.twix.task_certification.editor.model + +import androidx.compose.runtime.Immutable +import com.twix.designsystem.components.comment.model.CommentUiModel +import com.twix.navigation.serializer.TaskCertificationSerializer +import com.twix.ui.base.State + +@Immutable +data class TaskCertificationEditorUiState( + val nickname: String = "", + val goalName: String = "", + val photologId: Long = -1, + val imageUrl: String = "", + val comment: CommentUiModel = CommentUiModel(), +) : State { + fun updateInitialState(serializer: TaskCertificationSerializer) = + copy( + nickname = serializer.nickname, + goalName = serializer.goalName, + photologId = serializer.photologId, + imageUrl = serializer.imageUrl, + comment = CommentUiModel(serializer.comment.orEmpty()), + ) +} diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt b/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt index 187d744b..4f07975f 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt @@ -66,10 +66,9 @@ object TaskCertificationGraph : NavGraphContributor { ) { TaskCertificationEditorRoute( navigateToBack = navController::popBackStack, - navigateToCertification = { goalId -> + navigateToCertification = { navController.navigate( NavRoutes.TaskCertificationRoute.createRoute( - goalId = goalId, from = NavRoutes.TaskCertificationRoute.From.EDITOR, ), ) From 638b2acf4799dedf6db0ec7b8bd34b0e9b19ceda Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 14 Feb 2026 01:53:06 +0900 Subject: [PATCH 10/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20Serialization=20?= =?UTF-8?q?=ED=94=8C=EB=9F=AC=EA=B7=B8=EC=9D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/navigation/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/core/navigation/build.gradle.kts b/core/navigation/build.gradle.kts index b8ccc2de..1690e00a 100644 --- a/core/navigation/build.gradle.kts +++ b/core/navigation/build.gradle.kts @@ -2,6 +2,7 @@ plugins { alias(libs.plugins.twix.android.library) alias(libs.plugins.twix.android.compose) alias(libs.plugins.twix.koin) + alias(libs.plugins.serialization) } android { From a4ac5f6bb62f17fd01f6742a2920ff3b9ca04191 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 14 Feb 2026 12:11:38 +0900 Subject: [PATCH 11/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EB=AF=B8=EC=82=AC=EC=9A=A9=20=ED=8C=8C=EB=9D=BC=EB=AF=B8?= =?UTF-8?q?=ED=84=B0=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/comment/CommentTextField.kt | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt index d76fdd50..f5953a16 100644 --- a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt @@ -21,10 +21,7 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.layout.boundsInRoot -import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction @@ -50,7 +47,6 @@ fun CommentTextField( enabled: Boolean = true, onCommitComment: (String) -> Unit = {}, onFocusChanged: (Boolean) -> Unit = {}, - onPositioned: (Rect) -> Unit = {}, ) { val focusManager = LocalFocusManager.current val focusRequester = remember { FocusRequester() } @@ -74,15 +70,17 @@ fun CommentTextField( } LaunchedEffect(uiModel.isFocused) { + /** + * 외부에서 포커스 상태를 제어하는 경우 + * e.g 사진 촬영 & 사진 선택 + * */ if (uiModel.isFocused) focusRequester.requestFocus() } Box( modifier = modifier - .onGloballyPositioned { coordinates -> - onPositioned(coordinates.boundsInRoot()) - }.noRippleClickable { + .noRippleClickable { focusRequester.requestFocus() }, ) { @@ -159,7 +157,6 @@ private fun CommentTextFieldPreview() { CommentTextField( uiModel = CommentUiModel(text, isFocused), onFocusChanged = { isFocused = it }, - onPositioned = {}, ) } } From c2382feb826092d27a8d7c1f028d448bf2953039 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 14 Feb 2026 12:11:59 +0900 Subject: [PATCH 12/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20CommentB?= =?UTF-8?q?ox=EC=9D=98=20bottom=20padding=EC=9D=84=2024dp=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/designsystem/components/comment/CommentBox.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentBox.kt b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentBox.kt index 595136bd..cce7f80e 100644 --- a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentBox.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentBox.kt @@ -33,13 +33,14 @@ fun CommentBox( ) Spacer(modifier = Modifier.height(8.dp)) + CommentTextField( uiModel = uiModel, onCommitComment = onCommentChanged, onFocusChanged = onFocusChanged, modifier = Modifier - .padding(bottom = 20.dp), + .padding(bottom = 24.dp), ) } } From f5ff5865e56828db59723860b2d49c105b6c20a3 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 14 Feb 2026 14:39:38 +0900 Subject: [PATCH 13/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20`ic=5Fcl?= =?UTF-8?q?ose=5Fc100`=20=EB=A6=AC=EC=86=8C=EC=8A=A4=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EC=9D=84=20design-system=20=EB=AA=A8=EB=93=88=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20-=20TopBar=20=EB=86=92=EC=9D=B4=20?= =?UTF-8?q?=EA=B0=92=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../design-system}/src/main/res/drawable/ic_close_c100.xml | 0 .../certification/component/TaskCertificationTopBar.kt | 4 +++- 2 files changed, 3 insertions(+), 1 deletion(-) rename {feature/task-certification => core/design-system}/src/main/res/drawable/ic_close_c100.xml (100%) diff --git a/feature/task-certification/src/main/res/drawable/ic_close_c100.xml b/core/design-system/src/main/res/drawable/ic_close_c100.xml similarity index 100% rename from feature/task-certification/src/main/res/drawable/ic_close_c100.xml rename to core/design-system/src/main/res/drawable/ic_close_c100.xml diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/TaskCertificationTopBar.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/TaskCertificationTopBar.kt index 87309324..e5b962d5 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/TaskCertificationTopBar.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/TaskCertificationTopBar.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -12,8 +13,8 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.twix.designsystem.R import com.twix.designsystem.theme.GrayColor -import com.twix.task_certification.R import com.twix.ui.extension.noRippleClickable @Composable @@ -25,6 +26,7 @@ internal fun TaskCertificationTopBar( modifier = modifier .fillMaxWidth() + .height(72.dp) .background(color = GrayColor.C500), ) { Image( From 5ae6ff15cc3aaf30109a90687f53291d6fb1cd34 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Fri, 13 Feb 2026 21:43:35 +0900 Subject: [PATCH 14/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EC=83=81=EC=84=B8=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?TopBar=20=EA=B5=AC=EC=A1=B0=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Conflicts: # feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt --- .../task_certification/detail/TaskCertificationDetail.kt | 4 ++-- .../detail/component/TaskCertificationDetailTopBar.kt | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt index e1768bd6..4180991f 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt @@ -140,10 +140,10 @@ fun TaskCertificationDetailScreen( Scaffold( topBar = { TaskCertificationDetailTopBar( + actionTitle = stringResource(DesR.string.word_modify), goalTitle = uiState.currentGoal.goalName, onBack = onBack, - actionTitle = if (uiState.canModify) stringResource(DesR.string.word_modify) else null, - onClickModify = if (uiState.canModify) onClickModify else null, + onClickModify = onClickModify, modifier = Modifier .background(color = CommonColor.White), diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt index 6cccbc62..2ce38b74 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt @@ -33,9 +33,9 @@ import com.twix.designsystem.R as DesR fun TaskCertificationDetailTopBar( goalTitle: String, onBack: () -> Unit, - actionTitle: String?, - onClickModify: (() -> Unit)?, modifier: Modifier = Modifier, + actionTitle: String? = null, + onClickModify: (() -> Unit)? = null, ) { Column( modifier = @@ -114,14 +114,11 @@ fun TaskCertificationDetailTopBarPreview() { actionTitle = "수정", goalTitle = "목표 타이틀", onBack = {}, - onClickModify = {}, ) TaskCertificationDetailTopBar( goalTitle = "목표 타이틀", onBack = {}, - actionTitle = null, - onClickModify = null, ) } } From 475e5a4140bcb88db4cd252edec40cbffa850eff Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 14 Feb 2026 21:05:49 +0900 Subject: [PATCH 15/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20`CommentAnchorFrame`?= =?UTF-8?q?=20=EC=BB=B4=ED=8F=AC=EC=A0=80=EB=B8=94=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/comment/CommentAnchorFrame.kt | 105 ++++++++++++++++++ .../components/comment/CommentBox.kt | 11 +- .../certification/TaskCertificationScreen.kt | 104 ++--------------- .../TaskCertificationViewModel.kt | 5 +- .../model/TaskCertificationUiState.kt | 8 +- .../editor/TaskCertificationEditorRoute.kt | 96 ++++++++-------- 6 files changed, 176 insertions(+), 153 deletions(-) create mode 100644 core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentAnchorFrame.kt diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentAnchorFrame.kt b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentAnchorFrame.kt new file mode 100644 index 00000000..cce5a1ea --- /dev/null +++ b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentAnchorFrame.kt @@ -0,0 +1,105 @@ +package com.twix.designsystem.components.comment + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.ime +import androidx.compose.foundation.layout.offset +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.dp +import com.twix.designsystem.components.comment.model.CommentUiModel +import com.twix.designsystem.theme.DimmedColor +import com.twix.ui.extension.noRippleClickable + +/** + * 특정 UI 요소(Anchor) 하단에 [CommentBox]를 배치하고, 키보드 활성화 상태에 따라 위치를 동적으로 조정하는 프레임 컴포저블 + * + * 이 컴포저블은 평상시에는 [anchorBottom] 좌표를 기준으로 배치 + * 키보드가 올라와 코맨트창이 가려질 경우 키보드 바로 위로 위치를 자동으로 이동 + * + * @param uiModel 댓글창의 상태(텍스트, 포커스 상태)를 담고 있는 데이터 모델 + * @param anchorBottom 댓글창 배치의 기준이 되는 상위 요소의 바닥(Bottom) Y 좌표 (px 단위) + * @param onCommentChanged 댓글 내용이 변경될 때 호출되는 콜백 + * @param onFocusChanged 댓글창의 포커스 상태가 변경될 때 호출되는 콜백 (포커스 시 배경 딤 처리 등에 사용) + * @param modifier 레이아웃 수정을 위한 [Modifier] + */ +@Composable +fun CommentAnchorFrame( + uiModel: CommentUiModel, + anchorBottom: Float, + onCommentChanged: (String) -> Unit, + onFocusChanged: (Boolean) -> Unit, + modifier: Modifier = Modifier, +) { + if (anchorBottom == 0f) return + + val density = LocalDensity.current + val focusManager = LocalFocusManager.current + val imeBottom = WindowInsets.ime.getBottom(density) + val paddingBottom = with(density) { 24.dp.toPx() } + + var commentBoxHeight by remember { mutableFloatStateOf(0f) } + val defaultY = anchorBottom - commentBoxHeight - paddingBottom + + BoxWithConstraints(modifier = modifier.fillMaxSize()) { + val screenHeight = constraints.maxHeight.toFloat() + val keyboardTop = screenHeight - imeBottom + + AnimatedVisibility( + visible = uiModel.isFocused, + enter = fadeIn(), + exit = fadeOut(), + ) { + Box( + modifier = + Modifier + .fillMaxSize() + .background(DimmedColor.D070) + .noRippleClickable { focusManager.clearFocus() }, + ) + } + CommentBox( + uiModel = uiModel, + onCommentChanged = onCommentChanged, + onFocusChanged = onFocusChanged, + onHeightMeasured = { height -> + if (commentBoxHeight != height) commentBoxHeight = height + }, + modifier = + Modifier + .fillMaxWidth() + .offset { + /** + * 키보드가 활성화되었고, 댓글창이 키보드 위치보다 아래에 있어 가려지는 경우 + * */ + val targetY = + if (imeBottom > 0 && (defaultY + commentBoxHeight) > keyboardTop) { + /** + * 화면 전체 높이 - 키보드 높이 - 코멘트 높이 + * */ + (screenHeight - imeBottom - commentBoxHeight).toInt() + } else { + /** + * 키보드가 없거나, 댓글창이 키보드에 가려지지 않는 경우 + * */ + defaultY.toInt() + } + IntOffset(x = 0, y = targetY) + }, + ) + } +} diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentBox.kt b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentBox.kt index cce7f80e..1eaa61a6 100644 --- a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentBox.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentBox.kt @@ -3,10 +3,10 @@ package com.twix.designsystem.components.comment import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.twix.designsystem.R @@ -20,11 +20,15 @@ fun CommentBox( uiModel: CommentUiModel, onCommentChanged: (String) -> Unit, onFocusChanged: (Boolean) -> Unit, + onHeightMeasured: (Float) -> Unit, modifier: Modifier = Modifier, ) { Column( horizontalAlignment = Alignment.CenterHorizontally, - modifier = modifier, + modifier = + modifier.onSizeChanged { size -> + onHeightMeasured(size.height.toFloat()) + }, ) { AppText( text = if (uiModel.isFocused) stringResource(R.string.comment_condition_guide) else "", @@ -38,9 +42,6 @@ fun CommentBox( uiModel = uiModel, onCommitComment = onCommentChanged, onFocusChanged = onFocusChanged, - modifier = - Modifier - .padding(bottom = 24.dp), ) } } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt index c92e5412..f1e24bf4 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt @@ -4,20 +4,12 @@ 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.AnimatedVisibility -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.ime -import androidx.compose.foundation.layout.offset import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect @@ -30,21 +22,17 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.twix.designsystem.components.comment.CIRCLE_SIZE -import com.twix.designsystem.components.comment.CommentBox +import com.twix.designsystem.components.comment.CommentAnchorFrame import com.twix.designsystem.components.text.AppText import com.twix.designsystem.components.toast.ToastManager import com.twix.designsystem.components.toast.model.ToastData import com.twix.designsystem.components.toast.model.ToastType -import com.twix.designsystem.theme.DimmedColor import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme import com.twix.domain.model.enums.AppTextStyle @@ -198,27 +186,7 @@ private fun TaskCertificationScreen( onFocusChanged: (Boolean) -> Unit, ) { val focusManager = LocalFocusManager.current - var previewBoxBottom by remember { mutableFloatStateOf(0f) } - val density = LocalDensity.current - val imeBottom = WindowInsets.ime.getBottom(density) - - /** - * Comment Circle UI의 높이 - * */ - val commentBoxHeight = with(density) { CIRCLE_SIZE.toPx() } - - /** - * [기본 위치 설정] - * 프리뷰 박스 하단(previewBoxBottom)을 기준으로 배치 - * circlePx * 2 만큼 위로 올리고 패딩(+20f) - */ - val defaultY = previewBoxBottom - (commentBoxHeight * 2) + 20f - - /** - * 키보드가 올라왔을 때 CommentBox와 키보드 사이의 최소 간격 - */ - val keyboardPadding = 60f Box( modifier = @@ -227,11 +195,7 @@ private fun TaskCertificationScreen( .background(GrayColor.C500) .noRippleClickable { focusManager.clearFocus() }, ) { - Column( - Modifier - .fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { TaskCertificationTopBar(onClickClose = onClickClose) Spacer(modifier = Modifier.height(24.26.dp)) @@ -271,64 +235,12 @@ private fun TaskCertificationScreen( ) } - /** - * 프리뷰 박스의 하단 좌표(previewBoxBottom)가 측정되었을 때만 렌더링 시작 - * */ - if (previewBoxBottom != 0f) { - BoxWithConstraints(modifier = Modifier.fillMaxSize()) { - /** - * 화면의 전체 높이와 키보드 상단 경계선 좌표 계산 - * */ - val screenHeight = constraints.maxHeight.toFloat() - val keyboardTop = screenHeight - imeBottom - - /** - * Dimmed 배경 레이어 - * 키보드가 올라왔을 때만 나타나도록 하며, 클릭 시 포커스를 해제 - */ - AnimatedVisibility( - visible = uiState.commentUiModel.isFocused, - enter = fadeIn(), - exit = fadeOut(), - ) { - Box( - modifier = - Modifier - .fillMaxSize() - .background(DimmedColor.D070) - .noRippleClickable { focusManager.clearFocus() }, - ) - } - - CommentBox( - uiModel = uiState.commentUiModel, - onCommentChanged = onCommentChanged, - onFocusChanged = onFocusChanged, - modifier = - Modifier - .fillMaxWidth() - .offset { - IntOffset( - x = 0, - y = - if (imeBottom > 0 && (defaultY + commentBoxHeight) > keyboardTop) { - /** - * 키보드가 활성화되었고(imeBottom > 0), - * 기존 위치(defaultY)가 키보드에 가려질 상황일 때만 실행됩니다. - * imeBottom은 키보드가 닫히기 시작하면 실시간으로 줄어들어 0에 도달합니다. - * 이때 if (imeBottom > 0) 조건이 false로 바뀌면서 즉시 else 문인 defaultY 위치로 점프하게 되는데, - * 만약 시스템의 WindowInsets 애니메이션이 끝나기 전이나 레이아웃 재계산 중에 일시적으로 기준 좌표를 잃어 - * CommentBox가 화면 최하단을 갔다 원래 위치로 돌아오는 "튀는" 현상이 발생합니다. - */ - (screenHeight - imeBottom - commentBoxHeight - keyboardPadding).toInt() - } else { - defaultY.toInt() - }, - ) - }, - ) - } - } + CommentAnchorFrame( + uiModel = uiState.comment, + anchorBottom = previewBoxBottom, + onCommentChanged = onCommentChanged, + onFocusChanged = onFocusChanged, + ) } } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt index 87db9fac..79c60f4c 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt @@ -93,7 +93,7 @@ class TaskCertificationViewModel( val capture = currentState.capture if (capture !is CaptureStatus.Captured) return@launch - if (!currentState.commentUiModel.canUpload) { + if (!currentState.comment.canUpload) { reduce { showCommentError() } delay(ERROR_DISPLAY_DURATION_MS) reduce { hideCommentError() } @@ -134,13 +134,14 @@ class TaskCertificationViewModel( PhotologParam( goalId = goalId, fileName = fileName, - comment = currentState.commentUiModel.comment, + comment = currentState.comment.comment, verificationDate = LocalDate.now(), ), ) }, onSuccess = { when (NavRoutes.TaskCertificationRoute.From.valueOf(from)) { + NavRoutes.TaskCertificationRoute.From.EDITOR -> Unit NavRoutes.TaskCertificationRoute.From.DETAIL -> taskCertificationRefreshBus.notifyChanged() NavRoutes.TaskCertificationRoute.From.HOME -> goalRefreshBus.notifyGoalListChanged() } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationUiState.kt index 7283fcc4..dceabb2f 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationUiState.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationUiState.kt @@ -12,11 +12,11 @@ data class TaskCertificationUiState( val torch: TorchStatus = TorchStatus.Off, val lens: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA, val preview: CameraPreview? = null, - val commentUiModel: CommentUiModel = CommentUiModel(), + val comment: CommentUiModel = CommentUiModel(), val showCommentError: Boolean = false, ) : State { val hasMaxCommentLength: Boolean - get() = commentUiModel.hasMaxCommentLength + get() = comment.hasMaxCommentLength val showTorch: Boolean get() = capture is CaptureStatus.NotCaptured && lens == CameraSelector.DEFAULT_BACK_CAMERA @@ -47,9 +47,9 @@ data class TaskCertificationUiState( fun removePicture(): TaskCertificationUiState = copy(capture = CaptureStatus.NotCaptured) - fun updateComment(comment: String) = copy(commentUiModel = commentUiModel.updateComment(comment)) + fun updateComment(newComment: String) = copy(comment = comment.updateComment(newComment)) - fun updateCommentFocus(isFocused: Boolean) = copy(commentUiModel = commentUiModel.updateFocus(isFocused)) + fun updateCommentFocus(isFocused: Boolean) = copy(comment = comment.updateFocus(isFocused)) fun showCommentError() = copy(showCommentError = true) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt index 1b79a245..91d0d1bd 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt @@ -10,17 +10,20 @@ 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.imePadding import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberUpdatedState -import androidx.compose.ui.Alignment +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.boundsInParent +import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -30,7 +33,7 @@ import coil3.compose.AsyncImage import coil3.request.ImageRequest import coil3.request.crossfade import com.twix.designsystem.components.button.AppRoundButton -import com.twix.designsystem.components.comment.CommentTextField +import com.twix.designsystem.components.comment.CommentAnchorFrame import com.twix.designsystem.components.toast.ToastManager import com.twix.designsystem.components.toast.model.ToastData import com.twix.designsystem.components.toast.model.ToastType @@ -41,6 +44,7 @@ import com.twix.designsystem.theme.TwixTheme import com.twix.task_certification.R import com.twix.task_certification.detail.component.PhotologCard import com.twix.task_certification.detail.component.TaskCertificationDetailTopBar +import com.twix.task_certification.editor.model.TaskCertificationEditorIntent import com.twix.task_certification.editor.model.TaskCertificationEditorUiState import com.twix.ui.extension.findActivity import com.twix.ui.extension.hasCameraPermission @@ -96,8 +100,8 @@ fun TaskCertificationEditorRoute( TaskCertificationEditorScreen( uiState = uiState, onBack = navigateToBack, - onClickComment = {}, onClickSave = { }, + onFocusChanged = { viewModel.dispatch(TaskCertificationEditorIntent.CommentModify(it)) }, onClickRetake = { if (currentContext.hasCameraPermission()) { navigateToCertification() @@ -112,57 +116,50 @@ fun TaskCertificationEditorRoute( fun TaskCertificationEditorScreen( uiState: TaskCertificationEditorUiState, onBack: () -> Unit, - onClickComment: () -> Unit, onClickSave: () -> Unit, + onFocusChanged: (Boolean) -> Unit, onClickRetake: () -> Unit, ) { - Scaffold( - topBar = { + val focusManager = LocalFocusManager.current + var photologBottom by remember { mutableFloatStateOf(0f) } + + Box( + modifier = + Modifier + .fillMaxSize() + .background(color = CommonColor.White) + .noRippleClickable { focusManager.clearFocus() }, + ) { + Column { TaskCertificationDetailTopBar( actionTitle = stringResource(DesR.string.word_save), goalTitle = uiState.goalName, onBack = onBack, onClickModify = onClickSave, - modifier = - Modifier - .background(color = CommonColor.White), ) - }, - ) { innerPadding -> - Column( - Modifier - .padding(innerPadding) - .fillMaxSize() - .background(color = CommonColor.White) - .imePadding(), - ) { + Spacer(Modifier.height(103.dp)) - PhotologCard { - PhotologCard { - Box(Modifier.fillMaxSize()) { - AsyncImage( - model = - ImageRequest - .Builder(LocalContext.current) - .data(uiState.imageUrl) - .crossfade(true) - .build(), - contentDescription = null, - contentScale = ContentScale.Crop, - modifier = Modifier.fillMaxSize(), - ) - CommentTextField( - uiModel = uiState.comment, - enabled = false, - modifier = - Modifier - .align(Alignment.BottomCenter) - .padding(bottom = 28.dp) - .noRippleClickable { onClickComment() }, - ) - } - } + PhotologCard( + modifier = + Modifier + .onGloballyPositioned { coordinates -> + val bottom = coordinates.boundsInParent().bottom + if (photologBottom != bottom) { + photologBottom = bottom + } + }, + ) { + AsyncImage( + model = + ImageRequest + .Builder(LocalContext.current) + .data(uiState.imageUrl) + .crossfade(true) + .build(), + contentDescription = null, + contentScale = ContentScale.Crop, + ) } Spacer(Modifier.height(101.dp)) @@ -179,6 +176,13 @@ fun TaskCertificationEditorScreen( .noRippleClickable { onClickRetake() }, ) } + + CommentAnchorFrame( + uiModel = uiState.comment, + anchorBottom = photologBottom, + onCommentChanged = { }, + onFocusChanged = { onFocusChanged(it) }, + ) } } @@ -193,8 +197,8 @@ private fun TaskCertificationEditorScreenPreview() { goalName = "아이스크림 먹기", ), onBack = {}, - onClickComment = {}, onClickSave = {}, + onFocusChanged = {}, onClickRetake = {}, ) } From 7d7f8d68519ed23a9ac032878d8dfb86326bdd68 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 14 Feb 2026 21:14:47 +0900 Subject: [PATCH 16/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=EC=83=B7=20=EA=B4=80=EB=A0=A8=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EC=A0=80=EB=B8=94=EC=9D=84=20design-system=EC=9C=BC?= =?UTF-8?q?=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 --- .../components/photolog}/BackgroundCard.kt | 9 ++++----- .../components/photolog}/CertificatedCard.kt | 4 ++-- .../components/photolog}/ForegroundCard.kt | 13 +++++++------ .../components/photolog}/PhotologCard.kt | 4 ++-- core/design-system/src/main/res/values/strings.xml | 4 ++++ .../detail/TaskCertificationDetail.kt | 6 +++--- .../src/main/res/values/strings.xml | 2 -- 7 files changed, 22 insertions(+), 20 deletions(-) rename {feature/task-certification/src/main/java/com/twix/task_certification/detail/component => core/design-system/src/main/java/com/twix/designsystem/components/photolog}/BackgroundCard.kt (90%) rename {feature/task-certification/src/main/java/com/twix/task_certification/detail/component => core/design-system/src/main/java/com/twix/designsystem/components/photolog}/CertificatedCard.kt (95%) rename {feature/task-certification/src/main/java/com/twix/task_certification/detail/component => core/design-system/src/main/java/com/twix/designsystem/components/photolog}/ForegroundCard.kt (79%) rename {feature/task-certification/src/main/java/com/twix/task_certification/detail/component => core/design-system/src/main/java/com/twix/designsystem/components/photolog}/PhotologCard.kt (95%) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/BackgroundCard.kt b/core/design-system/src/main/java/com/twix/designsystem/components/photolog/BackgroundCard.kt similarity index 90% rename from feature/task-certification/src/main/java/com/twix/task_certification/detail/component/BackgroundCard.kt rename to core/design-system/src/main/java/com/twix/designsystem/components/photolog/BackgroundCard.kt index 284dcfb4..56e5829c 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/BackgroundCard.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/photolog/BackgroundCard.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.detail.component +package com.twix.designsystem.components.photolog import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Box @@ -16,15 +16,14 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.twix.designsystem.R import com.twix.designsystem.components.button.AppRoundButton import com.twix.designsystem.components.text.AppText import com.twix.designsystem.theme.CommonColor import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme import com.twix.domain.model.enums.AppTextStyle -import com.twix.task_certification.R import com.twix.ui.extension.noRippleClickable -import com.twix.designsystem.R as DesR @Composable fun BackgroundCard( @@ -69,7 +68,7 @@ fun BackgroundCard( } Image( - imageVector = ImageVector.vectorResource(DesR.drawable.ic_keepi_sting), + imageVector = ImageVector.vectorResource(R.drawable.ic_keepi_sting), contentDescription = null, modifier = Modifier @@ -86,7 +85,7 @@ fun BackgroundCard( fun PreviewBackgroundCard() { TwixTheme { BackgroundCard( - buttonTitle = stringResource(R.string.task_certification_detail_partner_sting), + buttonTitle = stringResource(R.string.partner_sting), uploadedAt = "2023.10.31 23:59", onClick = {}, isCertificated = true, diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/CertificatedCard.kt b/core/design-system/src/main/java/com/twix/designsystem/components/photolog/CertificatedCard.kt similarity index 95% rename from feature/task-certification/src/main/java/com/twix/task_certification/detail/component/CertificatedCard.kt rename to core/design-system/src/main/java/com/twix/designsystem/components/photolog/CertificatedCard.kt index 51967e1e..9d2ee54a 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/CertificatedCard.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/photolog/CertificatedCard.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.detail.component +package com.twix.designsystem.components.photolog import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize @@ -18,7 +18,7 @@ import com.twix.designsystem.components.comment.model.CommentUiModel import com.twix.designsystem.theme.TwixTheme @Composable -internal fun CertificatedCard( +fun CertificatedCard( imageUrl: String?, comment: String?, ) { diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/ForegroundCard.kt b/core/design-system/src/main/java/com/twix/designsystem/components/photolog/ForegroundCard.kt similarity index 79% rename from feature/task-certification/src/main/java/com/twix/task_certification/detail/component/ForegroundCard.kt rename to core/design-system/src/main/java/com/twix/designsystem/components/photolog/ForegroundCard.kt index 4d4851a9..7bc42d08 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/ForegroundCard.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/photolog/ForegroundCard.kt @@ -1,19 +1,18 @@ -package com.twix.task_certification.detail.component +package com.twix.designsystem.components.photolog import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import com.twix.designsystem.R import com.twix.designsystem.components.text.AppText import com.twix.designsystem.theme.CommonColor import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme import com.twix.domain.model.enums.AppTextStyle import com.twix.domain.model.enums.BetweenUs -import com.twix.task_certification.R -import com.twix.designsystem.R as DesR @Composable -internal fun ForegroundCard( +fun ForegroundCard( isCertificated: Boolean, nickName: String, imageUrl: String?, @@ -32,9 +31,11 @@ internal fun ForegroundCard( AppText( text = when (currentShow) { - BetweenUs.ME -> stringResource(DesR.string.keep_it_up) + BetweenUs.ME -> stringResource(R.string.keep_it_up) BetweenUs.PARTNER -> - stringResource(R.string.task_certification_detail_partner_not_task_certification).format(nickName) + stringResource(R.string.partner_not_task_certification).format( + nickName, + ) }, style = AppTextStyle.H2, color = GrayColor.C500, diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/PhotologCard.kt b/core/design-system/src/main/java/com/twix/designsystem/components/photolog/PhotologCard.kt similarity index 95% rename from feature/task-certification/src/main/java/com/twix/task_certification/detail/component/PhotologCard.kt rename to core/design-system/src/main/java/com/twix/designsystem/components/photolog/PhotologCard.kt index f7348629..c89f068b 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/PhotologCard.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/photolog/PhotologCard.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.detail.component +package com.twix.designsystem.components.photolog import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -21,7 +21,7 @@ import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme @Composable -internal fun PhotologCard( +fun PhotologCard( modifier: Modifier = Modifier, borderColor: Color = GrayColor.C500, background: Color = CommonColor.White, diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index ae854d14..30019d82 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -49,6 +49,10 @@ 코멘트추가 5글자로 코멘트를 남길 수 있어요 + + %s님은\n아직… + 찌르기 + 목표 직접 만들기 목표 수정하기 diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt index 4180991f..a7fe18ea 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt @@ -28,6 +28,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.core.app.ActivityCompat import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.twix.designsystem.components.photolog.BackgroundCard +import com.twix.designsystem.components.photolog.ForegroundCard import com.twix.designsystem.components.toast.ToastManager import com.twix.designsystem.components.toast.model.ToastData import com.twix.designsystem.components.toast.model.ToastType @@ -37,8 +39,6 @@ import com.twix.designsystem.theme.TwixTheme import com.twix.domain.model.enums.BetweenUs import com.twix.domain.model.enums.GoalReactionType import com.twix.task_certification.R -import com.twix.task_certification.detail.component.BackgroundCard -import com.twix.task_certification.detail.component.ForegroundCard import com.twix.task_certification.detail.component.TaskCertificationDetailTopBar import com.twix.task_certification.detail.model.TaskCertificationDetailIntent import com.twix.task_certification.detail.model.TaskCertificationDetailSideEffect @@ -165,7 +165,7 @@ fun TaskCertificationDetailScreen( buttonTitle = when (uiState.currentShow) { BetweenUs.ME -> stringResource(R.string.task_certification_take_picture) - BetweenUs.PARTNER -> stringResource(R.string.task_certification_detail_partner_sting) + BetweenUs.PARTNER -> stringResource(DesR.string.partner_sting) }, rotation = when (uiState.currentShow) { diff --git a/feature/task-certification/src/main/res/values/strings.xml b/feature/task-certification/src/main/res/values/strings.xml index 937a8ca3..92f6b79c 100644 --- a/feature/task-certification/src/main/res/values/strings.xml +++ b/feature/task-certification/src/main/res/values/strings.xml @@ -9,9 +9,7 @@ 코멘트는 5글자로 입력해주세요! - %s님은\n아직… 인증샷 조회에 실패했습니다. - 찌르기 다시 찍기 From 958c40e52e0f1307c9487c849b77a5e54b844b19 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 14 Feb 2026 21:45:35 +0900 Subject: [PATCH 17/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=EC=9E=90=EB=A7=8C=20=EC=9D=B8=EC=A6=9D=EC=9D=84=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EB=85=B8=EC=B6=9C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task_certification/detail/TaskCertificationDetail.kt | 4 ++-- .../detail/component/TaskCertificationDetailTopBar.kt | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt index a7fe18ea..8a19a77f 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt @@ -140,10 +140,10 @@ fun TaskCertificationDetailScreen( Scaffold( topBar = { TaskCertificationDetailTopBar( - actionTitle = stringResource(DesR.string.word_modify), goalTitle = uiState.currentGoal.goalName, onBack = onBack, - onClickModify = onClickModify, + actionTitle = if (uiState.canModify) stringResource(DesR.string.word_modify) else null, + onClickModify = if (uiState.canModify) onClickModify else null, modifier = Modifier .background(color = CommonColor.White), diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt index 2ce38b74..22581f1b 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt @@ -34,8 +34,8 @@ fun TaskCertificationDetailTopBar( goalTitle: String, onBack: () -> Unit, modifier: Modifier = Modifier, - actionTitle: String? = null, - onClickModify: (() -> Unit)? = null, + actionTitle: String?, + onClickModify: (() -> Unit)?, ) { Column( modifier = @@ -114,11 +114,14 @@ fun TaskCertificationDetailTopBarPreview() { actionTitle = "수정", goalTitle = "목표 타이틀", onBack = {}, + onClickModify = {}, ) TaskCertificationDetailTopBar( goalTitle = "목표 타이틀", onBack = {}, + actionTitle = null, + onClickModify = null, ) } } From 205a8e0eb26aea3a532b91b9649103dbfa170d36 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 14 Feb 2026 21:52:10 +0900 Subject: [PATCH 18/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=9D=B8=EC=A6=9D?= =?UTF-8?q?=EC=83=B7=20=EC=88=98=EC=A0=95=20=ED=99=94=EB=A9=B4=20=EB=8C=93?= =?UTF-8?q?=EA=B8=80=20=EC=88=98=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../editor/TaskCertificationEditorRoute.kt | 11 ++++++---- .../TaskCertificationEditorViewModel.kt | 22 +++++++++++++++---- .../model/TaskCertificationEditorIntent.kt | 10 ++++++++- .../model/TaskCertificationEditorUiState.kt | 4 ++++ 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt index 91d0d1bd..3ed7031a 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt @@ -34,6 +34,7 @@ import coil3.request.ImageRequest import coil3.request.crossfade import com.twix.designsystem.components.button.AppRoundButton import com.twix.designsystem.components.comment.CommentAnchorFrame +import com.twix.designsystem.components.photolog.PhotologCard import com.twix.designsystem.components.toast.ToastManager import com.twix.designsystem.components.toast.model.ToastData import com.twix.designsystem.components.toast.model.ToastType @@ -42,7 +43,6 @@ import com.twix.designsystem.theme.CommonColor import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme import com.twix.task_certification.R -import com.twix.task_certification.detail.component.PhotologCard import com.twix.task_certification.detail.component.TaskCertificationDetailTopBar import com.twix.task_certification.editor.model.TaskCertificationEditorIntent import com.twix.task_certification.editor.model.TaskCertificationEditorUiState @@ -101,7 +101,8 @@ fun TaskCertificationEditorRoute( uiState = uiState, onBack = navigateToBack, onClickSave = { }, - onFocusChanged = { viewModel.dispatch(TaskCertificationEditorIntent.CommentModify(it)) }, + onFocusChanged = { viewModel.dispatch(TaskCertificationEditorIntent.CommentFocusChanged(it)) }, + onCommentChanged = { viewModel.dispatch(TaskCertificationEditorIntent.ModifyComment(it)) }, onClickRetake = { if (currentContext.hasCameraPermission()) { navigateToCertification() @@ -117,6 +118,7 @@ fun TaskCertificationEditorScreen( uiState: TaskCertificationEditorUiState, onBack: () -> Unit, onClickSave: () -> Unit, + onCommentChanged: (String) -> Unit, onFocusChanged: (Boolean) -> Unit, onClickRetake: () -> Unit, ) { @@ -180,8 +182,8 @@ fun TaskCertificationEditorScreen( CommentAnchorFrame( uiModel = uiState.comment, anchorBottom = photologBottom, - onCommentChanged = { }, - onFocusChanged = { onFocusChanged(it) }, + onCommentChanged = onCommentChanged, + onFocusChanged = onFocusChanged, ) } } @@ -200,6 +202,7 @@ private fun TaskCertificationEditorScreenPreview() { onClickSave = {}, onFocusChanged = {}, onClickRetake = {}, + onCommentChanged = {}, ) } } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt index 0772bbd6..cd90a9d3 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt @@ -19,10 +19,12 @@ class TaskCertificationEditorViewModel( ) { val serializer = requireNotNull( - savedStateHandle.get(NavRoutes.TaskCertificationEditorRoute.ARG_DATA)?.let { encoded -> - val json = Uri.decode(encoded) - Json.decodeFromString(json) - }, + savedStateHandle + .get(NavRoutes.TaskCertificationEditorRoute.ARG_DATA) + ?.let { encoded -> + val json = Uri.decode(encoded) + Json.decodeFromString(json) + }, ) { SERIALIZER_NOT_FOUND } init { @@ -34,6 +36,18 @@ class TaskCertificationEditorViewModel( } override suspend fun handleIntent(intent: TaskCertificationEditorIntent) { + when (intent) { + is TaskCertificationEditorIntent.CommentFocusChanged -> reduceCommentFocus(intent.isFocused) + is TaskCertificationEditorIntent.ModifyComment -> reduceComment(intent.value) + } + } + + private fun reduceCommentFocus(isFocused: Boolean) { + reduce { updateCommentFocus(isFocused) } + } + + private fun reduceComment(comment: String) { + reduce { updateComment(comment) } } companion object { diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorIntent.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorIntent.kt index dbaaf9a2..a426d468 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorIntent.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorIntent.kt @@ -2,4 +2,12 @@ package com.twix.task_certification.editor.model import com.twix.ui.base.Intent -sealed interface TaskCertificationEditorIntent : Intent +sealed interface TaskCertificationEditorIntent : Intent { + data class CommentFocusChanged( + val isFocused: Boolean, + ) : TaskCertificationEditorIntent + + data class ModifyComment( + val value: String, + ) : TaskCertificationEditorIntent +} diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt index 923be7e4..b767b98e 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt @@ -13,6 +13,10 @@ data class TaskCertificationEditorUiState( val imageUrl: String = "", val comment: CommentUiModel = CommentUiModel(), ) : State { + fun updateCommentFocus(isFocus: Boolean) = copy(comment = comment.updateFocus(isFocus)) + + fun updateComment(value: String) = copy(comment = comment.updateComment(value)) + fun updateInitialState(serializer: TaskCertificationSerializer) = copy( nickname = serializer.nickname, From 6be3fccf58295396534a763113efc4862e204eab Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 14 Feb 2026 21:56:39 +0900 Subject: [PATCH 19/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=9D=B8=EC=A6=9D?= =?UTF-8?q?=EC=83=B7=20=EC=88=98=EC=A0=95=20=ED=99=94=EB=A9=B4=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EB=B2=84=ED=8A=BC=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/navigation/src/main/java/com/twix/navigation/AppNavHost.kt | 2 +- .../task_certification/editor/TaskCertificationEditorRoute.kt | 2 +- .../editor/TaskCertificationEditorViewModel.kt | 1 + .../editor/model/TaskCertificationEditorIntent.kt | 2 ++ .../editor/model/TaskCertificationEditorUiState.kt | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/core/navigation/src/main/java/com/twix/navigation/AppNavHost.kt b/core/navigation/src/main/java/com/twix/navigation/AppNavHost.kt index 6a20fb4a..65027324 100644 --- a/core/navigation/src/main/java/com/twix/navigation/AppNavHost.kt +++ b/core/navigation/src/main/java/com/twix/navigation/AppNavHost.kt @@ -23,7 +23,7 @@ fun AppNavHost() { } val start = contributors - .firstOrNull { it.graphRoute == NavRoutes.LoginGraph } + .firstOrNull { it.graphRoute == NavRoutes.MainGraph } ?.graphRoute ?: error("해당 Graph를 찾을 수 없습니다.") val duration = 300 diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt index 3ed7031a..0333086b 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt @@ -100,7 +100,7 @@ fun TaskCertificationEditorRoute( TaskCertificationEditorScreen( uiState = uiState, onBack = navigateToBack, - onClickSave = { }, + onClickSave = { viewModel.dispatch(TaskCertificationEditorIntent.Save) }, onFocusChanged = { viewModel.dispatch(TaskCertificationEditorIntent.CommentFocusChanged(it)) }, onCommentChanged = { viewModel.dispatch(TaskCertificationEditorIntent.ModifyComment(it)) }, onClickRetake = { diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt index cd90a9d3..c30c46c4 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt @@ -39,6 +39,7 @@ class TaskCertificationEditorViewModel( when (intent) { is TaskCertificationEditorIntent.CommentFocusChanged -> reduceCommentFocus(intent.isFocused) is TaskCertificationEditorIntent.ModifyComment -> reduceComment(intent.value) + TaskCertificationEditorIntent.Save -> TODO("인증샷 수정 API 연동") } } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorIntent.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorIntent.kt index a426d468..d4c9000b 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorIntent.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorIntent.kt @@ -3,6 +3,8 @@ package com.twix.task_certification.editor.model import com.twix.ui.base.Intent sealed interface TaskCertificationEditorIntent : Intent { + data object Save : TaskCertificationEditorIntent + data class CommentFocusChanged( val isFocused: Boolean, ) : TaskCertificationEditorIntent diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt index b767b98e..09cfae3b 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt @@ -12,6 +12,7 @@ data class TaskCertificationEditorUiState( val photologId: Long = -1, val imageUrl: String = "", val comment: CommentUiModel = CommentUiModel(), + val isImageChanged: Boolean = false, ) : State { fun updateCommentFocus(isFocus: Boolean) = copy(comment = comment.updateFocus(isFocus)) From 66e438ab15c5f6ee0cd00d8f2ccd5edc2a58886f Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 14 Feb 2026 21:57:26 +0900 Subject: [PATCH 20/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=9E=98=EB=AA=BB=20=EC=BB=A4=EB=B0=8B=ED=95=9C=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/navigation/src/main/java/com/twix/navigation/AppNavHost.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/navigation/src/main/java/com/twix/navigation/AppNavHost.kt b/core/navigation/src/main/java/com/twix/navigation/AppNavHost.kt index 65027324..6a20fb4a 100644 --- a/core/navigation/src/main/java/com/twix/navigation/AppNavHost.kt +++ b/core/navigation/src/main/java/com/twix/navigation/AppNavHost.kt @@ -23,7 +23,7 @@ fun AppNavHost() { } val start = contributors - .firstOrNull { it.graphRoute == NavRoutes.MainGraph } + .firstOrNull { it.graphRoute == NavRoutes.LoginGraph } ?.graphRoute ?: error("해당 Graph를 찾을 수 없습니다.") val duration = 300 From 2e5a9c89fec728823213684ab5d99fa3d5af6ad8 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 14 Feb 2026 23:38:25 +0900 Subject: [PATCH 21/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=ED=82=A4=EB=B3=B4=EB=93=9C=20=ED=99=9C?= =?UTF-8?q?=EC=84=B1=ED=99=94=20=EC=8B=9C=20=EC=BB=A4=EC=84=9C=20=EB=A7=88?= =?UTF-8?q?=EC=A7=80=EB=A7=89=EC=9C=BC=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 --- .../components/comment/CommentTextField.kt | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt index f5953a16..d68917de 100644 --- a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt @@ -24,7 +24,9 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -52,7 +54,15 @@ fun CommentTextField( val focusRequester = remember { FocusRequester() } val keyboardState by keyboardAsState() - var internalValue by rememberSaveable(uiModel.comment) { mutableStateOf(uiModel.comment) } + var internalValue by rememberSaveable(stateSaver = TextFieldValue.Saver) { + mutableStateOf( + TextFieldValue( + text = uiModel.comment, + selection = TextRange(uiModel.comment.length), + ), + ) + } + var isInitialized by remember { mutableStateOf(false) } LaunchedEffect(keyboardState) { @@ -62,7 +72,7 @@ fun CommentTextField( when (keyboardState) { Keyboard.Opened -> Unit Keyboard.Closed -> { - onCommitComment(internalValue.trim()) + onCommitComment(internalValue.text.trim()) focusManager.clearFocus() } } @@ -87,8 +97,11 @@ fun CommentTextField( TextField( value = internalValue, onValueChange = { newValue -> - if (newValue.length <= CommentUiModel.COMMENT_COUNT) { - internalValue = newValue + if (newValue.text.length <= CommentUiModel.COMMENT_COUNT) { + internalValue = + newValue.copy( + selection = TextRange(newValue.text.length), + ) } }, enabled = enabled, @@ -128,16 +141,16 @@ fun CommentTextField( ) { repeat(CommentUiModel.COMMENT_COUNT) { index -> val char = - if (uiModel.isFocused || internalValue.isNotEmpty()) { - internalValue.getOrNull(index)?.toString() + if (uiModel.isFocused || internalValue.text.isNotEmpty()) { + internalValue.text.getOrNull(index)?.toString() } else { stringResource(R.string.comment_text_field_placeholder)[index].toString() }.orEmpty() CommentCircle( text = char, - showPlaceholder = !uiModel.isFocused && internalValue.isEmpty(), - showCursor = uiModel.isFocused && index == internalValue.length, + showPlaceholder = !uiModel.isFocused && internalValue.text.isEmpty(), + showCursor = uiModel.isFocused && index == internalValue.text.length, modifier = Modifier.noRippleClickable { focusRequester.requestFocus() From cc22e06850118c1101157990dca563b0f7de3825 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 14 Feb 2026 23:52:29 +0900 Subject: [PATCH 22/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=9D=B8=EC=A6=9D?= =?UTF-8?q?=EC=83=B7=20=EC=88=98=EC=A0=95=20api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request/photolog/model/PhotologModifyRequest.kt | 10 ++++++++++ .../java/com/twix/network/service/PhotoLogService.kt | 7 +++++++ .../twix/data/repository/DefaultPhotoLogRepository.kt | 7 +++++++ .../com/twix/domain/repository/PhotoLogRepository.kt | 6 ++++++ 4 files changed, 30 insertions(+) create mode 100644 core/network/src/main/java/com/twix/network/model/request/photolog/model/PhotologModifyRequest.kt diff --git a/core/network/src/main/java/com/twix/network/model/request/photolog/model/PhotologModifyRequest.kt b/core/network/src/main/java/com/twix/network/model/request/photolog/model/PhotologModifyRequest.kt new file mode 100644 index 00000000..b839bed8 --- /dev/null +++ b/core/network/src/main/java/com/twix/network/model/request/photolog/model/PhotologModifyRequest.kt @@ -0,0 +1,10 @@ +package com.twix.network.model.request.photolog.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class PhotologModifyRequest( + @SerialName("fileName") val fileName: String, + @SerialName("comment") val comment: String, +) diff --git a/core/network/src/main/java/com/twix/network/service/PhotoLogService.kt b/core/network/src/main/java/com/twix/network/service/PhotoLogService.kt index 010284b5..66e52f7a 100644 --- a/core/network/src/main/java/com/twix/network/service/PhotoLogService.kt +++ b/core/network/src/main/java/com/twix/network/service/PhotoLogService.kt @@ -1,6 +1,7 @@ package com.twix.network.service import com.twix.network.model.request.ReactionRequest +import com.twix.network.model.request.photolog.model.PhotologModifyRequest import com.twix.network.model.request.photolog.model.PhotologRequest import com.twix.network.model.response.photo.model.PhotoLogUploadUrlResponse import com.twix.network.model.response.photolog.PhotoLogsResponse @@ -32,4 +33,10 @@ interface PhotoLogService { @Path("photologId") photologId: Long, @Body request: ReactionRequest, ) + + @PUT("api/v1/photologs/{photologId}") + suspend fun modifyPhotolog( + @Path("photologId") photologId: Long, + @Body request: PhotologModifyRequest, + ) } diff --git a/data/src/main/java/com/twix/data/repository/DefaultPhotoLogRepository.kt b/data/src/main/java/com/twix/data/repository/DefaultPhotoLogRepository.kt index 28d2b59d..bb5862fb 100644 --- a/data/src/main/java/com/twix/data/repository/DefaultPhotoLogRepository.kt +++ b/data/src/main/java/com/twix/data/repository/DefaultPhotoLogRepository.kt @@ -7,6 +7,7 @@ import com.twix.domain.model.photolog.PhotoLogs import com.twix.domain.repository.PhotoLogRepository import com.twix.network.execute.safeApiCall import com.twix.network.model.request.photolog.mapper.toRequest +import com.twix.network.model.request.photolog.model.PhotologModifyRequest import com.twix.network.model.request.toRequest import com.twix.network.model.response.photo.mapper.toDomain import com.twix.network.model.response.photolog.mapper.toDomain @@ -58,4 +59,10 @@ class DefaultPhotoLogRepository( photologId: Long, reaction: GoalReactionType, ): AppResult = safeApiCall { service.reactToPhotolog(photologId, reaction.toRequest()) } + + override suspend fun modifyPhotolog( + photologId: Long, + fileName: String, + comment: String, + ): AppResult = safeApiCall { service.modifyPhotolog(photologId, PhotologModifyRequest(fileName, comment)) } } diff --git a/domain/src/main/java/com/twix/domain/repository/PhotoLogRepository.kt b/domain/src/main/java/com/twix/domain/repository/PhotoLogRepository.kt index bddd2607..b186d161 100644 --- a/domain/src/main/java/com/twix/domain/repository/PhotoLogRepository.kt +++ b/domain/src/main/java/com/twix/domain/repository/PhotoLogRepository.kt @@ -23,4 +23,10 @@ interface PhotoLogRepository { photologId: Long, reaction: GoalReactionType, ): AppResult + + suspend fun modifyPhotolog( + photologId: Long, + fileName: String, + comment: String, + ): AppResult } From 93a3e61bdf7a0e4d34e8a4d03b769c3c7284496c Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 14 Feb 2026 23:53:09 +0900 Subject: [PATCH 23/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20CommentU?= =?UTF-8?q?iModel=EC=9D=98=20`comment`=20=EC=86=8D=EC=84=B1=EC=9D=84=20`va?= =?UTF-8?q?lue`=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 --- .../components/comment/CommentTextField.kt | 4 ++-- .../components/comment/model/CommentUiModel.kt | 10 +++++----- .../certification/TaskCertificationViewModel.kt | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt index d68917de..00904601 100644 --- a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt @@ -57,8 +57,8 @@ fun CommentTextField( var internalValue by rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf( TextFieldValue( - text = uiModel.comment, - selection = TextRange(uiModel.comment.length), + text = uiModel.value, + selection = TextRange(uiModel.value.length), ), ) } diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/comment/model/CommentUiModel.kt b/core/design-system/src/main/java/com/twix/designsystem/components/comment/model/CommentUiModel.kt index e44e5675..284c0bad 100644 --- a/core/design-system/src/main/java/com/twix/designsystem/components/comment/model/CommentUiModel.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/comment/model/CommentUiModel.kt @@ -4,19 +4,19 @@ import androidx.compose.runtime.Immutable @Immutable data class CommentUiModel( - val comment: String = "", + val value: String = "", val isFocused: Boolean = false, ) { val hasMaxCommentLength: Boolean - get() = comment.length == COMMENT_COUNT + get() = value.length == COMMENT_COUNT val canUpload: Boolean get() = - comment.isEmpty() || - comment.isNotEmpty() && + value.isEmpty() || + value.isNotEmpty() && hasMaxCommentLength - fun updateComment(newComment: String): CommentUiModel = copy(comment = newComment) + fun updateComment(newComment: String): CommentUiModel = copy(value = newComment) fun updateFocus(isFocused: Boolean) = copy(isFocused = isFocused) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt index 79c60f4c..89c60824 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt @@ -134,7 +134,7 @@ class TaskCertificationViewModel( PhotologParam( goalId = goalId, fileName = fileName, - comment = currentState.comment.comment, + comment = currentState.comment.value, verificationDate = LocalDate.now(), ), ) From b0a2d04183cc298a5a9550cba1c96411e9905d45 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 14 Feb 2026 23:53:27 +0900 Subject: [PATCH 24/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20UI=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=EC=83=B7=20=EC=BD=94=EB=A9=98=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../editor/TaskCertificationEditorRoute.kt | 14 ++++++ .../TaskCertificationEditorViewModel.kt | 46 ++++++++++++++++++- .../TaskCertificationEditorSideEffect.kt | 8 +++- .../model/TaskCertificationEditorUiState.kt | 13 +++++- .../src/main/res/values/strings.xml | 3 ++ 5 files changed, 81 insertions(+), 3 deletions(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt index 0333086b..b05decc2 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt @@ -45,7 +45,9 @@ import com.twix.designsystem.theme.TwixTheme import com.twix.task_certification.R import com.twix.task_certification.detail.component.TaskCertificationDetailTopBar import com.twix.task_certification.editor.model.TaskCertificationEditorIntent +import com.twix.task_certification.editor.model.TaskCertificationEditorSideEffect import com.twix.task_certification.editor.model.TaskCertificationEditorUiState +import com.twix.ui.base.ObserveAsEvents import com.twix.ui.extension.findActivity import com.twix.ui.extension.hasCameraPermission import com.twix.ui.extension.noRippleClickable @@ -66,6 +68,18 @@ fun TaskCertificationEditorRoute( val coroutineScope = rememberCoroutineScope() val uiState by viewModel.uiState.collectAsStateWithLifecycle() + ObserveAsEvents(viewModel.sideEffect) { sideEffect -> + when (sideEffect) { + is TaskCertificationEditorSideEffect.ShowToast -> + toastManager.tryShow( + ToastData( + currentContext.getString(sideEffect.message), + sideEffect.type, + ), + ) + } + } + val permissionLauncher = rememberLauncherForActivityResult( ActivityResultContracts.RequestPermission(), diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt index c30c46c4..7299bba6 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt @@ -2,17 +2,24 @@ package com.twix.task_certification.editor import android.net.Uri import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.viewModelScope +import com.twix.designsystem.components.toast.model.ToastType import com.twix.domain.repository.PhotoLogRepository import com.twix.navigation.NavRoutes import com.twix.navigation.serializer.TaskCertificationSerializer +import com.twix.result.AppResult +import com.twix.task_certification.R import com.twix.task_certification.editor.model.TaskCertificationEditorIntent import com.twix.task_certification.editor.model.TaskCertificationEditorSideEffect import com.twix.task_certification.editor.model.TaskCertificationEditorUiState import com.twix.ui.base.BaseViewModel +import com.twix.util.bus.TaskCertificationRefreshBus +import kotlinx.coroutines.launch import kotlinx.serialization.json.Json class TaskCertificationEditorViewModel( private val photologRepository: PhotoLogRepository, + private val detailRefreshBus: TaskCertificationRefreshBus, savedStateHandle: SavedStateHandle, ) : BaseViewModel( TaskCertificationEditorUiState(), @@ -39,7 +46,7 @@ class TaskCertificationEditorViewModel( when (intent) { is TaskCertificationEditorIntent.CommentFocusChanged -> reduceCommentFocus(intent.isFocused) is TaskCertificationEditorIntent.ModifyComment -> reduceComment(intent.value) - TaskCertificationEditorIntent.Save -> TODO("인증샷 수정 API 연동") + TaskCertificationEditorIntent.Save -> modifyComment() } } @@ -51,6 +58,43 @@ class TaskCertificationEditorViewModel( reduce { updateComment(comment) } } + private fun modifyComment() { + if (currentState.comment.canUpload.not()) { + showToast(R.string.comment_error_message, ToastType.ERROR) + } else if (currentState.isCommentNotChanged) { + showToast(R.string.task_certification_editor_not_modified, ToastType.ERROR) + } else { + launchResult( + block = { launchModifyComment() }, + onSuccess = { + detailRefreshBus.notifyChanged() + showToast(R.string.task_certification_editor_modify_success, ToastType.SUCCESS) + }, + onError = { + showToast(R.string.task_certification_editor_modify_fail, ToastType.ERROR) + }, + ) + } + } + + private fun showToast( + message: Int, + type: ToastType, + ) { + viewModelScope.launch { + emitSideEffect( + TaskCertificationEditorSideEffect.ShowToast(message, type), + ) + } + } + + private suspend fun launchModifyComment(): AppResult = + photologRepository.modifyPhotolog( + currentState.photologId, + currentState.imageName, + currentState.comment.value, + ) + companion object { private const val SERIALIZER_NOT_FOUND = "Serializer Not Found" } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorSideEffect.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorSideEffect.kt index 94b298ba..441b2a95 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorSideEffect.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorSideEffect.kt @@ -1,5 +1,11 @@ package com.twix.task_certification.editor.model +import com.twix.designsystem.components.toast.model.ToastType import com.twix.ui.base.SideEffect -sealed interface TaskCertificationEditorSideEffect : SideEffect +sealed interface TaskCertificationEditorSideEffect : SideEffect { + data class ShowToast( + val message: Int, + val type: ToastType, + ) : TaskCertificationEditorSideEffect +} diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt index 09cfae3b..0dead003 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt @@ -12,12 +12,18 @@ data class TaskCertificationEditorUiState( val photologId: Long = -1, val imageUrl: String = "", val comment: CommentUiModel = CommentUiModel(), - val isImageChanged: Boolean = false, + val originComment: String = "", ) : State { + val isCommentNotChanged: Boolean + get() = comment.value == originComment + fun updateCommentFocus(isFocus: Boolean) = copy(comment = comment.updateFocus(isFocus)) fun updateComment(value: String) = copy(comment = comment.updateComment(value)) + val imageName: String + get() = imageUrl.split(IMAGE_NAME_SEPARATOR).last() + fun updateInitialState(serializer: TaskCertificationSerializer) = copy( nickname = serializer.nickname, @@ -25,5 +31,10 @@ data class TaskCertificationEditorUiState( photologId = serializer.photologId, imageUrl = serializer.imageUrl, comment = CommentUiModel(serializer.comment.orEmpty()), + originComment = serializer.comment.orEmpty(), ) + + companion object { + private const val IMAGE_NAME_SEPARATOR = "/" + } } diff --git a/feature/task-certification/src/main/res/values/strings.xml b/feature/task-certification/src/main/res/values/strings.xml index 92f6b79c..e4757151 100644 --- a/feature/task-certification/src/main/res/values/strings.xml +++ b/feature/task-certification/src/main/res/values/strings.xml @@ -13,4 +13,7 @@ 다시 찍기 + 코멘트가 수정 되었어요. + 코멘트가 수정되지 않았어요. + 코멘트 수정에 실패했어요. From bf5c926b3276e21c5aaa1b03928bb9ac394c2fd6 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 00:53:01 +0900 Subject: [PATCH 25/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=EB=AA=85=20=EC=B9=B4=EB=A9=9C=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/data/repository/DefaultPhotoLogRepository.kt | 6 +++--- .../java/com/twix/domain/repository/PhotoLogRepository.kt | 6 +++--- .../certification/TaskCertificationViewModel.kt | 6 +++--- .../detail/TaskCertificationDetailViewModel.kt | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/data/src/main/java/com/twix/data/repository/DefaultPhotoLogRepository.kt b/data/src/main/java/com/twix/data/repository/DefaultPhotoLogRepository.kt index bb5862fb..cefe9778 100644 --- a/data/src/main/java/com/twix/data/repository/DefaultPhotoLogRepository.kt +++ b/data/src/main/java/com/twix/data/repository/DefaultPhotoLogRepository.kt @@ -21,10 +21,10 @@ class DefaultPhotoLogRepository( ) : PhotoLogRepository { override suspend fun getUploadUrl(goalId: Long): AppResult = safeApiCall { service.getUploadUrl(goalId).toDomain() } - override suspend fun uploadPhotoLog(photologParam: PhotologParam): AppResult = + override suspend fun uploadPhotolog(photologParam: PhotologParam): AppResult = safeApiCall { service.uploadPhotoLog(photologParam.toRequest()) } - override suspend fun uploadPhotoLogImage( + override suspend fun uploadPhotologImage( goalId: Long, bytes: ByteArray, contentType: String, @@ -50,7 +50,7 @@ class DefaultPhotoLogRepository( return AppResult.Success(info.fileName) } - override suspend fun fetchPhotoLogs(targetDate: String): AppResult = + override suspend fun fetchPhotologs(targetDate: String): AppResult = safeApiCall { service.fetchPhotoLogs(targetDate).toDomain() } diff --git a/domain/src/main/java/com/twix/domain/repository/PhotoLogRepository.kt b/domain/src/main/java/com/twix/domain/repository/PhotoLogRepository.kt index b186d161..a3f85950 100644 --- a/domain/src/main/java/com/twix/domain/repository/PhotoLogRepository.kt +++ b/domain/src/main/java/com/twix/domain/repository/PhotoLogRepository.kt @@ -9,15 +9,15 @@ import com.twix.result.AppResult interface PhotoLogRepository { suspend fun getUploadUrl(goalId: Long): AppResult - suspend fun uploadPhotoLog(photologParam: PhotologParam): AppResult + suspend fun uploadPhotolog(photologParam: PhotologParam): AppResult - suspend fun uploadPhotoLogImage( + suspend fun uploadPhotologImage( goalId: Long, bytes: ByteArray, contentType: String, ): AppResult - suspend fun fetchPhotoLogs(targetDate: String): AppResult + suspend fun fetchPhotologs(targetDate: String): AppResult suspend fun reactToPhotolog( photologId: Long, diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt index 89c60824..c3ca230b 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt @@ -109,8 +109,8 @@ class TaskCertificationViewModel( private fun upload(image: ByteArray) { launchResult( block = { - photologRepository.uploadPhotoLogImage( - goalId = goalId, + photologRepository.uploadPhotologImage( + goalId = argGoalId, bytes = image, contentType = "image/jpeg", ) @@ -130,7 +130,7 @@ class TaskCertificationViewModel( private fun uploadPhotoLog(fileName: String) { launchResult( block = { - photologRepository.uploadPhotoLog( + photologRepository.uploadPhotolog( PhotologParam( goalId = goalId, fileName = fileName, diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt index fbb63bad..02291084 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt @@ -100,7 +100,7 @@ class TaskCertificationDetailViewModel( private fun fetchPhotolog() { launchResult( - block = { photologRepository.fetchPhotoLogs(targetDate) }, + block = { photologRepository.fetchPhotologs(targetDate) }, onSuccess = { reduce { copy(photoLogs = it.toUiModel()) } }, From 0e0501c0351b633c85e02ec45e369eb9ac710209 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 00:58:06 +0900 Subject: [PATCH 26/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=ED=86=A0=EC=8A=A4=ED=8A=B8=20=EC=82=AC=EC=9D=B4=EB=93=9C=20?= =?UTF-8?q?=EC=9D=B4=ED=8E=99=ED=8A=B8=20=ED=95=A8=EC=88=98=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TaskCertificationViewModel.kt | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt index c3ca230b..b58c3a9b 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt @@ -117,12 +117,7 @@ class TaskCertificationViewModel( }, onSuccess = { fileName -> uploadPhotoLog(fileName) }, onError = { - emitSideEffect( - TaskCertificationSideEffect.ShowToast( - R.string.task_certification_upload_fail, - ToastType.ERROR, - ), - ) + showToast(R.string.task_certification_upload_fail, ToastType.ERROR) }, ) } @@ -148,16 +143,19 @@ class TaskCertificationViewModel( tryEmitSideEffect(TaskCertificationSideEffect.NavigateToDetail) }, onError = { - emitSideEffect( - TaskCertificationSideEffect.ShowToast( - R.string.task_certification_upload_fail, - ToastType.ERROR, - ), - ) + showToast(R.string.task_certification_upload_fail, ToastType.ERROR) }, ) } + private fun showToast( + message: Int, + type: ToastType, + ) { + viewModelScope.launch { + emitSideEffect(TaskCertificationSideEffect.ShowToast(message, type)) + } + } companion object { private const val ERROR_DISPLAY_DURATION_MS = 1500L private const val GOAL_ID_NOT_FOUND = "Goal Id Argument Not Found" From 5b91161f7ef8fe90213d9a91bf32bbd1437ce905 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 01:32:16 +0900 Subject: [PATCH 27/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=ED=86=A0=EC=8A=A4=ED=8A=B8=20=EC=B6=9C=EB=A0=A5=20=EC=82=AC?= =?UTF-8?q?=EC=9D=B4=EB=93=9C=20=EC=9D=B4=ED=8E=99=ED=8A=B8=20=ED=86=B5?= =?UTF-8?q?=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../certification/TaskCertificationScreen.kt | 11 +---------- .../certification/TaskCertificationViewModel.kt | 6 ++---- .../model/TaskCertificationSideEffect.kt | 4 +--- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt index f1e24bf4..30d00c7d 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt @@ -92,15 +92,6 @@ fun TaskCertificationRoute( val imageCaptureFailMessage = stringResource(R.string.task_certification_image_capture_fail) ObserveAsEvents(viewModel.sideEffect) { event -> when (event) { - TaskCertificationSideEffect.ShowImageCaptureFailToast -> { - toastManager.tryShow( - ToastData( - message = imageCaptureFailMessage, - type = ToastType.ERROR, - ), - ) - } - is TaskCertificationSideEffect.ShowToast -> { toastManager.tryShow( ToastData( @@ -128,7 +119,7 @@ fun TaskCertificationRoute( } } - TaskCertificationSideEffect.NavigateToDetail -> navigateToBack() + TaskCertificationSideEffect.NavigateToBack -> navigateToBack() } } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt index b58c3a9b..83ec2993 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt @@ -51,9 +51,7 @@ class TaskCertificationViewModel( private fun takePicture(uri: Uri?) { uri?.let { reducePicture(it) } ?: viewModelScope.launch { - emitSideEffect( - TaskCertificationSideEffect.ShowImageCaptureFailToast, - ) + showToast(R.string.task_certification_image_capture_fail, ToastType.ERROR) } } @@ -140,7 +138,7 @@ class TaskCertificationViewModel( NavRoutes.TaskCertificationRoute.From.DETAIL -> taskCertificationRefreshBus.notifyChanged() NavRoutes.TaskCertificationRoute.From.HOME -> goalRefreshBus.notifyGoalListChanged() } - tryEmitSideEffect(TaskCertificationSideEffect.NavigateToDetail) + tryEmitSideEffect(TaskCertificationSideEffect.NavigateToBack) }, onError = { showToast(R.string.task_certification_upload_fail, ToastType.ERROR) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationSideEffect.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationSideEffect.kt index 133c5c80..fefba96f 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationSideEffect.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationSideEffect.kt @@ -5,8 +5,6 @@ import com.twix.designsystem.components.toast.model.ToastType import com.twix.ui.base.SideEffect sealed interface TaskCertificationSideEffect : SideEffect { - data object ShowImageCaptureFailToast : TaskCertificationSideEffect - data class ShowToast( val message: Int, val type: ToastType, @@ -16,5 +14,5 @@ sealed interface TaskCertificationSideEffect : SideEffect { val uri: Uri, ) : TaskCertificationSideEffect - data object NavigateToDetail : TaskCertificationSideEffect + data object NavigateToBack : TaskCertificationSideEffect } From b168f86739257af6b21f23bfe12e5d1555431810 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 02:10:36 +0900 Subject: [PATCH 28/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EB=AF=B8=EC=82=AC=EC=9A=A9=20`TaskCertificationRefreshBus`=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/bus/TaskCertificationRefreshBus.kt | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 core/util/src/main/java/com/twix/util/bus/TaskCertificationRefreshBus.kt diff --git a/core/util/src/main/java/com/twix/util/bus/TaskCertificationRefreshBus.kt b/core/util/src/main/java/com/twix/util/bus/TaskCertificationRefreshBus.kt deleted file mode 100644 index f7a55423..00000000 --- a/core/util/src/main/java/com/twix/util/bus/TaskCertificationRefreshBus.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.twix.util.bus - -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow - -class TaskCertificationRefreshBus { - private val _events = - MutableSharedFlow( - replay = 0, - extraBufferCapacity = 1, - ) - - val events: SharedFlow = _events.asSharedFlow() - - fun notifyChanged() = _events.tryEmit(Unit) -} From d5bbf4f75d6906036a0da4ca2dee15ed48fa564d Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 02:11:36 +0900 Subject: [PATCH 29/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=9D=B8=EC=A6=9D=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EB=9D=BC=EC=9A=B0=ED=8C=85=20=EC=8B=9C=20?= =?UTF-8?q?photologId,=20comment=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/twix/navigation/NavRoutes.kt | 46 ++++++++++++++----- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt b/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt index 6b3ef1dd..c9037dc9 100644 --- a/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt +++ b/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt @@ -47,22 +47,44 @@ sealed class NavRoutes( ) = "task_certification_detail/$goalId/$date/$betweenUs" } - object TaskCertificationRoute : NavRoutes("task_certification/{goalId}/{from}") { + object TaskCertificationRoute : + NavRoutes("task_certification/{goalId}/{from}/{photologId}/{comment}") { const val ARG_GOAL_ID = "goalId" const val ARG_FROM = "from" - - private const val NOT_NEED_GOAL_ID = -1L - - enum class From { - HOME, - DETAIL, - EDITOR, + const val ARG_PHOTOLOG_ID = "photologId" + const val ARG_COMMENT = "comment" + + const val NAME_HOME = "HOME" + const val NAME_DETAIL = "DETAIL" + const val NAME_EDITOR = "EDITOR" + + private const val NOT_NEED_PHOTOLOG_ID = -1L + private const val NOT_NEED_COMMENT = -1L + + sealed class From( + val name: String, + ) { + data class Home( + val goalId: Long, + ) : From(NAME_HOME) + + data class Detail( + val goalId: Long, + ) : From(NAME_DETAIL) + + data class Editor( + val goalId: Long, + val photologId: Long, + val comment: String, + ) : From(NAME_EDITOR) } - fun createRoute( - goalId: Long = NOT_NEED_GOAL_ID, - from: From, - ) = "task_certification/$goalId/${from.name}" + fun createRoute(from: From): String = + when (from) { + is From.Home -> "task_certification/${from.goalId}/${from.name}/$NOT_NEED_PHOTOLOG_ID/$NOT_NEED_COMMENT" + is From.Detail -> "task_certification/${from.goalId}/${from.name}/$NOT_NEED_PHOTOLOG_ID/$NOT_NEED_COMMENT" + is From.Editor -> "task_certification/${from.goalId}/${from.name}/${from.photologId}/${from.comment}" + } } object TaskCertificationEditorRoute : From 82414d8350d516f967e4aa876ae312d6c890eec5 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 02:12:26 +0900 Subject: [PATCH 30/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=9D=B8=EC=A6=9D=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=ED=99=94=EB=A9=B4=20refresh=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20event=20bus=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bus/TaskCertificationDetailRefreshBus.kt | 17 +++++++++++++++++ .../detail/TaskCertificationDetailViewModel.kt | 6 +++--- 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 core/util/src/main/java/com/twix/util/bus/TaskCertificationDetailRefreshBus.kt diff --git a/core/util/src/main/java/com/twix/util/bus/TaskCertificationDetailRefreshBus.kt b/core/util/src/main/java/com/twix/util/bus/TaskCertificationDetailRefreshBus.kt new file mode 100644 index 00000000..7b781ae4 --- /dev/null +++ b/core/util/src/main/java/com/twix/util/bus/TaskCertificationDetailRefreshBus.kt @@ -0,0 +1,17 @@ +package com.twix.util.bus + +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow + +class TaskCertificationDetailRefreshBus { + private val _events = + MutableSharedFlow( + replay = 0, + extraBufferCapacity = 1, + ) + + val events: SharedFlow = _events.asSharedFlow() + + fun notifyChanged() = _events.tryEmit(Unit) +} diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt index 02291084..ae8529aa 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt @@ -14,7 +14,7 @@ import com.twix.task_certification.detail.model.TaskCertificationDetailUiState import com.twix.task_certification.detail.model.toUiModel import com.twix.ui.base.BaseViewModel import com.twix.util.bus.GoalRefreshBus -import com.twix.util.bus.TaskCertificationRefreshBus +import com.twix.util.bus.TaskCertificationDetailRefreshBus import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow @@ -24,7 +24,7 @@ import kotlinx.coroutines.launch class TaskCertificationDetailViewModel( private val photologRepository: PhotoLogRepository, - private val taskCertificationRefreshBus: TaskCertificationRefreshBus, + private val detailRefreshBus: TaskCertificationDetailRefreshBus, private val goalRefreshBus: GoalRefreshBus, savedStateHandle: SavedStateHandle, ) : BaseViewModel( @@ -83,7 +83,7 @@ class TaskCertificationDetailViewModel( private fun collectEventBus() { viewModelScope.launch { - taskCertificationRefreshBus.events.collect { + detailRefreshBus.events.collect { fetchPhotolog() goalRefreshBus.notifyGoalListChanged() } From b2c715b9557e3af908ded76f0dae0837690dba2c Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 02:13:04 +0900 Subject: [PATCH 31/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=9D=B8=EC=A6=9D=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EC=9D=B4=EB=8F=99=20=EC=8B=9C=20goalId=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../serializer/TaskCertificationSerializer.kt | 3 +- .../com/twix/main/navigation/MainNavGraph.kt | 7 +++-- .../model/TaskCertificationDetailUiState.kt | 1 + .../model/TaskCertificationEditorUiState.kt | 4 ++- .../navigation/TaskCertificationGraph.kt | 30 +++++++++++++++---- 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/core/navigation/src/main/java/com/twix/navigation/serializer/TaskCertificationSerializer.kt b/core/navigation/src/main/java/com/twix/navigation/serializer/TaskCertificationSerializer.kt index bfb16a5b..95abc471 100644 --- a/core/navigation/src/main/java/com/twix/navigation/serializer/TaskCertificationSerializer.kt +++ b/core/navigation/src/main/java/com/twix/navigation/serializer/TaskCertificationSerializer.kt @@ -4,8 +4,9 @@ import kotlinx.serialization.Serializable @Serializable data class TaskCertificationSerializer( - val nickname: String, + val goalId: Long, val goalName: String, + val nickname: String, val photologId: Long, val imageUrl: String, val comment: String?, diff --git a/feature/main/src/main/java/com/twix/main/navigation/MainNavGraph.kt b/feature/main/src/main/java/com/twix/main/navigation/MainNavGraph.kt index 2ba9c31a..318c2e86 100644 --- a/feature/main/src/main/java/com/twix/main/navigation/MainNavGraph.kt +++ b/feature/main/src/main/java/com/twix/main/navigation/MainNavGraph.kt @@ -36,11 +36,12 @@ object MainNavGraph : NavGraphContributor { launchSingleTop = true } }, - navigateToCertification = { + navigateToCertification = { goalId -> val destination = NavRoutes.TaskCertificationRoute.createRoute( - goalId = it, - from = NavRoutes.TaskCertificationRoute.From.HOME, + NavRoutes.TaskCertificationRoute.From.Home( + goalId, + ), ) navController.navigate(destination) { launchSingleTop = true diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt index 2a277b8d..975c7f6c 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt @@ -64,6 +64,7 @@ data class TaskCertificationDetailUiState( fun toSerializer() = TaskCertificationSerializer( + goalId = currentGoal.goalId, nickname = photoLogs.myNickname, goalName = currentGoal.goalName, photologId = currentGoal.myPhotolog?.photologId ?: -1, diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt index 0dead003..9a1e90a9 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt @@ -7,9 +7,10 @@ import com.twix.ui.base.State @Immutable data class TaskCertificationEditorUiState( + val goalId: Long = -1, + val photologId: Long = -1, val nickname: String = "", val goalName: String = "", - val photologId: Long = -1, val imageUrl: String = "", val comment: CommentUiModel = CommentUiModel(), val originComment: String = "", @@ -26,6 +27,7 @@ data class TaskCertificationEditorUiState( fun updateInitialState(serializer: TaskCertificationSerializer) = copy( + goalId = serializer.goalId, nickname = serializer.nickname, goalName = serializer.goalName, photologId = serializer.photologId, diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt b/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt index 4f07975f..65e79ae9 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt @@ -43,14 +43,17 @@ object TaskCertificationGraph : NavGraphContributor { navigateToUpload = { val destination = NavRoutes.TaskCertificationRoute.createRoute( - goalId = it, - from = NavRoutes.TaskCertificationRoute.From.DETAIL, + NavRoutes.TaskCertificationRoute.From.Detail(it), ) navController.navigate(destination) }, navigateToEditor = { uiState -> val serializer = uiState.toSerializer() - navController.navigate(NavRoutes.TaskCertificationEditorRoute.createRoute(serializer)) + navController.navigate( + NavRoutes.TaskCertificationEditorRoute.createRoute( + serializer, + ), + ) }, ) } @@ -66,10 +69,15 @@ object TaskCertificationGraph : NavGraphContributor { ) { TaskCertificationEditorRoute( navigateToBack = navController::popBackStack, - navigateToCertification = { + navigateToCertification = { goalId, photologId, comment -> navController.navigate( NavRoutes.TaskCertificationRoute.createRoute( - from = NavRoutes.TaskCertificationRoute.From.EDITOR, + from = + NavRoutes.TaskCertificationRoute.From.Editor( + goalId, + photologId, + comment, + ), ), ) }, @@ -86,10 +94,22 @@ object TaskCertificationGraph : NavGraphContributor { navArgument(NavRoutes.TaskCertificationRoute.ARG_FROM) { type = NavType.StringType }, + navArgument(NavRoutes.TaskCertificationRoute.ARG_PHOTOLOG_ID) { + type = NavType.LongType + }, + navArgument(NavRoutes.TaskCertificationRoute.ARG_COMMENT) { + type = NavType.StringType + }, ), ) { TaskCertificationRoute( navigateToBack = navController::popBackStack, + navigateToDetail = { + navController.popBackStack( + route = NavRoutes.TaskCertificationDetailRoute.route, + inclusive = false, + ) + }, ) } } From 5c4209a4631dee497c30ed9888b9cc26f876c0eb Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 02:13:15 +0900 Subject: [PATCH 32/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=AA=85=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/util/src/main/java/com/twix/util/di/UtilModule.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/util/src/main/java/com/twix/util/di/UtilModule.kt b/core/util/src/main/java/com/twix/util/di/UtilModule.kt index 56a5a0f1..5baca9fb 100644 --- a/core/util/src/main/java/com/twix/util/di/UtilModule.kt +++ b/core/util/src/main/java/com/twix/util/di/UtilModule.kt @@ -1,11 +1,11 @@ package com.twix.util.di import com.twix.util.bus.GoalRefreshBus -import com.twix.util.bus.TaskCertificationRefreshBus +import com.twix.util.bus.TaskCertificationDetailRefreshBus import org.koin.dsl.module val utilModule = module { single { GoalRefreshBus() } - single { TaskCertificationRefreshBus() } + single { TaskCertificationDetailRefreshBus() } } From 10e756303d4083df57342df68cbf10d0ee7e6cf4 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 02:13:30 +0900 Subject: [PATCH 33/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20UI=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=EC=83=B7=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../certification/TaskCertificationScreen.kt | 2 + .../TaskCertificationViewModel.kt | 69 +++++++++++++++---- .../model/TaskCertificationSideEffect.kt | 2 + .../editor/TaskCertificationEditorRoute.kt | 6 +- .../TaskCertificationEditorViewModel.kt | 4 +- .../src/main/res/values/strings.xml | 1 + 6 files changed, 64 insertions(+), 20 deletions(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt index 30d00c7d..84e104b4 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt @@ -62,6 +62,7 @@ fun TaskCertificationRoute( camera: Camera = koinInject(), viewModel: TaskCertificationViewModel = koinViewModel(), navigateToBack: () -> Unit, + navigateToDetail: () -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() val cameraPreview by camera.surfaceRequests.collectAsStateWithLifecycle() @@ -120,6 +121,7 @@ fun TaskCertificationRoute( } TaskCertificationSideEffect.NavigateToBack -> navigateToBack() + TaskCertificationSideEffect.NavigateToDetail -> navigateToDetail() } } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt index 83ec2993..2f4aac87 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt @@ -14,26 +14,36 @@ import com.twix.task_certification.certification.model.TaskCertificationSideEffe import com.twix.task_certification.certification.model.TaskCertificationUiState import com.twix.ui.base.BaseViewModel import com.twix.util.bus.GoalRefreshBus -import com.twix.util.bus.TaskCertificationRefreshBus +import com.twix.util.bus.TaskCertificationDetailRefreshBus import kotlinx.coroutines.delay import kotlinx.coroutines.launch import java.time.LocalDate class TaskCertificationViewModel( private val photologRepository: PhotoLogRepository, - private val taskCertificationRefreshBus: TaskCertificationRefreshBus, + private val detailRefreshBus: TaskCertificationDetailRefreshBus, private val goalRefreshBus: GoalRefreshBus, saveStateHandle: SavedStateHandle, ) : BaseViewModel( TaskCertificationUiState(), ) { - private val goalId: Long = - saveStateHandle[NavRoutes.TaskCertificationRoute.ARG_GOAL_ID] - ?: error(GOAL_ID_NOT_FOUND) + private val argGoalId: Long = + requireNotNull(saveStateHandle[NavRoutes.TaskCertificationRoute.ARG_GOAL_ID]) { GOAL_ID_NOT_FOUND } - private val from: String = - saveStateHandle[NavRoutes.TaskCertificationRoute.ARG_FROM] - ?: error(FROM_NOT_FOUND) + private val argFrom: String = + requireNotNull(saveStateHandle[NavRoutes.TaskCertificationRoute.ARG_FROM]) { FROM_NOT_FOUND } + + private val argPhotologId: Long = + requireNotNull(saveStateHandle[NavRoutes.TaskCertificationRoute.ARG_PHOTOLOG_ID]) { PHOTOLOG_ID_NOT_FOUND } + + private val argComment: String = + requireNotNull(saveStateHandle[NavRoutes.TaskCertificationRoute.ARG_COMMENT]) { COMMENT_NOT_FOUND } + + init { + if (argFrom == NavRoutes.TaskCertificationRoute.NAME_EDITOR && argComment.isNotEmpty()) { + reduceComment(argComment) + } + } override suspend fun handleIntent(intent: TaskCertificationIntent) { when (intent) { @@ -113,19 +123,27 @@ class TaskCertificationViewModel( contentType = "image/jpeg", ) }, - onSuccess = { fileName -> uploadPhotoLog(fileName) }, + onSuccess = { fileName -> + when (argFrom) { + NavRoutes.TaskCertificationRoute.NAME_DETAIL, + NavRoutes.TaskCertificationRoute.NAME_HOME, + -> uploadPhotolog(fileName) + + NavRoutes.TaskCertificationRoute.NAME_EDITOR -> modifyPhotolog(fileName) + } + }, onError = { showToast(R.string.task_certification_upload_fail, ToastType.ERROR) }, ) } - private fun uploadPhotoLog(fileName: String) { + private fun uploadPhotolog(fileName: String) { launchResult( block = { photologRepository.uploadPhotolog( PhotologParam( - goalId = goalId, + goalId = argGoalId, fileName = fileName, comment = currentState.comment.value, verificationDate = LocalDate.now(), @@ -133,10 +151,9 @@ class TaskCertificationViewModel( ) }, onSuccess = { - when (NavRoutes.TaskCertificationRoute.From.valueOf(from)) { - NavRoutes.TaskCertificationRoute.From.EDITOR -> Unit - NavRoutes.TaskCertificationRoute.From.DETAIL -> taskCertificationRefreshBus.notifyChanged() - NavRoutes.TaskCertificationRoute.From.HOME -> goalRefreshBus.notifyGoalListChanged() + when (argFrom) { + NavRoutes.TaskCertificationRoute.NAME_DETAIL -> detailRefreshBus.notifyChanged() + NavRoutes.TaskCertificationRoute.NAME_HOME -> goalRefreshBus.notifyGoalListChanged() } tryEmitSideEffect(TaskCertificationSideEffect.NavigateToBack) }, @@ -146,6 +163,25 @@ class TaskCertificationViewModel( ) } + private fun modifyPhotolog(fileName: String) { + launchResult( + block = { + photologRepository.modifyPhotolog( + photologId = argPhotologId, + fileName = fileName, + comment = currentState.comment.value, + ) + }, + onSuccess = { + detailRefreshBus.notifyChanged() + tryEmitSideEffect(TaskCertificationSideEffect.NavigateToDetail) + }, + onError = { + showToast(R.string.task_certification_modify_fail, ToastType.ERROR) + }, + ) + } + private fun showToast( message: Int, type: ToastType, @@ -154,9 +190,12 @@ class TaskCertificationViewModel( emitSideEffect(TaskCertificationSideEffect.ShowToast(message, type)) } } + companion object { private const val ERROR_DISPLAY_DURATION_MS = 1500L private const val GOAL_ID_NOT_FOUND = "Goal Id Argument Not Found" private const val FROM_NOT_FOUND = "From Argument Not Found" + private const val PHOTOLOG_ID_NOT_FOUND = "Photolog Id Argument Not Found" + private const val COMMENT_NOT_FOUND = "Comment Argument Not Found" } } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationSideEffect.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationSideEffect.kt index fefba96f..a2f4a62a 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationSideEffect.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationSideEffect.kt @@ -15,4 +15,6 @@ sealed interface TaskCertificationSideEffect : SideEffect { ) : TaskCertificationSideEffect data object NavigateToBack : TaskCertificationSideEffect + + data object NavigateToDetail : TaskCertificationSideEffect } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt index b05decc2..dff17cf6 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt @@ -59,7 +59,7 @@ import com.twix.designsystem.R as DesR @Composable fun TaskCertificationEditorRoute( navigateToBack: () -> Unit, - navigateToCertification: () -> Unit, + navigateToCertification: (Long, Long, String) -> Unit, toastManager: ToastManager = koinInject(), viewModel: TaskCertificationEditorViewModel = koinViewModel(), ) { @@ -86,7 +86,7 @@ fun TaskCertificationEditorRoute( ) { granted -> if (granted) { - navigateToCertification() + navigateToCertification(uiState.goalId, uiState.photologId, uiState.comment.value) return@rememberLauncherForActivityResult } val activity = currentContext.findActivity() ?: return@rememberLauncherForActivityResult @@ -119,7 +119,7 @@ fun TaskCertificationEditorRoute( onCommentChanged = { viewModel.dispatch(TaskCertificationEditorIntent.ModifyComment(it)) }, onClickRetake = { if (currentContext.hasCameraPermission()) { - navigateToCertification() + navigateToCertification(uiState.goalId, uiState.photologId, uiState.comment.value) } else { permissionLauncher.launch(Manifest.permission.CAMERA) } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt index 7299bba6..1ed3f47c 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt @@ -13,13 +13,13 @@ import com.twix.task_certification.editor.model.TaskCertificationEditorIntent import com.twix.task_certification.editor.model.TaskCertificationEditorSideEffect import com.twix.task_certification.editor.model.TaskCertificationEditorUiState import com.twix.ui.base.BaseViewModel -import com.twix.util.bus.TaskCertificationRefreshBus +import com.twix.util.bus.TaskCertificationDetailRefreshBus import kotlinx.coroutines.launch import kotlinx.serialization.json.Json class TaskCertificationEditorViewModel( private val photologRepository: PhotoLogRepository, - private val detailRefreshBus: TaskCertificationRefreshBus, + private val detailRefreshBus: TaskCertificationDetailRefreshBus, savedStateHandle: SavedStateHandle, ) : BaseViewModel( TaskCertificationEditorUiState(), diff --git a/feature/task-certification/src/main/res/values/strings.xml b/feature/task-certification/src/main/res/values/strings.xml index e4757151..9827977d 100644 --- a/feature/task-certification/src/main/res/values/strings.xml +++ b/feature/task-certification/src/main/res/values/strings.xml @@ -10,6 +10,7 @@ 인증샷 조회에 실패했습니다. + 인증샷 수정에 실패했어요. 다시 찍기 From e3cf77cfdc638e7fa7ac1d5e8c85077ed97bfa79 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 12:46:12 +0900 Subject: [PATCH 34/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=9D=B8=EC=A6=9D?= =?UTF-8?q?=EC=83=B7=20=EC=A1=B0=ED=9A=8C=20API=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=20?= =?UTF-8?q?(String=20->=20LocalDate)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/twix/network/service/PhotoLogService.kt | 3 ++- .../java/com/twix/data/repository/DefaultPhotoLogRepository.kt | 3 ++- .../main/java/com/twix/domain/repository/PhotoLogRepository.kt | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/network/src/main/java/com/twix/network/service/PhotoLogService.kt b/core/network/src/main/java/com/twix/network/service/PhotoLogService.kt index 66e52f7a..65bfc598 100644 --- a/core/network/src/main/java/com/twix/network/service/PhotoLogService.kt +++ b/core/network/src/main/java/com/twix/network/service/PhotoLogService.kt @@ -11,6 +11,7 @@ import de.jensklingenberg.ktorfit.http.POST import de.jensklingenberg.ktorfit.http.PUT import de.jensklingenberg.ktorfit.http.Path import de.jensklingenberg.ktorfit.http.Query +import java.time.LocalDate interface PhotoLogService { @GET("api/v1/photologs/upload-url") @@ -25,7 +26,7 @@ interface PhotoLogService { @GET("api/v1/photologs") suspend fun fetchPhotoLogs( - @Query("targetDate") request: String, + @Query("targetDate") request: LocalDate, ): PhotoLogsResponse @PUT("api/v1/photologs/{photologId}/reaction") diff --git a/data/src/main/java/com/twix/data/repository/DefaultPhotoLogRepository.kt b/data/src/main/java/com/twix/data/repository/DefaultPhotoLogRepository.kt index cefe9778..7803f930 100644 --- a/data/src/main/java/com/twix/data/repository/DefaultPhotoLogRepository.kt +++ b/data/src/main/java/com/twix/data/repository/DefaultPhotoLogRepository.kt @@ -14,6 +14,7 @@ import com.twix.network.model.response.photolog.mapper.toDomain import com.twix.network.service.PhotoLogService import com.twix.network.upload.PresignedUploader import com.twix.result.AppResult +import java.time.LocalDate class DefaultPhotoLogRepository( private val service: PhotoLogService, @@ -50,7 +51,7 @@ class DefaultPhotoLogRepository( return AppResult.Success(info.fileName) } - override suspend fun fetchPhotologs(targetDate: String): AppResult = + override suspend fun fetchPhotologs(targetDate: LocalDate): AppResult = safeApiCall { service.fetchPhotoLogs(targetDate).toDomain() } diff --git a/domain/src/main/java/com/twix/domain/repository/PhotoLogRepository.kt b/domain/src/main/java/com/twix/domain/repository/PhotoLogRepository.kt index a3f85950..063f5895 100644 --- a/domain/src/main/java/com/twix/domain/repository/PhotoLogRepository.kt +++ b/domain/src/main/java/com/twix/domain/repository/PhotoLogRepository.kt @@ -5,6 +5,7 @@ import com.twix.domain.model.photo.PhotoLogUploadInfo import com.twix.domain.model.photo.PhotologParam import com.twix.domain.model.photolog.PhotoLogs import com.twix.result.AppResult +import java.time.LocalDate interface PhotoLogRepository { suspend fun getUploadUrl(goalId: Long): AppResult @@ -17,7 +18,7 @@ interface PhotoLogRepository { contentType: String, ): AppResult - suspend fun fetchPhotologs(targetDate: String): AppResult + suspend fun fetchPhotologs(targetDate: LocalDate): AppResult suspend fun reactToPhotolog( photologId: Long, From 38a8f442263ccb1755b0ae36ef6e0a2918386729 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 12:47:19 +0900 Subject: [PATCH 35/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EC=83=88=EB=A1=9C=EA=B3=A0=EC=B9=A8=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EB=B2=84=EC=8A=A4=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bus/TaskCertificationDetailRefreshBus.kt | 17 -------------- .../util/bus/TaskCertificationRefreshBus.kt | 22 +++++++++++++++++++ .../main/java/com/twix/util/di/UtilModule.kt | 4 ++-- .../TaskCertificationEditorViewModel.kt | 10 ++++----- .../model/TaskCertificationEditorUiState.kt | 4 ++-- 5 files changed, 31 insertions(+), 26 deletions(-) delete mode 100644 core/util/src/main/java/com/twix/util/bus/TaskCertificationDetailRefreshBus.kt create mode 100644 core/util/src/main/java/com/twix/util/bus/TaskCertificationRefreshBus.kt diff --git a/core/util/src/main/java/com/twix/util/bus/TaskCertificationDetailRefreshBus.kt b/core/util/src/main/java/com/twix/util/bus/TaskCertificationDetailRefreshBus.kt deleted file mode 100644 index 7b781ae4..00000000 --- a/core/util/src/main/java/com/twix/util/bus/TaskCertificationDetailRefreshBus.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.twix.util.bus - -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow - -class TaskCertificationDetailRefreshBus { - private val _events = - MutableSharedFlow( - replay = 0, - extraBufferCapacity = 1, - ) - - val events: SharedFlow = _events.asSharedFlow() - - fun notifyChanged() = _events.tryEmit(Unit) -} diff --git a/core/util/src/main/java/com/twix/util/bus/TaskCertificationRefreshBus.kt b/core/util/src/main/java/com/twix/util/bus/TaskCertificationRefreshBus.kt new file mode 100644 index 00000000..578d3a57 --- /dev/null +++ b/core/util/src/main/java/com/twix/util/bus/TaskCertificationRefreshBus.kt @@ -0,0 +1,22 @@ +package com.twix.util.bus + +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow + +class TaskCertificationRefreshBus { + enum class Publisher { + PHOTOLOG, + EDITOR, + } + + private val _events = + MutableSharedFlow( + replay = 0, + extraBufferCapacity = 1, + ) + + val events: SharedFlow = _events.asSharedFlow() + + fun notifyChanged(value: Publisher) = _events.tryEmit(value) +} diff --git a/core/util/src/main/java/com/twix/util/di/UtilModule.kt b/core/util/src/main/java/com/twix/util/di/UtilModule.kt index 5baca9fb..56a5a0f1 100644 --- a/core/util/src/main/java/com/twix/util/di/UtilModule.kt +++ b/core/util/src/main/java/com/twix/util/di/UtilModule.kt @@ -1,11 +1,11 @@ package com.twix.util.di import com.twix.util.bus.GoalRefreshBus -import com.twix.util.bus.TaskCertificationDetailRefreshBus +import com.twix.util.bus.TaskCertificationRefreshBus import org.koin.dsl.module val utilModule = module { single { GoalRefreshBus() } - single { TaskCertificationDetailRefreshBus() } + single { TaskCertificationRefreshBus() } } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt index 1ed3f47c..3e1ec637 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt @@ -6,20 +6,20 @@ import androidx.lifecycle.viewModelScope import com.twix.designsystem.components.toast.model.ToastType import com.twix.domain.repository.PhotoLogRepository import com.twix.navigation.NavRoutes -import com.twix.navigation.serializer.TaskCertificationSerializer +import com.twix.navigation.serializer.EditorSerializer import com.twix.result.AppResult import com.twix.task_certification.R import com.twix.task_certification.editor.model.TaskCertificationEditorIntent import com.twix.task_certification.editor.model.TaskCertificationEditorSideEffect import com.twix.task_certification.editor.model.TaskCertificationEditorUiState import com.twix.ui.base.BaseViewModel -import com.twix.util.bus.TaskCertificationDetailRefreshBus +import com.twix.util.bus.TaskCertificationRefreshBus import kotlinx.coroutines.launch import kotlinx.serialization.json.Json class TaskCertificationEditorViewModel( private val photologRepository: PhotoLogRepository, - private val detailRefreshBus: TaskCertificationDetailRefreshBus, + private val detailRefreshBus: TaskCertificationRefreshBus, savedStateHandle: SavedStateHandle, ) : BaseViewModel( TaskCertificationEditorUiState(), @@ -30,7 +30,7 @@ class TaskCertificationEditorViewModel( .get(NavRoutes.TaskCertificationEditorRoute.ARG_DATA) ?.let { encoded -> val json = Uri.decode(encoded) - Json.decodeFromString(json) + Json.decodeFromString(json) }, ) { SERIALIZER_NOT_FOUND } @@ -67,7 +67,7 @@ class TaskCertificationEditorViewModel( launchResult( block = { launchModifyComment() }, onSuccess = { - detailRefreshBus.notifyChanged() + detailRefreshBus.notifyChanged(TaskCertificationRefreshBus.Publisher.EDITOR) showToast(R.string.task_certification_editor_modify_success, ToastType.SUCCESS) }, onError = { diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt index 9a1e90a9..e04d5e1d 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt @@ -2,7 +2,7 @@ package com.twix.task_certification.editor.model import androidx.compose.runtime.Immutable import com.twix.designsystem.components.comment.model.CommentUiModel -import com.twix.navigation.serializer.TaskCertificationSerializer +import com.twix.navigation.serializer.EditorSerializer import com.twix.ui.base.State @Immutable @@ -25,7 +25,7 @@ data class TaskCertificationEditorUiState( val imageName: String get() = imageUrl.split(IMAGE_NAME_SEPARATOR).last() - fun updateInitialState(serializer: TaskCertificationSerializer) = + fun updateInitialState(serializer: EditorSerializer) = copy( goalId = serializer.goalId, nickname = serializer.nickname, From ef18244b6b904238fcc16a570b5d5cfc19b373e2 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 12:48:50 +0900 Subject: [PATCH 36/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=9D=B8=EC=A6=9D=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EC=9D=B4=EB=8F=99=20=EC=8B=9C=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C=20=EC=A0=95=EB=B3=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `navigateToCertification` 호출 시 `selectedDate`를 함께 전달하도록 수정 - `TaskCertificationRoute`의 경로 생성 방식을 `DetailSerializer`를 사용하여 JSON으로 직렬화하도록 변경 - `From`을 sealed class에서 enum class로 변경하고 관련 로직 수정 --- .../java/com/twix/navigation/NavRoutes.kt | 53 ++++++------------- .../navigation/serializer/EditorSerializer.kt | 23 ++++++++ .../serializer/TaskCertificationSerializer.kt | 13 ----- .../src/main/java/com/twix/home/HomeScreen.kt | 4 +- .../src/main/java/com/twix/main/MainScreen.kt | 4 +- .../com/twix/main/navigation/MainNavGraph.kt | 15 +++--- .../navigation/TaskCertificationGraph.kt | 38 ++++++------- 7 files changed, 68 insertions(+), 82 deletions(-) create mode 100644 core/navigation/src/main/java/com/twix/navigation/serializer/EditorSerializer.kt delete mode 100644 core/navigation/src/main/java/com/twix/navigation/serializer/TaskCertificationSerializer.kt diff --git a/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt b/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt index c9037dc9..70de3a1c 100644 --- a/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt +++ b/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt @@ -1,7 +1,8 @@ package com.twix.navigation import android.net.Uri -import com.twix.navigation.serializer.TaskCertificationSerializer +import com.twix.navigation.serializer.DetailSerializer +import com.twix.navigation.serializer.EditorSerializer import kotlinx.serialization.json.Json import java.time.LocalDate @@ -47,51 +48,27 @@ sealed class NavRoutes( ) = "task_certification_detail/$goalId/$date/$betweenUs" } - object TaskCertificationRoute : - NavRoutes("task_certification/{goalId}/{from}/{photologId}/{comment}") { - const val ARG_GOAL_ID = "goalId" - const val ARG_FROM = "from" - const val ARG_PHOTOLOG_ID = "photologId" - const val ARG_COMMENT = "comment" - - const val NAME_HOME = "HOME" - const val NAME_DETAIL = "DETAIL" - const val NAME_EDITOR = "EDITOR" - - private const val NOT_NEED_PHOTOLOG_ID = -1L - private const val NOT_NEED_COMMENT = -1L - - sealed class From( - val name: String, - ) { - data class Home( - val goalId: Long, - ) : From(NAME_HOME) - - data class Detail( - val goalId: Long, - ) : From(NAME_DETAIL) - - data class Editor( - val goalId: Long, - val photologId: Long, - val comment: String, - ) : From(NAME_EDITOR) + object TaskCertificationRoute : NavRoutes("task_certification/{data}") { + const val ARG_DATA = "data" + + enum class From { + HOME, + DETAIL, + EDITOR, } - fun createRoute(from: From): String = - when (from) { - is From.Home -> "task_certification/${from.goalId}/${from.name}/$NOT_NEED_PHOTOLOG_ID/$NOT_NEED_COMMENT" - is From.Detail -> "task_certification/${from.goalId}/${from.name}/$NOT_NEED_PHOTOLOG_ID/$NOT_NEED_COMMENT" - is From.Editor -> "task_certification/${from.goalId}/${from.name}/${from.photologId}/${from.comment}" - } + fun createRoute(data: DetailSerializer): String { + val json = Json.encodeToString(data) + val encoded = Uri.encode(json) + return "task_certification/$encoded" + } } object TaskCertificationEditorRoute : NavRoutes("task_certification_editor/{data}") { const val ARG_DATA = "data" - fun createRoute(data: TaskCertificationSerializer): String { + fun createRoute(data: EditorSerializer): String { val json = Json.encodeToString(data) val encoded = Uri.encode(json) return "task_certification_editor/$encoded" diff --git a/core/navigation/src/main/java/com/twix/navigation/serializer/EditorSerializer.kt b/core/navigation/src/main/java/com/twix/navigation/serializer/EditorSerializer.kt new file mode 100644 index 00000000..e28775fe --- /dev/null +++ b/core/navigation/src/main/java/com/twix/navigation/serializer/EditorSerializer.kt @@ -0,0 +1,23 @@ +package com.twix.navigation.serializer + +import com.twix.navigation.NavRoutes +import kotlinx.serialization.Serializable + +@Serializable +data class EditorSerializer( + val goalId: Long, + val goalName: String, + val nickname: String, + val photologId: Long, + val imageUrl: String, + val comment: String?, +) + +@Serializable +data class DetailSerializer( + val goalId: Long, + val from: NavRoutes.TaskCertificationRoute.From, + val photologId: Long = -1, + val selectedDate: String = "", + val comment: String = "", +) diff --git a/core/navigation/src/main/java/com/twix/navigation/serializer/TaskCertificationSerializer.kt b/core/navigation/src/main/java/com/twix/navigation/serializer/TaskCertificationSerializer.kt deleted file mode 100644 index 95abc471..00000000 --- a/core/navigation/src/main/java/com/twix/navigation/serializer/TaskCertificationSerializer.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.twix.navigation.serializer - -import kotlinx.serialization.Serializable - -@Serializable -data class TaskCertificationSerializer( - val goalId: Long, - val goalName: String, - val nickname: String, - val photologId: Long, - val imageUrl: String, - val comment: String?, -) diff --git a/feature/main/src/main/java/com/twix/home/HomeScreen.kt b/feature/main/src/main/java/com/twix/home/HomeScreen.kt index 30089506..39839df0 100644 --- a/feature/main/src/main/java/com/twix/home/HomeScreen.kt +++ b/feature/main/src/main/java/com/twix/home/HomeScreen.kt @@ -71,7 +71,7 @@ fun HomeRoute( navigateToGoalEditor: () -> Unit, navigateToGoalManage: (LocalDate) -> Unit, navigateToSettings: () -> Unit, - navigateToCertification: (Long) -> Unit, + navigateToCertification: (Long, LocalDate) -> Unit, navigateToCertificationDetail: (Long, LocalDate, BetweenUs) -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -85,7 +85,7 @@ fun HomeRoute( ActivityResultContracts.RequestPermission(), ) { granted -> if (granted) { - pendingGoalId?.let { navigateToCertification(it) } + pendingGoalId?.let { navigateToCertification(it, uiState.selectedDate) } return@rememberLauncherForActivityResult } diff --git a/feature/main/src/main/java/com/twix/main/MainScreen.kt b/feature/main/src/main/java/com/twix/main/MainScreen.kt index 119df5dc..fbf83b75 100644 --- a/feature/main/src/main/java/com/twix/main/MainScreen.kt +++ b/feature/main/src/main/java/com/twix/main/MainScreen.kt @@ -31,7 +31,7 @@ fun MainRoute( navigateToGoalEditor: () -> Unit, navigateToGoalManage: (LocalDate) -> Unit, navigateToSettings: () -> Unit, - navigateToCertification: (Long) -> Unit, + navigateToCertification: (Long, LocalDate) -> Unit, navigateToCertificationDetail: (Long, LocalDate, BetweenUs) -> Unit, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() @@ -57,7 +57,7 @@ private fun MainScreen( navigateToGoalEditor: () -> Unit, navigateToGoalManage: (LocalDate) -> Unit, navigateToSettings: () -> Unit, - navigateToCertification: (Long) -> Unit, + navigateToCertification: (Long, LocalDate) -> Unit, navigateToCertificationDetail: (Long, LocalDate, BetweenUs) -> Unit, ) { val calendarState by homeViewModel.calendarState.collectAsStateWithLifecycle() diff --git a/feature/main/src/main/java/com/twix/main/navigation/MainNavGraph.kt b/feature/main/src/main/java/com/twix/main/navigation/MainNavGraph.kt index 318c2e86..b4f3281e 100644 --- a/feature/main/src/main/java/com/twix/main/navigation/MainNavGraph.kt +++ b/feature/main/src/main/java/com/twix/main/navigation/MainNavGraph.kt @@ -7,6 +7,7 @@ import androidx.navigation.navigation import com.twix.main.MainRoute import com.twix.navigation.NavRoutes import com.twix.navigation.base.NavGraphContributor +import com.twix.navigation.serializer.DetailSerializer object MainNavGraph : NavGraphContributor { override val graphRoute: NavRoutes @@ -36,11 +37,13 @@ object MainNavGraph : NavGraphContributor { launchSingleTop = true } }, - navigateToCertification = { goalId -> + navigateToCertification = { goalId, date -> val destination = NavRoutes.TaskCertificationRoute.createRoute( - NavRoutes.TaskCertificationRoute.From.Home( - goalId, + DetailSerializer( + goalId = goalId, + from = NavRoutes.TaskCertificationRoute.From.HOME, + selectedDate = date.toString(), ), ) navController.navigate(destination) { @@ -48,13 +51,13 @@ object MainNavGraph : NavGraphContributor { } }, navigateToCertificationDetail = { goalId, date, betweenUs -> - navController.navigate( + val destination = NavRoutes.TaskCertificationDetailRoute.createRoute( goalId, date, betweenUs.name, - ), - ) { + ) + navController.navigate(destination) { launchSingleTop = true } }, diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt b/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt index 65e79ae9..2b6c8b6d 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt @@ -8,6 +8,7 @@ import androidx.navigation.navArgument import androidx.navigation.navigation import com.twix.navigation.NavRoutes import com.twix.navigation.base.NavGraphContributor +import com.twix.navigation.serializer.DetailSerializer import com.twix.task_certification.certification.TaskCertificationRoute import com.twix.task_certification.detail.TaskCertificationDetailRoute import com.twix.task_certification.editor.TaskCertificationEditorRoute @@ -40,10 +41,14 @@ object TaskCertificationGraph : NavGraphContributor { ) { TaskCertificationDetailRoute( navigateToBack = navController::popBackStack, - navigateToUpload = { + navigateToCertification = { goalId, date -> val destination = NavRoutes.TaskCertificationRoute.createRoute( - NavRoutes.TaskCertificationRoute.From.Detail(it), + DetailSerializer( + goalId = goalId, + from = NavRoutes.TaskCertificationRoute.From.DETAIL, + selectedDate = date.toString(), + ), ) navController.navigate(destination) }, @@ -70,16 +75,16 @@ object TaskCertificationGraph : NavGraphContributor { TaskCertificationEditorRoute( navigateToBack = navController::popBackStack, navigateToCertification = { goalId, photologId, comment -> - navController.navigate( + val destination = NavRoutes.TaskCertificationRoute.createRoute( - from = - NavRoutes.TaskCertificationRoute.From.Editor( - goalId, - photologId, - comment, - ), - ), - ) + DetailSerializer( + goalId = goalId, + from = NavRoutes.TaskCertificationRoute.From.EDITOR, + photologId = photologId, + comment = comment, + ), + ) + navController.navigate(destination) }, ) } @@ -88,16 +93,7 @@ object TaskCertificationGraph : NavGraphContributor { route = NavRoutes.TaskCertificationRoute.route, arguments = listOf( - navArgument(NavRoutes.TaskCertificationRoute.ARG_GOAL_ID) { - type = NavType.LongType - }, - navArgument(NavRoutes.TaskCertificationRoute.ARG_FROM) { - type = NavType.StringType - }, - navArgument(NavRoutes.TaskCertificationRoute.ARG_PHOTOLOG_ID) { - type = NavType.LongType - }, - navArgument(NavRoutes.TaskCertificationRoute.ARG_COMMENT) { + navArgument(NavRoutes.TaskCertificationRoute.ARG_DATA) { type = NavType.StringType }, ), From 95f4390e9ec4f6b35011ec73f5a57a7f225b0778 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 12:49:19 +0900 Subject: [PATCH 37/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=9D=B8=EC=A6=9D=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EC=9D=B4=EB=8F=99=20=EC=8B=9C=20Serialize?= =?UTF-8?q?r=EB=A5=BC=20=ED=86=B5=ED=95=B4=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=20=EC=A0=84=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존에 여러 인자를 각각 전달하던 방식에서 `DetailSerializer`를 사용하여 데이터를 전달하도록 변경 - 이에 따라 ViewModel에서 `savedStateHandle`을 통해 데이터를 파싱하고 사용하는 로직 수정 - 변경된 데이터 전달 방식에 맞춰 화면 이동 로직 업데이트 --- .../TaskCertificationViewModel.kt | 77 ++++++++++--------- .../detail/TaskCertificationDetail.kt | 7 +- .../TaskCertificationDetailViewModel.kt | 34 +++++--- .../model/TaskCertificationDetailUiState.kt | 16 +++- 4 files changed, 82 insertions(+), 52 deletions(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt index 2f4aac87..50d90bc9 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt @@ -7,6 +7,7 @@ import com.twix.designsystem.components.toast.model.ToastType import com.twix.domain.model.photo.PhotologParam import com.twix.domain.repository.PhotoLogRepository import com.twix.navigation.NavRoutes +import com.twix.navigation.serializer.DetailSerializer import com.twix.task_certification.R import com.twix.task_certification.certification.model.CaptureStatus import com.twix.task_certification.certification.model.TaskCertificationIntent @@ -14,34 +15,33 @@ import com.twix.task_certification.certification.model.TaskCertificationSideEffe import com.twix.task_certification.certification.model.TaskCertificationUiState import com.twix.ui.base.BaseViewModel import com.twix.util.bus.GoalRefreshBus -import com.twix.util.bus.TaskCertificationDetailRefreshBus +import com.twix.util.bus.TaskCertificationRefreshBus import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import kotlinx.serialization.json.Json import java.time.LocalDate class TaskCertificationViewModel( private val photologRepository: PhotoLogRepository, - private val detailRefreshBus: TaskCertificationDetailRefreshBus, + private val detailRefreshBus: TaskCertificationRefreshBus, private val goalRefreshBus: GoalRefreshBus, - saveStateHandle: SavedStateHandle, + savedStateHandle: SavedStateHandle, ) : BaseViewModel( TaskCertificationUiState(), ) { - private val argGoalId: Long = - requireNotNull(saveStateHandle[NavRoutes.TaskCertificationRoute.ARG_GOAL_ID]) { GOAL_ID_NOT_FOUND } - - private val argFrom: String = - requireNotNull(saveStateHandle[NavRoutes.TaskCertificationRoute.ARG_FROM]) { FROM_NOT_FOUND } - - private val argPhotologId: Long = - requireNotNull(saveStateHandle[NavRoutes.TaskCertificationRoute.ARG_PHOTOLOG_ID]) { PHOTOLOG_ID_NOT_FOUND } - - private val argComment: String = - requireNotNull(saveStateHandle[NavRoutes.TaskCertificationRoute.ARG_COMMENT]) { COMMENT_NOT_FOUND } + private val serializer: DetailSerializer = + requireNotNull( + savedStateHandle + .get(NavRoutes.TaskCertificationRoute.ARG_DATA) + ?.let { encoded -> + val json = Uri.decode(encoded) + Json.decodeFromString(json) + }, + ) { SERIALIZER_NOT_FOUND } init { - if (argFrom == NavRoutes.TaskCertificationRoute.NAME_EDITOR && argComment.isNotEmpty()) { - reduceComment(argComment) + if (serializer.from == NavRoutes.TaskCertificationRoute.From.EDITOR) { + reduceComment(serializer.comment) } } @@ -118,18 +118,18 @@ class TaskCertificationViewModel( launchResult( block = { photologRepository.uploadPhotologImage( - goalId = argGoalId, + goalId = serializer.goalId, bytes = image, contentType = "image/jpeg", ) }, onSuccess = { fileName -> - when (argFrom) { - NavRoutes.TaskCertificationRoute.NAME_DETAIL, - NavRoutes.TaskCertificationRoute.NAME_HOME, + when (serializer.from) { + NavRoutes.TaskCertificationRoute.From.DETAIL, + NavRoutes.TaskCertificationRoute.From.HOME, -> uploadPhotolog(fileName) - NavRoutes.TaskCertificationRoute.NAME_EDITOR -> modifyPhotolog(fileName) + NavRoutes.TaskCertificationRoute.From.EDITOR -> modifyPhotolog(fileName) } }, onError = { @@ -143,37 +143,45 @@ class TaskCertificationViewModel( block = { photologRepository.uploadPhotolog( PhotologParam( - goalId = argGoalId, + goalId = serializer.goalId, fileName = fileName, comment = currentState.comment.value, - verificationDate = LocalDate.now(), + verificationDate = LocalDate.parse(serializer.selectedDate), ), ) }, - onSuccess = { - when (argFrom) { - NavRoutes.TaskCertificationRoute.NAME_DETAIL -> detailRefreshBus.notifyChanged() - NavRoutes.TaskCertificationRoute.NAME_HOME -> goalRefreshBus.notifyGoalListChanged() - } - tryEmitSideEffect(TaskCertificationSideEffect.NavigateToBack) - }, + onSuccess = { handleUploadPhotologSuccess() }, onError = { showToast(R.string.task_certification_upload_fail, ToastType.ERROR) }, ) } + private fun handleUploadPhotologSuccess() { + when (serializer.from) { + NavRoutes.TaskCertificationRoute.From.EDITOR -> + detailRefreshBus.notifyChanged(TaskCertificationRefreshBus.Publisher.EDITOR) + + NavRoutes.TaskCertificationRoute.From.DETAIL -> + detailRefreshBus.notifyChanged(TaskCertificationRefreshBus.Publisher.PHOTOLOG) + + NavRoutes.TaskCertificationRoute.From.HOME -> + goalRefreshBus.notifyGoalListChanged() + } + tryEmitSideEffect(TaskCertificationSideEffect.NavigateToBack) + } + private fun modifyPhotolog(fileName: String) { launchResult( block = { photologRepository.modifyPhotolog( - photologId = argPhotologId, + photologId = serializer.photologId, fileName = fileName, comment = currentState.comment.value, ) }, onSuccess = { - detailRefreshBus.notifyChanged() + detailRefreshBus.notifyChanged(TaskCertificationRefreshBus.Publisher.PHOTOLOG) tryEmitSideEffect(TaskCertificationSideEffect.NavigateToDetail) }, onError = { @@ -193,9 +201,6 @@ class TaskCertificationViewModel( companion object { private const val ERROR_DISPLAY_DURATION_MS = 1500L - private const val GOAL_ID_NOT_FOUND = "Goal Id Argument Not Found" - private const val FROM_NOT_FOUND = "From Argument Not Found" - private const val PHOTOLOG_ID_NOT_FOUND = "Photolog Id Argument Not Found" - private const val COMMENT_NOT_FOUND = "Comment Argument Not Found" + private const val SERIALIZER_NOT_FOUND = "Serializer Not Found" } } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt index 8a19a77f..361e2ed3 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt @@ -54,12 +54,13 @@ import com.twix.ui.extension.hasCameraPermission import kotlinx.coroutines.launch import org.koin.androidx.compose.koinViewModel import org.koin.compose.koinInject +import java.time.LocalDate import com.twix.designsystem.R as DesR @Composable fun TaskCertificationDetailRoute( navigateToBack: () -> Unit, - navigateToUpload: (Long) -> Unit, + navigateToCertification: (Long, LocalDate) -> Unit, navigateToEditor: (TaskCertificationDetailUiState) -> Unit, toastManager: ToastManager = koinInject(), viewModel: TaskCertificationDetailViewModel = koinViewModel(), @@ -85,7 +86,7 @@ fun TaskCertificationDetailRoute( ) { granted -> if (granted) { - navigateToUpload(uiState.currentGoalId) + navigateToCertification(uiState.currentGoalId, uiState.selectedDate) return@rememberLauncherForActivityResult } val activity = currentContext.findActivity() ?: return@rememberLauncherForActivityResult @@ -117,7 +118,7 @@ fun TaskCertificationDetailRoute( onClickReaction = { viewModel.dispatch(TaskCertificationDetailIntent.Reaction(it)) }, onClickUpload = { if (currentContext.hasCameraPermission()) { - navigateToUpload(uiState.currentGoalId) + navigateToCertification(uiState.currentGoalId, uiState.selectedDate) } else { permissionLauncher.launch(Manifest.permission.CAMERA) } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt index ae8529aa..44ae0128 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt @@ -3,7 +3,6 @@ package com.twix.task_certification.detail import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.twix.designsystem.components.toast.model.ToastType -import com.twix.domain.model.enums.BetweenUs import com.twix.domain.model.enums.GoalReactionType import com.twix.domain.repository.PhotoLogRepository import com.twix.navigation.NavRoutes @@ -14,17 +13,18 @@ import com.twix.task_certification.detail.model.TaskCertificationDetailUiState import com.twix.task_certification.detail.model.toUiModel import com.twix.ui.base.BaseViewModel import com.twix.util.bus.GoalRefreshBus -import com.twix.util.bus.TaskCertificationDetailRefreshBus +import com.twix.util.bus.TaskCertificationRefreshBus import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch +import java.time.LocalDate class TaskCertificationDetailViewModel( private val photologRepository: PhotoLogRepository, - private val detailRefreshBus: TaskCertificationDetailRefreshBus, + private val detailRefreshBus: TaskCertificationRefreshBus, private val goalRefreshBus: GoalRefreshBus, savedStateHandle: SavedStateHandle, ) : BaseViewModel( @@ -34,9 +34,11 @@ class TaskCertificationDetailViewModel( savedStateHandle[NavRoutes.TaskCertificationDetailRoute.ARG_GOAL_ID] ?: error(GOAL_ID_NOT_FOUND) - private val targetDate: String = - savedStateHandle[NavRoutes.TaskCertificationDetailRoute.ARG_DATE] - ?: error(TARGET_DATE_NOT_FOUND) + private val selectedDate: LocalDate = + LocalDate.parse( + savedStateHandle[NavRoutes.TaskCertificationDetailRoute.ARG_DATE] + ?: error(TARGET_DATE_NOT_FOUND), + ) private val betweenUs: String = savedStateHandle[NavRoutes.TaskCertificationDetailRoute.ARG_BETWEEN_US] @@ -78,14 +80,24 @@ class TaskCertificationDetailViewModel( private fun reduceInitialState() = reduce { - copy(currentGoalId = goalId, currentShow = BetweenUs.valueOf(betweenUs)) + setupInitialState( + goalId, + this@TaskCertificationDetailViewModel.selectedDate, + betweenUs, + ) } private fun collectEventBus() { viewModelScope.launch { - detailRefreshBus.events.collect { - fetchPhotolog() - goalRefreshBus.notifyGoalListChanged() + detailRefreshBus.events.collect { publisher -> + when (publisher) { + TaskCertificationRefreshBus.Publisher.PHOTOLOG -> { + fetchPhotolog() + goalRefreshBus.notifyGoalListChanged() + } + + TaskCertificationRefreshBus.Publisher.EDITOR -> fetchPhotolog() + } } } } @@ -100,7 +112,7 @@ class TaskCertificationDetailViewModel( private fun fetchPhotolog() { launchResult( - block = { photologRepository.fetchPhotologs(targetDate) }, + block = { photologRepository.fetchPhotologs(selectedDate) }, onSuccess = { reduce { copy(photoLogs = it.toUiModel()) } }, diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt index 975c7f6c..bbddba16 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt @@ -3,13 +3,15 @@ package com.twix.task_certification.detail.model import androidx.compose.runtime.Immutable import com.twix.domain.model.enums.BetweenUs import com.twix.domain.model.enums.GoalReactionType -import com.twix.navigation.serializer.TaskCertificationSerializer +import com.twix.navigation.serializer.EditorSerializer import com.twix.ui.base.State +import java.time.LocalDate @Immutable data class TaskCertificationDetailUiState( val currentGoalId: Long = -1L, val currentShow: BetweenUs = BetweenUs.PARTNER, + val selectedDate: LocalDate = LocalDate.now(), val photoLogs: PhotoLogsUiModel = PhotoLogsUiModel(), ) : State { val currentGoal: GoalPhotologUiModel @@ -62,8 +64,18 @@ data class TaskCertificationDetailUiState( fun updatePartnerReaction(type: GoalReactionType) = copy(photoLogs = photoLogs.updatePartnerReaction(currentGoalId, type)) + fun setupInitialState( + goalId: Long, + selectedDate: LocalDate, + currentShow: String, + ) = copy( + currentGoalId = goalId, + selectedDate = selectedDate, + currentShow = BetweenUs.valueOf(currentShow), + ) + fun toSerializer() = - TaskCertificationSerializer( + EditorSerializer( goalId = currentGoal.goalId, nickname = photoLogs.myNickname, goalName = currentGoal.goalName, From d8f3cc150c092715fc97aa1a2c6aba6d291a0563 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 12:55:59 +0900 Subject: [PATCH 38/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20'?= =?UTF-8?q?=EC=B0=8C=EB=A5=B4=EA=B8=B0'=20=EB=AC=B8=EC=9E=90=EC=97=B4=20?= =?UTF-8?q?=EB=A6=AC=EC=86=8C=EC=8A=A4=20=EA=B3=B5=EC=9A=A9=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/designsystem/components/photolog/BackgroundCard.kt | 2 +- core/design-system/src/main/res/values/strings.xml | 2 +- .../twix/task_certification/detail/TaskCertificationDetail.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/photolog/BackgroundCard.kt b/core/design-system/src/main/java/com/twix/designsystem/components/photolog/BackgroundCard.kt index 56e5829c..ab898f48 100644 --- a/core/design-system/src/main/java/com/twix/designsystem/components/photolog/BackgroundCard.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/photolog/BackgroundCard.kt @@ -85,7 +85,7 @@ fun BackgroundCard( fun PreviewBackgroundCard() { TwixTheme { BackgroundCard( - buttonTitle = stringResource(R.string.partner_sting), + buttonTitle = stringResource(R.string.word_sting), uploadedAt = "2023.10.31 23:59", onClick = {}, isCertificated = true, diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index 30019d82..8d1ad4da 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -20,6 +20,7 @@ 계정 정보 로그아웃 + 찌르기 매일 매주 @@ -51,7 +52,6 @@ %s님은\n아직… - 찌르기 목표 직접 만들기 diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt index 361e2ed3..f647eefb 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt @@ -166,7 +166,7 @@ fun TaskCertificationDetailScreen( buttonTitle = when (uiState.currentShow) { BetweenUs.ME -> stringResource(R.string.task_certification_take_picture) - BetweenUs.PARTNER -> stringResource(DesR.string.partner_sting) + BetweenUs.PARTNER -> stringResource(DesR.string.word_sting) }, rotation = when (uiState.currentShow) { From a953a72d55b9183bedfafc054247263613430dbf Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 13:03:00 +0900 Subject: [PATCH 39/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EC=83=81=EC=84=B8=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=99=94=EC=9D=84=20=EB=95=8C=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../certification/TaskCertificationViewModel.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt index 50d90bc9..10b34371 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt @@ -161,12 +161,9 @@ class TaskCertificationViewModel( when (serializer.from) { NavRoutes.TaskCertificationRoute.From.EDITOR -> detailRefreshBus.notifyChanged(TaskCertificationRefreshBus.Publisher.EDITOR) - - NavRoutes.TaskCertificationRoute.From.DETAIL -> - detailRefreshBus.notifyChanged(TaskCertificationRefreshBus.Publisher.PHOTOLOG) - NavRoutes.TaskCertificationRoute.From.HOME -> goalRefreshBus.notifyGoalListChanged() + NavRoutes.TaskCertificationRoute.From.DETAIL -> Unit } tryEmitSideEffect(TaskCertificationSideEffect.NavigateToBack) } From 5bc0c6a5d49afccfcc7898968ac35218d2fd802f Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 13:07:01 +0900 Subject: [PATCH 40/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20serializ?= =?UTF-8?q?er=20=EC=A0=91=EA=B7=BC=20=EC=A0=9C=EC=96=B4=EC=9E=90=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../editor/TaskCertificationEditorViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt index 3e1ec637..f0a31ffd 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt @@ -24,7 +24,7 @@ class TaskCertificationEditorViewModel( ) : BaseViewModel( TaskCertificationEditorUiState(), ) { - val serializer = + private val serializer = requireNotNull( savedStateHandle .get(NavRoutes.TaskCertificationEditorRoute.ARG_DATA) From 81068cd677a3cd4741e771335d1723058bd3f84c Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 13:31:05 +0900 Subject: [PATCH 41/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=9E=98=EB=AA=BB=20=EC=88=98=EC=A0=95=ED=95=9C=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../certification/TaskCertificationViewModel.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt index 10b34371..d05a3fd6 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt @@ -159,11 +159,12 @@ class TaskCertificationViewModel( private fun handleUploadPhotologSuccess() { when (serializer.from) { - NavRoutes.TaskCertificationRoute.From.EDITOR -> - detailRefreshBus.notifyChanged(TaskCertificationRefreshBus.Publisher.EDITOR) NavRoutes.TaskCertificationRoute.From.HOME -> goalRefreshBus.notifyGoalListChanged() - NavRoutes.TaskCertificationRoute.From.DETAIL -> Unit + NavRoutes.TaskCertificationRoute.From.DETAIL -> + detailRefreshBus.notifyChanged(TaskCertificationRefreshBus.Publisher.PHOTOLOG) + NavRoutes.TaskCertificationRoute.From.EDITOR -> Unit + } tryEmitSideEffect(TaskCertificationSideEffect.NavigateToBack) } From 38cb60e3acede96b986653e2a5df277c1a70e235 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 14:04:16 +0900 Subject: [PATCH 42/52] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EA=B0=95=EC=A0=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/comment/CommentTextField.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt index 00904601..8f3ca856 100644 --- a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt @@ -63,6 +63,16 @@ fun CommentTextField( ) } + LaunchedEffect(uiModel.value) { + if (uiModel.value != internalValue.text) { + internalValue = + TextFieldValue( + text = uiModel.value, + selection = TextRange(uiModel.value.length), + ) + } + } + var isInitialized by remember { mutableStateOf(false) } LaunchedEffect(keyboardState) { From 9c55fc5b97bbf98acd879bd472b9a32f9cd6e774 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 15 Feb 2026 14:04:35 +0900 Subject: [PATCH 43/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../certification/TaskCertificationViewModel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt index d05a3fd6..a67b5163 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt @@ -164,7 +164,6 @@ class TaskCertificationViewModel( NavRoutes.TaskCertificationRoute.From.DETAIL -> detailRefreshBus.notifyChanged(TaskCertificationRefreshBus.Publisher.PHOTOLOG) NavRoutes.TaskCertificationRoute.From.EDITOR -> Unit - } tryEmitSideEffect(TaskCertificationSideEffect.NavigateToBack) } From 06c4d97cb4c74ea1b26ef1376a693623aa9448f8 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Fri, 20 Feb 2026 14:45:44 +0900 Subject: [PATCH 44/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EB=84=A4=EB=B9=84=EA=B2=8C=EC=9D=B4=EC=85=98=20=EC=A0=84?= =?UTF-8?q?=EB=8B=AC=20=EC=9D=B8=EC=9E=90=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=EB=AA=85=20=EB=B0=8F=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/twix/navigation/NavRoutes.kt | 8 ++++---- .../EditorSerializer.kt => args/EditorNavArgs.kt} | 7 ++++--- .../main/java/com/twix/main/navigation/MainNavGraph.kt | 4 ++-- .../certification/TaskCertificationViewModel.kt | 6 +++--- .../detail/model/TaskCertificationDetailUiState.kt | 4 ++-- .../editor/TaskCertificationEditorViewModel.kt | 4 ++-- .../editor/model/TaskCertificationEditorUiState.kt | 4 ++-- .../navigation/TaskCertificationGraph.kt | 6 +++--- 8 files changed, 22 insertions(+), 21 deletions(-) rename core/navigation/src/main/java/com/twix/navigation/{serializer/EditorSerializer.kt => args/EditorNavArgs.kt} (75%) diff --git a/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt b/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt index 70de3a1c..21b5bd01 100644 --- a/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt +++ b/core/navigation/src/main/java/com/twix/navigation/NavRoutes.kt @@ -1,8 +1,8 @@ package com.twix.navigation import android.net.Uri -import com.twix.navigation.serializer.DetailSerializer -import com.twix.navigation.serializer.EditorSerializer +import com.twix.navigation.args.DetailNavArgs +import com.twix.navigation.args.EditorNavArgs import kotlinx.serialization.json.Json import java.time.LocalDate @@ -57,7 +57,7 @@ sealed class NavRoutes( EDITOR, } - fun createRoute(data: DetailSerializer): String { + fun createRoute(data: DetailNavArgs): String { val json = Json.encodeToString(data) val encoded = Uri.encode(json) return "task_certification/$encoded" @@ -68,7 +68,7 @@ sealed class NavRoutes( NavRoutes("task_certification_editor/{data}") { const val ARG_DATA = "data" - fun createRoute(data: EditorSerializer): String { + fun createRoute(data: EditorNavArgs): String { val json = Json.encodeToString(data) val encoded = Uri.encode(json) return "task_certification_editor/$encoded" diff --git a/core/navigation/src/main/java/com/twix/navigation/serializer/EditorSerializer.kt b/core/navigation/src/main/java/com/twix/navigation/args/EditorNavArgs.kt similarity index 75% rename from core/navigation/src/main/java/com/twix/navigation/serializer/EditorSerializer.kt rename to core/navigation/src/main/java/com/twix/navigation/args/EditorNavArgs.kt index e28775fe..ec21a755 100644 --- a/core/navigation/src/main/java/com/twix/navigation/serializer/EditorSerializer.kt +++ b/core/navigation/src/main/java/com/twix/navigation/args/EditorNavArgs.kt @@ -1,10 +1,11 @@ -package com.twix.navigation.serializer +package com.twix.navigation.args import com.twix.navigation.NavRoutes import kotlinx.serialization.Serializable +// TODO("인증샷 단일 조회 API 연동시 제거") @Serializable -data class EditorSerializer( +data class EditorNavArgs( val goalId: Long, val goalName: String, val nickname: String, @@ -14,7 +15,7 @@ data class EditorSerializer( ) @Serializable -data class DetailSerializer( +data class DetailNavArgs( val goalId: Long, val from: NavRoutes.TaskCertificationRoute.From, val photologId: Long = -1, diff --git a/feature/main/src/main/java/com/twix/main/navigation/MainNavGraph.kt b/feature/main/src/main/java/com/twix/main/navigation/MainNavGraph.kt index b4f3281e..92a56514 100644 --- a/feature/main/src/main/java/com/twix/main/navigation/MainNavGraph.kt +++ b/feature/main/src/main/java/com/twix/main/navigation/MainNavGraph.kt @@ -6,8 +6,8 @@ import androidx.navigation.compose.composable import androidx.navigation.navigation import com.twix.main.MainRoute import com.twix.navigation.NavRoutes +import com.twix.navigation.args.DetailNavArgs import com.twix.navigation.base.NavGraphContributor -import com.twix.navigation.serializer.DetailSerializer object MainNavGraph : NavGraphContributor { override val graphRoute: NavRoutes @@ -40,7 +40,7 @@ object MainNavGraph : NavGraphContributor { navigateToCertification = { goalId, date -> val destination = NavRoutes.TaskCertificationRoute.createRoute( - DetailSerializer( + DetailNavArgs( goalId = goalId, from = NavRoutes.TaskCertificationRoute.From.HOME, selectedDate = date.toString(), diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt index 40aff5ac..5b83af34 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt @@ -7,7 +7,7 @@ import com.twix.designsystem.components.toast.model.ToastType import com.twix.domain.model.photo.PhotologParam import com.twix.domain.repository.PhotoLogRepository import com.twix.navigation.NavRoutes -import com.twix.navigation.serializer.DetailSerializer +import com.twix.navigation.args.DetailNavArgs import com.twix.task_certification.R import com.twix.task_certification.certification.model.CaptureStatus import com.twix.task_certification.certification.model.TaskCertificationIntent @@ -33,13 +33,13 @@ class TaskCertificationViewModel( ) : BaseViewModel( TaskCertificationUiState(), ) { - private val serializer: DetailSerializer = + private val serializer: DetailNavArgs = requireNotNull( savedStateHandle .get(NavRoutes.TaskCertificationRoute.ARG_DATA) ?.let { encoded -> val json = Uri.decode(encoded) - Json.decodeFromString(json) + Json.decodeFromString(json) }, ) { SERIALIZER_NOT_FOUND } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt index ea6b8f04..3b362d71 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt @@ -5,7 +5,7 @@ import com.twix.domain.model.enums.BetweenUs import com.twix.domain.model.enums.GoalIconType import com.twix.domain.model.photolog.PhotoLogs import com.twix.domain.model.photolog.PhotologDetail -import com.twix.navigation.serializer.EditorSerializer +import com.twix.navigation.args.EditorNavArgs import com.twix.ui.base.State import com.twix.util.RelativeTimeFormatter import java.time.LocalDate @@ -75,7 +75,7 @@ data class TaskCertificationDetailUiState( currentShow == BetweenUs.PARTNER && isDisplayedGoalCertificated fun toSerializer() = - EditorSerializer( + EditorNavArgs( goalId = goalId, nickname = myNickname, goalName = goalName, diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt index f0a31ffd..eb1407b1 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt @@ -6,7 +6,7 @@ import androidx.lifecycle.viewModelScope import com.twix.designsystem.components.toast.model.ToastType import com.twix.domain.repository.PhotoLogRepository import com.twix.navigation.NavRoutes -import com.twix.navigation.serializer.EditorSerializer +import com.twix.navigation.args.EditorNavArgs import com.twix.result.AppResult import com.twix.task_certification.R import com.twix.task_certification.editor.model.TaskCertificationEditorIntent @@ -30,7 +30,7 @@ class TaskCertificationEditorViewModel( .get(NavRoutes.TaskCertificationEditorRoute.ARG_DATA) ?.let { encoded -> val json = Uri.decode(encoded) - Json.decodeFromString(json) + Json.decodeFromString(json) }, ) { SERIALIZER_NOT_FOUND } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt index e04d5e1d..ec107f2e 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt @@ -2,7 +2,7 @@ package com.twix.task_certification.editor.model import androidx.compose.runtime.Immutable import com.twix.designsystem.components.comment.model.CommentUiModel -import com.twix.navigation.serializer.EditorSerializer +import com.twix.navigation.args.EditorNavArgs import com.twix.ui.base.State @Immutable @@ -25,7 +25,7 @@ data class TaskCertificationEditorUiState( val imageName: String get() = imageUrl.split(IMAGE_NAME_SEPARATOR).last() - fun updateInitialState(serializer: EditorSerializer) = + fun updateInitialState(serializer: EditorNavArgs) = copy( goalId = serializer.goalId, nickname = serializer.nickname, diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt b/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt index 2b6c8b6d..ba37cbd8 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/navigation/TaskCertificationGraph.kt @@ -7,8 +7,8 @@ import androidx.navigation.compose.composable import androidx.navigation.navArgument import androidx.navigation.navigation import com.twix.navigation.NavRoutes +import com.twix.navigation.args.DetailNavArgs import com.twix.navigation.base.NavGraphContributor -import com.twix.navigation.serializer.DetailSerializer import com.twix.task_certification.certification.TaskCertificationRoute import com.twix.task_certification.detail.TaskCertificationDetailRoute import com.twix.task_certification.editor.TaskCertificationEditorRoute @@ -44,7 +44,7 @@ object TaskCertificationGraph : NavGraphContributor { navigateToCertification = { goalId, date -> val destination = NavRoutes.TaskCertificationRoute.createRoute( - DetailSerializer( + DetailNavArgs( goalId = goalId, from = NavRoutes.TaskCertificationRoute.From.DETAIL, selectedDate = date.toString(), @@ -77,7 +77,7 @@ object TaskCertificationGraph : NavGraphContributor { navigateToCertification = { goalId, photologId, comment -> val destination = NavRoutes.TaskCertificationRoute.createRoute( - DetailSerializer( + DetailNavArgs( goalId = goalId, from = NavRoutes.TaskCertificationRoute.From.EDITOR, photologId = photologId, From 47aa09e4a57fe64e591498bd16c2c9817b4e93fa Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Fri, 20 Feb 2026 14:49:22 +0900 Subject: [PATCH 45/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20UiState?= =?UTF-8?q?=20=EC=83=81=ED=83=9C=20=EB=B3=80=ED=99=98=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20ViewModel=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 --- .../TaskCertificationEditorViewModel.kt | 17 +++++------- .../model/TaskCertificationEditorUiState.kt | 26 ++++++++----------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt index eb1407b1..aca69321 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt @@ -12,6 +12,7 @@ import com.twix.task_certification.R import com.twix.task_certification.editor.model.TaskCertificationEditorIntent import com.twix.task_certification.editor.model.TaskCertificationEditorSideEffect import com.twix.task_certification.editor.model.TaskCertificationEditorUiState +import com.twix.task_certification.editor.model.toUiState import com.twix.ui.base.BaseViewModel import com.twix.util.bus.TaskCertificationRefreshBus import kotlinx.coroutines.launch @@ -24,7 +25,7 @@ class TaskCertificationEditorViewModel( ) : BaseViewModel( TaskCertificationEditorUiState(), ) { - private val serializer = + private val args: EditorNavArgs = requireNotNull( savedStateHandle .get(NavRoutes.TaskCertificationEditorRoute.ARG_DATA) @@ -35,11 +36,7 @@ class TaskCertificationEditorViewModel( ) { SERIALIZER_NOT_FOUND } init { - reduceInitialState() - } - - private fun reduceInitialState() { - reduce { updateInitialState(serializer) } + args.toUiState() } override suspend fun handleIntent(intent: TaskCertificationEditorIntent) { @@ -50,12 +47,12 @@ class TaskCertificationEditorViewModel( } } - private fun reduceCommentFocus(isFocused: Boolean) { - reduce { updateCommentFocus(isFocused) } + private fun reduceCommentFocus(value: Boolean) { + reduce { copy(comment = comment.copy(isFocused = value)) } } - private fun reduceComment(comment: String) { - reduce { updateComment(comment) } + private fun reduceComment(value: String) { + reduce { copy(comment = comment.copy(value = value)) } } private fun modifyComment() { diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt index ec107f2e..f06584c5 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt @@ -18,25 +18,21 @@ data class TaskCertificationEditorUiState( val isCommentNotChanged: Boolean get() = comment.value == originComment - fun updateCommentFocus(isFocus: Boolean) = copy(comment = comment.updateFocus(isFocus)) - - fun updateComment(value: String) = copy(comment = comment.updateComment(value)) - val imageName: String get() = imageUrl.split(IMAGE_NAME_SEPARATOR).last() - fun updateInitialState(serializer: EditorNavArgs) = - copy( - goalId = serializer.goalId, - nickname = serializer.nickname, - goalName = serializer.goalName, - photologId = serializer.photologId, - imageUrl = serializer.imageUrl, - comment = CommentUiModel(serializer.comment.orEmpty()), - originComment = serializer.comment.orEmpty(), - ) - companion object { private const val IMAGE_NAME_SEPARATOR = "/" } } + +internal fun EditorNavArgs.toUiState() = + TaskCertificationEditorUiState( + goalId = goalId, + nickname = nickname, + goalName = goalName, + photologId = photologId, + imageUrl = imageUrl, + comment = CommentUiModel(comment.orEmpty()), + originComment = comment.orEmpty(), + ) From 2fbe065a359e41ce45d4f1da1b474e224ad42b31 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Fri, 20 Feb 2026 16:25:20 +0900 Subject: [PATCH 46/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EB=84=A4=EB=B9=84=EA=B2=8C=EC=9D=B4=EC=85=98=20=EC=9D=B8?= =?UTF-8?q?=EC=9E=90=20=EB=94=94=EC=BD=94=EB=94=A9=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/navigation/build.gradle.kts | 1 + ...NavArgs.kt => TaskCertificationNavArgs.kt} | 0 .../twix/navigation/savedstate/SafeDecode.kt | 23 ++++++++++++++ .../TaskCertificationViewModel.kt | 30 +++++++------------ .../TaskCertificationEditorViewModel.kt | 16 +++------- 5 files changed, 39 insertions(+), 31 deletions(-) rename core/navigation/src/main/java/com/twix/navigation/args/{EditorNavArgs.kt => TaskCertificationNavArgs.kt} (100%) create mode 100644 core/navigation/src/main/java/com/twix/navigation/savedstate/SafeDecode.kt diff --git a/core/navigation/build.gradle.kts b/core/navigation/build.gradle.kts index 1690e00a..a9a48887 100644 --- a/core/navigation/build.gradle.kts +++ b/core/navigation/build.gradle.kts @@ -3,6 +3,7 @@ plugins { alias(libs.plugins.twix.android.compose) alias(libs.plugins.twix.koin) alias(libs.plugins.serialization) + alias(libs.plugins.twix.kermit) } android { diff --git a/core/navigation/src/main/java/com/twix/navigation/args/EditorNavArgs.kt b/core/navigation/src/main/java/com/twix/navigation/args/TaskCertificationNavArgs.kt similarity index 100% rename from core/navigation/src/main/java/com/twix/navigation/args/EditorNavArgs.kt rename to core/navigation/src/main/java/com/twix/navigation/args/TaskCertificationNavArgs.kt diff --git a/core/navigation/src/main/java/com/twix/navigation/savedstate/SafeDecode.kt b/core/navigation/src/main/java/com/twix/navigation/savedstate/SafeDecode.kt new file mode 100644 index 00000000..68c3b785 --- /dev/null +++ b/core/navigation/src/main/java/com/twix/navigation/savedstate/SafeDecode.kt @@ -0,0 +1,23 @@ +package com.twix.navigation.savedstate + +import android.net.Uri +import androidx.lifecycle.SavedStateHandle +import co.touchlab.kermit.Logger +import kotlinx.serialization.json.Json + +inline fun SavedStateHandle.decodeNavArgs(key: String): T { + val raw = get(key) + + try { + val decoded = + raw?.let(Uri::decode) + ?: error("Missing nav arg: $key") + + Logger.d { "NavArgs[$key] decoded=$decoded" } + + return Json.decodeFromString(decoded) + } catch (e: Exception) { + Logger.e(e) { "NavArgs[$key] decode failed raw=$raw" } + throw e + } +} diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt index 5b83af34..b4b05a1b 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt @@ -8,6 +8,7 @@ import com.twix.domain.model.photo.PhotologParam import com.twix.domain.repository.PhotoLogRepository import com.twix.navigation.NavRoutes import com.twix.navigation.args.DetailNavArgs +import com.twix.navigation.savedstate.decodeNavArgs import com.twix.task_certification.R import com.twix.task_certification.certification.model.CaptureStatus import com.twix.task_certification.certification.model.TaskCertificationIntent @@ -21,7 +22,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import kotlinx.serialization.json.Json import java.time.LocalDate class TaskCertificationViewModel( @@ -33,19 +33,12 @@ class TaskCertificationViewModel( ) : BaseViewModel( TaskCertificationUiState(), ) { - private val serializer: DetailNavArgs = - requireNotNull( - savedStateHandle - .get(NavRoutes.TaskCertificationRoute.ARG_DATA) - ?.let { encoded -> - val json = Uri.decode(encoded) - Json.decodeFromString(json) - }, - ) { SERIALIZER_NOT_FOUND } + private val navArgs: DetailNavArgs = + savedStateHandle.decodeNavArgs(NavRoutes.TaskCertificationRoute.ARG_DATA) init { - if (serializer.from == NavRoutes.TaskCertificationRoute.From.EDITOR) { - reduceComment(serializer.comment) + if (navArgs.from == NavRoutes.TaskCertificationRoute.From.EDITOR) { + reduceComment(navArgs.comment) } } @@ -135,13 +128,13 @@ class TaskCertificationViewModel( launchResult( block = { photologRepository.uploadPhotologImage( - goalId = serializer.goalId, + goalId = navArgs.goalId, bytes = image, contentType = "image/jpeg", ) }, onSuccess = { fileName -> - when (serializer.from) { + when (navArgs.from) { NavRoutes.TaskCertificationRoute.From.DETAIL, NavRoutes.TaskCertificationRoute.From.HOME, -> uploadPhotolog(fileName) @@ -160,10 +153,10 @@ class TaskCertificationViewModel( block = { photologRepository.uploadPhotolog( PhotologParam( - goalId = serializer.goalId, + goalId = navArgs.goalId, fileName = fileName, comment = currentState.comment.value, - verificationDate = LocalDate.parse(serializer.selectedDate), + verificationDate = LocalDate.parse(navArgs.selectedDate), ), ) }, @@ -175,7 +168,7 @@ class TaskCertificationViewModel( } private fun handleUploadPhotologSuccess() { - when (serializer.from) { + when (navArgs.from) { NavRoutes.TaskCertificationRoute.From.HOME -> goalRefreshBus.notifyGoalListChanged() NavRoutes.TaskCertificationRoute.From.DETAIL -> @@ -189,7 +182,7 @@ class TaskCertificationViewModel( launchResult( block = { photologRepository.modifyPhotolog( - photologId = serializer.photologId, + photologId = navArgs.photologId, fileName = fileName, comment = currentState.comment.value, ) @@ -216,6 +209,5 @@ class TaskCertificationViewModel( companion object { private const val ERROR_DISPLAY_DURATION_MS = 1500L - private const val SERIALIZER_NOT_FOUND = "Serializer Not Found" } } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt index aca69321..07cb289b 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt @@ -1,12 +1,12 @@ package com.twix.task_certification.editor -import android.net.Uri import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.twix.designsystem.components.toast.model.ToastType import com.twix.domain.repository.PhotoLogRepository import com.twix.navigation.NavRoutes import com.twix.navigation.args.EditorNavArgs +import com.twix.navigation.savedstate.decodeNavArgs import com.twix.result.AppResult import com.twix.task_certification.R import com.twix.task_certification.editor.model.TaskCertificationEditorIntent @@ -16,7 +16,6 @@ import com.twix.task_certification.editor.model.toUiState import com.twix.ui.base.BaseViewModel import com.twix.util.bus.TaskCertificationRefreshBus import kotlinx.coroutines.launch -import kotlinx.serialization.json.Json class TaskCertificationEditorViewModel( private val photologRepository: PhotoLogRepository, @@ -25,18 +24,11 @@ class TaskCertificationEditorViewModel( ) : BaseViewModel( TaskCertificationEditorUiState(), ) { - private val args: EditorNavArgs = - requireNotNull( - savedStateHandle - .get(NavRoutes.TaskCertificationEditorRoute.ARG_DATA) - ?.let { encoded -> - val json = Uri.decode(encoded) - Json.decodeFromString(json) - }, - ) { SERIALIZER_NOT_FOUND } + private val navArgs: EditorNavArgs = + savedStateHandle.decodeNavArgs(NavRoutes.TaskCertificationEditorRoute.ARG_DATA) init { - args.toUiState() + navArgs.toUiState() } override suspend fun handleIntent(intent: TaskCertificationEditorIntent) { From 657c68658f45c2d635b2c48b0655fa1ebe7ad1fc Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Fri, 20 Feb 2026 16:31:43 +0900 Subject: [PATCH 47/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EB=AA=A8=EB=93=88=20=EB=A6=AC=EC=86=8C?= =?UTF-8?q?=EC=8A=A4=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/res/drawable/ic_camara_rotate.xml | 0 .../main/res/drawable/ic_camera_retake.xml | 0 .../main/res/drawable/ic_camera_shutter.xml | 0 .../main/res/drawable/ic_camera_toggle.xml | 0 .../main/res/drawable/ic_camera_torch_off.xml | 0 .../main/res/drawable/ic_camera_torch_on.xml | 0 .../src/main/res/drawable/ic_gallery.xml | 0 .../src/main/res/values/strings.xml | 22 +++++++++++++++++ .../certification/TaskCertificationScreen.kt | 2 +- .../TaskCertificationViewModel.kt | 2 +- .../component/CameraControlBar.kt | 2 +- .../component/CameraPreviewBox.kt | 2 +- .../component/CommentErrorText.kt | 2 +- .../TaskCertificationDetailViewModel.kt | 2 +- .../component/TaskCertificationCardContent.kt | 2 +- .../editor/TaskCertificationEditorRoute.kt | 5 ++-- .../TaskCertificationEditorViewModel.kt | 2 +- .../src/main/res/values/strings.xml | 24 ------------------- 18 files changed, 32 insertions(+), 35 deletions(-) rename {feature/task-certification => core/design-system}/src/main/res/drawable/ic_camara_rotate.xml (100%) rename {feature/task-certification => core/design-system}/src/main/res/drawable/ic_camera_retake.xml (100%) rename {feature/task-certification => core/design-system}/src/main/res/drawable/ic_camera_shutter.xml (100%) rename {feature/task-certification => core/design-system}/src/main/res/drawable/ic_camera_toggle.xml (100%) rename {feature/task-certification => core/design-system}/src/main/res/drawable/ic_camera_torch_off.xml (100%) rename {feature/task-certification => core/design-system}/src/main/res/drawable/ic_camera_torch_on.xml (100%) rename {feature/task-certification => core/design-system}/src/main/res/drawable/ic_gallery.xml (100%) delete mode 100644 feature/task-certification/src/main/res/values/strings.xml diff --git a/feature/task-certification/src/main/res/drawable/ic_camara_rotate.xml b/core/design-system/src/main/res/drawable/ic_camara_rotate.xml similarity index 100% rename from feature/task-certification/src/main/res/drawable/ic_camara_rotate.xml rename to core/design-system/src/main/res/drawable/ic_camara_rotate.xml diff --git a/feature/task-certification/src/main/res/drawable/ic_camera_retake.xml b/core/design-system/src/main/res/drawable/ic_camera_retake.xml similarity index 100% rename from feature/task-certification/src/main/res/drawable/ic_camera_retake.xml rename to core/design-system/src/main/res/drawable/ic_camera_retake.xml diff --git a/feature/task-certification/src/main/res/drawable/ic_camera_shutter.xml b/core/design-system/src/main/res/drawable/ic_camera_shutter.xml similarity index 100% rename from feature/task-certification/src/main/res/drawable/ic_camera_shutter.xml rename to core/design-system/src/main/res/drawable/ic_camera_shutter.xml diff --git a/feature/task-certification/src/main/res/drawable/ic_camera_toggle.xml b/core/design-system/src/main/res/drawable/ic_camera_toggle.xml similarity index 100% rename from feature/task-certification/src/main/res/drawable/ic_camera_toggle.xml rename to core/design-system/src/main/res/drawable/ic_camera_toggle.xml diff --git a/feature/task-certification/src/main/res/drawable/ic_camera_torch_off.xml b/core/design-system/src/main/res/drawable/ic_camera_torch_off.xml similarity index 100% rename from feature/task-certification/src/main/res/drawable/ic_camera_torch_off.xml rename to core/design-system/src/main/res/drawable/ic_camera_torch_off.xml diff --git a/feature/task-certification/src/main/res/drawable/ic_camera_torch_on.xml b/core/design-system/src/main/res/drawable/ic_camera_torch_on.xml similarity index 100% rename from feature/task-certification/src/main/res/drawable/ic_camera_torch_on.xml rename to core/design-system/src/main/res/drawable/ic_camera_torch_on.xml diff --git a/feature/task-certification/src/main/res/drawable/ic_gallery.xml b/core/design-system/src/main/res/drawable/ic_gallery.xml similarity index 100% rename from feature/task-certification/src/main/res/drawable/ic_gallery.xml rename to core/design-system/src/main/res/drawable/ic_gallery.xml diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index d4a81c10..953d5f4f 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -98,4 +98,26 @@ 정말 탈퇴하시겠어요? 커플 연결이 끊어집니다.\n데이터는 전부 삭제되며 복구가 불가능합니다. + + 업로드 + 이미지 캡처에 실패했습니다. 다시 시도해 주세요. + 이미지를 불러오는 데 실패했습니다. 다시 시도해 주세요. + 인증샷 찍기 + 인증샷 등록에 실패했습니다. + 이미지 변환에 실패했습니다. + 코멘트는 5글자로 입력해주세요! + + + 인증샷 조회에 실패했습니다. + 인증샷 수정에 실패했어요. + + + 다시 찍기 + 코멘트가 수정 되었어요. + 코멘트가 수정되지 않았어요. + 코멘트 수정에 실패했어요. + 찌르기 + 리액션 요청에 실패했어요. + + 인증샷 촬영을 위해서 카메라 권한이 필요해요. diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt index 53542e9a..b1dc8b99 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.twix.designsystem.R import com.twix.designsystem.components.comment.CommentAnchorFrame import com.twix.designsystem.components.text.AppText import com.twix.designsystem.components.toast.ToastManager @@ -35,7 +36,6 @@ import com.twix.designsystem.components.toast.model.ToastData import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme import com.twix.domain.model.enums.AppTextStyle -import com.twix.task_certification.R import com.twix.task_certification.certification.camera.Camera import com.twix.task_certification.certification.component.CameraControlBar import com.twix.task_certification.certification.component.CameraPreviewBox diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt index b4b05a1b..a2eeb285 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt @@ -3,13 +3,13 @@ package com.twix.task_certification.certification import android.net.Uri import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope +import com.twix.designsystem.R import com.twix.designsystem.components.toast.model.ToastType import com.twix.domain.model.photo.PhotologParam import com.twix.domain.repository.PhotoLogRepository import com.twix.navigation.NavRoutes import com.twix.navigation.args.DetailNavArgs import com.twix.navigation.savedstate.decodeNavArgs -import com.twix.task_certification.R import com.twix.task_certification.certification.model.CaptureStatus import com.twix.task_certification.certification.model.TaskCertificationIntent import com.twix.task_certification.certification.model.TaskCertificationSideEffect diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CameraControlBar.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CameraControlBar.kt index d76e1e8a..c8020dc3 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CameraControlBar.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CameraControlBar.kt @@ -25,12 +25,12 @@ import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.core.net.toUri +import com.twix.designsystem.R import com.twix.designsystem.components.button.AppRoundButton import com.twix.designsystem.theme.CommonColor import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme import com.twix.domain.model.enums.AppTextStyle -import com.twix.task_certification.R import com.twix.task_certification.certification.model.CaptureStatus import com.twix.ui.extension.noRippleClickable diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CameraPreviewBox.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CameraPreviewBox.kt index 637b6dfc..ae45310d 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CameraPreviewBox.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CameraPreviewBox.kt @@ -19,9 +19,9 @@ import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage +import com.twix.designsystem.R import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme -import com.twix.task_certification.R import com.twix.task_certification.certification.model.CameraPreview import com.twix.task_certification.certification.model.CaptureStatus import com.twix.task_certification.certification.model.TorchStatus diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CommentErrorText.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CommentErrorText.kt index 685a016b..51bf8783 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CommentErrorText.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CommentErrorText.kt @@ -12,12 +12,12 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.twix.designsystem.R import com.twix.designsystem.components.text.AppText import com.twix.designsystem.theme.CommonColor import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme import com.twix.domain.model.enums.AppTextStyle -import com.twix.task_certification.R @Composable fun CommentErrorText(modifier: Modifier = Modifier) { diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt index 5eefa449..9733e9e3 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt @@ -2,12 +2,12 @@ package com.twix.task_certification.detail import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope +import com.twix.designsystem.R import com.twix.designsystem.components.toast.model.ToastType import com.twix.domain.model.enums.BetweenUs import com.twix.domain.model.enums.GoalReactionType import com.twix.domain.repository.PhotoLogRepository import com.twix.navigation.NavRoutes -import com.twix.task_certification.R import com.twix.task_certification.detail.model.TaskCertificationDetailIntent import com.twix.task_certification.detail.model.TaskCertificationDetailSideEffect import com.twix.task_certification.detail.model.TaskCertificationDetailUiState diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationCardContent.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationCardContent.kt index f0799010..2078d2e2 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationCardContent.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationCardContent.kt @@ -5,10 +5,10 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import com.twix.designsystem.R import com.twix.designsystem.components.photolog.BackgroundCard import com.twix.designsystem.components.photolog.ForegroundCard import com.twix.domain.model.enums.BetweenUs -import com.twix.task_certification.R import com.twix.task_certification.detail.model.TaskCertificationDetailUiState import com.twix.task_certification.detail.swipe.SwipeableCard diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt index b36c99d8..dba94104 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt @@ -32,6 +32,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import coil3.compose.AsyncImage import coil3.request.ImageRequest import coil3.request.crossfade +import com.twix.designsystem.R import com.twix.designsystem.components.button.AppRoundButton import com.twix.designsystem.components.comment.CommentAnchorFrame import com.twix.designsystem.components.photolog.PhotologCard @@ -42,7 +43,6 @@ import com.twix.designsystem.extension.showCameraPermissionToastWithNavigateToSe import com.twix.designsystem.theme.CommonColor import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme -import com.twix.task_certification.R import com.twix.task_certification.editor.component.TaskCertificationEditorTopBar import com.twix.task_certification.editor.model.TaskCertificationEditorIntent import com.twix.task_certification.editor.model.TaskCertificationEditorSideEffect @@ -54,7 +54,6 @@ import com.twix.ui.extension.noRippleClickable import kotlinx.coroutines.launch import org.koin.androidx.compose.koinViewModel import org.koin.compose.koinInject -import com.twix.designsystem.R as DesR @Composable fun TaskCertificationEditorRoute( @@ -102,7 +101,7 @@ fun TaskCertificationEditorRoute( toastManager.show( ToastData( currentContext.getString( - DesR.string.toast_camera_permission_request, + R.string.toast_camera_permission_request, ), ToastType.ERROR, ), diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt index 07cb289b..0b46990a 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt @@ -2,13 +2,13 @@ package com.twix.task_certification.editor import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope +import com.twix.designsystem.R import com.twix.designsystem.components.toast.model.ToastType import com.twix.domain.repository.PhotoLogRepository import com.twix.navigation.NavRoutes import com.twix.navigation.args.EditorNavArgs import com.twix.navigation.savedstate.decodeNavArgs import com.twix.result.AppResult -import com.twix.task_certification.R import com.twix.task_certification.editor.model.TaskCertificationEditorIntent import com.twix.task_certification.editor.model.TaskCertificationEditorSideEffect import com.twix.task_certification.editor.model.TaskCertificationEditorUiState diff --git a/feature/task-certification/src/main/res/values/strings.xml b/feature/task-certification/src/main/res/values/strings.xml deleted file mode 100644 index f4b900c4..00000000 --- a/feature/task-certification/src/main/res/values/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - 업로드 - 이미지 캡처에 실패했습니다. 다시 시도해 주세요. - 이미지를 불러오는 데 실패했습니다. 다시 시도해 주세요. - 인증샷 찍기 - 인증샷 등록에 실패했습니다. - 이미지 변환에 실패했습니다. - 코멘트는 5글자로 입력해주세요! - - - 인증샷 조회에 실패했습니다. - 인증샷 수정에 실패했어요. - - - 다시 찍기 - 코멘트가 수정 되었어요. - 코멘트가 수정되지 않았어요. - 코멘트 수정에 실패했어요. - 찌르기 - 리액션 요청에 실패했어요. - - 인증샷 촬영을 위해서 카메라 권한이 필요해요. - From 758d70fe08374f044e6287e6ff1d81534616991d Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Fri, 20 Feb 2026 16:34:18 +0900 Subject: [PATCH 48/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=ED=99=94=EB=A9=B4=20MVI=20Contract=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `certification`, `detail`, `editor` 각 화면의 MVI (Model-View-Intent) 관련 클래스들을 `contact` 패키지로 이동하여 구조를 통일합니다. - 패키지 구조 변경에 따라 관련 import 구문을 수정합니다. --- .../certification/TaskCertificationScreen.kt | 6 +++--- .../certification/TaskCertificationViewModel.kt | 6 +++--- .../{model => contract}/TaskCertificationIntent.kt | 2 +- .../{model => contract}/TaskCertificationSideEffect.kt | 2 +- .../{model => contract}/TaskCertificationUiState.kt | 7 +++++-- .../task_certification/detail/TaskCertificationDetail.kt | 6 +++--- .../detail/TaskCertificationDetailViewModel.kt | 8 ++++---- .../detail/component/TaskCertificationCardContent.kt | 2 +- .../{model => contract}/TaskCertificationDetailIntent.kt | 2 +- .../TaskCertificationDetailSideEffect.kt | 2 +- .../{model => contract}/TaskCertificationDetailUiState.kt | 2 +- .../preview/TaskCertificationDetailPreviewProvider.kt | 2 +- .../editor/TaskCertificationEditorRoute.kt | 6 +++--- .../editor/TaskCertificationEditorViewModel.kt | 8 ++++---- .../{model => contract}/TaskCertificationEditorIntent.kt | 2 +- .../TaskCertificationEditorSideEffect.kt | 2 +- .../{model => contract}/TaskCertificationEditorUiState.kt | 2 +- 17 files changed, 35 insertions(+), 32 deletions(-) rename feature/task-certification/src/main/java/com/twix/task_certification/certification/{model => contract}/TaskCertificationIntent.kt (94%) rename feature/task-certification/src/main/java/com/twix/task_certification/certification/{model => contract}/TaskCertificationSideEffect.kt (87%) rename feature/task-certification/src/main/java/com/twix/task_certification/certification/{model => contract}/TaskCertificationUiState.kt (84%) rename feature/task-certification/src/main/java/com/twix/task_certification/detail/{model => contract}/TaskCertificationDetailIntent.kt (87%) rename feature/task-certification/src/main/java/com/twix/task_certification/detail/{model => contract}/TaskCertificationDetailSideEffect.kt (84%) rename feature/task-certification/src/main/java/com/twix/task_certification/detail/{model => contract}/TaskCertificationDetailUiState.kt (98%) rename feature/task-certification/src/main/java/com/twix/task_certification/editor/{model => contract}/TaskCertificationEditorIntent.kt (87%) rename feature/task-certification/src/main/java/com/twix/task_certification/editor/{model => contract}/TaskCertificationEditorSideEffect.kt (84%) rename feature/task-certification/src/main/java/com/twix/task_certification/editor/{model => contract}/TaskCertificationEditorUiState.kt (95%) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt index b1dc8b99..871d2ba5 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt @@ -41,10 +41,10 @@ import com.twix.task_certification.certification.component.CameraControlBar import com.twix.task_certification.certification.component.CameraPreviewBox import com.twix.task_certification.certification.component.CommentErrorText import com.twix.task_certification.certification.component.TaskCertificationTopBar +import com.twix.task_certification.certification.contract.TaskCertificationIntent +import com.twix.task_certification.certification.contract.TaskCertificationSideEffect +import com.twix.task_certification.certification.contract.TaskCertificationUiState import com.twix.task_certification.certification.model.CameraPreview -import com.twix.task_certification.certification.model.TaskCertificationIntent -import com.twix.task_certification.certification.model.TaskCertificationSideEffect -import com.twix.task_certification.certification.model.TaskCertificationUiState import com.twix.ui.base.ObserveAsEvents import com.twix.ui.extension.noRippleClickable import kotlinx.coroutines.launch diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt index a2eeb285..86c033bf 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationViewModel.kt @@ -10,10 +10,10 @@ import com.twix.domain.repository.PhotoLogRepository import com.twix.navigation.NavRoutes import com.twix.navigation.args.DetailNavArgs import com.twix.navigation.savedstate.decodeNavArgs +import com.twix.task_certification.certification.contract.TaskCertificationIntent +import com.twix.task_certification.certification.contract.TaskCertificationSideEffect +import com.twix.task_certification.certification.contract.TaskCertificationUiState import com.twix.task_certification.certification.model.CaptureStatus -import com.twix.task_certification.certification.model.TaskCertificationIntent -import com.twix.task_certification.certification.model.TaskCertificationSideEffect -import com.twix.task_certification.certification.model.TaskCertificationUiState import com.twix.ui.base.BaseViewModel import com.twix.ui.image.ImageGenerator import com.twix.util.bus.GoalRefreshBus diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationIntent.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/contract/TaskCertificationIntent.kt similarity index 94% rename from feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationIntent.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/certification/contract/TaskCertificationIntent.kt index 23049957..cbf40808 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationIntent.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/contract/TaskCertificationIntent.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.certification.model +package com.twix.task_certification.certification.contract import android.net.Uri import com.twix.ui.base.Intent diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationSideEffect.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/contract/TaskCertificationSideEffect.kt similarity index 87% rename from feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationSideEffect.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/certification/contract/TaskCertificationSideEffect.kt index a782db3c..1d2b631f 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationSideEffect.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/contract/TaskCertificationSideEffect.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.certification.model +package com.twix.task_certification.certification.contract import com.twix.designsystem.components.toast.model.ToastType import com.twix.ui.base.SideEffect diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/contract/TaskCertificationUiState.kt similarity index 84% rename from feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationUiState.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/certification/contract/TaskCertificationUiState.kt index dceabb2f..1287bd48 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/TaskCertificationUiState.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/contract/TaskCertificationUiState.kt @@ -1,9 +1,12 @@ -package com.twix.task_certification.certification.model +package com.twix.task_certification.certification.contract import android.net.Uri import androidx.camera.core.CameraSelector import androidx.compose.runtime.Immutable import com.twix.designsystem.components.comment.model.CommentUiModel +import com.twix.task_certification.certification.model.CameraPreview +import com.twix.task_certification.certification.model.CaptureStatus +import com.twix.task_certification.certification.model.TorchStatus import com.twix.ui.base.State @Immutable @@ -35,7 +38,7 @@ data class TaskCertificationUiState( } fun toggleTorch(): TaskCertificationUiState { - val newFlashMode = TorchStatus.toggle(torch) + val newFlashMode = TorchStatus.Companion.toggle(torch) return copy(torch = newFlashMode) } diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt index b824e598..8a4f13f0 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt @@ -31,9 +31,9 @@ import com.twix.domain.model.enums.GoalReactionType import com.twix.task_certification.detail.component.ReactionContent import com.twix.task_certification.detail.component.TaskCertificationCardContent import com.twix.task_certification.detail.component.TaskCertificationDetailTopBar -import com.twix.task_certification.detail.model.TaskCertificationDetailIntent -import com.twix.task_certification.detail.model.TaskCertificationDetailSideEffect -import com.twix.task_certification.detail.model.TaskCertificationDetailUiState +import com.twix.task_certification.detail.contract.TaskCertificationDetailIntent +import com.twix.task_certification.detail.contract.TaskCertificationDetailSideEffect +import com.twix.task_certification.detail.contract.TaskCertificationDetailUiState import com.twix.task_certification.detail.preview.TaskCertificationDetailPreviewProvider import com.twix.ui.base.ObserveAsEvents import com.twix.ui.extension.findActivity diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt index 9733e9e3..e3cb4d09 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetailViewModel.kt @@ -8,10 +8,10 @@ import com.twix.domain.model.enums.BetweenUs import com.twix.domain.model.enums.GoalReactionType import com.twix.domain.repository.PhotoLogRepository import com.twix.navigation.NavRoutes -import com.twix.task_certification.detail.model.TaskCertificationDetailIntent -import com.twix.task_certification.detail.model.TaskCertificationDetailSideEffect -import com.twix.task_certification.detail.model.TaskCertificationDetailUiState -import com.twix.task_certification.detail.model.toUiState +import com.twix.task_certification.detail.contract.TaskCertificationDetailIntent +import com.twix.task_certification.detail.contract.TaskCertificationDetailSideEffect +import com.twix.task_certification.detail.contract.TaskCertificationDetailUiState +import com.twix.task_certification.detail.contract.toUiState import com.twix.ui.base.BaseViewModel import com.twix.util.bus.GoalRefreshBus import com.twix.util.bus.TaskCertificationRefreshBus diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationCardContent.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationCardContent.kt index 2078d2e2..1dedd470 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationCardContent.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationCardContent.kt @@ -9,7 +9,7 @@ import com.twix.designsystem.R import com.twix.designsystem.components.photolog.BackgroundCard import com.twix.designsystem.components.photolog.ForegroundCard import com.twix.domain.model.enums.BetweenUs -import com.twix.task_certification.detail.model.TaskCertificationDetailUiState +import com.twix.task_certification.detail.contract.TaskCertificationDetailUiState import com.twix.task_certification.detail.swipe.SwipeableCard @Composable diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailIntent.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/contract/TaskCertificationDetailIntent.kt similarity index 87% rename from feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailIntent.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/detail/contract/TaskCertificationDetailIntent.kt index f87884b3..650100dd 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailIntent.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/contract/TaskCertificationDetailIntent.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.detail.model +package com.twix.task_certification.detail.contract import com.twix.domain.model.enums.GoalReactionType import com.twix.ui.base.Intent diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailSideEffect.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/contract/TaskCertificationDetailSideEffect.kt similarity index 84% rename from feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailSideEffect.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/detail/contract/TaskCertificationDetailSideEffect.kt index b603e2f4..202348ef 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailSideEffect.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/contract/TaskCertificationDetailSideEffect.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.detail.model +package com.twix.task_certification.detail.contract import com.twix.designsystem.components.toast.model.ToastType import com.twix.ui.base.SideEffect diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/contract/TaskCertificationDetailUiState.kt similarity index 98% rename from feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/detail/contract/TaskCertificationDetailUiState.kt index 3b362d71..8dd2d896 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/model/TaskCertificationDetailUiState.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/contract/TaskCertificationDetailUiState.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.detail.model +package com.twix.task_certification.detail.contract import androidx.compose.runtime.Immutable import com.twix.domain.model.enums.BetweenUs diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/preview/TaskCertificationDetailPreviewProvider.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/preview/TaskCertificationDetailPreviewProvider.kt index a678d2cf..03eebdbf 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/preview/TaskCertificationDetailPreviewProvider.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/preview/TaskCertificationDetailPreviewProvider.kt @@ -5,7 +5,7 @@ import com.twix.domain.model.enums.BetweenUs import com.twix.domain.model.enums.GoalIconType import com.twix.domain.model.enums.GoalReactionType import com.twix.domain.model.photolog.PhotologDetail -import com.twix.task_certification.detail.model.TaskCertificationDetailUiState +import com.twix.task_certification.detail.contract.TaskCertificationDetailUiState class TaskCertificationDetailPreviewProvider : PreviewParameterProvider { override val values = diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt index dba94104..6990e8db 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt @@ -44,9 +44,9 @@ import com.twix.designsystem.theme.CommonColor import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme import com.twix.task_certification.editor.component.TaskCertificationEditorTopBar -import com.twix.task_certification.editor.model.TaskCertificationEditorIntent -import com.twix.task_certification.editor.model.TaskCertificationEditorSideEffect -import com.twix.task_certification.editor.model.TaskCertificationEditorUiState +import com.twix.task_certification.editor.contract.TaskCertificationEditorIntent +import com.twix.task_certification.editor.contract.TaskCertificationEditorSideEffect +import com.twix.task_certification.editor.contract.TaskCertificationEditorUiState import com.twix.ui.base.ObserveAsEvents import com.twix.ui.extension.findActivity import com.twix.ui.extension.hasCameraPermission diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt index 0b46990a..447a5aad 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt @@ -9,10 +9,10 @@ import com.twix.navigation.NavRoutes import com.twix.navigation.args.EditorNavArgs import com.twix.navigation.savedstate.decodeNavArgs import com.twix.result.AppResult -import com.twix.task_certification.editor.model.TaskCertificationEditorIntent -import com.twix.task_certification.editor.model.TaskCertificationEditorSideEffect -import com.twix.task_certification.editor.model.TaskCertificationEditorUiState -import com.twix.task_certification.editor.model.toUiState +import com.twix.task_certification.editor.contract.TaskCertificationEditorIntent +import com.twix.task_certification.editor.contract.TaskCertificationEditorSideEffect +import com.twix.task_certification.editor.contract.TaskCertificationEditorUiState +import com.twix.task_certification.editor.contract.toUiState import com.twix.ui.base.BaseViewModel import com.twix.util.bus.TaskCertificationRefreshBus import kotlinx.coroutines.launch diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorIntent.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/contract/TaskCertificationEditorIntent.kt similarity index 87% rename from feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorIntent.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/editor/contract/TaskCertificationEditorIntent.kt index d4c9000b..42bf9693 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorIntent.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/contract/TaskCertificationEditorIntent.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.editor.model +package com.twix.task_certification.editor.contract import com.twix.ui.base.Intent diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorSideEffect.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/contract/TaskCertificationEditorSideEffect.kt similarity index 84% rename from feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorSideEffect.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/editor/contract/TaskCertificationEditorSideEffect.kt index 441b2a95..f46c5660 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorSideEffect.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/contract/TaskCertificationEditorSideEffect.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.editor.model +package com.twix.task_certification.editor.contract import com.twix.designsystem.components.toast.model.ToastType import com.twix.ui.base.SideEffect diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/contract/TaskCertificationEditorUiState.kt similarity index 95% rename from feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/editor/contract/TaskCertificationEditorUiState.kt index f06584c5..13a217ea 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/model/TaskCertificationEditorUiState.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/contract/TaskCertificationEditorUiState.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.editor.model +package com.twix.task_certification.editor.contract import androidx.compose.runtime.Immutable import com.twix.designsystem.components.comment.model.CommentUiModel From 6a31eec5b83499b322fff9b08c74a9e54979c909 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Fri, 20 Feb 2026 16:50:56 +0900 Subject: [PATCH 49/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=B0=8F=20=EC=BB=B4=ED=8F=AC=EC=A0=80=EB=B8=94=20=EC=B6=94?= =?UTF-8?q?=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../certification/TaskCertificationScreen.kt | 4 +- .../component/CameraPreviewBox.kt | 2 +- .../contract/TaskCertificationUiState.kt | 2 +- .../{ => model}/camera/Camera.kt | 3 +- .../model/{ => camera}/CameraPreview.kt | 2 +- .../{ => model}/camera/CaptureCamera.kt | 3 +- .../detail/TaskCertificationDetail.kt | 2 +- .../component/TaskCertificationCardContent.kt | 2 +- .../{ => component}/reaction/ReactionBar.kt | 2 +- .../{ => reaction}/ReactionContent.kt | 5 +-- .../reaction/ReactionEffect.kt | 2 +- .../reaction/ReactionEffectSpec.kt | 2 +- .../reaction/ReactionParticle.kt | 2 +- .../reaction/ReactionUiModel.kt | 2 +- .../{ => component}/swipe/SwipeCardSpec.kt | 2 +- .../{ => component}/swipe/SwipeableCard.kt | 2 +- .../di/TaskCertificationModule.kt | 4 +- .../editor/TaskCertificationEditorRoute.kt | 13 +----- .../editor/component/RetakeButton.kt | 42 +++++++++++++++++++ 19 files changed, 63 insertions(+), 35 deletions(-) rename feature/task-certification/src/main/java/com/twix/task_certification/certification/{ => model}/camera/Camera.kt (79%) rename feature/task-certification/src/main/java/com/twix/task_certification/certification/model/{ => camera}/CameraPreview.kt (71%) rename feature/task-certification/src/main/java/com/twix/task_certification/certification/{ => model}/camera/CaptureCamera.kt (97%) rename feature/task-certification/src/main/java/com/twix/task_certification/detail/{ => component}/reaction/ReactionBar.kt (98%) rename feature/task-certification/src/main/java/com/twix/task_certification/detail/component/{ => reaction}/ReactionContent.kt (86%) rename feature/task-certification/src/main/java/com/twix/task_certification/detail/{ => component}/reaction/ReactionEffect.kt (99%) rename feature/task-certification/src/main/java/com/twix/task_certification/detail/{ => component}/reaction/ReactionEffectSpec.kt (91%) rename feature/task-certification/src/main/java/com/twix/task_certification/detail/{ => component}/reaction/ReactionParticle.kt (87%) rename feature/task-certification/src/main/java/com/twix/task_certification/detail/{ => component}/reaction/ReactionUiModel.kt (92%) rename feature/task-certification/src/main/java/com/twix/task_certification/detail/{ => component}/swipe/SwipeCardSpec.kt (92%) rename feature/task-certification/src/main/java/com/twix/task_certification/detail/{ => component}/swipe/SwipeableCard.kt (99%) create mode 100644 feature/task-certification/src/main/java/com/twix/task_certification/editor/component/RetakeButton.kt diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt index 871d2ba5..f68b37f1 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt @@ -36,7 +36,7 @@ import com.twix.designsystem.components.toast.model.ToastData import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme import com.twix.domain.model.enums.AppTextStyle -import com.twix.task_certification.certification.camera.Camera +import com.twix.task_certification.certification.model.camera.Camera import com.twix.task_certification.certification.component.CameraControlBar import com.twix.task_certification.certification.component.CameraPreviewBox import com.twix.task_certification.certification.component.CommentErrorText @@ -44,7 +44,7 @@ import com.twix.task_certification.certification.component.TaskCertificationTopB import com.twix.task_certification.certification.contract.TaskCertificationIntent import com.twix.task_certification.certification.contract.TaskCertificationSideEffect import com.twix.task_certification.certification.contract.TaskCertificationUiState -import com.twix.task_certification.certification.model.CameraPreview +import com.twix.task_certification.certification.model.camera.CameraPreview import com.twix.ui.base.ObserveAsEvents import com.twix.ui.extension.noRippleClickable import kotlinx.coroutines.launch diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CameraPreviewBox.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CameraPreviewBox.kt index ae45310d..c7f88fd5 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CameraPreviewBox.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CameraPreviewBox.kt @@ -22,7 +22,7 @@ import coil3.compose.AsyncImage import com.twix.designsystem.R import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme -import com.twix.task_certification.certification.model.CameraPreview +import com.twix.task_certification.certification.model.camera.CameraPreview import com.twix.task_certification.certification.model.CaptureStatus import com.twix.task_certification.certification.model.TorchStatus import com.twix.ui.extension.noRippleClickable diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/contract/TaskCertificationUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/contract/TaskCertificationUiState.kt index 1287bd48..86e814b0 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/contract/TaskCertificationUiState.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/contract/TaskCertificationUiState.kt @@ -4,7 +4,7 @@ import android.net.Uri import androidx.camera.core.CameraSelector import androidx.compose.runtime.Immutable import com.twix.designsystem.components.comment.model.CommentUiModel -import com.twix.task_certification.certification.model.CameraPreview +import com.twix.task_certification.certification.model.camera.CameraPreview import com.twix.task_certification.certification.model.CaptureStatus import com.twix.task_certification.certification.model.TorchStatus import com.twix.ui.base.State diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/camera/Camera.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/camera/Camera.kt similarity index 79% rename from feature/task-certification/src/main/java/com/twix/task_certification/certification/camera/Camera.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/certification/model/camera/Camera.kt index 51efc24a..bfd49e10 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/camera/Camera.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/camera/Camera.kt @@ -1,9 +1,8 @@ -package com.twix.task_certification.certification.camera +package com.twix.task_certification.certification.model.camera import android.net.Uri import androidx.camera.core.CameraSelector import androidx.lifecycle.LifecycleOwner -import com.twix.task_certification.certification.model.CameraPreview import com.twix.task_certification.certification.model.TorchStatus import kotlinx.coroutines.flow.StateFlow diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/CameraPreview.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/camera/CameraPreview.kt similarity index 71% rename from feature/task-certification/src/main/java/com/twix/task_certification/certification/model/CameraPreview.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/certification/model/camera/CameraPreview.kt index 17bea7a3..eca40459 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/CameraPreview.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/camera/CameraPreview.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.certification.model +package com.twix.task_certification.certification.model.camera import androidx.camera.core.SurfaceRequest import androidx.compose.runtime.Immutable diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/camera/CaptureCamera.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/camera/CaptureCamera.kt similarity index 97% rename from feature/task-certification/src/main/java/com/twix/task_certification/certification/camera/CaptureCamera.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/certification/model/camera/CaptureCamera.kt index 058f4bde..56f2a710 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/camera/CaptureCamera.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/model/camera/CaptureCamera.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.certification.camera +package com.twix.task_certification.certification.model.camera import android.content.ContentValues import android.content.Context @@ -15,7 +15,6 @@ import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.lifecycle.awaitInstance import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner -import com.twix.task_certification.certification.model.CameraPreview import com.twix.task_certification.certification.model.TorchStatus import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt index 8a4f13f0..81f9b3ce 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt @@ -28,7 +28,7 @@ import com.twix.designsystem.extension.showCameraPermissionToastWithNavigateToSe import com.twix.designsystem.theme.CommonColor import com.twix.designsystem.theme.TwixTheme import com.twix.domain.model.enums.GoalReactionType -import com.twix.task_certification.detail.component.ReactionContent +import com.twix.task_certification.detail.component.reaction.ReactionContent import com.twix.task_certification.detail.component.TaskCertificationCardContent import com.twix.task_certification.detail.component.TaskCertificationDetailTopBar import com.twix.task_certification.detail.contract.TaskCertificationDetailIntent diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationCardContent.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationCardContent.kt index 1dedd470..acd45632 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationCardContent.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationCardContent.kt @@ -10,7 +10,7 @@ import com.twix.designsystem.components.photolog.BackgroundCard import com.twix.designsystem.components.photolog.ForegroundCard import com.twix.domain.model.enums.BetweenUs import com.twix.task_certification.detail.contract.TaskCertificationDetailUiState -import com.twix.task_certification.detail.swipe.SwipeableCard +import com.twix.task_certification.detail.component.swipe.SwipeableCard @Composable internal fun TaskCertificationCardContent( diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/reaction/ReactionBar.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionBar.kt similarity index 98% rename from feature/task-certification/src/main/java/com/twix/task_certification/detail/reaction/ReactionBar.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionBar.kt index 58d068a8..cfdcf082 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/reaction/ReactionBar.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionBar.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.detail.reaction +package com.twix.task_certification.detail.component.reaction import androidx.compose.foundation.Image import androidx.compose.foundation.background diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/ReactionContent.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionContent.kt similarity index 86% rename from feature/task-certification/src/main/java/com/twix/task_certification/detail/component/ReactionContent.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionContent.kt index 1081c5a1..75da1cad 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/ReactionContent.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionContent.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.detail.component +package com.twix.task_certification.detail.component.reaction import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize @@ -13,9 +13,6 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.twix.designsystem.theme.TwixTheme import com.twix.domain.model.enums.GoalReactionType -import com.twix.task_certification.detail.reaction.ReactionBar -import com.twix.task_certification.detail.reaction.ReactionEffect -import com.twix.task_certification.detail.reaction.ReactionUiModel @Composable internal fun ReactionContent( diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/reaction/ReactionEffect.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionEffect.kt similarity index 99% rename from feature/task-certification/src/main/java/com/twix/task_certification/detail/reaction/ReactionEffect.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionEffect.kt index de5bbba7..696eb339 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/reaction/ReactionEffect.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionEffect.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.detail.reaction +package com.twix.task_certification.detail.component.reaction import androidx.compose.animation.core.FastOutLinearInEasing import androidx.compose.animation.core.LinearOutSlowInEasing diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/reaction/ReactionEffectSpec.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionEffectSpec.kt similarity index 91% rename from feature/task-certification/src/main/java/com/twix/task_certification/detail/reaction/ReactionEffectSpec.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionEffectSpec.kt index 3f1db381..455b1fdf 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/reaction/ReactionEffectSpec.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionEffectSpec.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.detail.reaction +package com.twix.task_certification.detail.component.reaction /** * ReactionEffect 애니메이션 설정 값 묶음. diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/reaction/ReactionParticle.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionParticle.kt similarity index 87% rename from feature/task-certification/src/main/java/com/twix/task_certification/detail/reaction/ReactionParticle.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionParticle.kt index 3b4c3dc2..43b72f30 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/reaction/ReactionParticle.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionParticle.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.detail.reaction +package com.twix.task_certification.detail.component.reaction import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.AnimationVector1D diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/reaction/ReactionUiModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionUiModel.kt similarity index 92% rename from feature/task-certification/src/main/java/com/twix/task_certification/detail/reaction/ReactionUiModel.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionUiModel.kt index fd0dc409..6f68fd1a 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/reaction/ReactionUiModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/reaction/ReactionUiModel.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.detail.reaction +package com.twix.task_certification.detail.component.reaction import com.twix.designsystem.R import com.twix.domain.model.enums.GoalReactionType diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/swipe/SwipeCardSpec.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/swipe/SwipeCardSpec.kt similarity index 92% rename from feature/task-certification/src/main/java/com/twix/task_certification/detail/swipe/SwipeCardSpec.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/detail/component/swipe/SwipeCardSpec.kt index 7ef4a845..54a445bd 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/swipe/SwipeCardSpec.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/swipe/SwipeCardSpec.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.detail.swipe +package com.twix.task_certification.detail.component.swipe import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/swipe/SwipeableCard.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/swipe/SwipeableCard.kt similarity index 99% rename from feature/task-certification/src/main/java/com/twix/task_certification/detail/swipe/SwipeableCard.kt rename to feature/task-certification/src/main/java/com/twix/task_certification/detail/component/swipe/SwipeableCard.kt index 7e42fefe..9c86d778 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/swipe/SwipeableCard.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/swipe/SwipeableCard.kt @@ -1,4 +1,4 @@ -package com.twix.task_certification.detail.swipe +package com.twix.task_certification.detail.component.swipe import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.spring diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/di/TaskCertificationModule.kt b/feature/task-certification/src/main/java/com/twix/task_certification/di/TaskCertificationModule.kt index 367dba25..fddd2a5a 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/di/TaskCertificationModule.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/di/TaskCertificationModule.kt @@ -3,8 +3,8 @@ package com.twix.task_certification.di import com.twix.navigation.NavRoutes import com.twix.navigation.base.NavGraphContributor import com.twix.task_certification.certification.TaskCertificationViewModel -import com.twix.task_certification.certification.camera.Camera -import com.twix.task_certification.certification.camera.CaptureCamera +import com.twix.task_certification.certification.model.camera.Camera +import com.twix.task_certification.certification.model.camera.CaptureCamera import com.twix.task_certification.detail.TaskCertificationDetailViewModel import com.twix.task_certification.editor.TaskCertificationEditorViewModel import com.twix.task_certification.navigation.TaskCertificationGraph diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt index 6990e8db..f775e4f0 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt @@ -43,6 +43,7 @@ import com.twix.designsystem.extension.showCameraPermissionToastWithNavigateToSe import com.twix.designsystem.theme.CommonColor import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme +import com.twix.task_certification.editor.component.RetakeButton import com.twix.task_certification.editor.component.TaskCertificationEditorTopBar import com.twix.task_certification.editor.contract.TaskCertificationEditorIntent import com.twix.task_certification.editor.contract.TaskCertificationEditorSideEffect @@ -178,17 +179,7 @@ fun TaskCertificationEditorScreen( Spacer(Modifier.height(101.dp)) - AppRoundButton( - text = stringResource(R.string.task_certification_editor_retake), - textColor = GrayColor.C500, - backgroundColor = CommonColor.White, - modifier = - Modifier - .fillMaxWidth() - .height(68.dp) - .padding(horizontal = 30.dp) - .noRippleClickable { onClickRetake() }, - ) + RetakeButton(onClickRetake = onClickRetake) } CommentAnchorFrame( diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/component/RetakeButton.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/component/RetakeButton.kt new file mode 100644 index 00000000..62d2ba9c --- /dev/null +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/component/RetakeButton.kt @@ -0,0 +1,42 @@ +package com.twix.task_certification.editor.component + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.twix.designsystem.R +import com.twix.designsystem.components.button.AppRoundButton +import com.twix.designsystem.theme.CommonColor +import com.twix.designsystem.theme.GrayColor +import com.twix.designsystem.theme.TwixTheme +import com.twix.ui.extension.noRippleClickable + +@Composable +internal fun RetakeButton( + onClickRetake: () -> Unit, + modifier: Modifier = Modifier +) { + AppRoundButton( + text = stringResource(R.string.task_certification_editor_retake), + textColor = GrayColor.C500, + backgroundColor = CommonColor.White, + modifier = + modifier + .fillMaxWidth() + .height(68.dp) + .padding(horizontal = 30.dp) + .noRippleClickable { onClickRetake() }, + ) +} + +@Preview +@Composable +private fun RetakeButtonPreview() { + TwixTheme { + RetakeButton(onClickRetake = {}) + } +} From 5f1921e6fb51d30962f4c607a283ddd918ec069f Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Fri, 20 Feb 2026 16:54:51 +0900 Subject: [PATCH 50/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EB=AF=B8=EC=82=AC=EC=9A=A9=20`as`=20import=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../certification/TaskCertificationScreen.kt | 2 +- .../certification/component/CameraPreviewBox.kt | 2 +- .../certification/contract/TaskCertificationUiState.kt | 2 +- .../task_certification/detail/TaskCertificationDetail.kt | 6 +++--- .../detail/component/TaskCertificationCardContent.kt | 2 +- .../detail/component/TaskCertificationDetailTopBar.kt | 6 +++--- .../editor/TaskCertificationEditorRoute.kt | 5 ----- .../task_certification/editor/component/RetakeButton.kt | 2 +- .../editor/component/TaskCertificationEditorTopBar.kt | 6 +++--- 9 files changed, 14 insertions(+), 19 deletions(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt index f68b37f1..7f4bbfaa 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/TaskCertificationScreen.kt @@ -36,7 +36,6 @@ import com.twix.designsystem.components.toast.model.ToastData import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme import com.twix.domain.model.enums.AppTextStyle -import com.twix.task_certification.certification.model.camera.Camera import com.twix.task_certification.certification.component.CameraControlBar import com.twix.task_certification.certification.component.CameraPreviewBox import com.twix.task_certification.certification.component.CommentErrorText @@ -44,6 +43,7 @@ import com.twix.task_certification.certification.component.TaskCertificationTopB import com.twix.task_certification.certification.contract.TaskCertificationIntent import com.twix.task_certification.certification.contract.TaskCertificationSideEffect import com.twix.task_certification.certification.contract.TaskCertificationUiState +import com.twix.task_certification.certification.model.camera.Camera import com.twix.task_certification.certification.model.camera.CameraPreview import com.twix.ui.base.ObserveAsEvents import com.twix.ui.extension.noRippleClickable diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CameraPreviewBox.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CameraPreviewBox.kt index c7f88fd5..d97d5d57 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CameraPreviewBox.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/component/CameraPreviewBox.kt @@ -22,9 +22,9 @@ import coil3.compose.AsyncImage import com.twix.designsystem.R import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme -import com.twix.task_certification.certification.model.camera.CameraPreview import com.twix.task_certification.certification.model.CaptureStatus import com.twix.task_certification.certification.model.TorchStatus +import com.twix.task_certification.certification.model.camera.CameraPreview import com.twix.ui.extension.noRippleClickable @Composable diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/certification/contract/TaskCertificationUiState.kt b/feature/task-certification/src/main/java/com/twix/task_certification/certification/contract/TaskCertificationUiState.kt index 86e814b0..2e523cbd 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/certification/contract/TaskCertificationUiState.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/certification/contract/TaskCertificationUiState.kt @@ -4,9 +4,9 @@ import android.net.Uri import androidx.camera.core.CameraSelector import androidx.compose.runtime.Immutable import com.twix.designsystem.components.comment.model.CommentUiModel -import com.twix.task_certification.certification.model.camera.CameraPreview import com.twix.task_certification.certification.model.CaptureStatus import com.twix.task_certification.certification.model.TorchStatus +import com.twix.task_certification.certification.model.camera.CameraPreview import com.twix.ui.base.State @Immutable diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt index 81f9b3ce..268d8ccf 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/TaskCertificationDetail.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.core.app.ActivityCompat import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.twix.designsystem.R import com.twix.designsystem.components.toast.ToastManager import com.twix.designsystem.components.toast.model.ToastData import com.twix.designsystem.components.toast.model.ToastType @@ -28,9 +29,9 @@ import com.twix.designsystem.extension.showCameraPermissionToastWithNavigateToSe import com.twix.designsystem.theme.CommonColor import com.twix.designsystem.theme.TwixTheme import com.twix.domain.model.enums.GoalReactionType -import com.twix.task_certification.detail.component.reaction.ReactionContent import com.twix.task_certification.detail.component.TaskCertificationCardContent import com.twix.task_certification.detail.component.TaskCertificationDetailTopBar +import com.twix.task_certification.detail.component.reaction.ReactionContent import com.twix.task_certification.detail.contract.TaskCertificationDetailIntent import com.twix.task_certification.detail.contract.TaskCertificationDetailSideEffect import com.twix.task_certification.detail.contract.TaskCertificationDetailUiState @@ -42,7 +43,6 @@ import kotlinx.coroutines.launch import org.koin.androidx.compose.koinViewModel import org.koin.compose.koinInject import java.time.LocalDate -import com.twix.designsystem.R as DesR @Composable fun TaskCertificationDetailRoute( @@ -89,7 +89,7 @@ fun TaskCertificationDetailRoute( toastManager.show( ToastData( currentContext.getString( - DesR.string.toast_camera_permission_request, + R.string.toast_camera_permission_request, ), ToastType.ERROR, ), diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationCardContent.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationCardContent.kt index acd45632..d61b2395 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationCardContent.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationCardContent.kt @@ -9,8 +9,8 @@ import com.twix.designsystem.R import com.twix.designsystem.components.photolog.BackgroundCard import com.twix.designsystem.components.photolog.ForegroundCard import com.twix.domain.model.enums.BetweenUs -import com.twix.task_certification.detail.contract.TaskCertificationDetailUiState import com.twix.task_certification.detail.component.swipe.SwipeableCard +import com.twix.task_certification.detail.contract.TaskCertificationDetailUiState @Composable internal fun TaskCertificationCardContent( diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt index ee916be2..4ee17ab4 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/detail/component/TaskCertificationDetailTopBar.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.twix.designsystem.R import com.twix.designsystem.components.text.AppText import com.twix.designsystem.components.topbar.CommonTopBar import com.twix.designsystem.theme.CommonColor @@ -21,7 +22,6 @@ import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme import com.twix.domain.model.enums.AppTextStyle import com.twix.ui.extension.noRippleClickable -import com.twix.designsystem.R as DesR @Composable internal fun TaskCertificationDetailTopBar( @@ -34,7 +34,7 @@ internal fun TaskCertificationDetailTopBar( title = title, left = { Image( - painter = painterResource(DesR.drawable.ic_arrow3_left), + painter = painterResource(R.drawable.ic_arrow3_left), contentDescription = "back", modifier = Modifier @@ -54,7 +54,7 @@ internal fun TaskCertificationDetailTopBar( contentAlignment = Alignment.Center, ) { AppText( - text = stringResource(DesR.string.word_modify), + text = stringResource(R.string.word_modify), style = AppTextStyle.T2, color = GrayColor.C500, ) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt index f775e4f0..29f3d33c 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorRoute.kt @@ -8,9 +8,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf @@ -24,7 +22,6 @@ import androidx.compose.ui.layout.boundsInParent import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.core.app.ActivityCompat @@ -33,7 +30,6 @@ import coil3.compose.AsyncImage import coil3.request.ImageRequest import coil3.request.crossfade import com.twix.designsystem.R -import com.twix.designsystem.components.button.AppRoundButton import com.twix.designsystem.components.comment.CommentAnchorFrame import com.twix.designsystem.components.photolog.PhotologCard import com.twix.designsystem.components.toast.ToastManager @@ -41,7 +37,6 @@ import com.twix.designsystem.components.toast.model.ToastData import com.twix.designsystem.components.toast.model.ToastType import com.twix.designsystem.extension.showCameraPermissionToastWithNavigateToSettingAction import com.twix.designsystem.theme.CommonColor -import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme import com.twix.task_certification.editor.component.RetakeButton import com.twix.task_certification.editor.component.TaskCertificationEditorTopBar diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/component/RetakeButton.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/component/RetakeButton.kt index 62d2ba9c..c55390cb 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/component/RetakeButton.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/component/RetakeButton.kt @@ -18,7 +18,7 @@ import com.twix.ui.extension.noRippleClickable @Composable internal fun RetakeButton( onClickRetake: () -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { AppRoundButton( text = stringResource(R.string.task_certification_editor_retake), diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/component/TaskCertificationEditorTopBar.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/component/TaskCertificationEditorTopBar.kt index 655b031c..bd3c2084 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/component/TaskCertificationEditorTopBar.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/component/TaskCertificationEditorTopBar.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.twix.designsystem.R import com.twix.designsystem.components.text.AppText import com.twix.designsystem.components.topbar.CommonTopBar import com.twix.designsystem.theme.CommonColor @@ -21,7 +22,6 @@ import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme import com.twix.domain.model.enums.AppTextStyle import com.twix.ui.extension.noRippleClickable -import com.twix.designsystem.R as DesR @Composable internal fun TaskCertificationEditorTopBar( @@ -33,7 +33,7 @@ internal fun TaskCertificationEditorTopBar( title = title, left = { Image( - painter = painterResource(DesR.drawable.ic_arrow3_left), + painter = painterResource(R.drawable.ic_arrow3_left), contentDescription = "back", modifier = Modifier @@ -52,7 +52,7 @@ internal fun TaskCertificationEditorTopBar( contentAlignment = Alignment.Center, ) { AppText( - text = stringResource(DesR.string.word_save), + text = stringResource(R.string.word_save), style = AppTextStyle.T2, color = GrayColor.C500, ) From ad532e9cdfe1567102f5898a085f01f5fb745da2 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Fri, 20 Feb 2026 17:42:42 +0900 Subject: [PATCH 51/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=EC=83=B7=20=ED=8E=B8=EC=A7=91=EA=B8=B0=20Vie?= =?UTF-8?q?wModel=20=EC=B4=88=EA=B8=B0=ED=99=94=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../editor/TaskCertificationEditorViewModel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt index 447a5aad..3031f426 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt @@ -25,10 +25,10 @@ class TaskCertificationEditorViewModel( TaskCertificationEditorUiState(), ) { private val navArgs: EditorNavArgs = - savedStateHandle.decodeNavArgs(NavRoutes.TaskCertificationEditorRoute.ARG_DATA) + savedStateHandle.decodeNavArgs(NavRoutes.TaskCertificationEditorRoute.ARG_DATA) init { - navArgs.toUiState() + reduce { navArgs.toUiState() } } override suspend fun handleIntent(intent: TaskCertificationEditorIntent) { From 564d94bdc664722fb9760598bfdc84e837593d34 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Fri, 20 Feb 2026 17:59:59 +0900 Subject: [PATCH 52/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EB=AF=B8=EC=82=AC=EC=9A=A9=20=EC=83=81=EC=88=98=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../editor/TaskCertificationEditorViewModel.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt index 3031f426..0cf58c24 100644 --- a/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt +++ b/feature/task-certification/src/main/java/com/twix/task_certification/editor/TaskCertificationEditorViewModel.kt @@ -83,8 +83,4 @@ class TaskCertificationEditorViewModel( currentState.imageName, currentState.comment.value, ) - - companion object { - private const val SERIALIZER_NOT_FOUND = "Serializer Not Found" - } }