Skip to content

Commit

Permalink
[FEAT] 알림 삭제 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
kim0hoon committed Aug 26, 2023
1 parent 610e8ce commit 49fbb39
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.polzzak_android.presentation.common.model.ModelState
import com.polzzak_android.presentation.common.model.copyWithData
import com.polzzak_android.presentation.feature.notification.list.NotificationItemStateController
import com.polzzak_android.presentation.feature.notification.list.model.NotificationModel
import com.polzzak_android.presentation.feature.notification.list.model.NotificationRefreshStatusType
Expand All @@ -16,6 +17,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject

@HiltViewModel
Expand All @@ -24,6 +26,9 @@ class NotificationViewModel @Inject constructor() : ViewModel(), NotificationIte
val notificationLiveData: LiveData<ModelState<NotificationsModel>> = _notificationLiveData
private var requestNotificationJobData: NotificationJobData? = null

//TODO 추가 삭제 등 알림목록 수정 이벤트
private var updateNotificationJobMap = HashMap<Int, Job?>()

var isRefreshed = false
private set

Expand All @@ -33,6 +38,8 @@ class NotificationViewModel @Inject constructor() : ViewModel(), NotificationIte
val settingMenusLiveData: LiveData<List<SettingMenuModel>> = _settingMenusLiveData
private var requestSettingMenusJob: Job? = null

private val notificationMutex = Mutex()

init {
initNotifications()
}
Expand All @@ -43,9 +50,11 @@ class NotificationViewModel @Inject constructor() : ViewModel(), NotificationIte
else if (requestNotificationJobData?.job?.isCompleted == false) return
requestNotificationJobData = NotificationJobData(
priority = priority,
job = viewModelScope.launch {
job = createJobWithUnlockOnCompleted {
isRefreshed = true
notificationMutex.lock()
_notificationLiveData.value = ModelState.Loading(NotificationsModel())
notificationMutex.unlock()
requestNotifications()
},
)
Expand All @@ -57,11 +66,13 @@ class NotificationViewModel @Inject constructor() : ViewModel(), NotificationIte
else if (requestNotificationJobData?.job?.isCompleted == false) return
requestNotificationJobData = NotificationJobData(
priority = priority,
job = viewModelScope.launch {
job = createJobWithUnlockOnCompleted {
isRefreshed = true
notificationMutex.lock()
val prevData = notificationLiveData.value?.data ?: NotificationsModel()
_notificationLiveData.value =
ModelState.Loading(prevData.copy(refreshStatusType = NotificationRefreshStatusType.Loading))
notificationMutex.unlock()
requestNotifications()
},
)
Expand All @@ -74,24 +85,29 @@ class NotificationViewModel @Inject constructor() : ViewModel(), NotificationIte
else if (requestNotificationJobData?.job?.isCompleted == false) return
requestNotificationJobData = NotificationJobData(
priority = priority,
job = viewModelScope.launch {
job = createJobWithUnlockOnCompleted {
isRefreshed = false
notificationMutex.lock()
val prevData = notificationLiveData.value?.data ?: NotificationsModel()
_notificationLiveData.value =
ModelState.Loading(prevData.copy(refreshStatusType = NotificationRefreshStatusType.Normal))
notificationMutex.unlock()
requestNotifications()
},
)
}

//TODO test용 delay를 위해 suspend 붙여줌(제거 필요)
private suspend fun requestNotifications() {
val prevData =
notificationLiveData.value?.data.takeIf { !isRefreshed } ?: NotificationsModel()
//TODO api 연동(현재 mock data)
//onSuccess
val nextOffset = notificationLiveData.value?.data?.nextOffset.takeIf { !isRefreshed } ?: 0
delay(2000)
val nextData = getMockData(prevData.nextOffset, NOTIFICATION_PAGE_SIZE)
val nextData = getMockNotificationData(nextOffset, NOTIFICATION_PAGE_SIZE)

//onSuccess
notificationMutex.lock()
val prevData =
notificationLiveData.value?.data.takeIf { !isRefreshed } ?: NotificationsModel()
if (isRefreshed) notificationHorizontalScrollPositionMap.clear()
_notificationLiveData.value =
ModelState.Success(
Expand All @@ -100,10 +116,44 @@ class NotificationViewModel @Inject constructor() : ViewModel(), NotificationIte
refreshStatusType = NotificationRefreshStatusType.Normal
)
)
notificationMutex.unlock()
}

fun deleteNotification() {
//TODO 알림 삭제 문의 중
fun deleteNotification(id: Int) {
if (updateNotificationJobMap[id]?.isCompleted == false) return
updateNotificationJobMap[id] = createJobWithUnlockOnCompleted {
//TODO api 적용
delay(1000)
deleteMockNotificationData(id = id)

//onSuccess
notificationMutex.lock()
val updatedList = notificationLiveData.value?.data?.items?.toMutableList()?.apply {
removeIf { it.id == id }
}
val updatedData =
notificationLiveData.value?.data?.copy(items = updatedList) ?: NotificationsModel()
_notificationLiveData.value =
_notificationLiveData.value?.copyWithData(newData = updatedData)
notificationMutex.unlock()
//TODO onError 이벤트 추가(Livedata, eventWrapper 등 필요할 수도 있음)
}
}

fun addNotification(model: NotificationModel) {
if (updateNotificationJobMap[model.id]?.isCompleted == false) return
updateNotificationJobMap[model.id] = createJobWithUnlockOnCompleted {
//TODO 푸쉬알림으로 인한 알림 목록 추가

}
}

private fun createJobWithUnlockOnCompleted(action: suspend () -> Unit) = viewModelScope.launch {
action.invoke()
}.apply {
invokeOnCompletion {
if (notificationMutex.isLocked) notificationMutex.unlock()
}
}

fun requestSettingMenu() {
Expand Down Expand Up @@ -136,7 +186,7 @@ class NotificationViewModel @Inject constructor() : ViewModel(), NotificationIte
}
}

private fun getMockData(nextOffset: Int, pageSize: Int): NotificationsModel {
private fun getMockNotificationData(nextOffset: Int, pageSize: Int): NotificationsModel {
return NotificationsModel(
hasNextPage = nextOffset + pageSize < mockNotification.size,
nextOffset = nextOffset + pageSize,
Expand All @@ -147,7 +197,11 @@ private fun getMockData(nextOffset: Int, pageSize: Int): NotificationsModel {
)
}

private val mockNotification = List(187) {
private fun deleteMockNotificationData(id: Int) {
mockNotification.removeIf { it.id == id }
}

private val mockNotification = MutableList(187) {
when (it % 4) {
0 -> NotificationModel.CompleteLink(
id = it,
Expand Down Expand Up @@ -179,7 +233,7 @@ private val mockNotification = List(187) {
}
}

private val mockSettingMenus = listOf(
private val mockSettingMenus = mutableListOf(
SettingMenuModel(type = SettingMenuType.All, isChecked = false),
SettingMenuModel(type = SettingMenuType.Menu.Link, isChecked = false),
SettingMenuModel(type = SettingMenuType.Menu.Level, isChecked = false),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.polzzak_android.presentation.feature.notification.list

interface NotificationListClickListener {
fun onClickDeleteNotification(id: Int)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import com.polzzak_android.presentation.common.util.BindableItemAdapter
import com.polzzak_android.presentation.common.util.toPx
import com.polzzak_android.presentation.feature.notification.NotificationViewModel
import com.polzzak_android.presentation.feature.notification.list.NotificationItemDecoration
import com.polzzak_android.presentation.feature.notification.list.NotificationListClickListener
import com.polzzak_android.presentation.feature.notification.list.item.NotificationEmptyItem
import com.polzzak_android.presentation.feature.notification.list.item.NotificationItem
import com.polzzak_android.presentation.feature.notification.list.item.NotificationRefreshItem
Expand All @@ -29,7 +30,8 @@ import dagger.hilt.android.AndroidEntryPoint

//TODO 하단 네비게이션 바 만큼 marign 필요
@AndroidEntryPoint
abstract class BaseNotificationListFragment : BaseFragment<FragmentNotificationListBinding>() {
abstract class BaseNotificationListFragment : BaseFragment<FragmentNotificationListBinding>(),
NotificationListClickListener {
override val layoutResId: Int = R.layout.fragment_notification_list

private val notificationViewModel by viewModels<NotificationViewModel>(ownerProducer = {
Expand Down Expand Up @@ -166,11 +168,16 @@ abstract class BaseNotificationListFragment : BaseFragment<FragmentNotificationL
return if (data.isNullOrEmpty()) listOf(NotificationEmptyItem()) else data.map {
NotificationItem(
model = it,
itemStateController = notificationViewModel
itemStateController = notificationViewModel,
clickListener = this@BaseNotificationListFragment
)
}
}

override fun onClickDeleteNotification(id: Int) {
notificationViewModel.deleteNotification(id = id)
}

companion object {
private const val LOADING_SKELETON_ITEM_COUNT = 5
private const val NOTIFICATIONS_PADDING_DP = 16
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import com.polzzak_android.presentation.common.util.BindableItem
import com.polzzak_android.presentation.common.util.loadCircleImageUrl
import com.polzzak_android.presentation.common.util.toPx
import com.polzzak_android.presentation.feature.notification.list.NotificationItemStateController
import com.polzzak_android.presentation.feature.notification.list.NotificationListClickListener
import com.polzzak_android.presentation.feature.notification.list.model.NotificationModel

//TODO 버튼 클릭 상태 적용
class NotificationItem(
private val model: NotificationModel,
private val itemStateController: NotificationItemStateController
private val itemStateController: NotificationItemStateController,
private val clickListener: NotificationListClickListener
) : BindableItem<ItemNotificationBinding>() {
override val layoutRes: Int = R.layout.item_notification
override fun areItemsTheSame(other: BindableItem<*>): Boolean =
Expand All @@ -32,7 +34,7 @@ class NotificationItem(
tvDate.text = model.date
tvContent.text = model.content
ivBtnRemoveNotification.setOnClickListener {

clickListener.onClickDeleteNotification(id = model.id)
}
bindBtnLayout(binding = binding)
bindProfile(binding = binding)
Expand Down

0 comments on commit 49fbb39

Please sign in to comment.