Skip to content

Commit 0d8d65d

Browse files
authored
[Mod/#1582] 앱잼탬프 qa 전 수정사항 반영 (#1584)
* [REFACTOR#1582] 앱잼팀 랭킹 화면 UI 및 텍스트 문구 수정 - AppjamtampRankingScreen 내 주요 텍스트 문구 변경 (랭킹 -> 현황, 득점 랭킹 -> 미션 달성 보드 등) - TodayScoreRaking 컴포넌트 내 순위 아이콘(Icon) 제거 및 레이아웃 패딩 조정 - AppjamtampFloatingButton의 버튼 텍스트를 '앱잼팀 현황'으로 변경 - 프리뷰 데이터 내 팀명을 '키어로'로 업데이트 * [REFACTOR#1582] AppjamtampFloatingButton 내 불필요한 import 제거 - `AppjamtampFloatingButton.kt`에서 사용되지 않는 `org.sopt.official.designsystem.Black` 임포트 문을 삭제함. * [CHORE#1584] 앱잼탬프 기간 대응을 위한 SoptLog 관련 로직 수정 - SoptLogUrl 및 MySoptLogItemType의 SOPTAMP URL을 `soptamp`에서 `appjamtamp`로 변경 - SoptLogScreen에서 솝탬프 로그 노출 조건을 활동 기수 여부(`isActive`)에서 앱잼 참여 여부(`isAppjamJoined`)로 변경 - SoptLogViewModel의 호출 메서드를 `getSoptLogInfo`에서 `getSoptLogInfoData`로 업데이트 * [FEAT#1584] AppServiceManager 구현 - `domain/home` 모듈에 Kotlin Coroutines 의존성 추가 - `AppService` 데이터를 관리하고 캐싱하는 `AppServiceManager` 클래스 신규 생성 - `StateFlow`를 통한 상태 노출 및 `forceUpdate` 기능을 포함한 데이터 페칭 로직 구현 * [REFACTOR#1584] AppServiceManager 도입을 통한 앱 서비스 데이터 관리 로직 개선 - `MainViewModel` 및 `NewHomeViewModel`에서 `GetAppServiceUseCase` 대신 `AppServiceManager`를 사용하도록 변경 - `MainViewModel`: `fetchMainTabs`를 `observeAppServices`로 변경하여 데이터 관찰 구조로 개선 및 알림 배지 표시 로직 수정 - `NewHomeViewModel`: `appServices` 데이터를 `AppServiceManager`를 통해 관찰하도록 변경하고, `refreshAll` 시 강제 업데이트 로직 적용 * [REFACTOR#1582] 코리반영 - AppServiceManager 내 onFailure 로직 제거 - `getAppServiceUseCase` 호출 시 실패 케이스에서 `_appServices`를 빈 리스트로 초기화하던 로직을 삭제합니다. * [FIX#1582] SoptLogInfo 기본값 설정 및 섹션 노출 로직 수정 - `SoptLogInfo` 내 `isActive` 필드에 기본값(`false`) 추가 - 솝탬프 로그 섹션 노출 조건을 `isAppjamJoined`에서 `soptLogInfo.isActive`로 변경 - 콕찌르기 로그 섹션의 엠티뷰를 제거하고 실제 섹션(`SoptLogSection`)으로 복구 * [REFACTOR#1582] SoptLogScreen 내 미사용 import 제거 - SoptLogEmptySection 임포트 문 삭제 * [REFACTOR#1582] MySoptLogItemType 내 COMPLETED_MISSION url 변경 - COMPLETED_MISSION의 url을 "appjamtamp"에서 "soptamp"로 수정하여 일반 솝탬프 경로로 원복하였습니다. * [FEAT#1582] SoptLogScreen 내 솝탬프 로그 섹션 임시 숨김 처리 - 기획 변경에 따라 앱잼 기간 동안 솝탬프 로그 섹션을 노출하지 않도록 관련 로직 주석 처리.
1 parent 70bd714 commit 0d8d65d

10 files changed

Lines changed: 90 additions & 66 deletions

File tree

domain/home/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ kotlin {
3232

3333
dependencies {
3434
implementation(libs.javax.inject)
35+
implementation(libs.kotlin.coroutines)
3536
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package org.sopt.official.domain.home
2+
3+
import kotlinx.coroutines.flow.MutableStateFlow
4+
import kotlinx.coroutines.flow.StateFlow
5+
import kotlinx.coroutines.flow.asStateFlow
6+
import org.sopt.official.domain.home.model.AppService
7+
import org.sopt.official.domain.home.usecase.GetAppServiceUseCase
8+
import javax.inject.Inject
9+
import javax.inject.Singleton
10+
11+
/**
12+
* AppServiceManager: app-service 관리 클래스
13+
* */
14+
@Singleton
15+
class AppServiceManager @Inject constructor(
16+
private val getAppServiceUseCase: GetAppServiceUseCase
17+
) {
18+
private val _appServices = MutableStateFlow<List<AppService>?>(null)
19+
val appServices: StateFlow<List<AppService>?> = _appServices.asStateFlow()
20+
21+
suspend fun fetchAppServices(forceUpdate: Boolean = false) {
22+
if (!forceUpdate && _appServices.value != null) return
23+
24+
getAppServiceUseCase()
25+
.onSuccess { _appServices.value = it }
26+
}
27+
}

domain/soptlog/src/main/java/org/sopt/official/domain/soptlog/model/SoptLogInfo.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
package org.sopt.official.domain.soptlog.model
2626

2727
data class SoptLogInfo(
28-
val isActive: Boolean,
28+
val isActive: Boolean = false,
2929
val isFortuneChecked: Boolean,
3030
val todayFortuneText: String,
3131
val soptampCount: Int? = null,

feature/appjamtamp/src/main/java/org/sopt/official/feature/appjamtamp/missionlist/component/AppjamtampFloatingButton.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ import androidx.compose.ui.graphics.vector.ImageVector
4040
import androidx.compose.ui.res.vectorResource
4141
import androidx.compose.ui.tooling.preview.Preview
4242
import androidx.compose.ui.unit.dp
43-
import org.sopt.official.designsystem.Black
4443
import org.sopt.official.designsystem.Gray950
4544
import org.sopt.official.designsystem.SoptTheme
4645
import org.sopt.official.designsystem.White
@@ -85,7 +84,7 @@ private fun AppjamtampFloatingBody() {
8584
)
8685

8786
Text(
88-
text = "앱잼팀 랭킹",
87+
text = "앱잼팀 현황",
8988
style = SoptTheme.typography.heading18B,
9089
color = Gray950
9190
)

feature/appjamtamp/src/main/java/org/sopt/official/feature/appjamtamp/ranking/AppjamtampRankingScreen.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ internal fun AppjamtampRankingScreen(
130130
.navigationBarsPadding(),
131131
topBar = {
132132
BackButtonHeader(
133-
title = "앱잼팀 랭킹",
133+
title = "앱잼팀 현황",
134134
onBackButtonClick = onBackButtonClick,
135135
modifier = Modifier
136136
.padding(vertical = 12.dp)
@@ -153,7 +153,7 @@ internal fun AppjamtampRankingScreen(
153153
verticalAlignment = Alignment.CenterVertically
154154
) {
155155
Text(
156-
text = "따끈따끈 합숙미션 구경하기",
156+
text = "따끈따끈 지금 앱잼팀은?",
157157
color = White,
158158
style = SoptTheme.typography.heading20B
159159
)
@@ -207,7 +207,7 @@ internal fun AppjamtampRankingScreen(
207207
verticalAlignment = Alignment.CenterVertically
208208
) {
209209
Text(
210-
text = "오늘의 득점 랭킹",
210+
text = "오늘의 미션 달성 보드",
211211
color = White,
212212
style = SoptTheme.typography.heading20B
213213
)
@@ -225,7 +225,7 @@ internal fun AppjamtampRankingScreen(
225225
Spacer(modifier = Modifier.height(height = 8.dp))
226226

227227
Text(
228-
text = "미션을 인증해 오늘의 순위를 뒤집어보세요!",
228+
text = "오늘 미션 인증하고 추가 점수를 획득해보세요!",
229229
color = SoptTheme.colors.onSurface200,
230230
style = SoptTheme.typography.body13M
231231
)
@@ -312,7 +312,7 @@ private fun AppjamtampRankingScreenPreview() {
312312
),
313313
TopMissionScoreUiModel(
314314
rank = 3,
315-
teamName = "비트",
315+
teamName = "키어로",
316316
teamNumber = "FIRST",
317317
todayPoints = 950,
318318
totalPoints = 4200

feature/appjamtamp/src/main/java/org/sopt/official/feature/appjamtamp/ranking/component/TodayScoreRaking.kt

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -25,51 +25,37 @@
2525
package org.sopt.official.feature.appjamtamp.ranking.component
2626

2727
import androidx.compose.foundation.background
28-
import androidx.compose.foundation.layout.Box
2928
import androidx.compose.foundation.layout.Column
3029
import androidx.compose.foundation.layout.Row
3130
import androidx.compose.foundation.layout.Spacer
3231
import androidx.compose.foundation.layout.fillMaxWidth
3332
import androidx.compose.foundation.layout.height
3433
import androidx.compose.foundation.layout.padding
3534
import androidx.compose.foundation.shape.RoundedCornerShape
36-
import androidx.compose.material3.Icon
3735
import androidx.compose.material3.Text
3836
import androidx.compose.runtime.Composable
3937
import androidx.compose.ui.Alignment
4038
import androidx.compose.ui.Modifier
4139
import androidx.compose.ui.draw.clip
42-
import androidx.compose.ui.graphics.Color
43-
import androidx.compose.ui.graphics.vector.ImageVector
44-
import androidx.compose.ui.res.vectorResource
4540
import androidx.compose.ui.text.style.TextOverflow
4641
import androidx.compose.ui.tooling.preview.Preview
4742
import androidx.compose.ui.unit.dp
43+
import org.sopt.official.common.util.noRippleClickable
4844
import org.sopt.official.designsystem.SoptTheme
4945
import org.sopt.official.designsystem.White
50-
import org.sopt.official.feature.appjamtamp.R
5146
import org.sopt.official.feature.appjamtamp.ranking.model.TopMissionScoreUiModel
52-
import org.sopt.official.common.util.noRippleClickable
5347

5448
@Composable
5549
internal fun TodayScoreRaking(
5650
topMissionScore: TopMissionScoreUiModel,
5751
modifier: Modifier = Modifier,
5852
onTeamRankingClick: (teamNumber: String) -> Unit = {}
5953
) {
60-
val rankIconRes = when (topMissionScore.rank) {
61-
1 -> R.drawable.ic_rank_1
62-
2 -> R.drawable.ic_rank_2
63-
3 -> R.drawable.ic_rank_3
64-
else -> R.drawable.ic_ranking_default
65-
}
66-
val rankTextColor = if (topMissionScore.rank <= 3) Color.White else SoptTheme.colors.onSurface100
67-
6854
Column(
6955
modifier = modifier
7056
.clip(shape = RoundedCornerShape(size = 10.dp))
7157
.background(color = SoptTheme.colors.onSurface900)
72-
.padding(start = 12.dp, end = 16.dp, top = 12.dp, bottom = 8.dp)
58+
.padding(start = 12.dp, end = 16.dp, top = 12.dp, bottom = 12.dp)
7359
.noRippleClickable {
7460
onTeamRankingClick(topMissionScore.teamNumber)
7561
}
@@ -80,31 +66,12 @@ internal fun TodayScoreRaking(
8066
.align(Alignment.Start),
8167
verticalAlignment = Alignment.CenterVertically
8268
) {
83-
Box(
84-
contentAlignment = Alignment.Center
85-
) {
86-
Icon(
87-
imageVector = ImageVector.vectorResource(id = rankIconRes),
88-
contentDescription = null,
89-
tint = Color.Unspecified
90-
)
91-
Text(
92-
text = topMissionScore.rank.toString(),
93-
style = SoptTheme.typography.heading16B,
94-
color = rankTextColor,
95-
modifier = Modifier.padding(vertical = 2.dp, horizontal = 9.dp)
96-
)
97-
}
98-
9969
Text(
10070
text = topMissionScore.teamName,
10171
color = White,
10272
style = SoptTheme.typography.heading16B,
10373
overflow = TextOverflow.Ellipsis,
10474
maxLines = 1,
105-
modifier = Modifier
106-
.padding(vertical = 2.dp)
107-
.padding(start = 5.dp)
10875
)
10976
}
11077

@@ -132,7 +99,7 @@ private fun TodayScoreRakingPreview() {
13299
SoptTheme {
133100
val mockTopMissionScoreUiModel = TopMissionScoreUiModel(
134101
rank = 2,
135-
teamName = "노바",
102+
teamName = "키어로",
136103
teamNumber = "FIRST",
137104
todayPoints = 1000,
138105
totalPoints = 3000

feature/home/src/main/java/org/sopt/official/feature/home/NewHomeViewModel.kt

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@ import kotlinx.coroutines.async
3535
import kotlinx.coroutines.flow.MutableStateFlow
3636
import kotlinx.coroutines.flow.SharingStarted
3737
import kotlinx.coroutines.flow.StateFlow
38+
import kotlinx.coroutines.flow.filterNotNull
3839
import kotlinx.coroutines.flow.map
3940
import kotlinx.coroutines.flow.stateIn
4041
import kotlinx.coroutines.flow.update
4142
import kotlinx.coroutines.launch
4243
import kotlinx.coroutines.tasks.await
4344
import org.sopt.official.common.util.successOr
45+
import org.sopt.official.domain.home.AppServiceManager
4446
import org.sopt.official.domain.home.model.AppService
4547
import org.sopt.official.domain.home.model.FloatingToast
4648
import org.sopt.official.domain.home.model.RecentCalendar
@@ -68,17 +70,16 @@ import org.sopt.official.feature.home.model.HomeUiState.Unauthenticated
6870
import org.sopt.official.feature.home.model.HomeUserSoptLogDashboardModel
6971
import org.sopt.official.feature.home.model.Schedule
7072
import org.sopt.official.feature.home.model.defaultAppServices
73+
import org.sopt.official.localstorage.source.UserStorage
7174
import timber.log.Timber
7275
import javax.inject.Inject
73-
import org.sopt.official.domain.home.usecase.GetAppServiceUseCase
74-
import org.sopt.official.localstorage.source.UserStorage
7576

7677
@HiltViewModel
7778
internal class NewHomeViewModel @Inject constructor(
7879
private val homeRepository: HomeRepository,
7980
private val checkNewInPokeUseCase: CheckNewInPokeUseCase,
8081
private val registerPushTokenUseCase: RegisterPushTokenUseCase,
81-
private val getAppServiceUseCase: GetAppServiceUseCase,
82+
private val appServiceManager: AppServiceManager,
8283
private val userStorage: UserStorage,
8384
) : ViewModel() {
8485
private val viewModelState: MutableStateFlow<HomeViewModelState> =
@@ -92,25 +93,42 @@ internal class NewHomeViewModel @Inject constructor(
9293
initialValue = viewModelState.value.toUiState(),
9394
)
9495

96+
init {
97+
observeAppServices()
98+
}
99+
100+
private fun observeAppServices() {
101+
viewModelScope.launch {
102+
appServiceManager.appServices
103+
.filterNotNull()
104+
.collect { appServices ->
105+
viewModelState.update { it.copy(appServices = appServices) }
106+
}
107+
}
108+
}
109+
95110
fun refreshAll() {
96111
viewModelState.update { it.copy(isLoading = true) }
97112

98113
viewModelScope.launch {
99114
val userInfoDeferred = async { homeRepository.getUserInfo() }
100115
val userDescriptionDeferred = async { homeRepository.getHomeDescription() }
101116
val recentCalendarDeferred = async { homeRepository.getRecentCalendar() }
102-
val appServiceDeferred = async { getAppServiceUseCase() }
103117
val surveyDataDeferred = async { homeRepository.getHomeReviewForm() }
104118
val toastDataDeferred = async { homeRepository.getHomeFloatingToast() }
105119
val popularPostsDeferred = async { homeRepository.getHomePopularPosts() }
106120
val latestPostsDeferred = async { homeRepository.getHomeLatestPosts() }
121+
val appServiceDeferred = async {
122+
appServiceManager.fetchAppServices(forceUpdate = true)
123+
}
124+
125+
appServiceDeferred.await()
107126

108127
val userInfoResult = userInfoDeferred.await()
109128

110129
val userDescription =
111130
userDescriptionDeferred.await().successOr(UserInfo.UserDescription())
112131
val recentCalendar = recentCalendarDeferred.await().successOr(RecentCalendar())
113-
val appService = appServiceDeferred.await().successOr(emptyList())
114132
val surveyData = surveyDataDeferred.await().successOr(ReviewForm.default)
115133
val toastData = toastDataDeferred.await().successOr(FloatingToast.default)
116134
val popularPostsData = popularPostsDeferred.await().successOr(emptyList())
@@ -138,7 +156,6 @@ internal class NewHomeViewModel @Inject constructor(
138156
userInfo = userInfo,
139157
userDescription = userDescription,
140158
recentCalendar = recentCalendar,
141-
appServices = appService,
142159
surveyData = surveyData.toModel(),
143160
toastData = toastData.toModel(),
144161
popularPostData = popularPostsData.map { post -> post.toModel() }.toImmutableList(),

feature/main/src/main/java/org/sopt/official/feature/main/MainViewModel.kt

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,15 @@ import jakarta.inject.Inject
3131
import kotlinx.coroutines.flow.MutableStateFlow
3232
import kotlinx.coroutines.flow.StateFlow
3333
import kotlinx.coroutines.flow.asStateFlow
34+
import kotlinx.coroutines.flow.filterNotNull
3435
import kotlinx.coroutines.flow.update
3536
import kotlinx.coroutines.launch
37+
import org.sopt.official.domain.home.AppServiceManager
3638
import org.sopt.official.domain.home.model.AppService
37-
import org.sopt.official.domain.home.usecase.GetAppServiceUseCase
3839

3940
@HiltViewModel
4041
class MainViewModel @Inject constructor(
41-
private val getAppServiceUseCase: GetAppServiceUseCase
42+
private val appServiceManager: AppServiceManager
4243
) : ViewModel() {
4344
private val _mainTabs = MutableStateFlow(MainTab.getActiveTabs(emptyList()))
4445
val mainTabs: StateFlow<List<MainTab>>
@@ -49,7 +50,7 @@ class MainViewModel @Inject constructor(
4950
get() = _badgeMap.asStateFlow()
5051

5152
init {
52-
fetchMainTabs()
53+
observeAppServices()
5354
}
5455

5556
fun updateBadge(badges: Map<String, String?>) {
@@ -60,17 +61,22 @@ class MainViewModel @Inject constructor(
6061
}
6162
}
6263

63-
private fun fetchMainTabs() {
64+
private fun observeAppServices() {
6465
viewModelScope.launch {
65-
getAppServiceUseCase()
66-
.onSuccess { appServices ->
66+
appServiceManager.fetchAppServices()
67+
68+
appServiceManager.appServices
69+
.filterNotNull()
70+
.collect { appServices ->
6771
updateMainTabs(appServices)
6872
}
6973
}
7074
}
7175

7276
private fun updateMainTabs(services: List<AppService>) {
73-
val badgeByDeeplink = services.associate { it.deepLink to it.alarmBadge }
77+
val badgeByDeeplink = services.associate {
78+
it.deepLink to if (it.displayAlarmBadge) it.alarmBadge else null
79+
}
7480

7581
_mainTabs.update {
7682
MainTab.getActiveTabs(services.map { it.deepLink })

0 commit comments

Comments
 (0)