From e6620f177c41d0bbb1c44ce5c4bc9ba6be839a99 Mon Sep 17 00:00:00 2001 From: flash159483 Date: Thu, 1 Aug 2024 23:00:06 +0900 Subject: [PATCH 01/11] =?UTF-8?q?[FEAT]#68:=20=EC=8B=A0=EA=B3=A0=20Bottom?= =?UTF-8?q?=20sheet=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/kotlin/com/bff/wespot/ui/ReportBottomSheet.kt | 8 ++++---- .../main/java/com/bff/wespot/vote/screen/VotingScreen.kt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/ui/src/main/kotlin/com/bff/wespot/ui/ReportBottomSheet.kt b/core/ui/src/main/kotlin/com/bff/wespot/ui/ReportBottomSheet.kt index e9fcabc7..2a307294 100644 --- a/core/ui/src/main/kotlin/com/bff/wespot/ui/ReportBottomSheet.kt +++ b/core/ui/src/main/kotlin/com/bff/wespot/ui/ReportBottomSheet.kt @@ -22,7 +22,7 @@ import com.bff.wespot.designsystem.theme.StaticTypeScale fun ReportBottomSheet( closeSheet: () -> Unit, sheetState: SheetState = rememberModalBottomSheetState( - skipPartiallyExpanded = true, + skipPartiallyExpanded = true ), options: List, optionsClickable: List<() -> Unit>, @@ -35,13 +35,13 @@ fun ReportBottomSheet( Column( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 20.dp, vertical = 28.dp), + .padding(horizontal = 20.dp, vertical = 28.dp) ) { options.forEachIndexed { index, option -> ReportSection(text = option, onClick = optionsClickable[index]) if (index != options.size - 1) { HorizontalDivider( - color = Color(0xFF4F5157), + color = Color(0xFF4F5157) ) } } @@ -60,4 +60,4 @@ private fun ReportSection(text: String, onClick: () -> Unit) { ) { Text(text = text, style = StaticTypeScale.Default.body4) } -} +} \ No newline at end of file diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/screen/VotingScreen.kt b/feature/vote/src/main/java/com/bff/wespot/vote/screen/VotingScreen.kt index ae70dad0..f3769fb0 100644 --- a/feature/vote/src/main/java/com/bff/wespot/vote/screen/VotingScreen.kt +++ b/feature/vote/src/main/java/com/bff/wespot/vote/screen/VotingScreen.kt @@ -245,7 +245,7 @@ private fun VotingProgressScreen( } Text( text = - "${state.currentVote.voteUser.name}${stringResource(id = R.string.vote_question)}", + "${state.currentVote.voteUser.name}${stringResource(id = R.string.vote_question)}", style = StaticTypeScale.Default.header1, modifier = Modifier.padding(horizontal = 20.dp), ) From 550727a4cbdd0e690379b01e2e9d1d947ea3a354 Mon Sep 17 00:00:00 2001 From: flash159483 Date: Fri, 2 Aug 2024 00:41:23 +0900 Subject: [PATCH 02/11] =?UTF-8?q?[FEAT]#68:=20ktLint=20=ED=98=95=EC=8B=9D?= =?UTF-8?q?=20=EB=A7=9E=EC=B6=94=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/kotlin/com/bff/wespot/ui/ReportBottomSheet.kt | 8 ++++---- .../main/java/com/bff/wespot/vote/screen/VotingScreen.kt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/ui/src/main/kotlin/com/bff/wespot/ui/ReportBottomSheet.kt b/core/ui/src/main/kotlin/com/bff/wespot/ui/ReportBottomSheet.kt index 2a307294..e9fcabc7 100644 --- a/core/ui/src/main/kotlin/com/bff/wespot/ui/ReportBottomSheet.kt +++ b/core/ui/src/main/kotlin/com/bff/wespot/ui/ReportBottomSheet.kt @@ -22,7 +22,7 @@ import com.bff.wespot.designsystem.theme.StaticTypeScale fun ReportBottomSheet( closeSheet: () -> Unit, sheetState: SheetState = rememberModalBottomSheetState( - skipPartiallyExpanded = true + skipPartiallyExpanded = true, ), options: List, optionsClickable: List<() -> Unit>, @@ -35,13 +35,13 @@ fun ReportBottomSheet( Column( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 20.dp, vertical = 28.dp) + .padding(horizontal = 20.dp, vertical = 28.dp), ) { options.forEachIndexed { index, option -> ReportSection(text = option, onClick = optionsClickable[index]) if (index != options.size - 1) { HorizontalDivider( - color = Color(0xFF4F5157) + color = Color(0xFF4F5157), ) } } @@ -60,4 +60,4 @@ private fun ReportSection(text: String, onClick: () -> Unit) { ) { Text(text = text, style = StaticTypeScale.Default.body4) } -} \ No newline at end of file +} diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/screen/VotingScreen.kt b/feature/vote/src/main/java/com/bff/wespot/vote/screen/VotingScreen.kt index f3769fb0..ae70dad0 100644 --- a/feature/vote/src/main/java/com/bff/wespot/vote/screen/VotingScreen.kt +++ b/feature/vote/src/main/java/com/bff/wespot/vote/screen/VotingScreen.kt @@ -245,7 +245,7 @@ private fun VotingProgressScreen( } Text( text = - "${state.currentVote.voteUser.name}${stringResource(id = R.string.vote_question)}", + "${state.currentVote.voteUser.name}${stringResource(id = R.string.vote_question)}", style = StaticTypeScale.Default.header1, modifier = Modifier.padding(horizontal = 20.dp), ) From b8febcda414ca0ed1ef839c734aece11928ce51c Mon Sep 17 00:00:00 2001 From: flash159483 Date: Fri, 2 Aug 2024 23:40:21 +0900 Subject: [PATCH 03/11] =?UTF-8?q?[FEAT]#69:=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95=20=EC=84=9C=EB=B2=84=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wespot/model/common/BackgroundColor.kt | 11 +++++ .../com/bff/wespot/model/common/Character.kt | 11 +++++ .../wespot/model/{ => common}/ReportType.kt | 2 +- .../remote/model/common/BackgroundColorDto.kt | 28 +++++++++++++ .../data/remote/model/common/CharacterDto.kt | 27 +++++++++++++ .../ProfanityDto.kt} | 2 +- .../remote/model/{ => common}/ReportDto.kt | 4 +- .../data/remote/source/CommonDataSource.kt | 9 ++++- .../remote/source/CommonDataSourceImpl.kt | 22 +++++++++- .../data/repository/CommonRepositoryImpl.kt | 16 ++++++-- .../domain/repository/CommonRepository.kt | 6 ++- .../viewmodel/CharacterSettingViewModel.kt | 40 +++++++++++++++++++ 12 files changed, 166 insertions(+), 12 deletions(-) create mode 100644 core/model/src/main/kotlin/com/bff/wespot/model/common/BackgroundColor.kt create mode 100644 core/model/src/main/kotlin/com/bff/wespot/model/common/Character.kt rename core/model/src/main/kotlin/com/bff/wespot/model/{ => common}/ReportType.kt (58%) create mode 100644 data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/BackgroundColorDto.kt create mode 100644 data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/CharacterDto.kt rename data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/{MessageContentDto.kt => common/ProfanityDto.kt} (69%) rename data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/{ => common}/ReportDto.kt (58%) create mode 100644 feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/CharacterSettingViewModel.kt diff --git a/core/model/src/main/kotlin/com/bff/wespot/model/common/BackgroundColor.kt b/core/model/src/main/kotlin/com/bff/wespot/model/common/BackgroundColor.kt new file mode 100644 index 00000000..5441015f --- /dev/null +++ b/core/model/src/main/kotlin/com/bff/wespot/model/common/BackgroundColor.kt @@ -0,0 +1,11 @@ +package com.bff.wespot.model.common + +data class BackgroundColor( + val id: Int, + val color: String, + val name: String, +) + +data class BackgroundColorList( + val backgrounds: List +) \ No newline at end of file diff --git a/core/model/src/main/kotlin/com/bff/wespot/model/common/Character.kt b/core/model/src/main/kotlin/com/bff/wespot/model/common/Character.kt new file mode 100644 index 00000000..13c4d5ab --- /dev/null +++ b/core/model/src/main/kotlin/com/bff/wespot/model/common/Character.kt @@ -0,0 +1,11 @@ +package com.bff.wespot.model.common + +data class Character( + val id: Int, + val name: String, + val iconUrl: String, +) + +data class CharacterList( + val characters: List +) \ No newline at end of file diff --git a/core/model/src/main/kotlin/com/bff/wespot/model/ReportType.kt b/core/model/src/main/kotlin/com/bff/wespot/model/common/ReportType.kt similarity index 58% rename from core/model/src/main/kotlin/com/bff/wespot/model/ReportType.kt rename to core/model/src/main/kotlin/com/bff/wespot/model/common/ReportType.kt index c4405d7a..8fbd3f74 100644 --- a/core/model/src/main/kotlin/com/bff/wespot/model/ReportType.kt +++ b/core/model/src/main/kotlin/com/bff/wespot/model/common/ReportType.kt @@ -1,4 +1,4 @@ -package com.bff.wespot.model +package com.bff.wespot.model.common enum class ReportType { MESSAGE, diff --git a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/BackgroundColorDto.kt b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/BackgroundColorDto.kt new file mode 100644 index 00000000..34384b82 --- /dev/null +++ b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/BackgroundColorDto.kt @@ -0,0 +1,28 @@ +package com.bff.wespot.data.remote.model.common + +import com.bff.wespot.model.common.BackgroundColor +import com.bff.wespot.model.common.BackgroundColorList +import kotlinx.serialization.Serializable + +@Serializable +data class BackgroundColorDto( + val id: Int, + val color: String, + val name: String, +) { + fun toBackgroundColor() = BackgroundColor( + id = id, + color = color, + name = name, + ) + +} + +@Serializable +data class BackgroundColorListDto( + val backgrounds: List +) { + fun toBackgroundColorList() = BackgroundColorList( + backgrounds = backgrounds.map { it.toBackgroundColor() } + ) +} \ No newline at end of file diff --git a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/CharacterDto.kt b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/CharacterDto.kt new file mode 100644 index 00000000..e6ced2f8 --- /dev/null +++ b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/CharacterDto.kt @@ -0,0 +1,27 @@ +package com.bff.wespot.data.remote.model.common + +import com.bff.wespot.model.common.Character +import com.bff.wespot.model.common.CharacterList +import kotlinx.serialization.Serializable + +@Serializable +data class CharacterDto( + val id: Int, + val name: String, + val iconUrl: String, +) { + fun toCharacter() = Character( + id = id, + name = name, + iconUrl = iconUrl, + ) +} + +@Serializable +data class CharacterListDto( + val characters: List +) { + fun toCharacterList() = CharacterList( + characters = characters.map { it.toCharacter() } + ) +} \ No newline at end of file diff --git a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/MessageContentDto.kt b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/ProfanityDto.kt similarity index 69% rename from data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/MessageContentDto.kt rename to data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/ProfanityDto.kt index 8204c6c1..7e270f51 100644 --- a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/MessageContentDto.kt +++ b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/ProfanityDto.kt @@ -1,4 +1,4 @@ -package com.bff.wespot.data.remote.model +package com.bff.wespot.data.remote.model.common import kotlinx.serialization.Serializable diff --git a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/ReportDto.kt b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/ReportDto.kt similarity index 58% rename from data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/ReportDto.kt rename to data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/ReportDto.kt index aa1359dd..78b51b83 100644 --- a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/ReportDto.kt +++ b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/ReportDto.kt @@ -1,6 +1,6 @@ -package com.bff.wespot.data.remote.model +package com.bff.wespot.data.remote.model.common -import com.bff.wespot.model.ReportType +import com.bff.wespot.model.common.ReportType import kotlinx.serialization.Serializable @Serializable diff --git a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/CommonDataSource.kt b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/CommonDataSource.kt index 5b23369a..4d874083 100644 --- a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/CommonDataSource.kt +++ b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/CommonDataSource.kt @@ -1,9 +1,14 @@ package com.bff.wespot.data.remote.source -import com.bff.wespot.data.remote.model.ProfanityDto -import com.bff.wespot.data.remote.model.ReportDto +import com.bff.wespot.data.remote.model.common.BackgroundColorListDto +import com.bff.wespot.data.remote.model.common.CharacterListDto +import com.bff.wespot.data.remote.model.common.ProfanityDto +import com.bff.wespot.data.remote.model.common.ReportDto interface CommonDataSource { suspend fun checkProfanity(content: ProfanityDto): Result suspend fun sendReport(report: ReportDto): Result + suspend fun getCharacters(): Result + suspend fun getBackgroundColors(): Result + } \ No newline at end of file diff --git a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/CommonDataSourceImpl.kt b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/CommonDataSourceImpl.kt index ecc74e09..feea2e9a 100644 --- a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/CommonDataSourceImpl.kt +++ b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/CommonDataSourceImpl.kt @@ -1,7 +1,9 @@ package com.bff.wespot.data.remote.source -import com.bff.wespot.data.remote.model.ProfanityDto -import com.bff.wespot.data.remote.model.ReportDto +import com.bff.wespot.data.remote.model.common.BackgroundColorListDto +import com.bff.wespot.data.remote.model.common.CharacterListDto +import com.bff.wespot.data.remote.model.common.ProfanityDto +import com.bff.wespot.data.remote.model.common.ReportDto import com.bff.wespot.network.extensions.safeRequest import io.ktor.client.HttpClient import io.ktor.client.request.setBody @@ -29,4 +31,20 @@ class CommonDataSourceImpl @Inject constructor( setBody(report) } } + + override suspend fun getCharacters(): Result = + httpClient.safeRequest { + url { + method = HttpMethod.Get + path("users/signup/characters") + } + } + + override suspend fun getBackgroundColors(): Result = + httpClient.safeRequest { + url { + method = HttpMethod.Get + path("users/signup/backgrounds") + } + } } \ No newline at end of file diff --git a/data/src/main/kotlin/com/bff/wespot/data/repository/CommonRepositoryImpl.kt b/data/src/main/kotlin/com/bff/wespot/data/repository/CommonRepositoryImpl.kt index fb30dae0..00d41a9c 100644 --- a/data/src/main/kotlin/com/bff/wespot/data/repository/CommonRepositoryImpl.kt +++ b/data/src/main/kotlin/com/bff/wespot/data/repository/CommonRepositoryImpl.kt @@ -1,10 +1,12 @@ package com.bff.wespot.data.repository -import com.bff.wespot.data.remote.model.ProfanityDto -import com.bff.wespot.data.remote.model.ReportDto +import com.bff.wespot.data.remote.model.common.ProfanityDto +import com.bff.wespot.data.remote.model.common.ReportDto import com.bff.wespot.data.remote.source.CommonDataSource import com.bff.wespot.domain.repository.CommonRepository -import com.bff.wespot.model.ReportType +import com.bff.wespot.model.common.BackgroundColor +import com.bff.wespot.model.common.Character +import com.bff.wespot.model.common.ReportType import javax.inject.Inject class CommonRepositoryImpl @Inject constructor( @@ -15,4 +17,12 @@ class CommonRepositoryImpl @Inject constructor( override suspend fun sendReport(report: ReportType, targetId: Int): Result = commonDataSource.sendReport(ReportDto(report, targetId)) + + override suspend fun getCharacters(): Result> = + commonDataSource.getCharacters() + .map { it.toCharacterList().characters } + + override suspend fun getBackgroundColors(): Result> = + commonDataSource.getBackgroundColors() + .map { it.toBackgroundColorList().backgrounds } } \ No newline at end of file diff --git a/domain/src/main/kotlin/com/bff/wespot/domain/repository/CommonRepository.kt b/domain/src/main/kotlin/com/bff/wespot/domain/repository/CommonRepository.kt index 070641b2..ca329adf 100644 --- a/domain/src/main/kotlin/com/bff/wespot/domain/repository/CommonRepository.kt +++ b/domain/src/main/kotlin/com/bff/wespot/domain/repository/CommonRepository.kt @@ -1,8 +1,12 @@ package com.bff.wespot.domain.repository -import com.bff.wespot.model.ReportType +import com.bff.wespot.model.common.BackgroundColor +import com.bff.wespot.model.common.Character +import com.bff.wespot.model.common.ReportType interface CommonRepository { suspend fun checkProfanity(content: String): Result suspend fun sendReport(report: ReportType, targetId: Int): Result + suspend fun getCharacters(): Result> + suspend fun getBackgroundColors(): Result> } diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/CharacterSettingViewModel.kt b/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/CharacterSettingViewModel.kt new file mode 100644 index 00000000..17387ce9 --- /dev/null +++ b/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/CharacterSettingViewModel.kt @@ -0,0 +1,40 @@ +package com.bff.wespot.vote.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.bff.wespot.domain.repository.CommonRepository +import com.bff.wespot.model.common.BackgroundColor +import com.bff.wespot.model.common.Character +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.stateIn +import javax.inject.Inject + +@HiltViewModel +class CharacterSettingViewModel @Inject constructor( + private val commonRepository: CommonRepository +) : ViewModel() { + val characters: StateFlow> = flow { + commonRepository.getCharacters() + .onSuccess { + emit(it) + } + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = emptyList(), + ) + + val backgroundColor: StateFlow> = flow { + commonRepository.getBackgroundColors() + .onSuccess { + emit(it) + } + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = emptyList(), + ) +} \ No newline at end of file From 8720b485d42f0c35d6298838061968ecdae159dd Mon Sep 17 00:00:00 2001 From: flash159483 Date: Sat, 3 Aug 2024 00:33:56 +0900 Subject: [PATCH 04/11] =?UTF-8?q?[FEAT]#69:=20=EC=BA=90=EB=A6=AD=ED=84=B0?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95=20=ED=99=94=EB=A9=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/ui/build.gradle.kts | 1 + .../com/bff/wespot/ui/CharacterScreen.kt | 345 ++++++++++++++++++ core/ui/src/main/res/drawable/background.png | Bin 0 -> 633 bytes core/ui/src/main/res/drawable/character.png | Bin 0 -> 639 bytes core/ui/src/main/res/values/strings.xml | 4 + designsystem/src/main/res/values/strings.xml | 1 + 6 files changed, 351 insertions(+) create mode 100644 core/ui/src/main/kotlin/com/bff/wespot/ui/CharacterScreen.kt create mode 100644 core/ui/src/main/res/drawable/background.png create mode 100644 core/ui/src/main/res/drawable/character.png diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index ac9660c2..99ba3f40 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -16,4 +16,5 @@ dependencies { implementation(libs.kotlinx.collections.immutable) implementation(libs.coil.core) implementation(libs.coil.compose) + implementation(libs.timber) } diff --git a/core/ui/src/main/kotlin/com/bff/wespot/ui/CharacterScreen.kt b/core/ui/src/main/kotlin/com/bff/wespot/ui/CharacterScreen.kt new file mode 100644 index 00000000..4f8eece4 --- /dev/null +++ b/core/ui/src/main/kotlin/com/bff/wespot/ui/CharacterScreen.kt @@ -0,0 +1,345 @@ +package com.bff.wespot.ui + +import androidx.compose.foundation.BorderStroke +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.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.FilterChip +import androidx.compose.material3.FilterChipDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex +import coil.compose.AsyncImage +import coil.request.ImageRequest +import com.bff.wespot.designsystem.component.button.WSButton +import com.bff.wespot.designsystem.theme.StaticTypeScale +import com.bff.wespot.designsystem.theme.WeSpotThemeManager +import com.bff.wespot.model.common.BackgroundColor +import com.bff.wespot.model.common.Character +import com.bff.wespot.util.hexToColor + +@Composable +fun CharacterScreen( + name: String, + characterList: List, + colorList: List, +) { + require(characterList.isNotEmpty()) { "Character list should not be empty" } + require(colorList.isNotEmpty()) { "Color list should not be empty" } + + var character by remember { + mutableStateOf(characterList.first().iconUrl) + } + + var color by remember { + mutableStateOf(colorList.first().color) + } + + var chipSelection by remember { + mutableIntStateOf(CHARACTER) + } + + Column( + modifier = Modifier.fillMaxSize() + ) { + Text( + text = stringResource(R.string.select_character, name), + style = StaticTypeScale.Default.header1, + modifier = Modifier.padding(horizontal = 24.dp) + ) + + Spacer(modifier = Modifier.height(48.dp)) + + Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) { + Box( + modifier = Modifier + .size(120.dp) + .clip(CircleShape) + .background(hexToColor(color)), contentAlignment = Alignment.Center + ) { + AsyncImage( + model = ImageRequest + .Builder(LocalContext.current) + .data(character) + .build(), + contentDescription = stringResource(id = R.string.user_character_image), + modifier = Modifier.size(75.dp) + ) + } + } + + Spacer(modifier = Modifier.height(42.dp)) + + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) { + Column { + SelectionTypeChipGroup(chipSelection) { + chipSelection = it + } + Column( + modifier = Modifier + .fillMaxWidth() + .background(WeSpotThemeManager.colors.modalColor) + ) { + when (chipSelection) { + CHARACTER -> { + CharacterPickerBox( + selected = character, + characterList = characterList, + onClick = { + character = it + } + ) + } + + COLOR -> { + ColorPickerBox( + selected = color, + colorList = colorList, + onClick = { + color = it + } + ) + } + } + + WSButton(onClick = { }, text = stringResource(R.string.complete)) { + it.invoke() + } + } + } + } + } +} + +@Composable +private fun SelectionTypeChipGroup(index: Int, onClick: (Int) -> Unit) { + val screenWidth = LocalConfiguration.current.screenWidthDp.dp + + val chipWidth = remember { + (screenWidth - 64.dp) / 2 + } + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 22.dp, vertical = 24.dp), + horizontalArrangement = Arrangement.spacedBy(16.dp), + ) { + SelectionTypeChip( + text = stringResource(R.string.character), + isSelected = index == CHARACTER, + onClick = { + onClick(CHARACTER) + }, + icon = painterResource(id = R.drawable.character), + modifier = Modifier.width(chipWidth) + ) + + SelectionTypeChip( + text = stringResource(R.string.background), + isSelected = index == COLOR, + onClick = { + onClick(COLOR) + }, + icon = painterResource(id = R.drawable.background), + modifier = Modifier.width(chipWidth) + ) + } +} + +@Composable +private fun SelectionTypeChip( + text: String, + isSelected: Boolean, + onClick: () -> Unit, + icon: Painter, + modifier: Modifier = Modifier +) { + FilterChip( + shape = WeSpotThemeManager.shapes.extraLarge, + selected = isSelected, + onClick = onClick, + label = { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(10.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Image( + painter = icon, + contentDescription = text, + modifier = Modifier.size(20.dp), + colorFilter = ColorFilter.tint( + if (isSelected) { + Color(0xFFD9D9D9) + } else { + Color(0xFF76777D) + } + ) + ) + Text(text = text, style = StaticTypeScale.Default.body6) + } + }, + border = if (!isSelected) { + BorderStroke( + width = 1.dp, + color = WeSpotThemeManager.colors.disableIcnColor, + ) + } else { + null + }, + colors = FilterChipDefaults.filterChipColors( + containerColor = WeSpotThemeManager.colors.backgroundColor, + labelColor = WeSpotThemeManager.colors.disableIcnColor, + selectedContainerColor = WeSpotThemeManager.colors.secondaryBtnColor, + selectedLabelColor = Color(0xFFF7F7F8), + ), + modifier = modifier + ) +} + +@Composable +private fun CharacterPickerBox( + selected: String, + characterList: List, + onClick: (String) -> Unit +) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(24.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + text = stringResource(id = R.string.character), + style = StaticTypeScale.Default.body6 + ) + + LazyVerticalGrid( + modifier = Modifier + .fillMaxWidth(), + columns = GridCells.Fixed(4), + horizontalArrangement = Arrangement.spacedBy(24.dp), + verticalArrangement = Arrangement.spacedBy(24.dp), + contentPadding = PaddingValues(vertical = 12.dp) + ) { + items(characterList, key = { + it.id + }) { + Box( + modifier = Modifier + .size(60.dp) + .aspectRatio(1f) + .clip(CircleShape) + .clickable { onClick(it.iconUrl) } + .let { modifier -> + if (it.iconUrl == selected) { + modifier.background(WeSpotThemeManager.colors.badgeColor) + .border(2.dp, Color.White, CircleShape) + } else { + modifier + } + }, + contentAlignment = Alignment.Center + ) { + AsyncImage( + model = ImageRequest + .Builder(LocalContext.current) + .data(it.iconUrl) + .build(), + contentDescription = stringResource(id = R.string.user_character_image), + modifier = Modifier + .size(60.dp) + ) + } + } + } + } +} + +@Composable +private fun ColorPickerBox( + selected: String, + colorList: List, + onClick: (String) -> Unit +) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(24.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + text = stringResource(id = R.string.background), + style = StaticTypeScale.Default.body6 + ) + + LazyVerticalGrid( + modifier = Modifier + .fillMaxWidth(), + columns = GridCells.Fixed(4), + horizontalArrangement = Arrangement.spacedBy(24.dp), + verticalArrangement = Arrangement.spacedBy(24.dp), + contentPadding = PaddingValues(vertical = 12.dp) + ) { + items(colorList, key = { + it.id + }) { + Box( + modifier = Modifier + .size(60.dp) + .aspectRatio(1f) + .clip(CircleShape) + .background(hexToColor(it.color)) + .zIndex(1f) + .clickable { onClick(it.color) } + .let { modifier -> + if (it.color == selected) { + modifier.border(2.dp, Color.White, CircleShape) + } else { + modifier + } + }, + ) + } + } + } +} + + +private const val CHARACTER = 0 +private const val COLOR = 1 \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/background.png b/core/ui/src/main/res/drawable/background.png new file mode 100644 index 0000000000000000000000000000000000000000..86fad41118bdd47c4c7285638c0aaa7f5e2fc04b GIT binary patch literal 633 zcmV-<0*3vGP)4OAn69}1W~^F8E;T+;Cwg`Z9rt-iBe3Z zY@`G^z#?@z9v|NwpP5FOwKh8*k1JzLhSxGp)8e&wrV&M& zA#H6wB4+jURBdF!AyhcZ5_~nsVUEsF^))kPLs^C6IG!xnPX_nRNL2qdV{9JDWWV1p zm5P32I*4Z2IBSpw`tbcJL4k6|{+NVf(ppN2;$8^-XwT6WKyULg2~fPBNfOTxzrcLk z`BhhCP<-FrX^b0cuHx&al*;9}a#ib^^A_Azb&HwRSr-gS=J+^Rw7Rad(*}*$P!#c& zp+3vHHV^HAZUr$m_|nlbV5PO@yYw>Rze+C~Q(rdK*IGXSSucs>n0_Ge6UJ3P5&eSy zg8nTqWHDKE$6ceXZ2woyPju)z@o9b7h5X){bwhA+QIvJa(3{)obXvaPvuA$+6iXFf TnBIT=00000NkvXXu0mjfPe>Yt literal 0 HcmV?d00001 diff --git a/core/ui/src/main/res/drawable/character.png b/core/ui/src/main/res/drawable/character.png new file mode 100644 index 0000000000000000000000000000000000000000..893d1333a31d3881b230ecebce7b203f7ad572e8 GIT binary patch literal 639 zcmV-_0)YLAP)u(g@ccc&j*;%H@x4Lj4zz zhCB`6EfC5yozM0XPbM}d9!kI091gpP-4M-{;y2&m@oH6nDc zztct;; User Character Image + %1$s님을 잘 나타낼 수 있는 \n캐릭터를 선택해 주세요 + 캐릭터 고르기 + 배경 색 고르기 + 선택 완료 \ No newline at end of file diff --git a/designsystem/src/main/res/values/strings.xml b/designsystem/src/main/res/values/strings.xml index aa9f82be..20851f47 100644 --- a/designsystem/src/main/res/values/strings.xml +++ b/designsystem/src/main/res/values/strings.xml @@ -33,4 +33,5 @@ \n차단 처리된 쪽지입니다 \n신고 처리된 쪽지입니다 비속어가 포함되어 있어요 + 닫기 From b4f99836ccecaab4ac20811434bc57292cc29883 Mon Sep 17 00:00:00 2001 From: flash159483 Date: Sat, 3 Aug 2024 00:34:27 +0900 Subject: [PATCH 05/11] =?UTF-8?q?[FEAT]#69:=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/bff/wespot/AppNavGraphs.kt | 2 + .../com/bff/wespot/CommonNavGraphNavigator.kt | 9 +- .../bff/wespot/domain/util/DataStoreKey.kt | 1 + .../vote/screen/CharacterSettingScreen.kt | 88 +++++++++++++++++++ .../bff/wespot/vote/screen/VoteHomeScreen.kt | 21 +++++ .../bff/wespot/vote/screen/VotingScreen.kt | 2 +- .../bff/wespot/vote/state/home/VoteAction.kt | 1 + .../bff/wespot/vote/state/home/VoteUiState.kt | 1 + .../vote/viewmodel/VoteHomeViewModel.kt | 16 ++++ .../wespot/vote/viewmodel/VotingViewModel.kt | 2 +- feature/vote/src/main/res/values/strings.xml | 8 +- 11 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 feature/vote/src/main/java/com/bff/wespot/vote/screen/CharacterSettingScreen.kt diff --git a/app/src/main/kotlin/com/bff/wespot/AppNavGraphs.kt b/app/src/main/kotlin/com/bff/wespot/AppNavGraphs.kt index 1a358284..dfd03e7c 100644 --- a/app/src/main/kotlin/com/bff/wespot/AppNavGraphs.kt +++ b/app/src/main/kotlin/com/bff/wespot/AppNavGraphs.kt @@ -25,6 +25,7 @@ import com.bff.wespot.message.screen.destinations.MessageWriteScreenDestination import com.bff.wespot.message.screen.destinations.ReceiverSelectionScreenDestination import com.bff.wespot.message.screen.destinations.ReservedMessageScreenDestination import com.bff.wespot.message.viewmodel.SendViewModel +import com.bff.wespot.vote.screen.destinations.CharacterSettingScreenDestination import com.bff.wespot.vote.screen.destinations.IndividualVoteScreenDestination import com.bff.wespot.vote.screen.destinations.VoteHomeScreenDestination import com.bff.wespot.vote.screen.destinations.VoteResultScreenDestination @@ -52,6 +53,7 @@ object AppNavGraphs { VoteResultScreenDestination, VoteStorageScreenDestination, IndividualVoteScreenDestination, + CharacterSettingScreenDestination, ).routedIn(this) .associateBy { it.route } } diff --git a/app/src/main/kotlin/com/bff/wespot/CommonNavGraphNavigator.kt b/app/src/main/kotlin/com/bff/wespot/CommonNavGraphNavigator.kt index 69fd5c13..c051d4e8 100644 --- a/app/src/main/kotlin/com/bff/wespot/CommonNavGraphNavigator.kt +++ b/app/src/main/kotlin/com/bff/wespot/CommonNavGraphNavigator.kt @@ -27,6 +27,7 @@ import com.bff.wespot.message.screen.send.MessageWriteNavigator import com.bff.wespot.message.screen.send.MessageWriteScreenArgs import com.bff.wespot.message.screen.send.ReceiverSelectionNavigator import com.bff.wespot.message.screen.send.ReceiverSelectionScreenArgs +import com.bff.wespot.vote.screen.CharacterSettingNavigator import com.bff.wespot.vote.screen.IndividualVoteArgs import com.bff.wespot.vote.screen.IndividualVoteNavigator import com.bff.wespot.vote.screen.VoteNavigator @@ -34,6 +35,7 @@ import com.bff.wespot.vote.screen.VoteResultNavigator import com.bff.wespot.vote.screen.VoteResultScreenArgs import com.bff.wespot.vote.screen.VoteStorageNavigator import com.bff.wespot.vote.screen.VotingNavigator +import com.bff.wespot.vote.screen.destinations.CharacterSettingScreenDestination import com.bff.wespot.vote.screen.destinations.IndividualVoteScreenDestination import com.bff.wespot.vote.screen.destinations.VoteResultScreenDestination import com.bff.wespot.vote.screen.destinations.VoteStorageScreenDestination @@ -60,7 +62,8 @@ class CommonNavGraphNavigator( VoteResultNavigator, VoteStorageNavigator, ReservedMessageNavigator, - IndividualVoteNavigator { + IndividualVoteNavigator, + CharacterSettingNavigator { override fun navigateUp() { navController.navigateUp() } @@ -131,4 +134,8 @@ class CommonNavGraphNavigator( override fun navigateToIndividualVote(args: IndividualVoteArgs) { navController.navigate(IndividualVoteScreenDestination(args) within navGraph) } + + override fun navigateToCharacterScreen() { + navController.navigate(CharacterSettingScreenDestination within navGraph) + } } diff --git a/domain/src/main/kotlin/com/bff/wespot/domain/util/DataStoreKey.kt b/domain/src/main/kotlin/com/bff/wespot/domain/util/DataStoreKey.kt index 5c312cd0..616e2491 100644 --- a/domain/src/main/kotlin/com/bff/wespot/domain/util/DataStoreKey.kt +++ b/domain/src/main/kotlin/com/bff/wespot/domain/util/DataStoreKey.kt @@ -5,4 +5,5 @@ object DataStoreKey { const val REFRESH_TOKEN = "refresh_token" const val REFRESH_TOKEN_EXPIRED_AT = "refresh_token_expired_date" const val SIGN_UP_TOKEN = "signup_token" + const val SETTING_DIALOG = "setting_dialog" } diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/screen/CharacterSettingScreen.kt b/feature/vote/src/main/java/com/bff/wespot/vote/screen/CharacterSettingScreen.kt new file mode 100644 index 00000000..807ff746 --- /dev/null +++ b/feature/vote/src/main/java/com/bff/wespot/vote/screen/CharacterSettingScreen.kt @@ -0,0 +1,88 @@ +package com.bff.wespot.vote.screen + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.bff.wespot.designsystem.component.button.WSTextButton +import com.bff.wespot.designsystem.component.modal.WSDialog +import com.bff.wespot.ui.CharacterScreen +import com.bff.wespot.vote.R +import com.bff.wespot.vote.viewmodel.CharacterSettingViewModel +import com.ramcosta.composedestinations.annotation.Destination + +interface CharacterSettingNavigator { + fun navigateUp() +} + +@Destination +@Composable +fun CharacterSettingScreen( + navigator: CharacterSettingNavigator, + viewModel: CharacterSettingViewModel = hiltViewModel() +) { + val color by viewModel.backgroundColor.collectAsStateWithLifecycle() + val character by viewModel.characters.collectAsStateWithLifecycle() + + var showDialog by remember { + mutableStateOf(false) + } + + if (color.isEmpty() || character.isEmpty()) { + Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + CircularProgressIndicator() + } + return + } + Scaffold( + topBar = { + Box( + modifier = Modifier + .fillMaxWidth() + .height(60.dp) + .padding(horizontal = 12.dp), + contentAlignment = Alignment.CenterEnd + ) { + WSTextButton( + text = stringResource(id = com.bff.wespot.designsystem.R.string.close), + onClick = { + showDialog = true + } + ) + } + } + ) { + Box(modifier = Modifier.padding(it)) { + CharacterScreen(name = "", characterList = character, colorList = color) + } + } + + if (showDialog) { + WSDialog( + title = stringResource(R.string.quite_setting), + subTitle = stringResource(R.string.auto_select), + okButtonText = stringResource(R.string.yes_stop), + cancelButtonText = stringResource(id = com.bff.wespot.designsystem.R.string.close), + okButtonClick = navigator::navigateUp, + cancelButtonClick = { + showDialog = false + } + ) { + showDialog = false + } + } +} \ No newline at end of file diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/screen/VoteHomeScreen.kt b/feature/vote/src/main/java/com/bff/wespot/vote/screen/VoteHomeScreen.kt index 185d8de7..c2ea6edd 100644 --- a/feature/vote/src/main/java/com/bff/wespot/vote/screen/VoteHomeScreen.kt +++ b/feature/vote/src/main/java/com/bff/wespot/vote/screen/VoteHomeScreen.kt @@ -38,6 +38,7 @@ import com.bff.wespot.designsystem.component.banner.WSBanner import com.bff.wespot.designsystem.component.banner.WSBannerType import com.bff.wespot.designsystem.component.button.WSButton import com.bff.wespot.designsystem.component.indicator.WSHomeTabRow +import com.bff.wespot.designsystem.component.modal.WSDialog import com.bff.wespot.designsystem.theme.Gray100 import com.bff.wespot.designsystem.theme.StaticTypeScale import com.bff.wespot.designsystem.theme.WeSpotThemeManager @@ -54,6 +55,7 @@ import com.bff.wespot.vote.ui.VoteCard import com.bff.wespot.vote.viewmodel.VoteHomeViewModel import com.ramcosta.composedestinations.annotation.Destination import kotlinx.collections.immutable.persistentListOf +import kotlinx.coroutines.delay import org.orbitmvi.orbit.compose.collectAsState import java.time.LocalDate @@ -62,6 +64,7 @@ interface VoteNavigator { fun navigateToVotingScreen() fun navigateToVoteResultScreen(args: VoteResultScreenArgs) fun navigateToVoteStorageScreen() + fun navigateToCharacterScreen() } @Destination @@ -99,6 +102,19 @@ internal fun VoteHomeScreen( } } + if (state.showSettingDialog) { + WSDialog( + title = stringResource(R.string.show_profile_setting), + subTitle = stringResource(R.string.write_introduction), + okButtonText = stringResource(R.string.sure), + cancelButtonText = stringResource(R.string.next_time), + okButtonClick = { + voteNavigator.navigateToCharacterScreen() + }, + onDismissRequest = {} + ) + } + OnLifecycleEvent { owner, event -> when (event) { Lifecycle.Event.ON_RESUME -> { @@ -112,6 +128,11 @@ internal fun VoteHomeScreen( else -> {} } } + + LaunchedEffect(Unit) { + delay(100) + action(VoteAction.GetSettingDialogOption) + } } @Composable diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/screen/VotingScreen.kt b/feature/vote/src/main/java/com/bff/wespot/vote/screen/VotingScreen.kt index ae70dad0..68bece67 100644 --- a/feature/vote/src/main/java/com/bff/wespot/vote/screen/VotingScreen.kt +++ b/feature/vote/src/main/java/com/bff/wespot/vote/screen/VotingScreen.kt @@ -196,7 +196,7 @@ fun VotingScreen( title = stringResource(R.string.not_your_classmate), subTitle = stringResource(R.string.wrong_report), okButtonText = stringResource(R.string.it_is_not), - cancelButtonText = stringResource(R.string.close), + cancelButtonText = stringResource(com.bff.wespot.designsystem.R.string.close), okButtonClick = { action(VotingAction.SendReport(state.currentVote.voteUser.id)) showReportDialog = false diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/state/home/VoteAction.kt b/feature/vote/src/main/java/com/bff/wespot/vote/state/home/VoteAction.kt index 0b434860..6ca4d2db 100644 --- a/feature/vote/src/main/java/com/bff/wespot/vote/state/home/VoteAction.kt +++ b/feature/vote/src/main/java/com/bff/wespot/vote/state/home/VoteAction.kt @@ -5,4 +5,5 @@ sealed class VoteAction { data object EndDate : VoteAction() data class GetFirst(val date: String) : VoteAction() data class OnTabChanged(val index: Int) : VoteAction() + data object GetSettingDialogOption: VoteAction() } diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/state/home/VoteUiState.kt b/feature/vote/src/main/java/com/bff/wespot/vote/state/home/VoteUiState.kt index a1e95d93..0a3c9243 100644 --- a/feature/vote/src/main/java/com/bff/wespot/vote/state/home/VoteUiState.kt +++ b/feature/vote/src/main/java/com/bff/wespot/vote/state/home/VoteUiState.kt @@ -6,4 +6,5 @@ data class VoteUiState( val voteResults: List = emptyList(), val isLoading: Boolean = false, val selectedTabIndex: Int = 0, + val showSettingDialog: Boolean = false, ) diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/VoteHomeViewModel.kt b/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/VoteHomeViewModel.kt index d9ae90f8..825a79ab 100644 --- a/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/VoteHomeViewModel.kt +++ b/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/VoteHomeViewModel.kt @@ -3,7 +3,9 @@ package com.bff.wespot.vote.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.bff.wespot.common.util.toDateTimeString +import com.bff.wespot.domain.repository.DataStoreRepository import com.bff.wespot.domain.repository.vote.VoteRepository +import com.bff.wespot.domain.util.DataStoreKey import com.bff.wespot.vote.state.home.VoteAction import com.bff.wespot.vote.state.home.VoteSideEffect import com.bff.wespot.vote.state.home.VoteUiState @@ -30,6 +32,7 @@ import javax.inject.Inject class VoteHomeViewModel @Inject constructor( private val dispatcher: CoroutineDispatcher, private val voteRepository: VoteRepository, + private val dataStoreRepository: DataStoreRepository, ) : ViewModel(), ContainerHost { override val container = container(VoteUiState()) @@ -56,6 +59,7 @@ class VoteHomeViewModel @Inject constructor( is VoteAction.EndDate -> stopUpdatingDate() is VoteAction.GetFirst -> getFirstVoteResults(action.date) is VoteAction.OnTabChanged -> onTabChanged(action.index) + is VoteAction.GetSettingDialogOption -> getSetting() } } @@ -91,4 +95,16 @@ class VoteHomeViewModel @Inject constructor( private fun onTabChanged(index: Int) = intent { reduce { state.copy(selectedTabIndex = index) } } + + private fun getSetting() = intent { + viewModelScope.launch { + dataStoreRepository.getBoolean(DataStoreKey.SETTING_DIALOG).collect { + reduce { state.copy(showSettingDialog = true) } + if (!it) { + reduce { state.copy(showSettingDialog = !it) } + dataStoreRepository.saveBoolean(DataStoreKey.SETTING_DIALOG, true) + } + } + } + } } diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/VotingViewModel.kt b/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/VotingViewModel.kt index bac7e751..d9756dc4 100644 --- a/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/VotingViewModel.kt +++ b/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/VotingViewModel.kt @@ -5,7 +5,7 @@ import androidx.lifecycle.viewModelScope import com.bff.wespot.common.di.extensions.onNetworkFailure import com.bff.wespot.domain.repository.CommonRepository import com.bff.wespot.domain.repository.vote.VoteRepository -import com.bff.wespot.model.ReportType +import com.bff.wespot.model.common.ReportType import com.bff.wespot.model.vote.request.VoteResultUpload import com.bff.wespot.model.vote.request.VoteResultsUpload import com.bff.wespot.model.vote.response.VoteItem diff --git a/feature/vote/src/main/res/values/strings.xml b/feature/vote/src/main/res/values/strings.xml index 35616ee7..801e9408 100644 --- a/feature/vote/src/main/res/values/strings.xml +++ b/feature/vote/src/main/res/values/strings.xml @@ -40,5 +40,11 @@ 우리 반 친구가 아닌가요? 우리 반 친구를 잘못 신고한 것이 확인될 경우 서비스 이용에 제한이 생실 수 았어요 네 아니에요 - 닫기 + 프로필 설정을 해볼까요? + 친구들이 알아볼 수 있도록\n캐릭터 선택과 한 줄 소개 작성을 완료해 주세요 + 네 좋아요 + 다음에 할래요 + 프로필 설정을 중단하시나요? + 선택하셨던 캐릭터와 배경색은 저장되지 않으며\n기본 캐릭터와 배경색으로 자동 설정됩니다 + 네 중단할래요 \ No newline at end of file From e75caf78d471eb1f006085c63355a23ee60fa303 Mon Sep 17 00:00:00 2001 From: flash159483 Date: Sat, 3 Aug 2024 01:50:53 +0900 Subject: [PATCH 06/11] =?UTF-8?q?[FEAT]#69:=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20=ED=8E=B8=EC=A7=91=20=EC=84=9C=EB=B2=84=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wespot/model/common/BackgroundColor.kt | 4 +- .../com/bff/wespot/model/common/Character.kt | 4 +- .../remote/model/common/EditProfileDto.kt | 15 +++ .../data/remote/source/CommonDataSource.kt | 5 +- .../remote/source/CommonDataSourceImpl.kt | 10 ++ .../data/repository/CommonRepositoryImpl.kt | 17 +++ .../domain/repository/CommonRepository.kt | 5 + .../wespot/vote/screen/IntroductionScreen.kt | 107 ++++++++++++++++++ .../vote/state/profile/ProfileAction.kt | 7 ++ .../vote/state/profile/ProfileSideEffect.kt | 5 + .../vote/state/profile/ProfileUiState.kt | 8 ++ .../vote/viewmodel/IntroductionViewModel.kt | 96 ++++++++++++++++ 12 files changed, 278 insertions(+), 5 deletions(-) create mode 100644 data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/EditProfileDto.kt create mode 100644 feature/vote/src/main/java/com/bff/wespot/vote/screen/IntroductionScreen.kt create mode 100644 feature/vote/src/main/java/com/bff/wespot/vote/state/profile/ProfileAction.kt create mode 100644 feature/vote/src/main/java/com/bff/wespot/vote/state/profile/ProfileSideEffect.kt create mode 100644 feature/vote/src/main/java/com/bff/wespot/vote/state/profile/ProfileUiState.kt create mode 100644 feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/IntroductionViewModel.kt diff --git a/core/model/src/main/kotlin/com/bff/wespot/model/common/BackgroundColor.kt b/core/model/src/main/kotlin/com/bff/wespot/model/common/BackgroundColor.kt index 5441015f..3add64dc 100644 --- a/core/model/src/main/kotlin/com/bff/wespot/model/common/BackgroundColor.kt +++ b/core/model/src/main/kotlin/com/bff/wespot/model/common/BackgroundColor.kt @@ -7,5 +7,5 @@ data class BackgroundColor( ) data class BackgroundColorList( - val backgrounds: List -) \ No newline at end of file + val backgrounds: List, +) diff --git a/core/model/src/main/kotlin/com/bff/wespot/model/common/Character.kt b/core/model/src/main/kotlin/com/bff/wespot/model/common/Character.kt index 13c4d5ab..5d28f5e9 100644 --- a/core/model/src/main/kotlin/com/bff/wespot/model/common/Character.kt +++ b/core/model/src/main/kotlin/com/bff/wespot/model/common/Character.kt @@ -7,5 +7,5 @@ data class Character( ) data class CharacterList( - val characters: List -) \ No newline at end of file + val characters: List, +) diff --git a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/EditProfileDto.kt b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/EditProfileDto.kt new file mode 100644 index 00000000..1659a2da --- /dev/null +++ b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/model/common/EditProfileDto.kt @@ -0,0 +1,15 @@ +package com.bff.wespot.data.remote.model.common + +import kotlinx.serialization.Serializable + +@Serializable +data class EditProfileDto( + val introduction: String, + val profile: UpdateProfileDto, +) + +@Serializable +data class UpdateProfileDto( + val backgroundColor: String, + val iconUrl: String, +) \ No newline at end of file diff --git a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/CommonDataSource.kt b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/CommonDataSource.kt index 4d874083..4df13040 100644 --- a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/CommonDataSource.kt +++ b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/CommonDataSource.kt @@ -2,6 +2,7 @@ package com.bff.wespot.data.remote.source import com.bff.wespot.data.remote.model.common.BackgroundColorListDto import com.bff.wespot.data.remote.model.common.CharacterListDto +import com.bff.wespot.data.remote.model.common.EditProfileDto import com.bff.wespot.data.remote.model.common.ProfanityDto import com.bff.wespot.data.remote.model.common.ReportDto @@ -10,5 +11,7 @@ interface CommonDataSource { suspend fun sendReport(report: ReportDto): Result suspend fun getCharacters(): Result suspend fun getBackgroundColors(): Result - + suspend fun EditProfile( + profile: EditProfileDto + ): Result } \ No newline at end of file diff --git a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/CommonDataSourceImpl.kt b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/CommonDataSourceImpl.kt index feea2e9a..ac70665e 100644 --- a/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/CommonDataSourceImpl.kt +++ b/data-remote/src/main/kotlin/com/bff/wespot/data/remote/source/CommonDataSourceImpl.kt @@ -2,6 +2,7 @@ package com.bff.wespot.data.remote.source import com.bff.wespot.data.remote.model.common.BackgroundColorListDto import com.bff.wespot.data.remote.model.common.CharacterListDto +import com.bff.wespot.data.remote.model.common.EditProfileDto import com.bff.wespot.data.remote.model.common.ProfanityDto import com.bff.wespot.data.remote.model.common.ReportDto import com.bff.wespot.network.extensions.safeRequest @@ -47,4 +48,13 @@ class CommonDataSourceImpl @Inject constructor( path("users/signup/backgrounds") } } + + override suspend fun EditProfile(profile: EditProfileDto): Result = + httpClient.safeRequest { + url { + method = HttpMethod.Put + path("users/me") + setBody(profile) + } + } } \ No newline at end of file diff --git a/data/src/main/kotlin/com/bff/wespot/data/repository/CommonRepositoryImpl.kt b/data/src/main/kotlin/com/bff/wespot/data/repository/CommonRepositoryImpl.kt index 00d41a9c..a88d6e5e 100644 --- a/data/src/main/kotlin/com/bff/wespot/data/repository/CommonRepositoryImpl.kt +++ b/data/src/main/kotlin/com/bff/wespot/data/repository/CommonRepositoryImpl.kt @@ -1,7 +1,9 @@ package com.bff.wespot.data.repository +import com.bff.wespot.data.remote.model.common.EditProfileDto import com.bff.wespot.data.remote.model.common.ProfanityDto import com.bff.wespot.data.remote.model.common.ReportDto +import com.bff.wespot.data.remote.model.common.UpdateProfileDto import com.bff.wespot.data.remote.source.CommonDataSource import com.bff.wespot.domain.repository.CommonRepository import com.bff.wespot.model.common.BackgroundColor @@ -25,4 +27,19 @@ class CommonRepositoryImpl @Inject constructor( override suspend fun getBackgroundColors(): Result> = commonDataSource.getBackgroundColors() .map { it.toBackgroundColorList().backgrounds } + + override suspend fun EditProfile( + introduction: String, + backgroundColor: String, + iconUrl: String + ): Result = + commonDataSource.EditProfile( + EditProfileDto( + introduction, + UpdateProfileDto( + backgroundColor, + iconUrl + ) + ) + ) } \ No newline at end of file diff --git a/domain/src/main/kotlin/com/bff/wespot/domain/repository/CommonRepository.kt b/domain/src/main/kotlin/com/bff/wespot/domain/repository/CommonRepository.kt index ca329adf..189c3cc3 100644 --- a/domain/src/main/kotlin/com/bff/wespot/domain/repository/CommonRepository.kt +++ b/domain/src/main/kotlin/com/bff/wespot/domain/repository/CommonRepository.kt @@ -9,4 +9,9 @@ interface CommonRepository { suspend fun sendReport(report: ReportType, targetId: Int): Result suspend fun getCharacters(): Result> suspend fun getBackgroundColors(): Result> + suspend fun EditProfile( + introduction: String, + backgroundColor: String, + iconUrl: String, + ): Result } diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/screen/IntroductionScreen.kt b/feature/vote/src/main/java/com/bff/wespot/vote/screen/IntroductionScreen.kt new file mode 100644 index 00000000..d606a7ec --- /dev/null +++ b/feature/vote/src/main/java/com/bff/wespot/vote/screen/IntroductionScreen.kt @@ -0,0 +1,107 @@ +package com.bff.wespot.vote.screen + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.hilt.navigation.compose.hiltViewModel +import com.bff.wespot.designsystem.component.button.WSTextButton +import com.bff.wespot.designsystem.component.header.WSTopBar +import com.bff.wespot.designsystem.component.modal.WSDialog +import com.bff.wespot.vote.R +import com.bff.wespot.vote.state.profile.ProfileAction +import com.bff.wespot.vote.state.profile.ProfileSideEffect +import com.bff.wespot.vote.viewmodel.IntroductionViewModel +import com.ramcosta.composedestinations.annotation.Destination +import org.orbitmvi.orbit.compose.collectAsState +import org.orbitmvi.orbit.compose.collectSideEffect + +interface IntroductionNavigator { + fun navigateToVoteHome() + fun navigateUp() +} + +data class IntroductionArgs( + val backgroundColor: String, + val iconUrl: String, +) + +@OptIn(ExperimentalMaterial3Api::class) +@Destination( + navArgsDelegate = IntroductionArgs::class, +) +@Composable +fun IntroductionScreen( + navigator: IntroductionNavigator, + viewModel: IntroductionViewModel = hiltViewModel(), +) { + val state by viewModel.collectAsState() + val action = viewModel::onAction + + var showDialog by remember { + mutableStateOf(false) + } + + viewModel.collectSideEffect { + when (it) { + is ProfileSideEffect.NavigateToVoteHome -> navigator.navigateToVoteHome() + } + } + + Scaffold( + topBar = { + WSTopBar( + title = "", + canNavigateBack = true, + navigateUp = navigator::navigateUp, + action = { + WSTextButton( + text = stringResource(id = com.bff.wespot.designsystem.R.string.close), + onClick = { showDialog = true }, + ) + }, + ) + }, + ) { + Box(modifier = Modifier.padding(it)) { + com.bff.wespot.ui.IntroductionScreen( + name = "", + introduction = state.introduction, + onTextChange = { + action(ProfileAction.UpdateIntroduction(introduction = it)) + }, + onEditComplete = { + action(ProfileAction.EditProfile) + }, + error = state.hasProfanity, + ) + } + } + + if (showDialog) { + WSDialog( + title = stringResource(R.string.quite_setting), + subTitle = stringResource(R.string.auto_select), + okButtonText = stringResource(R.string.yes_stop), + cancelButtonText = stringResource(id = com.bff.wespot.designsystem.R.string.close), + okButtonClick = navigator::navigateToVoteHome, + cancelButtonClick = { + showDialog = false + }, + ) { + showDialog = false + } + } + + LaunchedEffect(Unit) { + action(ProfileAction.StartIntroduction) + } +} diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/state/profile/ProfileAction.kt b/feature/vote/src/main/java/com/bff/wespot/vote/state/profile/ProfileAction.kt new file mode 100644 index 00000000..9984ef3e --- /dev/null +++ b/feature/vote/src/main/java/com/bff/wespot/vote/state/profile/ProfileAction.kt @@ -0,0 +1,7 @@ +package com.bff.wespot.vote.state.profile + +sealed class ProfileAction { + data class UpdateIntroduction(val introduction: String) : ProfileAction() + data object StartIntroduction : ProfileAction() + data object EditProfile : ProfileAction() +} diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/state/profile/ProfileSideEffect.kt b/feature/vote/src/main/java/com/bff/wespot/vote/state/profile/ProfileSideEffect.kt new file mode 100644 index 00000000..16bbfc7d --- /dev/null +++ b/feature/vote/src/main/java/com/bff/wespot/vote/state/profile/ProfileSideEffect.kt @@ -0,0 +1,5 @@ +package com.bff.wespot.vote.state.profile + +sealed class ProfileSideEffect { + data object NavigateToVoteHome : ProfileSideEffect() +} diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/state/profile/ProfileUiState.kt b/feature/vote/src/main/java/com/bff/wespot/vote/state/profile/ProfileUiState.kt new file mode 100644 index 00000000..9e82376b --- /dev/null +++ b/feature/vote/src/main/java/com/bff/wespot/vote/state/profile/ProfileUiState.kt @@ -0,0 +1,8 @@ +package com.bff.wespot.vote.state.profile + +data class ProfileUiState( + val introduction: String = "", + val iconUrl: String, + val backgroundColor: String, + val hasProfanity: Boolean = false, +) diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/IntroductionViewModel.kt b/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/IntroductionViewModel.kt new file mode 100644 index 00000000..154c8de1 --- /dev/null +++ b/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/IntroductionViewModel.kt @@ -0,0 +1,96 @@ +package com.bff.wespot.vote.viewmodel + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.bff.wespot.domain.repository.CommonRepository +import com.bff.wespot.domain.usecase.CheckProfanityUseCase +import com.bff.wespot.vote.state.profile.ProfileAction +import com.bff.wespot.vote.state.profile.ProfileSideEffect +import com.bff.wespot.vote.state.profile.ProfileUiState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.distinctUntilChanged +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 +import javax.inject.Inject + +@HiltViewModel +class IntroductionViewModel @Inject constructor( + private val savedStateHandle: SavedStateHandle, + private val commonRepository: CommonRepository, + private val dispatcher: CoroutineDispatcher, + private val checkProfanityUseCase: CheckProfanityUseCase, +) : ViewModel(), ContainerHost { + override val container: Container = + container( + ProfileUiState( + backgroundColor = savedStateHandle["backgroundColor"] ?: "", + iconUrl = savedStateHandle["iconUrl"] ?: "", + ), + ) + + private val userInput = MutableStateFlow("") + + fun onAction(action: ProfileAction) { + when (action) { + is ProfileAction.UpdateIntroduction -> updateIntroduction(action.introduction) + is ProfileAction.StartIntroduction -> startIntroduction() + is ProfileAction.EditProfile -> editProfile() + } + } + + private fun updateIntroduction(introduction: String) = intent { + reduce { + if (introduction.length > 20) { + return@reduce state + } + + userInput.value = introduction + state.copy( + introduction = introduction, + ) + } + } + + private fun startIntroduction() = intent { + viewModelScope.launch(dispatcher) { + userInput + .debounce(INPUT_DEBOUNCE_TIME) + .distinctUntilChanged() + .collect { + runCatching { + val result = checkProfanityUseCase(it) + reduce { + state.copy( + hasProfanity = result, + ) + } + } + } + } + } + + private fun editProfile() = intent { + viewModelScope.launch(dispatcher) { + commonRepository.EditProfile( + introduction = state.introduction, + backgroundColor = state.backgroundColor, + iconUrl = state.iconUrl, + ).onSuccess { + postSideEffect(ProfileSideEffect.NavigateToVoteHome) + } + } + } + + companion object { + private const val INPUT_DEBOUNCE_TIME = 500L + } +} From dddd8b38b89803ba9abc77c9019404c664893c14 Mon Sep 17 00:00:00 2001 From: flash159483 Date: Sat, 3 Aug 2024 01:52:07 +0900 Subject: [PATCH 07/11] =?UTF-8?q?[FEAT]#69:=20=EC=9E=90=EA=B8=B0=EC=86=8C?= =?UTF-8?q?=EA=B0=9C=20=ED=99=94=EB=A9=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/bff/wespot/ui/IntroductionScreen.kt | 86 +++++++++++++++++++ core/ui/src/main/res/values/strings.xml | 3 + .../designsystem/component/header/WSTopBar.kt | 2 - 3 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 core/ui/src/main/kotlin/com/bff/wespot/ui/IntroductionScreen.kt diff --git a/core/ui/src/main/kotlin/com/bff/wespot/ui/IntroductionScreen.kt b/core/ui/src/main/kotlin/com/bff/wespot/ui/IntroductionScreen.kt new file mode 100644 index 00000000..0a2301d7 --- /dev/null +++ b/core/ui/src/main/kotlin/com/bff/wespot/ui/IntroductionScreen.kt @@ -0,0 +1,86 @@ +package com.bff.wespot.ui + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.bff.wespot.designsystem.component.button.WSButton +import com.bff.wespot.designsystem.component.input.WsTextField +import com.bff.wespot.designsystem.theme.StaticTypeScale +import kotlinx.coroutines.delay + +@Composable +fun IntroductionScreen( + name: String, + introduction: String, + onTextChange: (String) -> Unit, + onEditComplete: () -> Unit, + error: Boolean, +) { + val keyboard = LocalSoftwareKeyboardController.current + val focusRequester = remember { FocusRequester() } + + Column( + modifier = Modifier + .fillMaxSize(), + ) { + Text( + text = stringResource(R.string.introduce_to_friend, name), + style = StaticTypeScale.Default.header1, + modifier = Modifier.padding(horizontal = 30.dp), + ) + + Spacer(modifier = Modifier.height(10.dp)) + + Box(modifier = Modifier.padding(horizontal = 20.dp)) { + WsTextField( + value = introduction, + onValueChange = onTextChange, + placeholder = stringResource(R.string.introduction_placeholder), + focusRequester = focusRequester, + singleLine = true, + ) + } + + Box(modifier = Modifier.fillMaxWidth().padding(20.dp), contentAlignment = Alignment.CenterEnd) { + LetterCountIndicator(currentCount = introduction.length, maxCount = 20) + } + + Box( + modifier = Modifier + .fillMaxSize() + .imePadding(), + contentAlignment = Alignment.BottomCenter, + ) { + WSButton( + onClick = { + onEditComplete() + }, + enabled = introduction.isNotEmpty() && !error, + text = stringResource(R.string.edit_complete), + ) { + it() + } + } + } + + LaunchedEffect(focusRequester) { + focusRequester.requestFocus() + delay(10) + keyboard?.show() + } +} diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml index 87d5efcf..aec778e9 100644 --- a/core/ui/src/main/res/values/strings.xml +++ b/core/ui/src/main/res/values/strings.xml @@ -5,4 +5,7 @@ 캐릭터 고르기 배경 색 고르기 선택 완료 + 친구들에게 %1$s을 소개하는 한 줄을 작성해주세요 + 안녕 나는 1반의 비타민 + 작성 완료 \ No newline at end of file diff --git a/designsystem/src/main/kotlin/com/bff/wespot/designsystem/component/header/WSTopBar.kt b/designsystem/src/main/kotlin/com/bff/wespot/designsystem/component/header/WSTopBar.kt index d8d63ab8..67e26a1d 100644 --- a/designsystem/src/main/kotlin/com/bff/wespot/designsystem/component/header/WSTopBar.kt +++ b/designsystem/src/main/kotlin/com/bff/wespot/designsystem/component/header/WSTopBar.kt @@ -2,7 +2,6 @@ package com.bff.wespot.designsystem.component.header import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.RowScope -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons @@ -63,7 +62,6 @@ fun WSTopBar( }, actions = { action(StaticTypeScale.Default.body4) - Spacer(modifier = Modifier.padding(end = 12.dp)) }, scrollBehavior = scrollBehavior, colors = TopAppBarDefaults.topAppBarColors( From 8081c75a147ef3f1ab01312e0cd2f702d91f5420 Mon Sep 17 00:00:00 2001 From: flash159483 Date: Sat, 3 Aug 2024 01:52:25 +0900 Subject: [PATCH 08/11] =?UTF-8?q?[FEAT]#69:=20=EC=9E=90=EA=B8=B0=EC=86=8C?= =?UTF-8?q?=EA=B0=9C=20=ED=99=94=EB=A9=B4=20=EC=9D=B4=EB=8F=99=EB=B0=8F=20?= =?UTF-8?q?=ED=8E=B8=EC=A7=91=20=EC=A0=9C=EC=B6=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 6 +- .../kotlin/com/bff/wespot/AppNavGraphs.kt | 2 + .../com/bff/wespot/CommonNavGraphNavigator.kt | 10 ++- .../com/bff/wespot/ui/CharacterScreen.kt | 63 ++++++++++--------- .../vote/screen/CharacterSettingScreen.kt | 26 +++++--- .../bff/wespot/vote/screen/VoteHomeScreen.kt | 8 ++- .../bff/wespot/vote/state/home/VoteAction.kt | 3 +- .../viewmodel/CharacterSettingViewModel.kt | 4 +- .../vote/viewmodel/VoteHomeViewModel.kt | 6 +- 9 files changed, 83 insertions(+), 45 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fcc3e80d..930cd752 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + + android:exported="false" + android:windowSoftInputMode="adjustResize"/> , colorList: List, + navigateToNext: (String, String) -> Unit, ) { require(characterList.isNotEmpty()) { "Character list should not be empty" } require(colorList.isNotEmpty()) { "Color list should not be empty" } @@ -74,12 +75,12 @@ fun CharacterScreen( } Column( - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), ) { Text( text = stringResource(R.string.select_character, name), style = StaticTypeScale.Default.header1, - modifier = Modifier.padding(horizontal = 24.dp) + modifier = Modifier.padding(horizontal = 24.dp), ) Spacer(modifier = Modifier.height(48.dp)) @@ -89,7 +90,8 @@ fun CharacterScreen( modifier = Modifier .size(120.dp) .clip(CircleShape) - .background(hexToColor(color)), contentAlignment = Alignment.Center + .background(hexToColor(color)), + contentAlignment = Alignment.Center, ) { AsyncImage( model = ImageRequest @@ -97,7 +99,7 @@ fun CharacterScreen( .data(character) .build(), contentDescription = stringResource(id = R.string.user_character_image), - modifier = Modifier.size(75.dp) + modifier = Modifier.size(75.dp), ) } } @@ -112,7 +114,7 @@ fun CharacterScreen( Column( modifier = Modifier .fillMaxWidth() - .background(WeSpotThemeManager.colors.modalColor) + .background(WeSpotThemeManager.colors.modalColor), ) { when (chipSelection) { CHARACTER -> { @@ -121,7 +123,7 @@ fun CharacterScreen( characterList = characterList, onClick = { character = it - } + }, ) } @@ -131,12 +133,17 @@ fun CharacterScreen( colorList = colorList, onClick = { color = it - } + }, ) } } - WSButton(onClick = { }, text = stringResource(R.string.complete)) { + WSButton( + onClick = { + navigateToNext(character, color) + }, + text = stringResource(R.string.complete), + ) { it.invoke() } } @@ -166,7 +173,7 @@ private fun SelectionTypeChipGroup(index: Int, onClick: (Int) -> Unit) { onClick(CHARACTER) }, icon = painterResource(id = R.drawable.character), - modifier = Modifier.width(chipWidth) + modifier = Modifier.width(chipWidth), ) SelectionTypeChip( @@ -176,7 +183,7 @@ private fun SelectionTypeChipGroup(index: Int, onClick: (Int) -> Unit) { onClick(COLOR) }, icon = painterResource(id = R.drawable.background), - modifier = Modifier.width(chipWidth) + modifier = Modifier.width(chipWidth), ) } } @@ -187,7 +194,7 @@ private fun SelectionTypeChip( isSelected: Boolean, onClick: () -> Unit, icon: Painter, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { FilterChip( shape = WeSpotThemeManager.shapes.extraLarge, @@ -197,7 +204,7 @@ private fun SelectionTypeChip( Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(10.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp) + horizontalArrangement = Arrangement.spacedBy(8.dp), ) { Image( painter = icon, @@ -208,8 +215,8 @@ private fun SelectionTypeChip( Color(0xFFD9D9D9) } else { Color(0xFF76777D) - } - ) + }, + ), ) Text(text = text, style = StaticTypeScale.Default.body6) } @@ -228,7 +235,7 @@ private fun SelectionTypeChip( selectedContainerColor = WeSpotThemeManager.colors.secondaryBtnColor, selectedLabelColor = Color(0xFFF7F7F8), ), - modifier = modifier + modifier = modifier, ) } @@ -236,17 +243,17 @@ private fun SelectionTypeChip( private fun CharacterPickerBox( selected: String, characterList: List, - onClick: (String) -> Unit + onClick: (String) -> Unit, ) { Column( modifier = Modifier .fillMaxWidth() .padding(24.dp), - verticalArrangement = Arrangement.spacedBy(12.dp) + verticalArrangement = Arrangement.spacedBy(12.dp), ) { Text( text = stringResource(id = R.string.character), - style = StaticTypeScale.Default.body6 + style = StaticTypeScale.Default.body6, ) LazyVerticalGrid( @@ -255,7 +262,7 @@ private fun CharacterPickerBox( columns = GridCells.Fixed(4), horizontalArrangement = Arrangement.spacedBy(24.dp), verticalArrangement = Arrangement.spacedBy(24.dp), - contentPadding = PaddingValues(vertical = 12.dp) + contentPadding = PaddingValues(vertical = 12.dp), ) { items(characterList, key = { it.id @@ -268,13 +275,14 @@ private fun CharacterPickerBox( .clickable { onClick(it.iconUrl) } .let { modifier -> if (it.iconUrl == selected) { - modifier.background(WeSpotThemeManager.colors.badgeColor) + modifier + .background(WeSpotThemeManager.colors.badgeColor) .border(2.dp, Color.White, CircleShape) } else { modifier } }, - contentAlignment = Alignment.Center + contentAlignment = Alignment.Center, ) { AsyncImage( model = ImageRequest @@ -283,7 +291,7 @@ private fun CharacterPickerBox( .build(), contentDescription = stringResource(id = R.string.user_character_image), modifier = Modifier - .size(60.dp) + .size(60.dp), ) } } @@ -295,17 +303,17 @@ private fun CharacterPickerBox( private fun ColorPickerBox( selected: String, colorList: List, - onClick: (String) -> Unit + onClick: (String) -> Unit, ) { Column( modifier = Modifier .fillMaxWidth() .padding(24.dp), - verticalArrangement = Arrangement.spacedBy(12.dp) + verticalArrangement = Arrangement.spacedBy(12.dp), ) { Text( text = stringResource(id = R.string.background), - style = StaticTypeScale.Default.body6 + style = StaticTypeScale.Default.body6, ) LazyVerticalGrid( @@ -314,7 +322,7 @@ private fun ColorPickerBox( columns = GridCells.Fixed(4), horizontalArrangement = Arrangement.spacedBy(24.dp), verticalArrangement = Arrangement.spacedBy(24.dp), - contentPadding = PaddingValues(vertical = 12.dp) + contentPadding = PaddingValues(vertical = 12.dp), ) { items(colorList, key = { it.id @@ -340,6 +348,5 @@ private fun ColorPickerBox( } } - private const val CHARACTER = 0 -private const val COLOR = 1 \ No newline at end of file +private const val COLOR = 1 diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/screen/CharacterSettingScreen.kt b/feature/vote/src/main/java/com/bff/wespot/vote/screen/CharacterSettingScreen.kt index 807ff746..b678189a 100644 --- a/feature/vote/src/main/java/com/bff/wespot/vote/screen/CharacterSettingScreen.kt +++ b/feature/vote/src/main/java/com/bff/wespot/vote/screen/CharacterSettingScreen.kt @@ -27,13 +27,14 @@ import com.ramcosta.composedestinations.annotation.Destination interface CharacterSettingNavigator { fun navigateUp() + fun navigateToIntroduction(args: IntroductionArgs) } @Destination @Composable fun CharacterSettingScreen( navigator: CharacterSettingNavigator, - viewModel: CharacterSettingViewModel = hiltViewModel() + viewModel: CharacterSettingViewModel = hiltViewModel(), ) { val color by viewModel.backgroundColor.collectAsStateWithLifecycle() val character by viewModel.characters.collectAsStateWithLifecycle() @@ -55,19 +56,30 @@ fun CharacterSettingScreen( .fillMaxWidth() .height(60.dp) .padding(horizontal = 12.dp), - contentAlignment = Alignment.CenterEnd + contentAlignment = Alignment.CenterEnd, ) { WSTextButton( text = stringResource(id = com.bff.wespot.designsystem.R.string.close), onClick = { showDialog = true - } + }, ) } - } + }, ) { Box(modifier = Modifier.padding(it)) { - CharacterScreen(name = "", characterList = character, colorList = color) + CharacterScreen( + name = "", + characterList = character, + colorList = color, + ) { iconUrl, color -> + navigator.navigateToIntroduction( + IntroductionArgs( + backgroundColor = color, + iconUrl = iconUrl, + ), + ) + } } } @@ -80,9 +92,9 @@ fun CharacterSettingScreen( okButtonClick = navigator::navigateUp, cancelButtonClick = { showDialog = false - } + }, ) { showDialog = false } } -} \ No newline at end of file +} diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/screen/VoteHomeScreen.kt b/feature/vote/src/main/java/com/bff/wespot/vote/screen/VoteHomeScreen.kt index c2ea6edd..33ba778d 100644 --- a/feature/vote/src/main/java/com/bff/wespot/vote/screen/VoteHomeScreen.kt +++ b/feature/vote/src/main/java/com/bff/wespot/vote/screen/VoteHomeScreen.kt @@ -110,8 +110,11 @@ internal fun VoteHomeScreen( cancelButtonText = stringResource(R.string.next_time), okButtonClick = { voteNavigator.navigateToCharacterScreen() + action(VoteAction.ChangeSettingDialog(false)) + }, + onDismissRequest = { + action(VoteAction.ChangeSettingDialog(false)) }, - onDismissRequest = {} ) } @@ -130,7 +133,7 @@ internal fun VoteHomeScreen( } LaunchedEffect(Unit) { - delay(100) + delay(EDIT_POPUP_TIME) action(VoteAction.GetSettingDialogOption) } } @@ -324,3 +327,4 @@ private fun CardResultContent( private const val HOME_SCREEN = 0 private const val RESULT_SCREEN = 1 +private const val EDIT_POPUP_TIME = 5_000L diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/state/home/VoteAction.kt b/feature/vote/src/main/java/com/bff/wespot/vote/state/home/VoteAction.kt index 6ca4d2db..cdfae1fb 100644 --- a/feature/vote/src/main/java/com/bff/wespot/vote/state/home/VoteAction.kt +++ b/feature/vote/src/main/java/com/bff/wespot/vote/state/home/VoteAction.kt @@ -5,5 +5,6 @@ sealed class VoteAction { data object EndDate : VoteAction() data class GetFirst(val date: String) : VoteAction() data class OnTabChanged(val index: Int) : VoteAction() - data object GetSettingDialogOption: VoteAction() + data object GetSettingDialogOption : VoteAction() + data class ChangeSettingDialog(val showDialog: Boolean) : VoteAction() } diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/CharacterSettingViewModel.kt b/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/CharacterSettingViewModel.kt index 17387ce9..88fef975 100644 --- a/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/CharacterSettingViewModel.kt +++ b/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/CharacterSettingViewModel.kt @@ -14,7 +14,7 @@ import javax.inject.Inject @HiltViewModel class CharacterSettingViewModel @Inject constructor( - private val commonRepository: CommonRepository + private val commonRepository: CommonRepository, ) : ViewModel() { val characters: StateFlow> = flow { commonRepository.getCharacters() @@ -37,4 +37,4 @@ class CharacterSettingViewModel @Inject constructor( started = SharingStarted.WhileSubscribed(5000), initialValue = emptyList(), ) -} \ No newline at end of file +} diff --git a/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/VoteHomeViewModel.kt b/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/VoteHomeViewModel.kt index 825a79ab..5634f995 100644 --- a/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/VoteHomeViewModel.kt +++ b/feature/vote/src/main/java/com/bff/wespot/vote/viewmodel/VoteHomeViewModel.kt @@ -60,6 +60,7 @@ class VoteHomeViewModel @Inject constructor( is VoteAction.GetFirst -> getFirstVoteResults(action.date) is VoteAction.OnTabChanged -> onTabChanged(action.index) is VoteAction.GetSettingDialogOption -> getSetting() + is VoteAction.ChangeSettingDialog -> changeSettingDialog(action.showDialog) } } @@ -99,7 +100,6 @@ class VoteHomeViewModel @Inject constructor( private fun getSetting() = intent { viewModelScope.launch { dataStoreRepository.getBoolean(DataStoreKey.SETTING_DIALOG).collect { - reduce { state.copy(showSettingDialog = true) } if (!it) { reduce { state.copy(showSettingDialog = !it) } dataStoreRepository.saveBoolean(DataStoreKey.SETTING_DIALOG, true) @@ -107,4 +107,8 @@ class VoteHomeViewModel @Inject constructor( } } } + + private fun changeSettingDialog(showDialog: Boolean) = intent { + reduce { state.copy(showSettingDialog = showDialog) } + } } From 28b1bd90c17be1f64610684eec3cc13316db002c Mon Sep 17 00:00:00 2001 From: flash159483 Date: Sun, 4 Aug 2024 18:33:50 +0900 Subject: [PATCH 09/11] =?UTF-8?q?[FEAT]#69:=20MultiLineText=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/bff/wespot/ui/MultiLineText.kt | 40 +++++++++---------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/core/ui/src/main/kotlin/com/bff/wespot/ui/MultiLineText.kt b/core/ui/src/main/kotlin/com/bff/wespot/ui/MultiLineText.kt index 8ec82dc2..b9a75de7 100644 --- a/core/ui/src/main/kotlin/com/bff/wespot/ui/MultiLineText.kt +++ b/core/ui/src/main/kotlin/com/bff/wespot/ui/MultiLineText.kt @@ -1,19 +1,14 @@ package com.bff.wespot.ui -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.layout.layout import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.Dp +import androidx.compose.ui.text.style.TextOverflow.Companion.Ellipsis @Composable fun MultiLineText( @@ -23,22 +18,23 @@ fun MultiLineText( modifier: Modifier = Modifier, textAlign: TextAlign = TextAlign.Start, ) { - var height by remember { mutableStateOf(Dp.Unspecified) } - val density = LocalDensity.current - + val textMeasurer = rememberTextMeasurer() Text( - modifier = modifier - .height(height) - .wrapContentHeight(), text = text, style = style, - onTextLayout = { - val newHeight = with(density) { it.size.height.toDp() } - if (it.lineCount < line) { - height = newHeight * (line / it.lineCount) - } - }, - overflow = TextOverflow.Ellipsis, + modifier = modifier + .fillMaxWidth() + .layout { measurable, constraints -> + val textLayoutResult = textMeasurer.measure("\n", constraints = constraints) + val layoutHeight = textLayoutResult.size.height + + val placeable = measurable.measure(constraints) + + layout(placeable.width, layoutHeight) { + placeable.placeRelative(0, 0) + } + }, + overflow = Ellipsis, maxLines = line, textAlign = textAlign, ) From 5ed5bed6d93763dbae170525fafe18dc1264cc10 Mon Sep 17 00:00:00 2001 From: flash159483 Date: Mon, 5 Aug 2024 22:47:35 +0900 Subject: [PATCH 10/11] =?UTF-8?q?[FEAT]#69:=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/kotlin/com/bff/wespot/ui/IntroductionScreen.kt | 7 ++++++- .../com/bff/wespot/data/repository/CommonRepositoryImpl.kt | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/core/ui/src/main/kotlin/com/bff/wespot/ui/IntroductionScreen.kt b/core/ui/src/main/kotlin/com/bff/wespot/ui/IntroductionScreen.kt index 0a2301d7..ab2b0f0d 100644 --- a/core/ui/src/main/kotlin/com/bff/wespot/ui/IntroductionScreen.kt +++ b/core/ui/src/main/kotlin/com/bff/wespot/ui/IntroductionScreen.kt @@ -56,7 +56,12 @@ fun IntroductionScreen( ) } - Box(modifier = Modifier.fillMaxWidth().padding(20.dp), contentAlignment = Alignment.CenterEnd) { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp), + contentAlignment = Alignment.CenterEnd + ) { LetterCountIndicator(currentCount = introduction.length, maxCount = 20) } diff --git a/data/src/main/kotlin/com/bff/wespot/data/repository/CommonRepositoryImpl.kt b/data/src/main/kotlin/com/bff/wespot/data/repository/CommonRepositoryImpl.kt index a88d6e5e..8df9a7b1 100644 --- a/data/src/main/kotlin/com/bff/wespot/data/repository/CommonRepositoryImpl.kt +++ b/data/src/main/kotlin/com/bff/wespot/data/repository/CommonRepositoryImpl.kt @@ -22,11 +22,11 @@ class CommonRepositoryImpl @Inject constructor( override suspend fun getCharacters(): Result> = commonDataSource.getCharacters() - .map { it.toCharacterList().characters } + .mapCatching { it.toCharacterList().characters } override suspend fun getBackgroundColors(): Result> = commonDataSource.getBackgroundColors() - .map { it.toBackgroundColorList().backgrounds } + .mapCatching { it.toBackgroundColorList().backgrounds } override suspend fun EditProfile( introduction: String, From d33361da58587b44466ecfd24370e3f82c3dd040 Mon Sep 17 00:00:00 2001 From: Jung Seungwon <123813671+flash159483@users.noreply.github.com> Date: Tue, 6 Aug 2024 23:03:32 +0900 Subject: [PATCH 11/11] =?UTF-8?q?facebook=5Fid=20secret=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jung Seungwon <123813671+flash159483@users.noreply.github.com> --- .github/workflows/opened-pr-notification.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/opened-pr-notification.yml b/.github/workflows/opened-pr-notification.yml index 6c9eafe4..bb23005b 100644 --- a/.github/workflows/opened-pr-notification.yml +++ b/.github/workflows/opened-pr-notification.yml @@ -27,6 +27,7 @@ jobs: echo KAKAO_APP_KEY=\"${{ secrets.KAKAO_APP_KEY }}\" >> ./local.properties echo SCHEME_KAKAO_APP_KEY=\"${{ secrets.SCHEME_KAKAO_APP_KEY }}\" >> ./local.properties echo MOCK_BASE_URL=\"${{ secrets.MOCK_BASE_URL }}\" >> ./local.properties + echo FACEBOOK_APP_ID=\"${{ secrets.FACEBOOK_APP_ID }}\" >> ./local.properties - name: add google-services.json run: echo '${{ secrets.GOOGLE_SERVICES_JSON }}' > ./app/google-services.json