1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2024-09-20 01:59:10 -05:00

Dashboard fixes (#1463)

This commit is contained in:
Rafał Borcz 2021-09-05 23:24:03 +02:00 committed by GitHub
parent 2b55ec02ff
commit 77c5330f91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 433 additions and 420 deletions

View File

@ -33,10 +33,16 @@ class GradeRepository @Inject constructor(
private val cacheKey = "grade"
fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
fun getGrades(
student: Student,
semester: Semester,
forceRefresh: Boolean,
notify: Boolean = false
) = networkBoundResource(
mutex = saveFetchResultMutex,
shouldFetch = { (details, summaries) ->
val isShouldBeRefreshed = refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester))
val isShouldBeRefreshed =
refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester))
details.isEmpty() || summaries.isEmpty() || forceRefresh || isShouldBeRefreshed
},
query = {
@ -59,8 +65,14 @@ class GradeRepository @Inject constructor(
}
)
private suspend fun refreshGradeDetails(student: Student, oldGrades: List<Grade>, newDetails: List<Grade>, notify: Boolean) {
val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date ?: student.registrationDate.toLocalDate()
private suspend fun refreshGradeDetails(
student: Student,
oldGrades: List<Grade>,
newDetails: List<Grade>,
notify: Boolean
) {
val notifyBreakDate =
oldGrades.maxByOrNull { it.date }?.date ?: student.registrationDate.toLocalDate()
gradeDb.deleteAll(oldGrades uniqueSubtract newDetails)
gradeDb.insertAll((newDetails uniqueSubtract oldGrades).onEach {
if (it.date >= notifyBreakDate) it.apply {
@ -70,10 +82,15 @@ class GradeRepository @Inject constructor(
})
}
private suspend fun refreshGradeSummaries(oldSummaries: List<GradeSummary>, newSummary: List<GradeSummary>, notify: Boolean) {
private suspend fun refreshGradeSummaries(
oldSummaries: List<GradeSummary>,
newSummary: List<GradeSummary>,
notify: Boolean
) {
gradeSummaryDb.deleteAll(oldSummaries uniqueSubtract newSummary)
gradeSummaryDb.insertAll((newSummary uniqueSubtract oldSummaries).onEach { summary ->
val oldSummary = oldSummaries.find { oldSummary -> oldSummary.subject == summary.subject }
val oldSummary =
oldSummaries.find { oldSummary -> oldSummary.subject == summary.subject }
summary.isPredictedGradeNotified = when {
summary.predictedGrade.isEmpty() -> true
notify && oldSummary?.predictedGrade != summary.predictedGrade -> false

View File

@ -22,7 +22,11 @@ class SemesterRepository @Inject constructor(
private val dispatchers: DispatchersProvider
) {
suspend fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false) = withContext(dispatchers.backgroundThread) {
suspend fun getSemesters(
student: Student,
forceRefresh: Boolean = false,
refreshOnNoCurrent: Boolean = false
) = withContext(dispatchers.backgroundThread) {
val semesters = semesterDb.loadAll(student.studentId, student.classId)
if (isShouldFetch(student, semesters, forceRefresh, refreshOnNoCurrent)) {
@ -31,14 +35,21 @@ class SemesterRepository @Inject constructor(
} else semesters
}
private fun isShouldFetch(student: Student, semesters: List<Semester>, forceRefresh: Boolean, refreshOnNoCurrent: Boolean): Boolean {
private fun isShouldFetch(
student: Student,
semesters: List<Semester>,
forceRefresh: Boolean,
refreshOnNoCurrent: Boolean
): Boolean {
val isNoSemesters = semesters.isEmpty()
val isRefreshOnModeChangeRequired = if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
semesters.firstOrNull { it.isCurrent }?.diaryId == 0
} else false
val isRefreshOnModeChangeRequired =
if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
semesters.firstOrNull { it.isCurrent }?.diaryId == 0
} else false
val isRefreshOnNoCurrentAppropriate = refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent }
val isRefreshOnNoCurrentAppropriate =
refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent }
return forceRefresh || isNoSemesters || isRefreshOnModeChangeRequired || isRefreshOnNoCurrentAppropriate
}
@ -52,7 +63,8 @@ class SemesterRepository @Inject constructor(
semesterDb.insertSemesters(new.uniqueSubtract(old))
}
suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) = withContext(dispatchers.backgroundThread) {
getSemesters(student, forceRefresh).getCurrentOrLast()
}
suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) =
withContext(dispatchers.backgroundThread) {
getSemesters(student, forceRefresh).getCurrentOrLast()
}
}

View File

@ -18,7 +18,7 @@ open class BasePresenter<T : BaseView>(
protected val studentRepository: StudentRepository
) : CoroutineScope {
private var job: Job = Job()
private var job = Job()
private val jobs = mutableMapOf<String, Job>()

View File

@ -8,7 +8,7 @@ import androidx.core.view.get
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.databinding.FragmentAccountBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment
@ -75,9 +75,7 @@ class AccountFragment : BaseFragment<FragmentAccountBinding>(R.layout.fragment_a
}
}
override fun openAccountDetailsView(studentWithSemesters: StudentWithSemesters) {
(activity as? MainActivity)?.pushView(
AccountDetailsFragment.newInstance(studentWithSemesters)
)
override fun openAccountDetailsView(student: Student) {
(activity as? MainActivity)?.pushView(AccountDetailsFragment.newInstance(student))
}
}

View File

@ -28,7 +28,7 @@ class AccountPresenter @Inject constructor(
}
fun onItemSelected(studentWithSemesters: StudentWithSemesters) {
view?.openAccountDetailsView(studentWithSemesters)
view?.openAccountDetailsView(studentWithSemesters.student)
}
private fun loadData() {

View File

@ -1,6 +1,6 @@
package io.github.wulkanowy.ui.modules.account
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.ui.base.BaseView
interface AccountView : BaseView {
@ -11,5 +11,5 @@ interface AccountView : BaseView {
fun openLoginView()
fun openAccountDetailsView(studentWithSemesters: StudentWithSemesters)
fun openAccountDetailsView(student: Student)
}

View File

@ -37,9 +37,9 @@ class AccountDetailsFragment :
private const val ARGUMENT_KEY = "Data"
fun newInstance(studentWithSemesters: StudentWithSemesters) =
fun newInstance(student: Student) =
AccountDetailsFragment().apply {
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, studentWithSemesters) }
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, student) }
}
}
@ -51,7 +51,7 @@ class AccountDetailsFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentAccountDetailsBinding.bind(view)
presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as StudentWithSemesters)
presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as Student)
}
override fun initView() {

View File

@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.account.accountdetails
import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.services.sync.SyncManager
@ -27,9 +28,9 @@ class AccountDetailsPresenter @Inject constructor(
private var studentId: Long? = null
fun onAttachView(view: AccountDetailsView, studentWithSemesters: StudentWithSemesters) {
fun onAttachView(view: AccountDetailsView, student: Student) {
super.onAttachView(view)
studentId = studentWithSemesters.student.id
studentId = student.id
view.initView()
errorHandler.showErrorMessage = ::showErrorViewOnError

View File

@ -14,6 +14,7 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.db.entities.TimetableHeader
import io.github.wulkanowy.databinding.ItemDashboardAccountBinding
@ -41,7 +42,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
private var lessonsTimer: Timer? = null
var onAccountTileClickListener: () -> Unit = {}
var onAccountTileClickListener: (Student) -> Unit = {}
var onLuckyNumberTileClickListener: () -> Unit = {}
@ -152,7 +153,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
dashboardAccountItemName.text = student?.nickOrName.orEmpty()
dashboardAccountItemSchoolName.text = student?.schoolName.orEmpty()
root.setOnClickListener { onAccountTileClickListener() }
root.setOnClickListener { student?.let(onAccountTileClickListener) }
}
}
@ -170,38 +171,41 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
val binding = horizontalGroupViewHolder.binding
val context = binding.root.context
val attendanceColor = when {
attendancePercentage ?: 0.0 <= ATTENDANCE_SECOND_WARNING_THRESHOLD -> {
attendancePercentage == null || attendancePercentage == .0 -> {
context.getThemeAttrColor(R.attr.colorOnSurface)
}
attendancePercentage <= ATTENDANCE_SECOND_WARNING_THRESHOLD -> {
context.getThemeAttrColor(R.attr.colorPrimary)
}
attendancePercentage ?: 0.0 <= ATTENDANCE_FIRST_WARNING_THRESHOLD -> {
attendancePercentage <= ATTENDANCE_FIRST_WARNING_THRESHOLD -> {
context.getThemeAttrColor(R.attr.colorTimetableChange)
}
else -> context.getThemeAttrColor(R.attr.colorOnSurface)
}
val attendanceString = if (attendancePercentage == null || attendancePercentage == .0) {
context.getString(R.string.dashboard_horizontal_group_no_data)
} else {
"%.2f%%".format(attendancePercentage)
}
with(binding.dashboardHorizontalGroupItemAttendanceValue) {
text = "%.2f%%".format(attendancePercentage)
text = attendanceString
setTextColor(attendanceColor)
}
with(binding) {
dashboardHorizontalGroupItemMessageValue.text = unreadMessagesCount.toString()
dashboardHorizontalGroupItemLuckyValue.text = if (luckyNumber == -1) {
context.getString(R.string.dashboard_horizontal_group_no_lukcy_number)
dashboardHorizontalGroupItemLuckyValue.text = if (luckyNumber == 0) {
context.getString(R.string.dashboard_horizontal_group_no_data)
} else luckyNumber?.toString()
if (dashboardHorizontalGroupItemInfoContainer.isVisible != (error != null || isLoading)) {
dashboardHorizontalGroupItemInfoContainer.isVisible = error != null || isLoading
}
if (dashboardHorizontalGroupItemInfoProgress.isVisible != isLoading) {
dashboardHorizontalGroupItemInfoProgress.isVisible = isLoading
}
dashboardHorizontalGroupItemInfoContainer.isVisible = error != null || isLoading
dashboardHorizontalGroupItemInfoProgress.isVisible =
(isLoading && !item.isDataLoaded) || (isLoading && !item.isFullDataLoaded)
dashboardHorizontalGroupItemInfoErrorText.isVisible = error != null
with(dashboardHorizontalGroupItemLuckyContainer) {
isVisible = error == null && !isLoading && luckyNumber != null
isVisible = luckyNumber != null && luckyNumber != -1
setOnClickListener { onLuckyNumberTileClickListener() }
updateLayoutParams<ViewGroup.MarginLayoutParams> {
@ -216,7 +220,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
}
with(dashboardHorizontalGroupItemAttendanceContainer) {
isVisible = error == null && !isLoading && attendancePercentage != null
isVisible = attendancePercentage != null && attendancePercentage != -1.0
updateLayoutParams<ConstraintLayout.LayoutParams> {
matchConstraintPercentWidth = when {
luckyNumber == null && unreadMessagesCount == null -> 1.0f
@ -228,7 +232,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
}
with(dashboardHorizontalGroupItemMessageContainer) {
isVisible = error == null && !isLoading && unreadMessagesCount != null
isVisible = unreadMessagesCount != null && unreadMessagesCount != -1
setOnClickListener { onMessageTileClickListener() }
}
}
@ -291,14 +295,14 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
updateLessonView(item, currentTimetable, binding)
binding.dashboardLessonsItemTitleTomorrow.isVisible = false
}
currentDayHeader != null && currentDayHeader.content.isNotBlank() -> {
updateLessonView(item, emptyList(), binding, currentDayHeader)
binding.dashboardLessonsItemTitleTomorrow.isVisible = false
}
tomorrowTimetable.isNotEmpty() -> {
updateLessonView(item, tomorrowTimetable, binding)
binding.dashboardLessonsItemTitleTomorrow.isVisible = true
}
currentDayHeader != null && currentDayHeader.content.isNotBlank() -> {
updateLessonView(item, emptyList(), binding, currentDayHeader)
binding.dashboardLessonsItemTitleTomorrow.isVisible = false
}
tomorrowDayHeader != null && tomorrowDayHeader.content.isNotBlank() -> {
updateLessonView(item, emptyList(), binding, tomorrowDayHeader)
binding.dashboardLessonsItemTitleTomorrow.isVisible = true
@ -348,6 +352,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
}
}
@SuppressLint("SetTextI18n")
private fun updateFirstLessonView(
binding: ItemDashboardLessonsBinding,
firstLesson: Timetable?,
@ -367,7 +372,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
firstLesson ?: return
val minutesToStartLesson =
Duration.between(currentDateTime, firstLesson.start).toMinutes()
Duration.between(currentDateTime, firstLesson.start).toMinutes() + 1
val isFirstTimeVisible: Boolean
val isFirstTimeRangeVisible: Boolean
val firstTimeText: String
@ -376,12 +381,12 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
val firstTitleAndValueTextColor: Int
val firstTitleAndValueTextFont: Typeface
if (currentDateTime.isBefore(firstLesson.start)) {
if (currentDateTime < firstLesson.start) {
if (minutesToStartLesson > 60) {
val formattedStartTime = firstLesson.start.toFormattedString("HH:mm")
val formattedEndTime = firstLesson.end.toFormattedString("HH:mm")
firstTimeRangeText = "${formattedStartTime}-${formattedEndTime}"
firstTimeRangeText = "$formattedStartTime - $formattedEndTime"
firstTimeText = ""
isFirstTimeRangeVisible = true
@ -421,7 +426,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
}
}
} else {
val minutesToEndLesson = firstLesson.left!!.toMinutes()
val minutesToEndLesson = firstLesson.left!!.toMinutes() + 1
firstTimeText = context.resources.getQuantityString(
R.plurals.dashboard_timetable_first_lesson_time_more_minutes,
@ -454,11 +459,8 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
with(binding.dashboardLessonsItemFirstValue) {
setTextColor(firstTitleAndValueTextColor)
typeface = firstTitleAndValueTextFont
text = context.getString(
R.string.dashboard_timetable_lesson_value,
firstLesson.subject,
firstLesson.room
)
text =
"${firstLesson.subject} ${if (firstLesson.room.isNotBlank()) "(${firstLesson.room})" else ""}"
}
}
@ -472,13 +474,11 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
val formattedStartTime = secondLesson?.start?.toFormattedString("HH:mm")
val formattedEndTime = secondLesson?.end?.toFormattedString("HH:mm")
val secondTimeText = "${formattedStartTime}-${formattedEndTime}"
val secondTimeText = "$formattedStartTime - $formattedEndTime"
val secondValueText = if (secondLesson != null) {
context.getString(
R.string.dashboard_timetable_lesson_value,
secondLesson.subject,
secondLesson.room
)
val roomString = if (secondLesson.room.isNotBlank()) "(${secondLesson.room})" else ""
"${secondLesson.subject} $roomString"
} else {
context.getString(R.string.dashboard_timetable_second_lesson_value_end)
}

View File

@ -14,7 +14,7 @@ import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.FragmentDashboardBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.account.AccountFragment
import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment
import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment
import io.github.wulkanowy.ui.modules.conference.ConferenceFragment
import io.github.wulkanowy.ui.modules.exam.ExamFragment
@ -77,7 +77,9 @@ class DashboardFragment : BaseFragment<FragmentDashboardBinding>(R.layout.fragme
)
dashboardAdapter.apply {
onAccountTileClickListener = { mainActivity.pushView(AccountFragment.newInstance()) }
onAccountTileClickListener = {
mainActivity.pushView(AccountDetailsFragment.newInstance(it))
}
onLuckyNumberTileClickListener = {
mainActivity.pushView(LuckyNumberFragment.newInstance())
}

View File

@ -35,6 +35,9 @@ sealed class DashboardItem(val type: Type) {
override val isDataLoaded
get() = unreadMessagesCount != null || attendancePercentage != null || luckyNumber != null
val isFullDataLoaded
get() = luckyNumber != -1 && attendancePercentage != -1.0 && unreadMessagesCount != -1
}
data class Grades(

View File

@ -2,6 +2,8 @@ package io.github.wulkanowy.ui.modules.dashboard
import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository
import io.github.wulkanowy.data.repositories.ConferenceRepository
@ -18,11 +20,18 @@ import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.calculatePercentage
import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.nextOrSameSchoolDay
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import timber.log.Timber
import java.time.LocalDate
import java.time.LocalDateTime
@ -48,9 +57,11 @@ class DashboardPresenter @Inject constructor(
private val dashboardItemRefreshLoadedList = mutableListOf<DashboardItem>()
private lateinit var dashboardItemsToLoad: Set<DashboardItem.Type>
private var dashboardItemsToLoad = emptySet<DashboardItem.Type>()
private var dashboardTilesToLoad: Set<DashboardItem.Tile> = emptySet()
private var dashboardTileLoadedList = emptySet<DashboardItem.Tile>()
private val firstLoadedItemList = mutableListOf<DashboardItem.Type>()
private lateinit var lastError: Throwable
@ -69,8 +80,10 @@ class DashboardPresenter @Inject constructor(
}
fun onDragAndDropEnd(list: List<DashboardItem>) {
dashboardItemLoadedList.clear()
dashboardItemLoadedList.addAll(list)
with(dashboardItemLoadedList) {
clear()
addAll(list)
}
val positionList =
list.mapIndexed { index, dashboardItem -> Pair(dashboardItem.type, index) }.toMap()
@ -78,87 +91,102 @@ class DashboardPresenter @Inject constructor(
preferencesRepository.dashboardItemsPosition = positionList
}
fun loadData(forceRefresh: Boolean = false, tilesToLoad: Set<DashboardItem.Tile>) {
val oldDashboardDataToLoad = dashboardTilesToLoad
fun loadData(
tilesToLoad: Set<DashboardItem.Tile>,
forceRefresh: Boolean = false,
) {
val oldDashboardTileLoadedList = dashboardTileLoadedList
dashboardItemsToLoad = tilesToLoad.map { it.toDashboardItemType() }.toSet()
dashboardTileLoadedList = tilesToLoad
dashboardTilesToLoad = tilesToLoad
dashboardItemsToLoad = dashboardTilesToLoad.map { it.toDashboardItemType() }.toSet()
val itemsToLoad = generateDashboardTileListToLoad(
dashboardTilesToLoad = tilesToLoad,
dashboardLoadedTiles = oldDashboardTileLoadedList,
forceRefresh = forceRefresh
).map { it.toDashboardItemType() }
removeUnselectedTiles()
val newTileList = generateTileListToLoad(oldDashboardDataToLoad, forceRefresh)
loadTiles(forceRefresh, newTileList)
removeUnselectedTiles(tilesToLoad.toList())
loadTiles(tileList = itemsToLoad, forceRefresh = forceRefresh)
}
private fun removeUnselectedTiles() {
val isLuckyNumberToLoad =
dashboardTilesToLoad.any { it == DashboardItem.Tile.LUCKY_NUMBER }
val isMessagesToLoad =
dashboardTilesToLoad.any { it == DashboardItem.Tile.MESSAGES }
val isAttendanceToLoad =
dashboardTilesToLoad.any { it == DashboardItem.Tile.ATTENDANCE }
private fun generateDashboardTileListToLoad(
dashboardTilesToLoad: Set<DashboardItem.Tile>,
dashboardLoadedTiles: Set<DashboardItem.Tile>,
forceRefresh: Boolean
) = dashboardTilesToLoad.filter { newItemToLoad ->
dashboardLoadedTiles.none { it == newItemToLoad } || forceRefresh
}
private fun removeUnselectedTiles(tilesToLoad: List<DashboardItem.Tile>) {
dashboardItemLoadedList.removeAll { loadedTile -> dashboardItemsToLoad.none { it == loadedTile.type } }
val horizontalGroup =
dashboardItemLoadedList.find { it is DashboardItem.HorizontalGroup } as DashboardItem.HorizontalGroup?
if (horizontalGroup != null) {
val horizontalIndex = dashboardItemLoadedList.indexOf(horizontalGroup)
dashboardItemLoadedList.remove(horizontalGroup)
val isLuckyNumberToLoad = DashboardItem.Tile.LUCKY_NUMBER in tilesToLoad
val isMessagesToLoad = DashboardItem.Tile.MESSAGES in tilesToLoad
val isAttendanceToLoad = DashboardItem.Tile.ATTENDANCE in tilesToLoad
var updatedHorizontalGroup = horizontalGroup
val horizontalGroupIndex = dashboardItemLoadedList.indexOf(horizontalGroup)
if (horizontalGroup.luckyNumber != null && !isLuckyNumberToLoad) {
updatedHorizontalGroup = updatedHorizontalGroup.copy(luckyNumber = null)
val newHorizontalGroup = horizontalGroup.copy(
attendancePercentage = horizontalGroup.attendancePercentage.takeIf { isAttendanceToLoad },
unreadMessagesCount = horizontalGroup.unreadMessagesCount.takeIf { isMessagesToLoad },
luckyNumber = horizontalGroup.luckyNumber.takeIf { isLuckyNumberToLoad }
)
with(dashboardItemLoadedList) {
removeAt(horizontalGroupIndex)
add(horizontalGroupIndex, newHorizontalGroup)
}
if (horizontalGroup.attendancePercentage != null && !isAttendanceToLoad) {
updatedHorizontalGroup = updatedHorizontalGroup.copy(attendancePercentage = null)
}
if (horizontalGroup.unreadMessagesCount != null && !isMessagesToLoad) {
updatedHorizontalGroup = updatedHorizontalGroup.copy(unreadMessagesCount = null)
}
if (horizontalGroup.error != null) {
updatedHorizontalGroup = updatedHorizontalGroup.copy(error = null, isLoading = true)
}
dashboardItemLoadedList.add(horizontalIndex, updatedHorizontalGroup)
}
view?.updateData(dashboardItemLoadedList)
}
private fun loadTiles(forceRefresh: Boolean, tileList: List<DashboardItem.Tile>) {
tileList.forEach {
when (it) {
DashboardItem.Tile.ACCOUNT -> loadCurrentAccount(forceRefresh)
DashboardItem.Tile.LUCKY_NUMBER -> loadLuckyNumber(forceRefresh)
DashboardItem.Tile.MESSAGES -> loadMessages(forceRefresh)
DashboardItem.Tile.ATTENDANCE -> loadAttendance(forceRefresh)
DashboardItem.Tile.LESSONS -> loadLessons(forceRefresh)
DashboardItem.Tile.GRADES -> loadGrades(forceRefresh)
DashboardItem.Tile.HOMEWORK -> loadHomework(forceRefresh)
DashboardItem.Tile.ANNOUNCEMENTS -> loadSchoolAnnouncements(forceRefresh)
DashboardItem.Tile.EXAMS -> loadExams(forceRefresh)
DashboardItem.Tile.CONFERENCES -> loadConferences(forceRefresh)
DashboardItem.Tile.ADS -> TODO()
private fun loadTiles(
tileList: List<DashboardItem.Type>,
forceRefresh: Boolean
) {
launch {
Timber.i("Loading dashboard account data started")
val student = runCatching { studentRepository.getCurrentStudent(true) }
.onFailure {
Timber.i("Loading dashboard account result: An exception occurred")
errorHandler.dispatch(it)
updateData(DashboardItem.Account(error = it), forceRefresh)
}
.onSuccess { Timber.i("Loading dashboard account result: Success") }
.getOrNull() ?: return@launch
tileList.forEach {
when (it) {
DashboardItem.Type.ACCOUNT -> {
updateData(DashboardItem.Account(student), forceRefresh)
}
DashboardItem.Type.HORIZONTAL_GROUP -> {
loadHorizontalGroup(student, forceRefresh)
}
DashboardItem.Type.LESSONS -> loadLessons(student, forceRefresh)
DashboardItem.Type.GRADES -> loadGrades(student, forceRefresh)
DashboardItem.Type.HOMEWORK -> loadHomework(student, forceRefresh)
DashboardItem.Type.ANNOUNCEMENTS -> {
loadSchoolAnnouncements(student, forceRefresh)
}
DashboardItem.Type.EXAMS -> loadExams(student, forceRefresh)
DashboardItem.Type.CONFERENCES -> {
loadConferences(student, forceRefresh)
}
DashboardItem.Type.ADS -> TODO()
}
}
}
}
private fun generateTileListToLoad(
oldDashboardTileToLoad: Set<DashboardItem.Tile>,
forceRefresh: Boolean
) = dashboardTilesToLoad.filter { newTileToLoad ->
oldDashboardTileToLoad.none { it == newTileToLoad } || forceRefresh
}
fun onSwipeRefresh() {
Timber.i("Force refreshing the dashboard")
loadData(true, preferencesRepository.selectedDashboardTiles)
loadData(preferencesRepository.selectedDashboardTiles, forceRefresh = true)
}
fun onRetry() {
@ -166,7 +194,7 @@ class DashboardPresenter @Inject constructor(
showErrorView(false)
showProgress(true)
}
loadData(true, preferencesRepository.selectedDashboardTiles)
loadData(preferencesRepository.selectedDashboardTiles, forceRefresh = true)
}
fun onViewReselected() {
@ -192,139 +220,86 @@ class DashboardPresenter @Inject constructor(
}.toSet()
}
private fun loadCurrentAccount(forceRefresh: Boolean) {
flowWithResource { studentRepository.getCurrentStudent(false) }
private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) {
flow {
val semester = semesterRepository.getCurrentSemester(student)
val selectedTiles = preferencesRepository.selectedDashboardTiles
val luckyNumberFlow = luckyNumberRepository.getLuckyNumber(student, forceRefresh)
.map {
if (it.data == null) {
it.copy(data = LuckyNumber(0, LocalDate.now(), 0))
} else it
}
.takeIf { DashboardItem.Tile.LUCKY_NUMBER in selectedTiles } ?: flowOf(null)
val messageFLow = messageRepository.getMessages(
student = student,
semester = semester,
folder = MessageFolder.RECEIVED,
forceRefresh = forceRefresh
).takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowOf(null)
val attendanceFlow = attendanceSummaryRepository.getAttendanceSummary(
student = student,
semester = semester,
subjectId = -1,
forceRefresh = forceRefresh
).takeIf { DashboardItem.Tile.ATTENDANCE in selectedTiles } ?: flowOf(null)
emitAll(
combine(
luckyNumberFlow,
messageFLow,
attendanceFlow
) { luckyNumberResource, messageResource, attendanceResource ->
val error =
luckyNumberResource?.error ?: messageResource?.error ?: attendanceResource?.error
error?.let { throw it }
val luckyNumber = luckyNumberResource?.data?.luckyNumber
val messageCount = messageResource?.data?.count { it.unread }
val attendancePercentage = attendanceResource?.data?.calculatePercentage()
val isLoading =
luckyNumberResource?.status == Status.LOADING || messageResource?.status == Status.LOADING || attendanceResource?.status == Status.LOADING
DashboardItem.HorizontalGroup(
isLoading = isLoading,
attendancePercentage = if (attendancePercentage == 0.0 && isLoading) -1.0 else attendancePercentage,
unreadMessagesCount = if (messageCount == 0 && isLoading) -1 else messageCount,
luckyNumber = if (luckyNumber == 0 && isLoading) -1 else luckyNumber
)
})
}
.filterNot { it.isLoading && forceRefresh }
.distinctUntilChanged()
.onEach {
when (it.status) {
Status.LOADING -> {
Timber.i("Loading dashboard account data started")
if (forceRefresh) return@onEach
updateData(DashboardItem.Account(it.data, isLoading = true), forceRefresh)
}
Status.SUCCESS -> {
Timber.i("Loading dashboard account result: Success")
updateData(DashboardItem.Account(it.data), forceRefresh)
}
Status.ERROR -> {
Timber.i("Loading dashboard account result: An exception occurred")
errorHandler.dispatch(it.error!!)
updateData(DashboardItem.Account(error = it.error), forceRefresh)
updateData(it, forceRefresh)
if (it.isLoading) {
Timber.i("Loading horizontal group data started")
if (it.isFullDataLoaded) {
firstLoadedItemList += DashboardItem.Type.HORIZONTAL_GROUP
}
} else {
Timber.i("Loading horizontal group result: Success")
}
}
.launch("dashboard_account")
}
private fun loadLuckyNumber(forceRefresh: Boolean) {
flowWithResourceIn {
val student = studentRepository.getCurrentStudent(true)
luckyNumberRepository.getLuckyNumber(student, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> {
Timber.i("Loading dashboard lucky number data started")
if (forceRefresh) return@onEach
processHorizontalGroupData(
luckyNumber = it.data?.luckyNumber,
isLoading = true,
forceRefresh = forceRefresh
)
}
Status.SUCCESS -> {
Timber.i("Loading dashboard lucky number result: Success")
processHorizontalGroupData(
luckyNumber = it.data?.luckyNumber ?: -1,
forceRefresh = forceRefresh
)
}
Status.ERROR -> {
Timber.i("Loading dashboard lucky number result: An exception occurred")
errorHandler.dispatch(it.error!!)
processHorizontalGroupData(error = it.error, forceRefresh = forceRefresh)
}
.catch {
Timber.i("Loading horizontal group result: An exception occurred")
updateData(
DashboardItem.HorizontalGroup(error = it),
forceRefresh,
)
errorHandler.dispatch(it)
}
}.launch("dashboard_lucky_number")
.launch("horizontal_group")
}
private fun loadMessages(forceRefresh: Boolean) {
private fun loadGrades(student: Student, forceRefresh: Boolean) {
flowWithResourceIn {
val student = studentRepository.getCurrentStudent(true)
val semester = semesterRepository.getCurrentSemester(student)
messageRepository.getMessages(student, semester, MessageFolder.RECEIVED, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> {
Timber.i("Loading dashboard messages data started")
if (forceRefresh) return@onEach
val unreadMessagesCount = it.data?.count { message -> message.unread }
processHorizontalGroupData(
unreadMessagesCount = unreadMessagesCount,
isLoading = true,
forceRefresh = forceRefresh
)
}
Status.SUCCESS -> {
Timber.i("Loading dashboard messages result: Success")
val unreadMessagesCount = it.data?.count { message -> message.unread }
processHorizontalGroupData(
unreadMessagesCount = unreadMessagesCount,
forceRefresh = forceRefresh
)
}
Status.ERROR -> {
Timber.i("Loading dashboard messages result: An exception occurred")
errorHandler.dispatch(it.error!!)
processHorizontalGroupData(error = it.error, forceRefresh = forceRefresh)
}
}
}.launch("dashboard_messages")
}
private fun loadAttendance(forceRefresh: Boolean) {
flowWithResourceIn {
val student = studentRepository.getCurrentStudent(true)
val semester = semesterRepository.getCurrentSemester(student)
attendanceSummaryRepository.getAttendanceSummary(student, semester, -1, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> {
Timber.i("Loading dashboard attendance data started")
if (forceRefresh) return@onEach
val attendancePercentage = it.data?.calculatePercentage()
processHorizontalGroupData(
attendancePercentage = attendancePercentage,
isLoading = true,
forceRefresh = forceRefresh
)
}
Status.SUCCESS -> {
Timber.i("Loading dashboard attendance result: Success")
val attendancePercentage = it.data?.calculatePercentage()
processHorizontalGroupData(
attendancePercentage = attendancePercentage,
forceRefresh = forceRefresh
)
}
Status.ERROR -> {
Timber.i("Loading dashboard attendance result: An exception occurred")
errorHandler.dispatch(it.error!!)
processHorizontalGroupData(error = it.error, forceRefresh = forceRefresh)
}
}
}.launch("dashboard_attendance")
}
private fun loadGrades(forceRefresh: Boolean) {
flowWithResourceIn {
val student = studentRepository.getCurrentStudent(true)
val semester = semesterRepository.getCurrentSemester(student)
gradeRepository.getGrades(student, semester, forceRefresh)
@ -353,6 +328,7 @@ class DashboardPresenter @Inject constructor(
Status.LOADING -> {
Timber.i("Loading dashboard grades data started")
if (forceRefresh) return@onEach
updateData(
DashboardItem.Grades(
subjectWithGrades = it.data,
@ -360,6 +336,10 @@ class DashboardPresenter @Inject constructor(
isLoading = true
), forceRefresh
)
if (!it.data.isNullOrEmpty()) {
firstLoadedItemList += DashboardItem.Type.GRADES
}
}
Status.SUCCESS -> {
Timber.i("Loading dashboard grades result: Success")
@ -367,7 +347,8 @@ class DashboardPresenter @Inject constructor(
DashboardItem.Grades(
subjectWithGrades = it.data,
gradeTheme = preferencesRepository.gradeColorTheme
), forceRefresh
),
forceRefresh
)
}
Status.ERROR -> {
@ -379,9 +360,8 @@ class DashboardPresenter @Inject constructor(
}.launch("dashboard_grades")
}
private fun loadLessons(forceRefresh: Boolean) {
private fun loadLessons(student: Student, forceRefresh: Boolean) {
flowWithResourceIn {
val student = studentRepository.getCurrentStudent(true)
val semester = semesterRepository.getCurrentSemester(student)
val date = LocalDate.now().nextOrSameSchoolDay
@ -398,24 +378,34 @@ class DashboardPresenter @Inject constructor(
Status.LOADING -> {
Timber.i("Loading dashboard lessons data started")
if (forceRefresh) return@onEach
updateData(DashboardItem.Lessons(it.data, isLoading = true), forceRefresh)
updateData(
DashboardItem.Lessons(it.data, isLoading = true),
forceRefresh
)
if (!it.data?.lessons.isNullOrEmpty()) {
firstLoadedItemList += DashboardItem.Type.LESSONS
}
}
Status.SUCCESS -> {
Timber.i("Loading dashboard lessons result: Success")
updateData(DashboardItem.Lessons(it.data), forceRefresh)
updateData(
DashboardItem.Lessons(it.data), forceRefresh
)
}
Status.ERROR -> {
Timber.i("Loading dashboard lessons result: An exception occurred")
errorHandler.dispatch(it.error!!)
updateData(DashboardItem.Lessons(error = it.error), forceRefresh)
updateData(
DashboardItem.Lessons(error = it.error), forceRefresh
)
}
}
}.launch("dashboard_lessons")
}
private fun loadHomework(forceRefresh: Boolean) {
private fun loadHomework(student: Student, forceRefresh: Boolean) {
flowWithResourceIn {
val student = studentRepository.getCurrentStudent(true)
val semester = semesterRepository.getCurrentSemester(student)
val date = LocalDate.now().nextOrSameSchoolDay
@ -443,6 +433,10 @@ class DashboardPresenter @Inject constructor(
DashboardItem.Homework(it.data ?: emptyList(), isLoading = true),
forceRefresh
)
if (!it.data.isNullOrEmpty()) {
firstLoadedItemList += DashboardItem.Type.HOMEWORK
}
}
Status.SUCCESS -> {
Timber.i("Loading dashboard homework result: Success")
@ -457,10 +451,8 @@ class DashboardPresenter @Inject constructor(
}.launch("dashboard_homework")
}
private fun loadSchoolAnnouncements(forceRefresh: Boolean) {
private fun loadSchoolAnnouncements(student: Student, forceRefresh: Boolean) {
flowWithResourceIn {
val student = studentRepository.getCurrentStudent(true)
schoolAnnouncementRepository.getSchoolAnnouncements(student, forceRefresh)
}.onEach {
when (it.status) {
@ -468,11 +460,13 @@ class DashboardPresenter @Inject constructor(
Timber.i("Loading dashboard announcements data started")
if (forceRefresh) return@onEach
updateData(
DashboardItem.Announcements(
it.data ?: emptyList(),
isLoading = true
), forceRefresh
DashboardItem.Announcements(it.data ?: emptyList(), isLoading = true),
forceRefresh
)
if (!it.data.isNullOrEmpty()) {
firstLoadedItemList += DashboardItem.Type.ANNOUNCEMENTS
}
}
Status.SUCCESS -> {
Timber.i("Loading dashboard announcements result: Success")
@ -487,9 +481,8 @@ class DashboardPresenter @Inject constructor(
}.launch("dashboard_announcements")
}
private fun loadExams(forceRefresh: Boolean) {
private fun loadExams(student: Student, forceRefresh: Boolean) {
flowWithResourceIn {
val student = studentRepository.getCurrentStudent(true)
val semester = semesterRepository.getCurrentSemester(student)
examRepository.getExams(
@ -508,6 +501,10 @@ class DashboardPresenter @Inject constructor(
DashboardItem.Exams(it.data.orEmpty(), isLoading = true),
forceRefresh
)
if (!it.data.isNullOrEmpty()) {
firstLoadedItemList += DashboardItem.Type.EXAMS
}
}
Status.SUCCESS -> {
Timber.i("Loading dashboard exams result: Success")
@ -522,9 +519,8 @@ class DashboardPresenter @Inject constructor(
}.launch("dashboard_exams")
}
private fun loadConferences(forceRefresh: Boolean) {
private fun loadConferences(student: Student, forceRefresh: Boolean) {
flowWithResourceIn {
val student = studentRepository.getCurrentStudent(true)
val semester = semesterRepository.getCurrentSemester(student)
conferenceRepository.getConferences(
@ -542,6 +538,10 @@ class DashboardPresenter @Inject constructor(
DashboardItem.Conferences(it.data ?: emptyList(), isLoading = true),
forceRefresh
)
if (!it.data.isNullOrEmpty()) {
firstLoadedItemList += DashboardItem.Type.CONFERENCES
}
}
Status.SUCCESS -> {
Timber.i("Loading dashboard conferences result: Success")
@ -556,145 +556,119 @@ class DashboardPresenter @Inject constructor(
}.launch("dashboard_conferences")
}
private fun processHorizontalGroupData(
luckyNumber: Int? = null,
unreadMessagesCount: Int? = null,
attendancePercentage: Double? = null,
error: Throwable? = null,
isLoading: Boolean = false,
forceRefresh: Boolean
) {
val isLuckyNumberToLoad =
dashboardTilesToLoad.any { it == DashboardItem.Tile.LUCKY_NUMBER }
val isMessagesToLoad =
dashboardTilesToLoad.any { it == DashboardItem.Tile.MESSAGES }
val isAttendanceToLoad =
dashboardTilesToLoad.any { it == DashboardItem.Tile.ATTENDANCE }
val isPushedToList =
dashboardItemLoadedList.any { it.type == DashboardItem.Type.HORIZONTAL_GROUP }
private fun updateData(dashboardItem: DashboardItem, forceRefresh: Boolean) {
val isForceRefreshError = forceRefresh && dashboardItem.error != null
val isFirstRunDataLoadedError =
dashboardItem.type in firstLoadedItemList && dashboardItem.error != null
if (error != null) {
updateData(DashboardItem.HorizontalGroup(error = error), forceRefresh)
return
with(dashboardItemLoadedList) {
removeAll { it.type == dashboardItem.type && !isForceRefreshError && !isFirstRunDataLoadedError }
if (!isForceRefreshError && !isFirstRunDataLoadedError) add(dashboardItem)
}
if (isLoading) {
val horizontalGroup =
dashboardItemLoadedList.find { it is DashboardItem.HorizontalGroup } as DashboardItem.HorizontalGroup?
val updatedHorizontalGroup =
horizontalGroup?.copy(isLoading = true) ?: DashboardItem.HorizontalGroup(isLoading = true)
sortDashboardItems()
updateData(updatedHorizontalGroup, forceRefresh)
}
if (forceRefresh && !isPushedToList) {
updateData(DashboardItem.HorizontalGroup(), forceRefresh)
}
val horizontalGroup =
dashboardItemLoadedList.single { it is DashboardItem.HorizontalGroup } as DashboardItem.HorizontalGroup
when {
luckyNumber != null -> {
updateData(horizontalGroup.copy(luckyNumber = luckyNumber), forceRefresh)
}
unreadMessagesCount != null -> {
updateData(
horizontalGroup.copy(unreadMessagesCount = unreadMessagesCount),
forceRefresh
)
}
attendancePercentage != null -> {
updateData(
horizontalGroup.copy(attendancePercentage = attendancePercentage),
forceRefresh
)
}
}
val isHorizontalGroupLoaded = dashboardItemLoadedList.any {
if (it !is DashboardItem.HorizontalGroup) return@any false
val isLuckyNumberStateCorrect = (it.luckyNumber != null) == isLuckyNumberToLoad
val isMessagesStateCorrect = (it.unreadMessagesCount != null) == isMessagesToLoad
val isAttendanceStateCorrect = (it.attendancePercentage != null) == isAttendanceToLoad
isLuckyNumberStateCorrect && isAttendanceStateCorrect && isMessagesStateCorrect
}
if (isHorizontalGroupLoaded) {
val updatedHorizontalGroup =
dashboardItemLoadedList.single { it is DashboardItem.HorizontalGroup } as DashboardItem.HorizontalGroup
updateData(updatedHorizontalGroup.copy(isLoading = false, error = null), forceRefresh)
if (forceRefresh) {
updateForceRefreshData(dashboardItem)
} else {
updateNormalData()
}
}
private fun updateData(dashboardItem: DashboardItem, forceRefresh: Boolean) {
val isForceRefreshError = forceRefresh && dashboardItem.error != null
val dashboardItemsPosition = preferencesRepository.dashboardItemsPosition
with(dashboardItemLoadedList) {
removeAll { it.type == dashboardItem.type && !isForceRefreshError }
if (!isForceRefreshError) add(dashboardItem)
sortBy { tile -> dashboardItemsToLoad.single { it == tile.type }.ordinal }
private fun updateNormalData() {
val isItemsLoaded =
dashboardItemsToLoad.all { type -> dashboardItemLoadedList.any { it.type == type } }
val isItemsDataLoaded = isItemsLoaded && dashboardItemLoadedList.all {
it.isDataLoaded || it.error != null
}
if (forceRefresh) {
with(dashboardItemRefreshLoadedList) {
removeAll { it.type == dashboardItem.type }
add(dashboardItem)
if (isItemsDataLoaded) {
view?.run {
showProgress(false)
showErrorView(false)
showContent(true)
updateData(dashboardItemLoadedList.toList())
}
}
showErrorIfExists(
isItemsLoaded = isItemsLoaded,
itemsLoadedList = dashboardItemLoadedList,
forceRefresh = false
)
}
private fun updateForceRefreshData(dashboardItem: DashboardItem) {
with(dashboardItemRefreshLoadedList) {
removeAll { it.type == dashboardItem.type }
add(dashboardItem)
}
val isRefreshItemLoaded =
dashboardItemsToLoad.all { type -> dashboardItemRefreshLoadedList.any { it.type == type } }
val isRefreshItemsDataLoaded = isRefreshItemLoaded && dashboardItemRefreshLoadedList.all {
it.isDataLoaded || it.error != null
}
if (isRefreshItemsDataLoaded) {
view?.run {
showRefresh(false)
showErrorView(false)
showContent(true)
updateData(dashboardItemLoadedList.toList())
}
}
showErrorIfExists(
isItemsLoaded = isRefreshItemLoaded,
itemsLoadedList = dashboardItemRefreshLoadedList,
forceRefresh = true
)
if (isRefreshItemsDataLoaded) dashboardItemRefreshLoadedList.clear()
}
private fun showErrorIfExists(
isItemsLoaded: Boolean,
itemsLoadedList: List<DashboardItem>,
forceRefresh: Boolean
) {
val filteredItems = itemsLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT }
val isAccountItemError =
itemsLoadedList.find { it.type == DashboardItem.Type.ACCOUNT }?.error != null
val isGeneralError =
filteredItems.none { it.error == null } && filteredItems.isNotEmpty() || isAccountItemError
val errorMessage = itemsLoadedList.map { it.error?.stackTraceToString() }.toString()
val filteredOriginalLoadedList =
dashboardItemLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT }
val wasAccountItemError =
dashboardItemLoadedList.find { it.type == DashboardItem.Type.ACCOUNT }?.error != null
val wasGeneralError =
filteredOriginalLoadedList.none { it.error == null } && filteredOriginalLoadedList.isNotEmpty() || wasAccountItemError
if (isGeneralError && isItemsLoaded) {
lastError = Exception(errorMessage)
view?.run {
showProgress(false)
showRefresh(false)
if ((forceRefresh && wasGeneralError) || !forceRefresh) {
showContent(false)
showErrorView(true)
}
}
}
}
private fun sortDashboardItems() {
val dashboardItemsPosition = preferencesRepository.dashboardItemsPosition
dashboardItemLoadedList.sortBy { tile ->
dashboardItemsPosition?.getOrDefault(
tile.type,
tile.type.ordinal + 100
) ?: tile.type.ordinal
}
val isItemsLoaded =
dashboardItemsToLoad.all { type -> dashboardItemLoadedList.any { it.type == type } }
val isRefreshItemLoaded =
dashboardItemsToLoad.all { type -> dashboardItemRefreshLoadedList.any { it.type == type } }
val isItemsDataLoaded = isItemsLoaded && dashboardItemLoadedList.all {
it.isDataLoaded || it.error != null
}
val isRefreshItemsDataLoaded = isRefreshItemLoaded && dashboardItemRefreshLoadedList.all {
it.isDataLoaded || it.error != null
}
if (isRefreshItemsDataLoaded) {
view?.showRefresh(false)
dashboardItemRefreshLoadedList.clear()
}
view?.run {
if (!forceRefresh) {
showProgress(!isItemsDataLoaded)
showContent(isItemsDataLoaded)
}
updateData(dashboardItemLoadedList.toList())
}
if (isItemsLoaded) {
val filteredItems =
dashboardItemLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT }
val isAccountItemError =
dashboardItemLoadedList.single { it.type == DashboardItem.Type.ACCOUNT }.error != null
val isGeneralError =
filteredItems.all { it.error != null } && filteredItems.isNotEmpty() || isAccountItemError
val errorMessage = filteredItems.map { it.error?.stackTraceToString() }.toString()
lastError = Exception(errorMessage)
view?.run {
showProgress(false)
showContent(!isGeneralError)
showErrorView(isGeneralError)
}
}
}
}

View File

@ -18,7 +18,7 @@ inline val Timetable.left: Duration?
get() = when {
canceled -> null
!isStudentPlan -> null
end.isAfter(now()) && start.isBefore(now()) -> between(now(), end)
end >= now() && start <= now() -> between(now(), end)
else -> null
}

View File

@ -4,14 +4,14 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="12dp"
android:layout_marginVertical="2dp"
android:clipToPadding="false">
android:clipToPadding="false"
android:paddingHorizontal="12dp">
<com.google.android.material.card.MaterialCardView
android:id="@+id/dashboard_horizontal_group_item_lucky_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_height="44dp"
android:layout_marginVertical="4dp"
app:cardElevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"
@ -62,7 +62,7 @@
<com.google.android.material.card.MaterialCardView
android:id="@+id/dashboard_horizontal_group_item_message_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_height="44dp"
android:layout_marginVertical="4dp"
android:layout_marginEnd="8dp"
app:cardElevation="4dp"
@ -119,7 +119,7 @@
<com.google.android.material.card.MaterialCardView
android:id="@+id/dashboard_horizontal_group_item_attendance_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_height="44dp"
android:layout_marginVertical="4dp"
app:cardElevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"
@ -169,10 +169,8 @@
<com.google.android.material.card.MaterialCardView
android:id="@+id/dashboard_horizontal_group_item_info_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="44dp"
android:layout_marginVertical="4dp"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:visibility="gone"
app:cardElevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"

View File

@ -496,7 +496,6 @@
<!--Dashboard-->
<string name="dashboard_timetable_title">Lekce</string>
<string name="dashboard_timetable_title_tomorrow">(Zítra)</string>
<string name="dashboard_timetable_lesson_value">%1$s (%2$s)</string>
<string name="dashboard_timetable_first_lesson_title_moment">Za chvíli:</string>
<string name="dashboard_timetable_first_lesson_title_soon">Brzy:</string>
<string name="dashboard_timetable_first_lesson_title_first">První:</string>
@ -566,7 +565,7 @@
<item quantity="other">Ještě %1$d dalších setkání</item>
</plurals>
<string name="dashboard_horizontal_group_error">Při načítání dat došlo k chybě</string>
<string name="dashboard_horizontal_group_no_lukcy_number">Žádné</string>
<string name="dashboard_horizontal_group_no_data">Žádné</string>
<!--Error dialog-->
<string name="dialog_error_check_update">Zkontrolovat aktualizace</string>
<string name="dialog_error_check_update_message">Před hlášením chyby zkontrolujte, zda je k dispozici aktualizace s opravou chyb</string>

View File

@ -432,7 +432,6 @@
<!--Dashboard-->
<string name="dashboard_timetable_title">Lektionen</string>
<string name="dashboard_timetable_title_tomorrow">(Morgen)</string>
<string name="dashboard_timetable_lesson_value">%1$s (%2$s)</string>
<string name="dashboard_timetable_first_lesson_title_moment">Gleich:</string>
<string name="dashboard_timetable_first_lesson_title_soon">Bald:</string>
<string name="dashboard_timetable_first_lesson_title_first">Erstens:</string>
@ -488,7 +487,7 @@
<item quantity="other">%1$d weitere Konferenzen</item>
</plurals>
<string name="dashboard_horizontal_group_error">Fehler beim Laden der Daten</string>
<string name="dashboard_horizontal_group_no_lukcy_number">Keine</string>
<string name="dashboard_horizontal_group_no_data">Keine</string>
<!--Error dialog-->
<string name="dialog_error_check_update">Auf Updates prüfen</string>
<string name="dialog_error_check_update_message">Bevor Sie einen Fehler melden, prüfen Sie zuerst, ob ein Update mit der Fehlerbehebung verfügbar ist</string>

View File

@ -496,7 +496,6 @@
<!--Dashboard-->
<string name="dashboard_timetable_title">Lekcje</string>
<string name="dashboard_timetable_title_tomorrow">(Jutro)</string>
<string name="dashboard_timetable_lesson_value">%1$s (%2$s)</string>
<string name="dashboard_timetable_first_lesson_title_moment">Za chwilę:</string>
<string name="dashboard_timetable_first_lesson_title_soon">Wkrótce:</string>
<string name="dashboard_timetable_first_lesson_title_first">Pierwsza:</string>
@ -566,7 +565,7 @@
<item quantity="other">Jeszcze %1$d dodatkowych zebrań</item>
</plurals>
<string name="dashboard_horizontal_group_error">Wystąpił błąd podczas ładowania danych</string>
<string name="dashboard_horizontal_group_no_lukcy_number">Brak</string>
<string name="dashboard_horizontal_group_no_data">Brak</string>
<!--Error dialog-->
<string name="dialog_error_check_update">Sprawdź dostępność aktualizacji</string>
<string name="dialog_error_check_update_message">Przed zgłoszeniem błędu sprawdź wcześniej, czy dostępna jest już aktualizacja z poprawką błędu</string>

View File

@ -496,7 +496,6 @@
<!--Dashboard-->
<string name="dashboard_timetable_title">Уроки</string>
<string name="dashboard_timetable_title_tomorrow">(Завтра)</string>
<string name="dashboard_timetable_lesson_value">%1$s (%2$s)</string>
<string name="dashboard_timetable_first_lesson_title_moment">Сейчас:</string>
<string name="dashboard_timetable_first_lesson_title_soon">Скоро:</string>
<string name="dashboard_timetable_first_lesson_title_first">Первый:</string>
@ -566,7 +565,7 @@
<item quantity="other">Еще %1$d конференций</item>
</plurals>
<string name="dashboard_horizontal_group_error">Произошла ошибка при загрузке данных</string>
<string name="dashboard_horizontal_group_no_lukcy_number">Отсутствует</string>
<string name="dashboard_horizontal_group_no_data">Отсутствует</string>
<!--Error dialog-->
<string name="dialog_error_check_update">Проверить наличие обновлений</string>
<string name="dialog_error_check_update_message">Прежде чем сообщать об ошибке, проверьте наличие обновлений</string>

View File

@ -496,7 +496,6 @@
<!--Dashboard-->
<string name="dashboard_timetable_title">Lekcie</string>
<string name="dashboard_timetable_title_tomorrow">(Zajtra)</string>
<string name="dashboard_timetable_lesson_value">%1$s (%2$s)</string>
<string name="dashboard_timetable_first_lesson_title_moment">Za chvíľu:</string>
<string name="dashboard_timetable_first_lesson_title_soon">Čoskoro:</string>
<string name="dashboard_timetable_first_lesson_title_first">Prvá:</string>
@ -566,7 +565,7 @@
<item quantity="other">Ešte %1$d ďalších stretnutí</item>
</plurals>
<string name="dashboard_horizontal_group_error">Pri načítaní dát došlo k chybe</string>
<string name="dashboard_horizontal_group_no_lukcy_number">Žiadne</string>
<string name="dashboard_horizontal_group_no_data">Žiadne</string>
<!--Error dialog-->
<string name="dialog_error_check_update">Skontrolovať aktualizácie</string>
<string name="dialog_error_check_update_message">Pred hlásením chyby skontrolujte, či je k dispozícii aktualizácia s opravou chýb</string>

View File

@ -496,7 +496,6 @@
<!--Dashboard-->
<string name="dashboard_timetable_title">Уроки</string>
<string name="dashboard_timetable_title_tomorrow">(Завтра)</string>
<string name="dashboard_timetable_lesson_value">%1$s (%2$s)</string>
<string name="dashboard_timetable_first_lesson_title_moment">Через мить:</string>
<string name="dashboard_timetable_first_lesson_title_soon">Незабаром:</string>
<string name="dashboard_timetable_first_lesson_title_first">Перше:</string>
@ -566,7 +565,7 @@
<item quantity="other">%1$d більше конференцій</item>
</plurals>
<string name="dashboard_horizontal_group_error">Помилка при завантаженні даних</string>
<string name="dashboard_horizontal_group_no_lukcy_number">Нічого</string>
<string name="dashboard_horizontal_group_no_data">Нічого</string>
<!--Error dialog-->
<string name="dialog_error_check_update">Провірити наявність оновлень</string>
<string name="dialog_error_check_update_message">Перед тим, як повідомлювати о помілці, перевірте наявність оновлень</string>

View File

@ -495,7 +495,6 @@
<!--Dashboard-->
<string name="dashboard_timetable_title">Lessons</string>
<string name="dashboard_timetable_title_tomorrow">(Tomorrow)</string>
<string name="dashboard_timetable_lesson_value">%1$s (%2$s)</string>
<string name="dashboard_timetable_first_lesson_title_moment">In a moment:</string>
<string name="dashboard_timetable_first_lesson_title_soon">Soon:</string>
<string name="dashboard_timetable_first_lesson_title_first">First:</string>
@ -557,7 +556,7 @@
</plurals>
<string name="dashboard_horizontal_group_error">An error occurred while loading data</string>
<string name="dashboard_horizontal_group_no_lukcy_number">None</string>
<string name="dashboard_horizontal_group_no_data">None</string>
<!--Error dialog-->

View File

@ -32,7 +32,22 @@ class TimetableExtensionTest {
assertEquals(null, getTimetableEntity(canceled = true).left)
assertEquals(null, getTimetableEntity(start = now().plusMinutes(5), end = now().plusMinutes(50)).left)
assertEquals(null, getTimetableEntity(start = now().minusMinutes(1), end = now().plusMinutes(44), isStudentPlan = false).left)
assertNotEquals(null, getTimetableEntity(start = now().minusMinutes(1), end = now().plusMinutes(44), isStudentPlan = true).left)
assertNotEquals(
null,
getTimetableEntity(
start = now().minusMinutes(1),
end = now().plusMinutes(44),
isStudentPlan = true
).left
)
assertNotEquals(
null,
getTimetableEntity(
start = now(),
end = now().plusMinutes(45),
isStudentPlan = true
).left
)
}
@Test