Skip to content

Commit

Permalink
[Refactor/#922] 오늘의 솝마디 콕찌르기 QA (#923)
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: 뷰모델 수정

* feature #875: delete paddingValue

* feature #875: connect TodayFortuneCard api

* feature #875: connect TodayFortuneCard data

* feature #875: delete paddingValue

* feat: 오늘 부적 받기 버튼 구현

* feature #875: change function name

* feature #875: apply typealias

* feature #875: apply slot for amuletDescription

* feat: 콕 찌르기 바텀시트 UI 구현

* refactor: 클릭 리스너 구현

* feature #875: connect navigation

* feat: 콕 찌르기 서버통신 구현

* feature #875: connect navigator with DeepLink

* feature #875: apply finishActivity

* feature #875: apply FortuneButton component

* feature #875: add contentDescription

* feature #875: apply design

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

* refactor: 배경색 수정

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

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

* refactor: 자잘한 개행 및 import

* test: 기본 생성 파일 삭제

* refactor: 추천인 이름 글자수 제한 설정

* test: 테스트 코드 수정

* refactor: uimodel의 isEmpty 프로퍼티 제거

* refactor: paddingValues 제거

* refactor: 변수명 변경 및 bottomsheet 관리 코루틴 로직 수정

* refactor: immutableListOf 적용

* refactor: 유저 프로필 사진 crop 버그 수정

* refactor: 콕찌르기 버튼 리플 반영

* refactor: 익명 체크 아이콘 기본값 수정

* feat: 외부 영역 터치 및 하단 스크롤 시 bottomsheet hide 기능 구현

* refactor: 포매팅 정리

* feat: snackbar 구현

* refactor: Icon으로 수정

* refactor: topAppBar Dim처리 및 snackBar 이동

* feature #875: change naming

* feature #875: add preview

* refactor: custom breaking word 로직 구현

* refactor: 매직넘버 상수화

* build: baseline-prof & spotless

* refactor: 가독성 개선

* feat: AmplitudeTracker 구현

* feat: AmplitudeTracker 적용

* refactor: NDS 적용 및 import 추가

* refactor: 람다 네이밍 수정

* feat: FortuneDetailErrorDialog 구현

* feat: FortuneDetailErrorDialog 및 CircularProgressIndicator 구현, 분기처리

* refactor: amplitudeTracker 선언 로직 수정

* refactor: 앰플리튜드 수정사항 반영

* refactor: 뷰모델 함수 네이밍 수정

* build: spotless & baseline

* refactor: 오늘의 솝마디 이미지 크기 변경

* feat: 콕찌르기 완료 스낵바 구현

* refactor: 콕찌르기 버튼 isEnabled 구현

* refactor: icon, title 변수 할당 및 로직 개선

* refactor: 어노테이션 수정

---------

Co-authored-by: Dongmin <[email protected]>
  • Loading branch information
s9hn and chattymin authored Oct 21, 2024
1 parent ec385e1 commit e0778d3
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,23 @@ 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.Alignment.Companion.TopCenter
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
import org.sopt.official.designsystem.SoptTheme
import org.sopt.official.feature.fortune.R.drawable.ic_alert
import org.sopt.official.feature.fortune.R.drawable.ic_poke_check
import org.sopt.official.feature.fortune.component.FortuneTopBar
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.component.PokeSnackBar
import org.sopt.official.feature.fortune.feature.fortuneDetail.model.SnackBarUiState
import org.sopt.official.feature.fortune.feature.fortuneDetail.model.SnackBarUiState.Anonymous
import org.sopt.official.feature.fortune.feature.fortuneDetail.model.SnackBarUiState.Poke
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
Expand All @@ -60,7 +65,14 @@ fun FoundationScreen(
navController: NavHostController = rememberNavController(),
) {
var isBottomSheetVisible by remember { mutableStateOf(false) }
var snackBarUiState by remember { mutableStateOf<SnackBarUiState>(Anonymous) }
val snackBarHostState = remember { SnackbarHostState() }
val (icon, title) = remember {
when (snackBarUiState) {
is Poke -> ic_poke_check to "콕 찌르기를 완료했어요."
is Anonymous -> ic_alert to "익명 해제 시, 상대방이 나를 알 수 있어요."
}
}

Scaffold(
snackbarHost = {
Expand All @@ -69,8 +81,13 @@ fun FoundationScreen(
hostState = snackBarHostState,
modifier = Modifier
.padding(top = 16.dp)
.align(alignment = Alignment.TopCenter),
snackbar = { PokeSnackBar() },
.align(alignment = TopCenter),
snackbar = {
PokeSnackBar(
icon = icon,
title = title,
)
},
)
}
},
Expand Down Expand Up @@ -106,6 +123,9 @@ fun FoundationScreen(
isBottomSheetVisible = it
},
snackBarHostState = snackBarHostState,
showSnackBar = { case ->
snackBarUiState = case
},
)

fortuneAmuletNavGraph(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ import org.sopt.official.designsystem.SoptTheme.colors
import org.sopt.official.feature.fortune.LocalAmplitudeTracker
import org.sopt.official.feature.fortune.feature.fortuneDetail.component.PokeMessageBottomSheetScreen
import org.sopt.official.feature.fortune.feature.fortuneDetail.model.FortuneDetailUiState.Success
import org.sopt.official.feature.fortune.feature.fortuneDetail.model.SnackBarUiState
import org.sopt.official.feature.fortune.feature.fortuneDetail.model.SnackBarUiState.Anonymous
import org.sopt.official.feature.fortune.feature.fortuneDetail.model.SnackBarUiState.Poke

internal const val DEFAULT_ID = -1

Expand All @@ -60,11 +63,13 @@ internal fun FortuneDetailRoute(
onFortuneAmuletClick: () -> Unit,
isBottomSheetVisible: (isVisible: Boolean) -> Unit,
snackBarHostState: SnackbarHostState,
showSnackBar: (case: SnackBarUiState) -> Unit,
viewModel: FortuneDetailViewModel = hiltViewModel(),
) {
val context = LocalContext.current
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
var isAnonymous by remember { mutableStateOf(true) }
var isPokeEnabled by remember { mutableStateOf(true) }
var selectedIndex by remember { mutableIntStateOf(DEFAULT_ID) }
val bottomSheetState = rememberModalBottomSheetState(initialValue = Hidden)
val scope = rememberCoroutineScope()
Expand Down Expand Up @@ -104,8 +109,14 @@ internal fun FortuneDetailRoute(
)
scope.launch {
selectedIndex = newSelectedIndex
isPokeEnabled = false
bottomSheetState.hide()
viewModel.poke(message)
showSnackBar(Poke)
snackBarHostState.showSnackbar(
message = "",
duration = Short,
)
}.invokeOnCompletion {
isBottomSheetVisible(false)
}
Expand All @@ -118,6 +129,7 @@ internal fun FortuneDetailRoute(
properties = mapOf("isAnonymous" to isAnonymous),
)
if (isAnonymous.not()) scope.launch {
showSnackBar(Anonymous)
snackBarHostState.showSnackbar(
message = "",
duration = Short,
Expand Down Expand Up @@ -155,6 +167,7 @@ internal fun FortuneDetailRoute(
},
onErrorDialogCheckClick = viewModel::updateUi,
uiState = uiState,
isEnabled = isPokeEnabled,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import timber.log.Timber
@Composable
internal fun FortuneDetailScreen(
date: String,
isEnabled: Boolean,
onFortuneAmuletClick: () -> Unit,
onPokeClick: (userId: Long) -> Unit,
onProfileClick: (userId: Long) -> Unit,
Expand All @@ -82,6 +83,7 @@ internal fun FortuneDetailScreen(
PokeRecommendationDashboard(
profile = uiState.userInfo.profile,
name = uiState.userInfo.userName,
isEnabled = isEnabled,
userDescription = uiState.userInfo.userDescription,
onPokeClick = { onPokeClick(uiState.userInfo.userId) },
onProfileClick = { onProfileClick(uiState.userInfo.userId) },
Expand Down Expand Up @@ -128,6 +130,7 @@ private fun FortuneDetailScreenPreview() {
FortuneDetailScreen(
date = "2024-09-09",
onFortuneAmuletClick = {},
isEnabled = true,
uiState = Success(
todaySentence = TodaySentence(
userName = "이현우",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ internal fun PokeRecommendationDashboard(
profile: String,
name: String,
userDescription: String,
isEnabled: Boolean,
onPokeClick: () -> Unit,
onProfileClick: () -> Unit,
modifier: Modifier = Modifier,
Expand Down Expand Up @@ -113,9 +114,9 @@ internal fun PokeRecommendationDashboard(
modifier = Modifier
.size(size = 44.dp)
.background(
color = colors.onSurface10,
color = if (isEnabled) colors.onSurface10 else colors.onSurface700,
shape = RoundedCornerShape(size = 18.dp),
).clickable { onPokeClick() },
).clickable { if (isEnabled) onPokeClick() },
) {
Icon(
imageVector = ImageVector.vectorResource(id = ic_poke),
Expand All @@ -134,6 +135,7 @@ private fun PokeRecommendationDashboardPreview() {
PokeRecommendationDashboard(
profile = "123",
name = "이현우이현우이현우이현우이현우",
isEnabled = true,
onPokeClick = { },
onProfileClick = { },
userDescription = "1100기 iOS",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
*/
package org.sopt.official.feature.fortune.feature.fortuneDetail.component

import android.annotation.SuppressLint
import androidx.annotation.DrawableRes
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
Expand All @@ -47,10 +49,14 @@ import org.sopt.official.designsystem.SoptTheme.typography
import org.sopt.official.feature.fortune.R.drawable.ic_alert

@Composable
internal fun PokeSnackBar() {
internal fun PokeSnackBar(
@DrawableRes icon: Int,
title: String,
modifier: Modifier = Modifier,
) {
Row(
verticalAlignment = CenterVertically,
modifier = Modifier
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.background(
Expand All @@ -60,13 +66,13 @@ internal fun PokeSnackBar() {
) {
Spacer(modifier = Modifier.width(width = 16.dp))
Icon(
imageVector = ImageVector.vectorResource(id = ic_alert),
imageVector = ImageVector.vectorResource(id = icon),
tint = Unspecified,
contentDescription = null,
)
Spacer(modifier = Modifier.width(width = 8.dp))
Text(
text = "익명 해제 시, 상대방이 나를 알 수 있어요.",
text = title,
style = typography.title14SB,
color = colors.onSurface900,
modifier = Modifier.padding(vertical = 16.dp),
Expand All @@ -78,6 +84,9 @@ internal fun PokeSnackBar() {
@Composable
private fun PokeSnackBarPreview() {
SoptTheme {
PokeSnackBar()
PokeSnackBar(
icon = ic_alert,
title = "익명 해제 시, 상대방이 나를 알 수 있어요.",
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.sopt.official.designsystem.SoptTheme
import org.sopt.official.designsystem.SoptTheme.colors
import org.sopt.official.feature.fortune.R.drawable.img_fortune_title
import org.sopt.official.designsystem.SoptTheme.typography
import org.sopt.official.feature.fortune.R.drawable.img_fortune_title_small

@Composable
internal fun TodayFortuneDashboard(
Expand All @@ -55,13 +56,13 @@ internal fun TodayFortuneDashboard(
) {
Spacer(modifier = Modifier.height(height = 32.dp))
Image(
imageVector = ImageVector.vectorResource(id = img_fortune_title),
imageVector = ImageVector.vectorResource(id = img_fortune_title_small),
contentDescription = "오늘의 솝마디",
)
Spacer(modifier = Modifier.height(height = 10.dp))
Text(
text = date,
style = SoptTheme.typography.title18SB,
style = typography.title18SB,
color = colors.onSurface100,
)
Spacer(modifier = Modifier.height(height = 20.dp))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* MIT License
* 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
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.sopt.official.feature.fortune.feature.fortuneDetail.model

import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable

@Stable
sealed interface SnackBarUiState {

@Immutable
data object Anonymous : SnackBarUiState

@Immutable
data object Poke : SnackBarUiState
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import androidx.navigation.compose.composable
import androidx.navigation.toRoute
import kotlinx.serialization.Serializable
import org.sopt.official.feature.fortune.feature.fortuneDetail.FortuneDetailRoute
import org.sopt.official.feature.fortune.feature.fortuneDetail.model.SnackBarUiState

@Serializable
data class FortuneDetail(val date: String)
Expand All @@ -38,6 +39,7 @@ fun NavGraphBuilder.fortuneDetailNavGraph(
navigateToFortuneAmulet: () -> Unit,
snackBarHostState: SnackbarHostState,
isBottomSheetVisible: (isVisible: Boolean) -> Unit,
showSnackBar: (case: SnackBarUiState) -> Unit,
) {
composable<FortuneDetail> { backStackEntry ->
val items = backStackEntry.toRoute<FortuneDetail>()
Expand All @@ -46,6 +48,7 @@ fun NavGraphBuilder.fortuneDetailNavGraph(
onFortuneAmuletClick = navigateToFortuneAmulet,
isBottomSheetVisible = isBottomSheetVisible,
snackBarHostState = snackBarHostState,
showSnackBar = showSnackBar,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ private fun HomeScreen(
Spacer(modifier = Modifier.height(8.dp))

Image(
imageVector = ImageVector.vectorResource(id = R.drawable.img_fortune_title),
imageVector = ImageVector.vectorResource(id = R.drawable.img_fortune_title_large),
contentDescription = null,
modifier = Modifier.padding(horizontal = 25.dp)
)
Expand Down
16 changes: 16 additions & 0 deletions feature/fortune/src/main/res/drawable/ic_poke_check.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:pathData="M10,0L10,0A10,10 0,0 1,20 10L20,10A10,10 0,0 1,10 20L10,20A10,10 0,0 1,0 10L0,10A10,10 0,0 1,10 0z"
android:fillColor="#82F6CB"/>
<path
android:pathData="M5.333,9.183L8.444,12.333L14.667,7.083"
android:strokeLineJoin="round"
android:strokeWidth="1.5"
android:fillColor="#00000000"
android:strokeColor="#136D4C"
android:strokeLineCap="round"/>
</vector>
Original file line number Diff line number Diff line change
@@ -1,27 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
MIT License
Copyright (c) 2024 SOPT Makers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="270dp"
Expand Down
Loading

0 comments on commit e0778d3

Please sign in to comment.