diff --git a/domain/fortune/src/main/java/org/sopt/official/domain/fortune/usecase/GetTodayFortuneCardUseCase.kt b/domain/fortune/src/main/java/org/sopt/official/domain/fortune/usecase/GetTodayFortuneCardUseCase.kt new file mode 100644 index 000000000..9a78cf2cf --- /dev/null +++ b/domain/fortune/src/main/java/org/sopt/official/domain/fortune/usecase/GetTodayFortuneCardUseCase.kt @@ -0,0 +1,11 @@ +package org.sopt.official.domain.fortune.usecase + +import org.sopt.official.domain.fortune.model.TodayFortuneCard +import org.sopt.official.domain.fortune.repository.FortuneRepository +import javax.inject.Inject + +class GetTodayFortuneCardUseCase @Inject constructor( + private val fortuneRepository: FortuneRepository, +) { + suspend operator fun invoke(): TodayFortuneCard = fortuneRepository.fetchTodayFortuneCard() +} diff --git a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/FortuneActivity.kt b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/FortuneActivity.kt index b488d9946..81fb42b11 100644 --- a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/FortuneActivity.kt +++ b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/FortuneActivity.kt @@ -30,8 +30,18 @@ import android.os.Bundle import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity import dagger.hilt.android.AndroidEntryPoint +import dagger.hilt.android.EntryPointAccessors +import org.sopt.official.common.context.appContext +import org.sopt.official.common.navigator.NavigatorEntryPoint import org.sopt.official.designsystem.SoptTheme +private val navigator by lazy { + EntryPointAccessors.fromApplication( + appContext, + NavigatorEntryPoint::class.java + ).navigatorProvider() +} + @AndroidEntryPoint class FortuneActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -39,7 +49,14 @@ class FortuneActivity : AppCompatActivity() { setContent { SoptTheme { - FoundationScreen() + FoundationScreen( + navigateToNotification = { + startActivity(navigator.getNotificationActivityIntent()) + }, + navigateToHome = { + startActivity(navigator.getAuthActivityIntent()) + }, + ) } } } diff --git a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/FoundationScreen.kt b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/FoundationScreen.kt index cb155d8f6..14108e739 100644 --- a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/FoundationScreen.kt +++ b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/FoundationScreen.kt @@ -37,23 +37,25 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.rememberNavController import org.sopt.official.designsystem.SoptTheme import org.sopt.official.feature.fortune.component.FortuneTopBar -import org.sopt.official.feature.fortune.feature.fortuneDetail.navigation.FortuneDetail -import org.sopt.official.feature.fortune.feature.fortuneDetail.navigation.fortuneDetailNavGraph import org.sopt.official.feature.fortune.feature.fortuneAmulet.navigation.FortuneAmulet import org.sopt.official.feature.fortune.feature.fortuneAmulet.navigation.fortuneAmuletNavGraph +import org.sopt.official.feature.fortune.feature.fortuneDetail.navigation.FortuneDetail +import org.sopt.official.feature.fortune.feature.fortuneDetail.navigation.fortuneDetailNavGraph import org.sopt.official.feature.fortune.feature.home.navigation.Home import org.sopt.official.feature.fortune.feature.home.navigation.homeNavGraph @Composable fun FoundationScreen( + navigateToNotification: () -> Unit, + navigateToHome: () -> Unit, navController: NavHostController = rememberNavController(), ) { Scaffold( modifier = Modifier.fillMaxSize(), topBar = { - FortuneTopBar { - // TODO: Navigate to NotificationActivity - } + FortuneTopBar( + onClickNavigationIcon = navigateToNotification + ) }, content = { paddingValue -> Box( @@ -67,7 +69,6 @@ fun FoundationScreen( startDestination = Home ) { homeNavGraph( - paddingValue = paddingValue, navigateToFortuneDetail = { date -> navController.navigate(FortuneDetail(date)) } @@ -81,10 +82,7 @@ fun FoundationScreen( ) fortuneAmuletNavGraph( - paddingValue = paddingValue, - navigateToHome = { - // TODO: Navigate to Home - } + navigateToHome = navigateToHome ) } } @@ -96,6 +94,9 @@ fun FoundationScreen( @Composable fun FoundationScreenPreview() { SoptTheme { - FoundationScreen() + FoundationScreen( + navigateToNotification = {}, + navigateToHome = {} + ) } } diff --git a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneAmulet/FortuneAmuletScreen.kt b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneAmulet/FortuneAmuletScreen.kt index 6ac63927a..73b9d7863 100644 --- a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneAmulet/FortuneAmuletScreen.kt +++ b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneAmulet/FortuneAmuletScreen.kt @@ -26,44 +26,77 @@ package org.sopt.official.feature.fortune.feature.fortuneAmulet import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight 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.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.sopt.official.designsystem.SoptTheme import org.sopt.official.feature.fortune.component.CircleShapeBorderButton import org.sopt.official.feature.fortune.component.UrlImage @Composable internal fun FortuneAmuletRoute( - paddingValue: PaddingValues, navigateToHome: () -> Unit, + viewModel: FortuneAmuletViewModel = hiltViewModel(), ) { - FortuneAmuletScreen( - paddingValue = paddingValue, - navigateToHome = navigateToHome - ) + val state by viewModel.state.collectAsStateWithLifecycle() + + when { + state.isLoading -> { + // Loading View + } + + state.isFailure -> { + // Error View + } + + else -> { + FortuneAmuletScreen( + description = state.description, + amuletDescription = { + Text( + text = buildAnnotatedString { + withStyle(style = SpanStyle(color = state.imageColor)) { + append(state.name) + } + withStyle(style = SpanStyle(color = SoptTheme.colors.onBackground)) { + append(state.nameSuffix) + } + }, + style = SoptTheme.typography.heading28B, + ) + }, + imageUrl = state.imageUrl, + navigateToHome = navigateToHome + ) + } + } } @Composable private fun FortuneAmuletScreen( - paddingValue: PaddingValues, + description: String, + amuletDescription: @Composable ColumnScope.() -> Unit, + imageUrl: String, navigateToHome: () -> Unit, ) { Column( modifier = Modifier - .padding(paddingValue) .fillMaxSize() .background(SoptTheme.colors.background), horizontalAlignment = Alignment.CenterHorizontally @@ -71,32 +104,23 @@ private fun FortuneAmuletScreen( Spacer(modifier = Modifier.height(12.dp)) Text( - text = "어려움을 전부 극복할", // 서버에서 받아온 텍스트 + text = description, style = SoptTheme.typography.title16SB, color = SoptTheme.colors.onSurface300, ) Spacer(modifier = Modifier.height(2.dp)) - Text( - text = buildAnnotatedString { - withStyle(style = SpanStyle(color = SoptTheme.colors.attention /* 서버에서 받아온 색상 */)) { - append("해결부적") // 서버에서 받아온 텍스트 - } - withStyle(style = SpanStyle(color = SoptTheme.colors.onBackground)) { - append("이 왔솝") - } - }, - style = SoptTheme.typography.heading28B, - ) + amuletDescription() + Spacer(modifier = Modifier.height(34.dp)) UrlImage( - url = "https://어쩌구저쩌구/test_fortune_card.png", // 서버에서 받아온 이미지 + url = imageUrl, contentDescription = null, modifier = Modifier .padding(horizontal = 33.dp) - .fillMaxHeight(0.55f) + .fillMaxWidth() ) Spacer(modifier = Modifier.weight(1f)) @@ -122,7 +146,21 @@ private fun FortuneAmuletScreen( fun PreviewFortuneAmuletScreen() { SoptTheme { FortuneAmuletScreen( - paddingValue = PaddingValues(16.dp), + description = "배고픔을 전부 극복할", + amuletDescription = { + Text( + text = buildAnnotatedString { + withStyle(style = SpanStyle(color = Color.Red)) { + append("포만감") + } + withStyle(style = SpanStyle(color = Color.White)) { + append("이 왔솝") + } + }, + style = SoptTheme.typography.heading28B, + ) + }, + imageUrl = "https://sopt-makers.s3.ap-northeast-2.amazonaws.com/mainpage/makers-app-img/test_fortune_card.png", navigateToHome = {} ) } diff --git a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneAmulet/FortuneAmuletState.kt b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneAmulet/FortuneAmuletState.kt new file mode 100644 index 000000000..4123da005 --- /dev/null +++ b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneAmulet/FortuneAmuletState.kt @@ -0,0 +1,13 @@ +package org.sopt.official.feature.fortune.feature.fortuneAmulet + +import androidx.compose.ui.graphics.Color + +data class FortuneAmuletState( + val isLoading: Boolean = false, + val isFailure: Boolean = false, + val description: String = "", + val imageColor: Color = Color.White, + val imageUrl: String = "", + val name: String = "", + val nameSuffix: String = "이 왔솝" +) diff --git a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneAmulet/FortuneAmuletViewModel.kt b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneAmulet/FortuneAmuletViewModel.kt new file mode 100644 index 000000000..72e339ecb --- /dev/null +++ b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneAmulet/FortuneAmuletViewModel.kt @@ -0,0 +1,50 @@ +package org.sopt.official.feature.fortune.feature.fortuneAmulet + +import androidx.compose.ui.graphics.Color +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import org.sopt.official.domain.fortune.usecase.GetTodayFortuneCardUseCase +import javax.inject.Inject + +typealias GraphicColor = android.graphics.Color + +@HiltViewModel +internal class FortuneAmuletViewModel @Inject constructor( + getTodayFortuneCardUseCase: GetTodayFortuneCardUseCase, +) : ViewModel() { + private val _state: MutableStateFlow = MutableStateFlow(FortuneAmuletState()) + val state: StateFlow = _state.asStateFlow() + + init { + viewModelScope.launch { + runCatching { + _state.value = _state.value.copy(isLoading = true) + getTodayFortuneCardUseCase() + }.onSuccess { todayFortuneCard -> + _state.update { + it.copy( + description = todayFortuneCard.description, + imageColor = parseColor(todayFortuneCard.imageColorCode), + imageUrl = todayFortuneCard.imageUrl, + name = todayFortuneCard.name, + isLoading = false + ) + } + }.onFailure { + _state.value = _state.value.copy(isFailure = true) + } + } + } + + private fun parseColor(colorCode: String): Color = try { + Color(GraphicColor.parseColor(colorCode)) + } catch (e: IllegalArgumentException) { + Color.White + } +} diff --git a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneAmulet/navigation/FortuneAmuletNavGraph.kt b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneAmulet/navigation/FortuneAmuletNavGraph.kt index e3a31b664..61b7103f8 100644 --- a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneAmulet/navigation/FortuneAmuletNavGraph.kt +++ b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneAmulet/navigation/FortuneAmuletNavGraph.kt @@ -24,7 +24,6 @@ */ package org.sopt.official.feature.fortune.feature.fortuneAmulet.navigation -import androidx.compose.foundation.layout.PaddingValues import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import kotlinx.serialization.Serializable @@ -34,12 +33,10 @@ import org.sopt.official.feature.fortune.feature.fortuneAmulet.FortuneAmuletRout data object FortuneAmulet fun NavGraphBuilder.fortuneAmuletNavGraph( - paddingValue: PaddingValues, navigateToHome: () -> Unit, ) { composable { FortuneAmuletRoute( - paddingValue = paddingValue, navigateToHome = navigateToHome ) } diff --git a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneDetail/component/TodayFortuneDashboard.kt b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneDetail/component/TodayFortuneDashboard.kt index 966b34a9d..4e0fe14c7 100644 --- a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneDetail/component/TodayFortuneDashboard.kt +++ b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/fortuneDetail/component/TodayFortuneDashboard.kt @@ -1,6 +1,6 @@ /* * MIT License - * Copyright 2023-2024 SOPT - Shout Our Passion Together + * Copyright 2024 SOPT - Shout Our Passion Together * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/home/HomeScreen.kt b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/home/HomeScreen.kt index c27100c91..df7f396d4 100644 --- a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/home/HomeScreen.kt +++ b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/home/HomeScreen.kt @@ -29,7 +29,6 @@ import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -38,7 +37,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -55,13 +54,11 @@ import java.util.Locale @Composable internal fun HomeRoute( - paddingValue: PaddingValues, navigateToFortuneDetail: (String) -> Unit, ) { - val date = remember { getTodayInfo() } + val date = rememberSaveable { getToday() } HomeScreen( - paddingValue = paddingValue, date = date, navigateToFortuneDetail = { navigateToFortuneDetail(date) @@ -71,13 +68,11 @@ internal fun HomeRoute( @Composable private fun HomeScreen( - paddingValue: PaddingValues, date: String, navigateToFortuneDetail: () -> Unit = {}, ) { Column( modifier = Modifier - .padding(paddingValue) .fillMaxSize() .background(SoptTheme.colors.background), horizontalAlignment = Alignment.CenterHorizontally @@ -139,7 +134,7 @@ private fun HomeScreen( } } -fun getTodayInfo(): String { +fun getToday(): String { val today = LocalDate.now() val monthDay = today.format(DateTimeFormatter.ofPattern("M월 d일")) @@ -156,8 +151,7 @@ fun getTodayInfo(): String { fun HomeScreenPreview() { SoptTheme { HomeScreen( - paddingValue = PaddingValues(0.dp), - date = getTodayInfo() + date = getToday() ) } } diff --git a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/home/navigation/HomeNavGraph.kt b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/home/navigation/HomeNavGraph.kt index adb65ced5..047737b66 100644 --- a/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/home/navigation/HomeNavGraph.kt +++ b/feature/fortune/src/main/java/org/sopt/official/feature/fortune/feature/home/navigation/HomeNavGraph.kt @@ -34,12 +34,10 @@ import org.sopt.official.feature.fortune.feature.home.HomeRoute data object Home fun NavGraphBuilder.homeNavGraph( - paddingValue: PaddingValues, navigateToFortuneDetail: (String) -> Unit, ) { composable { HomeRoute( - paddingValue = paddingValue, navigateToFortuneDetail = navigateToFortuneDetail ) }