Skip to content

Commit

Permalink
[Feature/#875] fortune amulet TodayFortuneCard API (#876)
Browse files Browse the repository at this point in the history
* feat: TodayFortuneBox 구현

* feat: 오늘의 운세(TodayFortuneDashboard) UI 구현

* feat: FortuneDetailRoute, FortuneDetailScreen 파일 분리

* refactor: 패키지 네이밍 수정

* feat: fortune 데이터레이어 구현

- di 모듈 추가
- network api 구현

* feat: fortune 도메인레이어 구현, internal 키워드 일괄 적용

- mapper 구현
- 레포지토리 인터페이스 생성 및 적용
- 힐트모듈 수정

* refactor: internal 키워드 삭제

* refactor: 함수 네이밍 수정

* feat: 레이어 의존성 추가

* refactor: 네트워크 path 수정

* feat: 네트워크 통신 구현 및 state 적용

* feat: 오늘의 솝마디 관련 유즈케이스 생성

* refactor: TodayFortuneBox 패딩값 추가

* refactor: 타입 및 네이밍 수정

* refactor: internal 추가

* test: FortuneDetailScreenTest 구현

* feat: 콕 찌르기 대시보드 UI 구현

* feat: 콕 찌르기 유저 추천 API 연결

* refactor: UI State 수정 및 적용

* refactor: SimpleDataFormatter로 리팩터링

* refactor: Timber 적용

* build: 의존성 수정

* refactor: break strategy 적용

* refactor: 뷰모델 수정

* refactor: 프로필 사진 사이즈 수정

* refactor: 배경색 수정

* feat: 프로필 클릭 시 플레이그라운드로 이동

* feat: 프로필 기본 이미지 구현

* refactor: 자잘한 개행 및 import

* feature #875: delete paddingValue

* feature #875: connect TodayFortuneCard api

* feature #875: connect TodayFortuneCard data

* feature #875: delete paddingValue

* feature #875: change function name

* feature #875: apply typealias

* feature #875: apply slot for amuletDescription

* feature #875: connect navigation

* feature #875: set nameSuffix

---------

Co-authored-by: s9hn <tnghqn@naver.com>
  • Loading branch information
chattymin and s9hn authored Oct 5, 2024
1 parent 76f11a3 commit 039eded
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -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()
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,33 @@ 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?) {
super.onCreate(savedInstanceState)

setContent {
SoptTheme {
FoundationScreen()
FoundationScreen(
navigateToNotification = {
startActivity(navigator.getNotificationActivityIntent())
},
navigateToHome = {
startActivity(navigator.getAuthActivityIntent())
},
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -67,7 +69,6 @@ fun FoundationScreen(
startDestination = Home
) {
homeNavGraph(
paddingValue = paddingValue,
navigateToFortuneDetail = { date ->
navController.navigate(FortuneDetail(date))
}
Expand All @@ -81,10 +82,7 @@ fun FoundationScreen(
)

fortuneAmuletNavGraph(
paddingValue = paddingValue,
navigateToHome = {
// TODO: Navigate to Home
}
navigateToHome = navigateToHome
)
}
}
Expand All @@ -96,6 +94,9 @@ fun FoundationScreen(
@Composable
fun FoundationScreenPreview() {
SoptTheme {
FoundationScreen()
FoundationScreen(
navigateToNotification = {},
navigateToHome = {}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,77 +26,101 @@ 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
) {
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))
Expand All @@ -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 = {}
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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 = "이 왔솝"
)
Original file line number Diff line number Diff line change
@@ -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<FortuneAmuletState> = MutableStateFlow(FortuneAmuletState())
val state: StateFlow<FortuneAmuletState> = _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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -34,12 +33,10 @@ import org.sopt.official.feature.fortune.feature.fortuneAmulet.FortuneAmuletRout
data object FortuneAmulet

fun NavGraphBuilder.fortuneAmuletNavGraph(
paddingValue: PaddingValues,
navigateToHome: () -> Unit,
) {
composable<FortuneAmulet> {
FortuneAmuletRoute(
paddingValue = paddingValue,
navigateToHome = navigateToHome
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Loading

0 comments on commit 039eded

Please sign in to comment.