1
0
mirror of https://github.com/wulkanowy/wulkanowy.git synced 2025-01-31 21:12:44 +01:00

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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
138 changed files with 2351 additions and 1892 deletions

View File

@ -92,6 +92,7 @@ android {
kotlinOptions { kotlinOptions {
jvmTarget = "1.8" jvmTarget = "1.8"
freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
} }
packagingOptions { packagingOptions {
@ -116,7 +117,7 @@ ext {
room = "2.2.5" room = "2.2.5"
dagger = "2.28.3" dagger = "2.28.3"
chucker = "3.2.0" chucker = "3.2.0"
mockk = "1.9.2" mockk = "1.10.0"
} }
configurations.all { configurations.all {
@ -129,6 +130,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.8'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-rx2:1.3.7' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-rx2:1.3.7'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7'
implementation "androidx.core:core-ktx:1.3.0" implementation "androidx.core:core-ktx:1.3.0"
implementation "androidx.activity:activity-ktx:1.1.0" implementation "androidx.activity:activity-ktx:1.1.0"
@ -156,7 +158,6 @@ dependencies {
implementation 'com.github.PaulinaSadowska:RxWorkManagerObservers:1.0.0' implementation 'com.github.PaulinaSadowska:RxWorkManagerObservers:1.0.0'
implementation "androidx.room:room-runtime:$room" implementation "androidx.room:room-runtime:$room"
implementation "androidx.room:room-rxjava2:$room"
implementation "androidx.room:room-ktx:$room" implementation "androidx.room:room-ktx:$room"
kapt "androidx.room:room-compiler:$room" kapt "androidx.room:room-compiler:$room"
@ -199,6 +200,7 @@ dependencies {
testImplementation "junit:junit:4.13" testImplementation "junit:junit:4.13"
testImplementation "io.mockk:mockk:$mockk" testImplementation "io.mockk:mockk:$mockk"
testImplementation "org.threeten:threetenbp:1.4.4" testImplementation "org.threeten:threetenbp:1.4.4"
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.7'
androidTestImplementation "androidx.test:core:1.2.0" androidTestImplementation "androidx.test:core:1.2.0"
androidTestImplementation "androidx.test:runner:1.2.0" androidTestImplementation "androidx.test:runner:1.2.0"

View File

@ -0,0 +1,11 @@
package io.github.wulkanowy.data
import io.github.wulkanowy.utils.DispatchersProvider
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
class TestDispatchersProvider : DispatchersProvider() {
override val backgroundThread: CoroutineDispatcher
get() = Dispatchers.Unconfined
}

View File

@ -1,6 +1,8 @@
package io.github.wulkanowy.data.repositories package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import org.threeten.bp.LocalDate.now
import org.threeten.bp.LocalDateTime import org.threeten.bp.LocalDateTime
fun getStudent(): Student { fun getStudent(): Student {
@ -27,3 +29,16 @@ fun getStudent(): Student {
isParent = false isParent = false
) )
} }
fun getSemester() = Semester(
semesterId = 1,
studentId = 1,
classId = 1,
diaryId = 2,
diaryName = "",
end = now(),
schoolYear = 2019,
semesterName = 1,
start = now(),
unitId = 1
)

View File

@ -6,6 +6,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -53,7 +54,7 @@ class AttendanceLocalTest {
runBlocking { attendanceLocal.saveAttendance(list) } runBlocking { attendanceLocal.saveAttendance(list) }
val semester = Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1) val semester = Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1)
val attendance = runBlocking { attendanceLocal.getAttendance(semester, of(2018, 9, 10), of(2018, 9, 14)) } val attendance = runBlocking { attendanceLocal.getAttendance(semester, of(2018, 9, 10), of(2018, 9, 14)).first() }
assertEquals(2, attendance.size) assertEquals(2, attendance.size)
assertEquals(attendance[0].date, of(2018, 9, 10)) assertEquals(attendance[0].date, of(2018, 9, 10))
assertEquals(attendance[1].date, of(2018, 9, 14)) assertEquals(attendance[1].date, of(2018, 9, 14))

View File

@ -6,6 +6,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.CompletedLesson import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -46,7 +47,7 @@ class CompletedLessonsLocalTest {
runBlocking { completedLessonsLocal.saveCompletedLessons(list) } runBlocking { completedLessonsLocal.saveCompletedLessons(list) }
val semester = Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1) val semester = Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1)
val completed = runBlocking { completedLessonsLocal.getCompletedLessons(semester, of(2018, 9, 10), of(2018, 9, 14)) } val completed = runBlocking { completedLessonsLocal.getCompletedLessons(semester, of(2018, 9, 10), of(2018, 9, 14)).first() }
assertEquals(2, completed.size) assertEquals(2, completed.size)
assertEquals(completed[0].date, of(2018, 9, 10)) assertEquals(completed[0].date, of(2018, 9, 10))
assertEquals(completed[1].date, of(2018, 9, 14)) assertEquals(completed[1].date, of(2018, 9, 14))

View File

@ -6,6 +6,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Exam import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -43,7 +44,7 @@ class ExamLocalTest {
runBlocking { examLocal.saveExams(list) } runBlocking { examLocal.saveExams(list) }
val semester = Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1) val semester = Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1)
val exams = runBlocking { examLocal.getExams(semester, of(2018, 9, 10), of(2018, 9, 14)) } val exams = runBlocking { examLocal.getExams(semester, of(2018, 9, 10), of(2018, 9, 14)).first() }
assertEquals(2, exams.size) assertEquals(2, exams.size)
assertEquals(exams[0].date, of(2018, 9, 10)) assertEquals(exams[0].date, of(2018, 9, 10))
assertEquals(exams[1].date, of(2018, 9, 14)) assertEquals(exams[1].date, of(2018, 9, 14))

View File

@ -5,6 +5,7 @@ import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -45,7 +46,7 @@ class GradeLocalTest {
val semester = Semester(1, 2, "", 2019, 2, 1, now(), now(), 1, 1) val semester = Semester(1, 2, "", 2019, 2, 1, now(), now(), 1, 1)
val grades = runBlocking { gradeLocal.getGradesDetails(semester) } val grades = runBlocking { gradeLocal.getGradesDetails(semester).first() }
assertEquals(2, grades.size) assertEquals(2, grades.size)
assertEquals(grades[0].date, LocalDate.of(2019, 2, 27)) assertEquals(grades[0].date, LocalDate.of(2019, 2, 27))

View File

@ -5,15 +5,18 @@ import androidx.room.Room
import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress import androidx.test.filters.SdkSuppress
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Grade
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.coEvery import io.mockk.coEvery
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -29,15 +32,12 @@ import kotlin.test.assertTrue
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class GradeRepositoryTest { class GradeRepositoryTest {
@MockK
private lateinit var mockSdk: Sdk
@MockK @MockK
private lateinit var semesterMock: Semester private lateinit var semesterMock: Semester
@MockK
private lateinit var studentMock: Student private lateinit var studentMock: Student
@MockK
private lateinit var gradeRemote: GradeRemote private lateinit var gradeRemote: GradeRemote
private lateinit var gradeLocal: GradeLocal private lateinit var gradeLocal: GradeLocal
@ -49,14 +49,12 @@ class GradeRepositoryTest {
MockKAnnotations.init(this) MockKAnnotations.init(this)
testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build() testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build()
gradeLocal = GradeLocal(testDb.gradeDao, testDb.gradeSummaryDao) gradeLocal = GradeLocal(testDb.gradeDao, testDb.gradeSummaryDao)
gradeRemote = GradeRemote(mockSdk) studentMock = getStudentMock()
every { studentMock.registrationDate } returns LocalDateTime.of(2019, 2, 27, 12, 0)
every { semesterMock.studentId } returns 1 every { semesterMock.studentId } returns 1
every { semesterMock.diaryId } returns 1 every { semesterMock.diaryId } returns 1
every { semesterMock.schoolYear } returns 2019 every { semesterMock.schoolYear } returns 2019
every { semesterMock.semesterId } returns 1 every { semesterMock.semesterId } returns 1
every { mockSdk.switchDiary(any(), any()) } returns mockSdk
} }
@After @After
@ -66,16 +64,17 @@ class GradeRepositoryTest {
@Test @Test
fun markOlderThanRegisterDateAsRead() { fun markOlderThanRegisterDateAsRead() {
coEvery { mockSdk.getGrades(1) } returns (listOf( coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf(
createGradeApi(5, 4.0, of(2019, 2, 25), "Ocena pojawiła się"), createGradeLocal(5, 4.0, of(2019, 2, 25), "Ocena pojawiła się"),
createGradeApi(5, 4.0, of(2019, 2, 26), "przed zalogowanie w aplikacji"), createGradeLocal(5, 4.0, of(2019, 2, 26), "przed zalogowanie w aplikacji"),
createGradeApi(5, 4.0, of(2019, 2, 27), "Ocena z dnia logowania"), createGradeLocal(5, 4.0, of(2019, 2, 27), "Ocena z dnia logowania"),
createGradeApi(5, 4.0, of(2019, 2, 28), "Ocena jeszcze nowsza") createGradeLocal(5, 4.0, of(2019, 2, 28), "Ocena jeszcze nowsza")
) to emptyList()) ) to emptyList())
val grades = runBlocking { val grades = runBlocking {
GradeRepository(gradeLocal, gradeRemote).getGrades(studentMock, semesterMock, true) GradeRepository(gradeLocal, gradeRemote)
.first.sortedByDescending { it.date } .getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!.first.sortedByDescending { it.date }
} }
assertFalse { grades[0].isRead } assertFalse { grades[0].isRead }
@ -93,16 +92,17 @@ class GradeRepositoryTest {
) )
runBlocking { gradeLocal.saveGrades(list) } runBlocking { gradeLocal.saveGrades(list) }
coEvery { mockSdk.getGrades(1) } returns (listOf( coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf(
createGradeApi(5, 2.0, of(2019, 2, 25), "Ocena ma datę, jest inna, ale nie zostanie powiadomiona"), createGradeLocal(5, 2.0, of(2019, 2, 25), "Ocena ma datę, jest inna, ale nie zostanie powiadomiona"),
createGradeApi(4, 3.0, of(2019, 2, 26), "starszą niż ostatnia lokalnie"), createGradeLocal(4, 3.0, of(2019, 2, 26), "starszą niż ostatnia lokalnie"),
createGradeApi(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"), createGradeLocal(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"),
createGradeApi(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa") createGradeLocal(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa")
) to emptyList()) ) to emptyList())
val grades = runBlocking { val grades = runBlocking {
GradeRepository(gradeLocal, gradeRemote).getGrades(studentMock, semesterMock, true) GradeRepository(gradeLocal, gradeRemote)
.first.sortedByDescending { it.date } .getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!.first.sortedByDescending { it.date }
} }
assertFalse { grades[0].isRead } assertFalse { grades[0].isRead }
@ -120,13 +120,15 @@ class GradeRepositoryTest {
) )
runBlocking { gradeLocal.saveGrades(list) } runBlocking { gradeLocal.saveGrades(list) }
coEvery { mockSdk.getGrades(1) } returns (listOf( coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
) to emptyList()) ) to emptyList())
val grades = runBlocking { val grades = runBlocking {
GradeRepository(gradeLocal, gradeRemote).getGrades(studentMock, semesterMock, true) GradeRepository(gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!
} }
assertEquals(2, grades.first.size) assertEquals(2, grades.first.size)
@ -140,14 +142,16 @@ class GradeRepositoryTest {
) )
runBlocking { gradeLocal.saveGrades(list) } runBlocking { gradeLocal.saveGrades(list) }
coEvery { mockSdk.getGrades(1) } returns (listOf( coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
) to emptyList()) ) to emptyList())
val grades = runBlocking { val grades = runBlocking {
GradeRepository(gradeLocal, gradeRemote).getGrades(studentMock, semesterMock, true) GradeRepository(gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!
} }
assertEquals(3, grades.first.size) assertEquals(3, grades.first.size)
@ -157,14 +161,16 @@ class GradeRepositoryTest {
fun emptyLocal() { fun emptyLocal() {
runBlocking { gradeLocal.saveGrades(listOf()) } runBlocking { gradeLocal.saveGrades(listOf()) }
coEvery { mockSdk.getGrades(1) } returns (listOf( coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
) to emptyList()) ) to emptyList())
val grades = runBlocking { val grades = runBlocking {
GradeRepository(gradeLocal, gradeRemote).getGrades(studentMock, semesterMock, true) GradeRepository(gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!
} }
assertEquals(3, grades.first.size) assertEquals(3, grades.first.size)
@ -178,12 +184,37 @@ class GradeRepositoryTest {
) )
runBlocking { gradeLocal.saveGrades(list) } runBlocking { gradeLocal.saveGrades(list) }
coEvery { mockSdk.getGrades(1) } returns (emptyList<Grade>() to emptyList()) coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (emptyList<Grade>() to emptyList())
val grades = runBlocking { val grades = runBlocking {
GradeRepository(gradeLocal, gradeRemote).getGrades(studentMock, semesterMock, true) GradeRepository(gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!
} }
assertEquals(0, grades.first.size) assertEquals(0, grades.first.size)
} }
private fun getStudentMock() = Student(
scrapperBaseUrl = "http://fakelog.cf",
email = "jan@fakelog.cf",
certificateKey = "",
classId = 0,
className = "",
isCurrent = false,
isParent = false,
loginMode = Sdk.Mode.SCRAPPER.name,
loginType = "STANDARD",
mobileBaseUrl = "",
password = "",
privateKey = "",
registrationDate = LocalDateTime.of(2019, 2, 27, 12, 0),
schoolName = "",
schoolShortName = "test",
schoolSymbol = "",
studentId = 0,
studentName = "",
symbol = "",
userLoginId = 0
)
} }

View File

@ -7,6 +7,7 @@ import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.GradePointsStatistics import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -42,7 +43,7 @@ class GradeStatisticsLocalTest {
) )
runBlocking { gradeStatisticsLocal.saveGradesStatistics(list) } runBlocking { gradeStatisticsLocal.saveGradesStatistics(list) }
val stats = runBlocking { gradeStatisticsLocal.getGradesStatistics(getSemester(), false, "Matematyka") } val stats = runBlocking { gradeStatisticsLocal.getGradesStatistics(getSemester(), false).first() }
assertEquals(1, stats.size) assertEquals(1, stats.size)
assertEquals(stats[0].subject, "Matematyka") assertEquals(stats[0].subject, "Matematyka")
} }
@ -56,11 +57,12 @@ class GradeStatisticsLocalTest {
) )
runBlocking { gradeStatisticsLocal.saveGradesStatistics(list) } runBlocking { gradeStatisticsLocal.saveGradesStatistics(list) }
val stats = runBlocking { gradeStatisticsLocal.getGradesStatistics(getSemester(), false, "Wszystkie") } val stats = runBlocking { gradeStatisticsLocal.getGradesStatistics(getSemester(), false).first() }
assertEquals(3, stats.size) assertEquals(2, stats.size)
assertEquals(stats[0].subject, "Wszystkie") // assertEquals(3, stats.size)
assertEquals(stats[1].subject, "Matematyka") // assertEquals(stats[0].subject, "Wszystkie") // now in main repo
assertEquals(stats[2].subject, "Chemia") assertEquals(stats[0].subject, "Matematyka")
assertEquals(stats[1].subject, "Chemia")
} }
@Test @Test
@ -72,7 +74,7 @@ class GradeStatisticsLocalTest {
) )
runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(list) } runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(list) }
val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Matematyka") } val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() }
with(stats[0]) { with(stats[0]) {
assertEquals(subject, "Matematyka") assertEquals(subject, "Matematyka")
assertEquals(others, 5.0) assertEquals(others, 5.0)
@ -84,7 +86,7 @@ class GradeStatisticsLocalTest {
fun saveAndRead_subjectEmpty() { fun saveAndRead_subjectEmpty() {
runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) } runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) }
val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Matematyka") } val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() }
assertEquals(emptyList(), stats) assertEquals(emptyList(), stats)
} }
@ -92,7 +94,7 @@ class GradeStatisticsLocalTest {
fun saveAndRead_allEmpty() { fun saveAndRead_allEmpty() {
runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) } runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) }
val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Wszystkie") } val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() }
assertEquals(emptyList(), stats) assertEquals(emptyList(), stats)
} }

View File

@ -6,6 +6,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -41,7 +42,7 @@ class LuckyNumberLocalTest {
runBlocking { luckyNumberLocal.saveLuckyNumber(number) } runBlocking { luckyNumberLocal.saveLuckyNumber(number) }
val student = Student("", "", "", "", "", "", false, "", "", "", 1, 1, "", "", "", "", "", 1, false, now()) val student = Student("", "", "", "", "", "", false, "", "", "", 1, 1, "", "", "", "", "", 1, false, now())
val luckyNumber = runBlocking { luckyNumberLocal.getLuckyNumber(student, LocalDate.of(2019, 1, 20)) } val luckyNumber = runBlocking { luckyNumberLocal.getLuckyNumber(student, LocalDate.of(2019, 1, 20)).first() }
assertEquals(1, luckyNumber?.studentId) assertEquals(1, luckyNumber?.studentId)
assertEquals(LocalDate.of(2019, 1, 20), luckyNumber?.date) assertEquals(LocalDate.of(2019, 1, 20), luckyNumber?.date)

View File

@ -4,6 +4,7 @@ import android.content.Context
import androidx.room.Room import androidx.room.Room
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.TestDispatchersProvider
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.repositories.getStudent import io.github.wulkanowy.data.repositories.getStudent
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@ -27,7 +28,7 @@ class StudentLocalTest {
val context = ApplicationProvider.getApplicationContext<Context>() val context = ApplicationProvider.getApplicationContext<Context>()
testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java) testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
.build() .build()
studentLocal = StudentLocal(testDb.studentDao, context) studentLocal = StudentLocal(testDb.studentDao, TestDispatchersProvider(), context)
} }
@After @After

View File

@ -5,6 +5,7 @@ import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -48,7 +49,7 @@ class TimetableLocalTest {
semester = semester, semester = semester,
startDate = LocalDate.of(2018, 9, 10), startDate = LocalDate.of(2018, 9, 10),
endDate = LocalDate.of(2018, 9, 14) endDate = LocalDate.of(2018, 9, 14)
) ).first()
} }
assertEquals(2, exams.size) assertEquals(2, exams.size)

View File

@ -5,17 +5,16 @@ import androidx.room.Room
import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress import androidx.test.filters.SdkSuppress
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.repositories.getSemester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.getStudent import io.github.wulkanowy.data.repositories.getStudent
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.coEvery import io.mockk.coEvery
import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
import io.mockk.mockk import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -29,45 +28,25 @@ import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class TimetableRepositoryTest { class TimetableRepositoryTest {
@MockK @MockK(relaxed = true)
private lateinit var mockSdk: Sdk
@MockK
private lateinit var studentMock: Student
private val student = getStudent()
@MockK
private lateinit var semesterMock: Semester
@MockK
private lateinit var timetableNotificationSchedulerHelper: TimetableNotificationSchedulerHelper private lateinit var timetableNotificationSchedulerHelper: TimetableNotificationSchedulerHelper
@MockK
private lateinit var timetableRemote: TimetableRemote private lateinit var timetableRemote: TimetableRemote
private lateinit var timetableLocal: TimetableLocal private lateinit var timetableLocal: TimetableLocal
private lateinit var testDb: AppDatabase private lateinit var testDb: AppDatabase
private val student = getStudent()
private val semester = getSemester()
@Before @Before
fun initApi() { fun initApi() {
MockKAnnotations.init(this) MockKAnnotations.init(this)
testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build() testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build()
timetableLocal = TimetableLocal(testDb.timetableDao) timetableLocal = TimetableLocal(testDb.timetableDao)
timetableRemote = TimetableRemote(mockSdk)
every { timetableNotificationSchedulerHelper.scheduleNotifications(any(), any()) } returns mockk()
every { timetableNotificationSchedulerHelper.cancelScheduled(any(), any()) } returns mockk()
every { studentMock.studentId } returns 1
every { studentMock.studentName } returns "Jan Kowalski"
every { semesterMock.studentId } returns 1
every { semesterMock.diaryId } returns 2
every { semesterMock.schoolYear } returns 2019
every { semesterMock.semesterId } returns 1
every { mockSdk.switchDiary(any(), any()) } returns mockSdk
} }
@After @After
@ -86,21 +65,21 @@ class TimetableRepositoryTest {
)) ))
} }
coEvery { mockSdk.getTimetable(any(), any()) } returns listOf( coEvery { timetableRemote.getTimetable(student, semester, any(), any()) } returns listOf(
createTimetableRemote(of(2019, 3, 5, 8, 0), 1, "", "Przyroda"), createTimetableLocal(of(2019, 3, 5, 8, 0), 1, "", "Przyroda"),
createTimetableRemote(of(2019, 3, 5, 8, 50), 2, "", "Religia"), createTimetableLocal(of(2019, 3, 5, 8, 50), 2, "", "Religia"),
createTimetableRemote(of(2019, 3, 5, 9, 40), 3, "", "W-F"), createTimetableLocal(of(2019, 3, 5, 9, 40), 3, "", "W-F"),
createTimetableRemote(of(2019, 3, 5, 10, 30), 4, "", "W-F") createTimetableLocal(of(2019, 3, 5, 10, 30), 4, "", "W-F")
) )
val lessons = runBlocking { val lessons = runBlocking {
TimetableRepository(timetableLocal, timetableRemote, timetableNotificationSchedulerHelper).getTimetable( TimetableRepository(timetableLocal, timetableRemote, timetableNotificationSchedulerHelper).getTimetable(
student = student, student = student,
semester = semesterMock, semester = semester,
start = LocalDate.of(2019, 3, 5), start = LocalDate.of(2019, 3, 5),
end = LocalDate.of(2019, 3, 5), end = LocalDate.of(2019, 3, 5),
forceRefresh = true forceRefresh = true
) ).filter { it.status == Status.SUCCESS }.first().data.orEmpty()
} }
assertEquals(4, lessons.size) assertEquals(4, lessons.size)
@ -129,31 +108,31 @@ class TimetableRepositoryTest {
) )
runBlocking { timetableLocal.saveTimetable(list) } runBlocking { timetableLocal.saveTimetable(list) }
coEvery { mockSdk.getTimetable(any(), any()) } returns listOf( coEvery { timetableRemote.getTimetable(student, semester, any(), any()) } returns listOf(
createTimetableRemote(of(2019, 12, 23, 8, 0), 1, "123", "Matematyka", "Paweł Poniedziałkowski", false), createTimetableLocal(of(2019, 12, 23, 8, 0), 1, "123", "Matematyka", "Paweł Poniedziałkowski", false),
createTimetableRemote(of(2019, 12, 23, 8, 50), 2, "124", "Matematyka", "Jakub Wtorkowski", true), createTimetableLocal(of(2019, 12, 23, 8, 50), 2, "124", "Matematyka", "Jakub Wtorkowski", true),
createTimetableRemote(of(2019, 12, 23, 9, 40), 3, "125", "Język polski", "Joanna Poniedziałkowska", false), createTimetableLocal(of(2019, 12, 23, 9, 40), 3, "125", "Język polski", "Joanna Poniedziałkowska", false),
createTimetableRemote(of(2019, 12, 23, 10, 40), 4, "126", "Język polski", "Joanna Wtorkowska", true), createTimetableLocal(of(2019, 12, 23, 10, 40), 4, "126", "Język polski", "Joanna Wtorkowska", true),
createTimetableRemote(of(2019, 12, 24, 8, 0), 1, "123", "Język polski", "", false), createTimetableLocal(of(2019, 12, 24, 8, 0), 1, "123", "Język polski", "", false),
createTimetableRemote(of(2019, 12, 24, 8, 50), 2, "124", "Język polski", "", true), createTimetableLocal(of(2019, 12, 24, 8, 50), 2, "124", "Język polski", "", true),
createTimetableRemote(of(2019, 12, 24, 9, 40), 3, "125", "Język polski", "", false), createTimetableLocal(of(2019, 12, 24, 9, 40), 3, "125", "Język polski", "", false),
createTimetableRemote(of(2019, 12, 24, 10, 40), 4, "126", "Język polski", "", true), createTimetableLocal(of(2019, 12, 24, 10, 40), 4, "126", "Język polski", "", true),
createTimetableRemote(of(2019, 12, 25, 8, 0), 1, "123", "Matematyka", "Paweł Środowski", false), createTimetableLocal(of(2019, 12, 25, 8, 0), 1, "123", "Matematyka", "Paweł Środowski", false),
createTimetableRemote(of(2019, 12, 25, 8, 50), 2, "124", "Matematyka", "Paweł Czwartkowski", true), createTimetableLocal(of(2019, 12, 25, 8, 50), 2, "124", "Matematyka", "Paweł Czwartkowski", true),
createTimetableRemote(of(2019, 12, 25, 9, 40), 3, "125", "Matematyka", "Paweł Środowski", false), createTimetableLocal(of(2019, 12, 25, 9, 40), 3, "125", "Matematyka", "Paweł Środowski", false),
createTimetableRemote(of(2019, 12, 25, 10, 40), 4, "126", "Matematyka", "Paweł Czwartkowski", true) createTimetableLocal(of(2019, 12, 25, 10, 40), 4, "126", "Matematyka", "Paweł Czwartkowski", true)
) )
val lessons = runBlocking { val lessons = runBlocking {
TimetableRepository(timetableLocal, timetableRemote, timetableNotificationSchedulerHelper).getTimetable( TimetableRepository(timetableLocal, timetableRemote, timetableNotificationSchedulerHelper).getTimetable(
student = student, student = student,
semester = semesterMock, semester = semester,
start = LocalDate.of(2019, 12, 23), start = LocalDate.of(2019, 12, 23),
end = LocalDate.of(2019, 12, 25), end = LocalDate.of(2019, 12, 25),
forceRefresh = true forceRefresh = true
) ).filter { it.status == Status.SUCCESS }.first().data.orEmpty()
} }
assertEquals(12, lessons.size) assertEquals(12, lessons.size)

View File

@ -18,8 +18,7 @@
android:supportsRtl="false" android:supportsRtl="false"
android:theme="@style/WulkanowyTheme" android:theme="@style/WulkanowyTheme"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute" tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
tools:replace="android:supportsRtl,android:allowBackup">
<activity <activity
android:name=".ui.modules.splash.SplashActivity" android:name=".ui.modules.splash.SplashActivity"
android:screenOrientation="portrait" 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.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Attendance
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Singleton import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface AttendanceDao : BaseDao<Attendance> { interface AttendanceDao : BaseDao<Attendance> {
@Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") @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.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.AttendanceSummary import io.github.wulkanowy.data.db.entities.AttendanceSummary
import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface AttendanceSummaryDao : BaseDao<AttendanceSummary> { interface AttendanceSummaryDao : BaseDao<AttendanceSummary> {
@Query("SELECT * FROM AttendanceSummary WHERE diary_id = :diaryId AND student_id = :studentId AND subject_id = :subjectId") @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.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.CompletedLesson import io.github.wulkanowy.data.db.entities.CompletedLesson
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Singleton import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface CompletedLessonsDao : BaseDao<CompletedLesson> { interface CompletedLessonsDao : BaseDao<CompletedLesson> {
@Query("SELECT * FROM CompletedLesson WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") @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.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Exam import io.github.wulkanowy.data.db.entities.Exam
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Singleton import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface ExamDao : BaseDao<Exam> { interface ExamDao : BaseDao<Exam> {
@Query("SELECT * FROM Exams WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") @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.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.Grade
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -10,5 +11,5 @@ import javax.inject.Singleton
interface GradeDao : BaseDao<Grade> { interface GradeDao : BaseDao<Grade> {
@Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId") @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.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradePointsStatistics import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -10,8 +11,8 @@ import javax.inject.Singleton
interface GradePointsStatisticsDao : BaseDao<GradePointsStatistics> { interface GradePointsStatisticsDao : BaseDao<GradePointsStatistics> {
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName") @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") @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.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradeStatistics import io.github.wulkanowy.data.db.entities.GradeStatistics
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -10,8 +11,8 @@ import javax.inject.Singleton
interface GradeStatisticsDao : BaseDao<GradeStatistics> { interface GradeStatisticsDao : BaseDao<GradeStatistics> {
@Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName AND is_semester = :isSemester") @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") @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.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.GradeSummary
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -10,5 +11,5 @@ import javax.inject.Singleton
interface GradeSummaryDao : BaseDao<GradeSummary> { interface GradeSummaryDao : BaseDao<GradeSummary> {
@Query("SELECT * FROM GradesSummary WHERE student_id = :studentId AND semester_id = :semesterId") @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.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.data.db.entities.Homework
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Singleton import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface HomeworkDao : BaseDao<Homework> { interface HomeworkDao : BaseDao<Homework> {
@Query("SELECT * FROM Homework WHERE semester_id = :semesterId AND student_id = :studentId AND date >= :from AND date <= :end") @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.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.LuckyNumber
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Singleton import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface LuckyNumberDao : BaseDao<LuckyNumber> { interface LuckyNumberDao : BaseDao<LuckyNumber> {
@Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date") @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 androidx.room.Transaction
import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageWithAttachment import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface MessagesDao : BaseDao<Message> { interface MessagesDao : BaseDao<Message> {
@Transaction @Transaction
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND message_id = :messageId") @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") @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") @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.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.MobileDevice import io.github.wulkanowy.data.db.entities.MobileDevice
import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface MobileDeviceDao : BaseDao<MobileDevice> { interface MobileDeviceDao : BaseDao<MobileDevice> {
@Query("SELECT * FROM MobileDevices WHERE student_id = :studentId ORDER BY date DESC") @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.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Note import io.github.wulkanowy.data.db.entities.Note
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -10,5 +11,5 @@ import javax.inject.Singleton
interface NoteDao : BaseDao<Note> { interface NoteDao : BaseDao<Note> {
@Query("SELECT * FROM Notes WHERE student_id = :studentId") @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.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.School import io.github.wulkanowy.data.db.entities.School
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -10,5 +11,5 @@ import javax.inject.Singleton
interface SchoolDao : BaseDao<School> { interface SchoolDao : BaseDao<School> {
@Query("SELECT * FROM School WHERE student_id = :studentId AND class_id = :classId") @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.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Subject import io.github.wulkanowy.data.db.entities.Subject
import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface SubjectDao : BaseDao<Subject> { interface SubjectDao : BaseDao<Subject> {
@Query("SELECT * FROM Subjects WHERE diary_id = :diaryId AND student_id = :studentId") @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.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Teacher import io.github.wulkanowy.data.db.entities.Teacher
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -10,5 +11,5 @@ import javax.inject.Singleton
interface TeacherDao : BaseDao<Teacher> { interface TeacherDao : BaseDao<Teacher> {
@Query("SELECT * FROM Teachers WHERE student_id = :studentId AND class_id = :classId") @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.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.Timetable
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Singleton import javax.inject.Singleton
@ -11,5 +12,5 @@ import javax.inject.Singleton
interface TimetableDao : BaseDao<Timetable> { interface TimetableDao : BaseDao<Timetable> {
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") @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 private val dispatchers: DispatchersProvider
) { ) {
suspend fun getAppCreators(): List<Contributor> { suspend fun getAppCreators() = withContext(dispatchers.backgroundThread) {
return withContext(dispatchers.backgroundThread) {
Gson().fromJson( Gson().fromJson(
assets.open("contributors.json").bufferedReader().use { it.readText() }, assets.open("contributors.json").bufferedReader().use { it.readText() },
Array<Contributor>::class.java Array<Contributor>::class.java
).toList() ).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.dao.AttendanceDao
import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -18,7 +19,7 @@ class AttendanceLocal @Inject constructor(private val attendanceDb: AttendanceDa
attendanceDb.deleteAll(attendance) 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) 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.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.sunday import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
@ -16,19 +17,18 @@ class AttendanceRepository @Inject constructor(
private val remote: AttendanceRemote private val remote: AttendanceRemote
) { ) {
suspend fun getAttendance(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean): List<Attendance> { fun getAttendance(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
return local.getAttendance(semester, start.monday, end.sunday).filter { !forceRefresh }.ifEmpty { shouldFetch = { it.isEmpty() || forceRefresh },
val new = remote.getAttendance(student, semester, start.monday, end.sunday) query = { local.getAttendance(semester, start.monday, end.sunday) },
val old = 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)) suspend fun excuseForAbsence(student: Student, semester: Semester, attendanceList: List<Attendance>, reason: String? = null) {
local.saveAttendance(new.uniqueSubtract(old)) remote.excuseAbsence(student, semester, attendanceList, reason)
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)
} }
} }

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.dao.AttendanceSummaryDao
import io.github.wulkanowy.data.db.entities.AttendanceSummary import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -17,7 +18,7 @@ class AttendanceSummaryLocal @Inject constructor(private val attendanceDb: Atten
attendanceDb.deleteAll(attendance) 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) return attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId)
} }
} }

View File

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

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.dao.CompletedLessonsDao
import io.github.wulkanowy.data.db.entities.CompletedLesson import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -18,7 +19,7 @@ class CompletedLessonsLocal @Inject constructor(private val completedLessonsDb:
completedLessonsDb.deleteAll(completedLessons) 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) return completedLessonsDb.loadAll(semester.diaryId, semester.studentId, start, end)
} }
} }

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.data.repositories.completedlessons 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.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.sunday import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
@ -16,15 +16,14 @@ class CompletedLessonsRepository @Inject constructor(
private val remote: CompletedLessonsRemote private val remote: CompletedLessonsRemote
) { ) {
suspend fun getCompletedLessons(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): List<CompletedLesson> { fun getCompletedLessons(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
return local.getCompletedLessons(semester, start.monday, end.sunday).filter { !forceRefresh }.ifEmpty { shouldFetch = { it.isEmpty() || forceRefresh },
val new = remote.getCompletedLessons(student, semester, start.monday, end.sunday) query = { local.getCompletedLessons(semester, start.monday, end.sunday) },
val old = 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.deleteCompleteLessons(old uniqueSubtract new)
local.saveCompletedLessons(new.uniqueSubtract(old)) local.saveCompletedLessons(new uniqueSubtract old)
},
local.getCompletedLessons(semester, start.monday, end.sunday) filterResult = { it.filter { item -> item.date in start..end } }
}.filter { it.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.dao.ExamDao
import io.github.wulkanowy.data.db.entities.Exam import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -10,7 +11,7 @@ import javax.inject.Singleton
@Singleton @Singleton
class ExamLocal @Inject constructor(private val examDb: ExamDao) { 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) return examDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate)
} }

View File

@ -1,9 +1,9 @@
package io.github.wulkanowy.data.repositories.exam 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.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.sunday import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
@ -16,15 +16,14 @@ class ExamRepository @Inject constructor(
private val remote: ExamRemote private val remote: ExamRemote
) { ) {
suspend fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): List<Exam> { fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
return local.getExams(semester, start.monday, end.sunday).filter { !forceRefresh }.ifEmpty { shouldFetch = { it.isEmpty() || forceRefresh },
val new = remote.getExams(student, semester, start.monday, end.sunday) query = { local.getExams(semester, start.monday, end.sunday) },
val old = 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.deleteExams(old uniqueSubtract new)
local.saveExams(new.uniqueSubtract(old)) local.saveExams(new uniqueSubtract old)
},
local.getExams(semester, start.monday, end.sunday) filterResult = { it.filter { item -> item.date in start..end } }
}.filter { it.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.Grade
import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -30,7 +31,7 @@ class GradeLocal @Inject constructor(
gradeSummaryDb.updateAll(gradesSummary) gradeSummaryDb.updateAll(gradesSummary)
} }
suspend fun getGradesDetails(semester: Semester): List<Grade> { fun getGradesDetails(semester: Semester): Flow<List<Grade>> {
return gradeDb.loadAll(semester.semesterId, semester.studentId) return gradeDb.loadAll(semester.semesterId, semester.studentId)
} }
@ -42,7 +43,7 @@ class GradeLocal @Inject constructor(
gradeSummaryDb.deleteAll(gradesSummary) gradeSummaryDb.deleteAll(gradesSummary)
} }
suspend fun getGradesSummary(semester: Semester): List<GradeSummary> { fun getGradesSummary(semester: Semester): Flow<List<GradeSummary>> {
return gradeSummaryDb.loadAll(semester.semesterId, semester.studentId) 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.GradeSummary
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract 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 org.threeten.bp.LocalDateTime
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -15,30 +19,30 @@ class GradeRepository @Inject constructor(
private val remote: GradeRemote private val remote: GradeRemote
) { ) {
suspend fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): Pair<List<Grade>, List<GradeSummary>> { fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
val details = local.getGradesDetails(semester) shouldFetch = { (details, summaries) -> details.isEmpty() || summaries.isEmpty() || forceRefresh },
val summaries = local.getGradesSummary(semester) query = { local.getGradesDetails(semester).combine(local.getGradesSummary(semester)) { details, summaries -> details to summaries } },
fetch = { remote.getGrades(student, semester) },
if ((details.isNotEmpty() || summaries.isNotEmpty()) && !forceRefresh) { saveFetchResult = { old, new ->
return details to summaries refreshGradeDetails(student, old.first, new.first, notify)
refreshGradeSummaries(old.second, new.second, notify)
} }
)
val (newDetails, newSummary) = remote.getGrades(student, semester) private suspend fun refreshGradeDetails(student: Student, oldGrades: List<Grade>, newDetails: List<Grade>, notify: Boolean) {
val oldGrades = local.getGradesDetails(semester)
val notifyBreakDate = oldGrades.maxBy { it.date }?.date ?: student.registrationDate.toLocalDate() val notifyBreakDate = oldGrades.maxBy { it.date }?.date ?: student.registrationDate.toLocalDate()
local.deleteGrades(oldGrades.uniqueSubtract(newDetails)) local.deleteGrades(oldGrades uniqueSubtract newDetails)
local.saveGrades(newDetails.uniqueSubtract(oldGrades).onEach { local.saveGrades((newDetails uniqueSubtract oldGrades).onEach {
if (it.date >= notifyBreakDate) it.apply { if (it.date >= notifyBreakDate) it.apply {
isRead = false isRead = false
if (notify) isNotified = false if (notify) isNotified = false
} }
}) })
}
val oldSummaries = local.getGradesSummary(semester) private suspend fun refreshGradeSummaries(oldSummaries: List<GradeSummary>, newSummary: List<GradeSummary>, notify: Boolean) {
local.deleteGradesSummary(oldSummaries uniqueSubtract newSummary)
local.deleteGradesSummary(oldSummaries.uniqueSubtract(newSummary)) local.saveGradesSummary((newSummary uniqueSubtract oldSummaries).onEach { summary ->
local.saveGradesSummary(newSummary.uniqueSubtract(oldSummaries).onEach { summary ->
val oldSummary = oldSummaries.find { oldSummary -> oldSummary.subject == summary.subject } val oldSummary = oldSummaries.find { oldSummary -> oldSummary.subject == summary.subject }
summary.isPredictedGradeNotified = when { summary.isPredictedGradeNotified = when {
summary.predictedGrade.isEmpty() -> true summary.predictedGrade.isEmpty() -> true
@ -62,24 +66,22 @@ class GradeRepository @Inject constructor(
else -> oldSummary.finalGradeLastChange else -> oldSummary.finalGradeLastChange
} }
}) })
return local.getGradesDetails(semester) to local.getGradesSummary(semester)
} }
suspend fun getUnreadGrades(semester: Semester): List<Grade> { fun getUnreadGrades(semester: Semester): Flow<List<Grade>> {
return local.getGradesDetails(semester).filter { grade -> !grade.isRead } return local.getGradesDetails(semester).map { it.filter { grade -> !grade.isRead } }
} }
suspend fun getNotNotifiedGrades(semester: Semester): List<Grade> { fun getNotNotifiedGrades(semester: Semester): Flow<List<Grade>> {
return local.getGradesDetails(semester).filter { grade -> !grade.isNotified } return local.getGradesDetails(semester).map { it.filter { grade -> !grade.isNotified } }
} }
suspend fun getNotNotifiedPredictedGrades(semester: Semester): List<GradeSummary> { fun getNotNotifiedPredictedGrades(semester: Semester): Flow<List<GradeSummary>> {
return local.getGradesSummary(semester).filter { gradeSummary -> !gradeSummary.isPredictedGradeNotified } return local.getGradesSummary(semester).map { it.filter { gradeSummary -> !gradeSummary.isPredictedGradeNotified } }
} }
suspend fun getNotNotifiedFinalGrades(semester: Semester): List<GradeSummary> { fun getNotNotifiedFinalGrades(semester: Semester): Flow<List<GradeSummary>> {
return local.getGradesSummary(semester).filter { gradeSummary -> !gradeSummary.isFinalGradeNotified } return local.getGradesSummary(semester).map { it.filter { gradeSummary -> !gradeSummary.isFinalGradeNotified } }
} }
suspend fun updateGrade(grade: Grade) { 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.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -14,34 +15,14 @@ class GradeStatisticsLocal @Inject constructor(
private val gradePointsStatisticsDb: GradePointsStatisticsDao 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) 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) 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>) { suspend fun saveGradesStatistics(gradesStatistics: List<GradeStatistics>) {
gradeStatisticsDb.insertAll(gradesStatistics) 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.db.entities.Student
import io.github.wulkanowy.data.pojos.GradeStatisticsItem import io.github.wulkanowy.data.pojos.GradeStatisticsItem
import io.github.wulkanowy.ui.modules.grade.statistics.ViewType import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -16,29 +17,40 @@ class GradeStatisticsRepository @Inject constructor(
private val remote: GradeStatisticsRemote private val remote: GradeStatisticsRemote
) { ) {
suspend fun getGradesStatistics(student: Student, semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean = false): List<GradeStatisticsItem> { fun getGradesStatistics(student: Student, semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean) = networkBoundResource(
return local.getGradesStatistics(semester, isSemester, subjectName).mapToStatisticItems().filter { !forceRefresh }.ifEmpty { shouldFetch = { it.isEmpty() || forceRefresh },
val new = remote.getGradeStatistics(student, semester, isSemester) query = { local.getGradesStatistics(semester, isSemester) },
val old = local.getGradesStatistics(semester, isSemester) fetch = { remote.getGradeStatistics(student, semester, isSemester) },
saveFetchResult = { old, new ->
local.deleteGradesStatistics(old.uniqueSubtract(new)) local.deleteGradesStatistics(old uniqueSubtract new)
local.saveGradesStatistics(new.uniqueSubtract(old)) local.saveGradesStatistics(new uniqueSubtract old)
},
local.getGradesStatistics(semester, isSemester, subjectName).mapToStatisticItems() 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> { fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
return local.getGradesPointsStatistics(semester, subjectName).mapToStatisticsItem().filter { !forceRefresh }.ifEmpty { shouldFetch = { it.isEmpty() || forceRefresh },
val new = remote.getGradePointsStatistics(student, semester) query = { local.getGradesPointsStatistics(semester) },
val old = local.getGradesPointsStatistics(semester) fetch = { remote.getGradePointsStatistics(student, semester) },
saveFetchResult = { old, new ->
local.deleteGradesPointsStatistics(old.uniqueSubtract(new)) local.deleteGradesPointsStatistics(old uniqueSubtract new)
local.saveGradesPointsStatistics(new.uniqueSubtract(old)) local.saveGradesPointsStatistics(new uniqueSubtract old)
},
local.getGradesPointsStatistics(semester, subjectName).mapToStatisticsItem() mapResult = { items ->
} when (subjectName) {
"Wszystkie" -> items
else -> items.filter { it.subject == subjectName }
}.mapToStatisticsItem()
} }
)
private fun List<GradeStatistics>.mapToStatisticItems() = groupBy { it.subject }.map { private fun List<GradeStatistics>.mapToStatisticItems() = groupBy { it.subject }.map {
GradeStatisticsItem( 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.dao.HomeworkDao
import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -22,7 +23,7 @@ class HomeworkLocal @Inject constructor(private val homeworkDb: HomeworkDao) {
homeworkDb.updateAll(homework) 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) 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.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.sunday import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
@ -16,18 +17,15 @@ class HomeworkRepository @Inject constructor(
private val remote: HomeworkRemote private val remote: HomeworkRemote
) { ) {
suspend fun getHomework(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): List<Homework> { fun getHomework(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
return local.getHomework(semester, start.monday, end.sunday).filter { !forceRefresh }.ifEmpty { shouldFetch = { it.isEmpty() || forceRefresh },
val new = remote.getHomework(student, semester, start.monday, end.sunday) query = { local.getHomework(semester, start.monday, end.sunday) },
fetch = { remote.getHomework(student, semester, start.monday, end.sunday) },
val old = local.getHomework(semester, start.monday, end.sunday) saveFetchResult = { old, new ->
local.deleteHomework(old uniqueSubtract new)
local.deleteHomework(old.uniqueSubtract(new)) local.saveHomework(new uniqueSubtract old)
local.saveHomework(new.uniqueSubtract(old))
local.getHomework(semester, start.monday, end.sunday)
}
} }
)
suspend fun toggleDone(homework: Homework) { suspend fun toggleDone(homework: Homework) {
local.updateHomework(listOf(homework.apply { local.updateHomework(listOf(homework.apply {

View File

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

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.dao.LuckyNumberDao
import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -22,7 +23,7 @@ class LuckyNumberLocal @Inject constructor(private val luckyNumberDb: LuckyNumbe
luckyNumberDb.deleteAll(listOfNotNull(luckyNumber)) 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) 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.LuckyNumber
import io.github.wulkanowy.data.db.entities.Student 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 org.threeten.bp.LocalDate.now
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -12,31 +14,25 @@ class LuckyNumberRepository @Inject constructor(
private val remote: LuckyNumberRemote private val remote: LuckyNumberRemote
) { ) {
suspend fun getLuckyNumber(student: Student, forceRefresh: Boolean = false, notify: Boolean = false): LuckyNumber? { fun getLuckyNumber(student: Student, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
return local.getLuckyNumber(student, now())?.takeIf { !forceRefresh } ?: run { shouldFetch = { it == null || forceRefresh },
val new = remote.getLuckyNumber(student) query = { local.getLuckyNumber(student, now()) },
val old = local.getLuckyNumber(student, now()) fetch = { remote.getLuckyNumber(student) },
saveFetchResult = { old, new ->
if (new != old) { if (new != old) {
old?.let { local.deleteLuckyNumber(it) } old?.let { local.deleteLuckyNumber(it) }
local.saveLuckyNumber(new?.apply { local.saveLuckyNumber(new?.apply {
if (notify) isNotified = false 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()) return local.getLuckyNumber(student, now())
} }
suspend fun updateLuckyNumber(luckyNumber: LuckyNumber) { suspend fun updateLuckyNumber(luckyNumber: LuckyNumber?) {
local.updateLuckyNumber(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.MessageWithAttachment
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -28,7 +29,7 @@ class MessageLocal @Inject constructor(
messagesDb.deleteAll(messages) 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) return messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId)
} }
@ -36,7 +37,7 @@ class MessageLocal @Inject constructor(
messageAttachmentDao.insertAttachments(attachments) messageAttachmentDao.insertAttachments(attachments)
} }
suspend fun getMessages(student: Student, folder: MessageFolder): List<Message> { fun getMessages(student: Student, folder: MessageFolder): Flow<List<Message>> {
return when (folder) { return when (folder) {
TRASHED -> messagesDb.loadDeleted(student.id.toInt()) TRASHED -> messagesDb.loadDeleted(student.id.toInt())
else -> messagesDb.loadAll(student.id.toInt(), folder.id) else -> messagesDb.loadAll(student.id.toInt(), folder.id)

View File

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

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.dao.MobileDeviceDao
import io.github.wulkanowy.data.db.entities.MobileDevice import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -17,7 +18,7 @@ class MobileDeviceLocal @Inject constructor(private val mobileDb: MobileDeviceDa
mobileDb.deleteAll(devices) mobileDb.deleteAll(devices)
} }
suspend fun getDevices(semester: Semester): List<MobileDevice> { fun getDevices(semester: Semester): Flow<List<MobileDevice>> {
return mobileDb.loadAll(semester.studentId) 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.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.pojos.MobileDeviceToken import io.github.wulkanowy.data.pojos.MobileDeviceToken
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -14,20 +15,19 @@ class MobileDeviceRepository @Inject constructor(
private val remote: MobileDeviceRemote private val remote: MobileDeviceRemote
) { ) {
suspend fun getDevices(student: Student, semester: Semester, forceRefresh: Boolean = false): List<MobileDevice> { fun getDevices(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
return local.getDevices(semester).filter { !forceRefresh }.ifEmpty { shouldFetch = { it.isEmpty() || forceRefresh },
val new = remote.getDevices(student, semester) query = { local.getDevices(semester) },
val old = local.getDevices(semester) fetch = { remote.getDevices(student, semester) },
saveFetchResult = { old, new ->
local.deleteDevices(old uniqueSubtract new) local.deleteDevices(old uniqueSubtract new)
local.saveDevices(new uniqueSubtract old) local.saveDevices(new uniqueSubtract old)
local.getDevices(semester)
}
} }
)
suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice): Boolean { suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) {
return remote.unregisterDevice(student, semester, device) remote.unregisterDevice(student, semester, device)
local.deleteDevices(listOf(device))
} }
suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken { 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.dao.NoteDao
import io.github.wulkanowy.data.db.entities.Note import io.github.wulkanowy.data.db.entities.Note
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -21,7 +22,7 @@ class NoteLocal @Inject constructor(private val noteDb: NoteDao) {
noteDb.deleteAll(notes) noteDb.deleteAll(notes)
} }
suspend fun getNotes(student: Student): List<Note> { fun getNotes(student: Student): Flow<List<Note>> {
return noteDb.loadAll(student.studentId) 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.Note
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -13,29 +16,27 @@ class NoteRepository @Inject constructor(
private val remote: NoteRemote private val remote: NoteRemote
) { ) {
suspend fun getNotes(student: Student, semester: Semester, forceRefresh: Boolean = false, notify: Boolean = false): List<Note> { fun getNotes(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
return local.getNotes(student).filter { !forceRefresh }.ifEmpty { shouldFetch = { it.isEmpty() || forceRefresh },
val new = remote.getNotes(student, semester) query = { local.getNotes(student) },
val old = local.getNotes(student) fetch = { remote.getNotes(student, semester) },
saveFetchResult = { old, new ->
local.deleteNotes(old.uniqueSubtract(new)) local.deleteNotes(old uniqueSubtract new)
local.saveNotes(new.uniqueSubtract(old).onEach { local.saveNotes((new uniqueSubtract old).onEach {
if (it.date >= student.registrationDate.toLocalDate()) it.apply { if (it.date >= student.registrationDate.toLocalDate()) it.apply {
isRead = false isRead = false
if (notify) isNotified = false if (notify) isNotified = false
} }
}) })
local.getNotes(student)
}
} }
)
suspend fun getNotNotifiedNotes(student: Student): List<Note> { fun getNotNotifiedNotes(student: Student): Flow<List<Note>> {
return local.getNotes(student).filter { note -> !note.isNotified } return local.getNotes(student).map { it.filter { note -> !note.isNotified } }
} }
suspend fun updateNote(note: Note) { suspend fun updateNote(note: Note) {
return local.updateNotes(listOf(note)) local.updateNotes(listOf(note))
} }
suspend fun updateNotes(notes: List<Note>) { suspend fun updateNotes(notes: List<Note>) {

View File

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

View File

@ -12,23 +12,27 @@ class ReportingUnitRepository @Inject constructor(
private val remote: ReportingUnitRemote private val remote: ReportingUnitRemote
) { ) {
suspend fun getReportingUnits(student: Student, forceRefresh: Boolean = false): List<ReportingUnit> { suspend fun refreshReportingUnits(student: Student) {
return local.getReportingUnits(student).filter { !forceRefresh }.ifEmpty {
val new = remote.getReportingUnits(student) val new = remote.getReportingUnits(student)
val old = local.getReportingUnits(student) val old = local.getReportingUnits(student)
local.deleteReportingUnits(old.uniqueSubtract(new)) local.deleteReportingUnits(old.uniqueSubtract(new))
local.saveReportingUnits(new.uniqueSubtract(old)) local.saveReportingUnits(new.uniqueSubtract(old))
}
suspend fun getReportingUnits(student: Student): List<ReportingUnit> {
return local.getReportingUnits(student).ifEmpty {
refreshReportingUnits(student)
local.getReportingUnits(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 { 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.dao.SchoolDao
import io.github.wulkanowy.data.db.entities.School import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject import javax.inject.Inject
class SchoolLocal @Inject constructor(private val schoolDb: SchoolDao) { class SchoolLocal @Inject constructor(private val schoolDb: SchoolDao) {
@ -15,7 +16,7 @@ class SchoolLocal @Inject constructor(private val schoolDb: SchoolDao) {
schoolDb.deleteAll(listOf(school)) schoolDb.deleteAll(listOf(school))
} }
suspend fun getSchool(semester: Semester): School? { fun getSchool(semester: Semester): Flow<School?> {
return schoolDb.load(semester.studentId, semester.classId) return schoolDb.load(semester.studentId, semester.classId)
} }
} }

View File

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

View File

@ -1,22 +1,24 @@
package io.github.wulkanowy.data.repositories.semester 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.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.DispatchersProvider
import io.github.wulkanowy.utils.getCurrentOrLast import io.github.wulkanowy.utils.getCurrentOrLast
import io.github.wulkanowy.utils.isCurrent import io.github.wulkanowy.utils.isCurrent
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.withContext
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class SemesterRepository @Inject constructor( class SemesterRepository @Inject constructor(
private val remote: SemesterRemote, 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> { suspend fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false) = withContext(dispatchers.backgroundThread) {
return local.getSemesters(student).let { semesters -> local.getSemesters(student).let { semesters ->
semesters.filter { semesters.filter {
!forceRefresh && when { !forceRefresh && when {
Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API -> semesters.firstOrNull { it.isCurrent }?.diaryId != 0 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 { suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) = withContext(dispatchers.backgroundThread) {
return getSemesters(student, forceRefresh).getCurrentOrLast() 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.dao.StudentDao
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk 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.decrypt
import io.github.wulkanowy.utils.security.encrypt import io.github.wulkanowy.utils.security.encrypt
import kotlinx.coroutines.withContext
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class StudentLocal @Inject constructor( class StudentLocal @Inject constructor(
private val studentDb: StudentDao, private val studentDb: StudentDao,
private val dispatchers: DispatchersProvider,
private val context: Context private val context: Context
) { ) {
suspend fun saveStudents(students: List<Student>): List<Long> { suspend fun saveStudents(students: List<Student>) = withContext(dispatchers.backgroundThread) {
return studentDb.insertAll(students.map { studentDb.insertAll(students.map {
if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) it.copy(password = encrypt(it.password, context)) if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) it.copy(password = encrypt(it.password, context))
else it else it
}) })
} }
suspend fun getStudents(decryptPass: Boolean): List<Student> { suspend fun getStudents(decryptPass: Boolean) = withContext(dispatchers.backgroundThread) {
return studentDb.loadAll().map { studentDb.loadAll().map {
it.apply { it.apply {
if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password) if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password)
} }
} }
} }
suspend fun getStudentById(id: Int): Student? { suspend fun getStudentById(id: Int) = withContext(dispatchers.backgroundThread) {
return studentDb.loadById(id)?.apply { studentDb.loadById(id)?.apply {
if (Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password) if (Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password)
} }
} }
suspend fun getCurrentStudent(decryptPass: Boolean): Student? { suspend fun getCurrentStudent(decryptPass: Boolean) = withContext(dispatchers.backgroundThread) {
return studentDb.loadCurrent()?.apply { studentDb.loadCurrent()?.apply {
if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password) if (decryptPass && Sdk.Mode.valueOf(loginMode) != Sdk.Mode.API) password = decrypt(password)
} }
} }
suspend fun setCurrentStudent(student: Student) { suspend fun setCurrentStudent(student: Student) = withContext(dispatchers.backgroundThread) {
return studentDb.run { studentDb.run {
resetCurrent() resetCurrent()
updateCurrent(student.id) updateCurrent(student.id)
} }
} }
suspend fun logoutStudent(student: Student) { suspend fun logoutStudent(student: Student) = withContext(dispatchers.backgroundThread) {
return studentDb.delete(student) 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.dao.SubjectDao
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Subject import io.github.wulkanowy.data.db.entities.Subject
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class SubjectLocal @Inject constructor(private val subjectDao: SubjectDao) { 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) 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.Semester
import io.github.wulkanowy.data.db.entities.Student 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 io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -13,15 +13,13 @@ class SubjectRepository @Inject constructor(
private val remote: SubjectRemote private val remote: SubjectRemote
) { ) {
suspend fun getSubjects(student: Student, semester: Semester, forceRefresh: Boolean = false): List<Subject> { fun getSubjects(student: Student, semester: Semester, forceRefresh: Boolean = false) = networkBoundResource(
return local.getSubjects(semester).filter { !forceRefresh }.ifEmpty { shouldFetch = { it.isEmpty() || forceRefresh },
val new = remote.getSubjects(student, semester) query = { local.getSubjects(semester) },
val old = local.getSubjects(semester) fetch = { remote.getSubjects(student, semester) },
saveFetchResult = { old, new ->
local.deleteSubjects(old.uniqueSubtract(new)) local.deleteSubjects(old uniqueSubtract new)
local.saveSubjects(new.uniqueSubtract(old)) local.saveSubjects(new uniqueSubtract old)
local.getSubjects(semester)
}
} }
)
} }

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.dao.TeacherDao
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Teacher import io.github.wulkanowy.data.db.entities.Teacher
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject import javax.inject.Inject
class TeacherLocal @Inject constructor(private val teacherDb: TeacherDao) { class TeacherLocal @Inject constructor(private val teacherDb: TeacherDao) {
@ -15,7 +16,7 @@ class TeacherLocal @Inject constructor(private val teacherDb: TeacherDao) {
teacherDb.deleteAll(teachers) teacherDb.deleteAll(teachers)
} }
suspend fun getTeachers(semester: Semester): List<Teacher> { fun getTeachers(semester: Semester): Flow<List<Teacher>> {
return teacherDb.loadAll(semester.studentId, semester.classId) 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.Semester
import io.github.wulkanowy.data.db.entities.Student 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 io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -13,15 +13,13 @@ class TeacherRepository @Inject constructor(
private val remote: TeacherRemote private val remote: TeacherRemote
) { ) {
suspend fun getTeachers(student: Student, semester: Semester, forceRefresh: Boolean = false): List<Teacher> { fun getTeachers(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
return local.getTeachers(semester).filter { !forceRefresh }.ifEmpty { shouldFetch = { it.isEmpty() || forceRefresh },
val new = remote.getTeachers(student, semester) query = { local.getTeachers(semester) },
val old = local.getTeachers(semester) fetch = { remote.getTeachers(student, semester) },
saveFetchResult = { old, new ->
local.deleteTeachers(old.uniqueSubtract(new)) local.deleteTeachers(old uniqueSubtract new)
local.saveTeachers(new.uniqueSubtract(old)) local.saveTeachers(new uniqueSubtract old)
local.getTeachers(semester)
}
} }
)
} }

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

View File

@ -12,7 +12,7 @@ class AttendanceSummaryWork @Inject constructor(
) : Work { ) : Work {
override fun create(student: Student, semester: Semester): Completable { 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.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.attendance.AttendanceRepository 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.monday
import io.github.wulkanowy.utils.sunday
import io.reactivex.Completable import io.reactivex.Completable
import kotlinx.coroutines.rx2.rxCompletable import kotlinx.coroutines.rx2.rxCompletable
import org.threeten.bp.LocalDate.now import org.threeten.bp.LocalDate.now
@ -13,6 +13,6 @@ import javax.inject.Inject
class AttendanceWork @Inject constructor(private val attendanceRepository: AttendanceRepository) : Work { class AttendanceWork @Inject constructor(private val attendanceRepository: AttendanceRepository) : Work {
override fun create(student: Student, semester: Semester): Completable { 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.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRepository 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.monday
import io.github.wulkanowy.utils.sunday
import io.reactivex.Completable import io.reactivex.Completable
import kotlinx.coroutines.rx2.rxCompletable import kotlinx.coroutines.rx2.rxCompletable
import org.threeten.bp.LocalDate.now import org.threeten.bp.LocalDate.now
@ -15,7 +15,6 @@ class CompletedLessonWork @Inject constructor(
) : Work { ) : Work {
override fun create(student: Student, semester: Semester): Completable { 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 { class ExamWork @Inject constructor(private val examRepository: ExamRepository) : Work {
override fun create(student: Student, semester: Semester): Completable { 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 kotlinx.coroutines.rx2.rxCompletable
import javax.inject.Inject 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 { 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.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable import io.reactivex.Completable
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.rx2.rxCompletable import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxSingle import kotlinx.coroutines.rx2.rxSingle
import javax.inject.Inject import javax.inject.Inject
@ -32,14 +33,14 @@ class GradeWork @Inject constructor(
) : Work { ) : Work {
override fun create(student: Student, semester: Semester): Completable { override fun create(student: Student, semester: Semester): Completable {
return rxCompletable { gradeRepository.getGrades(student, semester, true, preferencesRepository.isNotificationsEnable) } return rxCompletable { gradeRepository.getGrades(student, semester, true, preferencesRepository.isNotificationsEnable).waitForResult() }
.concatWith(Completable.concatArray(rxSingle { gradeRepository.getNotNotifiedGrades(semester) }.flatMapCompletable { .concatWith(Completable.concatArray(rxSingle { gradeRepository.getNotNotifiedGrades(semester).first() }.flatMapCompletable {
if (it.isNotEmpty()) notifyDetails(it) if (it.isNotEmpty()) notifyDetails(it)
rxCompletable { gradeRepository.updateGrades(it.onEach { grade -> grade.isNotified = true }) } 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) if (it.isNotEmpty()) notifyPredicted(it)
rxCompletable { gradeRepository.updateGradesSummary(it.onEach { grade -> grade.isPredictedGradeNotified = true }) } 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) if (it.isNotEmpty()) notifyFinal(it)
rxCompletable { gradeRepository.updateGradesSummary(it.onEach { grade -> grade.isFinalGradeNotified = true }) } 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.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.homework.HomeworkRepository 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.monday
import io.github.wulkanowy.utils.sunday
import io.reactivex.Completable import io.reactivex.Completable
import kotlinx.coroutines.rx2.rxCompletable import kotlinx.coroutines.rx2.rxCompletable
import org.threeten.bp.LocalDate.now import org.threeten.bp.LocalDate.now
@ -13,6 +13,6 @@ import javax.inject.Inject
class HomeworkWork @Inject constructor(private val homeworkRepository: HomeworkRepository) : Work { class HomeworkWork @Inject constructor(private val homeworkRepository: HomeworkRepository) : Work {
override fun create(student: Student, semester: Semester): Completable { 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.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable import io.reactivex.Completable
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.rx2.rxCompletable import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxMaybe import kotlinx.coroutines.rx2.rxMaybe
import javax.inject.Inject import javax.inject.Inject
@ -31,8 +32,8 @@ class LuckyNumberWork @Inject constructor(
) : Work { ) : Work {
override fun create(student: Student, semester: Semester): Completable { override fun create(student: Student, semester: Semester): Completable {
return rxMaybe { luckyNumberRepository.getLuckyNumber(student, true, preferencesRepository.isNotificationsEnable) } return rxMaybe { luckyNumberRepository.getLuckyNumber(student, true, preferencesRepository.isNotificationsEnable).waitForResult() }
.flatMap { rxMaybe { luckyNumberRepository.getNotNotifiedLuckyNumber(student) } } .flatMap { rxMaybe { luckyNumberRepository.getNotNotifiedLuckyNumber(student).first() } }
.flatMapCompletable { .flatMapCompletable {
notify(it) notify(it)
rxCompletable { luckyNumberRepository.updateLuckyNumber(it.apply { isNotified = true }) } 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.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable import io.reactivex.Completable
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.rx2.rxCompletable import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxSingle import kotlinx.coroutines.rx2.rxSingle
import javax.inject.Inject import javax.inject.Inject
@ -32,8 +33,8 @@ class MessageWork @Inject constructor(
) : Work { ) : Work {
override fun create(student: Student, semester: Semester): Completable { override fun create(student: Student, semester: Semester): Completable {
return rxSingle { messageRepository.getMessages(student, semester, RECEIVED, true, preferencesRepository.isNotificationsEnable) } return rxSingle { messageRepository.getMessages(student, semester, RECEIVED, true, preferencesRepository.isNotificationsEnable).waitForResult() }
.flatMap { rxSingle { messageRepository.getNotNotifiedMessages(student) } } .flatMap { rxSingle { messageRepository.getNotNotifiedMessages(student).first() } }
.flatMapCompletable { .flatMapCompletable {
if (it.isNotEmpty()) notify(it) if (it.isNotEmpty()) notify(it)
rxCompletable { messageRepository.updateMessages(it.onEach { message -> message.isNotified = true }) } 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.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.getCompatColor
import io.reactivex.Completable import io.reactivex.Completable
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.rx2.rxCompletable import kotlinx.coroutines.rx2.rxCompletable
import kotlinx.coroutines.rx2.rxSingle import kotlinx.coroutines.rx2.rxSingle
import javax.inject.Inject import javax.inject.Inject
@ -31,8 +32,8 @@ class NoteWork @Inject constructor(
) : Work { ) : Work {
override fun create(student: Student, semester: Semester): Completable { override fun create(student: Student, semester: Semester): Completable {
return rxSingle { noteRepository.getNotes(student, semester, true, preferencesRepository.isNotificationsEnable) } return rxSingle { noteRepository.getNotes(student, semester, true, preferencesRepository.isNotificationsEnable).waitForResult() }
.flatMap { rxSingle { noteRepository.getNotNotifiedNotes(student) } } .flatMap { rxSingle { noteRepository.getNotNotifiedNotes(student).first() } }
.flatMapCompletable { .flatMapCompletable {
if (it.isNotEmpty()) notify(it) if (it.isNotEmpty()) notify(it)
rxCompletable { noteRepository.updateNotes(it.onEach { note -> note.isNotified = true }) } rxCompletable { noteRepository.updateNotes(it.onEach { note -> note.isNotified = true }) }

View File

@ -15,10 +15,11 @@ class RecipientWork @Inject constructor(
) : Work { ) : Work {
override fun create(student: Student, semester: Semester): Completable { 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 -> .flatMapCompletable { units ->
Completable.mergeDelayError(units.map { 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 { class TeacherWork @Inject constructor(private val teacherRepository: TeacherRepository) : Work {
override fun create(student: Student, semester: Semester): Completable { 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 { class TimetableWork @Inject constructor(private val timetableRepository: TimetableRepository) : Work {
override fun create(student: Student, semester: Semester): Completable { 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 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.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.reactivex.Completable import io.reactivex.Completable
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.takeWhile
interface Work { interface Work {
fun create(student: Student, semester: Semester): Completable 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 package io.github.wulkanowy.ui.base
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.utils.SchedulersProvider import io.github.wulkanowy.utils.SchedulersProvider
import io.reactivex.Completable import io.github.wulkanowy.utils.flowWithResource
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import kotlinx.coroutines.rx2.rxCompletable import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.rx2.rxSingle 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 timber.log.Timber
import kotlin.coroutines.CoroutineContext
open class BasePresenter<T : BaseView>( open class BasePresenter<T : BaseView>(
protected val errorHandler: ErrorHandler, protected val errorHandler: ErrorHandler,
protected val studentRepository: StudentRepository, protected val studentRepository: StudentRepository,
protected val schedulers: SchedulersProvider 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() val disposable = CompositeDisposable()
var view: T? = null var view: T? = null
open fun onAttachView(view: T) { open fun onAttachView(view: T) {
job = Job()
this.view = view this.view = view
errorHandler.apply { errorHandler.apply {
showErrorMessage = view::showError showErrorMessage = view::showError
@ -28,30 +43,48 @@ open class BasePresenter<T : BaseView>(
} }
fun onExpiredLoginSelected() { fun onExpiredLoginSelected() {
Timber.i("Attempt to switch the student after the session expires") flowWithResource {
disposable.add(rxSingle { studentRepository.getCurrentStudent(false) } val student = studentRepository.getCurrentStudent(false)
.flatMapCompletable { rxCompletable { studentRepository.logoutStudent(it) } } studentRepository.logoutStudent(student)
.andThen(rxSingle { studentRepository.getSavedStudents(false) })
.flatMapCompletable { val students = studentRepository.getSavedStudents(false)
if (it.isNotEmpty()) { if (students.isNotEmpty()) {
Timber.i("Switching current student") Timber.i("Switching current student")
rxCompletable { studentRepository.switchStudent(it[0]) } studentRepository.switchStudent(students[0])
} else Completable.complete()
} }
.subscribeOn(schedulers.backgroundThread) }.onEach {
.observeOn(schedulers.mainThread) when (it.status) {
.subscribe({ Status.LOADING -> Timber.i("Attempt to switch the student after the session expires")
Status.SUCCESS -> {
Timber.i("Switch student result: Open login view") Timber.i("Switch student result: Open login view")
view?.openClearLoginView() view?.openClearLoginView()
}, { }
Status.ERROR -> {
Timber.i("Switch student result: An exception occurred") Timber.i("Switch student result: An exception occurred")
errorHandler.dispatch(it) 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() { open fun onDetachView() {
view = null view = null
disposable.clear() disposable.clear()
job.cancel()
errorHandler.clear() errorHandler.clear()
} }
} }

View File

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

View File

@ -1,12 +1,14 @@
package io.github.wulkanowy.ui.modules.about.contributor 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.pojos.Contributor
import io.github.wulkanowy.data.repositories.appcreator.AppCreatorRepository import io.github.wulkanowy.data.repositories.appcreator.AppCreatorRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider 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 import javax.inject.Inject
class ContributorPresenter @Inject constructor( class ContributorPresenter @Inject constructor(
@ -31,10 +33,15 @@ class ContributorPresenter @Inject constructor(
} }
private fun loadData() { private fun loadData() {
disposable.add(rxSingle { appCreatorRepository.getAppCreators() } flowWithResource { appCreatorRepository.getAppCreators() }.onEach {
.subscribeOn(schedulers.backgroundThread) when (it.status) {
.observeOn(schedulers.mainThread) Status.LOADING -> view?.showProgress(true)
.doFinally { view?.showProgress(false) } Status.SUCCESS -> view?.run {
.subscribe({ view?.run { updateData(it) } }, { errorHandler.dispatch(it) })) 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) { with(holder.binding) {
licenseItemName.text = item.libraryName 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) } root.setOnClickListener { onClickListener(item) }
} }

View File

@ -1,15 +1,22 @@
package io.github.wulkanowy.ui.modules.about.license package io.github.wulkanowy.ui.modules.about.license
import com.mikepenz.aboutlibraries.entity.Library import com.mikepenz.aboutlibraries.entity.Library
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.DispatchersProvider
import io.github.wulkanowy.utils.SchedulersProvider 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 import javax.inject.Inject
class LicensePresenter @Inject constructor( class LicensePresenter @Inject constructor(
schedulers: SchedulersProvider, schedulers: SchedulersProvider,
private val dispatchers: DispatchersProvider,
errorHandler: ErrorHandler, errorHandler: ErrorHandler,
studentRepository: StudentRepository studentRepository: StudentRepository
) : BasePresenter<LicenseView>(errorHandler, studentRepository, schedulers) { ) : BasePresenter<LicenseView>(errorHandler, studentRepository, schedulers) {
@ -21,14 +28,22 @@ class LicensePresenter @Inject constructor(
} }
fun onItemSelected(library: Library) { fun onItemSelected(library: Library) {
view?.run { library.license?.licenseDescription?.let { openLicense(it) } } view?.run { library.licenses?.firstOrNull()?.licenseDescription?.let { openLicense(it) } }
} }
private fun loadData() { private fun loadData() {
disposable.add(Single.fromCallable { view?.appLibraries.orEmpty() } flowWithResource {
.subscribeOn(schedulers.backgroundThread) withContext(dispatchers.backgroundThread) {
.observeOn(schedulers.mainThread) view?.appLibraries.orEmpty()
.doOnEvent { _, _ -> view?.showProgress(false) } }
.subscribe({ view?.run { updateData(it) } }, { errorHandler.dispatch(it) })) }.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 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.logger.LoggerRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.SchedulersProvider 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 timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -23,16 +25,19 @@ class LogViewerPresenter @Inject constructor(
} }
fun onShareLogsSelected(): Boolean { fun onShareLogsSelected(): Boolean {
disposable.add(rxSingle { loggerRepository.getLogFiles() } flowWithResource { loggerRepository.getLogFiles() }.onEach {
.subscribeOn(schedulers.backgroundThread) when (it.status) {
.observeOn(schedulers.mainThread) Status.LOADING -> Timber.d("Loading logs files started")
.subscribe({ files -> Status.SUCCESS -> {
Timber.i("Loading logs files result: ${files.joinToString { it.name }}") Timber.i("Loading logs files result: ${it.data!!.joinToString { file -> file.name }}")
view?.shareLogs(files) view?.shareLogs(it.data)
}, { }
Status.ERROR -> {
Timber.i("Loading logs files result: An exception occurred") Timber.i("Loading logs files result: An exception occurred")
errorHandler.dispatch(it) errorHandler.dispatch(it.error!!)
})) }
}
}.launch("share")
return true return true
} }
@ -41,15 +46,18 @@ class LogViewerPresenter @Inject constructor(
} }
private fun loadLogFile() { private fun loadLogFile() {
disposable.add(rxSingle { loggerRepository.getLastLogLines() } flowWithResource { loggerRepository.getLastLogLines() }.onEach {
.subscribeOn(schedulers.backgroundThread) when (it.status) {
.observeOn(schedulers.mainThread) Status.LOADING -> Timber.d("Loading last log file started")
.subscribe({ Status.SUCCESS -> {
Timber.i("Loading last log file result: load ${it.size} lines") Timber.i("Loading last log file result: load ${it.data!!.size} lines")
view?.setLines(it) view?.setLines(it.data)
}, { }
Status.ERROR -> {
Timber.i("Loading last log file result: An exception occurred") Timber.i("Loading last log file result: An exception occurred")
errorHandler.dispatch(it) errorHandler.dispatch(it.error!!)
})) }
}
}.launch("file")
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.grade.summary 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.db.entities.GradeSummary
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter 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.ui.modules.grade.GradeDetailsWithAverage
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import io.github.wulkanowy.utils.SchedulersProvider 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 timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -30,36 +33,45 @@ class GradeSummaryPresenter @Inject constructor(
fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) { fun onParentViewLoadData(semesterId: Int, forceRefresh: Boolean) {
Timber.i("Loading grade summary data started") Timber.i("Loading grade summary data started")
disposable.add(rxSingle { studentRepository.getCurrentStudent() }
.flatMap { rxSingle { averageProvider.getGradesDetailsWithAverage(it, semesterId, forceRefresh) } } loadData(semesterId, forceRefresh)
.map { createGradeSummaryItems(it) } if (!forceRefresh) view?.showErrorView(false)
.subscribeOn(schedulers.backgroundThread) }
.observeOn(schedulers.mainThread)
.doFinally { 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
)
}
Status.ERROR -> {
Timber.i("Loading grade summary result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.run { view?.run {
showRefresh(false) showRefresh(false)
showProgress(false) showProgress(false)
enableSwipe(true) enableSwipe(true)
notifyParentDataLoaded(semesterId) notifyParentDataLoaded(semesterId)
} }
}.subscribe({ }.launch()
Timber.i("Loading grade summary result: Success")
view?.run {
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
showErrorView(false)
updateData(it)
}
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)
})
} }
private fun showErrorViewOnError(message: String, error: Throwable) { private fun showErrorViewOnError(message: String, error: Throwable) {
@ -105,7 +117,7 @@ class GradeSummaryPresenter @Inject constructor(
showEmpty(false) showEmpty(false)
clearView() clearView()
} }
disposable.clear() cancelJobs("load")
} }
private fun createGradeSummaryItems(items: List<GradeDetailsWithAverage>): List<GradeSummary> { 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() { override fun clearData() {
with(homeworkAdapter) { with(homeworkAdapter) {
items = emptyList() items = emptyList()

View File

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

View File

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

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