forked from github/wulkanowy-mirror
Add notifications center (#1524)
This commit is contained in:
@ -24,6 +24,7 @@ import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||
import io.github.wulkanowy.ui.modules.notificationscenter.NotificationsCenterFragment
|
||||
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
|
||||
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
|
||||
import io.github.wulkanowy.utils.capitalise
|
||||
@ -120,6 +121,7 @@ class DashboardFragment : BaseFragment<FragmentDashboardBinding>(R.layout.fragme
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.dashboard_menu_tiles -> presenter.onDashboardTileSettingsSelected()
|
||||
R.id.dashboard_menu_notifaction_list -> presenter.onNotificationsCenterSelected()
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
@ -182,6 +184,10 @@ class DashboardFragment : BaseFragment<FragmentDashboardBinding>(R.layout.fragme
|
||||
if (::presenter.isInitialized) presenter.onViewReselected()
|
||||
}
|
||||
|
||||
override fun openNotificationsCenterView() {
|
||||
(requireActivity() as MainActivity).pushView(NotificationsCenterFragment.newInstance())
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
dashboardAdapter.clearTimers()
|
||||
presenter.onDetachView()
|
||||
|
@ -209,6 +209,11 @@ class DashboardPresenter @Inject constructor(
|
||||
view?.showErrorDetailsDialog(lastError)
|
||||
}
|
||||
|
||||
fun onNotificationsCenterSelected(): Boolean {
|
||||
view?.openNotificationsCenterView()
|
||||
return true
|
||||
}
|
||||
|
||||
fun onDashboardTileSettingsSelected(): Boolean {
|
||||
view?.showDashboardTileSettings(preferencesRepository.selectedDashboardTiles.toList())
|
||||
return true
|
||||
|
@ -23,4 +23,6 @@ interface DashboardView : BaseView {
|
||||
fun resetView()
|
||||
|
||||
fun popViewToRoot()
|
||||
|
||||
fun openNotificationsCenterView()
|
||||
}
|
@ -87,7 +87,7 @@ class NotificationDebugPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun withStudent(block: (Student) -> Unit) {
|
||||
private fun withStudent(block: suspend (Student) -> Unit) {
|
||||
launch {
|
||||
block(studentRepository.getCurrentStudent(false))
|
||||
}
|
||||
|
@ -0,0 +1,62 @@
|
||||
package io.github.wulkanowy.ui.modules.notificationscenter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Notification
|
||||
import io.github.wulkanowy.databinding.ItemNotificationsCenterBinding
|
||||
import io.github.wulkanowy.services.sync.notifications.NotificationType
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import javax.inject.Inject
|
||||
|
||||
class NotificationsCenterAdapter @Inject constructor() :
|
||||
ListAdapter<Notification, NotificationsCenterAdapter.ViewHolder>(DiffUtilCallback()) {
|
||||
|
||||
var onItemClickListener: (NotificationType) -> Unit = {}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
|
||||
ItemNotificationsCenterBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val item = getItem(position)
|
||||
|
||||
with(holder.binding) {
|
||||
notificationsCenterItemTitle.text = item.title
|
||||
notificationsCenterItemContent.text = item.content
|
||||
notificationsCenterItemDate.text = item.date.toFormattedString("HH:mm, d MMM")
|
||||
notificationsCenterItemIcon.setImageResource(item.type.toDrawableResId())
|
||||
|
||||
root.setOnClickListener { onItemClickListener(item.type) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun NotificationType.toDrawableResId() = when (this) {
|
||||
NotificationType.NEW_CONFERENCE -> R.drawable.ic_more_conferences
|
||||
NotificationType.NEW_EXAM -> R.drawable.ic_main_exam
|
||||
NotificationType.NEW_GRADE_DETAILS -> R.drawable.ic_stat_grade
|
||||
NotificationType.NEW_GRADE_PREDICTED -> R.drawable.ic_stat_grade
|
||||
NotificationType.NEW_GRADE_FINAL -> R.drawable.ic_stat_grade
|
||||
NotificationType.NEW_HOMEWORK -> R.drawable.ic_more_homework
|
||||
NotificationType.NEW_LUCKY_NUMBER -> R.drawable.ic_stat_luckynumber
|
||||
NotificationType.NEW_MESSAGE -> R.drawable.ic_stat_message
|
||||
NotificationType.NEW_NOTE -> R.drawable.ic_stat_note
|
||||
NotificationType.NEW_ANNOUNCEMENT -> R.drawable.ic_all_about
|
||||
NotificationType.PUSH -> R.drawable.ic_stat_all
|
||||
}
|
||||
|
||||
class ViewHolder(val binding: ItemNotificationsCenterBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
private class DiffUtilCallback : DiffUtil.ItemCallback<Notification>() {
|
||||
|
||||
override fun areContentsTheSame(oldItem: Notification, newItem: Notification) =
|
||||
oldItem == newItem
|
||||
|
||||
override fun areItemsTheSame(oldItem: Notification, newItem: Notification) =
|
||||
oldItem.id == newItem.id
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
package io.github.wulkanowy.ui.modules.notificationscenter
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Notification
|
||||
import io.github.wulkanowy.databinding.FragmentNotificationsCenterBinding
|
||||
import io.github.wulkanowy.services.sync.notifications.NotificationType
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.conference.ConferenceFragment
|
||||
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
|
||||
import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||
import io.github.wulkanowy.ui.modules.note.NoteFragment
|
||||
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class NotificationsCenterFragment :
|
||||
BaseFragment<FragmentNotificationsCenterBinding>(R.layout.fragment_notifications_center),
|
||||
NotificationsCenterView, MainView.TitledView {
|
||||
|
||||
@Inject
|
||||
lateinit var presenter: NotificationsCenterPresenter
|
||||
|
||||
@Inject
|
||||
lateinit var notificationsCenterAdapter: NotificationsCenterAdapter
|
||||
|
||||
companion object {
|
||||
|
||||
fun newInstance() = NotificationsCenterFragment()
|
||||
}
|
||||
|
||||
override val titleStringId: Int
|
||||
get() = R.string.notifications_center_title
|
||||
|
||||
override val isViewEmpty: Boolean
|
||||
get() = notificationsCenterAdapter.itemCount == 0
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentNotificationsCenterBinding.bind(view)
|
||||
presenter.onAttachView(this)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
notificationsCenterAdapter.onItemClickListener = { notificationType ->
|
||||
notificationType.toDestinationFragment()
|
||||
?.let { (requireActivity() as MainActivity).pushView(it) }
|
||||
}
|
||||
|
||||
with(binding.notificationsCenterRecycler) {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = notificationsCenterAdapter
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateData(data: List<Notification>) {
|
||||
notificationsCenterAdapter.submitList(data)
|
||||
}
|
||||
|
||||
override fun showEmpty(show: Boolean) {
|
||||
binding.notificationsCenterEmpty.isVisible = show
|
||||
}
|
||||
|
||||
override fun showProgress(show: Boolean) {
|
||||
binding.notificationsCenterProgress.isVisible = show
|
||||
}
|
||||
|
||||
override fun showContent(show: Boolean) {
|
||||
binding.notificationsCenterRecycler.isVisible = show
|
||||
}
|
||||
|
||||
override fun showErrorView(show: Boolean) {
|
||||
binding.notificationCenterError.isVisible = show
|
||||
}
|
||||
|
||||
override fun setErrorDetails(message: String) {
|
||||
binding.notificationCenterErrorMessage.text = message
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun NotificationType.toDestinationFragment(): Fragment? = when (this) {
|
||||
NotificationType.NEW_CONFERENCE -> ConferenceFragment.newInstance()
|
||||
NotificationType.NEW_EXAM -> ExamFragment.newInstance()
|
||||
NotificationType.NEW_GRADE_DETAILS -> GradeFragment.newInstance()
|
||||
NotificationType.NEW_GRADE_PREDICTED -> GradeFragment.newInstance()
|
||||
NotificationType.NEW_GRADE_FINAL -> GradeFragment.newInstance()
|
||||
NotificationType.NEW_HOMEWORK -> HomeworkFragment.newInstance()
|
||||
NotificationType.NEW_LUCKY_NUMBER -> LuckyNumberFragment.newInstance()
|
||||
NotificationType.NEW_MESSAGE -> MessageFragment.newInstance()
|
||||
NotificationType.NEW_NOTE -> NoteFragment.newInstance()
|
||||
NotificationType.NEW_ANNOUNCEMENT -> SchoolAnnouncementFragment.newInstance()
|
||||
NotificationType.PUSH -> null
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package io.github.wulkanowy.ui.modules.notificationscenter
|
||||
|
||||
import io.github.wulkanowy.data.repositories.NotificationRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class NotificationsCenterPresenter @Inject constructor(
|
||||
private val notificationRepository: NotificationRepository,
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository
|
||||
) : BasePresenter<NotificationsCenterView>(errorHandler, studentRepository) {
|
||||
|
||||
private lateinit var lastError: Throwable
|
||||
|
||||
override fun onAttachView(view: NotificationsCenterView) {
|
||||
super.onAttachView(view)
|
||||
view.initView()
|
||||
Timber.i("Notifications centre view was initialized")
|
||||
errorHandler.showErrorMessage = ::showErrorViewOnError
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onRetry() {
|
||||
view?.run {
|
||||
showErrorView(false)
|
||||
showProgress(true)
|
||||
}
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onDetailsClick() {
|
||||
view?.showErrorDetailsDialog(lastError)
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
Timber.i("Loading notifications data started")
|
||||
|
||||
flow {
|
||||
val studentId = studentRepository.getCurrentStudent(false).id
|
||||
emitAll(notificationRepository.getNotifications(studentId))
|
||||
}
|
||||
.map { notificationList -> notificationList.sortedByDescending { it.date } }
|
||||
.catch { Timber.i("Loading notifications result: An exception occurred") }
|
||||
.onEach {
|
||||
Timber.i("Loading notifications result: Success")
|
||||
|
||||
if (it.isEmpty()) {
|
||||
view?.run {
|
||||
showContent(false)
|
||||
showProgress(false)
|
||||
showEmpty(true)
|
||||
}
|
||||
} else {
|
||||
view?.run {
|
||||
showContent(true)
|
||||
showProgress(false)
|
||||
showEmpty(false)
|
||||
updateData(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
.launch()
|
||||
}
|
||||
|
||||
private fun showErrorViewOnError(message: String, error: Throwable) {
|
||||
view?.run {
|
||||
if (isViewEmpty) {
|
||||
lastError = error
|
||||
setErrorDetails(message)
|
||||
showErrorView(true)
|
||||
showEmpty(false)
|
||||
} else showError(message, error)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package io.github.wulkanowy.ui.modules.notificationscenter
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Notification
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface NotificationsCenterView : BaseView {
|
||||
|
||||
val isViewEmpty: Boolean
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<Notification>)
|
||||
|
||||
fun showProgress(show: Boolean)
|
||||
|
||||
fun showEmpty(show: Boolean)
|
||||
|
||||
fun showContent(show: Boolean)
|
||||
|
||||
fun showErrorView(show: Boolean)
|
||||
|
||||
fun setErrorDetails(message: String)
|
||||
}
|
@ -184,6 +184,7 @@ class NotificationsFragment : PreferenceFragmentCompat(),
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
setNotificationPiggybackPreferenceChecked(false)
|
||||
}
|
||||
.setOnDismissListener { setNotificationPiggybackPreferenceChecked(false) }
|
||||
.show()
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user