From 8d8b42199eb75de0be19633056bff4f13a55a5aa Mon Sep 17 00:00:00 2001 From: Sehwan Yun <39579912+l5x5l@users.noreply.github.com> Date: Mon, 8 Jul 2024 18:53:20 +0900 Subject: [PATCH 1/6] =?UTF-8?q?[Base]=20#10=20=08=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20=EC=B6=94=EA=B0=80=EA=B5=AC=ED=98=84=20(#11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [UI] #10 text 간격 조절 * [UI] #10 icon만 존재하는 PokitButton의 좌우 패딩값이 상하 패딩값과 동일해지도록 수정 * [UI] #10 컴포넌트 이름 변경 CategoryCard -> PokitList * [FEATURE] #10 PokitCard 컴포넌트 구현 * [UI] #10 LinkCard 컴포넌트에서 즐겨찾기 제거 및 안읽음 표시 UI 수정 * [UI] #10 LabeledInput에서 글자수 표시 부분을 선택 가능하도록 수정 * [UI] #10 PokitButton Large 사이즈의 높이 조정 * [UI] #10 typography의 글자 크기를 sp에서 dp로 수정 * [UI] #10 PokitBottomSheet가 시스템 navigation bar영역에 겹쳐보이는 문제 수정 * [UI] #10 ktlintFormat 적용 * [UI] #10 PokitInput Default, Input 상태일 시 디자인 수정 및 Round Shape일 때 높이 수정 * [UI] #10 PushCard 컴포넌트 구현 --- .../core/ui/components/atom/button/Preview.kt | 11 ++ .../container/PokitButtonContainerModifier.kt | 4 +- .../core/ui/components/atom/input/Preview.kt | 6 +- .../container/PokitInputContainerModifier.kt | 32 +++- .../attributes/CategoryCardState.kt | 5 - .../block/labeledinput/LabeledInput.kt | 14 +- .../components/block/labeledinput/Preview.kt | 1 + .../ui/components/block/linkcard/LinkCard.kt | 88 ++++----- .../ui/components/block/linkcard/Preview.kt | 3 +- .../components/block/pokitcard/PokitCard.kt | 100 +++++++++++ .../ui/components/block/pokitcard/Preview.kt | 48 +++++ .../PokitList.kt} | 41 ++--- .../{categorycard => pokitlist}/Preview.kt | 23 +-- .../pokitlist/attributes/PokitListState.kt | 5 + .../ui/components/block/pushcard/Preview.kt | 58 ++++++ .../ui/components/block/pushcard/PushCard.kt | 94 ++++++++++ .../template/bottomsheet/PokitBottomSheet.kt | 12 +- .../pokitmons/pokit/core/ui/theme/Type.kt | 169 ++++++++++-------- .../core/ui/theme/typography/PokitTypo.kt | 55 ++++++ core/ui/src/main/res/values/string.xml | 5 + 20 files changed, 578 insertions(+), 196 deletions(-) delete mode 100644 core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/categorycard/attributes/CategoryCardState.kt create mode 100644 core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitcard/PokitCard.kt create mode 100644 core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitcard/Preview.kt rename core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/{categorycard/CategoryCard.kt => pokitlist/PokitList.kt} (66%) rename core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/{categorycard => pokitlist}/Preview.kt (62%) create mode 100644 core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/attributes/PokitListState.kt create mode 100644 core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pushcard/Preview.kt create mode 100644 core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pushcard/PushCard.kt create mode 100644 core/ui/src/main/java/pokitmons/pokit/core/ui/theme/typography/PokitTypo.kt create mode 100644 core/ui/src/main/res/values/string.xml diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/button/Preview.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/button/Preview.kt index d45fda02..78a814b3 100644 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/button/Preview.kt +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/button/Preview.kt @@ -63,6 +63,17 @@ fun PokitButtonPreview() { style = style, type = type ) + + PokitButton( + text = null, + icon = PokitButtonIcon(resourceId = R.drawable.icon_24_search, position = PokitButtonIconPosition.LEFT), + onClick = {}, + enable = enable, + size = size, + shape = shape, + style = style, + type = type + ) } } } diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/button/subcomponents/container/PokitButtonContainerModifier.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/button/subcomponents/container/PokitButtonContainerModifier.kt index 0417d0f5..320ad4fe 100644 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/button/subcomponents/container/PokitButtonContainerModifier.kt +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/button/subcomponents/container/PokitButtonContainerModifier.kt @@ -124,7 +124,7 @@ private fun getPadding( getPaddingBySize( hasText = hasText, iconPosition = iconPosition, - verticalPaddingSize = 12.dp, + verticalPaddingSize = 13.dp, textSidePaddingSize = 24.dp, iconSidePaddingSize = 20.dp, textOnlyPadding = 20.dp @@ -142,7 +142,7 @@ private fun getPaddingBySize( textOnlyPadding: Dp, ): PaddingValues { return if (!hasText) { // icon only - PaddingValues(horizontal = iconSidePaddingSize, vertical = verticalPaddingSize) + PaddingValues(all = verticalPaddingSize) } else if (iconPosition == PokitButtonIconPosition.LEFT) { PaddingValues(start = iconSidePaddingSize, top = verticalPaddingSize, bottom = verticalPaddingSize, end = textSidePaddingSize) } else if (iconPosition == PokitButtonIconPosition.RIGHT) { diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/input/Preview.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/input/Preview.kt index 6e6ee806..aa576dba 100644 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/input/Preview.kt +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/input/Preview.kt @@ -35,7 +35,7 @@ fun PokitInputPreview() { enumValues().forEach { iconPosition -> PokitInput( text = "", - hintText = "내용을 입력해주세요.", + hintText = "error:$isError, enabled:$enabled, readOnly:$readOnly", onChangeText = {}, shape = shape, enable = enabled, @@ -45,8 +45,8 @@ fun PokitInputPreview() { ) } PokitInput( - text = "", - hintText = "내용을 입력해주세요.", + text = "val", + hintText = "error:$isError, enabled:$enabled, readOnly:$readOnly", onChangeText = {}, shape = shape, enable = enabled, diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/input/subcomponents/container/PokitInputContainerModifier.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/input/subcomponents/container/PokitInputContainerModifier.kt index ad53e8b8..45987e4b 100644 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/input/subcomponents/container/PokitInputContainerModifier.kt +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/atom/input/subcomponents/container/PokitInputContainerModifier.kt @@ -10,6 +10,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import pokitmons.pokit.core.ui.components.atom.input.attributes.PokitInputIconPosition import pokitmons.pokit.core.ui.components.atom.input.attributes.PokitInputShape @@ -58,21 +59,36 @@ private fun getPadding( shape: PokitInputShape, iconPosition: PokitInputIconPosition?, ): PaddingValues { + val verticalPadding = getVerticalPadding(shape = shape) + return when { shape == PokitInputShape.RECTANGLE -> { - PaddingValues(horizontal = 12.dp, vertical = 12.dp) + PaddingValues(horizontal = 12.dp, vertical = verticalPadding) } shape == PokitInputShape.ROUND && iconPosition == null -> { - PaddingValues(horizontal = 20.dp, vertical = 12.dp) + PaddingValues(horizontal = 20.dp, vertical = verticalPadding) } shape == PokitInputShape.ROUND && iconPosition == PokitInputIconPosition.LEFT -> { - PaddingValues(horizontal = 16.dp, vertical = 12.dp) + PaddingValues(horizontal = 16.dp, vertical = verticalPadding) } else -> { - PaddingValues(start = 20.dp, end = 16.dp, top = 12.dp, bottom = 12.dp) + PaddingValues(start = 20.dp, end = 16.dp, top = verticalPadding, bottom = verticalPadding) + } + } +} + +private fun getVerticalPadding( + shape: PokitInputShape, +): Dp { + return when (shape) { + PokitInputShape.ROUND -> { + 8.dp + } + PokitInputShape.RECTANGLE -> { + 13.dp } } } @@ -82,9 +98,9 @@ private fun getBackgroundColor( state: PokitInputState, ): Color { return when (state) { - PokitInputState.DEFAULT -> PokitTheme.colors.backgroundBase + PokitInputState.DEFAULT -> PokitTheme.colors.backgroundPrimary - PokitInputState.INPUT -> PokitTheme.colors.backgroundBase + PokitInputState.INPUT -> PokitTheme.colors.backgroundPrimary PokitInputState.ACTIVE -> PokitTheme.colors.backgroundBase @@ -101,9 +117,9 @@ private fun getStrokeColor( state: PokitInputState, ): Color { return when (state) { - PokitInputState.DEFAULT -> PokitTheme.colors.borderSecondary + PokitInputState.DEFAULT -> Color.Unspecified - PokitInputState.INPUT -> PokitTheme.colors.borderSecondary + PokitInputState.INPUT -> Color.Unspecified PokitInputState.ACTIVE -> PokitTheme.colors.brand diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/categorycard/attributes/CategoryCardState.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/categorycard/attributes/CategoryCardState.kt deleted file mode 100644 index 67b2fe0c..00000000 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/categorycard/attributes/CategoryCardState.kt +++ /dev/null @@ -1,5 +0,0 @@ -package pokitmons.pokit.core.ui.components.block.categorycard.attributes - -enum class CategoryCardState { - DEFAULT, ACTIVE, DISABLE -} diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/labeledinput/LabeledInput.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/labeledinput/LabeledInput.kt index a7c41d1f..76934443 100644 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/labeledinput/LabeledInput.kt +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/labeledinput/LabeledInput.kt @@ -28,12 +28,12 @@ import pokitmons.pokit.core.ui.theme.PokitTheme @Composable fun LabeledInput( label: String, - sub: String, - maxLength: Int, inputText: String, hintText: String, onChangeText: (String) -> Unit, modifier: Modifier = Modifier, + sub: String = "", + maxLength: Int? = null, readOnly: Boolean = false, enable: Boolean = true, isError: Boolean = false, @@ -97,10 +97,12 @@ fun LabeledInput( Spacer(modifier = Modifier.weight(1f)) - Text( - text = "${inputText.length}/$maxLength", - style = PokitTheme.typography.detail1.copy(color = subTextColor) - ) + if (maxLength != null) { + Text( + text = "${inputText.length}/$maxLength", + style = PokitTheme.typography.detail1.copy(color = subTextColor) + ) + } } } } diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/labeledinput/Preview.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/labeledinput/Preview.kt index 514045c6..ee2620dc 100644 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/labeledinput/Preview.kt +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/labeledinput/Preview.kt @@ -27,6 +27,7 @@ fun PokitInputPreview() { ) { enumValues().forEach { state -> LabeledInput(label = "Label", sub = "내용을 입력해주세요", maxLength = 10, inputText = "으앙", hintText = "내용을 입력해주세요", onChangeText = {}) + LabeledInput(label = "Label", sub = "", maxLength = null, inputText = "으앙", hintText = "내용을 입력해주세요", onChangeText = {}) } } } diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/linkcard/LinkCard.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/linkcard/LinkCard.kt index ca3ec0fa..b564f307 100644 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/linkcard/LinkCard.kt +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/linkcard/LinkCard.kt @@ -13,26 +13,22 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import androidx.compose.ui.zIndex import pokitmons.pokit.core.ui.R import pokitmons.pokit.core.ui.theme.PokitTheme @@ -42,32 +38,13 @@ fun LinkCard( title: String, sub: String, painter: Painter, - indicatorVisible: Boolean, - bookmarked: Boolean, + notRead: Boolean, badgeText: String, onClickKebab: (T) -> Unit, onClickItem: (T) -> Unit, modifier: Modifier = Modifier, ) { Box(modifier = modifier) { - if (indicatorVisible) { - Box( - modifier = Modifier - .size(16.dp) - .offset((-6).dp, (-6).dp) - .background( - color = PokitTheme.colors.brand, - shape = CircleShape - ) - .border( - color = PokitTheme.colors.inverseWh, - width = 2.dp, - shape = CircleShape - ) - .zIndex(1f) - ) - } - Row( modifier = Modifier .clip(RoundedCornerShape(8.dp)) @@ -87,26 +64,6 @@ fun LinkCard( .background(Color.Gray), contentScale = ContentScale.Crop ) - - if (bookmarked) { - Box( - modifier = Modifier - .size(24.dp) - .align(Alignment.BottomEnd) - .offset(x = (-8).dp, y = (-8).dp) - .background( - color = PokitTheme.colors.backgroundBaseIcon, - shape = CircleShape - ), - contentAlignment = Alignment.Center - ) { - Image( - painter = painterResource(id = R.drawable.icon_24_star), - contentDescription = null, - colorFilter = ColorFilter.tint(PokitTheme.colors.brand) - ) - } - } } Spacer(modifier = Modifier.width(12.dp)) @@ -153,16 +110,39 @@ fun LinkCard( Spacer(modifier = Modifier.height(4.dp)) - Text( - text = badgeText, - modifier = Modifier - .background( - color = PokitTheme.colors.backgroundPrimary, - shape = RoundedCornerShape(4.dp) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(6.dp) + ) { + Text( + text = badgeText, + modifier = Modifier + .background( + color = PokitTheme.colors.backgroundPrimary, + shape = RoundedCornerShape(4.dp) + ) + .padding(horizontal = 8.dp, vertical = 4.dp), + style = PokitTheme.typography.label4.copy(color = PokitTheme.colors.textTertiary) + ) + + if (notRead) { + Text( + text = stringResource(id = R.string.not_read), + modifier = Modifier + .border( + width = 1.dp, + color = PokitTheme.colors.borderTertiary, + shape = RoundedCornerShape(4.dp) + ) + .background( + color = PokitTheme.colors.backgroundBase, + shape = RoundedCornerShape(4.dp) + ) + .padding(horizontal = 8.dp, vertical = 4.dp), + style = PokitTheme.typography.label4.copy(color = PokitTheme.colors.textTertiary) ) - .padding(horizontal = 8.dp, vertical = 4.dp), - style = PokitTheme.typography.label4.copy(color = PokitTheme.colors.textTertiary) - ) + } + } } } } diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/linkcard/Preview.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/linkcard/Preview.kt index f3b91893..030a2395 100644 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/linkcard/Preview.kt +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/linkcard/Preview.kt @@ -28,8 +28,7 @@ fun LinkCardPreview() { sub = "2024.06.25. youtube.com", badgeText = "텍스트", painter = painterResource(id = R.drawable.icon_24_link), - indicatorVisible = true, - bookmarked = true, + notRead = true, item = 3, onClickKebab = { value: Int -> }, onClickItem = { value: Int -> } diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitcard/PokitCard.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitcard/PokitCard.kt new file mode 100644 index 00000000..8129a062 --- /dev/null +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitcard/PokitCard.kt @@ -0,0 +1,100 @@ +package pokitmons.pokit.core.ui.components.block.pokitcard + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import pokitmons.pokit.core.ui.R +import pokitmons.pokit.core.ui.theme.PokitTheme + +@Composable +fun PokitCard( + text: String, + linkCount: Int, + painter: Painter?, + onClick: () -> Unit, + onClickKebab: () -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .clip( + shape = RoundedCornerShape(8.dp) + ) + .background( + color = PokitTheme.colors.backgroundBase, + shape = RoundedCornerShape(8.dp) + ) + .clickable(onClick = onClick) + .padding(all = 12.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth() + ) { + Text( + modifier = Modifier.weight(1f), + text = text, + style = PokitTheme.typography.body1Bold.copy(color = PokitTheme.colors.textPrimary), + maxLines = 2 + ) + + IconButton( + onClick = onClickKebab, + modifier = Modifier + .size(24.dp) + .align(Alignment.Top) + ) { + Icon( + painter = painterResource(id = R.drawable.icon_24_kebab), + contentDescription = null, + modifier = Modifier.size(24.dp) + ) + } + } + + Spacer(modifier = Modifier.height(4.dp)) + + Text( + text = stringResource(id = R.string.pokit_count_format, linkCount), + style = PokitTheme.typography.detail2.copy(color = PokitTheme.colors.textTertiary) + ) + + Spacer(modifier = Modifier.height(18.dp)) + + Row( + modifier = Modifier + .height(68.dp) + .fillMaxWidth(1f), + horizontalArrangement = Arrangement.End + ) { + if (painter != null) { + Image( + modifier = Modifier.size(68.dp), + contentScale = ContentScale.Crop, + painter = painter, + contentDescription = null + ) + } + } + } +} diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitcard/Preview.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitcard/Preview.kt new file mode 100644 index 00000000..0720267d --- /dev/null +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitcard/Preview.kt @@ -0,0 +1,48 @@ +package pokitmons.pokit.core.ui.components.block.pokitcard + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import pokitmons.pokit.core.ui.R +import pokitmons.pokit.core.ui.theme.PokitTheme + +@Preview(showBackground = true) +@Composable +fun PokitardPreview() { + PokitTheme { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + PokitCard( + modifier = Modifier.weight(1f), + text = "요리/레시피", + linkCount = 10, + painter = null, + onClick = { }, + onClickKebab = { } + ) + + PokitCard( + modifier = Modifier.weight(1f), + text = "요리/레시피", + linkCount = 5, + painter = painterResource(id = R.drawable.icon_24_google), + onClick = { }, + onClickKebab = { } + ) + } + } + } +} diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/categorycard/CategoryCard.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/PokitList.kt similarity index 66% rename from core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/categorycard/CategoryCard.kt rename to core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/PokitList.kt index 0427ebe3..023ef41d 100644 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/categorycard/CategoryCard.kt +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/PokitList.kt @@ -1,6 +1,5 @@ -package pokitmons.pokit.core.ui.components.block.categorycard +package pokitmons.pokit.core.ui.components.block.pokitlist -import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -16,25 +15,22 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import pokitmons.pokit.core.ui.R -import pokitmons.pokit.core.ui.components.block.categorycard.attributes.CategoryCardState +import pokitmons.pokit.core.ui.components.block.pokitlist.attributes.PokitListState import pokitmons.pokit.core.ui.theme.PokitTheme @Composable -fun CategoryCard( +fun PokitList( item: T, - painter: Painter, title: String, sub: String, onClickKebab: (T) -> Unit, onClickItem: (T) -> Unit, modifier: Modifier = Modifier, - state: CategoryCardState = CategoryCardState.DISABLE, + state: PokitListState = PokitListState.DISABLE, ) { val titleTextColor = getTitleTextColor(state = state) val subTextColor = getSubTextColor(state = state) @@ -42,20 +38,13 @@ fun CategoryCard( Row( modifier = modifier .clickable( - enabled = state != CategoryCardState.DISABLE, + enabled = state != PokitListState.DISABLE, onClick = { onClickItem(item) } ) .padding(horizontal = 20.dp, vertical = 12.dp), verticalAlignment = Alignment.CenterVertically ) { - Image( - painter = painter, - contentDescription = null, - modifier = Modifier.size(60.dp), - contentScale = ContentScale.Crop - ) - - Spacer(modifier = Modifier.width(12.dp)) + Spacer(modifier = Modifier.width(8.dp)) Column( modifier = Modifier.weight(1f) @@ -79,7 +68,7 @@ fun CategoryCard( modifier = Modifier .padding(0.dp) .align(Alignment.Top), - enabled = state != CategoryCardState.DISABLE + enabled = state != PokitListState.DISABLE ) { Icon( painter = painterResource(id = R.drawable.icon_24_kebab), @@ -92,22 +81,22 @@ fun CategoryCard( @Composable private fun getTitleTextColor( - state: CategoryCardState, + state: PokitListState, ): Color { return when (state) { - CategoryCardState.DEFAULT -> PokitTheme.colors.textPrimary - CategoryCardState.ACTIVE -> PokitTheme.colors.textPrimary - CategoryCardState.DISABLE -> PokitTheme.colors.textDisable + PokitListState.DEFAULT -> PokitTheme.colors.textPrimary + PokitListState.ACTIVE -> PokitTheme.colors.textPrimary + PokitListState.DISABLE -> PokitTheme.colors.textDisable } } @Composable private fun getSubTextColor( - state: CategoryCardState, + state: PokitListState, ): Color { return when (state) { - CategoryCardState.DEFAULT -> PokitTheme.colors.textTertiary - CategoryCardState.ACTIVE -> PokitTheme.colors.textTertiary - CategoryCardState.DISABLE -> PokitTheme.colors.textDisable + PokitListState.DEFAULT -> PokitTheme.colors.textTertiary + PokitListState.ACTIVE -> PokitTheme.colors.textTertiary + PokitListState.DISABLE -> PokitTheme.colors.textDisable } } diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/categorycard/Preview.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/Preview.kt similarity index 62% rename from core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/categorycard/Preview.kt rename to core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/Preview.kt index dc92f7e6..74807d79 100644 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/categorycard/Preview.kt +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/Preview.kt @@ -1,49 +1,44 @@ -package pokitmons.pokit.core.ui.components.block.categorycard +package pokitmons.pokit.core.ui.components.block.pokitlist import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import pokitmons.pokit.core.ui.R -import pokitmons.pokit.core.ui.components.block.categorycard.attributes.CategoryCardState +import pokitmons.pokit.core.ui.components.block.pokitlist.attributes.PokitListState import pokitmons.pokit.core.ui.theme.PokitTheme @Preview(showBackground = true) @Composable -fun CategoryCardPreview() { +fun PokitListPreview() { PokitTheme { Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(8.dp) ) { - CategoryCard( - state = CategoryCardState.DISABLE, + PokitList( + state = PokitListState.DISABLE, item = "STRING", - painter = painterResource(id = R.drawable.icon_24_star), title = "카테고리입니당", sub = "15개 항목", onClickKebab = {}, onClickItem = {} ) - CategoryCard( - state = CategoryCardState.DEFAULT, + PokitList( + state = PokitListState.DEFAULT, item = "STRING", - painter = painterResource(id = R.drawable.icon_24_star), title = "카테고리입니당", sub = "15개 항목", onClickKebab = {}, onClickItem = {} ) - CategoryCard( - state = CategoryCardState.ACTIVE, + PokitList( + state = PokitListState.ACTIVE, item = "STRING", - painter = painterResource(id = R.drawable.icon_24_star), title = "카테고리입니당", sub = "15개 항목", onClickKebab = {}, diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/attributes/PokitListState.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/attributes/PokitListState.kt new file mode 100644 index 00000000..aab7815d --- /dev/null +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/attributes/PokitListState.kt @@ -0,0 +1,5 @@ +package pokitmons.pokit.core.ui.components.block.pokitlist.attributes + +enum class PokitListState { + DEFAULT, ACTIVE, DISABLE +} diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pushcard/Preview.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pushcard/Preview.kt new file mode 100644 index 00000000..77effd63 --- /dev/null +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pushcard/Preview.kt @@ -0,0 +1,58 @@ +package pokitmons.pokit.core.ui.components.block.pushcard + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import pokitmons.pokit.core.ui.R +import pokitmons.pokit.core.ui.theme.PokitTheme + +@Preview(showBackground = true) +@Composable +fun PushCardPreview() { + PokitTheme { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + PushCard( + title = "일단 글 링크의 타이틀은 조금 길수도 있으니까 아무렇게나 길게 적어봅니다", + sub = "내일 알림이 예정되어 있어요!\n잊지 말고 링크를 확인하세요!", + timeString = "1시간 전", + painter = null, + onClick = {}, + read = false + ) + + PushCard( + title = "일단 글 링크의 타이틀은 조금 길수도 있으니까 아무렇게나 길게 적어봅니다", + sub = "내일 알림이 예정되어 있어요!\n잊지 말고 링크를 확인하세요!", + timeString = "1시간 전", + painter = null, + onClick = {}, + read = true + ) + + PushCard( + title = "일단 글 링크의 타이틀은 조금 길수도 있으니까 아무렇게나 길게 적어봅니다", + sub = "내일 알림이 예정되어 있어요!\n잊지 말고 링크를 확인하세요!", + timeString = "1시간 전", + painter = painterResource(id = R.drawable.icon_24_google), + onClick = {}, + read = false + ) + PushCard( + title = "일단 글 링크의 타이틀은 조금 길수도 있으니까 아무렇게나 길게 적어봅니다", + sub = "내일 알림이 예정되어 있어요!\n잊지 말고 링크를 확인하세요!", + timeString = "1시간 전", + painter = painterResource(id = R.drawable.icon_24_google), + onClick = {}, + read = true + ) + } + } +} diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pushcard/PushCard.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pushcard/PushCard.kt new file mode 100644 index 00000000..bca982e5 --- /dev/null +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pushcard/PushCard.kt @@ -0,0 +1,94 @@ +package pokitmons.pokit.core.ui.components.block.pushcard + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import pokitmons.pokit.core.ui.theme.PokitTheme + +@Composable +fun PushCard( + title: String, + sub: String, + timeString: String, + painter: Painter?, + read: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + val titleTextColor = getTitleTextColor(read = read) + + Row( + modifier = modifier + .clickable(onClick = onClick) + .padding(all = 20.dp), + horizontalArrangement = Arrangement.spacedBy(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + if (painter != null) { + Image( + modifier = Modifier + .width(94.dp) + .height(70.dp) + .clip(shape = RoundedCornerShape(2.dp)), + painter = painter, + contentDescription = null, + contentScale = ContentScale.Crop + ) + } + + Column( + modifier = Modifier.weight(1f) + ) { + Text( + text = title, + style = PokitTheme.typography.body2Bold.copy(color = titleTextColor), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + + Spacer(modifier = Modifier.height(4.dp)) + + Text( + text = sub, + style = PokitTheme.typography.detail2.copy(color = PokitTheme.colors.textSecondary), + minLines = 2, + maxLines = 2 + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = timeString, + style = PokitTheme.typography.detail2.copy(color = PokitTheme.colors.textTertiary) + ) + } + } +} + +@Composable +private fun getTitleTextColor( + read: Boolean, +): Color { + return if (read) { + PokitTheme.colors.textPrimary + } else { + PokitTheme.colors.textDisable + } +} diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/bottomsheet/PokitBottomSheet.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/bottomsheet/PokitBottomSheet.kt index 3781095a..1a2ce82b 100644 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/bottomsheet/PokitBottomSheet.kt +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/bottomsheet/PokitBottomSheet.kt @@ -4,9 +4,13 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsIgnoringVisibility import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.windowInsetsBottomHeight import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ModalBottomSheet @@ -18,7 +22,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import pokitmons.pokit.core.ui.theme.PokitTheme -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) @Composable fun PokitBottomSheet( onHideBottomSheet: () -> Unit, @@ -49,5 +53,11 @@ fun PokitBottomSheet( } ) { content() + + Spacer( + Modifier.windowInsetsBottomHeight( + WindowInsets.navigationBarsIgnoringVisibility + ) + ) } } diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/theme/Type.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/theme/Type.kt index 309355bf..6a178ce2 100644 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/theme/Type.kt +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/theme/Type.kt @@ -1,139 +1,158 @@ package pokitmons.pokit.core.ui.theme +import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp +import pokitmons.pokit.core.ui.theme.typography.PokitTypo @Immutable data class PokitTypography( - val title1: TextStyle = TextStyle( + private val _title1: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 24.sp, + fontSize = 24, fontWeight = FontWeight.Bold, - lineHeight = 32.sp, - letterSpacing = (-1.1).sp + lineHeight = 32, + letterSpacing = (-0.26f) ), - val title2: TextStyle = TextStyle( + private val _title2: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 20.sp, + fontSize = 20, fontWeight = FontWeight.Bold, - lineHeight = 28.sp, - letterSpacing = (-3).sp + lineHeight = 28, + letterSpacing = (-0.6f) ), - val title3: TextStyle = TextStyle( + private val _title3: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 18.sp, + fontSize = 18, fontWeight = FontWeight.Medium, - lineHeight = 24.sp, - letterSpacing = (-1.1).sp + lineHeight = 24 ), - val body1Bold: TextStyle = TextStyle( + private val _body1Bold: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 18.sp, + fontSize = 18, fontWeight = FontWeight.Bold, - lineHeight = 24.sp, - letterSpacing = (-3).sp + lineHeight = 24, + letterSpacing = (-0.54f) ), - val body1Medium: TextStyle = TextStyle( + private val _body1Medium: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 18.sp, + fontSize = 18, fontWeight = FontWeight.Medium, - lineHeight = 24.sp, - letterSpacing = (-1.1).sp + lineHeight = 24, + letterSpacing = (-0.54f) ), - val body2Bold: TextStyle = TextStyle( + private val _body2Bold: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 16.sp, + fontSize = 16, fontWeight = FontWeight.Bold, - lineHeight = 20.sp, - letterSpacing = (-1.1).sp + lineHeight = 20, + letterSpacing = (-0.176f) ), - val body2Medium: TextStyle = TextStyle( + private val _body2Medium: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 16.sp, + fontSize = 16, fontWeight = FontWeight.Medium, - lineHeight = 20.sp, - letterSpacing = (-1.1).sp + lineHeight = 20, + letterSpacing = (-0.176f) ), - val body3Medium: TextStyle = TextStyle( + private val _body3Medium: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 14.sp, + fontSize = 14, fontWeight = FontWeight.Medium, - lineHeight = 18.sp, - letterSpacing = (-1.1).sp + lineHeight = 18, + letterSpacing = (-0.154f) ), - val body3Regular: TextStyle = TextStyle( + private val _body3Regular: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 14.sp, + fontSize = 14, fontWeight = FontWeight.W400, - lineHeight = 24.sp, - letterSpacing = (-3).sp + lineHeight = 24, + letterSpacing = (-0.42f) ), - val detail1: TextStyle = TextStyle( + private val _detail1: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 14.sp, + fontSize = 14, fontWeight = FontWeight.Medium, - lineHeight = 20.sp, - letterSpacing = (-1.1).sp + lineHeight = 20, + letterSpacing = (-0.154f) ), - val detail2: TextStyle = TextStyle( + private val _detail2: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 12.sp, + fontSize = 12, fontWeight = FontWeight.W400, - lineHeight = 16.sp, - letterSpacing = (-1.1).sp + lineHeight = 16, + letterSpacing = (-0.132f) ), - val label1SemiBold: TextStyle = TextStyle( + private val _label1SemiBold: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 18.sp, + fontSize = 18, fontWeight = FontWeight.SemiBold, - lineHeight = 24.sp, - letterSpacing = (-1.1).sp + lineHeight = 24, + letterSpacing = (-0.2f) ), - val label1Regular: TextStyle = TextStyle( + private val _label1Regular: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 18.sp, + fontSize = 18, fontWeight = FontWeight.W400, - lineHeight = 24.sp, - letterSpacing = (-1.1).sp + lineHeight = 24, + letterSpacing = (-0.2f) ), - val label2SemiBold: TextStyle = TextStyle( + private val _label2SemiBold: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 16.sp, + fontSize = 16, fontWeight = FontWeight.SemiBold, - lineHeight = 20.sp, - letterSpacing = (-1.1).sp + lineHeight = 20, + letterSpacing = (-0.18f) ), - val label2Regular: TextStyle = TextStyle( + private val _label2Regular: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 16.sp, + fontSize = 16, fontWeight = FontWeight.W400, - lineHeight = 20.sp, - letterSpacing = (-1.1).sp + lineHeight = 20, + letterSpacing = (-0.18f) ), - val label3SemiBold: TextStyle = TextStyle( + private val _label3SemiBold: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 14.sp, + fontSize = 14, fontWeight = FontWeight.SemiBold, - lineHeight = 16.sp, - letterSpacing = (-1.1).sp + lineHeight = 16, + letterSpacing = (-0.154f) ), - val label3Regular: TextStyle = TextStyle( + private val _label3Regular: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 14.sp, + fontSize = 14, fontWeight = FontWeight.W400, - lineHeight = 16.sp, - letterSpacing = (-1.1).sp + lineHeight = 16, + letterSpacing = (-0.154f) ), - val label4: TextStyle = TextStyle( + private val _label4: PokitTypo = PokitTypo( fontFamily = pretendard, - fontSize = 10.sp, + fontSize = 10, fontWeight = FontWeight.W400, - lineHeight = 12.sp, - letterSpacing = (-1.1).sp - ), -) + lineHeight = 12, + letterSpacing = (-0.11f) + ), +) { + val title1: TextStyle @Composable get() = _title1.toDpTextStyle + val title2: TextStyle @Composable get() = _title2.toDpTextStyle + val title3: TextStyle @Composable get() = _title3.toDpTextStyle + val body1Bold: TextStyle @Composable get() = _body1Bold.toDpTextStyle + val body1Medium: TextStyle @Composable get() = _body1Medium.toDpTextStyle + val body2Bold: TextStyle @Composable get() = _body2Bold.toDpTextStyle + val body2Medium: TextStyle @Composable get() = _body2Medium.toDpTextStyle + val body3Medium: TextStyle @Composable get() = _body3Medium.toDpTextStyle + val body3Regular: TextStyle @Composable get() = _body3Regular.toDpTextStyle + val detail1: TextStyle @Composable get() = _detail1.toDpTextStyle + val detail2: TextStyle @Composable get() = _detail2.toDpTextStyle + val label1SemiBold: TextStyle @Composable get() = _label1SemiBold.toDpTextStyle + val label1Regular: TextStyle @Composable get() = _label1Regular.toDpTextStyle + val label2SemiBold: TextStyle @Composable get() = _label2SemiBold.toDpTextStyle + val label2Regular: TextStyle @Composable get() = _label2Regular.toDpTextStyle + val label3SemiBold: TextStyle @Composable get() = _label3SemiBold.toDpTextStyle + val label3Regular: TextStyle @Composable get() = _label3Regular.toDpTextStyle + val label4: TextStyle @Composable get() = _label4.toDpTextStyle +} internal val LocalPokitTypography = staticCompositionLocalOf { PokitTypography() } diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/theme/typography/PokitTypo.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/theme/typography/PokitTypo.kt new file mode 100644 index 00000000..bdca7aba --- /dev/null +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/theme/typography/PokitTypo.kt @@ -0,0 +1,55 @@ +package pokitmons.pokit.core.ui.theme.typography + +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +data class PokitTypo( + val fontWeight: FontWeight, + val fontFamily: FontFamily, + val fontSize: Int, + val lineHeight: Int, + val letterSpacing: Float = 0f, +) { + val toDpTextStyle: TextStyle + @Composable get() = + TextStyle( + fontWeight = fontWeight, + fontFamily = fontFamily, + fontSize = fontSize.textDp, + letterSpacing = letterSpacing.textDp, + lineHeight = lineHeight.textDp + ) + + val toSpTextStyle: TextStyle + @Composable get() = + TextStyle( + fontWeight = fontWeight, + fontFamily = fontFamily, + fontSize = fontSize.sp, + letterSpacing = letterSpacing.sp, + lineHeight = lineHeight.sp + ) +} + +private fun Int.textDp(density: Density): TextUnit = + with(density) { + this@textDp.dp.toSp() + } + +private val Int.textDp: TextUnit + @Composable get() = this.textDp(density = LocalDensity.current) + +private fun Float.textDp(density: Density): TextUnit = + with(density) { + this@textDp.dp.toSp() + } + +private val Float.textDp: TextUnit + @Composable get() = this.textDp(density = LocalDensity.current) diff --git a/core/ui/src/main/res/values/string.xml b/core/ui/src/main/res/values/string.xml new file mode 100644 index 00000000..3e0864d8 --- /dev/null +++ b/core/ui/src/main/res/values/string.xml @@ -0,0 +1,5 @@ + + + 안읽음 + 링크 %d개 + \ No newline at end of file From f3fca5430048f9fcece5eb6cc9463d352e50847c Mon Sep 17 00:00:00 2001 From: Sehwan Yun <39579912+l5x5l@users.noreply.github.com> Date: Sun, 14 Jul 2024 09:32:03 +0900 Subject: [PATCH 2/6] =?UTF-8?q?[FEATURE]=20#9=20=EB=A7=81=ED=81=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=98=EA=B8=B0=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#12)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [BASE] #9 링크 추가 화면 모듈 생성 * [BASE] #9 링크 추가 화면 UI 구현 * [FEAT] #9 링크 추가 화면 ViewModel 구현 및 세부 UI 수정 * [FEAT] #9 링크 수정 화면 구현 * [CHORE] #9 접근 제한자 설정 * [FIX] #9 textField에 텍스트 입력시 cursor가 밀려 비정상적으로 입력되는 현상 수정 * [CHORE] #9 ktlink 형식 맞춤 --- feature/addlink/.gitignore | 1 + feature/addlink/build.gradle.kts | 64 ++++ feature/addlink/consumer-rules.pro | 0 feature/addlink/proguard-rules.pro | 21 ++ .../addlink/ExampleInstrumentedTest.kt | 22 ++ feature/addlink/src/main/AndroidManifest.xml | 4 + .../com/strayalpaca/addlink/AddLinkScreen.kt | 341 ++++++++++++++++++ .../strayalpaca/addlink/AddLinkViewModel.kt | 158 ++++++++ .../java/com/strayalpaca/addlink/Preview.kt | 42 +++ .../addlink/components/block/Link.kt | 69 ++++ .../addlink/components/block/Toolbar.kt | 53 +++ .../addlink/model/AddLinkScreenState.kt | 31 ++ .../com/strayalpaca/addlink/model/Link.kt | 9 + .../com/strayalpaca/addlink/model/Pokit.kt | 15 + .../addlink/utils/BackPressHandler.kt | 35 ++ .../addlink/src/main/res/values/string.xml | 24 ++ .../strayalpaca/addlink/ExampleUnitTest.kt | 16 + settings.gradle.kts | 1 + 18 files changed, 906 insertions(+) create mode 100644 feature/addlink/.gitignore create mode 100644 feature/addlink/build.gradle.kts create mode 100644 feature/addlink/consumer-rules.pro create mode 100644 feature/addlink/proguard-rules.pro create mode 100644 feature/addlink/src/androidTest/java/com/strayalpaca/addlink/ExampleInstrumentedTest.kt create mode 100644 feature/addlink/src/main/AndroidManifest.xml create mode 100644 feature/addlink/src/main/java/com/strayalpaca/addlink/AddLinkScreen.kt create mode 100644 feature/addlink/src/main/java/com/strayalpaca/addlink/AddLinkViewModel.kt create mode 100644 feature/addlink/src/main/java/com/strayalpaca/addlink/Preview.kt create mode 100644 feature/addlink/src/main/java/com/strayalpaca/addlink/components/block/Link.kt create mode 100644 feature/addlink/src/main/java/com/strayalpaca/addlink/components/block/Toolbar.kt create mode 100644 feature/addlink/src/main/java/com/strayalpaca/addlink/model/AddLinkScreenState.kt create mode 100644 feature/addlink/src/main/java/com/strayalpaca/addlink/model/Link.kt create mode 100644 feature/addlink/src/main/java/com/strayalpaca/addlink/model/Pokit.kt create mode 100644 feature/addlink/src/main/java/com/strayalpaca/addlink/utils/BackPressHandler.kt create mode 100644 feature/addlink/src/main/res/values/string.xml create mode 100644 feature/addlink/src/test/java/com/strayalpaca/addlink/ExampleUnitTest.kt diff --git a/feature/addlink/.gitignore b/feature/addlink/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/addlink/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/addlink/build.gradle.kts b/feature/addlink/build.gradle.kts new file mode 100644 index 00000000..d37b8318 --- /dev/null +++ b/feature/addlink/build.gradle.kts @@ -0,0 +1,64 @@ +plugins { + alias(libs.plugins.com.android.library) + alias(libs.plugins.org.jetbrains.kotlin.android) +} + +android { + namespace = "com.strayalpaca.addlink" + compileSdk = 34 + + defaultConfig { + minSdk = 24 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.5.1" + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.ui) + implementation(libs.androidx.ui.graphics) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.material3) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(platform(libs.androidx.compose.bom)) + androidTestImplementation(libs.androidx.ui.test.junit4) + debugImplementation(libs.androidx.ui.tooling) + debugImplementation(libs.androidx.ui.test.manifest) + + implementation(libs.orbit.compose) + implementation(libs.orbit.core) + implementation(libs.orbit.viewmodel) + + implementation(project(":core:ui")) +} diff --git a/feature/addlink/consumer-rules.pro b/feature/addlink/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/feature/addlink/proguard-rules.pro b/feature/addlink/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/addlink/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/addlink/src/androidTest/java/com/strayalpaca/addlink/ExampleInstrumentedTest.kt b/feature/addlink/src/androidTest/java/com/strayalpaca/addlink/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..8675bf96 --- /dev/null +++ b/feature/addlink/src/androidTest/java/com/strayalpaca/addlink/ExampleInstrumentedTest.kt @@ -0,0 +1,22 @@ +package com.strayalpaca.addlink + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.strayalpaca.addlink.test", appContext.packageName) + } +} diff --git a/feature/addlink/src/main/AndroidManifest.xml b/feature/addlink/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/addlink/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/feature/addlink/src/main/java/com/strayalpaca/addlink/AddLinkScreen.kt b/feature/addlink/src/main/java/com/strayalpaca/addlink/AddLinkScreen.kt new file mode 100644 index 00000000..10e36ede --- /dev/null +++ b/feature/addlink/src/main/java/com/strayalpaca/addlink/AddLinkScreen.kt @@ -0,0 +1,341 @@ +package com.strayalpaca.addlink + +import android.widget.Toast +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.LocalOverscrollConfiguration +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.strayalpaca.addlink.components.block.Link +import com.strayalpaca.addlink.components.block.Toolbar +import com.strayalpaca.addlink.model.AddLinkScreenSideEffect +import com.strayalpaca.addlink.model.AddLinkScreenState +import com.strayalpaca.addlink.model.Pokit +import com.strayalpaca.addlink.model.ScreenStep +import com.strayalpaca.addlink.utils.BackPressHandler +import org.orbitmvi.orbit.compose.collectAsState +import org.orbitmvi.orbit.compose.collectSideEffect +import pokitmons.pokit.core.ui.components.atom.button.PokitButton +import pokitmons.pokit.core.ui.components.atom.button.attributes.PokitButtonIcon +import pokitmons.pokit.core.ui.components.atom.button.attributes.PokitButtonIconPosition +import pokitmons.pokit.core.ui.components.atom.button.attributes.PokitButtonSize +import pokitmons.pokit.core.ui.components.atom.inputarea.PokitInputArea +import pokitmons.pokit.core.ui.components.block.labeledinput.LabeledInput +import pokitmons.pokit.core.ui.components.block.pokitlist.PokitList +import pokitmons.pokit.core.ui.components.block.pokitlist.attributes.PokitListState +import pokitmons.pokit.core.ui.components.block.select.PokitSelect +import pokitmons.pokit.core.ui.components.block.switchradio.PokitSwitchRadio +import pokitmons.pokit.core.ui.components.block.switchradio.attributes.PokitSwitchRadioStyle +import pokitmons.pokit.core.ui.components.template.bottomsheet.PokitBottomSheet +import pokitmons.pokit.core.ui.theme.PokitTheme + +@Composable +fun AddLinkScreenContainer( + linkId: String?, + viewModel: AddLinkViewModel, + onBackPressed: () -> Unit, +) { + val state by viewModel.collectAsState() + val context = LocalContext.current + + BackPressHandler(onBackPressed = viewModel::onBackPressed) + + LaunchedEffect(Unit) { + linkId?.let { + viewModel.loadPokitLink(it) + } + } + + viewModel.collectSideEffect { sideEffect -> + when (sideEffect) { + AddLinkScreenSideEffect.AddLinkSuccess -> { + onBackPressed() + } + AddLinkScreenSideEffect.OnNavigationBack -> { + onBackPressed() + } + is AddLinkScreenSideEffect.ToastMessage -> { + Toast.makeText(context, context.getString(sideEffect.toastMessageEvent.stringResourceId), Toast.LENGTH_SHORT).show() + } + } + } + + val url by viewModel.linkUrl.collectAsState() + val title by viewModel.title.collectAsState() + val memo by viewModel.memo.collectAsState() + val pokitName by viewModel.pokitName.collectAsState() + + AddLinkScreen( + isModifyLink = (linkId != null), + url = url, + title = title, + memo = memo, + state = state, + pokitName = pokitName, + inputUrl = viewModel::inputLinkUrl, + inputTitle = viewModel::inputTitle, + inputMemo = viewModel::inputMemo, + inputNewPokitName = viewModel::inputNewPokitName, + onClickAddPokit = viewModel::showAddPokitBottomSheet, + onClickSavePokit = viewModel::savePokit, + dismissPokitAddBottomSheet = viewModel::hideAddPokitBottomSheet, + onClickSelectPokit = viewModel::showSelectPokitBottomSheet, + onClickSelectPokitItem = viewModel::selectPokit, + dismissPokitSelectBottomSheet = viewModel::hideSelectPokitBottomSheet, + toggleRemindRadio = viewModel::setRemind, + onBackPressed = viewModel::onBackPressed, + onClickSaveButton = viewModel::saveLink + ) +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun AddLinkScreen( + isModifyLink: Boolean, + url: String, + title: String, + memo: String, + pokitName: String, + state: AddLinkScreenState, + inputUrl: (String) -> Unit, + inputTitle: (String) -> Unit, + inputMemo: (String) -> Unit, + inputNewPokitName: (String) -> Unit, + onClickAddPokit: () -> Unit, + onClickSavePokit: () -> Unit, + dismissPokitAddBottomSheet: () -> Unit, + onClickSelectPokit: () -> Unit, + onClickSelectPokitItem: (Pokit) -> Unit, + dismissPokitSelectBottomSheet: () -> Unit, + toggleRemindRadio: (Boolean) -> Unit, + onBackPressed: () -> Unit, + onClickSaveButton: () -> Unit, +) { + val scrollState = rememberScrollState() + val enable = remember(state.step) { + !( + state.step == ScreenStep.SAVE_LOADING || + state.step == ScreenStep.LOADING || + state.step == ScreenStep.POKIT_ADD_LOADING || + state.step == ScreenStep.LINK_LOADING + ) + } + + Column( + modifier = Modifier.fillMaxSize() + ) { + Toolbar( + modifier = Modifier.fillMaxWidth(), + onClickBack = onBackPressed, + title = if (isModifyLink) stringResource(id = R.string.modify_link) else stringResource(id = R.string.add_link) + ) + + CompositionLocalProvider( + LocalOverscrollConfiguration provides null + ) { + Column( + modifier = Modifier + .padding(vertical = 16.dp, horizontal = 20.dp) + .verticalScroll( + state = scrollState, + flingBehavior = null + ) + ) { + if (state.link != null) { + Link(state.link) + } + + Spacer(modifier = Modifier.height(16.dp)) + + LabeledInput( + label = stringResource(id = R.string.link), + sub = "", + maxLength = null, + inputText = url, + hintText = stringResource(id = R.string.placeholder_link), + onChangeText = inputUrl, + enable = enable + ) + + Spacer(modifier = Modifier.height(24.dp)) + + LabeledInput( + label = stringResource(id = R.string.title), + sub = "", + maxLength = 20, + inputText = title, + hintText = stringResource(id = R.string.placeholder_title), + onChangeText = inputTitle, + enable = enable + ) + + Spacer(modifier = Modifier.height(24.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.Bottom + ) { + PokitSelect( + text = if (state.currentPokit == null) stringResource(id = R.string.uncategorized) else state.currentPokit.title, + hintText = stringResource(id = R.string.uncategorized), + label = stringResource(id = R.string.pokit), + modifier = Modifier.weight(1f), + onClick = onClickSelectPokit, + enable = enable + ) + + Spacer(modifier = Modifier.width(8.dp)) + + PokitButton( + text = null, + icon = PokitButtonIcon( + resourceId = pokitmons.pokit.core.ui.R.drawable.icon_24_plus, + position = PokitButtonIconPosition.LEFT + ), + size = PokitButtonSize.LARGE, + onClick = onClickAddPokit, + enable = enable + ) + } + + Spacer(modifier = Modifier.height(24.dp)) + + Text( + text = stringResource(id = R.string.memo), + style = PokitTheme.typography.body2Medium.copy(color = PokitTheme.colors.textSecondary) + ) + + Spacer(modifier = Modifier.height(8.dp)) + + PokitInputArea( + text = memo, + hintText = stringResource(id = R.string.placeholder_memo), + onChangeText = inputMemo, + enable = enable + ) + + Spacer(modifier = Modifier.height(4.dp)) + + Text( + modifier = Modifier.fillMaxWidth(), + text = "${memo.length}/100", + style = PokitTheme.typography.detail1.copy(color = PokitTheme.colors.textTertiary), + textAlign = TextAlign.End + ) + + Spacer(modifier = Modifier.height(24.dp)) + + Text( + text = stringResource(id = R.string.title_remind), + style = PokitTheme.typography.body2Medium.copy(color = PokitTheme.colors.textSecondary) + ) + + Spacer(modifier = Modifier.height(12.dp)) + + PokitSwitchRadio( + modifier = Modifier.fillMaxWidth(), + itemList = listOf( + Pair(stringResource(id = R.string.reject_remind), false), + Pair(stringResource(id = R.string.accept_remind), true) + ), + style = PokitSwitchRadioStyle.STROKE, + selectedItem = if (state.useRemind) { + Pair(stringResource(id = R.string.accept_remind), true) + } else { + Pair(stringResource(id = R.string.reject_remind), false) + }, + onClickItem = { + toggleRemindRadio(it.second) + }, + getTitleFromItem = { it.first }, + enabled = enable + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = stringResource(id = R.string.sub_remind), + style = PokitTheme.typography.detail1.copy(color = PokitTheme.colors.textTertiary) + ) + + Spacer(modifier = Modifier.height(32.dp)) + + PokitButton( + text = stringResource(id = R.string.save), + icon = null, + onClick = onClickSaveButton, + modifier = Modifier.fillMaxWidth(), + size = PokitButtonSize.LARGE + ) + } + } + + if (state.step == ScreenStep.POKIT_SELECT) { + PokitBottomSheet(onHideBottomSheet = dismissPokitSelectBottomSheet) { + LazyColumn { + items( + items = state.pokitList + ) { + PokitList( + item = it, + title = it.title, + sub = stringResource(id = R.string.count_format, it.count), + onClickKebab = onClickSelectPokitItem, + onClickItem = onClickSelectPokitItem, + state = PokitListState.ACTIVE + ) + } + } + } + } + + if (state.step == ScreenStep.POKIT_ADD) { + PokitBottomSheet(onHideBottomSheet = dismissPokitAddBottomSheet) { + Column( + modifier = Modifier.padding(horizontal = 20.dp) + ) { + LabeledInput( + label = "", + inputText = pokitName, + hintText = stringResource(id = R.string.placeholder_input_pokit_name), + onChangeText = inputNewPokitName, + maxLength = 10 + ) + + Spacer(modifier = Modifier.height(12.dp)) + + PokitButton( + text = stringResource(id = R.string.add), + icon = null, + onClick = onClickSavePokit, + modifier = Modifier.fillMaxWidth(), + size = PokitButtonSize.LARGE, + enable = pokitName.isNotEmpty() + ) + } + } + } + } +} diff --git a/feature/addlink/src/main/java/com/strayalpaca/addlink/AddLinkViewModel.kt b/feature/addlink/src/main/java/com/strayalpaca/addlink/AddLinkViewModel.kt new file mode 100644 index 00000000..8f607b45 --- /dev/null +++ b/feature/addlink/src/main/java/com/strayalpaca/addlink/AddLinkViewModel.kt @@ -0,0 +1,158 @@ +package com.strayalpaca.addlink + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.strayalpaca.addlink.model.AddLinkScreenSideEffect +import com.strayalpaca.addlink.model.AddLinkScreenState +import com.strayalpaca.addlink.model.Pokit +import com.strayalpaca.addlink.model.ScreenStep +import com.strayalpaca.addlink.model.sampleLink +import com.strayalpaca.addlink.model.samplePokitList +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import org.orbitmvi.orbit.Container +import org.orbitmvi.orbit.ContainerHost +import org.orbitmvi.orbit.syntax.simple.intent +import org.orbitmvi.orbit.syntax.simple.postSideEffect +import org.orbitmvi.orbit.syntax.simple.reduce +import org.orbitmvi.orbit.viewmodel.container + +class AddLinkViewModel : ContainerHost, ViewModel() { + override val container: Container = container(AddLinkScreenState()) + + private val _linkUrl = MutableStateFlow("") + val linkUrl: StateFlow = _linkUrl.asStateFlow() + + private val _title = MutableStateFlow("") + val title: StateFlow = _title.asStateFlow() + + private val _memo = MutableStateFlow("") + val memo: StateFlow = _memo.asStateFlow() + + private val _pokitName = MutableStateFlow("") + val pokitName: StateFlow = _pokitName.asStateFlow() + + init { + loadPokitList() + } + + private var inputLinkJob: Job? = null + + private fun loadPokitList() = intent { + viewModelScope.launch(Dispatchers.IO) { + reduce { state.copy(step = ScreenStep.LOADING) } + // todo 포킷 목록 가져오기 api 연결 + delay(1000L) + reduce { + state.copy( + step = ScreenStep.IDLE, + pokitList = samplePokitList + ) + } + } + } + + fun loadPokitLink(linkId: String) = intent { + viewModelScope.launch(Dispatchers.IO) { + reduce { state.copy(step = ScreenStep.LOADING) } + // todo 포킷 링크 가져오기 api 연결 + delay(1000L) + reduce { + state.copy( + step = ScreenStep.IDLE + ) + } + } + } + + fun inputLinkUrl(linkUrl: String) { + this._linkUrl.update { linkUrl } + + intent { + inputLinkJob?.cancel() + inputLinkJob = viewModelScope.launch(Dispatchers.IO) { + delay(1000L) + reduce { state.copy(step = ScreenStep.LINK_LOADING) } + // todo 링크 카드 정보 가져오기 api 연결 + delay(1000L) + reduce { state.copy(step = ScreenStep.IDLE, link = sampleLink.copy(url = linkUrl)) } + } + } + } + + fun inputTitle(title: String) { + _title.update { title } + } + + fun inputMemo(memo: String) { + _memo.update { memo } + } + + fun showAddPokitBottomSheet() = intent { + reduce { state.copy(step = ScreenStep.POKIT_ADD) } + } + + fun hideAddPokitBottomSheet() = intent { + reduce { state.copy(step = ScreenStep.IDLE) } + } + + fun showSelectPokitBottomSheet() = intent { + reduce { state.copy(step = ScreenStep.POKIT_SELECT) } + } + + fun hideSelectPokitBottomSheet() = intent { + reduce { state.copy(step = ScreenStep.IDLE) } + } + + fun selectPokit(pokit: Pokit) = intent { + reduce { state.copy(currentPokit = pokit, step = ScreenStep.IDLE) } + } + + fun onBackPressed() = intent { + val currentStep = container.stateFlow.value.step + when (currentStep) { + is ScreenStep.POKIT_ADD_LOADING -> {} // discard + + is ScreenStep.SAVE_LOADING -> {} // discard + + ScreenStep.POKIT_SELECT -> reduce { state.copy(step = ScreenStep.IDLE) } + + is ScreenStep.POKIT_ADD -> reduce { state.copy(step = ScreenStep.IDLE) } + + else -> postSideEffect(AddLinkScreenSideEffect.OnNavigationBack) + } + } + + fun inputNewPokitName(pokitName: String) { + _pokitName.update { pokitName } + } + + fun savePokit() = intent { + viewModelScope.launch(Dispatchers.IO) { + reduce { state.copy(step = ScreenStep.POKIT_ADD_LOADING) } + // todo 포킷 저장 useCase 연결 + delay(1000L) + reduce { state.copy(step = ScreenStep.IDLE) } + } + } + + fun saveLink() = intent { + viewModelScope.launch(Dispatchers.IO) { + reduce { state.copy(step = ScreenStep.LINK_LOADING) } + // todo 링크 저장 useCase 연결 + delay(1000L) + reduce { state.copy(step = ScreenStep.IDLE) } + postSideEffect(AddLinkScreenSideEffect.AddLinkSuccess) + } + } + + fun setRemind(remind: Boolean) = intent { + reduce { state.copy(useRemind = remind) } + } +} diff --git a/feature/addlink/src/main/java/com/strayalpaca/addlink/Preview.kt b/feature/addlink/src/main/java/com/strayalpaca/addlink/Preview.kt new file mode 100644 index 00000000..869196a4 --- /dev/null +++ b/feature/addlink/src/main/java/com/strayalpaca/addlink/Preview.kt @@ -0,0 +1,42 @@ +package com.strayalpaca.addlink + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.strayalpaca.addlink.model.AddLinkScreenState +import pokitmons.pokit.core.ui.theme.PokitTheme + +@Preview(showBackground = true) +@Composable +fun AddLinkScreenPreview() { + PokitTheme { + Column( + modifier = Modifier.fillMaxSize().background(PokitTheme.colors.backgroundBase) + ) { + AddLinkScreen( + isModifyLink = false, + url = "", + title = "", + memo = "", + pokitName = "", + state = AddLinkScreenState(), + inputUrl = {}, + inputTitle = {}, + inputMemo = {}, + inputNewPokitName = {}, + onClickAddPokit = {}, + onClickSavePokit = {}, + dismissPokitAddBottomSheet = {}, + onClickSelectPokit = {}, + onClickSelectPokitItem = {}, + dismissPokitSelectBottomSheet = {}, + toggleRemindRadio = {}, + onBackPressed = {}, + onClickSaveButton = {} + ) + } + } +} diff --git a/feature/addlink/src/main/java/com/strayalpaca/addlink/components/block/Link.kt b/feature/addlink/src/main/java/com/strayalpaca/addlink/components/block/Link.kt new file mode 100644 index 00000000..821e6614 --- /dev/null +++ b/feature/addlink/src/main/java/com/strayalpaca/addlink/components/block/Link.kt @@ -0,0 +1,69 @@ +package com.strayalpaca.addlink.components.block + +import androidx.compose.foundation.Image +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import com.strayalpaca.addlink.model.Link +import pokitmons.pokit.core.ui.theme.PokitTheme + +@Composable +internal fun Link( + link: Link, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .height(IntrinsicSize.Min) + .border( + width = 1.dp, + color = PokitTheme.colors.borderTertiary, + shape = RoundedCornerShape(12.dp) + ) + ) { + Image( + painter = painterResource(id = pokitmons.pokit.core.ui.R.drawable.icon_24_google), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier.width(124.dp) + ) + + Column( + modifier = Modifier + .padding(start = 16.dp, end = 20.dp, top = 16.dp, bottom = 16.dp) + .weight(1f) + ) { + Text( + modifier = Modifier.fillMaxWidth(), + text = link.title, + maxLines = 2, + style = PokitTheme.typography.body3Medium.copy(color = PokitTheme.colors.textSecondary) + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + modifier = Modifier.fillMaxWidth(), + text = link.url, + maxLines = 2, + style = PokitTheme.typography.detail2.copy(color = PokitTheme.colors.textTertiary) + ) + } + } +} diff --git a/feature/addlink/src/main/java/com/strayalpaca/addlink/components/block/Toolbar.kt b/feature/addlink/src/main/java/com/strayalpaca/addlink/components/block/Toolbar.kt new file mode 100644 index 00000000..5a5c5254 --- /dev/null +++ b/feature/addlink/src/main/java/com/strayalpaca/addlink/components/block/Toolbar.kt @@ -0,0 +1,53 @@ +package com.strayalpaca.addlink.components.block + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import pokitmons.pokit.core.ui.theme.PokitTheme + +@Composable +internal fun Toolbar( + onClickBack: () -> Unit, + title: String, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .height(56.dp) + .padding(horizontal = 12.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + IconButton( + modifier = Modifier.size(48.dp), + onClick = onClickBack + ) { + Icon( + painter = painterResource(id = pokitmons.pokit.core.ui.R.drawable.icon_24_arrow_left), + contentDescription = "back button" + ) + } + + Text( + modifier = Modifier.weight(1f), + text = title, + style = PokitTheme.typography.title3.copy(color = PokitTheme.colors.textPrimary), + textAlign = TextAlign.Center + ) + + Spacer(modifier = Modifier.width(48.dp)) + } +} diff --git a/feature/addlink/src/main/java/com/strayalpaca/addlink/model/AddLinkScreenState.kt b/feature/addlink/src/main/java/com/strayalpaca/addlink/model/AddLinkScreenState.kt new file mode 100644 index 00000000..c6a722eb --- /dev/null +++ b/feature/addlink/src/main/java/com/strayalpaca/addlink/model/AddLinkScreenState.kt @@ -0,0 +1,31 @@ +package com.strayalpaca.addlink.model + +import com.strayalpaca.addlink.R + +data class AddLinkScreenState( + val link: Link? = null, + val currentPokit: Pokit? = null, + val pokitList: List = emptyList(), + val useRemind: Boolean = false, + val step: ScreenStep = ScreenStep.IDLE, +) + +sealed class ScreenStep { + data object LOADING : ScreenStep() + data object IDLE : ScreenStep() + data object LINK_LOADING : ScreenStep() + data object POKIT_SELECT : ScreenStep() + data object POKIT_ADD : ScreenStep() + data object POKIT_ADD_LOADING : ScreenStep() + data object SAVE_LOADING : ScreenStep() +} + +sealed class AddLinkScreenSideEffect() { + data object AddLinkSuccess : AddLinkScreenSideEffect() + data class ToastMessage(val toastMessageEvent: ToastMessageEvent) : AddLinkScreenSideEffect() + data object OnNavigationBack : AddLinkScreenSideEffect() +} + +enum class ToastMessageEvent(val stringResourceId: Int) { + NETWORK_ERROR(R.string.network_error), +} diff --git a/feature/addlink/src/main/java/com/strayalpaca/addlink/model/Link.kt b/feature/addlink/src/main/java/com/strayalpaca/addlink/model/Link.kt new file mode 100644 index 00000000..b05521d4 --- /dev/null +++ b/feature/addlink/src/main/java/com/strayalpaca/addlink/model/Link.kt @@ -0,0 +1,9 @@ +package com.strayalpaca.addlink.model + +data class Link( + val url: String, + val title: String, + val imageUrl: String?, +) + +internal val sampleLink = Link(url = "https://pokit.com/watch?v=xSTwqkUyM8k", title = "자연 친화적인 라이프스타일을 위한 환경 보호 방법", imageUrl = null) diff --git a/feature/addlink/src/main/java/com/strayalpaca/addlink/model/Pokit.kt b/feature/addlink/src/main/java/com/strayalpaca/addlink/model/Pokit.kt new file mode 100644 index 00000000..0088afe6 --- /dev/null +++ b/feature/addlink/src/main/java/com/strayalpaca/addlink/model/Pokit.kt @@ -0,0 +1,15 @@ +package com.strayalpaca.addlink.model + +data class Pokit( + val title: String, + val id: String, + val count: Int, +) + +internal val samplePokitList = listOf( + Pokit(title = "안드로이드", id = "1", count = 2), + Pokit(title = "IOS", id = "2", count = 2), + Pokit(title = "디자인", id = "3", count = 2), + Pokit(title = "PM", id = "4", count = 1), + Pokit(title = "서버", id = "5", count = 2) +) diff --git a/feature/addlink/src/main/java/com/strayalpaca/addlink/utils/BackPressHandler.kt b/feature/addlink/src/main/java/com/strayalpaca/addlink/utils/BackPressHandler.kt new file mode 100644 index 00000000..dc0796d5 --- /dev/null +++ b/feature/addlink/src/main/java/com/strayalpaca/addlink/utils/BackPressHandler.kt @@ -0,0 +1,35 @@ +package com.strayalpaca.addlink.utils + +import androidx.activity.OnBackPressedCallback +import androidx.activity.OnBackPressedDispatcher +import androidx.activity.compose.LocalOnBackPressedDispatcherOwner +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState + +@Composable +internal fun BackPressHandler( + backPressedDispatcher: OnBackPressedDispatcher? = + LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher, + onBackPressed: () -> Unit, +) { + val currentOnBackPressed by rememberUpdatedState(newValue = onBackPressed) + + val backCallback = remember { + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + currentOnBackPressed() + } + } + } + + DisposableEffect(backPressedDispatcher) { + backPressedDispatcher?.addCallback(backCallback) + + onDispose { + backCallback.remove() + } + } +} diff --git a/feature/addlink/src/main/res/values/string.xml b/feature/addlink/src/main/res/values/string.xml new file mode 100644 index 00000000..cf43735d --- /dev/null +++ b/feature/addlink/src/main/res/values/string.xml @@ -0,0 +1,24 @@ + + + 링크 추가 + 링크 수정 + 링크 + 내용을 입력해주세요. + 제목 + 내용을 입력해주세요. + 포킷 + 메모 + 내용을 입력해주세요. + 리마인드 알림을 보내드릴까요? + 안받을래요 + 받을래요 + 일주일 후에 알림을 전송해드립니다. + 저장하기 + 미분류 + + 포킷명을 입력해주세요. + 추가하기 + 링크 %d개 + + 네트워크 에러가 발생했습니다. 네트워크 환경을 확인해주세요. + \ No newline at end of file diff --git a/feature/addlink/src/test/java/com/strayalpaca/addlink/ExampleUnitTest.kt b/feature/addlink/src/test/java/com/strayalpaca/addlink/ExampleUnitTest.kt new file mode 100644 index 00000000..5093231d --- /dev/null +++ b/feature/addlink/src/test/java/com/strayalpaca/addlink/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package com.strayalpaca.addlink + +import org.junit.Assert.assertEquals +import org.junit.Test + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 995d8d42..04541faf 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -25,3 +25,4 @@ include(":domain") include(":data") include(":feature:login") include(":core:ui") +include(":feature:addlink") From 8074bf3cceb1bd3b995f79b85e8fa4e35e4ccc9f Mon Sep 17 00:00:00 2001 From: Sehwan Yun <39579912+l5x5l@users.noreply.github.com> Date: Sun, 14 Jul 2024 14:56:12 +0900 Subject: [PATCH 3/6] =?UTF-8?q?[Feature]=20#13=20=ED=8F=AC=ED=82=B7=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=ED=99=94=EB=A9=B4=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(#15)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [BASE] #13 feature:addpokit 모듈 생성 * [BASE] #13 이미지 선택부분을 제외한 포킷 추가 화면 구현 * [FEATURE] #13 포킷 프로필 선택부분 구현 * [CHORE] #13 ktlint 적용 * [CHORE] #13 포킷 이름 최대 글자수 상수로 분리 * [CHORE] #13 core 모듈 resource를 사용하는 부분을 alias import로 변경 --- feature/addpokit/.gitignore | 1 + feature/addpokit/build.gradle.kts | 64 +++++ feature/addpokit/consumer-rules.pro | 0 feature/addpokit/proguard-rules.pro | 21 ++ .../addpokit/ExampleInstrumentedTest.kt | 22 ++ feature/addpokit/src/main/AndroidManifest.xml | 4 + .../strayalpaca/addpokit/AddPokitScreen.kt | 259 ++++++++++++++++++ .../strayalpaca/addpokit/AddPokitViewModel.kt | 116 ++++++++ .../java/com/strayalpaca/addpokit/Preview.kt | 24 ++ .../components/atom/PokitProfileImage.kt | 49 ++++ .../addpokit/components/block/Toolbar.kt | 54 ++++ .../com/strayalpaca/addpokit/const/Consts.kt | 3 + .../addpokit/model/AddPokitScreenState.kt | 29 ++ .../com/strayalpaca/addpokit/model/Pokit.kt | 15 + .../addpokit/model/PokitProfile.kt | 13 + .../addpokit/utils/BackPressHandler.kt | 35 +++ .../addpokit/src/main/res/values/string.xml | 11 + .../strayalpaca/addpokit/ExampleUnitTest.kt | 16 ++ 18 files changed, 736 insertions(+) create mode 100644 feature/addpokit/.gitignore create mode 100644 feature/addpokit/build.gradle.kts create mode 100644 feature/addpokit/consumer-rules.pro create mode 100644 feature/addpokit/proguard-rules.pro create mode 100644 feature/addpokit/src/androidTest/java/com/strayalpaca/addpokit/ExampleInstrumentedTest.kt create mode 100644 feature/addpokit/src/main/AndroidManifest.xml create mode 100644 feature/addpokit/src/main/java/com/strayalpaca/addpokit/AddPokitScreen.kt create mode 100644 feature/addpokit/src/main/java/com/strayalpaca/addpokit/AddPokitViewModel.kt create mode 100644 feature/addpokit/src/main/java/com/strayalpaca/addpokit/Preview.kt create mode 100644 feature/addpokit/src/main/java/com/strayalpaca/addpokit/components/atom/PokitProfileImage.kt create mode 100644 feature/addpokit/src/main/java/com/strayalpaca/addpokit/components/block/Toolbar.kt create mode 100644 feature/addpokit/src/main/java/com/strayalpaca/addpokit/const/Consts.kt create mode 100644 feature/addpokit/src/main/java/com/strayalpaca/addpokit/model/AddPokitScreenState.kt create mode 100644 feature/addpokit/src/main/java/com/strayalpaca/addpokit/model/Pokit.kt create mode 100644 feature/addpokit/src/main/java/com/strayalpaca/addpokit/model/PokitProfile.kt create mode 100644 feature/addpokit/src/main/java/com/strayalpaca/addpokit/utils/BackPressHandler.kt create mode 100644 feature/addpokit/src/main/res/values/string.xml create mode 100644 feature/addpokit/src/test/java/com/strayalpaca/addpokit/ExampleUnitTest.kt diff --git a/feature/addpokit/.gitignore b/feature/addpokit/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/addpokit/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/addpokit/build.gradle.kts b/feature/addpokit/build.gradle.kts new file mode 100644 index 00000000..0c700c02 --- /dev/null +++ b/feature/addpokit/build.gradle.kts @@ -0,0 +1,64 @@ +plugins { + alias(libs.plugins.com.android.library) + alias(libs.plugins.org.jetbrains.kotlin.android) +} + +android { + namespace = "com.strayalpaca.addpokit" + compileSdk = 34 + + defaultConfig { + minSdk = 24 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.5.1" + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.ui) + implementation(libs.androidx.ui.graphics) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.material3) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(platform(libs.androidx.compose.bom)) + androidTestImplementation(libs.androidx.ui.test.junit4) + debugImplementation(libs.androidx.ui.tooling) + debugImplementation(libs.androidx.ui.test.manifest) + + implementation(libs.orbit.compose) + implementation(libs.orbit.core) + implementation(libs.orbit.viewmodel) + + implementation(project(":core:ui")) +} diff --git a/feature/addpokit/consumer-rules.pro b/feature/addpokit/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/feature/addpokit/proguard-rules.pro b/feature/addpokit/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/addpokit/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/addpokit/src/androidTest/java/com/strayalpaca/addpokit/ExampleInstrumentedTest.kt b/feature/addpokit/src/androidTest/java/com/strayalpaca/addpokit/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..943ab772 --- /dev/null +++ b/feature/addpokit/src/androidTest/java/com/strayalpaca/addpokit/ExampleInstrumentedTest.kt @@ -0,0 +1,22 @@ +package com.strayalpaca.addpokit + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.strayalpaca.addpokit.test", appContext.packageName) + } +} diff --git a/feature/addpokit/src/main/AndroidManifest.xml b/feature/addpokit/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/addpokit/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/feature/addpokit/src/main/java/com/strayalpaca/addpokit/AddPokitScreen.kt b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/AddPokitScreen.kt new file mode 100644 index 00000000..120d66be --- /dev/null +++ b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/AddPokitScreen.kt @@ -0,0 +1,259 @@ +package com.strayalpaca.addpokit + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.strayalpaca.addpokit.components.atom.PokitProfileImage +import com.strayalpaca.addpokit.components.block.Toolbar +import com.strayalpaca.addpokit.model.AddPokitScreenState +import com.strayalpaca.addpokit.model.AddPokitScreenStep +import com.strayalpaca.addpokit.model.AddPokitSideEffect +import com.strayalpaca.addpokit.model.Pokit +import com.strayalpaca.addpokit.model.PokitProfile +import com.strayalpaca.addpokit.model.samplePokitProfileList +import com.strayalpaca.addpokit.utils.BackPressHandler +import org.orbitmvi.orbit.compose.collectSideEffect +import pokitmons.pokit.core.ui.components.atom.button.PokitButton +import pokitmons.pokit.core.ui.components.atom.button.attributes.PokitButtonSize +import pokitmons.pokit.core.ui.components.block.labeledinput.LabeledInput +import pokitmons.pokit.core.ui.components.block.pokitlist.PokitList +import pokitmons.pokit.core.ui.components.block.pokitlist.attributes.PokitListState +import pokitmons.pokit.core.ui.components.template.bottomsheet.PokitBottomSheet +import pokitmons.pokit.core.ui.theme.PokitTheme +import pokitmons.pokit.core.ui.R.drawable as coreDrawable +import pokitmons.pokit.core.ui.R.string as coreString + +@Composable +fun AddPokitScreenContainer( + viewModel: AddPokitViewModel, + onBackPressed: () -> Unit, +) { + val state by viewModel.container.stateFlow.collectAsState() + val pokitName by viewModel.pokitName.collectAsState() + + val saveButtonEnable = remember { + derivedStateOf { + state.step != AddPokitScreenStep.POKIT_SAVE_LOADING && + state.step != AddPokitScreenStep.POKIT_LIST_LOADING && + state.pokitInputErrorMessage == null && + state.pokitProfile != null + } + } + + viewModel.collectSideEffect { sideEffect -> + when (sideEffect) { + AddPokitSideEffect.AddPokitSuccess -> { + onBackPressed() + } + + AddPokitSideEffect.OnNavigationBack -> { + onBackPressed() + } + } + } + + BackPressHandler(onBackPressed = viewModel::onBackPressed) + + AddPokitScreen( + pokitName = pokitName, + state = state, + saveButtonEnable = saveButtonEnable.value, + onclickAddPokit = viewModel::savePokit, + inputPokitName = viewModel::inputPokitName, + onBackPressed = viewModel::onBackPressed, + hideProfileSelectBottomSheet = viewModel::hidePokitProfileSelectBottomSheet, + showSelectProfileBottomSheet = viewModel::showPokitProfileSelectBottomSheet, + selectPokitProfileImage = viewModel::selectPoktiProfile + ) +} + +@Composable +fun AddPokitScreen( + pokitName: String = "", + state: AddPokitScreenState = AddPokitScreenState(), + saveButtonEnable: Boolean = true, + onclickAddPokit: () -> Unit = {}, + inputPokitName: (String) -> Unit = {}, + onBackPressed: () -> Unit = {}, + hideProfileSelectBottomSheet: () -> Unit = {}, + showSelectProfileBottomSheet: () -> Unit = {}, + selectPokitProfileImage: (PokitProfile) -> Unit = {}, +) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(vertical = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Toolbar( + onClickBack = onBackPressed, + title = stringResource(id = R.string.title_add_pokit) + ) + + Box(modifier = Modifier.size(80.dp)) { + Image( + painter = painterResource(id = coreDrawable.icon_24_google), + contentDescription = null, + modifier = Modifier + .size(80.dp) + .clip(shape = RoundedCornerShape(12.dp)) + ) + + Box( + modifier = Modifier + .align(Alignment.BottomEnd) + .offset(x = 7.dp, y = (-7).dp) + .size(24.dp) + .background( + color = PokitTheme.colors.inverseWh, + shape = CircleShape + ) + .border( + width = 1.dp, + color = PokitTheme.colors.borderSecondary, + shape = CircleShape + ) + .clip( + shape = CircleShape + ) + .clickable( + onClick = showSelectProfileBottomSheet + ) + .padding(3.dp) + ) { + Image( + painter = painterResource(id = coreDrawable.icon_24_edit), + contentDescription = "null", + modifier = Modifier + .size(18.dp), + colorFilter = ColorFilter.tint( + color = PokitTheme.colors.iconTertiary + ) + ) + } + } + + Spacer(modifier = Modifier.height(12.dp)) + + LabeledInput( + modifier = Modifier.padding(horizontal = 20.dp), + label = stringResource(id = R.string.pokit_name), + inputText = pokitName, + hintText = stringResource(id = R.string.placeholder_pokit_name), + onChangeText = inputPokitName, + isError = state.pokitInputErrorMessage != null, + sub = state.pokitInputErrorMessage?.let { stringResource(id = it.resourceId) } ?: "", + enable = (state.step != AddPokitScreenStep.POKIT_SAVE_LOADING), + maxLength = 10 + ) + + Spacer(modifier = Modifier.height(28.dp)) + + Text( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp), + text = stringResource(id = R.string.my_pokit), + style = PokitTheme.typography.body2Medium.copy(color = PokitTheme.colors.textSecondary) + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Box( + modifier = Modifier + .fillMaxSize() + .weight(1f), + contentAlignment = Alignment.Center + ) { + LazyColumn( + modifier = Modifier.fillMaxSize() + ) { + items(state.pokitList) { item: Pokit -> + PokitList( + item = item, + title = item.title, + sub = stringResource(id = coreString.pokit_count_format, item.count), + onClickKebab = {}, + onClickItem = {}, + state = PokitListState.DEFAULT + ) + } + } + + if (state.step == AddPokitScreenStep.POKIT_LIST_LOADING) { + CircularProgressIndicator( + modifier = Modifier.width(64.dp), + color = PokitTheme.colors.brand, + trackColor = PokitTheme.colors.backgroundSecondary + ) + } + } + + Box( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp) + ) { + PokitButton( + text = stringResource(id = R.string.save), + icon = null, + onClick = onclickAddPokit, + modifier = Modifier.fillMaxWidth(), + size = PokitButtonSize.LARGE, + enable = saveButtonEnable + ) + } + + if (state.step == AddPokitScreenStep.SELECT_PROFILE) { + PokitBottomSheet(onHideBottomSheet = hideProfileSelectBottomSheet) { + LazyVerticalGrid( + modifier = Modifier.padding(vertical = 12.dp, horizontal = 40.dp), + columns = GridCells.Adaptive(66.dp), + horizontalArrangement = Arrangement.spacedBy(20.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + items(samplePokitProfileList) { profileImage -> + PokitProfileImage( + pokitProfile = profileImage, + onClick = selectPokitProfileImage, + focused = (state.pokitProfile?.id == profileImage.id) + ) + } + } + } + } + } +} diff --git a/feature/addpokit/src/main/java/com/strayalpaca/addpokit/AddPokitViewModel.kt b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/AddPokitViewModel.kt new file mode 100644 index 00000000..590775db --- /dev/null +++ b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/AddPokitViewModel.kt @@ -0,0 +1,116 @@ +package com.strayalpaca.addpokit + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.strayalpaca.addpokit.const.POKIT_NAME_MAX_LENGTH +import com.strayalpaca.addpokit.model.AddPokitScreenState +import com.strayalpaca.addpokit.model.AddPokitScreenStep +import com.strayalpaca.addpokit.model.AddPokitSideEffect +import com.strayalpaca.addpokit.model.PokitInputErrorMessage +import com.strayalpaca.addpokit.model.PokitProfile +import com.strayalpaca.addpokit.model.samplePokitList +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import org.orbitmvi.orbit.Container +import org.orbitmvi.orbit.ContainerHost +import org.orbitmvi.orbit.syntax.simple.intent +import org.orbitmvi.orbit.syntax.simple.postSideEffect +import org.orbitmvi.orbit.syntax.simple.reduce +import org.orbitmvi.orbit.viewmodel.container + +class AddPokitViewModel : ContainerHost, ViewModel() { + override val container: Container = container(AddPokitScreenState()) + + private val _pokitName = MutableStateFlow("") + val pokitName: StateFlow = _pokitName.asStateFlow() + + init { + loadPokitList() + } + + private fun loadPokitList() = intent { + viewModelScope.launch { + reduce { + state.copy( + step = AddPokitScreenStep.POKIT_LIST_LOADING, + pokitInputErrorMessage = null, + pokitList = emptyList() + ) + } + // todo 포킷 리스트 로드 api 연동 + delay(1000L) + + reduce { + state.copy( + step = AddPokitScreenStep.IDLE, + pokitList = samplePokitList + ) + } + } + } + + fun inputPokitName(pokitName: String) { + _pokitName.update { pokitName } + + intent { + val isInAvailableLength = pokitName.length > POKIT_NAME_MAX_LENGTH + val isDuplicatePokitName = state.pokitList.find { it.title == pokitName } != null + + val errorMessage = if (isInAvailableLength) { + PokitInputErrorMessage.TEXT_LENGTH_LIMIT + } else if (isDuplicatePokitName) { + PokitInputErrorMessage.ALREADY_USED_POKIT_NAME + } else { + null + } + reduce { state.copy(pokitInputErrorMessage = errorMessage) } + } + } + + fun savePokit() = intent { + reduce { + state.copy(step = AddPokitScreenStep.POKIT_SAVE_LOADING) + } + // todo 포킷 저장 api 연동 + delay(1000L) + reduce { + state.copy(step = AddPokitScreenStep.IDLE) + } + postSideEffect(AddPokitSideEffect.AddPokitSuccess) + } + + fun onBackPressed() = intent { + val currentStep = state.step + when (currentStep) { + AddPokitScreenStep.POKIT_SAVE_LOADING -> {} // discard + AddPokitScreenStep.SELECT_PROFILE -> { + reduce { state.copy(step = AddPokitScreenStep.IDLE) } + } + else -> { + postSideEffect(AddPokitSideEffect.OnNavigationBack) + } + } + } + + fun showPokitProfileSelectBottomSheet() = intent { + reduce { + state.copy(step = AddPokitScreenStep.SELECT_PROFILE) + } + } + + fun hidePokitProfileSelectBottomSheet() = intent { + reduce { + state.copy(step = AddPokitScreenStep.IDLE) + } + } + + fun selectPoktiProfile(pokitProfile: PokitProfile) = intent { + reduce { + state.copy(pokitProfile = pokitProfile) + } + } +} diff --git a/feature/addpokit/src/main/java/com/strayalpaca/addpokit/Preview.kt b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/Preview.kt new file mode 100644 index 00000000..dbb00c58 --- /dev/null +++ b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/Preview.kt @@ -0,0 +1,24 @@ +package com.strayalpaca.addpokit + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.strayalpaca.addpokit.model.AddPokitScreenState +import com.strayalpaca.addpokit.model.samplePokitList +import pokitmons.pokit.core.ui.theme.PokitTheme + +@Preview(showBackground = true) +@Composable +fun Preview() { + PokitTheme { + Column( + modifier = Modifier.fillMaxSize() + ) { + AddPokitScreen( + state = AddPokitScreenState().copy(pokitList = samplePokitList) + ) + } + } +} diff --git a/feature/addpokit/src/main/java/com/strayalpaca/addpokit/components/atom/PokitProfileImage.kt b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/components/atom/PokitProfileImage.kt new file mode 100644 index 00000000..91579c71 --- /dev/null +++ b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/components/atom/PokitProfileImage.kt @@ -0,0 +1,49 @@ +package com.strayalpaca.addpokit.components.atom + +import androidx.compose.foundation.Image +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import com.strayalpaca.addpokit.model.PokitProfile +import pokitmons.pokit.core.ui.theme.PokitTheme +import pokitmons.pokit.core.ui.R.drawable as coreDrawable + +@Composable +fun PokitProfileImage( + pokitProfile: PokitProfile, + onClick: (PokitProfile) -> Unit, + focused: Boolean = false, +) { + val activeStrokeColor = PokitTheme.colors.brand + val strokeColor = remember(focused) { + if (focused) { + activeStrokeColor + } else { + Color.Unspecified + } + } + + Image( + painter = painterResource(id = coreDrawable.icon_24_plus_r), + contentDescription = "pokit profile image", + modifier = Modifier + .size(66.dp) + .clip(shape = RoundedCornerShape(12.dp)) + .clickable { + onClick(pokitProfile) + } + .border( + color = strokeColor, + width = 1.dp, + shape = RoundedCornerShape(12.dp) + ) + ) +} diff --git a/feature/addpokit/src/main/java/com/strayalpaca/addpokit/components/block/Toolbar.kt b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/components/block/Toolbar.kt new file mode 100644 index 00000000..8154ecce --- /dev/null +++ b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/components/block/Toolbar.kt @@ -0,0 +1,54 @@ +package com.strayalpaca.addpokit.components.block + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import pokitmons.pokit.core.ui.theme.PokitTheme +import pokitmons.pokit.core.ui.R.drawable as coreDrawable + +@Composable +internal fun Toolbar( + onClickBack: () -> Unit, + title: String, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .height(56.dp) + .padding(horizontal = 12.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + IconButton( + modifier = Modifier.size(48.dp), + onClick = onClickBack + ) { + Icon( + painter = painterResource(id = coreDrawable.icon_24_arrow_left), + contentDescription = "back button" + ) + } + + Text( + modifier = Modifier.weight(1f), + text = title, + style = PokitTheme.typography.title3.copy(color = PokitTheme.colors.textPrimary), + textAlign = TextAlign.Center + ) + + Spacer(modifier = Modifier.width(48.dp)) + } +} diff --git a/feature/addpokit/src/main/java/com/strayalpaca/addpokit/const/Consts.kt b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/const/Consts.kt new file mode 100644 index 00000000..9ec703a4 --- /dev/null +++ b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/const/Consts.kt @@ -0,0 +1,3 @@ +package com.strayalpaca.addpokit.const + +internal const val POKIT_NAME_MAX_LENGTH = 10 diff --git a/feature/addpokit/src/main/java/com/strayalpaca/addpokit/model/AddPokitScreenState.kt b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/model/AddPokitScreenState.kt new file mode 100644 index 00000000..511ee69c --- /dev/null +++ b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/model/AddPokitScreenState.kt @@ -0,0 +1,29 @@ +package com.strayalpaca.addpokit.model + +import androidx.compose.runtime.Immutable +import com.strayalpaca.addpokit.R + +@Immutable +data class AddPokitScreenState( + val pokitInputErrorMessage: PokitInputErrorMessage? = null, + val pokitList: List = emptyList(), + val step: AddPokitScreenStep = AddPokitScreenStep.POKIT_LIST_LOADING, + val pokitProfile: PokitProfile? = null, +) + +sealed class AddPokitScreenStep { + data object IDLE : AddPokitScreenStep() + data object POKIT_LIST_LOADING : AddPokitScreenStep() + data object POKIT_SAVE_LOADING : AddPokitScreenStep() + data object SELECT_PROFILE : AddPokitScreenStep() +} + +sealed class AddPokitSideEffect { + data object AddPokitSuccess : AddPokitSideEffect() + data object OnNavigationBack : AddPokitSideEffect() +} + +enum class PokitInputErrorMessage(val resourceId: Int) { + TEXT_LENGTH_LIMIT(R.string.text_length_limit_format), + ALREADY_USED_POKIT_NAME(R.string.already_used_pokit_name), +} diff --git a/feature/addpokit/src/main/java/com/strayalpaca/addpokit/model/Pokit.kt b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/model/Pokit.kt new file mode 100644 index 00000000..1b004274 --- /dev/null +++ b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/model/Pokit.kt @@ -0,0 +1,15 @@ +package com.strayalpaca.addpokit.model + +data class Pokit( + val title: String, + val id: String, + val count: Int, +) + +internal val samplePokitList = listOf( + Pokit(title = "안드로이드", id = "1", count = 2), + Pokit(title = "IOS", id = "2", count = 2), + Pokit(title = "디자인", id = "3", count = 2), + Pokit(title = "PM", id = "4", count = 1), + Pokit(title = "서버", id = "5", count = 2) +) diff --git a/feature/addpokit/src/main/java/com/strayalpaca/addpokit/model/PokitProfile.kt b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/model/PokitProfile.kt new file mode 100644 index 00000000..41274e56 --- /dev/null +++ b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/model/PokitProfile.kt @@ -0,0 +1,13 @@ +package com.strayalpaca.addpokit.model + +data class PokitProfile( + val id: String, +) + +internal val samplePokitProfileList = + listOf( + PokitProfile("1"), PokitProfile("2"), PokitProfile("3"), PokitProfile("4"), + PokitProfile("5"), PokitProfile("6"), PokitProfile("7"), PokitProfile("8"), + PokitProfile("9"), PokitProfile("10"), PokitProfile("11"), PokitProfile("12"), + PokitProfile("13"), PokitProfile("14"), PokitProfile("15") + ) diff --git a/feature/addpokit/src/main/java/com/strayalpaca/addpokit/utils/BackPressHandler.kt b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/utils/BackPressHandler.kt new file mode 100644 index 00000000..2459aad0 --- /dev/null +++ b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/utils/BackPressHandler.kt @@ -0,0 +1,35 @@ +package com.strayalpaca.addpokit.utils + +import androidx.activity.OnBackPressedCallback +import androidx.activity.OnBackPressedDispatcher +import androidx.activity.compose.LocalOnBackPressedDispatcherOwner +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState + +@Composable +internal fun BackPressHandler( + backPressedDispatcher: OnBackPressedDispatcher? = + LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher, + onBackPressed: () -> Unit, +) { + val currentOnBackPressed by rememberUpdatedState(newValue = onBackPressed) + + val backCallback = remember { + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + currentOnBackPressed() + } + } + } + + DisposableEffect(backPressedDispatcher) { + backPressedDispatcher?.addCallback(backCallback) + + onDispose { + backCallback.remove() + } + } +} diff --git a/feature/addpokit/src/main/res/values/string.xml b/feature/addpokit/src/main/res/values/string.xml new file mode 100644 index 00000000..09e348fc --- /dev/null +++ b/feature/addpokit/src/main/res/values/string.xml @@ -0,0 +1,11 @@ + + + 포킷 추가 + 포킷 이름 + 카테고리 이름을 입력해주세요. + 저장하기 + 내 포킷 + + 최대 10자까지 입력 가능합니다. + 사용 중인 포킷명입니다. + \ No newline at end of file diff --git a/feature/addpokit/src/test/java/com/strayalpaca/addpokit/ExampleUnitTest.kt b/feature/addpokit/src/test/java/com/strayalpaca/addpokit/ExampleUnitTest.kt new file mode 100644 index 00000000..03eade60 --- /dev/null +++ b/feature/addpokit/src/test/java/com/strayalpaca/addpokit/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package com.strayalpaca.addpokit + +import org.junit.Assert.assertEquals +import org.junit.Test + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} From 38d8ceb02bc8a23d30ca82896f81f61c03178fa1 Mon Sep 17 00:00:00 2001 From: yunsehwan Date: Sun, 14 Jul 2024 15:01:59 +0900 Subject: [PATCH 4/6] =?UTF-8?q?[CHORE]=20#13=20settings.gradle=EC=97=90=20?= =?UTF-8?q?feature:addpokit=20=EB=AA=A8=EB=93=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- settings.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/settings.gradle.kts b/settings.gradle.kts index 04541faf..df401b7e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -26,3 +26,4 @@ include(":data") include(":feature:login") include(":core:ui") include(":feature:addlink") +include(":feature:addpokit") From 7ac909c938062536a794ae2c3316bf5b7ddc151e Mon Sep 17 00:00:00 2001 From: Sehwan Yun <39579912+l5x5l@users.noreply.github.com> Date: Tue, 16 Jul 2024 19:24:17 +0900 Subject: [PATCH 5/6] =?UTF-8?q?[Base]=20#14=20=EB=94=94=EC=9E=90=EC=9D=B8?= =?UTF-8?q?=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EC=88=98=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=EA=B5=AC=ED=98=84=20=20(#17)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [FEATURE] #14 아이템 수정 bottomSheet, 아이템 삭제 bottomSheet 구현 * [UI] #14 PokitList에서 state기본값을 Disable에서 Default로 수정 및 케밥 버튼 제거 * [CHORE] #14 PokitList 케밥 버튼 제거로 인한 인자 변경내용 반영 (AddLinkScreen, AddPokitScreen) * [CHORE] #14 ModifyBottomSheet, RemoveItemBottomSheet를 PokitBottomSheet 를 포함하지 않는 방식으로 수정 --- .../components/block/pokitlist/PokitList.kt | 22 +---- .../ui/components/block/pokitlist/Preview.kt | 3 - .../ModifyBottomSheetContent.kt | 45 +++++++++++ .../template/modifybottomsheet/Preview.kt | 22 +++++ .../subcomponents/ModifyBottomSheetItem.kt | 45 +++++++++++ .../template/removeItemBottomSheet/Preview.kt | 23 ++++++ .../RemoveItemBottomSheetContent.kt | 80 +++++++++++++++++++ .../attributes/RemoveItemType.kt | 14 ++++ core/ui/src/main/res/values/string.xml | 12 +++ .../com/strayalpaca/addlink/AddLinkScreen.kt | 1 - .../strayalpaca/addpokit/AddPokitScreen.kt | 1 - 11 files changed, 242 insertions(+), 26 deletions(-) create mode 100644 core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/modifybottomsheet/ModifyBottomSheetContent.kt create mode 100644 core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/modifybottomsheet/Preview.kt create mode 100644 core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/modifybottomsheet/subcomponents/ModifyBottomSheetItem.kt create mode 100644 core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/removeItemBottomSheet/Preview.kt create mode 100644 core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/removeItemBottomSheet/RemoveItemBottomSheetContent.kt create mode 100644 core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/removeItemBottomSheet/attributes/RemoveItemType.kt diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/PokitList.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/PokitList.kt index 023ef41d..9d346866 100644 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/PokitList.kt +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/PokitList.kt @@ -6,19 +6,14 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import pokitmons.pokit.core.ui.R import pokitmons.pokit.core.ui.components.block.pokitlist.attributes.PokitListState import pokitmons.pokit.core.ui.theme.PokitTheme @@ -27,10 +22,9 @@ fun PokitList( item: T, title: String, sub: String, - onClickKebab: (T) -> Unit, onClickItem: (T) -> Unit, modifier: Modifier = Modifier, - state: PokitListState = PokitListState.DISABLE, + state: PokitListState = PokitListState.DEFAULT, ) { val titleTextColor = getTitleTextColor(state = state) val subTextColor = getSubTextColor(state = state) @@ -62,20 +56,6 @@ fun PokitList( style = PokitTheme.typography.detail1.copy(color = subTextColor) ) } - - IconButton( - onClick = { onClickKebab(item) }, - modifier = Modifier - .padding(0.dp) - .align(Alignment.Top), - enabled = state != PokitListState.DISABLE - ) { - Icon( - painter = painterResource(id = R.drawable.icon_24_kebab), - contentDescription = null, - modifier = Modifier.size(24.dp) - ) - } } } diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/Preview.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/Preview.kt index 74807d79..b245fcba 100644 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/Preview.kt +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/block/pokitlist/Preview.kt @@ -23,7 +23,6 @@ fun PokitListPreview() { item = "STRING", title = "카테고리입니당", sub = "15개 항목", - onClickKebab = {}, onClickItem = {} ) @@ -32,7 +31,6 @@ fun PokitListPreview() { item = "STRING", title = "카테고리입니당", sub = "15개 항목", - onClickKebab = {}, onClickItem = {} ) @@ -41,7 +39,6 @@ fun PokitListPreview() { item = "STRING", title = "카테고리입니당", sub = "15개 항목", - onClickKebab = {}, onClickItem = {} ) } diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/modifybottomsheet/ModifyBottomSheetContent.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/modifybottomsheet/ModifyBottomSheetContent.kt new file mode 100644 index 00000000..850e6ddd --- /dev/null +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/modifybottomsheet/ModifyBottomSheetContent.kt @@ -0,0 +1,45 @@ +package pokitmons.pokit.core.ui.components.template.modifybottomsheet + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import pokitmons.pokit.core.ui.R +import pokitmons.pokit.core.ui.components.template.modifybottomsheet.subcomponents.ModifyBottomSheetItem + +@Composable +fun ModifyBottomSheetContent( + onClickShare: (() -> Unit)? = null, + onClickModify: (() -> Unit)? = null, + onClickRemove: (() -> Unit)? = null, +) { + Column( + modifier = Modifier.fillMaxWidth() + ) { + onClickShare?.let { onClickShare -> + ModifyBottomSheetItem( + onClick = {}, + title = stringResource(id = R.string.share), + painter = painterResource(id = R.drawable.icon_24_share) + ) + } + + onClickModify?.let { onClickModify -> + ModifyBottomSheetItem( + onClick = onClickModify, + title = stringResource(id = R.string.modify), + painter = painterResource(id = R.drawable.icon_24_edit) + ) + } + + onClickRemove?.let { onClickRemove -> + ModifyBottomSheetItem( + onClick = onClickRemove, + title = stringResource(id = R.string.remove), + painter = painterResource(id = R.drawable.icon_24_trash) + ) + } + } +} diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/modifybottomsheet/Preview.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/modifybottomsheet/Preview.kt new file mode 100644 index 00000000..ec0be10f --- /dev/null +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/modifybottomsheet/Preview.kt @@ -0,0 +1,22 @@ +package pokitmons.pokit.core.ui.components.template.modifybottomsheet + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import pokitmons.pokit.core.ui.theme.PokitTheme + +@Preview(showBackground = true) +@Composable +private fun ModifyBottomSheetContentPreview() { + PokitTheme { + Surface(modifier = Modifier.fillMaxSize()) { + ModifyBottomSheetContent( + onClickRemove = {}, + onClickModify = {}, + onClickShare = {} + ) + } + } +} diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/modifybottomsheet/subcomponents/ModifyBottomSheetItem.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/modifybottomsheet/subcomponents/ModifyBottomSheetItem.kt new file mode 100644 index 00000000..404c4f8a --- /dev/null +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/modifybottomsheet/subcomponents/ModifyBottomSheetItem.kt @@ -0,0 +1,45 @@ +package pokitmons.pokit.core.ui.components.template.modifybottomsheet.subcomponents + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.unit.dp +import pokitmons.pokit.core.ui.theme.PokitTheme + +@Composable +internal fun ModifyBottomSheetItem( + onClick: () -> Unit, + title: String, + painter: Painter, +) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + onClick() + } + .padding(horizontal = 24.dp, vertical = 20.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = title, + style = PokitTheme.typography.body1Medium.copy(color = PokitTheme.colors.textSecondary) + ) + + Image( + modifier = Modifier.size(24.dp), + painter = painter, + contentDescription = null + ) + } +} diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/removeItemBottomSheet/Preview.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/removeItemBottomSheet/Preview.kt new file mode 100644 index 00000000..7f2131e0 --- /dev/null +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/removeItemBottomSheet/Preview.kt @@ -0,0 +1,23 @@ +package pokitmons.pokit.core.ui.components.template.removeItemBottomSheet + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import pokitmons.pokit.core.ui.components.template.removeItemBottomSheet.attributes.RemoveItemType +import pokitmons.pokit.core.ui.theme.PokitTheme + +@Preview(showBackground = true) +@Composable +private fun RemoveItemBottomSheetContentPreview() { + PokitTheme { + Surface(modifier = Modifier.fillMaxSize()) { + RemoveItemBottomSheetContent( + removeItemType = RemoveItemType.LINK, + onClickCancel = {}, + onClickRemove = {} + ) + } + } +} diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/removeItemBottomSheet/RemoveItemBottomSheetContent.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/removeItemBottomSheet/RemoveItemBottomSheetContent.kt new file mode 100644 index 00000000..6f171700 --- /dev/null +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/removeItemBottomSheet/RemoveItemBottomSheetContent.kt @@ -0,0 +1,80 @@ +package pokitmons.pokit.core.ui.components.template.removeItemBottomSheet + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import pokitmons.pokit.core.ui.R +import pokitmons.pokit.core.ui.components.atom.button.PokitButton +import pokitmons.pokit.core.ui.components.atom.button.attributes.PokitButtonShape +import pokitmons.pokit.core.ui.components.atom.button.attributes.PokitButtonSize +import pokitmons.pokit.core.ui.components.atom.button.attributes.PokitButtonStyle +import pokitmons.pokit.core.ui.components.atom.button.attributes.PokitButtonType +import pokitmons.pokit.core.ui.components.template.removeItemBottomSheet.attributes.RemoveItemType +import pokitmons.pokit.core.ui.theme.PokitTheme + +@Composable +fun RemoveItemBottomSheetContent( + removeItemType: RemoveItemType, + onClickCancel: () -> Unit, + onClickRemove: () -> Unit, +) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 36.dp, start = 20.dp, end = 20.dp, bottom = 20.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = stringResource(id = removeItemType.titleStringResourceId), + style = PokitTheme.typography.title2.copy(color = PokitTheme.colors.textPrimary) + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = stringResource(id = removeItemType.subStringResourceId), + style = PokitTheme.typography.body2Medium.copy(color = PokitTheme.colors.textSecondary), + textAlign = TextAlign.Center + ) + } + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp, start = 20.dp, end = 20.dp, bottom = 28.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + PokitButton( + text = stringResource(id = R.string.cancellation), + icon = null, + onClick = onClickCancel, + shape = PokitButtonShape.RECTANGLE, + type = PokitButtonType.SECONDARY, + size = PokitButtonSize.LARGE, + style = PokitButtonStyle.STROKE, + modifier = Modifier.weight(1f) + ) + + PokitButton( + text = stringResource(id = R.string.removal), + icon = null, + onClick = onClickRemove, + shape = PokitButtonShape.RECTANGLE, + type = PokitButtonType.PRIMARY, + size = PokitButtonSize.LARGE, + style = PokitButtonStyle.FILLED, + modifier = Modifier.weight(1f) + ) + } +} diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/removeItemBottomSheet/attributes/RemoveItemType.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/removeItemBottomSheet/attributes/RemoveItemType.kt new file mode 100644 index 00000000..70fd6b0a --- /dev/null +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/removeItemBottomSheet/attributes/RemoveItemType.kt @@ -0,0 +1,14 @@ +package pokitmons.pokit.core.ui.components.template.removeItemBottomSheet.attributes + +import pokitmons.pokit.core.ui.R + +enum class RemoveItemType(val titleStringResourceId: Int, val subStringResourceId: Int) { + POKIT( + titleStringResourceId = R.string.title_remove_pokit, + subStringResourceId = R.string.sub_remove_link + ), + LINK( + titleStringResourceId = R.string.title_remove_link, + subStringResourceId = R.string.sub_remove_link + ), +} diff --git a/core/ui/src/main/res/values/string.xml b/core/ui/src/main/res/values/string.xml index 3e0864d8..865a964e 100644 --- a/core/ui/src/main/res/values/string.xml +++ b/core/ui/src/main/res/values/string.xml @@ -2,4 +2,16 @@ 안읽음 링크 %d개 + + 포킷을 정말 삭제하시겠습니까? + 함께 저장한 모든 링크가 삭제되며,\n복구하실 수 없습니다. + 링크를 정말 삭제하시겠습니까? + 함께 저장한 모든 정보가 삭제되며,\n복구하실 수 없습니다. + + 취소 + 삭제 + + 공유하기 + 수정하기 + 삭제하기 \ No newline at end of file diff --git a/feature/addlink/src/main/java/com/strayalpaca/addlink/AddLinkScreen.kt b/feature/addlink/src/main/java/com/strayalpaca/addlink/AddLinkScreen.kt index 10e36ede..522fa644 100644 --- a/feature/addlink/src/main/java/com/strayalpaca/addlink/AddLinkScreen.kt +++ b/feature/addlink/src/main/java/com/strayalpaca/addlink/AddLinkScreen.kt @@ -302,7 +302,6 @@ fun AddLinkScreen( item = it, title = it.title, sub = stringResource(id = R.string.count_format, it.count), - onClickKebab = onClickSelectPokitItem, onClickItem = onClickSelectPokitItem, state = PokitListState.ACTIVE ) diff --git a/feature/addpokit/src/main/java/com/strayalpaca/addpokit/AddPokitScreen.kt b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/AddPokitScreen.kt index 120d66be..42bbef6b 100644 --- a/feature/addpokit/src/main/java/com/strayalpaca/addpokit/AddPokitScreen.kt +++ b/feature/addpokit/src/main/java/com/strayalpaca/addpokit/AddPokitScreen.kt @@ -206,7 +206,6 @@ fun AddPokitScreen( item = item, title = item.title, sub = stringResource(id = coreString.pokit_count_format, item.count), - onClickKebab = {}, onClickItem = {}, state = PokitListState.DEFAULT ) From 4b085456af30c8e2e68e12bb9651daa3a0abf4a3 Mon Sep 17 00:00:00 2001 From: yunsehwan Date: Tue, 16 Jul 2024 19:40:03 +0900 Subject: [PATCH 6/6] =?UTF-8?q?[FIX]=20#14=20ModifyBottomSheetContent=20?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EA=B3=B5=EC=9C=A0=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=ED=81=B4=EB=A6=AD=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=EA=B0=80=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8D=98=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../template/modifybottomsheet/ModifyBottomSheetContent.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/modifybottomsheet/ModifyBottomSheetContent.kt b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/modifybottomsheet/ModifyBottomSheetContent.kt index 850e6ddd..4ea40913 100644 --- a/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/modifybottomsheet/ModifyBottomSheetContent.kt +++ b/core/ui/src/main/java/pokitmons/pokit/core/ui/components/template/modifybottomsheet/ModifyBottomSheetContent.kt @@ -20,7 +20,7 @@ fun ModifyBottomSheetContent( ) { onClickShare?.let { onClickShare -> ModifyBottomSheetItem( - onClick = {}, + onClick = onClickShare, title = stringResource(id = R.string.share), painter = painterResource(id = R.drawable.icon_24_share) )