Skip to content

Commit

Permalink
Merge pull request #54 from Team-Wable/feature/#53-notification-api-p…
Browse files Browse the repository at this point in the history
…aging

[Feature/#53] : notification api paging
  • Loading branch information
Eonji-sw authored Sep 4, 2024
2 parents 22b48d6 + 6d19089 commit 8b560a2
Show file tree
Hide file tree
Showing 19 changed files with 352 additions and 200 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.teamwable.data.mapper.toModel

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

Expand All @@ -25,5 +25,6 @@ internal fun ResponseInformationDto.toNotificationInformationModel(): Notificati
NotificationInformationModel(
infoNotificationType = infoNotificationType,
time = time,
imageUrl = imageUrl
imageUrl = imageUrl,
infoNotificationId = infoNotificationId,
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package com.teamwable.data.repository

import androidx.paging.PagingData
import com.teamwable.model.notification.NotificationActionModel
import com.teamwable.model.notification.NotificationInformationModel
import kotlinx.coroutines.flow.Flow

interface NotificationRepository {
suspend fun getNumber(): Result<Int>
suspend fun patchCheck(): Result<Boolean>
fun getNotifications(): Flow<PagingData<NotificationActionModel>>
fun getInformation(): Flow<PagingData<NotificationInformationModel>>
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
package com.teamwable.data.repositoryimpl

import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.map
import com.teamwable.data.mapper.toModel.toNotificationActionModel
import com.teamwable.data.mapper.toModel.toNotificationInformationModel
import com.teamwable.data.repository.NotificationRepository
import com.teamwable.model.notification.NotificationActionModel
import com.teamwable.model.notification.NotificationInformationModel
import com.teamwable.network.datasource.NotificationService
import com.teamwable.network.util.GenericPagingSource
import com.teamwable.network.util.handleThrowable
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject

internal class DefaultNotificationRepository @Inject constructor(
Expand All @@ -19,4 +30,26 @@ internal class DefaultNotificationRepository @Inject constructor(
notificationService.patchCheck().success
}.onFailure { return it.handleThrowable() }
}

override fun getNotifications(): Flow<PagingData<NotificationActionModel>> {
return Pager(PagingConfig(pageSize = 15, prefetchDistance = 1)) {
GenericPagingSource(
apiCall = { cursor -> notificationService.getNotifications(cursor).data },
getNextCursor = { notifications -> notifications.lastOrNull()?.notificationId },
)
}.flow.map { pagingData ->
pagingData.map { it.toNotificationActionModel() }
}
}

override fun getInformation(): Flow<PagingData<NotificationInformationModel>> {
return Pager(PagingConfig(pageSize = 15, prefetchDistance = 1)) {
GenericPagingSource(
apiCall = { cursor -> notificationService.getInformation(cursor).data },
getNextCursor = { information -> information.lastOrNull()?.infoNotificationId },
)
}.flow.map { pagingData ->
pagingData.map { it.toNotificationInformationModel() }
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.teamwable.model
package com.teamwable.model.notification

data class NotificationActionModel(
val memberId: Int,
Expand All @@ -11,6 +11,6 @@ data class NotificationActionModel(
val notificationText: String,
val isNotificationChecked: Boolean,
val isDeleted: Boolean,
val notificationId: Int,
val notificationId: Long,
val triggerMemberId: Int,
)
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.teamwable.model
package com.teamwable.model.notification

data class NotificationInformationModel(
val infoNotificationType: String,
val time: String,
val imageUrl: String,
val infoNotificationId: Long,
)
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface NotificationService {
@Query(value = "cursor") notificationId: Long = -1,
): BaseResponse<List<ResponseNotificationsDto>>

@GET("api/v1/notification/info/all")
@GET("api/v1/notification/info")
suspend fun getInformation(
@Query(value = "cursor") notificationId: Long = -1,
): BaseResponse<List<ResponseInformationDto>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import kotlinx.serialization.Serializable

@Serializable
data class ResponseInformationDto(
@SerialName("InfoNotificationType")
@SerialName("infoNotificationType")
val infoNotificationType: String = "",
@SerialName("time")
val time: String = "",
@SerialName("imageUrl")
val imageUrl: String = "",
@SerialName("infoNotificationId")
val infoNotificationId: Long = -1,
)
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ data class ResponseNotificationsDto(
@SerialName("isDeleted")
val isDeleted: Boolean = false,
@SerialName("notificationId")
val notificationId: Int = -1,
val notificationId: Long = -1,
@SerialName("triggerMemberId")
val triggerMemberId: Int = -1,
)
1 change: 1 addition & 0 deletions feature/notification/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ dependencies {
implementation(libs.androidx.navigation.fragment.ktx)
implementation(libs.androidx.navigation.ui.ktx)
implementation(libs.swipe.refresh.layout)
implementation(libs.paging)

// Third Party
implementation(libs.coil.core)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,21 @@ package com.teamwable.notification

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import com.teamwable.common.uistate.UiState
import com.teamwable.data.repository.NotificationRepository
import com.teamwable.model.NotificationActionModel
import com.teamwable.model.NotificationInformationModel
import com.teamwable.model.notification.NotificationActionModel
import com.teamwable.model.notification.NotificationInformationModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
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(private val notificationRepository: NotificationRepository) : ViewModel() {
val mockNotificationEmptyList = emptyList<Any>()

val mockNotificationActionList = listOf(
NotificationActionModel(0, "배차은우", "배차은우", "", "contentLiked", "2024-08-18 00:00:00", 0, "안녕? 난 차은우보다 잘생긴 배 차은우 하하", false, false, 1, 0),
NotificationActionModel(0, "배차은우", "배차은우", "", "comment", "2024-08-17 06:50:00", 0, "안녕? 난 배 차은우", false, false, 1, 0),
NotificationActionModel(0, "배차은우", "배차은우", "", "commentLiked", "2024-08-17 00:00:00", 0, "안녕? 난 배 차은우", false, false, 1, 0),
NotificationActionModel(0, "배차은우", "배차은우", "", "actingContinue", "2024-08-16 20:00:00", 0, "안녕? 난 배 차은우", false, false, 1, 0),
NotificationActionModel(0, "배차은우", "배차은우", "", "beGhost", "2024-08-16 18:00:00", 0, "안녕? 난 배 차은우", false, false, 1, 0),
NotificationActionModel(0, "배차은우", "배차은우", "", "contentGhost", "2024-08-14 00:00:00", 0, "안녕? 난 배 차은우", false, false, 1, 0),
NotificationActionModel(0, "배차은우", "배차은우", "", "commentGhost", "2024-08-13 00:00:00", 0, "안녕? 난 배 차은우", false, false, 1, 0),
NotificationActionModel(0, "배차은우", "배차은우", "", "userBan", "2024-08-13 00:00:00", 0, "안녕? 난 배 차은우", false, false, 1, 0),
NotificationActionModel(0, "배차은우", "배차은우", "", "popularWriter", "2024-08-12 00:00:00", 0, "안녕? 난 배 차은우", false, false, 1, 0),
)

val mockNotificationInformationList = listOf(
NotificationInformationModel("GAMEDONE", "2024-08-18 00:00:00", ""),
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()

Expand All @@ -47,4 +27,8 @@ class NotificationViewModel
.onSuccess { _checkUiState.emit(UiState.Success(it)) }
.onFailure { _checkUiState.emit(UiState.Failure(it.message.toString())) }
}

fun getNotifications(): Flow<PagingData<NotificationActionModel>> = notificationRepository.getNotifications()

fun getInformation(): Flow<PagingData<NotificationInformationModel>> = notificationRepository.getInformation()
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,27 @@
package com.teamwable.notification.action

import android.content.Context
import android.os.Build
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.annotation.RequiresApi
import androidx.recyclerview.widget.ListAdapter
import com.teamwable.model.NotificationActionModel
import androidx.paging.PagingDataAdapter
import com.teamwable.model.notification.NotificationActionModel
import com.teamwable.notification.databinding.ItemNotificationVpBinding
import com.teamwable.ui.extensions.ItemDiffCallback

class NotificationActionAdapter(
context: Context,
private val click: (NotificationActionModel, Int) -> Unit
) : ListAdapter<NotificationActionModel, NotificationActionViewHolder>(
private val onNotificationClick: (NotificationActionModel, Int) -> Unit,
private val onProfileClick: (Int) -> Unit,
) : PagingDataAdapter<NotificationActionModel, NotificationActionViewHolder>(
NotificationActionAdapterDiffCallback,
) {
private val inflater by lazy { LayoutInflater.from(context) }

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotificationActionViewHolder {
val binding = ItemNotificationVpBinding.inflate(inflater, parent, false)
return NotificationActionViewHolder(binding, click)
return NotificationActionViewHolder.from(parent, onNotificationClick, onProfileClick)
}

@RequiresApi(Build.VERSION_CODES.O)
override fun onBindViewHolder(holder: NotificationActionViewHolder, position: Int) {
holder.bind(getItem(position))
getItem(position)?.let { holder.bind(it) }
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,27 @@ 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.stringOf
import com.teamwable.ui.extensions.toast
import com.teamwable.ui.extensions.viewLifeCycle
import com.teamwable.ui.extensions.viewLifeCycleScope
import com.teamwable.ui.extensions.visible
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import timber.log.Timber

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

override fun initView() {
setupCheckObserve()

initNotificationActionAdapter()

setupCheckObserve()
}

private fun setupCheckObserve() {
Expand All @@ -36,31 +40,61 @@ class NotificationActionFragment : BindingFragment<FragmentNotificationVpBinding
}


private fun initNotificationActionAdapter() {
if (viewModel.mockNotificationActionList.isEmpty()) {
binding.llNotificationVpEmpty.visible(true)
} else {
binding.llNotificationVpEmpty.visible(false)
private fun initNotificationActionAdapter() = with(binding) {
notificationAdapter = NotificationActionAdapter(
onNotificationClick = { notificationActionData, position ->
when (notificationActionData.notificationTriggerType) {
requireContext().stringOf(NotificationActionType.CONTENT_LIKED.title) -> toast("contentLiked") // Todo : 게시글 상세 이동
requireContext().stringOf(NotificationActionType.COMMENT.title) -> toast("comment") // Todo : 게시글 상세 이동
requireContext().stringOf(NotificationActionType.COMMENT_LIKED.title) -> toast("commentLiked") // Todo : 게시글 상세 이동
requireContext().stringOf(NotificationActionType.ACTING_CONTINUE.title) -> toast("actingContinue") // Todo : 글쓰기 이동
requireContext().stringOf(NotificationActionType.BE_GHOST.title) -> toast("beGhost") // Todo : 게시글 상세 이동
requireContext().stringOf(NotificationActionType.CONTENT_GHOST.title) -> toast("contentGhost") // Todo : 게시글 상세 이동
requireContext().stringOf(NotificationActionType.COMMENT_GHOST.title) -> toast("commentGhost") // Todo : 게시글 상세 이동
requireContext().stringOf(NotificationActionType.USER_BAN.title) -> toast("userBan") // Todo : Unit
requireContext().stringOf(NotificationActionType.POPULAR_WRITER.title) -> toast("popularWriter") // Todo : 게시글 상세 이동
requireContext().stringOf(NotificationActionType.POPULAR_CONTENT.title) -> toast("popularContent") // Todo : 게시글 상세 이동
}
},
onProfileClick = {
toast("profile") // Todo : 마페뷰 이동
})

rvNotificationContent.adapter = notificationAdapter
if (rvNotificationContent.itemDecorationCount == 0) {
rvNotificationContent.addItemDecoration(NotificationItemDecorator(requireContext()))
}

submitList()
setEmptyLayout()
setSwipeLayout()
}

private fun submitList() {
viewLifeCycleScope.launch {
viewModel.getNotifications().collectLatest { pagingData ->
notificationAdapter.submitData(pagingData)
}
viewModel.patchCheck()
}
}

binding.rvNotificationContent.adapter =
NotificationActionAdapter(requireContext(), click = { notificationActionData, position ->
when (notificationActionData.notificationTriggerType) {
"contentLiked" -> toast("contentLiked")
"comment" -> toast("comment")
"commentLiked" -> toast("commentLiked")
"actingContinue" -> toast("actingContinue")
"beGhost" -> toast("beGhost")
"contentGhost" -> toast("contentGhost")
"commentGhost" -> toast("commentGhost")
"userBan" -> toast("userBan")
"popularWriter" -> toast("popularWriter")
}
}).apply {
submitList(viewModel.mockNotificationActionList)
private fun setEmptyLayout() = with(binding) {
notificationAdapter.addLoadStateListener { combinedLoadStates ->
if (combinedLoadStates.append.endOfPaginationReached) {
if (notificationAdapter.itemCount < 1) {
llNotificationVpEmpty.visible(true)
} else {
llNotificationVpEmpty.visible(false)
}
binding.rvNotificationContent.addItemDecoration(NotificationItemDecorator(requireContext()))
}
}
}

viewModel.patchCheck()
private fun setSwipeLayout() = with(binding) {
swipeNotificationVp.setOnRefreshListener {
notificationAdapter.refresh()
swipeNotificationVp.isRefreshing = false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.teamwable.notification.action

import androidx.annotation.StringRes
import com.teamwable.notification.R

enum class NotificationActionType(@StringRes val title: Int, @StringRes val content: Int) {
CONTENT_LIKED(title = R.string.label_notification_action_content_liked, content = R.string.tv_notification_action_content_liked),
COMMENT(title = R.string.label_notification_action_feed_comment, content = R.string.tv_notification_action_feed_comment),
COMMENT_LIKED(title = R.string.label_notification_action_comment_liked, content = R.string.tv_notification_action_comment_liked),
ACTING_CONTINUE(title = R.string.label_notification_action_acting_continue, content = R.string.tv_notification_action_acting_continue),
BE_GHOST(title = R.string.label_notification_action_be_ghost, content = R.string.tv_notification_action_be_ghost),
CONTENT_GHOST(title = R.string.label_notification_action_content_ghost, content = R.string.tv_notification_action_content_ghost),
COMMENT_GHOST(title = R.string.label_notification_action_comment_ghost, content = R.string.tv_notification_action_comment_ghost),
USER_BAN(title = R.string.label_notification_action_user_ban, content = R.string.tv_notification_action_user_ban),
POPULAR_WRITER(title = R.string.label_notification_action_popular_writer, content = R.string.tv_notification_action_popular_writer),
POPULAR_CONTENT(title = R.string.label_notification_action_popular_content, content = R.string.tv_notification_action_popular_content),
}
Loading

0 comments on commit 8b560a2

Please sign in to comment.