1
0

Migrate presenters from rxjava to coroutines flow (#894)

This commit is contained in:
Mikołaj Pich
2020-07-19 13:30:29 +02:00
committed by GitHub
parent b0a674b471
commit 1ac42bb56d
138 changed files with 2351 additions and 1892 deletions

View File

@ -18,8 +18,7 @@
android:supportsRtl="false"
android:theme="@style/WulkanowyTheme"
android:usesCleartextTraffic="true"
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute"
tools:replace="android:supportsRtl,android:allowBackup">
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
<activity
android:name=".ui.modules.splash.SplashActivity"
android:screenOrientation="portrait"

View File

@ -0,0 +1,23 @@
package io.github.wulkanowy.data
data class Resource<out T>(val status: Status, val data: T?, val error: Throwable?) {
companion object {
fun <T> success(data: T?): Resource<T> {
return Resource(Status.SUCCESS, data, null)
}
fun <T> error(error: Throwable?, data: T? = null): Resource<T> {
return Resource(Status.ERROR, data, error)
}
fun <T> loading(data: T? = null): Resource<T> {
return Resource(Status.LOADING, data, null)
}
}
}
enum class Status {
LOADING,
SUCCESS,
ERROR
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Attendance
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface AttendanceDao : BaseDao<Attendance> {
@Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
suspend fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List<Attendance>
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Attendance>>
}

View File

@ -3,10 +3,11 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.AttendanceSummary
import kotlinx.coroutines.flow.Flow
@Dao
interface AttendanceSummaryDao : BaseDao<AttendanceSummary> {
@Query("SELECT * FROM AttendanceSummary WHERE diary_id = :diaryId AND student_id = :studentId AND subject_id = :subjectId")
suspend fun loadAll(diaryId: Int, studentId: Int, subjectId: Int): List<AttendanceSummary>
fun loadAll(diaryId: Int, studentId: Int, subjectId: Int): Flow<List<AttendanceSummary>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.CompletedLesson
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface CompletedLessonsDao : BaseDao<CompletedLesson> {
@Query("SELECT * FROM CompletedLesson WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
suspend fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List<CompletedLesson>
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<CompletedLesson>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Exam
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface ExamDao : BaseDao<Exam> {
@Query("SELECT * FROM Exams WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
suspend fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List<Exam>
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Exam>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Grade
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@ -10,5 +11,5 @@ import javax.inject.Singleton
interface GradeDao : BaseDao<Grade> {
@Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId")
suspend fun loadAll(semesterId: Int, studentId: Int): List<Grade>
fun loadAll(semesterId: Int, studentId: Int): Flow<List<Grade>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@ -10,8 +11,8 @@ import javax.inject.Singleton
interface GradePointsStatisticsDao : BaseDao<GradePointsStatistics> {
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName")
suspend fun loadSubject(semesterId: Int, studentId: Int, subjectName: String): List<GradePointsStatistics>
fun loadSubject(semesterId: Int, studentId: Int, subjectName: String): Flow<List<GradePointsStatistics>>
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId")
suspend fun loadAll(semesterId: Int, studentId: Int): List<GradePointsStatistics>
fun loadAll(semesterId: Int, studentId: Int): Flow<List<GradePointsStatistics>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradeStatistics
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@ -10,8 +11,8 @@ import javax.inject.Singleton
interface GradeStatisticsDao : BaseDao<GradeStatistics> {
@Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName AND is_semester = :isSemester")
suspend fun loadSubject(semesterId: Int, studentId: Int, subjectName: String, isSemester: Boolean): List<GradeStatistics>
fun loadSubject(semesterId: Int, studentId: Int, subjectName: String, isSemester: Boolean): Flow<List<GradeStatistics>>
@Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND is_semester = :isSemester")
suspend fun loadAll(semesterId: Int, studentId: Int, isSemester: Boolean): List<GradeStatistics>
fun loadAll(semesterId: Int, studentId: Int, isSemester: Boolean): Flow<List<GradeStatistics>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradeSummary
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@ -10,5 +11,5 @@ import javax.inject.Singleton
interface GradeSummaryDao : BaseDao<GradeSummary> {
@Query("SELECT * FROM GradesSummary WHERE student_id = :studentId AND semester_id = :semesterId")
suspend fun loadAll(semesterId: Int, studentId: Int): List<GradeSummary>
fun loadAll(semesterId: Int, studentId: Int): Flow<List<GradeSummary>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Homework
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface HomeworkDao : BaseDao<Homework> {
@Query("SELECT * FROM Homework WHERE semester_id = :semesterId AND student_id = :studentId AND date >= :from AND date <= :end")
suspend fun loadAll(semesterId: Int, studentId: Int, from: LocalDate, end: LocalDate): List<Homework>
fun loadAll(semesterId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Homework>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.LuckyNumber
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface LuckyNumberDao : BaseDao<LuckyNumber> {
@Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date")
suspend fun load(studentId: Int, date: LocalDate): LuckyNumber
fun load(studentId: Int, date: LocalDate): Flow<LuckyNumber>
}

View File

@ -5,17 +5,18 @@ import androidx.room.Query
import androidx.room.Transaction
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import kotlinx.coroutines.flow.Flow
@Dao
interface MessagesDao : BaseDao<Message> {
@Transaction
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND message_id = :messageId")
suspend fun loadMessageWithAttachment(studentId: Int, messageId: Int): MessageWithAttachment
fun loadMessageWithAttachment(studentId: Int, messageId: Int): Flow<MessageWithAttachment>
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder AND removed = 0 ORDER BY date DESC")
suspend fun loadAll(studentId: Int, folder: Int): List<Message>
fun loadAll(studentId: Int, folder: Int): Flow<List<Message>>
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND removed = 1 ORDER BY date DESC")
suspend fun loadDeleted(studentId: Int): List<Message>
fun loadDeleted(studentId: Int): Flow<List<Message>>
}

View File

@ -3,10 +3,11 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.MobileDevice
import kotlinx.coroutines.flow.Flow
@Dao
interface MobileDeviceDao : BaseDao<MobileDevice> {
@Query("SELECT * FROM MobileDevices WHERE student_id = :studentId ORDER BY date DESC")
suspend fun loadAll(studentId: Int): List<MobileDevice>
fun loadAll(studentId: Int): Flow<List<MobileDevice>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Note
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@ -10,5 +11,5 @@ import javax.inject.Singleton
interface NoteDao : BaseDao<Note> {
@Query("SELECT * FROM Notes WHERE student_id = :studentId")
suspend fun loadAll(studentId: Int): List<Note>
fun loadAll(studentId: Int): Flow<List<Note>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.School
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@ -10,5 +11,5 @@ import javax.inject.Singleton
interface SchoolDao : BaseDao<School> {
@Query("SELECT * FROM School WHERE student_id = :studentId AND class_id = :classId")
suspend fun load(studentId: Int, classId: Int): School?
fun load(studentId: Int, classId: Int): Flow<School?>
}

View File

@ -3,10 +3,11 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Subject
import kotlinx.coroutines.flow.Flow
@Dao
interface SubjectDao : BaseDao<Subject> {
@Query("SELECT * FROM Subjects WHERE diary_id = :diaryId AND student_id = :studentId")
suspend fun loadAll(diaryId: Int, studentId: Int): List<Subject>
fun loadAll(diaryId: Int, studentId: Int): Flow<List<Subject>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Teacher
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@ -10,5 +11,5 @@ import javax.inject.Singleton
interface TeacherDao : BaseDao<Teacher> {
@Query("SELECT * FROM Teachers WHERE student_id = :studentId AND class_id = :classId")
suspend fun loadAll(studentId: Int, classId: Int): List<Teacher>
fun loadAll(studentId: Int, classId: Int): Flow<List<Teacher>>
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Timetable
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface TimetableDao : BaseDao<Timetable> {
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
suspend fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List<Timetable>
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Timetable>>
}

View File

@ -14,12 +14,10 @@ class AppCreatorRepository @Inject constructor(
private val dispatchers: DispatchersProvider
) {
suspend fun getAppCreators(): List<Contributor> {
return withContext(dispatchers.backgroundThread) {
Gson().fromJson(
assets.open("contributors.json").bufferedReader().use { it.readText() },
Array<Contributor>::class.java
).toList()
}
suspend fun getAppCreators() = withContext(dispatchers.backgroundThread) {
Gson().fromJson(
assets.open("contributors.json").bufferedReader().use { it.readText() },
Array<Contributor>::class.java
).toList()
}
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.attendance
import io.github.wulkanowy.data.db.dao.AttendanceDao
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@ -18,7 +19,7 @@ class AttendanceLocal @Inject constructor(private val attendanceDb: AttendanceDa
attendanceDb.deleteAll(attendance)
}
suspend fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): List<Attendance> {
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Flow<List<Attendance>> {
return attendanceDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate)
}
}

View File

@ -4,6 +4,7 @@ import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.uniqueSubtract
import org.threeten.bp.LocalDate
@ -16,19 +17,18 @@ class AttendanceRepository @Inject constructor(
private val remote: AttendanceRemote
) {
suspend fun getAttendance(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean): List<Attendance> {
return local.getAttendance(semester, start.monday, end.sunday).filter { !forceRefresh }.ifEmpty {
val new = remote.getAttendance(student, semester, start.monday, end.sunday)
val old = local.getAttendance(semester, start.monday, end.sunday)
fun getAttendance(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getAttendance(semester, start.monday, end.sunday) },
fetch = { remote.getAttendance(student, semester, start.monday, end.sunday) },
saveFetchResult = { old, new ->
local.deleteAttendance(old uniqueSubtract new)
local.saveAttendance(new uniqueSubtract old)
},
filterResult = { it.filter { item -> item.date in start..end } }
)
local.deleteAttendance(old.uniqueSubtract(new))
local.saveAttendance(new.uniqueSubtract(old))
local.getAttendance(semester, start.monday, end.sunday)
}.filter { it.date in start..end }
}
suspend fun excuseForAbsence(student: Student, semester: Semester, attendanceList: List<Attendance>, reason: String? = null): Boolean {
return remote.excuseAbsence(student, semester, attendanceList, reason)
suspend fun excuseForAbsence(student: Student, semester: Semester, attendanceList: List<Attendance>, reason: String? = null) {
remote.excuseAbsence(student, semester, attendanceList, reason)
}
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.attendancesummary
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
@ -17,7 +18,7 @@ class AttendanceSummaryLocal @Inject constructor(private val attendanceDb: Atten
attendanceDb.deleteAll(attendance)
}
suspend fun getAttendanceSummary(semester: Semester, subjectId: Int): List<AttendanceSummary> {
fun getAttendanceSummary(semester: Semester, subjectId: Int): Flow<List<AttendanceSummary>> {
return attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId)
}
}

View File

@ -1,8 +1,8 @@
package io.github.wulkanowy.data.repositories.attendancesummary
import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject
import javax.inject.Singleton
@ -13,15 +13,13 @@ class AttendanceSummaryRepository @Inject constructor(
private val remote: AttendanceSummaryRemote
) {
suspend fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int, forceRefresh: Boolean = false): List<AttendanceSummary> {
return local.getAttendanceSummary(semester, subjectId).filter { !forceRefresh }.ifEmpty {
val new = remote.getAttendanceSummary(student, semester, subjectId)
val old = local.getAttendanceSummary(semester, subjectId)
local.deleteAttendanceSummary(old.uniqueSubtract(new))
local.saveAttendanceSummary(new.uniqueSubtract(old))
return local.getAttendanceSummary(semester, subjectId)
fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getAttendanceSummary(semester, subjectId) },
fetch = { remote.getAttendanceSummary(student, semester, subjectId) },
saveFetchResult = { old, new ->
local.deleteAttendanceSummary(old uniqueSubtract new)
local.saveAttendanceSummary(new uniqueSubtract old)
}
}
)
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.completedlessons
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@ -18,7 +19,7 @@ class CompletedLessonsLocal @Inject constructor(private val completedLessonsDb:
completedLessonsDb.deleteAll(completedLessons)
}
suspend fun getCompletedLessons(semester: Semester, start: LocalDate, end: LocalDate): List<CompletedLesson> {
fun getCompletedLessons(semester: Semester, start: LocalDate, end: LocalDate): Flow<List<CompletedLesson>> {
return completedLessonsDb.loadAll(semester.diaryId, semester.studentId, start, end)
}
}

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.data.repositories.completedlessons
import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.uniqueSubtract
import org.threeten.bp.LocalDate
@ -16,15 +16,14 @@ class CompletedLessonsRepository @Inject constructor(
private val remote: CompletedLessonsRemote
) {
suspend fun getCompletedLessons(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): List<CompletedLesson> {
return local.getCompletedLessons(semester, start.monday, end.sunday).filter { !forceRefresh }.ifEmpty {
val new = remote.getCompletedLessons(student, semester, start.monday, end.sunday)
val old = local.getCompletedLessons(semester, start.monday, end.sunday)
local.deleteCompleteLessons(old.uniqueSubtract(new))
local.saveCompletedLessons(new.uniqueSubtract(old))
local.getCompletedLessons(semester, start.monday, end.sunday)
}.filter { it.date in start..end }
}
fun getCompletedLessons(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getCompletedLessons(semester, start.monday, end.sunday) },
fetch = { remote.getCompletedLessons(student, semester, start.monday, end.sunday) },
saveFetchResult = { old, new ->
local.deleteCompleteLessons(old uniqueSubtract new)
local.saveCompletedLessons(new uniqueSubtract old)
},
filterResult = { it.filter { item -> item.date in start..end } }
)
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.exam
import io.github.wulkanowy.data.db.dao.ExamDao
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@ -10,7 +11,7 @@ import javax.inject.Singleton
@Singleton
class ExamLocal @Inject constructor(private val examDb: ExamDao) {
suspend fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): List<Exam> {
fun getExams(semester: Semester, startDate: LocalDate, endDate: LocalDate): Flow<List<Exam>> {
return examDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate)
}

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.data.repositories.exam
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.uniqueSubtract
import org.threeten.bp.LocalDate
@ -16,15 +16,14 @@ class ExamRepository @Inject constructor(
private val remote: ExamRemote
) {
suspend fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): List<Exam> {
return local.getExams(semester, start.monday, end.sunday).filter { !forceRefresh }.ifEmpty {
val new = remote.getExams(student, semester, start.monday, end.sunday)
val old = local.getExams(semester, start.monday, end.sunday)
local.deleteExams(old.uniqueSubtract(new))
local.saveExams(new.uniqueSubtract(old))
local.getExams(semester, start.monday, end.sunday)
}.filter { it.date in start..end }
}
fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getExams(semester, start.monday, end.sunday) },
fetch = { remote.getExams(student, semester, start.monday, end.sunday) },
saveFetchResult = { old, new ->
local.deleteExams(old uniqueSubtract new)
local.saveExams(new uniqueSubtract old)
},
filterResult = { it.filter { item -> item.date in start..end } }
)
}

View File

@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.dao.GradeSummaryDao
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
@ -30,7 +31,7 @@ class GradeLocal @Inject constructor(
gradeSummaryDb.updateAll(gradesSummary)
}
suspend fun getGradesDetails(semester: Semester): List<Grade> {
fun getGradesDetails(semester: Semester): Flow<List<Grade>> {
return gradeDb.loadAll(semester.semesterId, semester.studentId)
}
@ -42,7 +43,7 @@ class GradeLocal @Inject constructor(
gradeSummaryDb.deleteAll(gradesSummary)
}
suspend fun getGradesSummary(semester: Semester): List<GradeSummary> {
fun getGradesSummary(semester: Semester): Flow<List<GradeSummary>> {
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId)
}
}

View File

@ -4,7 +4,11 @@ import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import org.threeten.bp.LocalDateTime
import javax.inject.Inject
import javax.inject.Singleton
@ -15,30 +19,30 @@ class GradeRepository @Inject constructor(
private val remote: GradeRemote
) {
suspend fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Pair<List<Grade>, List<GradeSummary>> {
val details = local.getGradesDetails(semester)
val summaries = local.getGradesSummary(semester)
if ((details.isNotEmpty() || summaries.isNotEmpty()) && !forceRefresh) {
return details to summaries
fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
shouldFetch = { (details, summaries) -> details.isEmpty() || summaries.isEmpty() || forceRefresh },
query = { local.getGradesDetails(semester).combine(local.getGradesSummary(semester)) { details, summaries -> details to summaries } },
fetch = { remote.getGrades(student, semester) },
saveFetchResult = { old, new ->
refreshGradeDetails(student, old.first, new.first, notify)
refreshGradeSummaries(old.second, new.second, notify)
}
)
val (newDetails, newSummary) = remote.getGrades(student, semester)
val oldGrades = local.getGradesDetails(semester)
private suspend fun refreshGradeDetails(student: Student, oldGrades: List<Grade>, newDetails: List<Grade>, notify: Boolean) {
val notifyBreakDate = oldGrades.maxBy { it.date }?.date ?: student.registrationDate.toLocalDate()
local.deleteGrades(oldGrades.uniqueSubtract(newDetails))
local.saveGrades(newDetails.uniqueSubtract(oldGrades).onEach {
if (it.date >= notifyBreakDate) it.apply {
isRead = false
if (notify) isNotified = false
}
})
local.deleteGrades(oldGrades uniqueSubtract newDetails)
local.saveGrades((newDetails uniqueSubtract oldGrades).onEach {
if (it.date >= notifyBreakDate) it.apply {
isRead = false
if (notify) isNotified = false
}
})
}
val oldSummaries = local.getGradesSummary(semester)
local.deleteGradesSummary(oldSummaries.uniqueSubtract(newSummary))
local.saveGradesSummary(newSummary.uniqueSubtract(oldSummaries).onEach { summary ->
private suspend fun refreshGradeSummaries(oldSummaries: List<GradeSummary>, newSummary: List<GradeSummary>, notify: Boolean) {
local.deleteGradesSummary(oldSummaries uniqueSubtract newSummary)
local.saveGradesSummary((newSummary uniqueSubtract oldSummaries).onEach { summary ->
val oldSummary = oldSummaries.find { oldSummary -> oldSummary.subject == summary.subject }
summary.isPredictedGradeNotified = when {
summary.predictedGrade.isEmpty() -> true
@ -62,24 +66,22 @@ class GradeRepository @Inject constructor(
else -> oldSummary.finalGradeLastChange
}
})
return local.getGradesDetails(semester) to local.getGradesSummary(semester)
}
suspend fun getUnreadGrades(semester: Semester): List<Grade> {
return local.getGradesDetails(semester).filter { grade -> !grade.isRead }
fun getUnreadGrades(semester: Semester): Flow<List<Grade>> {
return local.getGradesDetails(semester).map { it.filter { grade -> !grade.isRead } }
}
suspend fun getNotNotifiedGrades(semester: Semester): List<Grade> {
return local.getGradesDetails(semester).filter { grade -> !grade.isNotified }
fun getNotNotifiedGrades(semester: Semester): Flow<List<Grade>> {
return local.getGradesDetails(semester).map { it.filter { grade -> !grade.isNotified } }
}
suspend fun getNotNotifiedPredictedGrades(semester: Semester): List<GradeSummary> {
return local.getGradesSummary(semester).filter { gradeSummary -> !gradeSummary.isPredictedGradeNotified }
fun getNotNotifiedPredictedGrades(semester: Semester): Flow<List<GradeSummary>> {
return local.getGradesSummary(semester).map { it.filter { gradeSummary -> !gradeSummary.isPredictedGradeNotified } }
}
suspend fun getNotNotifiedFinalGrades(semester: Semester): List<GradeSummary> {
return local.getGradesSummary(semester).filter { gradeSummary -> !gradeSummary.isFinalGradeNotified }
fun getNotNotifiedFinalGrades(semester: Semester): Flow<List<GradeSummary>> {
return local.getGradesSummary(semester).map { it.filter { gradeSummary -> !gradeSummary.isFinalGradeNotified } }
}
suspend fun updateGrade(grade: Grade) {

View File

@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.dao.GradeStatisticsDao
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
@ -14,34 +15,14 @@ class GradeStatisticsLocal @Inject constructor(
private val gradePointsStatisticsDb: GradePointsStatisticsDao
) {
suspend fun getGradesStatistics(semester: Semester, isSemester: Boolean): List<GradeStatistics> {
fun getGradesStatistics(semester: Semester, isSemester: Boolean): Flow<List<GradeStatistics>> {
return gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester)
}
suspend fun getGradesPointsStatistics(semester: Semester): List<GradePointsStatistics> {
fun getGradesPointsStatistics(semester: Semester): Flow<List<GradePointsStatistics>> {
return gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId)
}
suspend fun getGradesStatistics(semester: Semester, isSemester: Boolean, subjectName: String): List<GradeStatistics> {
return when (subjectName) {
"Wszystkie" -> {
val statistics = gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester)
statistics.groupBy { it.grade }.map {
GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key,
it.value.fold(0) { acc, e -> acc + e.amount }, false)
} + statistics
}
else -> gradeStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName, isSemester)
}
}
suspend fun getGradesPointsStatistics(semester: Semester, subjectName: String): List<GradePointsStatistics> {
return when (subjectName) {
"Wszystkie" -> gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId)
else -> gradePointsStatisticsDb.loadSubject(semester.semesterId, semester.studentId, subjectName)
}
}
suspend fun saveGradesStatistics(gradesStatistics: List<GradeStatistics>) {
gradeStatisticsDb.insertAll(gradesStatistics)
}

View File

@ -6,6 +6,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.pojos.GradeStatisticsItem
import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject
import javax.inject.Singleton
@ -16,29 +17,40 @@ class GradeStatisticsRepository @Inject constructor(
private val remote: GradeStatisticsRemote
) {
suspend fun getGradesStatistics(student: Student, semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false): List<GradeStatisticsItem> {
return local.getGradesStatistics(semester, isSemester, subjectName).mapToStatisticItems().filter { !forceRefresh }.ifEmpty {
val new = remote.getGradeStatistics(student, semester, isSemester)
val old = local.getGradesStatistics(semester, isSemester)
local.deleteGradesStatistics(old.uniqueSubtract(new))
local.saveGradesStatistics(new.uniqueSubtract(old))
local.getGradesStatistics(semester, isSemester, subjectName).mapToStatisticItems()
fun getGradesStatistics(student: Student, semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getGradesStatistics(semester, isSemester) },
fetch = { remote.getGradeStatistics(student, semester, isSemester) },
saveFetchResult = { old, new ->
local.deleteGradesStatistics(old uniqueSubtract new)
local.saveGradesStatistics(new uniqueSubtract old)
},
mapResult = { items ->
when (subjectName) {
"Wszystkie" -> items.groupBy { it.grade }.map {
GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key,
it.value.fold(0) { acc, e -> acc + e.amount }, false)
} + items
else -> items.filter { it.subject == subjectName }
}.mapToStatisticItems()
}
}
)
suspend fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean): List<GradeStatisticsItem> {
return local.getGradesPointsStatistics(semester, subjectName).mapToStatisticsItem().filter { !forceRefresh }.ifEmpty {
val new = remote.getGradePointsStatistics(student, semester)
val old = local.getGradesPointsStatistics(semester)
local.deleteGradesPointsStatistics(old.uniqueSubtract(new))
local.saveGradesPointsStatistics(new.uniqueSubtract(old))
local.getGradesPointsStatistics(semester, subjectName).mapToStatisticsItem()
fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getGradesPointsStatistics(semester) },
fetch = { remote.getGradePointsStatistics(student, semester) },
saveFetchResult = { old, new ->
local.deleteGradesPointsStatistics(old uniqueSubtract new)
local.saveGradesPointsStatistics(new uniqueSubtract old)
},
mapResult = { items ->
when (subjectName) {
"Wszystkie" -> items
else -> items.filter { it.subject == subjectName }
}.mapToStatisticsItem()
}
}
)
private fun List<GradeStatistics>.mapToStatisticItems() = groupBy { it.subject }.map {
GradeStatisticsItem(

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.homework
import io.github.wulkanowy.data.db.dao.HomeworkDao
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@ -22,7 +23,7 @@ class HomeworkLocal @Inject constructor(private val homeworkDb: HomeworkDao) {
homeworkDb.updateAll(homework)
}
suspend fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): List<Homework> {
fun getHomework(semester: Semester, startDate: LocalDate, endDate: LocalDate): Flow<List<Homework>> {
return homeworkDb.loadAll(semester.semesterId, semester.studentId, startDate, endDate)
}
}

View File

@ -4,6 +4,7 @@ import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.uniqueSubtract
import org.threeten.bp.LocalDate
@ -16,18 +17,15 @@ class HomeworkRepository @Inject constructor(
private val remote: HomeworkRemote
) {
suspend fun getHomework(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): List<Homework> {
return local.getHomework(semester, start.monday, end.sunday).filter { !forceRefresh }.ifEmpty {
val new = remote.getHomework(student, semester, start.monday, end.sunday)
val old = local.getHomework(semester, start.monday, end.sunday)
local.deleteHomework(old.uniqueSubtract(new))
local.saveHomework(new.uniqueSubtract(old))
local.getHomework(semester, start.monday, end.sunday)
fun getHomework(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getHomework(semester, start.monday, end.sunday) },
fetch = { remote.getHomework(student, semester, start.monday, end.sunday) },
saveFetchResult = { old, new ->
local.deleteHomework(old uniqueSubtract new)
local.saveHomework(new uniqueSubtract old)
}
}
)
suspend fun toggleDone(homework: Homework) {
local.updateHomework(listOf(homework.apply {

View File

@ -12,16 +12,12 @@ class LoggerRepository @Inject constructor(
private val dispatchers: DispatchersProvider
) {
suspend fun getLastLogLines(): List<String> {
return getLastModified().readText().split("\n")
}
suspend fun getLastLogLines() = getLastModified().readText().split("\n")
suspend fun getLogFiles(): List<File> {
return withContext(dispatchers.backgroundThread) {
File(context.filesDir.absolutePath).listFiles(File::isFile)?.filter {
it.name.endsWith(".log")
}!!
}
suspend fun getLogFiles() = withContext(dispatchers.backgroundThread) {
File(context.filesDir.absolutePath).listFiles(File::isFile)?.filter {
it.name.endsWith(".log")
}!!
}
private suspend fun getLastModified(): File {

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.luckynumber
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Student
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@ -22,7 +23,7 @@ class LuckyNumberLocal @Inject constructor(private val luckyNumberDb: LuckyNumbe
luckyNumberDb.deleteAll(listOfNotNull(luckyNumber))
}
suspend fun getLuckyNumber(student: Student, date: LocalDate): LuckyNumber? {
fun getLuckyNumber(student: Student, date: LocalDate): Flow<LuckyNumber?> {
return luckyNumberDb.load(student.studentId, date)
}
}

View File

@ -2,6 +2,8 @@ package io.github.wulkanowy.data.repositories.luckynumber
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.networkBoundResource
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate.now
import javax.inject.Inject
import javax.inject.Singleton
@ -12,31 +14,25 @@ class LuckyNumberRepository @Inject constructor(
private val remote: LuckyNumberRemote
) {
suspend fun getLuckyNumber(student: Student, forceRefresh: Boolean = false, notify: Boolean = false): LuckyNumber? {
return local.getLuckyNumber(student, now())?.takeIf { !forceRefresh } ?: run {
val new = remote.getLuckyNumber(student)
val old = local.getLuckyNumber(student, now())
fun getLuckyNumber(student: Student, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
shouldFetch = { it == null || forceRefresh },
query = { local.getLuckyNumber(student, now()) },
fetch = { remote.getLuckyNumber(student) },
saveFetchResult = { old, new ->
if (new != old) {
old?.let { local.deleteLuckyNumber(it) }
local.saveLuckyNumber(new?.apply {
if (notify) isNotified = false
})
}
local.saveLuckyNumber(new?.apply {
if (notify) isNotified = false
})
local.getLuckyNumber(student, now())
}
}
)
suspend fun getNotNotifiedLuckyNumber(student: Student): LuckyNumber? {
fun getNotNotifiedLuckyNumber(student: Student): Flow<LuckyNumber?> {
return local.getLuckyNumber(student, now())
}
suspend fun updateLuckyNumber(luckyNumber: LuckyNumber) {
suspend fun updateLuckyNumber(luckyNumber: LuckyNumber?) {
local.updateLuckyNumber(luckyNumber)
}
}

View File

@ -7,6 +7,7 @@ import io.github.wulkanowy.data.db.entities.MessageAttachment
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
@ -28,7 +29,7 @@ class MessageLocal @Inject constructor(
messagesDb.deleteAll(messages)
}
suspend fun getMessageWithAttachment(student: Student, message: Message): MessageWithAttachment {
fun getMessageWithAttachment(student: Student, message: Message): Flow<MessageWithAttachment> {
return messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId)
}
@ -36,7 +37,7 @@ class MessageLocal @Inject constructor(
messageAttachmentDao.insertAttachments(attachments)
}
suspend fun getMessages(student: Student, folder: MessageFolder): List<Message> {
fun getMessages(student: Student, folder: MessageFolder): Flow<List<Message>> {
return when (folder) {
TRASHED -> messagesDb.loadDeleted(student.id.toInt())
else -> messagesDb.loadAll(student.id.toInt(), folder.id)

View File

@ -1,13 +1,15 @@
package io.github.wulkanowy.data.repositories.message
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED
import io.github.wulkanowy.sdk.pojo.SentMessage
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@ -18,46 +20,37 @@ class MessageRepository @Inject constructor(
private val remote: MessageRemote
) {
suspend fun getMessages(student: Student, semester: Semester, folder: MessageFolder, forceRefresh: Boolean = false, notify: Boolean = false): List<Message> {
return local.getMessages(student, folder).filter { !forceRefresh }.ifEmpty {
val new = remote.getMessages(student, semester, folder)
val old = local.getMessages(student, folder)
local.deleteMessages(old.uniqueSubtract(new))
local.saveMessages(new.uniqueSubtract(old).onEach {
fun getMessages(student: Student, semester: Semester, folder: MessageFolder, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getMessages(student, folder) },
fetch = { remote.getMessages(student, semester, folder) },
saveFetchResult = { old, new ->
local.deleteMessages(old uniqueSubtract new)
local.saveMessages((new uniqueSubtract old).onEach {
it.isNotified = !notify
})
local.getMessages(student, folder)
}
}
)
suspend fun getMessage(student: Student, message: Message, markAsRead: Boolean = false): MessageWithAttachment {
return local.getMessageWithAttachment(student, message).let {
if (it.message.content.isNotEmpty().also { status ->
Timber.d("Message content in db empty: ${!status}")
} && !it.message.unread) {
return@let it
}
val dbMessage = local.getMessageWithAttachment(student, message)
val (downloadedMessage, attachments) = remote.getMessagesContentDetails(student, dbMessage.message, markAsRead)
local.updateMessages(listOf(dbMessage.message.copy(unread = !markAsRead).apply {
id = dbMessage.message.id
fun getMessage(student: Student, message: Message, markAsRead: Boolean = false) = networkBoundResource(
shouldFetch = {
Timber.d("Message content in db empty: ${it.message.content.isEmpty()}")
it.message.unread || it.message.content.isEmpty()
},
query = { local.getMessageWithAttachment(student, message) },
fetch = { remote.getMessagesContentDetails(student, it.message, markAsRead) },
saveFetchResult = { old, (downloadedMessage, attachments) ->
local.updateMessages(listOf(old.message.copy(unread = !markAsRead).apply {
id = old.message.id
content = content.ifBlank { downloadedMessage }
}))
local.saveMessageAttachments(attachments)
Timber.d("Message ${message.messageId} with blank content: ${dbMessage.message.content.isBlank()}, marked as read")
local.getMessageWithAttachment(student, message)
Timber.d("Message ${message.messageId} with blank content: ${old.message.content.isBlank()}, marked as read")
}
}
)
suspend fun getNotNotifiedMessages(student: Student): List<Message> {
return local.getMessages(student, RECEIVED)
.filter { message -> !message.isNotified && message.unread }
fun getNotNotifiedMessages(student: Student): Flow<List<Message>> {
return local.getMessages(student, RECEIVED).map { it.filter { message -> !message.isNotified && message.unread } }
}
suspend fun updateMessages(messages: List<Message>) {
@ -68,15 +61,12 @@ class MessageRepository @Inject constructor(
return remote.sendMessage(student, subject, content, recipients)
}
suspend fun deleteMessage(student: Student, message: Message): Boolean {
val delete = remote.deleteMessage(student, message)
suspend fun deleteMessage(student: Student, message: Message) {
val isDeleted = remote.deleteMessage(student, message)
if (!message.removed) local.updateMessages(listOf(message.copy(removed = true).apply {
if (!message.removed) local.updateMessages(listOf(message.copy(removed = isDeleted).apply {
id = message.id
content = message.content
}))
else local.deleteMessages(listOf(message))
return delete // TODO: wtf
})) else local.deleteMessages(listOf(message))
}
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.mobiledevice
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
@ -17,7 +18,7 @@ class MobileDeviceLocal @Inject constructor(private val mobileDb: MobileDeviceDa
mobileDb.deleteAll(devices)
}
suspend fun getDevices(semester: Semester): List<MobileDevice> {
fun getDevices(semester: Semester): Flow<List<MobileDevice>> {
return mobileDb.loadAll(semester.studentId)
}
}

View File

@ -4,6 +4,7 @@ import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.pojos.MobileDeviceToken
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject
import javax.inject.Singleton
@ -14,20 +15,19 @@ class MobileDeviceRepository @Inject constructor(
private val remote: MobileDeviceRemote
) {
suspend fun getDevices(student: Student, semester: Semester, forceRefresh: Boolean = false): List<MobileDevice> {
return local.getDevices(semester).filter { !forceRefresh }.ifEmpty {
val new = remote.getDevices(student, semester)
val old = local.getDevices(semester)
fun getDevices(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getDevices(semester) },
fetch = { remote.getDevices(student, semester) },
saveFetchResult = { old, new ->
local.deleteDevices(old uniqueSubtract new)
local.saveDevices(new uniqueSubtract old)
local.getDevices(semester)
}
}
)
suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice): Boolean {
return remote.unregisterDevice(student, semester, device)
suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) {
remote.unregisterDevice(student, semester, device)
local.deleteDevices(listOf(device))
}
suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken {

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.note
import io.github.wulkanowy.data.db.dao.NoteDao
import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.data.db.entities.Student
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
@ -21,7 +22,7 @@ class NoteLocal @Inject constructor(private val noteDb: NoteDao) {
noteDb.deleteAll(notes)
}
suspend fun getNotes(student: Student): List<Note> {
fun getNotes(student: Student): Flow<List<Note>> {
return noteDb.loadAll(student.studentId)
}
}

View File

@ -3,7 +3,10 @@ package io.github.wulkanowy.data.repositories.note
import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject
import javax.inject.Singleton
@ -13,29 +16,27 @@ class NoteRepository @Inject constructor(
private val remote: NoteRemote
) {
suspend fun getNotes(student: Student, semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): List<Note> {
return local.getNotes(student).filter { !forceRefresh }.ifEmpty {
val new = remote.getNotes(student, semester)
val old = local.getNotes(student)
local.deleteNotes(old.uniqueSubtract(new))
local.saveNotes(new.uniqueSubtract(old).onEach {
fun getNotes(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getNotes(student) },
fetch = { remote.getNotes(student, semester) },
saveFetchResult = { old, new ->
local.deleteNotes(old uniqueSubtract new)
local.saveNotes((new uniqueSubtract old).onEach {
if (it.date >= student.registrationDate.toLocalDate()) it.apply {
isRead = false
if (notify) isNotified = false
}
})
local.getNotes(student)
}
}
)
suspend fun getNotNotifiedNotes(student: Student): List<Note> {
return local.getNotes(student).filter { note -> !note.isNotified }
fun getNotNotifiedNotes(student: Student): Flow<List<Note>> {
return local.getNotes(student).map { it.filter { note -> !note.isNotified } }
}
suspend fun updateNote(note: Note) {
return local.updateNotes(listOf(note))
local.updateNotes(listOf(note))
}
suspend fun updateNotes(notes: List<Note>) {

View File

@ -14,13 +14,17 @@ class RecipientRepository @Inject constructor(
private val remote: RecipientRemote
) {
suspend fun getRecipients(student: Student, role: Int, unit: ReportingUnit, forceRefresh: Boolean = false): List<Recipient> {
return local.getRecipients(student, role, unit).filter { !forceRefresh }.ifEmpty {
val new = remote.getRecipients(student, role, unit)
val old = local.getRecipients(student, role, unit)
suspend fun refreshRecipients(student: Student, role: Int, unit: ReportingUnit) {
val new = remote.getRecipients(student, role, unit)
val old = local.getRecipients(student, role, unit)
local.deleteRecipients(old.uniqueSubtract(new))
local.saveRecipients(new.uniqueSubtract(old))
local.deleteRecipients(old uniqueSubtract new)
local.saveRecipients(new uniqueSubtract old)
}
suspend fun getRecipients(student: Student, role: Int, unit: ReportingUnit): List<Recipient> {
return local.getRecipients(student, role, unit).ifEmpty {
refreshRecipients(student, role, unit)
local.getRecipients(student, role, unit)
}

View File

@ -12,23 +12,27 @@ class ReportingUnitRepository @Inject constructor(
private val remote: ReportingUnitRemote
) {
suspend fun getReportingUnits(student: Student, forceRefresh: Boolean = false): List<ReportingUnit> {
return local.getReportingUnits(student).filter { !forceRefresh }.ifEmpty {
val new = remote.getReportingUnits(student)
val old = local.getReportingUnits(student)
suspend fun refreshReportingUnits(student: Student) {
val new = remote.getReportingUnits(student)
val old = local.getReportingUnits(student)
local.deleteReportingUnits(old.uniqueSubtract(new))
local.saveReportingUnits(new.uniqueSubtract(old))
local.deleteReportingUnits(old.uniqueSubtract(new))
local.saveReportingUnits(new.uniqueSubtract(old))
}
suspend fun getReportingUnits(student: Student): List<ReportingUnit> {
return local.getReportingUnits(student).ifEmpty {
refreshReportingUnits(student)
local.getReportingUnits(student)
}
}
suspend fun getReportingUnit(student: Student, unitId: Int): ReportingUnit {
suspend fun getReportingUnit(student: Student, unitId: Int): ReportingUnit? {
return local.getReportingUnit(student, unitId) ?: run {
getReportingUnits(student, true)
refreshReportingUnits(student)
return local.getReportingUnit(student, unitId)!!
return local.getReportingUnit(student, unitId)
}
}
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.school
import io.github.wulkanowy.data.db.dao.SchoolDao
import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
class SchoolLocal @Inject constructor(private val schoolDb: SchoolDao) {
@ -15,7 +16,7 @@ class SchoolLocal @Inject constructor(private val schoolDb: SchoolDao) {
schoolDb.deleteAll(listOf(school))
}
suspend fun getSchool(semester: Semester): School? {
fun getSchool(semester: Semester): Flow<School?> {
return schoolDb.load(semester.studentId, semester.classId)
}
}

View File

@ -1,8 +1,8 @@
package io.github.wulkanowy.data.repositories.school
import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.networkBoundResource
import javax.inject.Inject
import javax.inject.Singleton
@ -12,18 +12,16 @@ class SchoolRepository @Inject constructor(
private val remote: SchoolRemote
) {
suspend fun getSchoolInfo(student: Student, semester: Semester, forceRefresh: Boolean = false): School {
return local.getSchool(semester).takeIf { it != null && !forceRefresh } ?: run {
val new = remote.getSchoolInfo(student, semester)
val old = local.getSchool(semester)
fun getSchoolInfo(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it == null || forceRefresh },
query = { local.getSchool(semester) },
fetch = { remote.getSchoolInfo(student, semester) },
saveFetchResult = { old, new ->
if (new != old && old != null) {
local.deleteSchool(old)
local.saveSchool(new)
}
local.saveSchool(new)
local.getSchool(semester)!!
}
}
)
}

View File

@ -1,22 +1,24 @@
package io.github.wulkanowy.data.repositories.semester
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.DispatchersProvider
import io.github.wulkanowy.utils.getCurrentOrLast
import io.github.wulkanowy.utils.isCurrent
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.withContext
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SemesterRepository @Inject constructor(
private val remote: SemesterRemote,
private val local: SemesterLocal
private val local: SemesterLocal,
private val dispatchers: DispatchersProvider
) {
suspend fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false): List<Semester> {
return local.getSemesters(student).let { semesters ->
suspend fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false) = withContext(dispatchers.backgroundThread) {
local.getSemesters(student).let { semesters ->
semesters.filter {
!forceRefresh && when {
Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API -> semesters.firstOrNull { it.isCurrent }?.diaryId != 0
@ -36,7 +38,7 @@ class SemesterRepository @Inject constructor(
}
}
suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false): Semester {
return getSemesters(student, forceRefresh).getCurrentOrLast()
suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) = withContext(dispatchers.backgroundThread) {
getSemesters(student, forceRefresh).getCurrentOrLast()
}
}

View File

@ -4,52 +4,55 @@ import android.content.Context
import io.github.wulkanowy.data.db.dao.StudentDao
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.DispatchersProvider
import io.github.wulkanowy.utils.security.decrypt
import io.github.wulkanowy.utils.security.encrypt
import kotlinx.coroutines.withContext
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class StudentLocal @Inject constructor(
private val studentDb: StudentDao,
private val dispatchers: DispatchersProvider,
private val context: Context
) {
suspend fun saveStudents(students: List<Student>): List<Long> {
return studentDb.insertAll(students.map {
suspend fun saveStudents(students: List<Student>) = withContext(dispatchers.backgroundThread) {
studentDb.insertAll(students.map {
if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) it.copy(password = encrypt(it.password, context))
else it
})
}
suspend fun getStudents(decryptPass: Boolean): List<Student> {
return studentDb.loadAll().map {
suspend fun getStudents(decryptPass: Boolean) = withContext(dispatchers.backgroundThread) {
studentDb.loadAll().map {
it.apply {
if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password)
}
}
}
suspend fun getStudentById(id: Int): Student? {
return studentDb.loadById(id)?.apply {
suspend fun getStudentById(id: Int) = withContext(dispatchers.backgroundThread) {
studentDb.loadById(id)?.apply {
if (Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password)
}
}
suspend fun getCurrentStudent(decryptPass: Boolean): Student? {
return studentDb.loadCurrent()?.apply {
suspend fun getCurrentStudent(decryptPass: Boolean) = withContext(dispatchers.backgroundThread) {
studentDb.loadCurrent()?.apply {
if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password)
}
}
suspend fun setCurrentStudent(student: Student) {
return studentDb.run {
suspend fun setCurrentStudent(student: Student) = withContext(dispatchers.backgroundThread) {
studentDb.run {
resetCurrent()
updateCurrent(student.id)
}
}
suspend fun logoutStudent(student: Student) {
return studentDb.delete(student)
suspend fun logoutStudent(student: Student) = withContext(dispatchers.backgroundThread) {
studentDb.delete(student)
}
}

View File

@ -3,13 +3,14 @@ package io.github.wulkanowy.data.repositories.subject
import io.github.wulkanowy.data.db.dao.SubjectDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Subject
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SubjectLocal @Inject constructor(private val subjectDao: SubjectDao) {
suspend fun getSubjects(semester: Semester): List<Subject> {
fun getSubjects(semester: Semester): Flow<List<Subject>> {
return subjectDao.loadAll(semester.diaryId, semester.studentId)
}

View File

@ -2,7 +2,7 @@ package io.github.wulkanowy.data.repositories.subject
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject
import javax.inject.Singleton
@ -13,15 +13,13 @@ class SubjectRepository @Inject constructor(
private val remote: SubjectRemote
) {
suspend fun getSubjects(student: Student, semester: Semester, forceRefresh: Boolean = false): List<Subject> {
return local.getSubjects(semester).filter { !forceRefresh }.ifEmpty {
val new = remote.getSubjects(student, semester)
val old = local.getSubjects(semester)
local.deleteSubjects(old.uniqueSubtract(new))
local.saveSubjects(new.uniqueSubtract(old))
local.getSubjects(semester)
fun getSubjects(student: Student, semester: Semester, forceRefresh: Boolean = false) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getSubjects(semester) },
fetch = { remote.getSubjects(student, semester) },
saveFetchResult = { old, new ->
local.deleteSubjects(old uniqueSubtract new)
local.saveSubjects(new uniqueSubtract old)
}
}
)
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.teacher
import io.github.wulkanowy.data.db.dao.TeacherDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Teacher
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
class TeacherLocal @Inject constructor(private val teacherDb: TeacherDao) {
@ -15,7 +16,7 @@ class TeacherLocal @Inject constructor(private val teacherDb: TeacherDao) {
teacherDb.deleteAll(teachers)
}
suspend fun getTeachers(semester: Semester): List<Teacher> {
fun getTeachers(semester: Semester): Flow<List<Teacher>> {
return teacherDb.loadAll(semester.studentId, semester.classId)
}
}

View File

@ -2,7 +2,7 @@ package io.github.wulkanowy.data.repositories.teacher
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Teacher
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject
import javax.inject.Singleton
@ -13,15 +13,13 @@ class TeacherRepository @Inject constructor(
private val remote: TeacherRemote
) {
suspend fun getTeachers(student: Student, semester: Semester, forceRefresh: Boolean = false): List<Teacher> {
return local.getTeachers(semester).filter { !forceRefresh }.ifEmpty {
val new = remote.getTeachers(student, semester)
val old = local.getTeachers(semester)
local.deleteTeachers(old.uniqueSubtract(new))
local.saveTeachers(new.uniqueSubtract(old))
local.getTeachers(semester)
fun getTeachers(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getTeachers(semester) },
fetch = { remote.getTeachers(student, semester) },
saveFetchResult = { old, new ->
local.deleteTeachers(old uniqueSubtract new)
local.saveTeachers(new uniqueSubtract old)
}
}
)
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.repositories.timetable
import io.github.wulkanowy.data.db.dao.TimetableDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Timetable
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@ -18,7 +19,7 @@ class TimetableLocal @Inject constructor(private val timetableDb: TimetableDao)
timetableDb.deleteAll(timetables)
}
suspend fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): List<Timetable> {
fun getTimetable(semester: Semester, startDate: LocalDate, endDate: LocalDate): Flow<List<Timetable>> {
return timetableDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate)
}
}

View File

@ -2,11 +2,12 @@ package io.github.wulkanowy.data.repositories.timetable
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.map
import org.threeten.bp.LocalDate
import javax.inject.Inject
import javax.inject.Singleton
@ -18,11 +19,11 @@ class TimetableRepository @Inject constructor(
private val schedulerHelper: TimetableNotificationSchedulerHelper
) {
suspend fun getTimetable(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): List<Timetable> {
return local.getTimetable(semester, start.monday, start.sunday).filter { !forceRefresh }.ifEmpty {
val new = remote.getTimetable(student, semester, start.monday, start.sunday)
val old = local.getTimetable(semester, start.monday, start.sunday)
fun getTimetable(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getTimetable(semester, start.monday, end.sunday).map { schedulerHelper.scheduleNotifications(it, student); it } },
fetch = { remote.getTimetable(student, semester, start.monday, end.sunday) },
saveFetchResult = { old, new ->
local.deleteTimetable(old.uniqueSubtract(new).also { schedulerHelper.cancelScheduled(it) })
local.saveTimetable(new.uniqueSubtract(old).also { schedulerHelper.scheduleNotifications(it, student) }.map { item ->
item.also { new ->
@ -34,8 +35,7 @@ class TimetableRepository @Inject constructor(
}
}
})
local.getTimetable(semester, start.monday, start.sunday)
}.filter { it.date in start..end }.also { schedulerHelper.scheduleNotifications(it, student) }
}
},
filterResult = { it.filter { item -> item.date in start..end } }
)
}

View File

@ -12,7 +12,7 @@ class AttendanceSummaryWork @Inject constructor(
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { attendanceSummaryRepository.getAttendanceSummary(student, semester, -1, true) }
return rxCompletable { attendanceSummaryRepository.getAttendanceSummary(student, semester, -1, true).waitForResult() }
}
}

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.sunday
import io.reactivex.Completable
import kotlinx.coroutines.rx2.rxCompletable
import org.threeten.bp.LocalDate.now
@ -13,6 +13,6 @@ import javax.inject.Inject
class AttendanceWork @Inject constructor(private val attendanceRepository: AttendanceRepository) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { attendanceRepository.getAttendance(student, semester, now().monday, now().sunday, true) }
return rxCompletable { attendanceRepository.getAttendance(student, semester, now().monday, now().sunday, true).waitForResult() }
}
}

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRepository
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.sunday
import io.reactivex.Completable
import kotlinx.coroutines.rx2.rxCompletable
import org.threeten.bp.LocalDate.now
@ -15,7 +15,6 @@ class CompletedLessonWork @Inject constructor(
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { completedLessonsRepository.getCompletedLessons(student, semester, now().monday, now().sunday, true) }
return rxCompletable { completedLessonsRepository.getCompletedLessons(student, semester, now().monday, now().sunday, true).waitForResult() }
}
}

View File

@ -13,6 +13,6 @@ import javax.inject.Inject
class ExamWork @Inject constructor(private val examRepository: ExamRepository) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { examRepository.getExams(student, semester, now().monday, now().sunday, true) }
return rxCompletable { examRepository.getExams(student, semester, now().monday, now().sunday, true).waitForResult() }
}
}

View File

@ -7,10 +7,17 @@ import io.reactivex.Completable
import kotlinx.coroutines.rx2.rxCompletable
import javax.inject.Inject
class GradeStatisticsWork @Inject constructor(private val gradeStatisticsRepository: GradeStatisticsRepository) : Work {
class GradeStatisticsWork @Inject constructor(
private val gradeStatisticsRepository: GradeStatisticsRepository
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { gradeStatisticsRepository.getGradesStatistics(student, semester, "Wszystkie", false, forceRefresh = true) }
return rxCompletable {
with(gradeStatisticsRepository) {
getGradesStatistics(student, semester, "Wszystkie", isSemester = true, forceRefresh = true).waitForResult()
getGradesStatistics(student, semester, "Wszystkie", isSemester = false, forceRefresh = true).waitForResult()
getGradesPointsStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult()
}
}
}
}

View File

@ -19,6 +19,7 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxSingle
import javax.inject.Inject
@ -32,14 +33,14 @@ class GradeWork @Inject constructor(
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { gradeRepository.getGrades(student, semester, true, preferencesRepository.isNotificationsEnable) }
.concatWith(Completable.concatArray(rxSingle { gradeRepository.getNotNotifiedGrades(semester) }.flatMapCompletable {
return rxCompletable { gradeRepository.getGrades(student, semester, true, preferencesRepository.isNotificationsEnable).waitForResult() }
.concatWith(Completable.concatArray(rxSingle { gradeRepository.getNotNotifiedGrades(semester).first() }.flatMapCompletable {
if (it.isNotEmpty()) notifyDetails(it)
rxCompletable { gradeRepository.updateGrades(it.onEach { grade -> grade.isNotified = true }) }
}, rxSingle { gradeRepository.getNotNotifiedPredictedGrades(semester) }.flatMapCompletable {
}, rxSingle { gradeRepository.getNotNotifiedPredictedGrades(semester).first() }.flatMapCompletable {
if (it.isNotEmpty()) notifyPredicted(it)
rxCompletable { gradeRepository.updateGradesSummary(it.onEach { grade -> grade.isPredictedGradeNotified = true }) }
}, rxSingle { gradeRepository.getNotNotifiedFinalGrades(semester) }.flatMapCompletable {
}, rxSingle { gradeRepository.getNotNotifiedFinalGrades(semester).first() }.flatMapCompletable {
if (it.isNotEmpty()) notifyFinal(it)
rxCompletable { gradeRepository.updateGradesSummary(it.onEach { grade -> grade.isFinalGradeNotified = true }) }
}))

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.homework.HomeworkRepository
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.sunday
import io.reactivex.Completable
import kotlinx.coroutines.rx2.rxCompletable
import org.threeten.bp.LocalDate.now
@ -13,6 +13,6 @@ import javax.inject.Inject
class HomeworkWork @Inject constructor(private val homeworkRepository: HomeworkRepository) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { homeworkRepository.getHomework(student, semester, now().monday, now().sunday, true) }
return rxCompletable { homeworkRepository.getHomework(student, semester, now().monday, now().sunday, true).waitForResult() }
}
}

View File

@ -18,6 +18,7 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxMaybe
import javax.inject.Inject
@ -31,8 +32,8 @@ class LuckyNumberWork @Inject constructor(
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxMaybe { luckyNumberRepository.getLuckyNumber(student, true, preferencesRepository.isNotificationsEnable) }
.flatMap { rxMaybe { luckyNumberRepository.getNotNotifiedLuckyNumber(student) } }
return rxMaybe { luckyNumberRepository.getLuckyNumber(student, true, preferencesRepository.isNotificationsEnable).waitForResult() }
.flatMap { rxMaybe { luckyNumberRepository.getNotNotifiedLuckyNumber(student).first() } }
.flatMapCompletable {
notify(it)
rxCompletable { luckyNumberRepository.updateLuckyNumber(it.apply { isNotified = true }) }

View File

@ -19,6 +19,7 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxSingle
import javax.inject.Inject
@ -32,8 +33,8 @@ class MessageWork @Inject constructor(
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxSingle { messageRepository.getMessages(student, semester, RECEIVED, true, preferencesRepository.isNotificationsEnable) }
.flatMap { rxSingle { messageRepository.getNotNotifiedMessages(student) } }
return rxSingle { messageRepository.getMessages(student, semester, RECEIVED, true, preferencesRepository.isNotificationsEnable).waitForResult() }
.flatMap { rxSingle { messageRepository.getNotNotifiedMessages(student).first() } }
.flatMapCompletable {
if (it.isNotEmpty()) notify(it)
rxCompletable { messageRepository.updateMessages(it.onEach { message -> message.isNotified = true }) }

View File

@ -18,6 +18,7 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxSingle
import javax.inject.Inject
@ -31,8 +32,8 @@ class NoteWork @Inject constructor(
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxSingle { noteRepository.getNotes(student, semester, true, preferencesRepository.isNotificationsEnable) }
.flatMap { rxSingle { noteRepository.getNotNotifiedNotes(student) } }
return rxSingle { noteRepository.getNotes(student, semester, true, preferencesRepository.isNotificationsEnable).waitForResult() }
.flatMap { rxSingle { noteRepository.getNotNotifiedNotes(student).first() } }
.flatMapCompletable {
if (it.isNotEmpty()) notify(it)
rxCompletable { noteRepository.updateNotes(it.onEach { note -> note.isNotified = true }) }

View File

@ -15,10 +15,11 @@ class RecipientWork @Inject constructor(
) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxSingle { reportingUnitRepository.getReportingUnits(student, true) }
return rxSingle { reportingUnitRepository.refreshReportingUnits(student) }
.flatMap { rxSingle { reportingUnitRepository.getReportingUnits(student) } }
.flatMapCompletable { units ->
Completable.mergeDelayError(units.map {
rxCompletable { recipientRepository.getRecipients(student, 2, it, true) }
rxCompletable { recipientRepository.refreshRecipients(student, 2, it) }
})
}
}

View File

@ -10,6 +10,6 @@ import javax.inject.Inject
class TeacherWork @Inject constructor(private val teacherRepository: TeacherRepository) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { teacherRepository.getTeachers(student, semester, true) }
return rxCompletable { teacherRepository.getTeachers(student, semester, true).waitForResult() }
}
}

View File

@ -13,6 +13,6 @@ import javax.inject.Inject
class TimetableWork @Inject constructor(private val timetableRepository: TimetableRepository) : Work {
override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { timetableRepository.getTimetable(student, semester, now().monday, now().sunday, true) }
return rxCompletable { timetableRepository.getTimetable(student, semester, now().monday, now().sunday, true).waitForResult() }
}
}

View File

@ -1,11 +1,19 @@
package io.github.wulkanowy.services.sync.works
import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.reactivex.Completable
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.takeWhile
interface Work {
fun create(student: Student, semester: Semester): Completable
}
suspend fun <T> Flow<Resource<T>>.waitForResult() = takeWhile {
it.status == Status.LOADING
}.collect()
}

View File

@ -1,24 +1,39 @@
package io.github.wulkanowy.ui.base
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Completable
import io.github.wulkanowy.utils.flowWithResource
import io.reactivex.disposables.CompositeDisposable
import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxSingle
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import kotlin.coroutines.CoroutineContext
open class BasePresenter<T : BaseView>(
protected val errorHandler: ErrorHandler,
protected val studentRepository: StudentRepository,
protected val schedulers: SchedulersProvider
) {
) : CoroutineScope {
private var job: Job = Job()
private val jobs = mutableMapOf<String, Job>()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
@Deprecated("Use flow instead :)")
val disposable = CompositeDisposable()
var view: T? = null
open fun onAttachView(view: T) {
job = Job()
this.view = view
errorHandler.apply {
showErrorMessage = view::showError
@ -28,30 +43,48 @@ open class BasePresenter<T : BaseView>(
}
fun onExpiredLoginSelected() {
Timber.i("Attempt to switch the student after the session expires")
disposable.add(rxSingle { studentRepository.getCurrentStudent(false) }
.flatMapCompletable { rxCompletable { studentRepository.logoutStudent(it) } }
.andThen(rxSingle { studentRepository.getSavedStudents(false) })
.flatMapCompletable {
if (it.isNotEmpty()) {
Timber.i("Switching current student")
rxCompletable { studentRepository.switchStudent(it[0]) }
} else Completable.complete()
flowWithResource {
val student = studentRepository.getCurrentStudent(false)
studentRepository.logoutStudent(student)
val students = studentRepository.getSavedStudents(false)
if (students.isNotEmpty()) {
Timber.i("Switching current student")
studentRepository.switchStudent(students[0])
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.i("Switch student result: Open login view")
view?.openClearLoginView()
}, {
Timber.i("Switch student result: An exception occurred")
errorHandler.dispatch(it)
}))
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Attempt to switch the student after the session expires")
Status.SUCCESS -> {
Timber.i("Switch student result: Open login view")
view?.openClearLoginView()
}
Status.ERROR -> {
Timber.i("Switch student result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.launch("expired")
}
fun <T> Flow<T>.launch(individualJobTag: String = "load"): Job {
jobs[individualJobTag]?.cancel()
val job = launchIn(this@BasePresenter)
jobs[individualJobTag] = job
Timber.d("Job $individualJobTag launched in ${this@BasePresenter.javaClass.simpleName}: $job")
return job
}
fun cancelJobs(vararg names: String) {
names.forEach {
jobs[it]?.cancel()
}
}
open fun onDetachView() {
view = null
disposable.clear()
job.cancel()
errorHandler.clear()
}
}

View File

@ -24,10 +24,10 @@ open class ErrorHandler @Inject constructor(protected val resources: Resources,
}
protected open fun proceed(error: Throwable) {
showErrorMessage(resources.getString(error), error)
when (error) {
is ScramblerException, is BadCredentialsException -> onSessionExpired()
is NoCurrentStudentException -> onNoCurrentStudent()
else -> showErrorMessage(resources.getString(error), error)
}
}

View File

@ -1,12 +1,14 @@
package io.github.wulkanowy.ui.modules.about.contributor
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.pojos.Contributor
import io.github.wulkanowy.data.repositories.appcreator.AppCreatorRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider
import kotlinx.coroutines.rx2.rxSingle
import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach
import javax.inject.Inject
class ContributorPresenter @Inject constructor(
@ -31,10 +33,15 @@ class ContributorPresenter @Inject constructor(
}
private fun loadData() {
disposable.add(rxSingle { appCreatorRepository.getAppCreators() }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally { view?.showProgress(false) }
.subscribe({ view?.run { updateData(it) } }, { errorHandler.dispatch(it) }))
flowWithResource { appCreatorRepository.getAppCreators() }.onEach {
when (it.status) {
Status.LOADING -> view?.showProgress(true)
Status.SUCCESS -> view?.run {
showProgress(false)
updateData(it.data!!)
}
Status.ERROR -> errorHandler.dispatch(it.error!!)
}
}
}
}

View File

@ -24,7 +24,7 @@ class LicenseAdapter @Inject constructor() : RecyclerView.Adapter<LicenseAdapter
with(holder.binding) {
licenseItemName.text = item.libraryName
licenseItemSummary.text = item.license?.licenseName?.takeIf { it.isNotBlank() } ?: item.libraryVersion
licenseItemSummary.text = item.licenses?.firstOrNull()?.licenseName?.takeIf { it.isNotBlank() } ?: item.libraryVersion
root.setOnClickListener { onClickListener(item) }
}

View File

@ -1,15 +1,22 @@
package io.github.wulkanowy.ui.modules.about.license
import com.mikepenz.aboutlibraries.entity.Library
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.DispatchersProvider
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Single
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject
class LicensePresenter @Inject constructor(
schedulers: SchedulersProvider,
private val dispatchers: DispatchersProvider,
errorHandler: ErrorHandler,
studentRepository: StudentRepository
) : BasePresenter<LicenseView>(errorHandler, studentRepository, schedulers) {
@ -21,14 +28,22 @@ class LicensePresenter @Inject constructor(
}
fun onItemSelected(library: Library) {
view?.run { library.license?.licenseDescription?.let { openLicense(it) } }
view?.run { library.licenses?.firstOrNull()?.licenseDescription?.let { openLicense(it) } }
}
private fun loadData() {
disposable.add(Single.fromCallable { view?.appLibraries.orEmpty() }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnEvent { _, _ -> view?.showProgress(false) }
.subscribe({ view?.run { updateData(it) } }, { errorHandler.dispatch(it) }))
flowWithResource {
withContext(dispatchers.backgroundThread) {
view?.appLibraries.orEmpty()
}
}.onEach {
when (it.status) {
Status.LOADING -> Timber.d("License data load started")
Status.SUCCESS -> view?.updateData(it.data!!)
Status.ERROR -> errorHandler.dispatch(it.error!!)
}
}.afterLoading {
view?.showProgress(false)
}.launch()
}
}

View File

@ -1,11 +1,13 @@
package io.github.wulkanowy.ui.modules.about.logviewer
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.logger.LoggerRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider
import kotlinx.coroutines.rx2.rxSingle
import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@ -23,16 +25,19 @@ class LogViewerPresenter @Inject constructor(
}
fun onShareLogsSelected(): Boolean {
disposable.add(rxSingle { loggerRepository.getLogFiles() }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({ files ->
Timber.i("Loading logs files result: ${files.joinToString { it.name }}")
view?.shareLogs(files)
}, {
Timber.i("Loading logs files result: An exception occurred")
errorHandler.dispatch(it)
}))
flowWithResource { loggerRepository.getLogFiles() }.onEach {
when (it.status) {
Status.LOADING -> Timber.d("Loading logs files started")
Status.SUCCESS -> {
Timber.i("Loading logs files result: ${it.data!!.joinToString { file -> file.name }}")
view?.shareLogs(it.data)
}
Status.ERROR -> {
Timber.i("Loading logs files result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.launch("share")
return true
}
@ -41,15 +46,18 @@ class LogViewerPresenter @Inject constructor(
}
private fun loadLogFile() {
disposable.add(rxSingle { loggerRepository.getLastLogLines() }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.i("Loading last log file result: load ${it.size} lines")
view?.setLines(it)
}, {
Timber.i("Loading last log file result: An exception occurred")
errorHandler.dispatch(it)
}))
flowWithResource { loggerRepository.getLastLogLines() }.onEach {
when (it.status) {
Status.LOADING -> Timber.d("Loading last log file started")
Status.SUCCESS -> {
Timber.i("Loading last log file result: load ${it.data!!.size} lines")
view?.setLines(it.data)
}
Status.ERROR -> {
Timber.i("Loading last log file result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.launch("file")
}
}

View File

@ -1,14 +1,15 @@
package io.github.wulkanowy.ui.modules.account
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Single
import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxSingle
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@ -37,20 +38,20 @@ class AccountPresenter @Inject constructor(
}
fun onLogoutConfirm() {
Timber.i("Attempt to logout current user ")
disposable.add(rxSingle { studentRepository.getCurrentStudent(false) }
.flatMapCompletable { rxCompletable { studentRepository.logoutStudent(it) } }
.andThen(rxSingle { studentRepository.getSavedStudents(false) })
.flatMap {
if (it.isNotEmpty()) rxCompletable { studentRepository.switchStudent(it[0]) }.toSingle { it }
else Single.just(it)
flowWithResource {
val student = studentRepository.getCurrentStudent(false)
studentRepository.logoutStudent(student)
val students = studentRepository.getSavedStudents(false)
if (students.isNotEmpty()) {
studentRepository.switchStudent(students[0])
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally { view?.dismissView() }
.subscribe({
view?.apply {
if (it.isEmpty()) {
students
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Attempt to logout current user ")
Status.SUCCESS -> view?.run {
if (it.data!!.isEmpty()) {
Timber.i("Logout result: Open login view")
syncManager.stopSyncWorker()
openClearLoginView()
@ -59,30 +60,35 @@ class AccountPresenter @Inject constructor(
recreateMainView()
}
}
}, {
Timber.i("Logout result: An exception occurred")
errorHandler.dispatch(it)
}))
Status.ERROR -> {
Timber.i("Logout result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.dismissView()
}.launch("logout")
}
fun onItemSelected(student: Student) {
Timber.i("Select student item ${student.id}")
if (student.isCurrent) {
view?.dismissView()
} else {
Timber.i("Attempt to change a student")
disposable.add(rxSingle { studentRepository.switchStudent(student) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally { view?.dismissView() }
.subscribe({
} else flowWithResource { studentRepository.switchStudent(student) }.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Attempt to change a student")
Status.SUCCESS -> {
Timber.i("Change a student result: Success")
view?.recreateMainView()
}, {
}
Status.ERROR -> {
Timber.i("Change a student result: An exception occurred")
errorHandler.dispatch(it)
}))
}
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.dismissView()
}.launch("switch")
}
private fun createAccountItems(items: List<Student>): List<AccountItem<*>> {
@ -94,17 +100,18 @@ class AccountPresenter @Inject constructor(
}
private fun loadData() {
Timber.i("Loading account data started")
disposable.add(rxSingle { studentRepository.getSavedStudents(false) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.map { createAccountItems(it) }
.subscribe({
Timber.i("Loading account result: Success")
view?.updateData(it)
}, {
Timber.i("Loading account result: An exception occurred")
errorHandler.dispatch(it)
}))
flowWithResource { studentRepository.getSavedStudents(false) }.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading account data started")
Status.SUCCESS -> {
Timber.i("Loading account result: Success")
view?.updateData(createAccountItems(it.data!!))
}
Status.ERROR -> {
Timber.i("Loading account result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.launch()
}
}

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.ui.modules.attendance
import android.annotation.SuppressLint
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
@ -10,13 +11,18 @@ import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
import io.github.wulkanowy.utils.isHolidays
import io.github.wulkanowy.utils.nextSchoolDay
import io.github.wulkanowy.utils.previousOrSameSchoolDay
import io.github.wulkanowy.utils.previousSchoolDay
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.rx2.rxSingle
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDate.now
import org.threeten.bp.LocalDate.ofEpochDay
@ -168,100 +174,93 @@ class AttendancePresenter @Inject constructor(
}
private fun setBaseDateOnHolidays() {
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { rxSingle { semesterRepository.getCurrentSemester(it) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
currentDate = baseDate
reloadNavigation()
}) {
Timber.i("Loading semester result: An exception occurred")
})
flow {
val student = studentRepository.getCurrentStudent()
emit(semesterRepository.getCurrentSemester(student))
}.catch {
Timber.i("Loading semester result: An exception occurred")
}.onEach {
baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
currentDate = baseDate
reloadNavigation()
}.launch("holidays")
}
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
Timber.i("Loading attendance data started")
currentDate = date
disposable.apply {
clear()
add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { student ->
rxSingle { semesterRepository.getCurrentSemester(student) }.flatMap { semester ->
rxSingle { attendanceRepository.getAttendance(student, semester, date, date, forceRefresh) }
}
}
.map { list ->
if (prefRepository.isShowPresent) list
else list.filter { !it.presence }
}
.map { items -> items.sortedBy { it.number } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}
.subscribe({
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
attendanceRepository.getAttendance(student, semester, date, date, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> view?.showExcuseButton(false)
Status.SUCCESS -> {
Timber.i("Loading attendance result: Success")
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
updateData(it.data!!.let { items ->
if (prefRepository.isShowPresent) items
else items.filter { item -> !item.presence }
}.sortedBy { item -> item.number })
showEmpty(it.data.isEmpty())
showErrorView(false)
showContent(it.isNotEmpty())
showExcuseButton(it.any { item -> item.excusable })
showContent(it.data.isNotEmpty())
showExcuseButton(it.data.any { item -> item.excusable })
}
analytics.logEvent(
"load_data",
"type" to "attendance",
"items" to it.size,
"force_refresh" to forceRefresh
"items" to it.data!!.size
)
}) {
Timber.i("Loading attendance result: An exception occurred")
errorHandler.dispatch(it)
}
)
}
Status.ERROR -> {
Timber.i("Loading attendance result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}.launch()
}
private fun excuseAbsence(reason: String?, toExcuseList: List<Attendance>) {
Timber.i("Excusing absence started")
disposable.apply {
add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { student ->
rxSingle { semesterRepository.getCurrentSemester(student) }.flatMap { semester ->
rxSingle { attendanceRepository.excuseForAbsence(student, semester, toExcuseList, reason) }
}
flowWithResource {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
attendanceRepository.excuseForAbsence(student, semester, toExcuseList, reason)
}.onEach {
when (it.status) {
Status.LOADING -> view?.run {
Timber.i("Excusing absence started")
showProgress(true)
showContent(false)
showExcuseButton(false)
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnSubscribe {
view?.apply {
showProgress(true)
showContent(false)
showExcuseButton(false)
}
}
.subscribe({
Status.SUCCESS -> {
Timber.i("Excusing for absence result: Success")
analytics.logEvent("excuse_absence", "items" to attendanceToExcuseList.size)
attendanceToExcuseList.clear()
view?.apply {
view?.run {
showExcuseButton(false)
showMessage(excuseSuccessString)
showContent(true)
showProgress(false)
}
loadData(currentDate, true)
}) {
loadData(currentDate, forceRefresh = true)
}
Status.ERROR -> {
Timber.i("Excusing for absence result: An exception occurred")
view?.showProgress(false)
errorHandler.dispatch(it)
})
}
errorHandler.dispatch(it.error!!)
loadData(currentDate)
}
}
}.launch("excuse")
}
private fun showErrorViewOnError(message: String, error: Throwable) {

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.attendance.summary
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.data.repositories.attendancesummary.AttendanceSummaryRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
@ -9,7 +10,9 @@ import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import kotlinx.coroutines.rx2.rxSingle
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.onEach
import org.threeten.bp.Month
import timber.log.Timber
import javax.inject.Inject
@ -73,46 +76,43 @@ class AttendanceSummaryPresenter @Inject constructor(
}
private fun loadData(subjectId: Int, forceRefresh: Boolean = false) {
Timber.i("Loading attendance summary data started")
currentSubjectId = subjectId
disposable.apply {
clear()
add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { student ->
rxSingle { semesterRepository.getCurrentSemester(student) }.flatMap {
rxSingle { attendanceSummaryRepository.getAttendanceSummary(student, it, subjectId, forceRefresh) }
}
}
.map { items -> items.sortedByDescending { if (it.month.value <= Month.JUNE.value) it.month.value + 12 else it.month.value } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}
.subscribe({
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
attendanceSummaryRepository.getAttendanceSummary(student, semester, subjectId, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading attendance summary data started")
Status.SUCCESS -> {
Timber.i("Loading attendance summary result: Success")
view?.apply {
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
updateDataSet(it)
showEmpty(it.data!!.isEmpty())
showContent(it.data.isNotEmpty())
updateDataSet(it.data.sortedByDescending { item ->
if (item.month.value <= Month.JUNE.value) item.month.value + 12 else item.month.value
})
}
analytics.logEvent(
"load_data",
"type" to "attendance_summary",
"items" to it.size,
"force_refresh" to forceRefresh,
"items" to it.data!!.size,
"item_id" to subjectId
)
}) {
Timber.i("Loading attendance summary result: An exception occurred")
errorHandler.dispatch(it)
}
)
}
Status.ERROR -> {
Timber.i("Loading attendance summary result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}.launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
@ -127,27 +127,27 @@ class AttendanceSummaryPresenter @Inject constructor(
}
private fun loadSubjects() {
Timber.i("Loading attendance summary subjects started")
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { student ->
rxSingle { semesterRepository.getCurrentSemester(student) }.flatMap { semester ->
rxSingle { subjectRepository.getSubjects(student, semester) }
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
subjectRepository.getSubjects(student, semester)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading attendance summary subjects started")
Status.SUCCESS -> {
subjects = it.data!!
Timber.i("Loading attendance summary subjects result: Success")
view?.run {
view?.updateSubjects(ArrayList(it.data.map { subject -> subject.name }))
showSubjects(true)
}
}
Status.ERROR -> {
Timber.i("Loading attendance summary subjects result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
.doOnSuccess { subjects = it }
.map { ArrayList(it.map { subject -> subject.name }) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.i("Loading attendance summary subjects result: Success")
view?.run {
view?.updateSubjects(it)
showSubjects(true)
}
}, {
Timber.i("Loading attendance summary subjects result: An exception occurred")
errorHandler.dispatch(it)
})
)
}.launch("subjects")
}
}

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.exam
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.repositories.exam.ExamRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
@ -8,13 +9,17 @@ import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
import io.github.wulkanowy.utils.isHolidays
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.nextOrSameSchoolDay
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.rx2.rxSingle
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDate.now
import org.threeten.bp.LocalDate.ofEpochDay
@ -90,59 +95,54 @@ class ExamPresenter @Inject constructor(
}
private fun setBaseDateOnHolidays() {
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { rxSingle { semesterRepository.getCurrentSemester(it) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
currentDate = baseDate
reloadNavigation()
}) {
Timber.i("Loading semester result: An exception occurred")
})
flow {
val student = studentRepository.getCurrentStudent()
emit(semesterRepository.getCurrentSemester(student))
}.catch {
Timber.i("Loading semester result: An exception occurred")
}.onEach {
baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
currentDate = baseDate
reloadNavigation()
}.launch("holidays")
}
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
Timber.i("Loading exam data started")
currentDate = date
disposable.apply {
clear()
add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { student ->
rxSingle { semesterRepository.getCurrentSemester(student) }.flatMap { semester ->
rxSingle { examRepository.getExams(student, semester, currentDate.monday, currentDate.sunday, forceRefresh) }
}
}
.map { createExamItems(it) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}
.subscribe({
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
examRepository.getExams(student, semester, currentDate.monday, currentDate.sunday, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading exam data started")
Status.SUCCESS -> {
Timber.i("Loading exam result: Success")
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
updateData(createExamItems(it.data!!))
showEmpty(it.data.isEmpty())
showErrorView(false)
showContent(it.isNotEmpty())
showContent(it.data.isNotEmpty())
}
analytics.logEvent(
"load_data",
"type" to "exam",
"items" to it.size,
"force_refresh" to forceRefresh
"items" to it.data!!.size
)
}) {
}
Status.ERROR -> {
Timber.i("Loading exam result: An exception occurred")
errorHandler.dispatch(it)
})
}
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}.launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {

View File

@ -1,5 +1,7 @@
package io.github.wulkanowy.ui.modules.grade
import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester
@ -13,8 +15,17 @@ import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.BOTH_SEMESTERS
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.ONE_SEMESTER
import io.github.wulkanowy.utils.calcAverage
import io.github.wulkanowy.utils.changeModifier
import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import javax.inject.Inject
@OptIn(FlowPreview::class)
class GradeAverageProvider @Inject constructor(
private val semesterRepository: SemesterRepository,
private val gradeRepository: GradeRepository,
@ -25,63 +36,64 @@ class GradeAverageProvider @Inject constructor(
private val minusModifier get() = preferencesRepository.gradeMinusModifier
suspend fun getGradesDetailsWithAverage(student: Student, semesterId: Int, forceRefresh: Boolean = false): List<GradeDetailsWithAverage> {
return semesterRepository.getSemesters(student).let { semesters ->
when (preferencesRepository.gradeAverageMode) {
ONE_SEMESTER -> getSemesterDetailsWithAverage(student, semesters.single { it.semesterId == semesterId }, forceRefresh)
BOTH_SEMESTERS -> calculateBothSemestersAverage(student, semesters, semesterId, forceRefresh)
ALL_YEAR -> calculateAllYearAverage(student, semesters, semesterId, forceRefresh)
}
}
}
fun getGradesDetailsWithAverage(student: Student, semesterId: Int, forceRefresh: Boolean) = flowWithResourceIn {
val semesters = semesterRepository.getSemesters(student)
private suspend fun calculateBothSemestersAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): List<GradeDetailsWithAverage> {
when (preferencesRepository.gradeAverageMode) {
ONE_SEMESTER -> getSemesterDetailsWithAverage(student, semesters.single { it.semesterId == semesterId }, forceRefresh)
BOTH_SEMESTERS -> calculateBothSemestersAverage(student, semesters, semesterId, forceRefresh)
ALL_YEAR -> calculateAllYearAverage(student, semesters, semesterId, forceRefresh)
}
}.distinctUntilChanged()
private fun calculateBothSemestersAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Flow<Resource<List<GradeDetailsWithAverage>>> {
val selectedSemester = semesters.single { it.semesterId == semesterId }
val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
return getSemesterDetailsWithAverage(student, selectedSemester, forceRefresh).let { selectedDetails ->
val isAnyAverage = selectedDetails.any { it.average != .0 }
return getSemesterDetailsWithAverage(student, selectedSemester, forceRefresh).flatMapConcat { selectedDetails ->
val isAnyAverage = selectedDetails.data.orEmpty().any { it.average != .0 }
if (selectedSemester != firstSemester) {
getSemesterDetailsWithAverage(student, firstSemester, forceRefresh).let { secondDetails ->
selectedDetails.map { selected ->
val second = secondDetails.singleOrNull { it.subject == selected.subject }
getSemesterDetailsWithAverage(student, firstSemester, forceRefresh).map { secondDetails ->
secondDetails.copy(data = selectedDetails.data?.map { selected ->
val second = secondDetails.data.orEmpty().singleOrNull { it.subject == selected.subject }
selected.copy(average = if (!isAnyAverage || preferencesRepository.gradeAverageForceCalc) {
val selectedGrades = selected.grades.updateModifiers(student).calcAverage()
(selectedGrades + (second?.grades?.updateModifiers(student)?.calcAverage() ?: selectedGrades)) / 2
} else (selected.average + (second?.average ?: selected.average)) / 2)
}
}
} else selectedDetails
})
}.filter { it.status != Status.LOADING }.filter { it.data != null }
} else flowOf(selectedDetails)
}
}
private suspend fun calculateAllYearAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): List<GradeDetailsWithAverage> {
private fun calculateAllYearAverage(student: Student, semesters: List<Semester>, semesterId: Int, forceRefresh: Boolean): Flow<Resource<List<GradeDetailsWithAverage>>> {
val selectedSemester = semesters.single { it.semesterId == semesterId }
val firstSemester = semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
return getSemesterDetailsWithAverage(student, selectedSemester, forceRefresh).let { selectedDetails ->
val isAnyAverage = selectedDetails.any { it.average != .0 }
return getSemesterDetailsWithAverage(student, selectedSemester, forceRefresh).flatMapConcat { selectedDetails ->
val isAnyAverage = selectedDetails.data.orEmpty().any { it.average != .0 }
if (selectedSemester != firstSemester) {
getSemesterDetailsWithAverage(student, firstSemester, forceRefresh).let { secondDetails ->
selectedDetails.map { selected ->
val second = secondDetails.singleOrNull { it.subject == selected.subject }
getSemesterDetailsWithAverage(student, firstSemester, forceRefresh).map { secondDetails ->
secondDetails.copy(data = selectedDetails.data?.map { selected ->
val second = secondDetails.data.orEmpty().singleOrNull { it.subject == selected.subject }
selected.copy(average = if (!isAnyAverage || preferencesRepository.gradeAverageForceCalc) {
(selected.grades.updateModifiers(student) + second?.grades?.updateModifiers(student).orEmpty()).calcAverage()
} else selected.average)
}
}
} else selectedDetails
})
}.filter { it.status != Status.LOADING }.filter { it.data != null }
} else flowOf(selectedDetails)
}
}
private suspend fun getSemesterDetailsWithAverage(student: Student, semester: Semester, forceRefresh: Boolean): List<GradeDetailsWithAverage> {
return gradeRepository.getGrades(student, semester, forceRefresh).let { (details, summaries) ->
val isAnyAverage = summaries.any { it.average != .0 }
val allGrades = details.groupBy { it.subject }
private fun getSemesterDetailsWithAverage(student: Student, semester: Semester, forceRefresh: Boolean): Flow<Resource<List<GradeDetailsWithAverage>>> {
return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh).map { res ->
val (details, summaries) = res.data ?: null to null
val isAnyAverage = summaries.orEmpty().any { it.average != .0 }
val allGrades = details.orEmpty().groupBy { it.subject }
summaries.emulateEmptySummaries(student, semester, allGrades.toList(), isAnyAverage).map { summary ->
Resource(res.status, summaries?.emulateEmptySummaries(student, semester, allGrades.toList(), isAnyAverage)?.map { summary ->
val grades = allGrades[summary.subject].orEmpty()
GradeDetailsWithAverage(
subject = summary.subject,
@ -92,7 +104,7 @@ class GradeAverageProvider @Inject constructor(
summary = summary,
grades = grades
)
}
}, res.error)
}
}

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.grade
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
@ -7,10 +8,11 @@ import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.getCurrentOrLast
import kotlinx.coroutines.rx2.rxSingle
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject
class GradePresenter @Inject constructor(
@ -99,29 +101,33 @@ class GradePresenter @Inject constructor(
}
private fun loadData() {
Timber.i("Loading grade data started")
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { rxSingle { semesterRepository.getSemesters(it, refreshOnNoCurrent = true) } }
.delay(200, MILLISECONDS)
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
val current = it.getCurrentOrLast()
selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex
schoolYear = current.schoolYear
semesters = it.filter { semester -> semester.diaryId == current.diaryId }
view?.setCurrentSemesterName(current.semesterName, schoolYear)
flowWithResource {
val student = studentRepository.getCurrentStudent()
delay(200)
semesterRepository.getSemesters(student, refreshOnNoCurrent = true)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading grade data started")
Status.SUCCESS -> {
val current = it.data!!.getCurrentOrLast()
selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex
schoolYear = current.schoolYear
semesters = it.data.filter { semester -> semester.diaryId == current.diaryId }
view?.setCurrentSemesterName(current.semesterName, schoolYear)
view?.run {
Timber.i("Loading grade result: Attempt load index $currentPageIndex")
loadChild(currentPageIndex)
showErrorView(false)
showSemesterSwitch(true)
view?.run {
Timber.i("Loading grade result: Attempt load index $currentPageIndex")
loadChild(currentPageIndex)
showErrorView(false)
showSemesterSwitch(true)
}
}
}) {
Timber.i("Loading grade result: An exception occurred")
errorHandler.dispatch(it)
})
Status.ERROR -> {
Timber.i("Loading grade result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.grade.details
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.repositories.grade.GradeRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
@ -11,8 +12,11 @@ import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
import io.github.wulkanowy.ui.modules.grade.GradeDetailsWithAverage
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxSingle
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@ -41,7 +45,9 @@ class GradeDetailsPresenter @Inject constructor(
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
currentSemesterId = semesterId
loadData(semesterId, forceRefresh)
if (!forceRefresh) view?.showErrorView(false)
}
fun onGradeItemSelected(grade: Grade, position: Int) {
@ -63,24 +69,24 @@ class GradeDetailsPresenter @Inject constructor(
}
fun onMarkAsReadSelected(): Boolean {
Timber.i("Select mark grades as read")
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { rxSingle { semesterRepository.getSemesters(it) } }
.flatMap { rxSingle { gradeRepository.getUnreadGrades(it.first { item -> item.semesterId == currentSemesterId }) } }
.map { it.map { grade -> grade.apply { isRead = true } } }
.flatMapCompletable {
Timber.i("Mark as read ${it.size} grades")
rxCompletable { gradeRepository.updateGrades(it) }
flowWithResource {
val student = studentRepository.getCurrentStudent()
val semesters = semesterRepository.getSemesters(student)
val semester = semesters.first { item -> item.semesterId == currentSemesterId }
val unreadGrades = gradeRepository.getUnreadGrades(semester).first()
Timber.i("Mark as read ${unreadGrades.size} grades")
gradeRepository.updateGrades(unreadGrades.map { it.apply { isRead = true } })
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Select mark grades as read")
Status.SUCCESS -> Timber.i("Mark as read result: Success")
Status.ERROR -> {
Timber.i("Mark as read result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.i("Mark as read result: Success")
loadData(currentSemesterId, false)
}, {
Timber.i("Mark as read result: An exception occurred")
errorHandler.dispatch(it)
}))
}.launch("mark")
return true
}
@ -119,7 +125,7 @@ class GradeDetailsPresenter @Inject constructor(
showEmpty(false)
clearView()
}
disposable.clear()
cancelJobs("load")
}
fun updateMarkAsDoneButton() {
@ -127,43 +133,46 @@ class GradeDetailsPresenter @Inject constructor(
}
private fun loadData(semesterId: Int, forceRefresh: Boolean) {
Timber.i("Loading grade details data started")
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { rxSingle { averageProvider.getGradesDetailsWithAverage(it, semesterId, forceRefresh) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
showRefresh(false)
showProgress(false)
enableSwipe(true)
notifyParentDataLoaded(semesterId)
}
}
.subscribe({ grades ->
Timber.i("Loading grade details result: Success")
newGradesAmount = grades.sumBy { it.grades.sumBy { grade -> if (!grade.isRead) 1 else 0 } }
updateMarkAsDoneButton()
view?.run {
showEmpty(grades.isEmpty())
showErrorView(false)
showContent(grades.isNotEmpty())
updateData(
data = createGradeItems(grades),
isGradeExpandable = preferencesRepository.isGradeExpandable,
gradeColorTheme = preferencesRepository.gradeColorTheme
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
averageProvider.getGradesDetailsWithAverage(student, semesterId, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading grade details data started")
Status.SUCCESS -> {
Timber.i("Loading grade details result: Success")
newGradesAmount = it.data!!.sumBy { item -> item.grades.sumBy { grade -> if (!grade.isRead) 1 else 0 } }
updateMarkAsDoneButton()
val items = createGradeItems(it.data)
view?.run {
showEmpty(items.isEmpty())
showErrorView(false)
showContent(items.isNotEmpty())
updateData(
data = items,
isGradeExpandable = preferencesRepository.isGradeExpandable,
gradeColorTheme = preferencesRepository.gradeColorTheme
)
}
analytics.logEvent(
"load_data",
"type" to "grade_details",
"items" to it.data.size
)
}
analytics.logEvent(
"load_data",
"type" to "grade_details",
"items" to grades.size,
"force_refresh" to forceRefresh
)
}) {
Timber.i("Loading grade details result: An exception occurred")
errorHandler.dispatch(it)
})
Status.ERROR -> {
Timber.i("Loading grade details result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.run {
showRefresh(false)
showProgress(false)
enableSwipe(true)
notifyParentDataLoaded(semesterId)
}
}.launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
@ -197,15 +206,15 @@ class GradeDetailsPresenter @Inject constructor(
}
private fun updateGrade(grade: Grade) {
Timber.i("Attempt to update grade ${grade.id}")
disposable.add(rxCompletable { gradeRepository.updateGrade(grade) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.i("Update grade result: Success")
}) { error ->
Timber.i("Update grade result: An exception occurred")
errorHandler.dispatch(error)
})
flowWithResource { gradeRepository.updateGrade(grade) }.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Attempt to update grade ${grade.id}")
Status.SUCCESS -> Timber.i("Update grade result: Success")
Status.ERROR -> {
Timber.i("Update grade result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.launch("update")
}
}

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.grade.statistics
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.data.repositories.gradestatistics.GradeStatisticsRepository
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
@ -10,7 +11,9 @@ import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import kotlinx.coroutines.rx2.rxSingle
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@ -46,6 +49,7 @@ class GradeStatisticsPresenter @Inject constructor(
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
currentSemesterId = semesterId
loadSubjects()
if (!forceRefresh) view?.showErrorView(false)
loadDataByType(semesterId, currentSubjectName, currentType, forceRefresh)
}
@ -65,7 +69,7 @@ class GradeStatisticsPresenter @Inject constructor(
showEmpty(false)
clearView()
}
disposable.clear()
cancelJobs("load")
}
fun onSwipeRefresh() {
@ -103,7 +107,7 @@ class GradeStatisticsPresenter @Inject constructor(
fun onTypeChange() {
val type = view?.currentType ?: ViewType.POINTS
Timber.i("Select grade stats semester: $type")
disposable.clear()
cancelJobs("load")
view?.run {
showContent(false)
showProgress(true)
@ -116,77 +120,77 @@ class GradeStatisticsPresenter @Inject constructor(
}
private fun loadSubjects() {
Timber.i("Loading grade stats subjects started")
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { student ->
rxSingle { semesterRepository.getCurrentSemester(student) }.flatMap { semester ->
rxSingle { subjectRepository.getSubjects(student, semester) }
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
subjectRepository.getSubjects(student, semester)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading grade stats subjects started")
Status.SUCCESS -> {
subjects = it.data!!
Timber.i("Loading grade stats subjects result: Success")
view?.run {
view?.updateSubjects(ArrayList(it.data.map { subject -> subject.name }))
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
}
}
Status.ERROR -> {
Timber.i("Loading grade stats subjects result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
.doOnSuccess { subjects = it }
.map { ArrayList(it.map { subject -> subject.name }) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.i("Loading grade stats subjects result: Success")
view?.updateSubjects(it)
}, {
Timber.i("Loading grade stats subjects result: An exception occurred")
errorHandler.dispatch(it)
})
)
}.launch("subjects")
}
private fun loadDataByType(semesterId: Int, subjectName: String, type: ViewType, forceRefresh: Boolean = false) {
currentSubjectName = if (preferencesRepository.showAllSubjectsOnStatisticsList) "Wszystkie" else subjectName
currentType = type
Timber.i("Loading grade stats data started")
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { student ->
rxSingle { semesterRepository.getSemesters(student) }.flatMap { semesters ->
val semester = semesters.first { item -> item.semesterId == semesterId }
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semesters = semesterRepository.getSemesters(student)
val semester = semesters.first { item -> item.semesterId == semesterId }
rxSingle {
with(gradeStatisticsRepository) {
when (type) {
ViewType.SEMESTER -> getGradesStatistics(student, semester, currentSubjectName, true, forceRefresh)
ViewType.PARTIAL -> getGradesStatistics(student, semester, currentSubjectName, false, forceRefresh)
ViewType.POINTS -> getGradesPointsStatistics(student, semester, currentSubjectName, forceRefresh)
}
}
with(gradeStatisticsRepository) {
when (type) {
ViewType.SEMESTER -> getGradesStatistics(student, semester, currentSubjectName, true, forceRefresh)
ViewType.PARTIAL -> getGradesStatistics(student, semester, currentSubjectName, false, forceRefresh)
ViewType.POINTS -> getGradesPointsStatistics(student, semester, currentSubjectName, forceRefresh)
}
}
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading grade stats data started")
Status.SUCCESS -> {
Timber.i("Loading grade stats result: Success")
view?.run {
showEmpty(it.data!!.isEmpty())
showContent(it.data.isNotEmpty())
showErrorView(false)
updateData(it.data, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList)
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
}
analytics.logEvent(
"load_data",
"type" to "grade_statistics",
"items" to it.data!!.size
)
}
Status.ERROR -> {
Timber.i("Loading grade stats result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
showRefresh(false)
showProgress(false)
enableSwipe(true)
notifyParentDataLoaded(semesterId)
}
}.afterLoading {
view?.run {
showRefresh(false)
showProgress(false)
enableSwipe(true)
notifyParentDataLoaded(semesterId)
}
.subscribe({
Timber.i("Loading grade stats result: Success")
view?.run {
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
showErrorView(false)
updateData(it, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList)
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
}
analytics.logEvent(
"load_data",
"type" to "grade_statistics",
"items" to it.size,
"force_refresh" to forceRefresh
)
}) {
Timber.i("Loading grade stats result: An exception occurred")
errorHandler.dispatch(it)
})
}.launch("load")
}
private fun showErrorViewOnError(message: String, error: Throwable) {

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.grade.summary
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
@ -8,7 +9,9 @@ import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
import io.github.wulkanowy.ui.modules.grade.GradeDetailsWithAverage
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import kotlinx.coroutines.rx2.rxSingle
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@ -30,36 +33,45 @@ class GradeSummaryPresenter @Inject constructor(
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
Timber.i("Loading grade summary data started")
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { rxSingle { averageProvider.getGradesDetailsWithAverage(it, semesterId, forceRefresh) } }
.map { createGradeSummaryItems(it) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
showRefresh(false)
showProgress(false)
enableSwipe(true)
notifyParentDataLoaded(semesterId)
loadData(semesterId, forceRefresh)
if (!forceRefresh) view?.showErrorView(false)
}
private fun loadData(semesterId: Int, forceRefresh: Boolean) {
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
averageProvider.getGradesDetailsWithAverage(student, semesterId, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading grade summary started")
Status.SUCCESS -> {
Timber.i("Loading grade summary result: Success")
view?.run {
showEmpty(it.data!!.isEmpty())
showContent(it.data.isNotEmpty())
showErrorView(false)
updateData(createGradeSummaryItems(it.data))
}
analytics.logEvent(
"load_data",
"type" to "grade_summary",
"items" to it.data!!.size
)
}
}.subscribe({
Timber.i("Loading grade summary result: Success")
view?.run {
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
showErrorView(false)
updateData(it)
Status.ERROR -> {
Timber.i("Loading grade summary result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
analytics.logEvent(
"load_data",
"type" to "grade_summary",
"items" to it.size,
"force_refresh" to forceRefresh
)
}) {
Timber.i("Loading grade summary result: An exception occurred")
errorHandler.dispatch(it)
})
}
}.afterLoading {
view?.run {
showRefresh(false)
showProgress(false)
enableSwipe(true)
notifyParentDataLoaded(semesterId)
}
}.launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {
@ -105,7 +117,7 @@ class GradeSummaryPresenter @Inject constructor(
showEmpty(false)
clearView()
}
disposable.clear()
cancelJobs("load")
}
private fun createGradeSummaryItems(items: List<GradeDetailsWithAverage>): List<GradeSummary> {

View File

@ -70,10 +70,6 @@ class HomeworkFragment : BaseFragment<FragmentHomeworkBinding>(R.layout.fragment
}
}
fun onReloadList() {
presenter.reloadData()
}
override fun clearData() {
with(homeworkAdapter) {
items = emptyList()

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.homework
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.repositories.homework.HomeworkRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
@ -8,13 +9,17 @@ import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
import io.github.wulkanowy.utils.isHolidays
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.nextOrSameSchoolDay
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.rx2.rxSingle
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDate.ofEpochDay
import timber.log.Timber
@ -79,64 +84,54 @@ class HomeworkPresenter @Inject constructor(
}
private fun setBaseDateOnHolidays() {
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { rxSingle { semesterRepository.getCurrentSemester(it) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
currentDate = baseDate
reloadNavigation()
}) {
Timber.i("Loading semester result: An exception occurred")
})
}
fun reloadData() {
loadData(currentDate, false)
flow {
val student = studentRepository.getCurrentStudent()
emit(semesterRepository.getCurrentSemester(student))
}.catch {
Timber.i("Loading semester result: An exception occurred")
}.onEach {
baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
currentDate = baseDate
reloadNavigation()
}.launch("holidays")
}
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
Timber.i("Loading homework data started")
currentDate = date
disposable.apply {
clear()
add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { student ->
rxSingle { semesterRepository.getCurrentSemester(student) }.flatMap { semester ->
rxSingle { homeworkRepository.getHomework(student, semester, currentDate, currentDate, forceRefresh) }
}
}
.map { createHomeworkItem(it) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}
.subscribe({
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
homeworkRepository.getHomework(student, semester, date, date, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading homework data started")
Status.SUCCESS -> {
Timber.i("Loading homework result: Success")
view?.apply {
updateData(it)
showEmpty(it.isEmpty())
updateData(createHomeworkItem(it.data!!))
showEmpty(it.data.isEmpty())
showErrorView(false)
showContent(it.isNotEmpty())
showContent(it.data.isNotEmpty())
}
analytics.logEvent(
"load_data",
"type" to "homework",
"items" to it.size,
"force_refresh" to forceRefresh
"items" to it.data!!.size
)
}) {
}
Status.ERROR -> {
Timber.i("Loading homework result: An exception occurred")
errorHandler.dispatch(it)
})
}
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}.launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {

View File

@ -73,7 +73,6 @@ class HomeworkDetailsDialog : BaseDialogFragment<DialogHomeworkBinding>(), Homew
}
override fun updateMarkAsDoneLabel(isDone: Boolean) {
(parentFragment as? HomeworkFragment)?.onReloadList()
binding.homeworkDialogRead.text = view?.context?.getString(if (isDone) R.string.homework_mark_as_undone else R.string.homework_mark_as_done)
}

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.homework.details
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.repositories.homework.HomeworkRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
@ -7,7 +8,8 @@ import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import kotlinx.coroutines.rx2.rxSingle
import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@ -26,20 +28,19 @@ class HomeworkDetailsPresenter @Inject constructor(
}
fun toggleDone(homework: Homework) {
Timber.i("Homework details update start")
disposable.add(rxSingle { homeworkRepository.toggleDone(homework) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
Timber.i("Homework details update: Success")
view?.run {
updateMarkAsDoneLabel(homework.isDone)
flowWithResource { homeworkRepository.toggleDone(homework) }.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Homework details update start")
Status.SUCCESS -> {
Timber.i("Homework details update: Success")
view?.updateMarkAsDoneLabel(homework.isDone)
analytics.logEvent("homework_mark_as_done")
}
Status.ERROR -> {
Timber.i("Homework details update result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
analytics.logEvent("homework_mark_as_done")
}) {
Timber.i("Homework details update result: An exception occurred")
errorHandler.dispatch(it)
}
)
}.launch("toggle")
}
}

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.login.advanced
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.sdk.Sdk
@ -7,9 +8,10 @@ import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank
import io.reactivex.Single
import kotlinx.coroutines.rx2.rxSingle
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@ -126,35 +128,42 @@ class LoginAdvancedPresenter @Inject constructor(
fun onSignInClick() {
if (!validateCredentials()) return
disposable.add(getStudentsAppropriatesToLoginType()
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnSubscribe {
view?.apply {
flowWithResource { getStudentsAppropriatesToLoginType() }.onEach {
when (it.status) {
Status.LOADING -> view?.run {
Timber.i("Login started")
hideSoftKeyboard()
showProgress(true)
showContent(false)
}
Timber.i("Login started")
}
.doFinally {
view?.apply {
showProgress(false)
showContent(true)
Status.SUCCESS -> {
Timber.i("Login result: Success")
analytics.logEvent("registration_form",
"success" to true,
"students" to it.data!!.size,
"error" to "No error"
)
view?.notifyParentAccountLogged(it.data)
}
Status.ERROR -> {
Timber.i("Login result: An exception occurred")
analytics.logEvent(
"registration_form",
"success" to false, "students" to -1,
"error" to it.error!!.message.ifNullOrBlank { "No message" }
)
loginErrorHandler.dispatch(it.error)
}
}
.subscribe({
Timber.i("Login result: Success")
analytics.logEvent("registration_form", "success" to true, "students" to it.size, "error" to "No error")
view?.notifyParentAccountLogged(it)
}, {
Timber.i("Login result: An exception occurred")
analytics.logEvent("registration_form", "success" to false, "students" to -1, "error" to it.message.ifNullOrBlank { "No message" })
loginErrorHandler.dispatch(it)
}))
}.afterLoading {
view?.apply {
showProgress(false)
showContent(true)
}
}.launch("login")
}
private fun getStudentsAppropriatesToLoginType(): Single<List<Student>> {
private suspend fun getStudentsAppropriatesToLoginType(): List<Student> {
val email = view?.formUsernameValue.orEmpty()
val password = view?.formPassValue.orEmpty()
val endpoint = view?.formHostValue.orEmpty()
@ -163,12 +172,10 @@ class LoginAdvancedPresenter @Inject constructor(
val symbol = view?.formSymbolValue.orEmpty()
val token = view?.formTokenValue.orEmpty()
return rxSingle {
when (Sdk.Mode.valueOf(view?.formLoginType ?: "")) {
Sdk.Mode.API -> studentRepository.getStudentsApi(pin, symbol, token)
Sdk.Mode.SCRAPPER -> studentRepository.getStudentsScrapper(email, password, endpoint, symbol)
Sdk.Mode.HYBRID -> studentRepository.getStudentsHybrid(email, password, endpoint, symbol)
}
return when (Sdk.Mode.valueOf(view?.formLoginType.orEmpty())) {
Sdk.Mode.API -> studentRepository.getStudentsApi(pin, symbol, token)
Sdk.Mode.SCRAPPER -> studentRepository.getStudentsScrapper(email, password, endpoint, symbol)
Sdk.Mode.HYBRID -> studentRepository.getStudentsHybrid(email, password, endpoint, symbol)
}
}

View File

@ -1,12 +1,15 @@
package io.github.wulkanowy.ui.modules.login.form
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank
import kotlinx.coroutines.rx2.rxSingle
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@ -75,34 +78,44 @@ class LoginFormPresenter @Inject constructor(
if (!validateCredentials(email, password, host)) return
disposable.add(rxSingle { studentRepository.getStudentsScrapper(email, password, host, symbol) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnSubscribe {
view?.apply {
flowWithResource { studentRepository.getStudentsScrapper(email, password, host, symbol) }.onEach {
when (it.status) {
Status.LOADING -> view?.run {
Timber.i("Login started")
hideSoftKeyboard()
showProgress(true)
showContent(false)
}
Timber.i("Login started")
}
.doFinally {
view?.apply {
showProgress(false)
showContent(true)
Status.SUCCESS -> {
Timber.i("Login result: Success")
analytics.logEvent(
"registration_form",
"success" to true,
"students" to it.data!!.size,
"scrapperBaseUrl" to host,
"error" to "No error"
)
view?.notifyParentAccountLogged(it.data, Triple(email, password, host))
}
Status.ERROR -> {
Timber.i("Login result: An exception occurred")
analytics.logEvent(
"registration_form",
"success" to false,
"students" to -1,
"scrapperBaseUrl" to host,
"error" to it.error!!.message.ifNullOrBlank { "No message" })
loginErrorHandler.dispatch(it.error)
lastError = it.error
view?.showContact(true)
}
}
.subscribe({
Timber.i("Login result: Success")
analytics.logEvent("registration_form", "success" to true, "students" to it.size, "scrapperBaseUrl" to host, "error" to "No error")
view?.notifyParentAccountLogged(it, Triple(email, password, host))
}, {
Timber.i("Login result: An exception occurred")
analytics.logEvent("registration_form", "success" to false, "students" to -1, "scrapperBaseUrl" to host, "error" to it.message.ifNullOrBlank { "No message" })
loginErrorHandler.dispatch(it)
lastError = it
view?.showContact(true)
}))
}.afterLoading {
view?.apply {
showProgress(false)
showContent(true)
}
}.launch("login")
}
fun onFaqClick() {

View File

@ -1,12 +1,15 @@
package io.github.wulkanowy.ui.modules.login.recover
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.recover.RecoverRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank
import kotlinx.coroutines.rx2.rxSingle
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@ -56,24 +59,22 @@ class LoginRecoverPresenter @Inject constructor(
if (!validateInput(username, host)) return
disposable.add(rxSingle { recoverRepository.getReCaptchaSiteKey(host, symbol.ifBlank { "Default" }) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnSubscribe {
view?.run {
flowWithResource { recoverRepository.getReCaptchaSiteKey(host, symbol.ifBlank { "Default" }) }.onEach {
when (it.status) {
Status.LOADING -> view?.run {
hideSoftKeyboard()
showRecoverForm(false)
showProgress(true)
showErrorView(false)
showCaptcha(false)
}
Status.SUCCESS -> view?.loadReCaptcha(siteKey = it.data!!.first, url = it.data.second)
Status.ERROR -> {
Timber.i("Obtain captcha site key result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
.subscribe({ (resetUrl, siteKey) ->
view?.loadReCaptcha(siteKey, resetUrl)
}) {
Timber.i("Obtain captcha site key result: An exception occurred")
errorHandler.dispatch(it)
})
}.launch("captcha")
}
private fun validateInput(username: String, host: String): Boolean {
@ -97,35 +98,28 @@ class LoginRecoverPresenter @Inject constructor(
val host = view?.recoverHostValue.orEmpty()
val symbol = view?.formHostSymbol.ifNullOrBlank { "Default" }
with(disposable) {
clear()
add(rxSingle { recoverRepository.sendRecoverRequest(host, symbol, username, reCaptchaResponse) }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnSubscribe {
view?.run {
showProgress(true)
showRecoverForm(false)
showCaptcha(false)
}
flowWithResource { recoverRepository.sendRecoverRequest(host, symbol, username, reCaptchaResponse) }.onEach {
when (it.status) {
Status.LOADING -> view?.run {
showProgress(true)
showRecoverForm(false)
showCaptcha(false)
}
.doFinally {
view?.showProgress(false)
}
.subscribe({
view?.run {
showSuccessView(true)
setSuccessTitle(it.substringBefore(". "))
setSuccessMessage(it.substringAfter(". "))
}
Status.SUCCESS -> view?.run {
showSuccessView(true)
setSuccessTitle(it.data!!.substringBefore(". "))
setSuccessMessage(it.data.substringAfter(". "))
analytics.logEvent("account_recover", "register" to host, "symbol" to symbol, "success" to true)
}) {
}
Status.ERROR -> {
Timber.i("Send recover request result: An exception occurred")
errorHandler.dispatch(it)
errorHandler.dispatch(it.error!!)
analytics.logEvent("account_recover", "register" to host, "symbol" to symbol, "success" to false)
})
}
}
}
}.afterLoading {
view?.showProgress(false)
}.launch("verified")
}
fun onDetailsClick() {

View File

@ -1,14 +1,15 @@
package io.github.wulkanowy.ui.modules.login.studentselect
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank
import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxSingle
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import java.io.Serializable
import javax.inject.Inject
@ -28,7 +29,7 @@ class LoginStudentSelectPresenter @Inject constructor(
fun onAttachView(view: LoginStudentSelectView, students: Serializable?) {
super.onAttachView(view)
view.run {
with(view) {
initView()
showContact(false)
enableSignIn(false)
@ -73,22 +74,20 @@ class LoginStudentSelectPresenter @Inject constructor(
private fun loadData(students: List<Student>) {
resetSelectedState()
this.students = students
disposable.add(rxSingle { studentRepository.getSavedStudents(false) }
.map { savedStudents ->
students.map { student ->
student to savedStudents.any { compareStudents(student, it) }
flowWithResource { studentRepository.getSavedStudents(false) }.onEach {
when (it.status) {
Status.LOADING -> Timber.d("Login student select students load started")
Status.SUCCESS -> view?.updateData(students.map { student ->
student to it.data!!.any { item -> compareStudents(student, item) }
})
Status.ERROR -> {
errorHandler.dispatch(it.error!!)
lastError = it.error
view?.updateData(students.map { student -> student to false })
}
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
view?.updateData(it)
}, {
errorHandler.dispatch(it)
lastError = it
view?.updateData(students.map { student -> student to false })
})
)
}.launch()
}
private fun resetSelectedState() {
@ -97,33 +96,35 @@ class LoginStudentSelectPresenter @Inject constructor(
}
private fun registerStudents(students: List<Student>) {
disposable.add(rxSingle { studentRepository.saveStudents(students) }
.map { students.first().apply { id = it.first() } }
.flatMapCompletable { rxCompletable { studentRepository.switchStudent(it) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnSubscribe {
view?.apply {
flowWithResource {
val savedStudents = studentRepository.saveStudents(students)
val firstRegistered = students.first().apply { id = savedStudents.first() }
studentRepository.switchStudent(firstRegistered)
}.onEach {
when (it.status) {
Status.LOADING -> view?.run {
Timber.i("Registration started")
showProgress(true)
showContent(false)
}
Timber.i("Registration started")
}
.subscribe({
students.forEach { analytics.logEvent("registration_student_select", "success" to true, "scrapperBaseUrl" to it.scrapperBaseUrl, "symbol" to it.symbol, "error" to "No error") }
Timber.i("Registration result: Success")
view?.openMainView()
}, { error ->
students.forEach { analytics.logEvent("registration_student_select", "success" to false, "scrapperBaseUrl" to it.scrapperBaseUrl, "symbol" to it.symbol, "error" to error.message.ifNullOrBlank { "No message" }) }
Timber.i("Registration result: An exception occurred ")
loginErrorHandler.dispatch(error)
lastError = error
view?.apply {
showProgress(false)
showContent(true)
showContact(true)
Status.SUCCESS -> {
Timber.i("Registration result: Success")
view?.openMainView()
logRegisterEvent(students)
}
}))
Status.ERROR -> {
Timber.i("Registration result: An exception occurred ")
view?.apply {
showProgress(false)
showContent(true)
showContact(true)
}
lastError = it.error
loginErrorHandler.dispatch(it.error!!)
logRegisterEvent(students, it.error)
}
}
}.launch("register")
}
fun onDiscordClick() {
@ -133,4 +134,15 @@ class LoginStudentSelectPresenter @Inject constructor(
fun onEmailClick() {
view?.openEmail(lastError?.message.ifNullOrBlank { "empty" })
}
private fun logRegisterEvent(students: List<Student>, error: Throwable? = null) {
students.forEach { student ->
analytics.logEvent(
"registration_student_select",
"success" to (error != null),
"scrapperBaseUrl" to student.scrapperBaseUrl,
"symbol" to student.symbol,
"error" to (error?.message?.ifBlank { "No message" } ?: "No error"))
}
}
}

View File

@ -1,13 +1,15 @@
package io.github.wulkanowy.ui.modules.login.symbol
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank
import io.reactivex.Single
import kotlinx.coroutines.rx2.rxSingle
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import java.io.Serializable
import javax.inject.Inject
@ -47,44 +49,55 @@ class LoginSymbolPresenter @Inject constructor(
return
}
disposable.add(
Single.fromCallable { loginData }
.flatMap { rxSingle { studentRepository.getStudentsScrapper(it.first, it.second, it.third, symbol) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnSubscribe {
view?.apply {
hideSoftKeyboard()
showProgress(true)
showContent(false)
}
flowWithResource { studentRepository.getStudentsScrapper(loginData!!.first, loginData!!.second, loginData!!.third, symbol) }.onEach {
when (it.status) {
Status.LOADING -> view?.run {
Timber.i("Login with symbol started")
hideSoftKeyboard()
showProgress(true)
showContent(false)
}
.doFinally {
view?.apply {
showProgress(false)
showContent(true)
}
}
.subscribe({
analytics.logEvent("registration_symbol", "success" to true, "students" to it.size, "scrapperBaseUrl" to loginData?.third, "symbol" to symbol, "error" to "No error")
view?.apply {
if (it.isEmpty()) {
Status.SUCCESS -> {
view?.run {
if (it.data!!.isEmpty()) {
Timber.i("Login with symbol result: Empty student list")
setErrorSymbolIncorrect()
view?.showContact(true)
} else {
Timber.i("Login with symbol result: Success")
notifyParentAccountLogged(it)
notifyParentAccountLogged(it.data)
}
}
}, {
analytics.logEvent(
"registration_symbol",
"success" to true,
"students" to it.data!!.size,
"scrapperBaseUrl" to loginData?.third,
"symbol" to symbol,
"error" to "No error"
)
}
Status.ERROR -> {
Timber.i("Login with symbol result: An exception occurred")
analytics.logEvent("registration_symbol", "success" to false, "students" to -1, "scrapperBaseUrl" to loginData?.third, "symbol" to symbol, "error" to it.message.ifNullOrBlank { "No message" })
loginErrorHandler.dispatch(it)
lastError = it
analytics.logEvent(
"registration_symbol",
"success" to false,
"students" to -1,
"scrapperBaseUrl" to loginData?.third,
"symbol" to symbol,
"error" to it.error!!.message.ifNullOrBlank { "No message" }
)
loginErrorHandler.dispatch(it.error)
lastError = it.error
view?.showContact(true)
}))
}
}
}.afterLoading {
view?.apply {
showProgress(false)
showContent(true)
}
}.launch("login")
}
fun onParentInitSymbolView(loginData: Triple<String, String, String>) {

View File

@ -1,13 +1,15 @@
package io.github.wulkanowy.ui.modules.luckynumber
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import kotlinx.coroutines.rx2.rxMaybe
import kotlinx.coroutines.rx2.rxSingle
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@ -34,47 +36,47 @@ class LuckyNumberPresenter @Inject constructor(
}
private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading lucky number started")
disposable.apply {
clear()
add(rxSingle { studentRepository.getCurrentStudent() }
.flatMapMaybe { rxMaybe { luckyNumberRepository.getLuckyNumber(it, forceRefresh) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
luckyNumberRepository.getLuckyNumber(student, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading lucky number started")
Status.SUCCESS -> {
if (it.data != null) {
Timber.i("Loading lucky number result: Success")
view?.apply {
updateData(it.data)
showContent(true)
showEmpty(false)
showErrorView(false)
}
analytics.logEvent(
"load_item",
"type" to "lucky_number",
"number" to it.data.luckyNumber
)
} else {
Timber.i("Loading lucky number result: No lucky number found")
view?.run {
showContent(false)
showEmpty(true)
showErrorView(false)
}
}
}
.subscribe({
Timber.i("Loading lucky number result: Success")
view?.apply {
updateData(it)
showContent(true)
showEmpty(false)
showErrorView(false)
}
analytics.logEvent(
"load_item",
"type" to "lucky_number",
"number" to it.luckyNumber,
"force_refresh" to forceRefresh
)
}, {
Status.ERROR -> {
Timber.i("Loading lucky number result: An exception occurred")
errorHandler.dispatch(it)
}, {
Timber.i("Loading lucky number result: No lucky number found")
view?.run {
showContent(false)
showEmpty(true)
showErrorView(false)
}
})
)
}
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}.launch()
}
private fun showErrorViewOnError(message: String, error: Throwable) {

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.luckynumberwidget
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.student.StudentRepository
@ -8,7 +9,9 @@ import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider.Companion.getStudentWidgetKey
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider.Companion.getThemeWidgetKey
import io.github.wulkanowy.utils.SchedulersProvider
import kotlinx.coroutines.rx2.rxSingle
import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
class LuckyNumberWidgetConfigurePresenter @Inject constructor(
@ -46,23 +49,25 @@ class LuckyNumberWidgetConfigurePresenter @Inject constructor(
}
private fun loadData() {
disposable.add(rxSingle { studentRepository.getSavedStudents(false) }
.map { it to appWidgetId?.let { id -> sharedPref.getLong(getStudentWidgetKey(id), 0) } }
.map { (students, currentStudentId) ->
students.map { student -> student to (student.id == currentStudentId) }
}
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.subscribe({
when {
it.isEmpty() -> view?.openLoginView()
it.size == 1 -> {
selectedStudent = it.single().first
view?.showThemeDialog()
flowWithResource { studentRepository.getSavedStudents(false) }.onEach {
when (it.status) {
Status.LOADING -> Timber.d("Lucky number widget configure students data load")
Status.SUCCESS -> {
val widgetId = appWidgetId?.let { id -> sharedPref.getLong(getStudentWidgetKey(id), 0) }
when {
it.data!!.isEmpty() -> view?.openLoginView()
it.data.size == 1 -> {
selectedStudent = it.data.single()
view?.showThemeDialog()
}
else -> view?.updateData(it.data.map { student ->
student to (student.id == widgetId)
})
}
else -> view?.updateData(it)
}
}, { errorHandler.dispatch(it) }))
Status.ERROR -> errorHandler.dispatch(it.error!!)
}
}.launch()
}
private fun registerStudent(student: Student?) {

View File

@ -15,6 +15,7 @@ import android.view.View.VISIBLE
import android.widget.RemoteViews
import dagger.android.AndroidInjection
import io.github.wulkanowy.R
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
@ -24,6 +25,8 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Maybe
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.rx2.rxMaybe
import kotlinx.coroutines.rx2.rxSingle
import timber.log.Timber
@ -157,7 +160,7 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
else -> Maybe.empty()
}
}
.flatMap { rxMaybe { luckyNumberRepository.getLuckyNumber(it) } }
.flatMap { rxMaybe { luckyNumberRepository.getLuckyNumber(it, false).takeWhile { it.status == Status.LOADING }.first().data } }
.subscribeOn(schedulers.backgroundThread)
.blockingGet()
} catch (e: Exception) {

View File

@ -5,7 +5,6 @@ import android.view.View
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.repositories.message.MessageFolder.RECEIVED
import io.github.wulkanowy.data.repositories.message.MessageFolder.SENT
import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED
@ -77,10 +76,6 @@ class MessageFragment : BaseFragment<FragmentMessageBinding>(R.layout.fragment_m
binding.messageProgress.visibility = if (show) VISIBLE else INVISIBLE
}
fun onDeleteMessage(message: Message) {
presenter.onDeleteMessage(message)
}
fun onChildFragmentLoaded() {
presenter.onChildViewLoaded()
}

View File

@ -1,13 +1,12 @@
package io.github.wulkanowy.ui.modules.message
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Completable
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import timber.log.Timber
import java.util.concurrent.TimeUnit.MILLISECONDS
import javax.inject.Inject
class MessagePresenter @Inject constructor(
@ -18,12 +17,12 @@ class MessagePresenter @Inject constructor(
override fun onAttachView(view: MessageView) {
super.onAttachView(view)
disposable.add(Completable.timer(150, MILLISECONDS, schedulers.mainThread)
.subscribe {
view.initView()
Timber.i("Message view was initialized")
loadData()
})
launch {
delay(150)
view.initView()
Timber.i("Message view was initialized")
loadData()
}
}
fun onPageSelected(index: Int) {
@ -46,15 +45,6 @@ class MessagePresenter @Inject constructor(
}
}
fun onDeleteMessage(message: Message) {
view?.notifyChildMessageDeleted(
when (message.removed) {
true -> 2
else -> message.folderId - 1
}
)
}
fun onSendMessageButtonClicked() {
view?.openSendMessage()
}

View File

@ -203,10 +203,6 @@ class MessagePreviewFragment :
(activity as MainActivity).popView()
}
override fun notifyParentMessageDeleted(message: Message) {
parentFragmentManager.fragments.forEach { if (it is MessageFragment) it.onDeleteMessage(message) }
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putSerializable(MESSAGE_ID_KEY, presenter.message)
super.onSaveInstanceState(outState)

View File

@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.message.preview
import android.annotation.SuppressLint
import android.os.Build
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageAttachment
import io.github.wulkanowy.data.repositories.message.MessageRepository
@ -11,8 +12,11 @@ import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.rx2.rxSingle
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@ -54,33 +58,35 @@ class MessagePreviewPresenter @Inject constructor(
}
private fun loadData(message: Message) {
Timber.i("Loading message ${message.messageId} preview started")
disposable.apply {
clear()
add(rxSingle { studentRepository.getStudentById(message.studentId) }
.flatMap { rxSingle { messageRepository.getMessage(it, message, true) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doFinally { view?.showProgress(false) }
.subscribe({ message ->
Timber.i("Loading message ${message.message.messageId} preview result: Success ")
this@MessagePreviewPresenter.message = message.message
this@MessagePreviewPresenter.attachments = message.attachments
flowWithResourceIn {
val student = studentRepository.getStudentById(message.studentId)
messageRepository.getMessage(student, message, true)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading message ${message.messageId} preview started")
Status.SUCCESS -> {
Timber.i("Loading message ${it.data!!.message.messageId} preview result: Success ")
this@MessagePreviewPresenter.message = it.data.message
this@MessagePreviewPresenter.attachments = it.data.attachments
view?.apply {
setMessageWithAttachment(message)
setMessageWithAttachment(it.data)
initOptions()
}
analytics.logEvent(
"load_item",
"type" to "message_preview",
"length" to message.message.content.length
"length" to it.data.message.content.length
)
}) {
}
Status.ERROR -> {
Timber.i("Loading message ${message.messageId} preview result: An exception occurred ")
retryCallback = { onMessageLoadRetry(message) }
errorHandler.dispatch(it)
})
}
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.showProgress(false)
}.launch()
}
fun onReply(): Boolean {
@ -152,34 +158,37 @@ class MessagePreviewPresenter @Inject constructor(
}
private fun deleteMessage() {
message?.let { message ->
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { rxSingle { messageRepository.deleteMessage(it, message) } }
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnSubscribe {
message ?: return
view?.run {
showContent(false)
showProgress(true)
showOptions(false)
showErrorView(false)
}
flowWithResource {
val student = studentRepository.getCurrentStudent()
messageRepository.deleteMessage(student, message!!)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.d("Message ${message?.id} delete started")
Status.SUCCESS -> {
Timber.d("Message ${message?.id} delete success")
view?.run {
showContent(false)
showProgress(true)
showOptions(false)
showErrorView(false)
}
}
.doFinally {
view?.showProgress(false)
}
.subscribe({
view?.run {
notifyParentMessageDeleted(message)
showMessage(deleteMessageSuccessString)
popView()
}
}, { error ->
}
Status.ERROR -> {
Timber.d("Message ${message?.id} delete failed")
retryCallback = { onMessageDelete() }
errorHandler.dispatch(error)
})
)
}
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.showProgress(false)
}.launch("delete")
}
private fun showErrorViewOnError(message: String, error: Throwable) {

Some files were not shown because too many files have changed in this diff Show More