Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature/#47] notification api #49

Merged
merged 18 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f833046
#47 [FEAT] : notification repository κ΅¬ν˜„
Eonji-sw Aug 22, 2024
9cb25f0
#47 [FEAT] : notification service κ΅¬ν˜„
Eonji-sw Aug 22, 2024
44a990c
#47 [FEAT] : notification repository impl κ΅¬ν˜„
Eonji-sw Aug 22, 2024
ea07a37
#47 [FEAT] : notification responseInformationDto κ΅¬ν˜„
Eonji-sw Aug 22, 2024
94ebaab
#47 [FEAT] : notification mapper κ΅¬ν˜„
Eonji-sw Aug 22, 2024
a4a8c7d
#47 [FEAT] : notification responseNotificationsDto κ΅¬ν˜„
Eonji-sw Aug 22, 2024
a8033e5
#47 [FEAT] : notification responseNumberDto κ΅¬ν˜„
Eonji-sw Aug 22, 2024
9bbb8d7
#47 [FEAT] : notification repositoryModule μΆ”κ°€
Eonji-sw Aug 22, 2024
733b9ed
#47 [FEAT] : notification serviceyModule μΆ”κ°€
Eonji-sw Aug 22, 2024
e789258
#47 [FEAT] : notification viewModel API μ—°κ²°
Eonji-sw Aug 22, 2024
811cea5
#47 [FEAT] : notification viewModel API μ—°κ²°
Eonji-sw Aug 22, 2024
5344e6b
#47 [FEAT] : notification action API μ—°κ²°
Eonji-sw Aug 22, 2024
9b69d36
#47 [FEAT] : notification information API μ—°κ²°
Eonji-sw Aug 22, 2024
5cde1c4
#47 [FEAT] : notification main API μ—°κ²°
Eonji-sw Aug 22, 2024
8f1e27f
#47 [MOD] : notification information glide μ—°κ²°
Eonji-sw Aug 22, 2024
733668f
#47 [UI] : notification swipe refresh animation κ΅¬ν˜„
Eonji-sw Aug 22, 2024
f598448
#47 [MOD] : ν•„μš”μ—†λŠ” μ½”λ“œ undo
Eonji-sw Aug 23, 2024
a1a0639
#47 [MOD] : getInformation νŽ˜μ΄μ§• query μΆ”κ°€
Eonji-sw Aug 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package com.teamwable.data.di

import com.teamwable.data.repository.AuthRepository
import com.teamwable.data.repository.DummyRepository
import com.teamwable.data.repository.NotificationRepository
import com.teamwable.data.repository.UserInfoRepository
import com.teamwable.data.repositoryimpl.DefaultAuthRepository
import com.teamwable.data.repositoryimpl.DefaultDummyRepository
import com.teamwable.data.repositoryimpl.DefaultNotificationRepository
import com.teamwable.data.repositoryimpl.DefaultUserInfoRepository
import com.teamwable.datastore.datasource.DefaultWablePreferenceDatasource
import com.teamwable.datastore.datasource.WablePreferencesDataSource
Expand Down Expand Up @@ -38,4 +40,8 @@ internal abstract class RepositoryModule {
abstract fun bindsAuthRepository(
repositoryImpl: DefaultAuthRepository,
): AuthRepository

@Binds
@Singleton
abstract fun bindsNotificationRepository(repositoryImpl: DefaultNotificationRepository): NotificationRepository
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.teamwable.data.mapper.toModel

import com.teamwable.model.NotificationActionModel
import com.teamwable.model.NotificationInformationModel
import com.teamwable.network.dto.response.notification.ResponseInformationDto
import com.teamwable.network.dto.response.notification.ResponseNotificationsDto

internal fun ResponseNotificationsDto.toNotificationActionModel(): NotificationActionModel =
NotificationActionModel(
memberId = memberId,
memberNickname = memberNickname,
triggerMemberNickname = triggerMemberNickname,
triggerMemberProfileUrl = triggerMemberProfileUrl,
notificationTriggerType = notificationTriggerType,
time = time,
notificationTriggerId = notificationTriggerId,
notificationText = notificationText ?: "",
isNotificationChecked = isNotificationChecked,
isDeleted = isDeleted,
notificationId = notificationId,
triggerMemberId = triggerMemberId
)

internal fun ResponseInformationDto.toNotificationInformationModel(): NotificationInformationModel =
NotificationInformationModel(
infoNotificationType = infoNotificationType,
time = time,
imageUrl = imageUrl
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.teamwable.data.repository

interface NotificationRepository {
suspend fun getNumber(): Result<Int>
suspend fun patchCheck(): Result<Boolean>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.teamwable.data.repositoryimpl

import com.teamwable.data.repository.NotificationRepository
import com.teamwable.network.datasource.NotificationService
import com.teamwable.network.util.handleThrowable
import javax.inject.Inject

internal class DefaultNotificationRepository @Inject constructor(
private val notificationService: NotificationService,
) : NotificationRepository {
override suspend fun getNumber(): Result<Int> {
return runCatching {
notificationService.getNumber().data.notificationNumber
}.onFailure { return it.handleThrowable() }
}

override suspend fun patchCheck(): Result<Boolean> {
return runCatching {
notificationService.patchCheck().success
}.onFailure { return it.handleThrowable() }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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.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>

@PATCH("api/v1/notification-check")
suspend fun patchCheck(): BaseResponse<Unit>

@GET("api/v1/notifications")
suspend fun getNotifications(
@Query(value = "cursor") notificationId: Long = -1,
): BaseResponse<List<ResponseNotificationsDto>>

@GET("api/v1/notification/info/all")
suspend fun getInformation(
@Query(value = "cursor") notificationId: Long = -1,
): BaseResponse<List<ResponseInformationDto>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.teamwable.network.di

import com.teamwable.network.datasource.AuthService
import com.teamwable.network.datasource.DummyService
import com.teamwable.network.datasource.NotificationService
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -23,4 +24,10 @@ internal object ServiceModule {
fun provideAuthService(
@WithoutTokenInterceptor retrofit: Retrofit,
): AuthService = retrofit.create(AuthService::class.java)

@Singleton
@Provides
fun provideNotificationService(
@WableRetrofit retrofit: Retrofit,
): NotificationService = retrofit.create(NotificationService::class.java)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.teamwable.network.dto.response.notification

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

@Serializable
data class ResponseInformationDto(
@SerialName("InfoNotificationType")
val infoNotificationType: String = "",
@SerialName("time")
val time: String = "",
@SerialName("imageUrl")
val imageUrl: String = "",
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.teamwable.network.dto.response.notification

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

@Serializable
data class ResponseNotificationsDto(
@SerialName("memberId")
val memberId: Int = -1,
@SerialName("memberNickname")
val memberNickname: String = "",
@SerialName("triggerMemberNickname")
val triggerMemberNickname: String = "",
@SerialName("triggerMemberProfileUrl")
val triggerMemberProfileUrl: String = "",
@SerialName("notificationTriggerType")
val notificationTriggerType: String = "",
@SerialName("time")
val time: String = "",
@SerialName("notificationTriggerId")
val notificationTriggerId: Int = -1,
@SerialName("notificationText")
val notificationText: String? = "",
@SerialName("isNotificationChecked")
val isNotificationChecked: Boolean = false,
@SerialName("isDeleted")
val isDeleted: Boolean = false,
@SerialName("notificationId")
val notificationId: Int = -1,
@SerialName("triggerMemberId")
val triggerMemberId: Int = -1,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.teamwable.network.dto.response.notification

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

@Serializable
data class ResponseNumberDto(
@SerialName("notificationNumber")
val notificationNumber: Int = 0
)
29 changes: 26 additions & 3 deletions feature/main/src/main/java/com/teamwable/main/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,27 @@ import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
import androidx.navigation.NavDestination
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController
import com.teamwable.common.uistate.UiState
import com.teamwable.main.databinding.ActivityMainBinding
import com.teamwable.ui.extensions.colorOf
import com.teamwable.ui.extensions.visible
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import timber.log.Timber

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val viewModel: MainViewModel by viewModels()

@SuppressLint("SourceLockedOrientationActivity")
override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -30,16 +38,31 @@ class MainActivity : AppCompatActivity() {

private fun initView() {
setBottomNavigation()

setupNumberObserve()
}

private fun setupNumberObserve() {
viewModel.notificationNumberUiState.flowWithLifecycle(lifecycle).onEach {
when (it) {
is UiState.Success -> {
if (it.data > 0) {
setBadgeOnNotification(true)
} else if (it.data < 0) {
Timber.tag("main").e("μ•Œλ§žμ§€ μ•Šμ€ notification number get : ${it.data}")
}
}

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

private fun setBottomNavigation() {
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.fcv_main) as NavHostFragment
val navController = navHostFragment.navController

// TODO : λ‚˜μ€‘μ— BADGE 보이게 ν•˜λŠ” 둜직으둜 이동
setBadgeOnNotification(true)

binding.bnvMain.apply {
itemIconTintList = null
setupWithNavController(navController)
Expand Down
31 changes: 31 additions & 0 deletions feature/main/src/main/java/com/teamwable/main/MainViewModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.teamwable.main

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.teamwable.common.uistate.UiState
import com.teamwable.data.repository.NotificationRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class MainViewModel
@Inject constructor(private val notificationRepository: NotificationRepository) : ViewModel() {
private val _notificationNumberUiState = MutableStateFlow<UiState<Int>>(UiState.Empty)
val notificationNumberUiState = _notificationNumberUiState.asStateFlow()

init {
getNotificationNumber()
}

private fun getNotificationNumber() {
viewModelScope.launch {
_notificationNumberUiState.value = UiState.Loading
notificationRepository.getNumber()
.onSuccess { _notificationNumberUiState.value = UiState.Success(it) }
.onFailure { _notificationNumberUiState.value = UiState.Failure(it.message.toString()) }
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
package com.teamwable.notification

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.teamwable.common.uistate.UiState
import com.teamwable.data.repository.NotificationRepository
import com.teamwable.model.NotificationActionModel
import com.teamwable.model.NotificationInformationModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class NotificationViewModel
@Inject constructor() : ViewModel() {
@Inject constructor(private val notificationRepository: NotificationRepository) : ViewModel() {
val mockNotificationEmptyList = emptyList<Any>()

val mockNotificationActionList = listOf(
Expand All @@ -28,4 +36,15 @@ class NotificationViewModel
NotificationInformationModel("GAMESTART", "2024-08-18 00:00:00", ""),
NotificationInformationModel("WEEKDONE", "2024-08-18 00:00:00", ""),
)

private val _checkUiState = MutableSharedFlow<UiState<Boolean>>()
val checkUiState = _checkUiState.asSharedFlow()

fun patchCheck() =
viewModelScope.launch {
_checkUiState.emit(UiState.Loading)
notificationRepository.patchCheck()
.onSuccess { _checkUiState.emit(UiState.Success(it)) }
.onFailure { _checkUiState.emit(UiState.Failure(it.message.toString())) }
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,40 @@
package com.teamwable.notification.action

import androidx.fragment.app.viewModels
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import com.teamwable.common.uistate.UiState
import com.teamwable.notification.NotificationItemDecorator
import com.teamwable.notification.NotificationViewModel
import com.teamwable.notification.databinding.FragmentNotificationVpBinding
import com.teamwable.ui.base.BindingFragment
import com.teamwable.ui.extensions.toast
import com.teamwable.ui.extensions.visible
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import timber.log.Timber

@AndroidEntryPoint
class NotificationActionFragment : BindingFragment<FragmentNotificationVpBinding>(FragmentNotificationVpBinding::inflate) {
private val viewModel: NotificationViewModel by viewModels()

override fun initView() {
setupCheckObserve()

initNotificationActionAdapter()
}

private fun setupCheckObserve() {
viewModel.checkUiState.flowWithLifecycle(lifecycle).onEach {
when (it) {
is UiState.Success -> Timber.tag("notification").i("patch 성곡 : ${it.data}")
else -> Unit
}
}.launchIn(lifecycleScope)
}


private fun initNotificationActionAdapter() {
if (viewModel.mockNotificationActionList.isEmpty()) {
binding.llNotificationVpEmpty.visible(true)
Expand All @@ -41,5 +59,7 @@ class NotificationActionFragment : BindingFragment<FragmentNotificationVpBinding
}
binding.rvNotificationContent.addItemDecoration(NotificationItemDecorator(requireContext()))
}

viewModel.patchCheck()
}
}
Loading