Skip to content

Commit

Permalink
Merge pull request #124 from Team-Wable/feature/#121-news-number-api
Browse files Browse the repository at this point in the history
Feature/#121 news number api
  • Loading branch information
Eonji-sw authored Nov 26, 2024
2 parents 78dd7c8 + fae3d68 commit a987de3
Show file tree
Hide file tree
Showing 15 changed files with 219 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ interface NewsRepository {
fun getNewsInfo(): Flow<PagingData<NewsInfoModel>>

fun getNoticeInfo(): Flow<PagingData<NewsInfoModel>>

suspend fun getNumber(): Result<Map<String, Int>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ interface UserInfoRepository {

fun getIsAdmin(): Flow<Boolean>

fun getNewsNumber(): Flow<Int>

fun getNoticeNumber(): Flow<Int>

suspend fun saveAccessToken(accessToken: String)

suspend fun saveRefreshToken(refreshToken: String)
Expand All @@ -35,6 +39,10 @@ interface UserInfoRepository {

suspend fun saveIsAdmin(isAdmin: Boolean)

suspend fun saveNewsNumber(newsNumber: Int)

suspend fun saveNoticeNumber(noticeNumber: Int)

suspend fun clearAll()

suspend fun clearForRefreshToken()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,13 @@ internal class DefaultNewsRepository @Inject constructor(
pagingData.map { it.toNoticeInfoModel() }
}
}

override suspend fun getNumber(): Result<Map<String, Int>> {
return runCatching {
mapOf(
"news" to newsService.getNumber().data.newsNumber,
"notice" to newsService.getNumber().data.noticeNumber
)
}.onFailure { return it.handleThrowable() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ internal class DefaultUserInfoRepository @Inject constructor(
override fun getIsAdmin(): Flow<Boolean> =
wablePreferencesDataSource.isAdmin

override fun getNewsNumber(): Flow<Int> =
wablePreferencesDataSource.newsNumber

override fun getNoticeNumber(): Flow<Int> =
wablePreferencesDataSource.noticeNumber

override suspend fun saveAccessToken(accessToken: String) {
wablePreferencesDataSource.updateAccessToken(accessToken)
}
Expand Down Expand Up @@ -64,6 +70,14 @@ internal class DefaultUserInfoRepository @Inject constructor(
wablePreferencesDataSource.updateIsAdmin(isAdmin)
}

override suspend fun saveNewsNumber(newsNumber: Int) {
wablePreferencesDataSource.updateNewsNumber(newsNumber)
}

override suspend fun saveNoticeNumber(noticeNumber: Int) {
wablePreferencesDataSource.updateNoticeNumber(noticeNumber)
}

override suspend fun clearAll() {
wablePreferencesDataSource.clear()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class DefaultWablePreferenceDatasource @Inject constructor(
val MemberProfileUrl = stringPreferencesKey("memberProfileUrl")
val IsPushAlarmAllowed = booleanPreferencesKey("isPushAlarmAllowed")
val IsAdmin = booleanPreferencesKey("isAdmin")
val NewsNumber = intPreferencesKey("newsNumber")
val NoticeNumber = intPreferencesKey("noticeNumber")
}

override val accessToken: Flow<String> = dataStore.data
Expand Down Expand Up @@ -76,6 +78,18 @@ class DefaultWablePreferenceDatasource @Inject constructor(
preferences[PreferencesKeys.IsAdmin] ?: false
}

override val newsNumber: Flow<Int> = dataStore.data
.catch { handleError(it) }
.map { preferences ->
preferences[PreferencesKeys.NewsNumber] ?: -1
}

override val noticeNumber: Flow<Int> = dataStore.data
.catch { handleError(it) }
.map { preferences ->
preferences[PreferencesKeys.NoticeNumber] ?: -1
}

override suspend fun updateAccessToken(accessToken: String) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.AccessToken] = accessToken
Expand Down Expand Up @@ -124,6 +138,18 @@ class DefaultWablePreferenceDatasource @Inject constructor(
}
}

override suspend fun updateNewsNumber(newsNumber: Int) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.NewsNumber] = newsNumber
}
}

override suspend fun updateNoticeNumber(noticeNumber: Int) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.NoticeNumber] = noticeNumber
}
}

override suspend fun clear() {
dataStore.edit { preferences ->
preferences.clear()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ interface WablePreferencesDataSource {
val memberProfileUrl: Flow<String>
val isPushAlarmAllowed: Flow<Boolean>
val isAdmin: Flow<Boolean>
val newsNumber: Flow<Int>
val noticeNumber: Flow<Int>

suspend fun updateAccessToken(accessToken: String)

Expand All @@ -28,6 +30,10 @@ interface WablePreferencesDataSource {

suspend fun updateIsAdmin(isAdmin: Boolean)

suspend fun updateNewsNumber(newsNumber: Int)

suspend fun updateNoticeNumber(noticeNumber: Int)

suspend fun clear()

suspend fun clearForRefreshToken()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.teamwable.network.datasource

import com.teamwable.network.dto.response.main.ResponseNewsNumberDto
import com.teamwable.network.dto.response.news.ResponseGameTypeDto
import com.teamwable.network.dto.response.news.ResponseNewsInfoDto
import com.teamwable.network.dto.response.news.ResponseNoticeInfoDto
Expand Down Expand Up @@ -28,4 +29,7 @@ interface NewsService {
suspend fun getNoticeInfo(
@Query(value = "cursor") contentId: Long = -1,
): BaseResponse<List<ResponseNoticeInfoDto>>

@GET("api/v1/information/number")
suspend fun getNumber(): BaseResponse<ResponseNewsNumberDto>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ package com.teamwable.network.datasource

import com.teamwable.network.dto.response.notification.ResponseInformationDto
import com.teamwable.network.dto.response.notification.ResponseNotificationsDto
import com.teamwable.network.dto.response.notification.ResponseNumberDto
import com.teamwable.network.dto.response.notification.ResponseNotificationNumberDto
import com.teamwable.network.util.BaseResponse
import retrofit2.http.GET
import retrofit2.http.PATCH
import retrofit2.http.Query

interface NotificationService {
@GET("api/v1/notification/number")
suspend fun getNumber(): BaseResponse<ResponseNumberDto>
suspend fun getNumber(): BaseResponse<ResponseNotificationNumberDto>

@PATCH("api/v1/notification-check")
suspend fun patchCheck(): BaseResponse<Unit>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.teamwable.network.dto.response.main

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ResponseNewsNumberDto(
@SerialName("newsNumber")
val newsNumber: Int = 0,
@SerialName("noticeNumber")
val noticeNumber: Int = 0
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ResponseNumberDto(
data class ResponseNotificationNumberDto(
@SerialName("notificationNumber")
val notificationNumber: Int = 0
)
79 changes: 67 additions & 12 deletions feature/news/src/main/java/com/teamwable/news/NewsFragment.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.teamwable.news

import androidx.fragment.app.viewModels
import androidx.lifecycle.flowWithLifecycle
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import com.teamwable.common.uistate.UiState
import com.teamwable.common.util.AmplitudeNewsTag.CLICK_GAMESCHEDULE
import com.teamwable.common.util.AmplitudeNewsTag.CLICK_NEWS
import com.teamwable.common.util.AmplitudeNewsTag.CLICK_NOTICE
Expand All @@ -13,19 +16,69 @@ import com.teamwable.ui.extensions.colorOf
import com.teamwable.ui.extensions.statusBarColorOf
import com.teamwable.ui.extensions.statusBarModeOf
import com.teamwable.ui.extensions.stringOf
import com.teamwable.ui.extensions.viewLifeCycle
import com.teamwable.ui.extensions.viewLifeCycleScope
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import timber.log.Timber

@AndroidEntryPoint
class NewsFragment : BindingFragment<FragmentNewsBinding>(FragmentNewsBinding::inflate) {
private val viewModel: NewsViewModel by viewModels()

private var serverNewsNumber = -1
private var serverNoticeNumber = -1

override fun initView() {
statusBarColorOf(com.teamwable.ui.R.color.black)
statusBarModeOf(false)

initNewsViewPagerAdapter()
initTabClickListener()

setBadgeOnNews(NewsTabType.NEWS.idx, true)
setBadgeOnNews(NewsTabType.NOTICE.idx, true)
setupNumberObserve()
}

private fun setupNumberObserve() {
viewModel.newsNumberUiState.flowWithLifecycle(viewLifeCycle).onEach { state ->
when (state) {
is UiState.Success -> {
serverNewsNumber = getServerNumber(state, "news")
serverNoticeNumber = getServerNumber(state, "notice")

saveNumberFromServerToLocal()
}

else -> Unit
}
}.launchIn(viewLifeCycleScope)
}

private fun getServerNumber(state: UiState.Success<Map<String, Int>>, idx: String) =
state.data[idx]?.takeIf { it >= 0 } ?: 0


private suspend fun saveNumberFromServerToLocal() {
// viewModel.saveNewsNumber(1)
// viewModel.saveNoticeNumber(2)

val localNewsNumber = viewModel.getNewsNumberFromLocal()
val localNoticeNumber = viewModel.getNoticeNumberFromLocal()

if (serverNewsNumber > localNewsNumber) {
Timber.tag("here").d("news server: $serverNewsNumber, local: $localNewsNumber")
setBadgeOnNews(NewsTabType.NEWS.ordinal, true)
} else {
Timber.tag("here").d("equal news server: $serverNewsNumber, local: $localNewsNumber")
}

if (serverNoticeNumber > localNoticeNumber) {
Timber.tag("here").d("notice server: $serverNoticeNumber, local: $localNoticeNumber")
setBadgeOnNews(NewsTabType.NOTICE.ordinal, true)
} else {
Timber.tag("here").d("equal notice server: $serverNoticeNumber, local: $localNoticeNumber")
}
}

private fun setBadgeOnNews(idx: Int, isVisible: Boolean) {
Expand All @@ -41,10 +94,10 @@ class NewsFragment : BindingFragment<FragmentNewsBinding>(FragmentNewsBinding::i
vpNews.adapter = NewsViewPagerAdapter(this@NewsFragment)
TabLayoutMediator(tlNews, vpNews) { tab, position ->
when (position) {
NewsTabType.MATCH.idx -> tab.text = stringOf(R.string.tv_news_tab_match)
NewsTabType.RANK.idx -> tab.text = stringOf(R.string.tv_news_tab_rank)
NewsTabType.NEWS.idx -> tab.text = stringOf(R.string.tv_news_tab_news)
NewsTabType.NOTICE.idx -> tab.text = stringOf(R.string.tv_news_tab_notice)
NewsTabType.MATCH.ordinal -> tab.text = stringOf(R.string.tv_news_tab_match)
NewsTabType.RANK.ordinal -> tab.text = stringOf(R.string.tv_news_tab_rank)
NewsTabType.NEWS.ordinal -> tab.text = stringOf(R.string.tv_news_tab_news)
NewsTabType.NOTICE.ordinal -> tab.text = stringOf(R.string.tv_news_tab_notice)
}
}.attach()
}
Expand All @@ -54,16 +107,18 @@ class NewsFragment : BindingFragment<FragmentNewsBinding>(FragmentNewsBinding::i
binding.tlNews.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
when (tab?.position) {
NewsTabType.MATCH.idx -> trackEvent(CLICK_GAMESCHEDULE)
NewsTabType.RANK.idx -> trackEvent(CLICK_RANKING)
NewsTabType.NEWS.idx -> {
NewsTabType.MATCH.ordinal -> trackEvent(CLICK_GAMESCHEDULE)
NewsTabType.RANK.ordinal -> trackEvent(CLICK_RANKING)
NewsTabType.NEWS.ordinal -> {
trackEvent(CLICK_NEWS)
setBadgeOnNews(NewsTabType.NEWS.idx, false)
setBadgeOnNews(NewsTabType.NEWS.ordinal, false)
viewModel.saveNewsNumber(serverNewsNumber)
}

NewsTabType.NOTICE.idx -> {
NewsTabType.NOTICE.ordinal -> {
trackEvent(CLICK_NOTICE)
setBadgeOnNews(NewsTabType.NOTICE.idx, false)
setBadgeOnNews(NewsTabType.NOTICE.ordinal, false)
viewModel.saveNoticeNumber(serverNoticeNumber)
}
}
}
Expand Down
12 changes: 5 additions & 7 deletions feature/news/src/main/java/com/teamwable/news/NewsTabType.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package com.teamwable.news

import androidx.annotation.StringRes

enum class NewsTabType(@StringRes val idx: Int) {
MATCH(0),
RANK(1),
NEWS(2),
NOTICE(3),
enum class NewsTabType {
MATCH,
RANK,
NEWS,
NOTICE,
}
35 changes: 34 additions & 1 deletion feature/news/src/main/java/com/teamwable/news/NewsViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,22 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.teamwable.common.uistate.UiState
import com.teamwable.data.repository.NewsRepository
import com.teamwable.data.repository.UserInfoRepository
import com.teamwable.model.news.NewsMatchModel
import com.teamwable.model.news.NewsRankModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class NewsViewModel
@Inject constructor(private val newsRepository: NewsRepository) : ViewModel() {
@Inject constructor(
private val newsRepository: NewsRepository,
private val userInfoRepository: UserInfoRepository
) : ViewModel() {
private val _gameTypeUiState = MutableStateFlow<UiState<String>>(UiState.Loading)
val gameTypeUiState = _gameTypeUiState.asStateFlow()

Expand All @@ -24,8 +29,12 @@ class NewsViewModel
private val _rankUiState = MutableStateFlow<UiState<List<NewsRankModel>>>(UiState.Loading)
val rankUiState = _rankUiState.asStateFlow()

private val _newsNumberUiState = MutableStateFlow<UiState<Map<String, Int>>>(UiState.Loading)
val newsNumberUiState = _newsNumberUiState.asStateFlow()

init {
getGameType()
getNewsNumber()
}

private fun getGameType() {
Expand All @@ -51,4 +60,28 @@ class NewsViewModel
.onFailure { _rankUiState.value = UiState.Failure(it.message.toString()) }
}
}

private fun getNewsNumber() {
viewModelScope.launch {
newsRepository.getNumber()
.onSuccess { _newsNumberUiState.value = UiState.Success(it) }
.onFailure { _newsNumberUiState.value = UiState.Failure(it.message.toString()) }
}
}

suspend fun getNewsNumberFromLocal() = userInfoRepository.getNewsNumber().first()

fun saveNewsNumber(newsNumber: Int) {
viewModelScope.launch {
userInfoRepository.saveNewsNumber(newsNumber)
}
}

suspend fun getNoticeNumberFromLocal() = userInfoRepository.getNoticeNumber().first()

fun saveNoticeNumber(noticeNumber: Int) {
viewModelScope.launch {
userInfoRepository.saveNoticeNumber(noticeNumber)
}
}
}
Loading

0 comments on commit a987de3

Please sign in to comment.