diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 96f51801..eaf824b4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -61,6 +61,8 @@ dependencies { implementation(project(":feature:disablepolicy")) implementation(project(":feature:policylist")) implementation(project(":feature:policyfilter")) + implementation(project(":feature:search")) + implementation(project(":feature:balancegame")) implementation(project(":core:ui")) implementation(project(":core:interceptor")) implementation(project(":core:data")) @@ -69,6 +71,5 @@ dependencies { implementation(project(":core:domain")) implementation(project(":core:analytics")) implementation(project(":core:designsystem")) - implementation(project(":feature:search")) testImplementation(project(":core:testing")) } diff --git a/app/src/main/java/com/withpeace/withpeace/navigation/NavHost.kt b/app/src/main/java/com/withpeace/withpeace/navigation/NavHost.kt index 9528f929..b3c75b94 100644 --- a/app/src/main/java/com/withpeace/withpeace/navigation/NavHost.kt +++ b/app/src/main/java/com/withpeace/withpeace/navigation/NavHost.kt @@ -14,6 +14,8 @@ import com.app.profileeditor.navigation.navigateProfileEditor import com.app.profileeditor.navigation.profileEditorNavGraph import com.withpeace.withpeace.core.designsystem.ui.snackbar.SnackbarState import com.withpeace.withpeace.core.designsystem.ui.snackbar.SnackbarType +import com.withpeace.withpeace.feature.balancegame.navigation.balanceGameGraph +import com.withpeace.withpeace.feature.balancegame.navigation.navigateToBalanceGame import com.withpeace.withpeace.feature.disablepolicy.navigation.disabledPolicyNavGraph import com.withpeace.withpeace.feature.disablepolicy.navigation.navigateDisabledPolicy import com.withpeace.withpeace.feature.gallery.navigation.galleryNavGraph @@ -215,6 +217,9 @@ fun WithpeaceNavHost( onSearchClick = { navController.navigateToSearch() }, + onClickBalanceGame = { + navController.navigateToBalanceGame() + } ) searchGraph( onAuthExpired = {}, @@ -421,6 +426,13 @@ fun WithpeaceNavHost( navController.navigateToPolicyDetail(policyId = it) }, ) + balanceGameGraph( + onShowSnackBar = { + onShowSnackBar(SnackbarState(it)) + }, + onAuthExpired = {}, + onClickBackButton = navController::popBackStack, + ) } } diff --git a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/di/RepositoryModule.kt b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/di/RepositoryModule.kt index 60e63ffd..41c86e1e 100644 --- a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/di/RepositoryModule.kt +++ b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/di/RepositoryModule.kt @@ -1,6 +1,7 @@ package com.withpeace.withpeace.core.data.di import com.withpeace.withpeace.core.data.repository.DefaultAppUpdateRepository +import com.withpeace.withpeace.core.data.repository.DefaultBalanceGameRepository import com.withpeace.withpeace.core.data.repository.DefaultImageRepository import com.withpeace.withpeace.core.data.repository.DefaultPostRepository import com.withpeace.withpeace.core.data.repository.DefaultRecentSearchKeywordRepository @@ -8,6 +9,7 @@ import com.withpeace.withpeace.core.data.repository.DefaultTokenRepository import com.withpeace.withpeace.core.data.repository.DefaultUserRepository import com.withpeace.withpeace.core.data.repository.DefaultYouthPolicyRepository import com.withpeace.withpeace.core.domain.repository.AppUpdateRepository +import com.withpeace.withpeace.core.domain.repository.BalanceGameRepository import com.withpeace.withpeace.core.domain.repository.ImageRepository import com.withpeace.withpeace.core.domain.repository.PostRepository import com.withpeace.withpeace.core.domain.repository.RecentSearchKeywordRepository @@ -60,4 +62,11 @@ interface RepositoryModule { fun bindsAppUpdateRepository( appUpdateRepository: DefaultAppUpdateRepository, ): AppUpdateRepository + + @Binds + @ViewModelScoped + fun bindsBalanceGameRepository( + defaultBalanceGameRepository: DefaultBalanceGameRepository + ): BalanceGameRepository + } diff --git a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultBalanceGameRepository.kt b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultBalanceGameRepository.kt new file mode 100644 index 00000000..5b2b247d --- /dev/null +++ b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultBalanceGameRepository.kt @@ -0,0 +1,18 @@ +package com.withpeace.withpeace.core.data.repository + +import com.withpeace.withpeace.core.datastore.dataStore.balancegame.BalanceGameDataStore +import com.withpeace.withpeace.core.domain.repository.BalanceGameRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class DefaultBalanceGameRepository @Inject constructor( + private val balanceGameDataStore: BalanceGameDataStore, +): BalanceGameRepository { + override fun isVisited(): Flow { + return balanceGameDataStore.isVisited + } + + override suspend fun updateVisitedStatus(visited: Boolean) { + balanceGameDataStore.updateVisitedStatus(visited) + } +} \ No newline at end of file diff --git a/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/dataStore/balancegame/BalanceGameDataStore.kt b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/dataStore/balancegame/BalanceGameDataStore.kt new file mode 100644 index 00000000..8bd2c498 --- /dev/null +++ b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/dataStore/balancegame/BalanceGameDataStore.kt @@ -0,0 +1,12 @@ +package com.withpeace.withpeace.core.datastore.dataStore.balancegame + +import kotlinx.coroutines.flow.Flow + +interface BalanceGameDataStore { + + val isVisited: Flow + + suspend fun updateVisitedStatus(visited: Boolean) +} + +//TODO 구현체 설정 \ No newline at end of file diff --git a/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/dataStore/balancegame/DefaultBalanceGameDataStore.kt b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/dataStore/balancegame/DefaultBalanceGameDataStore.kt new file mode 100644 index 00000000..d11d7bb4 --- /dev/null +++ b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/dataStore/balancegame/DefaultBalanceGameDataStore.kt @@ -0,0 +1,28 @@ +package com.withpeace.withpeace.core.datastore.dataStore.balancegame + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import javax.inject.Inject +import javax.inject.Named + +class DefaultBalanceGameDataStore @Inject constructor( + @Named("balance_game") private val dataStore: DataStore, +) : BalanceGameDataStore { + override val isVisited: Flow = dataStore.data.map { preferences -> + preferences[BALANCE_GAME_VISITED] ?: false + } + + override suspend fun updateVisitedStatus(visited: Boolean) { + dataStore.edit { preferences -> + preferences[BALANCE_GAME_VISITED] = visited + } + } + + companion object { + private val BALANCE_GAME_VISITED = booleanPreferencesKey("BALANCE_GAME_VISITED") + } +} \ No newline at end of file diff --git a/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/DataStoreModule.kt b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/DataStoreModule.kt index ae814fcf..9ceeea4e 100644 --- a/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/DataStoreModule.kt +++ b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/DataStoreModule.kt @@ -18,9 +18,11 @@ object DataStoreModule { private const val AUTH_DATASTORE_NAME = "AUTH_PREFERENCES" private const val USER_DATASTORE_NAME = "USER_PREFERENCES" + private const val BALANCE_GAME_DATASTORE_NAME = "BALANCE_GAME_DATASTORE_NAME" private val Context.authDataStore: DataStore by preferencesDataStore(name = AUTH_DATASTORE_NAME) private val Context.userDataStore: DataStore by preferencesDataStore(name = USER_DATASTORE_NAME) + private val Context.balanceGameDataStore: DataStore by preferencesDataStore(name = BALANCE_GAME_DATASTORE_NAME) @Provides @Singleton @@ -35,4 +37,11 @@ object DataStoreModule { fun providesUserDataStore( @ApplicationContext context: Context, ): DataStore = context.userDataStore + + @Provides + @Singleton + @Named("balance_game") + fun providesBalanceGameDataStore( + @ApplicationContext context: Context, + ): DataStore = context.balanceGameDataStore } \ No newline at end of file diff --git a/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/PreferenceDataSourceModule.kt b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/PreferenceDataSourceModule.kt index fc1d0ef2..fa345172 100644 --- a/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/PreferenceDataSourceModule.kt +++ b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/PreferenceDataSourceModule.kt @@ -1,5 +1,7 @@ package com.withpeace.withpeace.core.datastore.di +import com.withpeace.withpeace.core.datastore.dataStore.balancegame.BalanceGameDataStore +import com.withpeace.withpeace.core.datastore.dataStore.balancegame.DefaultBalanceGameDataStore import com.withpeace.withpeace.core.datastore.dataStore.token.DefaultTokenPreferenceDataSource import com.withpeace.withpeace.core.datastore.dataStore.token.TokenPreferenceDataSource import com.withpeace.withpeace.core.datastore.dataStore.user.DefaultUserPreferenceDataSource @@ -25,4 +27,10 @@ interface PreferenceDataSourceModule { fun bindsUserPreferenceDataSource( defaultUserPreferenceDataSource: DefaultUserPreferenceDataSource, ): UserPreferenceDataSource + + @Binds + @Singleton + fun bindsBalanceGamePreferenceDataSource( + defaultBalanceGameDataStore: DefaultBalanceGameDataStore + ): BalanceGameDataStore } \ No newline at end of file diff --git a/core/designsystem/src/main/java/com/withpeace/withpeace/core/designsystem/theme/Type.kt b/core/designsystem/src/main/java/com/withpeace/withpeace/core/designsystem/theme/Type.kt index 751781fc..33507297 100644 --- a/core/designsystem/src/main/java/com/withpeace/withpeace/core/designsystem/theme/Type.kt +++ b/core/designsystem/src/main/java/com/withpeace/withpeace/core/designsystem/theme/Type.kt @@ -181,4 +181,10 @@ data class WithPeaceTypography( lineHeight = 21.sp, letterSpacing = 0.16.sp, ), + val medium16LineHeight20: TextStyle = TextStyle( + fontFamily = PretendardFont, + fontWeight = FontWeight.Medium, + fontSize = 16.sp, + lineHeight = 20.sp, + ), ) diff --git a/core/domain/src/main/java/com/withpeace/withpeace/core/domain/repository/BalanceGameRepository.kt b/core/domain/src/main/java/com/withpeace/withpeace/core/domain/repository/BalanceGameRepository.kt new file mode 100644 index 00000000..cac8b160 --- /dev/null +++ b/core/domain/src/main/java/com/withpeace/withpeace/core/domain/repository/BalanceGameRepository.kt @@ -0,0 +1,9 @@ +package com.withpeace.withpeace.core.domain.repository + +import kotlinx.coroutines.flow.Flow + +interface BalanceGameRepository { + fun isVisited(): Flow + + suspend fun updateVisitedStatus(visited: Boolean) +} \ No newline at end of file diff --git a/core/domain/src/main/java/com/withpeace/withpeace/core/domain/usecase/BalanceGameVisitedUseCase.kt b/core/domain/src/main/java/com/withpeace/withpeace/core/domain/usecase/BalanceGameVisitedUseCase.kt new file mode 100644 index 00000000..6adb2a57 --- /dev/null +++ b/core/domain/src/main/java/com/withpeace/withpeace/core/domain/usecase/BalanceGameVisitedUseCase.kt @@ -0,0 +1,11 @@ +package com.withpeace.withpeace.core.domain.usecase + +import com.withpeace.withpeace.core.domain.repository.BalanceGameRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class BalanceGameVisitedUseCase @Inject constructor( + private val balanceGameRepository: BalanceGameRepository, +) { + operator fun invoke(): Flow = balanceGameRepository.isVisited() +} \ No newline at end of file diff --git a/core/domain/src/main/java/com/withpeace/withpeace/core/domain/usecase/UpdateBalanceGameVisitedStatus.kt b/core/domain/src/main/java/com/withpeace/withpeace/core/domain/usecase/UpdateBalanceGameVisitedStatus.kt new file mode 100644 index 00000000..7b04df84 --- /dev/null +++ b/core/domain/src/main/java/com/withpeace/withpeace/core/domain/usecase/UpdateBalanceGameVisitedStatus.kt @@ -0,0 +1,12 @@ +package com.withpeace.withpeace.core.domain.usecase + +import com.withpeace.withpeace.core.domain.repository.BalanceGameRepository +import javax.inject.Inject + +class UpdateBalanceGameVisitedStatus @Inject constructor( + private val balanceGameRepository: BalanceGameRepository, +){ + suspend operator fun invoke(isVisited: Boolean) { + balanceGameRepository.updateVisitedStatus(isVisited) + } +} \ No newline at end of file diff --git a/feature/postdetail/src/main/java/com/withpeace/withpeace/feature/postdetail/CommentSection.kt b/core/ui/src/main/java/com/withpeace/withpeace/core/ui/comment/CommentSection.kt similarity index 98% rename from feature/postdetail/src/main/java/com/withpeace/withpeace/feature/postdetail/CommentSection.kt rename to core/ui/src/main/java/com/withpeace/withpeace/core/ui/comment/CommentSection.kt index c885fcb9..42395ea2 100644 --- a/feature/postdetail/src/main/java/com/withpeace/withpeace/feature/postdetail/CommentSection.kt +++ b/core/ui/src/main/java/com/withpeace/withpeace/core/ui/comment/CommentSection.kt @@ -1,4 +1,4 @@ -package com.withpeace.withpeace.feature.postdetail +package com.withpeace.withpeace.core.ui.comment import androidx.compose.foundation.Image import androidx.compose.foundation.clickable @@ -41,10 +41,12 @@ import com.skydoves.landscapist.glide.GlideImage import com.withpeace.withpeace.core.designsystem.theme.PretendardFont import com.withpeace.withpeace.core.designsystem.theme.WithpeaceTheme import com.withpeace.withpeace.core.ui.DateUiModel +import com.withpeace.withpeace.core.ui.R import com.withpeace.withpeace.core.ui.R.drawable import com.withpeace.withpeace.core.ui.post.CommentUiModel import com.withpeace.withpeace.core.ui.post.CommentUserUiModel import com.withpeace.withpeace.core.ui.post.ReportTypeUiModel +import com.withpeace.withpeace.core.ui.report.PostDetailReportBottomSheet import com.withpeace.withpeace.core.ui.toRelativeString import java.time.LocalDateTime diff --git a/core/ui/src/main/java/com/withpeace/withpeace/core/ui/comment/CommentSize.kt b/core/ui/src/main/java/com/withpeace/withpeace/core/ui/comment/CommentSize.kt new file mode 100644 index 00000000..6f583839 --- /dev/null +++ b/core/ui/src/main/java/com/withpeace/withpeace/core/ui/comment/CommentSize.kt @@ -0,0 +1,37 @@ +package com.withpeace.withpeace.core.ui.comment + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import com.withpeace.withpeace.core.designsystem.theme.WithpeaceTheme +import com.withpeace.withpeace.core.ui.R + +@Composable +fun CommentSize( + modifier: Modifier = Modifier, + commentSize: Int, + horizontalArrangement: Arrangement.Horizontal = Arrangement.Start +) { + Row( + modifier = modifier.padding(horizontal = WithpeaceTheme.padding.BasicHorizontalPadding), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = horizontalArrangement + ) { + Icon( + painter = painterResource(id = R.drawable.ic_chat), + contentDescription = "댓글 개수", + modifier = Modifier.padding(end = 4.dp), + ) + Text( + text = "$commentSize", + style = WithpeaceTheme.typography.caption, + ) + } +} \ No newline at end of file diff --git a/feature/postdetail/src/main/java/com/withpeace/withpeace/feature/postdetail/RegisterCommentSection.kt b/core/ui/src/main/java/com/withpeace/withpeace/core/ui/comment/RegisterCommentSection.kt similarity index 98% rename from feature/postdetail/src/main/java/com/withpeace/withpeace/feature/postdetail/RegisterCommentSection.kt rename to core/ui/src/main/java/com/withpeace/withpeace/core/ui/comment/RegisterCommentSection.kt index 3963a17e..1a3a632c 100644 --- a/feature/postdetail/src/main/java/com/withpeace/withpeace/feature/postdetail/RegisterCommentSection.kt +++ b/core/ui/src/main/java/com/withpeace/withpeace/core/ui/comment/RegisterCommentSection.kt @@ -1,4 +1,4 @@ -package com.withpeace.withpeace.feature.postdetail +package com.withpeace.withpeace.core.ui.comment import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource @@ -27,6 +27,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.dp import com.withpeace.withpeace.core.designsystem.theme.WithpeaceTheme +import com.withpeace.withpeace.core.ui.R @Composable fun RegisterCommentSection( diff --git a/core/ui/src/main/java/com/withpeace/withpeace/core/ui/report/PostDetailReportBottomSheet.kt b/core/ui/src/main/java/com/withpeace/withpeace/core/ui/report/PostDetailReportBottomSheet.kt new file mode 100644 index 00000000..7470aecb --- /dev/null +++ b/core/ui/src/main/java/com/withpeace/withpeace/core/ui/report/PostDetailReportBottomSheet.kt @@ -0,0 +1,62 @@ +package com.withpeace.withpeace.core.ui.report + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.RectangleShape +import com.withpeace.withpeace.core.designsystem.theme.WithpeaceTheme +import com.withpeace.withpeace.core.designsystem.ui.WithPeaceBackButtonTopAppBar +import com.withpeace.withpeace.core.ui.post.ReportTypeUiModel + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun PostDetailReportBottomSheet( + isPostReport: Boolean, + id: Long, + onDismissRequest: () -> Unit, + onClickReportType: (id: Long, ReportTypeUiModel) -> Unit, +) { + val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + ModalBottomSheet( + contentWindowInsets = { WindowInsets(0, 0, 0, 0) }, + containerColor = WithpeaceTheme.colors.SystemWhite, + onDismissRequest = onDismissRequest, + sheetState = bottomSheetState, + shape = RectangleShape, + ) { + Column( + modifier = Modifier.fillMaxSize(), + ) { + WithPeaceBackButtonTopAppBar( + onClickBackButton = onDismissRequest, + title = { + Text(text = "신고하는 이유를 선택해주세요", style = WithpeaceTheme.typography.title1) + }, + ) + HorizontalDivider(Modifier.padding(horizontal = WithpeaceTheme.padding.BasicHorizontalPadding)) + Column( + modifier = Modifier + .fillMaxWidth(), + ) { + ReportTypeUiModel.entries.forEach { reportTypeUiModel -> + ReportTypeItem( + isPostReport = isPostReport, + id = id, + reportTypeUiModel = reportTypeUiModel, + onClickReportType = onClickReportType, + onDismissRequest = onDismissRequest, + ) + } + } + } + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/withpeace/withpeace/core/ui/report/ReportDialog.kt b/core/ui/src/main/java/com/withpeace/withpeace/core/ui/report/ReportDialog.kt new file mode 100644 index 00000000..52bf2d05 --- /dev/null +++ b/core/ui/src/main/java/com/withpeace/withpeace/core/ui/report/ReportDialog.kt @@ -0,0 +1,82 @@ +package com.withpeace.withpeace.core.ui.report + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.background +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.wrapContentSize +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +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.unit.dp +import androidx.compose.ui.window.Dialog +import com.withpeace.withpeace.core.designsystem.theme.WithpeaceTheme +import com.withpeace.withpeace.core.ui.R + +@Composable +fun ReportDialog( + title: String, + onClickReportButton: () -> Unit, + onDismissRequest: () -> Unit, +) { + Dialog(onDismissRequest = onDismissRequest) { + Column( + modifier = Modifier + .background( + WithpeaceTheme.colors.SystemWhite, + RoundedCornerShape(10.dp), + ) + .wrapContentSize(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + modifier = Modifier.padding(top = 24.dp, bottom = 16.dp), + text = title, + style = WithpeaceTheme.typography.title2, + ) + Row { + Button( + modifier = Modifier + .padding(start = 16.dp, end = 4.dp) + .weight(1f), + onClick = { onDismissRequest() }, + shape = RoundedCornerShape(10.dp), + border = BorderStroke(width = 1.dp, color = WithpeaceTheme.colors.MainPurple), + colors = ButtonDefaults.buttonColors(containerColor = WithpeaceTheme.colors.SystemWhite), + ) { + Text( + text = stringResource(R.string.delete_cancel), + style = WithpeaceTheme.typography.caption, + color = WithpeaceTheme.colors.MainPurple, + ) + } + Button( + modifier = Modifier + .padding(start = 4.dp, end = 16.dp) + .weight(1f), + onClick = { + onClickReportButton() + onDismissRequest() + }, + shape = RoundedCornerShape(10.dp), + colors = ButtonDefaults.buttonColors(containerColor = WithpeaceTheme.colors.MainPurple), + ) { + Text( + text = "신고하기", + style = WithpeaceTheme.typography.caption, + color = WithpeaceTheme.colors.SystemWhite, + ) + } + } + Spacer(modifier = Modifier.height(16.dp)) + } + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/withpeace/withpeace/core/ui/report/ReportTypeItem.kt b/core/ui/src/main/java/com/withpeace/withpeace/core/ui/report/ReportTypeItem.kt new file mode 100644 index 00000000..8f96d63f --- /dev/null +++ b/core/ui/src/main/java/com/withpeace/withpeace/core/ui/report/ReportTypeItem.kt @@ -0,0 +1,53 @@ +package com.withpeace.withpeace.core.ui.report + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.withpeace.withpeace.core.designsystem.theme.WithpeaceTheme +import com.withpeace.withpeace.core.ui.post.ReportTypeUiModel + +@Composable +fun ReportTypeItem( + modifier: Modifier = Modifier, + isPostReport: Boolean, + id: Long, + reportTypeUiModel: ReportTypeUiModel, + onClickReportType: (id: Long, ReportTypeUiModel) -> Unit, + onDismissRequest: () -> Unit, +) { + var showReportDialog by rememberSaveable { + mutableStateOf(false) + } + val title = if (isPostReport) reportTypeUiModel.postTitle else reportTypeUiModel.commentTitle + Column( + modifier = modifier.clickable { + showReportDialog = true + }.padding(horizontal = WithpeaceTheme.padding.BasicHorizontalPadding), + ) { + Text( + text = title, + style = WithpeaceTheme.typography.body, + modifier = Modifier.padding(start = 4.dp, top = 16.dp, bottom = 16.dp), + ) + HorizontalDivider() + } + if (showReportDialog) { + ReportDialog( + title = title, + onClickReportButton = { + onClickReportType(id, reportTypeUiModel) + onDismissRequest() + }, + onDismissRequest = { showReportDialog = false }, + ) + } +} \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/ic_chat.xml b/core/ui/src/main/res/drawable/ic_chat.xml new file mode 100644 index 00000000..a2caa1c4 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_chat.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_complain.xml b/core/ui/src/main/res/drawable/ic_complain.xml new file mode 100644 index 00000000..4022176f --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_complain.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_delete.xml b/core/ui/src/main/res/drawable/ic_delete.xml new file mode 100644 index 00000000..048edcc9 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_delete.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_edit.xml b/core/ui/src/main/res/drawable/ic_edit.xml new file mode 100644 index 00000000..a45e6ab1 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_edit.xml @@ -0,0 +1,13 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_hide.xml b/core/ui/src/main/res/drawable/ic_hide.xml new file mode 100644 index 00000000..9ff27923 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_hide.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_more.xml b/core/ui/src/main/res/drawable/ic_more.xml new file mode 100644 index 00000000..eab1c085 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_more.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/core/ui/src/main/res/drawable/ic_send.xml b/core/ui/src/main/res/drawable/ic_send.xml new file mode 100644 index 00000000..314e3002 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_send.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml index 7b910434..c935fcff 100644 --- a/core/ui/src/main/res/values/strings.xml +++ b/core/ui/src/main/res/values/strings.xml @@ -40,4 +40,24 @@ 정책분야 전체 해제 검색하기 + + + 수정하기 아이콘 + 수정하기 + 삭제하기 아이콘 + 삭제하기 + 신고하기 아이콘 + 신고하기 + 사용자 글 안보기 아이콘 + 이 사용자의 글 보지 않기 + 네트워크 상태를 확인해주세요 + 게시글이 존재하지 않습니다. + 취소하기 + 게시글을 삭제할까요? + 게시글을 삭제하면 모든 데이터가\n삭제되고 다시 볼 수 없어요. + 기본 유저 이미지 + 사진을 불러올 수 없어요 + 댓글 더보기 + 댓글 작성 아이콘 + 댓글을 입력해주세요 diff --git a/feature/balancegame/.gitignore b/feature/balancegame/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/balancegame/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/balancegame/build.gradle.kts b/feature/balancegame/build.gradle.kts new file mode 100644 index 00000000..3cb0da06 --- /dev/null +++ b/feature/balancegame/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("convention.feature") +} + +android { + namespace = "com.withpeace.withpeace.feature.balancegame" +} + +dependencies { +} \ No newline at end of file diff --git a/feature/balancegame/consumer-rules.pro b/feature/balancegame/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/feature/balancegame/proguard-rules.pro b/feature/balancegame/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/balancegame/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/balancegame/src/androidTest/java/com/withpeace/withpeace/feature/balancegame/ExampleInstrumentedTest.kt b/feature/balancegame/src/androidTest/java/com/withpeace/withpeace/feature/balancegame/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..bd26feaf --- /dev/null +++ b/feature/balancegame/src/androidTest/java/com/withpeace/withpeace/feature/balancegame/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.withpeace.withpeace.feature.balancegame + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * 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.withpeace.withpeace.feature.balancegame.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/balancegame/src/main/AndroidManifest.xml b/feature/balancegame/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/balancegame/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/feature/balancegame/src/main/java/com/withpeace/withpeace/feature/balancegame/BalanceGameScreen.kt b/feature/balancegame/src/main/java/com/withpeace/withpeace/feature/balancegame/BalanceGameScreen.kt new file mode 100644 index 00000000..67c37c0c --- /dev/null +++ b/feature/balancegame/src/main/java/com/withpeace/withpeace/feature/balancegame/BalanceGameScreen.kt @@ -0,0 +1,301 @@ +package com.withpeace.withpeace.feature.balancegame + +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.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.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.LinearProgressIndicator +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.graphics.StrokeCap +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import com.withpeace.withpeace.core.designsystem.theme.WithpeaceTheme +import com.withpeace.withpeace.core.designsystem.ui.WithPeaceBackButtonTopAppBar +import com.withpeace.withpeace.core.ui.DateUiModel +import com.withpeace.withpeace.core.ui.comment.CommentSection +import com.withpeace.withpeace.core.ui.comment.CommentSize +import com.withpeace.withpeace.core.ui.post.CommentUiModel +import com.withpeace.withpeace.core.ui.post.CommentUserUiModel +import java.time.LocalDateTime + +@Composable +fun BalanceGameRoute( + viewModel: BalanceGameViewModel = hiltViewModel(), + onShowSnackBar: (String) -> Unit, + onClickBackButton: () -> Unit, +) { + BalanceGameScreen( + onClickBackButton = onClickBackButton, + onClickBeforeDay = viewModel::onClickBeforeDay, + onClickAfterDay = viewModel::onClickAfterDay, + ) +} + +@Composable +fun BalanceGameScreen( + modifier: Modifier = Modifier, + onClickBackButton: () -> Unit, + onClickBeforeDay: () -> Unit, + onClickAfterDay: () -> Unit, +) { + Column( + modifier = modifier + .fillMaxSize() + .background(WithpeaceTheme.colors.SystemWhite), + ) { + Column( + modifier = modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + WithPeaceBackButtonTopAppBar( + modifier = modifier, + onClickBackButton = { onClickBackButton() }, + title = { + Text( + text = "밸런스 게임", + style = WithpeaceTheme.typography.title1, + color = WithpeaceTheme.colors.SystemBlack, + ) + }, + ) + HorizontalDivider( + color = WithpeaceTheme.colors.SystemGray3, + modifier = modifier.fillMaxWidth(), + thickness = 1.dp, + ) + Spacer(modifier = modifier.height(24.dp)) + Box( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 24.dp), + ) { + Image( + modifier = modifier + .align(Alignment.CenterStart) + .clickable { onClickBeforeDay() }, + painter = painterResource(R.drawable.ic_left), + contentDescription = "전날", + ) + Text( + text = "오늘의 밸런스 게임", + modifier = modifier + .align(Alignment.Center) + .background( + color = WithpeaceTheme.colors.SubPurple, + shape = RoundedCornerShape(999.dp), + ) + .padding(horizontal = 14.dp, vertical = 8.dp), + style = WithpeaceTheme.typography.disablePolicyTitle, + color = WithpeaceTheme.colors.MainPurple, + ) + Image( + modifier = modifier.align(Alignment.CenterEnd), + painter = painterResource(R.drawable.ic_right), + contentDescription = "다음날", + ) + } + Spacer(modifier = modifier.height(24.dp)) + Text( + "내가 가지고 싶은 복지 카드는?", + style = WithpeaceTheme.typography.title2, + color = WithpeaceTheme.colors.SnackbarBlack, + ) + Spacer(modifier = modifier.height(32.dp)) + // isActive && selectedStatus == null & isToday + // + Box( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 24.dp), + ) { + Column( + modifier + .fillMaxWidth() + .border( + width = 1.dp, + color = WithpeaceTheme.colors.MainPurple, + shape = RoundedCornerShape(16.dp), + ) + .align(Alignment.BottomCenter), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Spacer(modifier = modifier.height(28.dp)) + Text( + "오프라인에서만 쓸 수 있는 80만원 카드", + style = WithpeaceTheme.typography.body, + color = WithpeaceTheme.colors.SnackbarBlack, + ) + Spacer(modifier = modifier.height(20.dp)) + Row( + modifier.padding(start = 28.dp, end = 24.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + LinearProgressIndicator( + gapSize = (-10).dp, // 갭을 -2이하로 안할시 진행도와 남은 track이 분리되어 보임 + strokeCap = StrokeCap.Round, + trackColor = Color(0xffd9d9d9), + drawStopIndicator = {}, + color = WithpeaceTheme.colors.MainPurple, + progress = { 0.7f }, + ) + Spacer(modifier.width(6.dp)) + Text( + "68%", + style = WithpeaceTheme.typography.Tag, + color = WithpeaceTheme.colors.SystemGray1, + ) + } + Spacer(modifier = modifier.height(20.dp)) + } + Image( + painter = painterResource(R.drawable.ic_a), + modifier = modifier + .offset((-7).dp, (-5).dp) + .align(Alignment.TopStart) + .background( + shape = CircleShape, + color = WithpeaceTheme.colors.MainPurple, + ) + .padding(horizontal = 8.3.dp, vertical = 7.7.dp), + contentDescription = "a 선택", + ) + } + Spacer(modifier = modifier.height(16.dp)) + Text( + text = "VS", + style = WithpeaceTheme.typography.title2, + color = WithpeaceTheme.colors.MainPurple, + ) + Spacer(modifier = modifier.height(16.dp)) + Box( + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 24.dp), + ) { + Column( + modifier + .fillMaxWidth() + .border( + width = 1.dp, + color = WithpeaceTheme.colors.MainPurple, + shape = RoundedCornerShape(16.dp), + ) + .align(Alignment.BottomCenter), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Spacer(modifier = modifier.height(28.dp)) + Text( + "오프라인에서만 쓸 수 있는 80만원 카드", + style = WithpeaceTheme.typography.body, + color = WithpeaceTheme.colors.SnackbarBlack, + ) + Spacer(modifier = modifier.height(20.dp)) + Row( + modifier.padding(start = 28.dp, end = 24.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + LinearProgressIndicator( + gapSize = (-10).dp, // 갭을 -2이하로 안할시 진행도와 남은 track이 분리되어 보임 + strokeCap = StrokeCap.Round, + trackColor = Color(0xffd9d9d9), + drawStopIndicator = {}, + color = WithpeaceTheme.colors.MainPurple, + progress = { 0.7f }, + ) + Spacer(modifier.width(6.dp)) + Text( + "68%", + style = WithpeaceTheme.typography.Tag, + color = WithpeaceTheme.colors.SystemGray1, + ) + } + Spacer(modifier = modifier.height(20.dp)) + } + Image( + painter = painterResource(R.drawable.ic_b), + modifier = modifier + .offset((-7).dp, (-5).dp) + .align(Alignment.TopStart) + .background( + shape = CircleShape, + color = WithpeaceTheme.colors.MainPurple, + ) + .padding(horizontal = 9.0.dp, vertical = 7.7.dp), + contentDescription = "b 선택", + ) + } + Spacer(modifier = modifier.height(12.dp)) + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = modifier + .fillMaxWidth() + .padding(horizontal = 24.dp), + horizontalArrangement = Arrangement.End, + ) { + Image( + painter = painterResource(R.drawable.ic_user), + contentDescription = "", + ) + Spacer(modifier = modifier.width(2.dp)) + Text( + "100명 참여", + style = WithpeaceTheme.typography.medium16LineHeight20, + color = WithpeaceTheme.colors.SystemGray2, + ) + } + Spacer(modifier = modifier.height(8.dp)) + CommentSize(modifier.fillMaxWidth(), commentSize = 5) + Spacer(modifier = modifier.height(8.dp)) + LazyColumn { + CommentSection( + comments = List(5) { + CommentUiModel( + id = it.toLong(), + content = "가나다", + createDate = DateUiModel(LocalDateTime.now()), + commentUser = CommentUserUiModel( + id = it.toLong(), + nickname = "청하다", + profileImageUrl = "" + ), + isMyComment = false + ) + }, + onReportComment = { _, _ -> }, + ) + } + } + } +} + +@Composable +@Preview +fun BalanceGamePreview() { + WithpeaceTheme { + BalanceGameScreen( + onClickBackButton = {}, + onClickBeforeDay = {}, + ) { } + } +} \ No newline at end of file diff --git a/feature/balancegame/src/main/java/com/withpeace/withpeace/feature/balancegame/BalanceGameViewModel.kt b/feature/balancegame/src/main/java/com/withpeace/withpeace/feature/balancegame/BalanceGameViewModel.kt new file mode 100644 index 00000000..c36d7b2e --- /dev/null +++ b/feature/balancegame/src/main/java/com/withpeace/withpeace/feature/balancegame/BalanceGameViewModel.kt @@ -0,0 +1,20 @@ +package com.withpeace.withpeace.feature.balancegame + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class BalanceGameViewModel @Inject constructor( +) : ViewModel() { + init { + } + + fun onClickBeforeDay() { + + } + + fun onClickAfterDay() { + + } +} \ No newline at end of file diff --git a/feature/balancegame/src/main/java/com/withpeace/withpeace/feature/balancegame/navigation/BalanceGameNavigation.kt b/feature/balancegame/src/main/java/com/withpeace/withpeace/feature/balancegame/navigation/BalanceGameNavigation.kt new file mode 100644 index 00000000..0141acab --- /dev/null +++ b/feature/balancegame/src/main/java/com/withpeace/withpeace/feature/balancegame/navigation/BalanceGameNavigation.kt @@ -0,0 +1,41 @@ +package com.withpeace.withpeace.feature.balancegame.navigation + +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.core.tween +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import com.withpeace.withpeace.feature.balancegame.BalanceGameRoute + +private const val BALANCE_GAME_ROUTE = "BALANCE_GAME_ROUTE" + +fun NavController.navigateToBalanceGame(navOptions: NavOptions? = null) = + navigate(BALANCE_GAME_ROUTE, navOptions) + +fun NavGraphBuilder.balanceGameGraph( + onShowSnackBar: (String) -> Unit, + onAuthExpired: () -> Unit, + onClickBackButton: () -> Unit, +) { + composable( + route = BALANCE_GAME_ROUTE, + enterTransition = { + slideIntoContainer( + AnimatedContentTransitionScope.SlideDirection.Left, + animationSpec = tween(500), + ) + }, + exitTransition = { + slideOutOfContainer( + AnimatedContentTransitionScope.SlideDirection.Right, + animationSpec = tween(500), + ) + }, + ) { + BalanceGameRoute( + onShowSnackBar = onShowSnackBar, + onClickBackButton = onClickBackButton, + ) + } +} diff --git a/feature/balancegame/src/main/res/drawable/ic_a.xml b/feature/balancegame/src/main/res/drawable/ic_a.xml new file mode 100644 index 00000000..ff74dca0 --- /dev/null +++ b/feature/balancegame/src/main/res/drawable/ic_a.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/balancegame/src/main/res/drawable/ic_b.xml b/feature/balancegame/src/main/res/drawable/ic_b.xml new file mode 100644 index 00000000..17642abc --- /dev/null +++ b/feature/balancegame/src/main/res/drawable/ic_b.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/balancegame/src/main/res/drawable/ic_left.xml b/feature/balancegame/src/main/res/drawable/ic_left.xml new file mode 100644 index 00000000..4d6243d3 --- /dev/null +++ b/feature/balancegame/src/main/res/drawable/ic_left.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/balancegame/src/main/res/drawable/ic_right.xml b/feature/balancegame/src/main/res/drawable/ic_right.xml new file mode 100644 index 00000000..640f8638 --- /dev/null +++ b/feature/balancegame/src/main/res/drawable/ic_right.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/balancegame/src/main/res/drawable/ic_user.xml b/feature/balancegame/src/main/res/drawable/ic_user.xml new file mode 100644 index 00000000..19f11465 --- /dev/null +++ b/feature/balancegame/src/main/res/drawable/ic_user.xml @@ -0,0 +1,20 @@ + + + + diff --git a/feature/balancegame/src/test/java/com/withpeace/withpeace/feature/balancegame/ExampleUnitTest.kt b/feature/balancegame/src/test/java/com/withpeace/withpeace/feature/balancegame/ExampleUnitTest.kt new file mode 100644 index 00000000..302f7cbd --- /dev/null +++ b/feature/balancegame/src/test/java/com/withpeace/withpeace/feature/balancegame/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.withpeace.withpeace.feature.balancegame + +import org.junit.Test + +import org.junit.Assert.* + +/** + * 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) + } +} \ No newline at end of file diff --git a/feature/home/src/main/java/com/withpeace/withpeace/feature/home/HomeScreen.kt b/feature/home/src/main/java/com/withpeace/withpeace/feature/home/HomeScreen.kt index fda76bbb..e5ac22a5 100644 --- a/feature/home/src/main/java/com/withpeace/withpeace/feature/home/HomeScreen.kt +++ b/feature/home/src/main/java/com/withpeace/withpeace/feature/home/HomeScreen.kt @@ -49,6 +49,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.skydoves.balloon.BalloonAnimation import com.skydoves.balloon.BalloonSizeSpec import com.skydoves.balloon.compose.Balloon +import com.skydoves.balloon.compose.BalloonWindow import com.skydoves.balloon.compose.rememberBalloonBuilder import com.skydoves.balloon.compose.setBackgroundColor import com.withpeace.withpeace.core.designsystem.theme.WithpeaceTheme @@ -71,12 +72,14 @@ fun HomeRoute( onPolicyClick: (String) -> Unit, onPostClick: (PostTopicUiModel) -> Unit, onSearchClick: () -> Unit, + onClickBalanceGame: () -> Unit, ) { val selectingFilterUiState = viewModel.selectingFilters.collectAsStateWithLifecycle() val recentPosts = viewModel.recentPostsUiState.collectAsStateWithLifecycle() val hotPolicies = viewModel.hotPolicyUiState.collectAsStateWithLifecycle() val recommendPolicies = viewModel.recommendPolicyUiState.collectAsStateWithLifecycle() val completedFilterUiState = viewModel.completedFilters.collectAsStateWithLifecycle() + val isVisitedBalanceGame = viewModel.isBalanceGameVisited.collectAsStateWithLifecycle(true) HomeScreen( selectedFilterUiState = selectingFilterUiState.value, onClassificationCheckChanged = viewModel::onCheckClassification, @@ -92,6 +95,11 @@ fun HomeRoute( recommendPolicyUiState = recommendPolicies.value, completedFilterState = completedFilterUiState.value, onSearchClick = onSearchClick, + onClickBalanceGame = { + viewModel.visitBalanceGame() + onClickBalanceGame() + }, + isBalanceGameVisited = isVisitedBalanceGame.value, ) } @@ -99,6 +107,7 @@ fun HomeRoute( fun HomeScreen( modifier: Modifier = Modifier, recentPosts: RecentPostsUiState, + isBalanceGameVisited: Boolean, hotPolicyUiState: HotPolicyUiState, recommendPolicyUiState: RecommendPolicyUiState, selectedFilterUiState: PolicyFiltersUiModel, @@ -112,11 +121,14 @@ fun HomeScreen( onPolicyClick: (String) -> Unit, completedFilterState: PolicyFiltersUiModel, onSearchClick: () -> Unit = {}, + onClickBalanceGame: () -> Unit, ) { Column(modifier = modifier.fillMaxSize()) { HomeHeader( modifier = modifier, onSearchClick = onSearchClick, + onClickBalanceGame = onClickBalanceGame, + isBalanceGameVisited = isBalanceGameVisited, ) HorizontalDivider( modifier = modifier.height(1.dp), @@ -481,7 +493,24 @@ private fun ScrollSection( private fun HomeHeader( modifier: Modifier, onSearchClick: () -> Unit, + onClickBalanceGame: () -> Unit, + isBalanceGameVisited: Boolean, ) { + var balloonWindow: BalloonWindow? by remember { mutableStateOf(null) } + val builder = rememberBalloonBuilder { + setIsVisibleArrow(true) + setWidth(BalloonSizeSpec.WRAP) + setHeight(BalloonSizeSpec.WRAP) + setPaddingLeft(6) + setPaddingRight(6) + setPaddingTop(6) + setPaddingBottom(6) + setCornerRadius(3f) + setDismissWhenTouchOutside(false) + setBackgroundColor(Color(0xFF9A70E2)) + setBalloonAnimation(BalloonAnimation.FADE) + setArrowSize(8) + } Box( modifier = modifier .fillMaxWidth() @@ -496,15 +525,56 @@ private fun HomeHeader( painter = painterResource(id = R.drawable.ic_home_logo), contentDescription = stringResource(R.string.cheongha_logo), ) - Image( - painter = painterResource(R.drawable.ic_search), - contentDescription = "검색", + Row( modifier = modifier .align(Alignment.CenterEnd) - .padding(top = 16.dp) - .clickable { - onSearchClick() - }, - ) + .padding(top = 16.dp), + ) { + if (isBalanceGameVisited) { + Image( + painter = painterResource(R.drawable.ic_balance_game), + contentDescription = "밸런스 게임", + modifier = modifier + .clickable { + onClickBalanceGame() + }, + ) + } else { + Balloon( + builder = builder, + onBalloonWindowInitialized = { balloonWindow = it }, + onComposedAnchor = { balloonWindow?.showAlignBottom() }, + balloonContent = { + Text( + text = "밸런스게임", + color = WithpeaceTheme.colors.SystemWhite, + style = WithpeaceTheme.typography.Tag, + ) + }, + ) { _ -> + Image( + painter = painterResource(R.drawable.ic_balance_game), + contentDescription = "밸런스 게임", + modifier = modifier + .clickable { + onClickBalanceGame() + }, + ) + } + } + + Spacer(modifier.width(12.dp)) + Image( + painter = painterResource(R.drawable.ic_search), + contentDescription = "검색", + modifier = modifier + .clickable { + balloonWindow?.dismiss() + onSearchClick() + }, + ) + + } + } } \ No newline at end of file diff --git a/feature/home/src/main/java/com/withpeace/withpeace/feature/home/HomeViewModel.kt b/feature/home/src/main/java/com/withpeace/withpeace/feature/home/HomeViewModel.kt index 55794f2b..4d2eebc9 100644 --- a/feature/home/src/main/java/com/withpeace/withpeace/feature/home/HomeViewModel.kt +++ b/feature/home/src/main/java/com/withpeace/withpeace/feature/home/HomeViewModel.kt @@ -3,10 +3,12 @@ package com.withpeace.withpeace.feature.home import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.withpeace.withpeace.core.domain.model.policy.PolicyFilters +import com.withpeace.withpeace.core.domain.usecase.BalanceGameVisitedUseCase import com.withpeace.withpeace.core.domain.usecase.GetHotPoliciesUseCase import com.withpeace.withpeace.core.domain.usecase.GetPolicyFilterUseCase import com.withpeace.withpeace.core.domain.usecase.GetRecentPostUseCase import com.withpeace.withpeace.core.domain.usecase.GetRecommendPoliciesUseCase +import com.withpeace.withpeace.core.domain.usecase.UpdateBalanceGameVisitedStatus import com.withpeace.withpeace.core.domain.usecase.UpdatePolicyFilterUseCase import com.withpeace.withpeace.core.ui.policy.ClassificationUiModel import com.withpeace.withpeace.core.ui.policy.RegionUiModel @@ -37,6 +39,9 @@ class HomeViewModel @Inject constructor( private val getHotPoliciesUseCase: GetHotPoliciesUseCase, private val getPolicyFilterUseCase: GetPolicyFilterUseCase, private val updatePolicyFilterUseCase: UpdatePolicyFilterUseCase, + private val updateBalanceGameVisitedStatus: UpdateBalanceGameVisitedStatus, + balanceGameVisitedUseCase: BalanceGameVisitedUseCase, + ) : ViewModel() { private val _recentPostsUiState: MutableStateFlow = MutableStateFlow(RecentPostsUiState.Loading) @@ -66,6 +71,8 @@ class HomeViewModel @Inject constructor( PolicyFiltersUiModel(), ) + val isBalanceGameVisited = balanceGameVisitedUseCase() + init { viewModelScope.launch { @@ -162,4 +169,10 @@ class HomeViewModel @Inject constructor( it.removeAll() } } + + fun visitBalanceGame() { + viewModelScope.launch { + updateBalanceGameVisitedStatus(true) + } + } } diff --git a/feature/home/src/main/java/com/withpeace/withpeace/feature/home/navigation/HomeNavigation.kt b/feature/home/src/main/java/com/withpeace/withpeace/feature/home/navigation/HomeNavigation.kt index e754cc5a..a42c47e0 100644 --- a/feature/home/src/main/java/com/withpeace/withpeace/feature/home/navigation/HomeNavigation.kt +++ b/feature/home/src/main/java/com/withpeace/withpeace/feature/home/navigation/HomeNavigation.kt @@ -21,6 +21,7 @@ fun NavGraphBuilder.homeNavGraph( onPolicyClick: (String) -> Unit, onPostClick: (PostTopicUiModel) -> Unit, onSearchClick: () -> Unit, + onClickBalanceGame: () -> Unit, ) { composable( route = HOME_ROUTE, @@ -33,6 +34,7 @@ fun NavGraphBuilder.homeNavGraph( onPolicyClick = onPolicyClick, onPostClick = onPostClick, onSearchClick = onSearchClick, + onClickBalanceGame = onClickBalanceGame ) } } \ No newline at end of file diff --git a/feature/home/src/main/res/drawable/ic_vote.xml b/feature/home/src/main/res/drawable/ic_balance_game.xml similarity index 100% rename from feature/home/src/main/res/drawable/ic_vote.xml rename to feature/home/src/main/res/drawable/ic_balance_game.xml diff --git a/feature/postdetail/src/main/java/com/withpeace/withpeace/feature/postdetail/PostDetailScreen.kt b/feature/postdetail/src/main/java/com/withpeace/withpeace/feature/postdetail/PostDetailScreen.kt index 714a3c78..73102f7a 100644 --- a/feature/postdetail/src/main/java/com/withpeace/withpeace/feature/postdetail/PostDetailScreen.kt +++ b/feature/postdetail/src/main/java/com/withpeace/withpeace/feature/postdetail/PostDetailScreen.kt @@ -52,6 +52,8 @@ import com.withpeace.withpeace.core.designsystem.theme.WithpeaceTheme import com.withpeace.withpeace.core.designsystem.ui.KeyboardAware import com.withpeace.withpeace.core.designsystem.ui.WithPeaceBackButtonTopAppBar import com.withpeace.withpeace.core.ui.DateUiModel +import com.withpeace.withpeace.core.ui.comment.CommentSection +import com.withpeace.withpeace.core.ui.comment.RegisterCommentSection import com.withpeace.withpeace.core.ui.post.CommentUiModel import com.withpeace.withpeace.core.ui.post.CommentUserUiModel import com.withpeace.withpeace.core.ui.post.PostDetailUiModel @@ -525,9 +527,11 @@ fun ReportTypeItem( } val title = if (isPostReport) reportTypeUiModel.postTitle else reportTypeUiModel.commentTitle Column( - modifier = modifier.clickable { - showReportDialog = true - }.padding(horizontal = WithpeaceTheme.padding.BasicHorizontalPadding), + modifier = modifier + .clickable { + showReportDialog = true + } + .padding(horizontal = WithpeaceTheme.padding.BasicHorizontalPadding), ) { Text( text = title, @@ -648,3 +652,4 @@ private fun PostDetailScreenPreview() { ) } } +//TODO 하단 댓글창 및 댓글 창 밸런스 게임 선택 \ No newline at end of file diff --git a/feature/postdetail/src/main/java/com/withpeace/withpeace/feature/postdetail/PostSection.kt b/feature/postdetail/src/main/java/com/withpeace/withpeace/feature/postdetail/PostSection.kt index d573d089..fcab8350 100644 --- a/feature/postdetail/src/main/java/com/withpeace/withpeace/feature/postdetail/PostSection.kt +++ b/feature/postdetail/src/main/java/com/withpeace/withpeace/feature/postdetail/PostSection.kt @@ -33,10 +33,10 @@ import com.skydoves.landscapist.glide.GlideImage import com.withpeace.withpeace.core.designsystem.theme.WithpeaceTheme import com.withpeace.withpeace.core.ui.DateUiModel import com.withpeace.withpeace.core.ui.R +import com.withpeace.withpeace.core.ui.comment.CommentSize import com.withpeace.withpeace.core.ui.post.PostTopicUiModel import com.withpeace.withpeace.core.ui.post.PostUserUiModel import com.withpeace.withpeace.core.ui.toRelativeString -import com.withpeace.withpeace.feature.postdetail.R.drawable import com.withpeace.withpeace.feature.postdetail.R.string fun LazyListScope.PostSection( @@ -217,23 +217,3 @@ fun PostUserProfile( } } } - -@Composable -fun CommentSize( - commentSize: Int, -) { - Row( - modifier = Modifier.padding(horizontal = WithpeaceTheme.padding.BasicHorizontalPadding), - verticalAlignment = Alignment.CenterVertically, - ) { - Icon( - painter = painterResource(id = drawable.ic_chat), - contentDescription = "댓글 개수", - modifier = Modifier.padding(end = 4.dp), - ) - Text( - text = "$commentSize", - style = WithpeaceTheme.typography.caption, - ) - } -} diff --git a/feature/search/src/main/java/com/withpeace/withpeace/feature/search/SearchViewModel.kt b/feature/search/src/main/java/com/withpeace/withpeace/feature/search/SearchViewModel.kt index 67f9a5f9..e38cde02 100644 --- a/feature/search/src/main/java/com/withpeace/withpeace/feature/search/SearchViewModel.kt +++ b/feature/search/src/main/java/com/withpeace/withpeace/feature/search/SearchViewModel.kt @@ -81,7 +81,7 @@ class SearchViewModel @Inject constructor( val recentSearchKeywords = _recentSearchKeywords.asStateFlow() private val _totalCountFlow = MutableStateFlow(0) - val totalCountFlow = _totalCountFlow.asStateFlow() + val totalCountFlow = _totalCountFlow.asStateFlow() // Paging3에서 데이터를 같이 가져오는 것이 어려워 해당 프로퍼티로 구현 init { viewModelScope.launch { @@ -125,14 +125,12 @@ class SearchViewModel @Inject constructor( onError = { }, onReceiveTotalCount = { - Log.d("test",it.toString()) _totalCountFlow.value = it }, ).onStart { _uiState.update { SearchUiState.PagingData } - // 여기서 검색 state로 변경 }.map { data -> data.map { it.second.toUiModel() diff --git a/settings.gradle.kts b/settings.gradle.kts index 326f3cff..a93b306a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -48,3 +48,4 @@ include(":feature:policylist") include(":feature:policyfilter") include(":feature:search") include(":core:database") +include(":feature:balancegame")