diff --git a/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/datasource/AppjamtampDataSource.kt b/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/datasource/AppjamtampDataSource.kt index 4912061f3..47e31cc9a 100644 --- a/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/datasource/AppjamtampDataSource.kt +++ b/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/datasource/AppjamtampDataSource.kt @@ -1,6 +1,7 @@ package org.sopt.official.data.appjamtamp.datasource import org.sopt.official.data.appjamtamp.dto.response.AppjamtampMissionsResponseDto +import org.sopt.official.data.appjamtamp.dto.response.AppjamtampMyAppjamInfoResponseDto import org.sopt.official.data.appjamtamp.dto.response.AppjamtampPostStampResponseDto import org.sopt.official.data.appjamtamp.dto.response.AppjamtampStampResponseDto import org.sopt.official.data.appjamtamp.dto.response.AppjamtampTop10MissionScoreResponse @@ -24,6 +25,8 @@ interface AppjamtampDataSource { activityDate: String ): AppjamtampPostStampResponseDto + suspend fun getMyAppjamInfo(): AppjamtampMyAppjamInfoResponseDto + suspend fun getAppjamtampMissionTop3(size: Int): AppjamtampTop3RecentMissionResponse suspend fun getAppjamtampMissionRanking(size: Int): AppjamtampTop10MissionScoreResponse diff --git a/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/datasourceimpl/AppjamtampDataSourceImpl.kt b/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/datasourceimpl/AppjamtampDataSourceImpl.kt index 7b2212265..a3e7a558b 100644 --- a/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/datasourceimpl/AppjamtampDataSourceImpl.kt +++ b/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/datasourceimpl/AppjamtampDataSourceImpl.kt @@ -4,6 +4,7 @@ import javax.inject.Inject import org.sopt.official.data.appjamtamp.datasource.AppjamtampDataSource import org.sopt.official.data.appjamtamp.dto.request.AppjamtampPostStampRequestDto import org.sopt.official.data.appjamtamp.dto.response.AppjamtampMissionsResponseDto +import org.sopt.official.data.appjamtamp.dto.response.AppjamtampMyAppjamInfoResponseDto import org.sopt.official.data.appjamtamp.dto.response.AppjamtampPostStampResponseDto import org.sopt.official.data.appjamtamp.dto.response.AppjamtampStampResponseDto import org.sopt.official.data.appjamtamp.dto.response.AppjamtampTop10MissionScoreResponse @@ -38,6 +39,8 @@ internal class AppjamtampDataSourceImpl @Inject constructor( ) ) + override suspend fun getMyAppjamInfo(): AppjamtampMyAppjamInfoResponseDto = appjamtampService.getMyAppjamInfo() + override suspend fun getAppjamtampMissionTop3(size: Int): AppjamtampTop3RecentMissionResponse = appjamtampService.getAppjamtampMissionTop3(size = size) diff --git a/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/dto/response/AppjamtampMyAppjamInfoResponseDto.kt b/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/dto/response/AppjamtampMyAppjamInfoResponseDto.kt new file mode 100644 index 000000000..58b054797 --- /dev/null +++ b/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/dto/response/AppjamtampMyAppjamInfoResponseDto.kt @@ -0,0 +1,25 @@ +package org.sopt.official.data.appjamtamp.dto.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.sopt.official.domain.appjamtamp.entity.AppjamtampMyAppjamInfoEntity + +@Serializable +data class AppjamtampMyAppjamInfoResponseDto( + @SerialName("teamNumber") + val teamNumber: String, + + @SerialName("teamName") + val teamName: String, + + @SerialName("isAppjamJoined") + val isAppjamJoined: Boolean +) { + fun toEntity(): AppjamtampMyAppjamInfoEntity { + return AppjamtampMyAppjamInfoEntity( + teamNumber = teamNumber, + teamName = teamName, + isAppjamJoined = isAppjamJoined + ) + } +} diff --git a/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/repository/AppjamtampRepositoryImpl.kt b/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/repository/AppjamtampRepositoryImpl.kt index 00db1d5ad..862671061 100644 --- a/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/repository/AppjamtampRepositoryImpl.kt +++ b/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/repository/AppjamtampRepositoryImpl.kt @@ -7,6 +7,7 @@ import org.sopt.official.data.appjamtamp.mapper.toDomain import org.sopt.official.data.appjamtamp.mapper.toEntity import org.sopt.official.domain.appjamtamp.entity.AppjamtampMissionListEntity import org.sopt.official.domain.appjamtamp.entity.AppjamtampMissionScore +import org.sopt.official.domain.appjamtamp.entity.AppjamtampMyAppjamInfoEntity import org.sopt.official.domain.appjamtamp.entity.AppjamtampRecentMission import org.sopt.official.domain.appjamtamp.entity.AppjamtampStampEntity import org.sopt.official.domain.appjamtamp.repository.AppjamtampRepository @@ -42,6 +43,10 @@ internal class AppjamtampRepositoryImpl @Inject constructor( ) } + override suspend fun getMyAppjamInfo(): Result = suspendRunCatching { + appjamtampDataSource.getMyAppjamInfo().toEntity() + } + override suspend fun getAppjamtampMissionRanking( size: Int ): Result> = suspendRunCatching { diff --git a/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/service/AppjamtampService.kt b/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/service/AppjamtampService.kt index 97e22cc16..19eb3fca3 100644 --- a/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/service/AppjamtampService.kt +++ b/data/appjamtamp/src/main/java/org/sopt/official/data/appjamtamp/service/AppjamtampService.kt @@ -2,6 +2,7 @@ package org.sopt.official.data.appjamtamp.service import org.sopt.official.data.appjamtamp.dto.request.AppjamtampPostStampRequestDto import org.sopt.official.data.appjamtamp.dto.response.AppjamtampMissionsResponseDto +import org.sopt.official.data.appjamtamp.dto.response.AppjamtampMyAppjamInfoResponseDto import org.sopt.official.data.appjamtamp.dto.response.AppjamtampPostStampResponseDto import org.sopt.official.data.appjamtamp.dto.response.AppjamtampStampResponseDto import org.sopt.official.data.appjamtamp.dto.response.AppjamtampTop10MissionScoreResponse @@ -29,6 +30,9 @@ interface AppjamtampService { @Body body: AppjamtampPostStampRequestDto ): AppjamtampPostStampResponseDto + @GET("user/appjam-info") + suspend fun getMyAppjamInfo(): AppjamtampMyAppjamInfoResponseDto + /** * 앱잼에 참여하는 전체 팀의 득점 랭킹 조회 * - 서버 명세에서 API명: 앱잼팀 득점 랭킹 TOP10 조회 diff --git a/domain/appjamtamp/src/main/java/org/sopt/official/domain/appjamtamp/entity/AppjamtampMyAppjamInfoEntity.kt b/domain/appjamtamp/src/main/java/org/sopt/official/domain/appjamtamp/entity/AppjamtampMyAppjamInfoEntity.kt new file mode 100644 index 000000000..274e7734d --- /dev/null +++ b/domain/appjamtamp/src/main/java/org/sopt/official/domain/appjamtamp/entity/AppjamtampMyAppjamInfoEntity.kt @@ -0,0 +1,7 @@ +package org.sopt.official.domain.appjamtamp.entity + +data class AppjamtampMyAppjamInfoEntity( + val teamNumber: String, + val teamName: String, + val isAppjamJoined: Boolean +) diff --git a/domain/appjamtamp/src/main/java/org/sopt/official/domain/appjamtamp/repository/AppjamtampRepository.kt b/domain/appjamtamp/src/main/java/org/sopt/official/domain/appjamtamp/repository/AppjamtampRepository.kt index 41269cebd..db36bcaa7 100644 --- a/domain/appjamtamp/src/main/java/org/sopt/official/domain/appjamtamp/repository/AppjamtampRepository.kt +++ b/domain/appjamtamp/src/main/java/org/sopt/official/domain/appjamtamp/repository/AppjamtampRepository.kt @@ -2,6 +2,7 @@ package org.sopt.official.domain.appjamtamp.repository import org.sopt.official.domain.appjamtamp.entity.AppjamtampMissionListEntity import org.sopt.official.domain.appjamtamp.entity.AppjamtampMissionScore +import org.sopt.official.domain.appjamtamp.entity.AppjamtampMyAppjamInfoEntity import org.sopt.official.domain.appjamtamp.entity.AppjamtampRecentMission import org.sopt.official.domain.appjamtamp.entity.AppjamtampStampEntity @@ -23,6 +24,8 @@ interface AppjamtampRepository { activityDate: String ): Result + suspend fun getMyAppjamInfo(): Result + suspend fun getAppjamtampMissionRanking( size: Int ): Result> diff --git a/feature/soptlog/build.gradle.kts b/feature/soptlog/build.gradle.kts index 42cc5b18a..d999b4319 100644 --- a/feature/soptlog/build.gradle.kts +++ b/feature/soptlog/build.gradle.kts @@ -46,6 +46,7 @@ dependencies { implementation(projects.core.analytics) // domain + implementation(projects.domain.appjamtamp) implementation(projects.domain.soptlog) implementation(projects.domain.poke) diff --git a/feature/soptlog/src/main/java/org/sopt/official/feature/soptlog/SoptLogScreen.kt b/feature/soptlog/src/main/java/org/sopt/official/feature/soptlog/SoptLogScreen.kt index ed0645346..a964b7fca 100644 --- a/feature/soptlog/src/main/java/org/sopt/official/feature/soptlog/SoptLogScreen.kt +++ b/feature/soptlog/src/main/java/org/sopt/official/feature/soptlog/SoptLogScreen.kt @@ -71,7 +71,7 @@ internal fun SoptLogRoute( viewModel: SoptLogViewModel = hiltViewModel() ) { LaunchedEffect(Unit) { - viewModel.getSoptLogInfo() + viewModel.getSoptLogInfoData() } LaunchedEffect(Unit) { @@ -95,18 +95,20 @@ internal fun SoptLogRoute( val soptLogState by viewModel.soptLogInfo.collectAsStateWithLifecycle() val soptLogInfo = soptLogState.soptLogInfo + val isAppjamJoined by viewModel.isAppjamJoined.collectAsStateWithLifecycle(false) val todayFortuneText by viewModel.todayFortuneText.collectAsStateWithLifecycle("") val tracker = LocalTracker.current when { soptLogState.isLoading -> LoadingIndicator() soptLogState.isError -> { - SoptLogErrorDialog(onCheckClick = viewModel::getSoptLogInfo) + SoptLogErrorDialog(onCheckClick = viewModel::getSoptLogInfoData) } else -> { with(receiver = soptLogState.soptLogInfo) { SoptlogScreen( + isAppjamJoined = isAppjamJoined, soptLogInfo = soptLogInfo, todayFortuneText = todayFortuneText, onNavigationClick = viewModel::onNavigationClick, @@ -126,6 +128,7 @@ internal fun SoptLogRoute( @OptIn(ExperimentalMaterial3Api::class) @Composable private fun SoptlogScreen( + isAppjamJoined: Boolean, soptLogInfo: SoptLogInfo, todayFortuneText: String, onNavigationClick: (url: String) -> Unit, @@ -164,7 +167,9 @@ private fun SoptlogScreen( Spacer(modifier = Modifier.height(36.dp)) SoptlogContents { - if (soptLogInfo.isActive) { + // 활동 기수 여부에 관계없이 앱잼참여 회원만 보여줌 (앱잼탬프 기간만) + // 일반 솝탬프의 경우는 (기존) soptLogInfo.isActive인 경우에만 보여줌 (활동 기수인 경우에만) + if (isAppjamJoined) { SoptLogSection( title = "솝탬프 로그", items = MySoptLogItemType.entries.filter { it.category == SoptLogCategory.SOPTAMP }.toImmutableList(), @@ -243,6 +248,7 @@ private fun PreviewSoptlogScreen() { } SoptlogScreen( + isAppjamJoined = false, soptLogInfo = soptLogInfo, todayFortuneText = "오늘의 운세", onNavigationClick = { url -> diff --git a/feature/soptlog/src/main/java/org/sopt/official/feature/soptlog/SoptLogViewModel.kt b/feature/soptlog/src/main/java/org/sopt/official/feature/soptlog/SoptLogViewModel.kt index ec21b450d..3cf10979c 100644 --- a/feature/soptlog/src/main/java/org/sopt/official/feature/soptlog/SoptLogViewModel.kt +++ b/feature/soptlog/src/main/java/org/sopt/official/feature/soptlog/SoptLogViewModel.kt @@ -28,6 +28,7 @@ import androidx.core.net.toUri import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.async import kotlinx.coroutines.channels.Channel import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow @@ -37,6 +38,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import org.sopt.official.domain.appjamtamp.repository.AppjamtampRepository import org.sopt.official.domain.poke.entity.ApiResult import org.sopt.official.domain.poke.entity.CheckNewInPoke import org.sopt.official.domain.poke.usecase.CheckNewInPokeUseCase @@ -49,6 +51,7 @@ import timber.log.Timber @HiltViewModel class SoptLogViewModel @Inject constructor( private val soptLogRepository: SoptLogRepository, + private val appjamtampRepository: AppjamtampRepository, private val checkNewInPokeUseCase: CheckNewInPokeUseCase, ) : ViewModel() { private val _soptLogInfo = MutableStateFlow(SoptLogState()) @@ -57,6 +60,10 @@ class SoptLogViewModel @Inject constructor( val todayFortuneText = _soptLogInfo.map { it.soptLogInfo.todayFortuneText } + private val _isAppjamJoined = MutableStateFlow(false) + val isAppjamJoined: StateFlow + get() = _isAppjamJoined.asStateFlow() + private val _navigationEvent = Channel() val navigationEvent = _navigationEvent.receiveAsFlow() @@ -99,6 +106,34 @@ class SoptLogViewModel @Inject constructor( } } + // 나의 앱잼 정보도 같이 가져옴 (앱잼탬프 기간만) + fun getSoptLogInfoData() { + viewModelScope.launch { + _soptLogInfo.update { it.copy(isLoading = true) } + + val soptLogDeferred = async { soptLogRepository.getSoptLogInfo() } + val appjamInfoDeferred = async { appjamtampRepository.getMyAppjamInfo() } + + val soptLogResult = soptLogDeferred.await() + val appjamResult = appjamInfoDeferred.await() + + if (soptLogResult.isSuccess && appjamResult.isSuccess) { + _soptLogInfo.update { + it.copy( + soptLogInfo = soptLogResult.getOrThrow(), + isLoading = false, + isError = false + ) + } + _isAppjamJoined.value = appjamResult.getOrThrow().isAppjamJoined + } else { + val error = soptLogResult.exceptionOrNull() ?: appjamResult.exceptionOrNull() ?: Exception("Unknown error") + Timber.e(error) + _soptLogInfo.update { it.copy(isLoading = false, isError = true) } + } + } + } + fun getSoptLogInfo() { viewModelScope.launch { _soptLogInfo.update { diff --git a/feature/soptlog/src/main/java/org/sopt/official/feature/soptlog/model/MySoptLogItemType.kt b/feature/soptlog/src/main/java/org/sopt/official/feature/soptlog/model/MySoptLogItemType.kt index ca5ad5c7a..200b255e8 100644 --- a/feature/soptlog/src/main/java/org/sopt/official/feature/soptlog/model/MySoptLogItemType.kt +++ b/feature/soptlog/src/main/java/org/sopt/official/feature/soptlog/model/MySoptLogItemType.kt @@ -35,7 +35,8 @@ internal enum class MySoptLogItemType( val hasArrow: Boolean = true ) { // 솝탬프 로그 - COMPLETED_MISSION(title = "완료미션", category = SoptLogCategory.SOPTAMP, url = "soptamp", count = { it.soptampCount ?: 0 }), + // 일반 솝탬프의 경우는 (기존) url = "soptamp" / 앱잼탬프만 appjamtamp 사용 (앱잼탬프 기간만) + COMPLETED_MISSION(title = "완료미션", category = SoptLogCategory.SOPTAMP, url = "appjamtamp", count = { it.soptampCount ?: 0 }), VIEW_COUNT(title = "조회수", category = SoptLogCategory.SOPTAMP, hasHelpIcon = true, hasArrow = false, count = { it.viewCount ?: 0 }), RECEIVED_CLAP(title = "받은 박수", category = SoptLogCategory.SOPTAMP, hasArrow = false, count = { it.myClapCount ?: 0 }), SENT_CLAP(title = "쳐준 박수", category = SoptLogCategory.SOPTAMP, hasArrow = false, count = { it.clapCount ?: 0 }), diff --git a/feature/soptlog/src/main/java/org/sopt/official/feature/soptlog/navigation/SoptLogUrl.kt b/feature/soptlog/src/main/java/org/sopt/official/feature/soptlog/navigation/SoptLogUrl.kt index e7a155298..2e8b2cfa7 100644 --- a/feature/soptlog/src/main/java/org/sopt/official/feature/soptlog/navigation/SoptLogUrl.kt +++ b/feature/soptlog/src/main/java/org/sopt/official/feature/soptlog/navigation/SoptLogUrl.kt @@ -26,7 +26,7 @@ package org.sopt.official.feature.soptlog.navigation enum class SoptLogUrl(val url: String) { POKE("home/poke"), - SOPTAMP("soptamp"), + SOPTAMP("appjamtamp"), // 일반 솝탬프의 경우는 (기존) url = "soptamp" / 앱잼탬프만 appjamtamp 사용 (앱잼탬프 기간만) POKE_FRIEND_SUMMARY("home/poke/friend-list-summary"), UNKNOWN("");