mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-17 11:25:34 -04:00
Merge pull request #4322 from kiwix/Fixes#4308
Refactored the `RxJava` to coroutines in `PageViewModel`.
This commit is contained in:
commit
307e7249de
@ -112,7 +112,7 @@ class KiwixRoomDatabaseTest {
|
|||||||
|
|
||||||
// test inserting into history database
|
// test inserting into history database
|
||||||
historyRoomDao.saveHistory(historyItem)
|
historyRoomDao.saveHistory(historyItem)
|
||||||
var historyList = historyRoomDao.historyRoomEntity().blockingFirst()
|
var historyList = historyRoomDao.historyRoomEntity().first()
|
||||||
with(historyList.first()) {
|
with(historyList.first()) {
|
||||||
assertThat(historyTitle, equalTo(historyItem.title))
|
assertThat(historyTitle, equalTo(historyItem.title))
|
||||||
assertThat(zimId, equalTo(historyItem.zimId))
|
assertThat(zimId, equalTo(historyItem.zimId))
|
||||||
@ -126,7 +126,7 @@ class KiwixRoomDatabaseTest {
|
|||||||
|
|
||||||
// test deleting the history
|
// test deleting the history
|
||||||
historyRoomDao.deleteHistory(listOf(historyItem))
|
historyRoomDao.deleteHistory(listOf(historyItem))
|
||||||
historyList = historyRoomDao.historyRoomEntity().blockingFirst()
|
historyList = historyRoomDao.historyRoomEntity().first()
|
||||||
assertEquals(historyList.size, 0)
|
assertEquals(historyList.size, 0)
|
||||||
|
|
||||||
// test deleting all history
|
// test deleting all history
|
||||||
@ -134,10 +134,10 @@ class KiwixRoomDatabaseTest {
|
|||||||
historyRoomDao.saveHistory(
|
historyRoomDao.saveHistory(
|
||||||
getHistoryItem(databaseId = 2)
|
getHistoryItem(databaseId = 2)
|
||||||
)
|
)
|
||||||
historyList = historyRoomDao.historyRoomEntity().blockingFirst()
|
historyList = historyRoomDao.historyRoomEntity().first()
|
||||||
assertEquals(historyList.size, 2)
|
assertEquals(historyList.size, 2)
|
||||||
historyRoomDao.deleteAllHistory()
|
historyRoomDao.deleteAllHistory()
|
||||||
historyList = historyRoomDao.historyRoomEntity().blockingFirst()
|
historyList = historyRoomDao.historyRoomEntity().first()
|
||||||
assertEquals(historyList.size, 0)
|
assertEquals(historyList.size, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ class KiwixRoomDatabaseTest {
|
|||||||
runBlocking {
|
runBlocking {
|
||||||
notesRoomDao = db.notesRoomDao()
|
notesRoomDao = db.notesRoomDao()
|
||||||
// delete all the notes from database to properly run the test cases.
|
// delete all the notes from database to properly run the test cases.
|
||||||
notesRoomDao.deleteNotes(notesRoomDao.notes().blockingFirst() as List<NoteListItem>)
|
notesRoomDao.deleteNotes(notesRoomDao.notes().first() as List<NoteListItem>)
|
||||||
val noteItem =
|
val noteItem =
|
||||||
getNoteListItem(
|
getNoteListItem(
|
||||||
zimUrl = "http://kiwix.app/MainPage",
|
zimUrl = "http://kiwix.app/MainPage",
|
||||||
@ -155,7 +155,7 @@ class KiwixRoomDatabaseTest {
|
|||||||
|
|
||||||
// Save and retrieve a notes item
|
// Save and retrieve a notes item
|
||||||
notesRoomDao.saveNote(noteItem)
|
notesRoomDao.saveNote(noteItem)
|
||||||
var notesList = notesRoomDao.notes().blockingFirst() as List<NoteListItem>
|
var notesList = notesRoomDao.notes().first() as List<NoteListItem>
|
||||||
with(notesList.first()) {
|
with(notesList.first()) {
|
||||||
assertThat(zimId, equalTo(noteItem.zimId))
|
assertThat(zimId, equalTo(noteItem.zimId))
|
||||||
assertThat(zimUrl, equalTo(noteItem.zimUrl))
|
assertThat(zimUrl, equalTo(noteItem.zimUrl))
|
||||||
@ -168,7 +168,7 @@ class KiwixRoomDatabaseTest {
|
|||||||
|
|
||||||
// test deleting the history
|
// test deleting the history
|
||||||
notesRoomDao.deleteNotes(listOf(noteItem))
|
notesRoomDao.deleteNotes(listOf(noteItem))
|
||||||
notesList = notesRoomDao.notes().blockingFirst() as List<NoteListItem>
|
notesList = notesRoomDao.notes().first() as List<NoteListItem>
|
||||||
assertEquals(notesList.size, 0)
|
assertEquals(notesList.size, 0)
|
||||||
|
|
||||||
// test deleting all notes
|
// test deleting all notes
|
||||||
@ -179,10 +179,10 @@ class KiwixRoomDatabaseTest {
|
|||||||
zimUrl = "http://kiwix.app/Installing"
|
zimUrl = "http://kiwix.app/Installing"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
notesList = notesRoomDao.notes().blockingFirst() as List<NoteListItem>
|
notesList = notesRoomDao.notes().first() as List<NoteListItem>
|
||||||
assertEquals(notesList.size, 2)
|
assertEquals(notesList.size, 2)
|
||||||
notesRoomDao.deletePages(notesRoomDao.notes().blockingFirst())
|
notesRoomDao.deletePages(notesRoomDao.notes().first())
|
||||||
notesList = notesRoomDao.notes().blockingFirst() as List<NoteListItem>
|
notesList = notesRoomDao.notes().first() as List<NoteListItem>
|
||||||
assertEquals(notesList.size, 0)
|
assertEquals(notesList.size, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,7 +215,7 @@ class ObjectBoxToRoomMigratorTest {
|
|||||||
kiwixRoomDatabase.recentSearchRoomDao().deleteSearchHistory()
|
kiwixRoomDatabase.recentSearchRoomDao().deleteSearchHistory()
|
||||||
kiwixRoomDatabase.historyRoomDao().deleteAllHistory()
|
kiwixRoomDatabase.historyRoomDao().deleteAllHistory()
|
||||||
kiwixRoomDatabase.notesRoomDao()
|
kiwixRoomDatabase.notesRoomDao()
|
||||||
.deletePages(kiwixRoomDatabase.notesRoomDao().notes().blockingFirst())
|
.deletePages(kiwixRoomDatabase.notesRoomDao().notes().first())
|
||||||
box.removeAll()
|
box.removeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,7 +238,7 @@ class ObjectBoxToRoomMigratorTest {
|
|||||||
// migrate data into room database
|
// migrate data into room database
|
||||||
objectBoxToRoomMigrator.migrateHistory(box)
|
objectBoxToRoomMigrator.migrateHistory(box)
|
||||||
// check if data successfully migrated to room
|
// check if data successfully migrated to room
|
||||||
val actual = kiwixRoomDatabase.historyRoomDao().historyRoomEntity().blockingFirst()
|
val actual = kiwixRoomDatabase.historyRoomDao().historyRoomEntity().first()
|
||||||
with(actual.first()) {
|
with(actual.first()) {
|
||||||
assertThat(historyTitle, equalTo(historyItem.title))
|
assertThat(historyTitle, equalTo(historyItem.title))
|
||||||
assertThat(zimId, equalTo(historyItem.zimId))
|
assertThat(zimId, equalTo(historyItem.zimId))
|
||||||
@ -254,7 +254,7 @@ class ObjectBoxToRoomMigratorTest {
|
|||||||
|
|
||||||
// Migrate data from empty ObjectBox database
|
// Migrate data from empty ObjectBox database
|
||||||
objectBoxToRoomMigrator.migrateHistory(box)
|
objectBoxToRoomMigrator.migrateHistory(box)
|
||||||
var actualData = kiwixRoomDatabase.historyRoomDao().historyRoomEntity().blockingFirst()
|
var actualData = kiwixRoomDatabase.historyRoomDao().historyRoomEntity().first()
|
||||||
assertTrue(actualData.isEmpty())
|
assertTrue(actualData.isEmpty())
|
||||||
|
|
||||||
// Test if data successfully migrated to Room and existing data is preserved
|
// Test if data successfully migrated to Room and existing data is preserved
|
||||||
@ -262,7 +262,7 @@ class ObjectBoxToRoomMigratorTest {
|
|||||||
box.put(HistoryEntity(historyItem2))
|
box.put(HistoryEntity(historyItem2))
|
||||||
// Migrate data into Room database
|
// Migrate data into Room database
|
||||||
objectBoxToRoomMigrator.migrateHistory(box)
|
objectBoxToRoomMigrator.migrateHistory(box)
|
||||||
actualData = kiwixRoomDatabase.historyRoomDao().historyRoomEntity().blockingFirst()
|
actualData = kiwixRoomDatabase.historyRoomDao().historyRoomEntity().first()
|
||||||
assertEquals(2, actualData.size)
|
assertEquals(2, actualData.size)
|
||||||
val existingItem =
|
val existingItem =
|
||||||
actualData.find {
|
actualData.find {
|
||||||
@ -281,7 +281,7 @@ class ObjectBoxToRoomMigratorTest {
|
|||||||
kiwixRoomDatabase.historyRoomDao().saveHistory(historyItem)
|
kiwixRoomDatabase.historyRoomDao().saveHistory(historyItem)
|
||||||
box.put(HistoryEntity(historyItem))
|
box.put(HistoryEntity(historyItem))
|
||||||
objectBoxToRoomMigrator.migrateHistory(box)
|
objectBoxToRoomMigrator.migrateHistory(box)
|
||||||
actualData = kiwixRoomDatabase.historyRoomDao().historyRoomEntity().blockingFirst()
|
actualData = kiwixRoomDatabase.historyRoomDao().historyRoomEntity().first()
|
||||||
assertEquals(1, actualData.size)
|
assertEquals(1, actualData.size)
|
||||||
|
|
||||||
clearRoomAndBoxStoreDatabases(box)
|
clearRoomAndBoxStoreDatabases(box)
|
||||||
@ -296,7 +296,7 @@ class ObjectBoxToRoomMigratorTest {
|
|||||||
kiwixRoomDatabase.historyRoomDao().saveHistory(historyItem4)
|
kiwixRoomDatabase.historyRoomDao().saveHistory(historyItem4)
|
||||||
box.put(HistoryEntity(historyItem))
|
box.put(HistoryEntity(historyItem))
|
||||||
objectBoxToRoomMigrator.migrateHistory(box)
|
objectBoxToRoomMigrator.migrateHistory(box)
|
||||||
actualData = kiwixRoomDatabase.historyRoomDao().historyRoomEntity().blockingFirst()
|
actualData = kiwixRoomDatabase.historyRoomDao().historyRoomEntity().first()
|
||||||
assertEquals(2, actualData.size)
|
assertEquals(2, actualData.size)
|
||||||
|
|
||||||
clearRoomAndBoxStoreDatabases(box)
|
clearRoomAndBoxStoreDatabases(box)
|
||||||
@ -310,7 +310,7 @@ class ObjectBoxToRoomMigratorTest {
|
|||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
}
|
}
|
||||||
// Ensure Room database remains empty or unaffected by the invalid data
|
// Ensure Room database remains empty or unaffected by the invalid data
|
||||||
actualData = kiwixRoomDatabase.historyRoomDao().historyRoomEntity().blockingFirst()
|
actualData = kiwixRoomDatabase.historyRoomDao().historyRoomEntity().first()
|
||||||
assertTrue(actualData.isEmpty())
|
assertTrue(actualData.isEmpty())
|
||||||
|
|
||||||
// Test large data migration for recent searches
|
// Test large data migration for recent searches
|
||||||
@ -332,7 +332,7 @@ class ObjectBoxToRoomMigratorTest {
|
|||||||
val endTime = System.currentTimeMillis()
|
val endTime = System.currentTimeMillis()
|
||||||
val migrationTime = endTime - startTime
|
val migrationTime = endTime - startTime
|
||||||
// Check if data successfully migrated to Room
|
// Check if data successfully migrated to Room
|
||||||
actualData = kiwixRoomDatabase.historyRoomDao().historyRoomEntity().blockingFirst()
|
actualData = kiwixRoomDatabase.historyRoomDao().historyRoomEntity().first()
|
||||||
assertEquals(numEntities, actualData.size)
|
assertEquals(numEntities, actualData.size)
|
||||||
// Assert that the migration completes within a reasonable time frame
|
// Assert that the migration completes within a reasonable time frame
|
||||||
assertTrue(
|
assertTrue(
|
||||||
@ -367,7 +367,7 @@ class ObjectBoxToRoomMigratorTest {
|
|||||||
// migrate data into room database
|
// migrate data into room database
|
||||||
objectBoxToRoomMigrator.migrateNotes(box)
|
objectBoxToRoomMigrator.migrateNotes(box)
|
||||||
// check if data successfully migrated to room
|
// check if data successfully migrated to room
|
||||||
var notesList = kiwixRoomDatabase.notesRoomDao().notes().blockingFirst() as List<NoteListItem>
|
var notesList = kiwixRoomDatabase.notesRoomDao().notes().first() as List<NoteListItem>
|
||||||
with(notesList.first()) {
|
with(notesList.first()) {
|
||||||
assertThat(zimId, equalTo(noteItem.zimId))
|
assertThat(zimId, equalTo(noteItem.zimId))
|
||||||
assertThat(zimUrl, equalTo(noteItem.zimUrl))
|
assertThat(zimUrl, equalTo(noteItem.zimUrl))
|
||||||
@ -382,7 +382,7 @@ class ObjectBoxToRoomMigratorTest {
|
|||||||
|
|
||||||
// Migrate data from empty ObjectBox database
|
// Migrate data from empty ObjectBox database
|
||||||
objectBoxToRoomMigrator.migrateNotes(box)
|
objectBoxToRoomMigrator.migrateNotes(box)
|
||||||
notesList = kiwixRoomDatabase.notesRoomDao().notes().blockingFirst() as List<NoteListItem>
|
notesList = kiwixRoomDatabase.notesRoomDao().notes().first() as List<NoteListItem>
|
||||||
assertTrue(notesList.isEmpty())
|
assertTrue(notesList.isEmpty())
|
||||||
|
|
||||||
// Test if data successfully migrated to Room and existing data is preserved
|
// Test if data successfully migrated to Room and existing data is preserved
|
||||||
@ -390,7 +390,7 @@ class ObjectBoxToRoomMigratorTest {
|
|||||||
box.put(NotesEntity(noteItem))
|
box.put(NotesEntity(noteItem))
|
||||||
// Migrate data into Room database
|
// Migrate data into Room database
|
||||||
objectBoxToRoomMigrator.migrateNotes(box)
|
objectBoxToRoomMigrator.migrateNotes(box)
|
||||||
notesList = kiwixRoomDatabase.notesRoomDao().notes().blockingFirst() as List<NoteListItem>
|
notesList = kiwixRoomDatabase.notesRoomDao().notes().first() as List<NoteListItem>
|
||||||
assertEquals(noteItem.title, notesList.first().title)
|
assertEquals(noteItem.title, notesList.first().title)
|
||||||
assertEquals(2, notesList.size)
|
assertEquals(2, notesList.size)
|
||||||
val existingItem =
|
val existingItem =
|
||||||
@ -411,7 +411,7 @@ class ObjectBoxToRoomMigratorTest {
|
|||||||
box.put(NotesEntity(noteItem1))
|
box.put(NotesEntity(noteItem1))
|
||||||
// Migrate data into Room database
|
// Migrate data into Room database
|
||||||
objectBoxToRoomMigrator.migrateNotes(box)
|
objectBoxToRoomMigrator.migrateNotes(box)
|
||||||
notesList = kiwixRoomDatabase.notesRoomDao().notes().blockingFirst() as List<NoteListItem>
|
notesList = kiwixRoomDatabase.notesRoomDao().notes().first() as List<NoteListItem>
|
||||||
assertEquals(1, notesList.size)
|
assertEquals(1, notesList.size)
|
||||||
|
|
||||||
clearRoomAndBoxStoreDatabases(box)
|
clearRoomAndBoxStoreDatabases(box)
|
||||||
@ -426,7 +426,7 @@ class ObjectBoxToRoomMigratorTest {
|
|||||||
kiwixRoomDatabase.notesRoomDao().saveNote(noteItem1)
|
kiwixRoomDatabase.notesRoomDao().saveNote(noteItem1)
|
||||||
box.put(NotesEntity(noteItem2))
|
box.put(NotesEntity(noteItem2))
|
||||||
objectBoxToRoomMigrator.migrateNotes(box)
|
objectBoxToRoomMigrator.migrateNotes(box)
|
||||||
notesList = kiwixRoomDatabase.notesRoomDao().notes().blockingFirst() as List<NoteListItem>
|
notesList = kiwixRoomDatabase.notesRoomDao().notes().first() as List<NoteListItem>
|
||||||
assertEquals(2, notesList.size)
|
assertEquals(2, notesList.size)
|
||||||
|
|
||||||
clearRoomAndBoxStoreDatabases(box)
|
clearRoomAndBoxStoreDatabases(box)
|
||||||
@ -440,7 +440,7 @@ class ObjectBoxToRoomMigratorTest {
|
|||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
}
|
}
|
||||||
// Ensure Room database remains empty or unaffected by the invalid data
|
// Ensure Room database remains empty or unaffected by the invalid data
|
||||||
notesList = kiwixRoomDatabase.notesRoomDao().notes().blockingFirst() as List<NoteListItem>
|
notesList = kiwixRoomDatabase.notesRoomDao().notes().first() as List<NoteListItem>
|
||||||
assertTrue(notesList.isEmpty())
|
assertTrue(notesList.isEmpty())
|
||||||
|
|
||||||
// Test large data migration for recent searches
|
// Test large data migration for recent searches
|
||||||
@ -462,7 +462,7 @@ class ObjectBoxToRoomMigratorTest {
|
|||||||
val endTime = System.currentTimeMillis()
|
val endTime = System.currentTimeMillis()
|
||||||
val migrationTime = endTime - startTime
|
val migrationTime = endTime - startTime
|
||||||
// Check if data successfully migrated to Room
|
// Check if data successfully migrated to Room
|
||||||
notesList = kiwixRoomDatabase.notesRoomDao().notes().blockingFirst() as List<NoteListItem>
|
notesList = kiwixRoomDatabase.notesRoomDao().notes().first() as List<NoteListItem>
|
||||||
assertEquals(numEntities, notesList.size)
|
assertEquals(numEntities, notesList.size)
|
||||||
// Assert that the migration completes within a reasonable time frame
|
// Assert that the migration completes within a reasonable time frame
|
||||||
assertTrue(
|
assertTrue(
|
||||||
|
@ -22,6 +22,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 kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.hamcrest.MatcherAssert.assertThat
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
import org.hamcrest.core.IsEqual.equalTo
|
import org.hamcrest.core.IsEqual.equalTo
|
||||||
@ -66,7 +67,7 @@ class HistoryRoomDaoTest {
|
|||||||
|
|
||||||
// Save and retrieve a history item
|
// Save and retrieve a history item
|
||||||
historyRoomDao.saveHistory(historyItem)
|
historyRoomDao.saveHistory(historyItem)
|
||||||
var historyList = historyRoomDao.historyRoomEntity().blockingFirst()
|
var historyList = historyRoomDao.historyRoomEntity().first()
|
||||||
with(historyList.first()) {
|
with(historyList.first()) {
|
||||||
assertThat(historyTitle, equalTo(historyItem.title))
|
assertThat(historyTitle, equalTo(historyItem.title))
|
||||||
assertThat(zimId, equalTo(historyItem.zimId))
|
assertThat(zimId, equalTo(historyItem.zimId))
|
||||||
@ -80,26 +81,26 @@ class HistoryRoomDaoTest {
|
|||||||
|
|
||||||
// Test to update the same day history for url
|
// Test to update the same day history for url
|
||||||
historyRoomDao.saveHistory(historyItem)
|
historyRoomDao.saveHistory(historyItem)
|
||||||
historyList = historyRoomDao.historyRoomEntity().blockingFirst()
|
historyList = historyRoomDao.historyRoomEntity().first()
|
||||||
assertEquals(historyList.size, 1)
|
assertEquals(historyList.size, 1)
|
||||||
|
|
||||||
// Delete the saved history item
|
// Delete the saved history item
|
||||||
historyRoomDao.deleteHistory(listOf(historyItem))
|
historyRoomDao.deleteHistory(listOf(historyItem))
|
||||||
historyList = historyRoomDao.historyRoomEntity().blockingFirst()
|
historyList = historyRoomDao.historyRoomEntity().first()
|
||||||
assertEquals(historyList.size, 0)
|
assertEquals(historyList.size, 0)
|
||||||
|
|
||||||
// Save and delete all history items
|
// Save and delete all history items
|
||||||
historyRoomDao.saveHistory(historyItem)
|
historyRoomDao.saveHistory(historyItem)
|
||||||
historyRoomDao.saveHistory(getHistoryItem(databaseId = 2, dateString = "31 May 2024"))
|
historyRoomDao.saveHistory(getHistoryItem(databaseId = 2, dateString = "31 May 2024"))
|
||||||
historyRoomDao.deleteAllHistory()
|
historyRoomDao.deleteAllHistory()
|
||||||
historyList = historyRoomDao.historyRoomEntity().blockingFirst()
|
historyList = historyRoomDao.historyRoomEntity().first()
|
||||||
assertThat(historyList.size, equalTo(0))
|
assertThat(historyList.size, equalTo(0))
|
||||||
|
|
||||||
// Save history item with empty fields
|
// Save history item with empty fields
|
||||||
val emptyHistoryUrl = ""
|
val emptyHistoryUrl = ""
|
||||||
val emptyTitle = ""
|
val emptyTitle = ""
|
||||||
historyRoomDao.saveHistory(getHistoryItem(emptyTitle, emptyHistoryUrl, databaseId = 1))
|
historyRoomDao.saveHistory(getHistoryItem(emptyTitle, emptyHistoryUrl, databaseId = 1))
|
||||||
historyList = historyRoomDao.historyRoomEntity().blockingFirst()
|
historyList = historyRoomDao.historyRoomEntity().first()
|
||||||
assertThat(historyList.size, equalTo(1))
|
assertThat(historyList.size, equalTo(1))
|
||||||
historyRoomDao.deleteAllHistory()
|
historyRoomDao.deleteAllHistory()
|
||||||
|
|
||||||
@ -113,14 +114,14 @@ class HistoryRoomDaoTest {
|
|||||||
dateString = "31 May 2024"
|
dateString = "31 May 2024"
|
||||||
)
|
)
|
||||||
historyRoomDao.saveHistory(historyItem1)
|
historyRoomDao.saveHistory(historyItem1)
|
||||||
historyList = historyRoomDao.historyRoomEntity().blockingFirst()
|
historyList = historyRoomDao.historyRoomEntity().first()
|
||||||
assertThat(historyList.size, equalTo(2))
|
assertThat(historyList.size, equalTo(2))
|
||||||
historyRoomDao.deleteAllHistory()
|
historyRoomDao.deleteAllHistory()
|
||||||
|
|
||||||
// Save two entity with same and database id with same date to see if it's updated or not.
|
// Save two entity with same and database id with same date to see if it's updated or not.
|
||||||
historyRoomDao.saveHistory(historyItem)
|
historyRoomDao.saveHistory(historyItem)
|
||||||
historyRoomDao.saveHistory(historyItem)
|
historyRoomDao.saveHistory(historyItem)
|
||||||
historyList = historyRoomDao.historyRoomEntity().blockingFirst()
|
historyList = historyRoomDao.historyRoomEntity().first()
|
||||||
assertThat(historyList.size, equalTo(1))
|
assertThat(historyList.size, equalTo(1))
|
||||||
historyRoomDao.deleteAllHistory()
|
historyRoomDao.deleteAllHistory()
|
||||||
|
|
||||||
@ -132,7 +133,7 @@ class HistoryRoomDaoTest {
|
|||||||
"Undefined value was saved into database",
|
"Undefined value was saved into database",
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (_: Exception) {
|
||||||
assertThat("Undefined value was not saved, as expected.", true)
|
assertThat("Undefined value was not saved, as expected.", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,13 +141,13 @@ class HistoryRoomDaoTest {
|
|||||||
val unicodeTitle = "title \u03A3" // Unicode character for Greek capital letter Sigma
|
val unicodeTitle = "title \u03A3" // Unicode character for Greek capital letter Sigma
|
||||||
val historyItem2 = getHistoryItem(title = unicodeTitle, databaseId = 2)
|
val historyItem2 = getHistoryItem(title = unicodeTitle, databaseId = 2)
|
||||||
historyRoomDao.saveHistory(historyItem2)
|
historyRoomDao.saveHistory(historyItem2)
|
||||||
historyList = historyRoomDao.historyRoomEntity().blockingFirst()
|
historyList = historyRoomDao.historyRoomEntity().first()
|
||||||
assertThat(historyList.first().historyTitle, equalTo("title Σ"))
|
assertThat(historyList.first().historyTitle, equalTo("title Σ"))
|
||||||
|
|
||||||
// Test deletePages function
|
// Test deletePages function
|
||||||
historyRoomDao.saveHistory(historyItem)
|
historyRoomDao.saveHistory(historyItem)
|
||||||
historyRoomDao.deletePages(listOf(historyItem, historyItem2))
|
historyRoomDao.deletePages(listOf(historyItem, historyItem2))
|
||||||
historyList = historyRoomDao.historyRoomEntity().blockingFirst()
|
historyList = historyRoomDao.historyRoomEntity().first()
|
||||||
assertThat(historyList.size, equalTo(0))
|
assertThat(historyList.size, equalTo(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,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 kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.hamcrest.MatcherAssert.assertThat
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
import org.hamcrest.core.IsEqual.equalTo
|
import org.hamcrest.core.IsEqual.equalTo
|
||||||
@ -65,7 +66,7 @@ class NoteRoomDaoTest {
|
|||||||
|
|
||||||
// Save and retrieve a notes item
|
// Save and retrieve a notes item
|
||||||
notesRoomDao.saveNote(noteItem)
|
notesRoomDao.saveNote(noteItem)
|
||||||
var notesList = notesRoomDao.notes().blockingFirst() as List<NoteListItem>
|
var notesList = notesRoomDao.notes().first() as List<NoteListItem>
|
||||||
with(notesList.first()) {
|
with(notesList.first()) {
|
||||||
assertThat(zimId, equalTo(noteItem.zimId))
|
assertThat(zimId, equalTo(noteItem.zimId))
|
||||||
assertThat(zimUrl, equalTo(noteItem.zimUrl))
|
assertThat(zimUrl, equalTo(noteItem.zimUrl))
|
||||||
@ -78,25 +79,25 @@ class NoteRoomDaoTest {
|
|||||||
|
|
||||||
// Test update the existing note
|
// Test update the existing note
|
||||||
notesRoomDao.saveNote(noteItem)
|
notesRoomDao.saveNote(noteItem)
|
||||||
notesList = notesRoomDao.notes().blockingFirst() as List<NoteListItem>
|
notesList = notesRoomDao.notes().first() as List<NoteListItem>
|
||||||
assertEquals(notesList.size, 1)
|
assertEquals(notesList.size, 1)
|
||||||
|
|
||||||
// Delete the saved note item with all delete methods available in NoteRoomDao.
|
// Delete the saved note item with all delete methods available in NoteRoomDao.
|
||||||
// delete via noteTitle
|
// delete via noteTitle
|
||||||
notesRoomDao.deleteNote(noteItem.title)
|
notesRoomDao.deleteNote(noteItem.title)
|
||||||
notesList = notesRoomDao.notes().blockingFirst() as List<NoteListItem>
|
notesList = notesRoomDao.notes().first() as List<NoteListItem>
|
||||||
assertEquals(notesList.size, 0)
|
assertEquals(notesList.size, 0)
|
||||||
|
|
||||||
// delete with deletePages method
|
// delete with deletePages method
|
||||||
notesRoomDao.saveNote(noteItem)
|
notesRoomDao.saveNote(noteItem)
|
||||||
notesRoomDao.deletePages(listOf(noteItem))
|
notesRoomDao.deletePages(listOf(noteItem))
|
||||||
notesList = notesRoomDao.notes().blockingFirst() as List<NoteListItem>
|
notesList = notesRoomDao.notes().first() as List<NoteListItem>
|
||||||
assertEquals(notesList.size, 0)
|
assertEquals(notesList.size, 0)
|
||||||
|
|
||||||
// delete with list of NoteListItem
|
// delete with list of NoteListItem
|
||||||
notesRoomDao.saveNote(noteItem)
|
notesRoomDao.saveNote(noteItem)
|
||||||
notesRoomDao.deleteNotes(listOf(noteItem))
|
notesRoomDao.deleteNotes(listOf(noteItem))
|
||||||
notesList = notesRoomDao.notes().blockingFirst() as List<NoteListItem>
|
notesList = notesRoomDao.notes().first() as List<NoteListItem>
|
||||||
assertEquals(notesList.size, 0)
|
assertEquals(notesList.size, 0)
|
||||||
|
|
||||||
// Save note with empty title
|
// Save note with empty title
|
||||||
@ -107,7 +108,7 @@ class NoteRoomDaoTest {
|
|||||||
noteFilePath = "/storage/emulated/0/Download/Notes/Alpine linux/Installing.txt"
|
noteFilePath = "/storage/emulated/0/Download/Notes/Alpine linux/Installing.txt"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
notesList = notesRoomDao.notes().blockingFirst() as List<NoteListItem>
|
notesList = notesRoomDao.notes().first() as List<NoteListItem>
|
||||||
assertEquals(notesList.size, 1)
|
assertEquals(notesList.size, 1)
|
||||||
clearNotes()
|
clearNotes()
|
||||||
|
|
||||||
@ -127,7 +128,7 @@ class NoteRoomDaoTest {
|
|||||||
)
|
)
|
||||||
kiwixRoomDatabase.notesRoomDao().saveNote(noteItem2)
|
kiwixRoomDatabase.notesRoomDao().saveNote(noteItem2)
|
||||||
kiwixRoomDatabase.notesRoomDao().saveNote(noteItem3)
|
kiwixRoomDatabase.notesRoomDao().saveNote(noteItem3)
|
||||||
notesList = notesRoomDao.notes().blockingFirst() as List<NoteListItem>
|
notesList = notesRoomDao.notes().first() as List<NoteListItem>
|
||||||
assertEquals(2, notesList.size)
|
assertEquals(2, notesList.size)
|
||||||
clearNotes()
|
clearNotes()
|
||||||
|
|
||||||
@ -139,7 +140,7 @@ class NoteRoomDaoTest {
|
|||||||
"Undefined value was saved into database",
|
"Undefined value was saved into database",
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (_: Exception) {
|
||||||
assertThat("Undefined value was not saved, as expected.", true)
|
assertThat("Undefined value was not saved, as expected.", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,11 +149,11 @@ class NoteRoomDaoTest {
|
|||||||
val noteListItem2 =
|
val noteListItem2 =
|
||||||
getNoteListItem(title = unicodeTitle, zimUrl = "http://kiwix.app/Installing")
|
getNoteListItem(title = unicodeTitle, zimUrl = "http://kiwix.app/Installing")
|
||||||
notesRoomDao.saveNote(noteListItem2)
|
notesRoomDao.saveNote(noteListItem2)
|
||||||
notesList = notesRoomDao.notes().blockingFirst() as List<NoteListItem>
|
notesList = notesRoomDao.notes().first() as List<NoteListItem>
|
||||||
assertThat(notesList.first().title, equalTo("title Σ"))
|
assertThat(notesList.first().title, equalTo("title Σ"))
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun clearNotes() {
|
private suspend fun clearNotes() {
|
||||||
notesRoomDao.deleteNotes(notesRoomDao.notes().blockingFirst() as List<NoteListItem>)
|
notesRoomDao.deleteNotes(notesRoomDao.notes().first() as List<NoteListItem>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,6 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.platform.ComposeView
|
import androidx.compose.ui.platform.ComposeView
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
|
||||||
import org.kiwix.kiwixmobile.cachedComponent
|
import org.kiwix.kiwixmobile.cachedComponent
|
||||||
import org.kiwix.kiwixmobile.core.R
|
import org.kiwix.kiwixmobile.core.R
|
||||||
import org.kiwix.kiwixmobile.core.base.BaseActivity
|
import org.kiwix.kiwixmobile.core.base.BaseActivity
|
||||||
@ -55,7 +54,6 @@ class LanguageFragment : BaseFragment() {
|
|||||||
|
|
||||||
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
|
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||||
private var composeView: ComposeView? = null
|
private var composeView: ComposeView? = null
|
||||||
private val compositeDisposable = CompositeDisposable()
|
|
||||||
|
|
||||||
override fun inject(baseActivity: BaseActivity) {
|
override fun inject(baseActivity: BaseActivity) {
|
||||||
baseActivity.cachedComponent.inject(this)
|
baseActivity.cachedComponent.inject(this)
|
||||||
@ -153,7 +151,6 @@ class LanguageFragment : BaseFragment() {
|
|||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
compositeDisposable.clear()
|
|
||||||
composeView?.disposeComposition()
|
composeView?.disposeComposition()
|
||||||
composeView = null
|
composeView = null
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,9 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
|||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.flow.onEach
|
||||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||||
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
|
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
|
||||||
import org.kiwix.kiwixmobile.language.composables.LanguageListItem.LanguageItem
|
import org.kiwix.kiwixmobile.language.composables.LanguageListItem.LanguageItem
|
||||||
@ -55,21 +56,17 @@ class LanguageViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun observeActions() =
|
private fun observeActions() =
|
||||||
viewModelScope.launch {
|
|
||||||
actions
|
actions
|
||||||
.map { action -> reduce(action, state.value) }
|
.map { action -> reduce(action, state.value) }
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.collect { newState -> state.value = newState }
|
.onEach { newState -> state.value = newState }
|
||||||
}
|
.launchIn(viewModelScope)
|
||||||
|
|
||||||
private fun observeLanguages() =
|
private fun observeLanguages() =
|
||||||
viewModelScope.launch {
|
|
||||||
languageDao.languages()
|
languageDao.languages()
|
||||||
.filter { it.isNotEmpty() }
|
.filter { it.isNotEmpty() }
|
||||||
.collect { languages ->
|
.onEach { languages -> actions.tryEmit(UpdateLanguages(languages)) }
|
||||||
actions.tryEmit(UpdateLanguages(languages))
|
.launchIn(viewModelScope)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
coroutineJobs.forEach {
|
coroutineJobs.forEach {
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.kiwix.kiwixmobile.core.base
|
package org.kiwix.kiwixmobile.core.base
|
||||||
|
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
|
||||||
import org.kiwix.kiwixmobile.core.base.BaseContract.Presenter
|
import org.kiwix.kiwixmobile.core.base.BaseContract.Presenter
|
||||||
import org.kiwix.kiwixmobile.core.base.BaseContract.View
|
import org.kiwix.kiwixmobile.core.base.BaseContract.View
|
||||||
|
|
||||||
@ -26,9 +25,6 @@ import org.kiwix.kiwixmobile.core.base.BaseContract.View
|
|||||||
*/
|
*/
|
||||||
@Suppress("UnnecessaryAbstractClass")
|
@Suppress("UnnecessaryAbstractClass")
|
||||||
abstract class BasePresenter<T : View<*>?> : Presenter<T> {
|
abstract class BasePresenter<T : View<*>?> : Presenter<T> {
|
||||||
@JvmField
|
|
||||||
val compositeDisposable = CompositeDisposable()
|
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
var view: T? = null
|
var view: T? = null
|
||||||
|
|
||||||
@ -38,6 +34,5 @@ abstract class BasePresenter<T : View<*>?> : Presenter<T> {
|
|||||||
|
|
||||||
override fun detachView() {
|
override fun detachView() {
|
||||||
view = null
|
view = null
|
||||||
compositeDisposable.clear()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,8 @@ package org.kiwix.kiwixmobile.core.dao
|
|||||||
import io.objectbox.Box
|
import io.objectbox.Box
|
||||||
import io.objectbox.kotlin.query
|
import io.objectbox.kotlin.query
|
||||||
import io.objectbox.query.QueryBuilder
|
import io.objectbox.query.QueryBuilder
|
||||||
import io.reactivex.Flowable
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import org.kiwix.kiwixmobile.core.dao.entities.HistoryEntity
|
import org.kiwix.kiwixmobile.core.dao.entities.HistoryEntity
|
||||||
import org.kiwix.kiwixmobile.core.dao.entities.HistoryEntity_
|
import org.kiwix.kiwixmobile.core.dao.entities.HistoryEntity_
|
||||||
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
||||||
@ -29,8 +30,8 @@ import org.kiwix.kiwixmobile.core.reader.ZimReaderSource.Companion.fromDatabaseV
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class HistoryDao @Inject constructor(val box: Box<HistoryEntity>) : PageDao {
|
class HistoryDao @Inject constructor(val box: Box<HistoryEntity>) : PageDao {
|
||||||
fun history(): Flowable<List<Page>> =
|
fun history(): Flow<List<Page>> =
|
||||||
box.asFlowable(
|
box.asFlow(
|
||||||
box.query {
|
box.query {
|
||||||
orderDesc(HistoryEntity_.timeStamp)
|
orderDesc(HistoryEntity_.timeStamp)
|
||||||
}
|
}
|
||||||
@ -46,7 +47,7 @@ class HistoryDao @Inject constructor(val box: Box<HistoryEntity>) : PageDao {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pages(): Flowable<List<Page>> = history()
|
override fun pages(): Flow<List<Page>> = history()
|
||||||
override fun deletePages(pagesToDelete: List<Page>) =
|
override fun deletePages(pagesToDelete: List<Page>) =
|
||||||
deleteHistory(pagesToDelete as List<HistoryItem>)
|
deleteHistory(pagesToDelete as List<HistoryItem>)
|
||||||
|
|
||||||
|
@ -24,7 +24,8 @@ import androidx.room.Insert
|
|||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import androidx.room.TypeConverter
|
import androidx.room.TypeConverter
|
||||||
import androidx.room.Update
|
import androidx.room.Update
|
||||||
import io.reactivex.Flowable
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import org.kiwix.kiwixmobile.core.dao.entities.HistoryRoomEntity
|
import org.kiwix.kiwixmobile.core.dao.entities.HistoryRoomEntity
|
||||||
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
||||||
import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem
|
import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem
|
||||||
@ -33,9 +34,9 @@ import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
|
|||||||
@Dao
|
@Dao
|
||||||
abstract class HistoryRoomDao : PageDao {
|
abstract class HistoryRoomDao : PageDao {
|
||||||
@Query("SELECT * FROM HistoryRoomEntity ORDER BY HistoryRoomEntity.timeStamp DESC")
|
@Query("SELECT * FROM HistoryRoomEntity ORDER BY HistoryRoomEntity.timeStamp DESC")
|
||||||
abstract fun historyRoomEntity(): Flowable<List<HistoryRoomEntity>>
|
abstract fun historyRoomEntity(): Flow<List<HistoryRoomEntity>>
|
||||||
|
|
||||||
fun history(): Flowable<List<Page>> =
|
fun history(): Flow<List<Page>> =
|
||||||
historyRoomEntity().map {
|
historyRoomEntity().map {
|
||||||
it.map { historyEntity ->
|
it.map { historyEntity ->
|
||||||
historyEntity.zimFilePath?.let { filePath ->
|
historyEntity.zimFilePath?.let { filePath ->
|
||||||
|
@ -21,7 +21,6 @@ package org.kiwix.kiwixmobile.core.dao
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import io.reactivex.Flowable
|
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -30,7 +29,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.reactive.asPublisher
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.kiwix.kiwixmobile.core.CoreApp
|
import org.kiwix.kiwixmobile.core.CoreApp
|
||||||
@ -121,10 +119,7 @@ class LibkiwixBookmarks @Inject constructor(
|
|||||||
bookmarkListFlow
|
bookmarkListFlow
|
||||||
.map { it }
|
.map { it }
|
||||||
|
|
||||||
// Currently kept in RxJava Flowable because `PageViewModel` still expects RxJava streams.
|
override fun pages(): Flow<List<Page>> = bookmarks()
|
||||||
// This can be refactored to use Kotlin Flow once `PageViewModel` is migrated to coroutines.
|
|
||||||
override fun pages(): Flowable<List<Page>> =
|
|
||||||
Flowable.fromPublisher(bookmarks().asPublisher())
|
|
||||||
|
|
||||||
override fun deletePages(pagesToDelete: List<Page>) =
|
override fun deletePages(pagesToDelete: List<Page>) =
|
||||||
deleteBookmarks(pagesToDelete as List<LibkiwixBookmarkItem>)
|
deleteBookmarks(pagesToDelete as List<LibkiwixBookmarkItem>)
|
||||||
|
@ -20,8 +20,11 @@ package org.kiwix.kiwixmobile.core.dao
|
|||||||
import io.objectbox.Box
|
import io.objectbox.Box
|
||||||
import io.objectbox.kotlin.query
|
import io.objectbox.kotlin.query
|
||||||
import io.objectbox.query.QueryBuilder
|
import io.objectbox.query.QueryBuilder
|
||||||
import io.reactivex.Flowable
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import io.reactivex.schedulers.Schedulers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity
|
import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity
|
||||||
import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity_
|
import org.kiwix.kiwixmobile.core.dao.entities.BookmarkEntity_
|
||||||
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
||||||
@ -31,8 +34,8 @@ import org.kiwix.kiwixmobile.core.reader.ZimReaderSource.Companion.fromDatabaseV
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NewBookmarksDao @Inject constructor(val box: Box<BookmarkEntity>) : PageDao {
|
class NewBookmarksDao @Inject constructor(val box: Box<BookmarkEntity>) : PageDao {
|
||||||
fun bookmarks(): Flowable<List<Page>> =
|
fun bookmarks(): Flow<List<Page>> =
|
||||||
box.asFlowable(
|
box.asFlow(
|
||||||
box.query {
|
box.query {
|
||||||
order(BookmarkEntity_.bookmarkTitle)
|
order(BookmarkEntity_.bookmarkTitle)
|
||||||
}
|
}
|
||||||
@ -48,7 +51,7 @@ class NewBookmarksDao @Inject constructor(val box: Box<BookmarkEntity>) : PageDa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pages(): Flowable<List<Page>> = bookmarks()
|
override fun pages(): Flow<List<Page>> = bookmarks()
|
||||||
override fun deletePages(pagesToDelete: List<Page>) =
|
override fun deletePages(pagesToDelete: List<Page>) =
|
||||||
deleteBookmarks(pagesToDelete as List<BookmarkItem>)
|
deleteBookmarks(pagesToDelete as List<BookmarkItem>)
|
||||||
|
|
||||||
@ -71,8 +74,11 @@ class NewBookmarksDao @Inject constructor(val box: Box<BookmarkEntity>) : PageDa
|
|||||||
.toList()
|
.toList()
|
||||||
.distinct()
|
.distinct()
|
||||||
|
|
||||||
fun bookmarkUrlsForCurrentBook(zimFileReader: ZimFileReader?): Flowable<List<String>> =
|
fun bookmarkUrlsForCurrentBook(
|
||||||
box.asFlowable(
|
zimFileReader: ZimFileReader?,
|
||||||
|
dispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||||
|
): Flow<List<String>> =
|
||||||
|
box.asFlow(
|
||||||
box.query {
|
box.query {
|
||||||
equal(
|
equal(
|
||||||
BookmarkEntity_.zimId,
|
BookmarkEntity_.zimId,
|
||||||
@ -88,7 +94,7 @@ class NewBookmarksDao @Inject constructor(val box: Box<BookmarkEntity>) : PageDa
|
|||||||
order(BookmarkEntity_.bookmarkTitle)
|
order(BookmarkEntity_.bookmarkTitle)
|
||||||
}
|
}
|
||||||
).map { it.map(BookmarkEntity::bookmarkUrl) }
|
).map { it.map(BookmarkEntity::bookmarkUrl) }
|
||||||
.subscribeOn(Schedulers.io())
|
.flowOn(dispatcher)
|
||||||
|
|
||||||
fun saveBookmark(bookmarkItem: BookmarkItem) {
|
fun saveBookmark(bookmarkItem: BookmarkItem) {
|
||||||
box.put(BookmarkEntity(bookmarkItem))
|
box.put(BookmarkEntity(bookmarkItem))
|
||||||
|
@ -21,9 +21,6 @@ import io.objectbox.Box
|
|||||||
import io.objectbox.kotlin.flow
|
import io.objectbox.kotlin.flow
|
||||||
import io.objectbox.kotlin.query
|
import io.objectbox.kotlin.query
|
||||||
import io.objectbox.query.Query
|
import io.objectbox.query.Query
|
||||||
import io.objectbox.rx.RxQuery
|
|
||||||
import io.reactivex.BackpressureStrategy
|
|
||||||
import io.reactivex.BackpressureStrategy.LATEST
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
@ -53,9 +50,3 @@ fun <T> Box<T>.asFlow(query: Query<T> = query {}): Flow<List<T>> {
|
|||||||
.map { it.toList() }
|
.map { it.toList() }
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun <T> Box<T>.asFlowable(
|
|
||||||
query: Query<T> = query {},
|
|
||||||
backpressureStrategy: BackpressureStrategy = LATEST
|
|
||||||
) =
|
|
||||||
RxQuery.observable(query).toFlowable(backpressureStrategy)
|
|
||||||
|
@ -21,7 +21,8 @@ package org.kiwix.kiwixmobile.core.dao
|
|||||||
import io.objectbox.Box
|
import io.objectbox.Box
|
||||||
import io.objectbox.kotlin.query
|
import io.objectbox.kotlin.query
|
||||||
import io.objectbox.query.QueryBuilder
|
import io.objectbox.query.QueryBuilder
|
||||||
import io.reactivex.Flowable
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import org.kiwix.kiwixmobile.core.dao.entities.NotesEntity
|
import org.kiwix.kiwixmobile.core.dao.entities.NotesEntity
|
||||||
import org.kiwix.kiwixmobile.core.dao.entities.NotesEntity_
|
import org.kiwix.kiwixmobile.core.dao.entities.NotesEntity_
|
||||||
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
||||||
@ -30,8 +31,8 @@ import org.kiwix.kiwixmobile.core.reader.ZimReaderSource.Companion.fromDatabaseV
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NewNoteDao @Inject constructor(val box: Box<NotesEntity>) : PageDao {
|
class NewNoteDao @Inject constructor(val box: Box<NotesEntity>) : PageDao {
|
||||||
fun notes(): Flowable<List<Page>> =
|
fun notes(): Flow<List<Page>> =
|
||||||
box.asFlowable(
|
box.asFlow(
|
||||||
box.query {
|
box.query {
|
||||||
order(NotesEntity_.noteTitle)
|
order(NotesEntity_.noteTitle)
|
||||||
}
|
}
|
||||||
@ -47,7 +48,7 @@ class NewNoteDao @Inject constructor(val box: Box<NotesEntity>) : PageDao {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pages(): Flowable<List<Page>> = notes()
|
override fun pages(): Flow<List<Page>> = notes()
|
||||||
|
|
||||||
override fun deletePages(pagesToDelete: List<Page>) =
|
override fun deletePages(pagesToDelete: List<Page>) =
|
||||||
deleteNotes(pagesToDelete as List<NoteListItem>)
|
deleteNotes(pagesToDelete as List<NoteListItem>)
|
||||||
|
@ -22,10 +22,11 @@ import androidx.room.Dao
|
|||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.OnConflictStrategy
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import io.reactivex.Flowable
|
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.kiwix.kiwixmobile.core.dao.entities.NotesRoomEntity
|
import org.kiwix.kiwixmobile.core.dao.entities.NotesRoomEntity
|
||||||
import org.kiwix.kiwixmobile.core.extensions.deleteFile
|
import org.kiwix.kiwixmobile.core.extensions.deleteFile
|
||||||
@ -38,9 +39,9 @@ import java.io.File
|
|||||||
@Dao
|
@Dao
|
||||||
abstract class NotesRoomDao : PageDao {
|
abstract class NotesRoomDao : PageDao {
|
||||||
@Query("SELECT * FROM NotesRoomEntity ORDER BY NotesRoomEntity.noteTitle")
|
@Query("SELECT * FROM NotesRoomEntity ORDER BY NotesRoomEntity.noteTitle")
|
||||||
abstract fun notesAsEntity(): Flowable<List<NotesRoomEntity>>
|
abstract fun notesAsEntity(): Flow<List<NotesRoomEntity>>
|
||||||
|
|
||||||
fun notes(): Flowable<List<Page>> =
|
fun notes(): Flow<List<Page>> =
|
||||||
notesAsEntity().map {
|
notesAsEntity().map {
|
||||||
it.map { notesEntity ->
|
it.map { notesEntity ->
|
||||||
notesEntity.zimFilePath?.let { filePath ->
|
notesEntity.zimFilePath?.let { filePath ->
|
||||||
@ -53,7 +54,7 @@ abstract class NotesRoomDao : PageDao {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pages(): Flowable<List<Page>> = notes()
|
override fun pages(): Flow<List<Page>> = notes()
|
||||||
override fun deletePages(pagesToDelete: List<Page>) =
|
override fun deletePages(pagesToDelete: List<Page>) =
|
||||||
deleteNotes(pagesToDelete as List<NoteListItem>)
|
deleteNotes(pagesToDelete as List<NoteListItem>)
|
||||||
|
|
||||||
|
@ -18,10 +18,10 @@
|
|||||||
|
|
||||||
package org.kiwix.kiwixmobile.core.dao
|
package org.kiwix.kiwixmobile.core.dao
|
||||||
|
|
||||||
import io.reactivex.Flowable
|
import kotlinx.coroutines.flow.Flow
|
||||||
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
||||||
|
|
||||||
interface PageDao {
|
interface PageDao {
|
||||||
fun pages(): Flowable<List<Page>>
|
fun pages(): Flow<List<Page>>
|
||||||
fun deletePages(pagesToDelete: List<Page>)
|
fun deletePages(pagesToDelete: List<Page>)
|
||||||
}
|
}
|
||||||
|
@ -32,13 +32,16 @@ import androidx.compose.runtime.MutableState
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.referentialEqualityPolicy
|
import androidx.compose.runtime.referentialEqualityPolicy
|
||||||
import androidx.compose.ui.platform.ComposeView
|
import androidx.compose.ui.platform.ComposeView
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import org.kiwix.kiwixmobile.core.R
|
import org.kiwix.kiwixmobile.core.R
|
||||||
import org.kiwix.kiwixmobile.core.base.BaseFragment
|
import org.kiwix.kiwixmobile.core.base.BaseFragment
|
||||||
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
|
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
|
||||||
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
|
||||||
|
import org.kiwix.kiwixmobile.core.extensions.update
|
||||||
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
|
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
|
||||||
import org.kiwix.kiwixmobile.core.page.adapter.OnItemClickListener
|
import org.kiwix.kiwixmobile.core.page.adapter.OnItemClickListener
|
||||||
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
||||||
@ -66,7 +69,7 @@ abstract class PageFragment : OnItemClickListener, BaseFragment(), FragmentActiv
|
|||||||
|
|
||||||
@Inject lateinit var alertDialogShower: AlertDialogShower
|
@Inject lateinit var alertDialogShower: AlertDialogShower
|
||||||
private var actionMode: ActionMode? = null
|
private var actionMode: ActionMode? = null
|
||||||
val compositeDisposable = CompositeDisposable()
|
private val coroutineJobs = mutableListOf<Job>()
|
||||||
abstract val screenTitle: Int
|
abstract val screenTitle: Int
|
||||||
abstract val noItemsString: String
|
abstract val noItemsString: String
|
||||||
abstract val switchString: String
|
abstract val switchString: String
|
||||||
@ -116,36 +119,51 @@ abstract class PageFragment : OnItemClickListener, BaseFragment(), FragmentActiv
|
|||||||
|
|
||||||
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
|
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
|
||||||
if (item.itemId == R.id.menu_context_delete) {
|
if (item.itemId == R.id.menu_context_delete) {
|
||||||
pageViewModel.actions.offer(Action.UserClickedDeleteSelectedPages)
|
pageViewModel.actions.tryEmit(Action.UserClickedDeleteSelectedPages)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
pageViewModel.actions.offer(Action.ExitActionModeMenu)
|
pageViewModel.actions.tryEmit(Action.ExitActionModeMenu)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyActionMode(mode: ActionMode) {
|
override fun onDestroyActionMode(mode: ActionMode) {
|
||||||
pageViewModel.actions.offer(Action.ExitActionModeMenu)
|
pageViewModel.actions.tryEmit(Action.ExitActionModeMenu)
|
||||||
actionMode = null
|
actionMode = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
pageScreenState.value = pageScreenState.value.copy(
|
pageScreenState.update {
|
||||||
searchQueryHint = searchQueryHint,
|
copy(
|
||||||
|
searchQueryHint = this@PageFragment.searchQueryHint,
|
||||||
searchText = "",
|
searchText = "",
|
||||||
searchValueChangedListener = { onTextChanged(it) },
|
searchValueChangedListener = { onTextChanged(it) },
|
||||||
clearSearchButtonClickListener = { onTextChanged("") },
|
clearSearchButtonClickListener = { onTextChanged("") },
|
||||||
screenTitle = screenTitle,
|
screenTitle = this@PageFragment.screenTitle,
|
||||||
noItemsString = noItemsString,
|
noItemsString = this@PageFragment.noItemsString,
|
||||||
switchString = switchString,
|
switchString = this@PageFragment.switchString,
|
||||||
switchIsChecked = switchIsChecked,
|
switchIsChecked = this@PageFragment.switchIsChecked,
|
||||||
onSwitchCheckedChanged = { onSwitchCheckedChanged(it).invoke() },
|
onSwitchCheckedChanged = { onSwitchChanged(it).invoke() },
|
||||||
deleteIconTitle = deleteIconTitle
|
deleteIconTitle = this@PageFragment.deleteIconTitle
|
||||||
)
|
)
|
||||||
|
}
|
||||||
val activity = requireActivity() as CoreMainActivity
|
val activity = requireActivity() as CoreMainActivity
|
||||||
compositeDisposable.add(pageViewModel.effects.subscribe { it.invokeWith(activity) })
|
cancelCoroutineJobs()
|
||||||
pageViewModel.state.observe(viewLifecycleOwner, Observer(::render))
|
coroutineJobs.apply {
|
||||||
|
add(
|
||||||
|
pageViewModel
|
||||||
|
.effects
|
||||||
|
.onEach { it.invokeWith(activity) }
|
||||||
|
.launchIn(lifecycleScope)
|
||||||
|
)
|
||||||
|
add(
|
||||||
|
pageViewModel
|
||||||
|
.state
|
||||||
|
.onEach { render(it) }
|
||||||
|
.launchIn(lifecycleScope)
|
||||||
|
)
|
||||||
|
}
|
||||||
pageViewModel.alertDialogShower = alertDialogShower
|
pageViewModel.alertDialogShower = alertDialogShower
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,9 +186,9 @@ abstract class PageFragment : OnItemClickListener, BaseFragment(), FragmentActiv
|
|||||||
isSearchActive = pageScreenState.value.isSearchActive,
|
isSearchActive = pageScreenState.value.isSearchActive,
|
||||||
onSearchClick = {
|
onSearchClick = {
|
||||||
// Set the `isSearchActive` when the search button is clicked.
|
// Set the `isSearchActive` when the search button is clicked.
|
||||||
pageScreenState.value = pageScreenState.value.copy(isSearchActive = true)
|
pageScreenState.update { copy(isSearchActive = true) }
|
||||||
},
|
},
|
||||||
onDeleteClick = { pageViewModel.actions.offer(Action.UserClickedDeleteButton) }
|
onDeleteClick = { pageViewModel.actions.tryEmit(Action.UserClickedDeleteButton) }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
DialogHost(alertDialogShower)
|
DialogHost(alertDialogShower)
|
||||||
@ -186,8 +204,8 @@ abstract class PageFragment : OnItemClickListener, BaseFragment(), FragmentActiv
|
|||||||
* @param searchText The current text entered in the search bar.
|
* @param searchText The current text entered in the search bar.
|
||||||
*/
|
*/
|
||||||
private fun onTextChanged(searchText: String) {
|
private fun onTextChanged(searchText: String) {
|
||||||
pageScreenState.value = pageScreenState.value.copy(searchText = searchText)
|
pageScreenState.update { copy(searchText = searchText) }
|
||||||
pageViewModel.actions.offer(Action.Filter(searchText))
|
pageViewModel.actions.tryEmit(Action.Filter(searchText))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -197,9 +215,9 @@ abstract class PageFragment : OnItemClickListener, BaseFragment(), FragmentActiv
|
|||||||
*
|
*
|
||||||
* @param isChecked The new checked state of the switch.
|
* @param isChecked The new checked state of the switch.
|
||||||
*/
|
*/
|
||||||
private fun onSwitchCheckedChanged(isChecked: Boolean): () -> Unit = {
|
private fun onSwitchChanged(isChecked: Boolean): () -> Unit = {
|
||||||
pageScreenState.value = pageScreenState.value.copy(switchIsChecked = isChecked)
|
pageScreenState.update { copy(switchIsChecked = isChecked) }
|
||||||
pageViewModel.actions.offer(Action.UserClickedShowAllToggle(isChecked))
|
pageViewModel.actions.tryEmit(Action.UserClickedShowAllToggle(isChecked))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -209,7 +227,7 @@ abstract class PageFragment : OnItemClickListener, BaseFragment(), FragmentActiv
|
|||||||
*/
|
*/
|
||||||
private fun navigationIconClick(): () -> Unit = {
|
private fun navigationIconClick(): () -> Unit = {
|
||||||
if (pageScreenState.value.isSearchActive) {
|
if (pageScreenState.value.isSearchActive) {
|
||||||
pageScreenState.value = pageScreenState.value.copy(isSearchActive = false)
|
pageScreenState.update { copy(isSearchActive = false) }
|
||||||
onTextChanged("")
|
onTextChanged("")
|
||||||
} else {
|
} else {
|
||||||
requireActivity().onBackPressedDispatcher.onBackPressed()
|
requireActivity().onBackPressedDispatcher.onBackPressed()
|
||||||
@ -255,21 +273,20 @@ abstract class PageFragment : OnItemClickListener, BaseFragment(), FragmentActiv
|
|||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
compositeDisposable.clear()
|
cancelCoroutineJobs()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cancelCoroutineJobs() {
|
||||||
|
coroutineJobs.forEach {
|
||||||
|
it.cancel()
|
||||||
|
}
|
||||||
|
coroutineJobs.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun render(state: PageState<*>) {
|
private fun render(state: PageState<*>) {
|
||||||
pageScreenState.value = pageScreenState.value.copy(
|
pageScreenState.update {
|
||||||
switchIsEnabled = !state.isInSelectionState,
|
copy(switchIsEnabled = !state.isInSelectionState, pageState = state)
|
||||||
// First, assign the existing state to force Compose to recognize a change.
|
}
|
||||||
// This helps when internal properties of items (like `isSelected`) change,
|
|
||||||
// but the list reference itself remains the same — Compose won't detect it otherwise.
|
|
||||||
pageState = pageState.value
|
|
||||||
)
|
|
||||||
// Then, assign the actual updated state to trigger full recomposition.
|
|
||||||
pageScreenState.value = pageScreenState.value.copy(
|
|
||||||
pageState = state
|
|
||||||
)
|
|
||||||
if (state.isInSelectionState) {
|
if (state.isInSelectionState) {
|
||||||
if (actionMode == null) {
|
if (actionMode == null) {
|
||||||
actionMode =
|
actionMode =
|
||||||
@ -282,9 +299,9 @@ abstract class PageFragment : OnItemClickListener, BaseFragment(), FragmentActiv
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemClick(page: Page) {
|
override fun onItemClick(page: Page) {
|
||||||
pageViewModel.actions.offer(Action.OnItemClick(page))
|
pageViewModel.actions.tryEmit(Action.OnItemClick(page))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemLongClick(page: Page): Boolean =
|
override fun onItemLongClick(page: Page): Boolean =
|
||||||
pageViewModel.actions.offer(Action.OnItemLongClick(page))
|
pageViewModel.actions.tryEmit(Action.OnItemLongClick(page))
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ class BookmarkViewModel @Inject constructor(
|
|||||||
action: Action.UserClickedShowAllToggle,
|
action: Action.UserClickedShowAllToggle,
|
||||||
state: BookmarkState
|
state: BookmarkState
|
||||||
): BookmarkState {
|
): BookmarkState {
|
||||||
effects.offer(UpdateAllBookmarksPreference(sharedPreferenceUtil, action.isChecked))
|
effects.tryEmit(UpdateAllBookmarksPreference(sharedPreferenceUtil, action.isChecked))
|
||||||
return state.copy(showAll = action.isChecked)
|
return state.copy(showAll = action.isChecked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,8 +19,8 @@ package org.kiwix.kiwixmobile.core.page.bookmark.viewmodel.effects
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import io.reactivex.processors.PublishProcessor
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||||
import org.kiwix.kiwixmobile.core.dao.PageDao
|
import org.kiwix.kiwixmobile.core.dao.PageDao
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent
|
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent
|
||||||
@ -32,7 +32,7 @@ import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.DeleteAllBookmarks
|
|||||||
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.DeleteSelectedBookmarks
|
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.DeleteSelectedBookmarks
|
||||||
|
|
||||||
data class ShowDeleteBookmarksDialog(
|
data class ShowDeleteBookmarksDialog(
|
||||||
private val effects: PublishProcessor<SideEffect<*>>,
|
private val effects: MutableSharedFlow<SideEffect<*>>,
|
||||||
private val state: PageState<LibkiwixBookmarkItem>,
|
private val state: PageState<LibkiwixBookmarkItem>,
|
||||||
private val pageDao: PageDao,
|
private val pageDao: PageDao,
|
||||||
private val viewModelScope: CoroutineScope,
|
private val viewModelScope: CoroutineScope,
|
||||||
@ -42,7 +42,7 @@ data class ShowDeleteBookmarksDialog(
|
|||||||
activity.cachedComponent.inject(this)
|
activity.cachedComponent.inject(this)
|
||||||
dialogShower.show(
|
dialogShower.show(
|
||||||
if (state.isInSelectionState) DeleteSelectedBookmarks else DeleteAllBookmarks,
|
if (state.isInSelectionState) DeleteSelectedBookmarks else DeleteAllBookmarks,
|
||||||
{ effects.offer(DeletePageItems(state, pageDao, viewModelScope)) }
|
{ effects.tryEmit(DeletePageItems(state, pageDao, viewModelScope)) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ class HistoryViewModel @Inject constructor(
|
|||||||
action: Action.UserClickedShowAllToggle,
|
action: Action.UserClickedShowAllToggle,
|
||||||
state: HistoryState
|
state: HistoryState
|
||||||
): HistoryState {
|
): HistoryState {
|
||||||
effects.offer(UpdateAllHistoryPreference(sharedPreferenceUtil, action.isChecked))
|
effects.tryEmit(UpdateAllHistoryPreference(sharedPreferenceUtil, action.isChecked))
|
||||||
return state.copy(showAll = action.isChecked)
|
return state.copy(showAll = action.isChecked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,8 +19,8 @@ package org.kiwix.kiwixmobile.core.page.history.viewmodel.effects
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import io.reactivex.processors.PublishProcessor
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||||
import org.kiwix.kiwixmobile.core.dao.PageDao
|
import org.kiwix.kiwixmobile.core.dao.PageDao
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent
|
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent
|
||||||
@ -31,7 +31,7 @@ import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.DeleteAllHistory
|
|||||||
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.DeleteSelectedHistory
|
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.DeleteSelectedHistory
|
||||||
|
|
||||||
data class ShowDeleteHistoryDialog(
|
data class ShowDeleteHistoryDialog(
|
||||||
private val effects: PublishProcessor<SideEffect<*>>,
|
private val effects: MutableSharedFlow<SideEffect<*>>,
|
||||||
private val state: HistoryState,
|
private val state: HistoryState,
|
||||||
private val pageDao: PageDao,
|
private val pageDao: PageDao,
|
||||||
private val viewModelScope: CoroutineScope,
|
private val viewModelScope: CoroutineScope,
|
||||||
@ -40,7 +40,7 @@ data class ShowDeleteHistoryDialog(
|
|||||||
override fun invokeWith(activity: AppCompatActivity) {
|
override fun invokeWith(activity: AppCompatActivity) {
|
||||||
activity.cachedComponent.inject(this)
|
activity.cachedComponent.inject(this)
|
||||||
dialogShower.show(if (state.isInSelectionState) DeleteSelectedHistory else DeleteAllHistory, {
|
dialogShower.show(if (state.isInSelectionState) DeleteSelectedHistory else DeleteAllHistory, {
|
||||||
effects.offer(DeletePageItems(state, pageDao, viewModelScope))
|
effects.tryEmit(DeletePageItems(state, pageDao, viewModelScope))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ class NotesViewModel @Inject constructor(
|
|||||||
action: Action.UserClickedShowAllToggle,
|
action: Action.UserClickedShowAllToggle,
|
||||||
state: NotesState
|
state: NotesState
|
||||||
): NotesState {
|
): NotesState {
|
||||||
effects.offer(UpdateAllNotesPreference(sharedPreferenceUtil, action.isChecked))
|
effects.tryEmit(UpdateAllNotesPreference(sharedPreferenceUtil, action.isChecked))
|
||||||
return state.copy(showAll = action.isChecked)
|
return state.copy(showAll = action.isChecked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,8 +19,8 @@ package org.kiwix.kiwixmobile.core.page.notes.viewmodel.effects
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import io.reactivex.processors.PublishProcessor
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||||
import org.kiwix.kiwixmobile.core.dao.PageDao
|
import org.kiwix.kiwixmobile.core.dao.PageDao
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent
|
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent
|
||||||
@ -31,7 +31,7 @@ import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.DeleteAllNotes
|
|||||||
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.DeleteSelectedNotes
|
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.DeleteSelectedNotes
|
||||||
|
|
||||||
data class ShowDeleteNotesDialog(
|
data class ShowDeleteNotesDialog(
|
||||||
private val effects: PublishProcessor<SideEffect<*>>,
|
private val effects: MutableSharedFlow<SideEffect<*>>,
|
||||||
private val state: NotesState,
|
private val state: NotesState,
|
||||||
private val pageDao: PageDao,
|
private val pageDao: PageDao,
|
||||||
private val viewModelScope: CoroutineScope,
|
private val viewModelScope: CoroutineScope,
|
||||||
@ -42,7 +42,7 @@ data class ShowDeleteNotesDialog(
|
|||||||
dialogShower.show(
|
dialogShower.show(
|
||||||
if (state.isInSelectionState) DeleteSelectedNotes else DeleteAllNotes,
|
if (state.isInSelectionState) DeleteSelectedNotes else DeleteAllNotes,
|
||||||
{
|
{
|
||||||
effects.offer(DeletePageItems(state, pageDao, viewModelScope))
|
effects.tryEmit(DeletePageItems(state, pageDao, viewModelScope))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
package org.kiwix.kiwixmobile.core.page.notes.viewmodel.effects
|
package org.kiwix.kiwixmobile.core.page.notes.viewmodel.effects
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import io.reactivex.processors.PublishProcessor
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent
|
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.cachedComponent
|
||||||
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
||||||
@ -31,7 +31,7 @@ import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower
|
|||||||
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.ShowNoteDialog
|
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.ShowNoteDialog
|
||||||
|
|
||||||
data class ShowOpenNoteDialog(
|
data class ShowOpenNoteDialog(
|
||||||
private val effects: PublishProcessor<SideEffect<*>>,
|
private val effects: MutableSharedFlow<SideEffect<*>>,
|
||||||
private val page: Page,
|
private val page: Page,
|
||||||
private val zimReaderContainer: ZimReaderContainer,
|
private val zimReaderContainer: ZimReaderContainer,
|
||||||
private val dialogShower: DialogShower
|
private val dialogShower: DialogShower
|
||||||
@ -40,10 +40,10 @@ data class ShowOpenNoteDialog(
|
|||||||
activity.cachedComponent.inject(this)
|
activity.cachedComponent.inject(this)
|
||||||
dialogShower.show(
|
dialogShower.show(
|
||||||
ShowNoteDialog,
|
ShowNoteDialog,
|
||||||
{ effects.offer(OpenPage(page, zimReaderContainer)) },
|
{ effects.tryEmit(OpenPage(page, zimReaderContainer)) },
|
||||||
{
|
{
|
||||||
val item = page as NoteListItem
|
val item = page as NoteListItem
|
||||||
effects.offer(OpenNote(item))
|
effects.tryEmit(OpenNote(item))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,8 @@ package org.kiwix.kiwixmobile.core.page.viewmodel
|
|||||||
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
||||||
import org.kiwix.kiwixmobile.core.page.adapter.PageRelated
|
import org.kiwix.kiwixmobile.core.page.adapter.PageRelated
|
||||||
import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem
|
import org.kiwix.kiwixmobile.core.page.bookmark.adapter.LibkiwixBookmarkItem
|
||||||
|
import org.kiwix.kiwixmobile.core.page.history.adapter.HistoryListItem.HistoryItem
|
||||||
|
import org.kiwix.kiwixmobile.core.page.notes.adapter.NoteListItem
|
||||||
|
|
||||||
abstract class PageState<T : Page> {
|
abstract class PageState<T : Page> {
|
||||||
abstract val pageItems: List<T>
|
abstract val pageItems: List<T>
|
||||||
@ -43,8 +45,12 @@ abstract class PageState<T : Page> {
|
|||||||
val currentItemIdentifier = if (it is LibkiwixBookmarkItem) it.url else it.id
|
val currentItemIdentifier = if (it is LibkiwixBookmarkItem) it.url else it.id
|
||||||
val pageIdentifier = if (it is LibkiwixBookmarkItem) page.url else page.id
|
val pageIdentifier = if (it is LibkiwixBookmarkItem) page.url else page.id
|
||||||
if (currentItemIdentifier == pageIdentifier) {
|
if (currentItemIdentifier == pageIdentifier) {
|
||||||
it.apply {
|
when (it) {
|
||||||
isSelected = !isSelected
|
is LibkiwixBookmarkItem -> it.copy(isSelected = !it.isSelected) as T
|
||||||
|
is HistoryItem -> it.copy(isSelected = !it.isSelected) as T
|
||||||
|
is NoteListItem -> it.copy(isSelected = !it.isSelected) as T
|
||||||
|
// For test cases only.
|
||||||
|
else -> it.apply { isSelected = !isSelected }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
it
|
it
|
||||||
|
@ -18,14 +18,21 @@
|
|||||||
|
|
||||||
package org.kiwix.kiwixmobile.core.page.viewmodel
|
package org.kiwix.kiwixmobile.core.page.viewmodel
|
||||||
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import io.reactivex.disposables.Disposable
|
|
||||||
import io.reactivex.processors.PublishProcessor
|
|
||||||
import io.reactivex.schedulers.Schedulers
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import org.jetbrains.annotations.VisibleForTesting
|
||||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||||
import org.kiwix.kiwixmobile.core.dao.PageDao
|
import org.kiwix.kiwixmobile.core.dao.PageDao
|
||||||
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
||||||
@ -54,32 +61,32 @@ abstract class PageViewModel<T : Page, S : PageState<T>>(
|
|||||||
lateinit var alertDialogShower: AlertDialogShower
|
lateinit var alertDialogShower: AlertDialogShower
|
||||||
|
|
||||||
private lateinit var pageViewModelClickListener: PageViewModelClickListener
|
private lateinit var pageViewModelClickListener: PageViewModelClickListener
|
||||||
|
private val _state = MutableStateFlow(initialState())
|
||||||
|
val state: StateFlow<S> = _state.asStateFlow()
|
||||||
|
val effects = MutableSharedFlow<SideEffect<*>>(extraBufferCapacity = Int.MAX_VALUE)
|
||||||
|
val actions = MutableSharedFlow<Action>(extraBufferCapacity = Int.MAX_VALUE)
|
||||||
|
private val coroutineJobs = mutableListOf<Job>()
|
||||||
|
|
||||||
val state: MutableLiveData<S> by lazy {
|
@VisibleForTesting
|
||||||
MutableLiveData<S>().apply {
|
fun getMutableStateForTestCases() = _state
|
||||||
value = initialState()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val compositeDisposable = CompositeDisposable()
|
|
||||||
val effects = PublishProcessor.create<SideEffect<*>>()
|
|
||||||
val actions = PublishProcessor.create<Action>()
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
addDisposablesToCompositeDisposable()
|
coroutineJobs.apply {
|
||||||
|
add(observeActions())
|
||||||
|
add(observePages())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun viewStateReducer(): Disposable =
|
private fun observeActions() =
|
||||||
actions.map { state.value?.let { value -> reduce(it, value) } }
|
actions.map { action -> reduce(action, state.value) }
|
||||||
.subscribe(state::postValue, Throwable::printStackTrace)
|
.onEach { newState -> _state.value = newState }
|
||||||
|
.launchIn(viewModelScope)
|
||||||
|
|
||||||
protected fun addDisposablesToCompositeDisposable() {
|
private fun observePages(dispatcher: CoroutineDispatcher = Dispatchers.IO) =
|
||||||
compositeDisposable.addAll(
|
pageDao.pages()
|
||||||
viewStateReducer(),
|
.flowOn(dispatcher)
|
||||||
pageDao.pages().subscribeOn(Schedulers.io())
|
.onEach { actions.tryEmit(UpdatePages(it)) }
|
||||||
.subscribe({ actions.offer(UpdatePages(it)) }, Throwable::printStackTrace)
|
.launchIn(viewModelScope)
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun reduce(action: Action, state: S): S =
|
private fun reduce(action: Action, state: S): S =
|
||||||
when (action) {
|
when (action) {
|
||||||
@ -103,7 +110,7 @@ abstract class PageViewModel<T : Page, S : PageState<T>>(
|
|||||||
): S
|
): S
|
||||||
|
|
||||||
private fun offerShowDeleteDialog(state: S): S {
|
private fun offerShowDeleteDialog(state: S): S {
|
||||||
effects.offer(createDeletePageDialogEffect(state, viewModelScope = viewModelScope))
|
effects.tryEmit(createDeletePageDialogEffect(state, viewModelScope = viewModelScope))
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,9 +124,9 @@ abstract class PageViewModel<T : Page, S : PageState<T>>(
|
|||||||
return copyWithNewItems(state, state.getItemsAfterToggleSelectionOfItem(action.page))
|
return copyWithNewItems(state, state.getItemsAfterToggleSelectionOfItem(action.page))
|
||||||
}
|
}
|
||||||
if (::pageViewModelClickListener.isInitialized) {
|
if (::pageViewModelClickListener.isInitialized) {
|
||||||
effects.offer(pageViewModelClickListener.onItemClick(action.page))
|
effects.tryEmit(pageViewModelClickListener.onItemClick(action.page))
|
||||||
} else {
|
} else {
|
||||||
effects.offer(OpenPage(action.page, zimReaderContainer))
|
effects.tryEmit(OpenPage(action.page, zimReaderContainer))
|
||||||
}
|
}
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
@ -131,12 +138,15 @@ abstract class PageViewModel<T : Page, S : PageState<T>>(
|
|||||||
abstract fun deselectAllPages(state: S): S
|
abstract fun deselectAllPages(state: S): S
|
||||||
|
|
||||||
private fun exitFragment(state: S): S {
|
private fun exitFragment(state: S): S {
|
||||||
effects.offer(PopFragmentBackstack)
|
effects.tryEmit(PopFragmentBackstack)
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
compositeDisposable.clear()
|
coroutineJobs.forEach {
|
||||||
|
it.cancel()
|
||||||
|
}
|
||||||
|
coroutineJobs.clear()
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,11 +21,9 @@ package org.kiwix.kiwixmobile.core.page.bookmark.viewmodel
|
|||||||
import io.mockk.clearAllMocks
|
import io.mockk.clearAllMocks
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.reactivex.Flowable
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.reactive.asPublisher
|
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
@ -44,6 +42,7 @@ import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
|
|||||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
|
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
|
||||||
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
|
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
|
||||||
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
|
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
|
||||||
|
import org.kiwix.kiwixmobile.core.utils.files.testFlow
|
||||||
import org.kiwix.sharedFunctions.InstantExecutorExtension
|
import org.kiwix.sharedFunctions.InstantExecutorExtension
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@ -67,9 +66,7 @@ internal class BookmarkViewModelTest {
|
|||||||
every { zimReaderContainer.name } returns "zimName"
|
every { zimReaderContainer.name } returns "zimName"
|
||||||
every { sharedPreferenceUtil.showBookmarksAllBooks } returns true
|
every { sharedPreferenceUtil.showBookmarksAllBooks } returns true
|
||||||
every { libkiwixBookMarks.bookmarks() } returns itemsFromDb
|
every { libkiwixBookMarks.bookmarks() } returns itemsFromDb
|
||||||
every { libkiwixBookMarks.pages() } returns Flowable.fromPublisher(
|
every { libkiwixBookMarks.pages() } returns libkiwixBookMarks.bookmarks()
|
||||||
libkiwixBookMarks.bookmarks().asPublisher()
|
|
||||||
)
|
|
||||||
viewModel =
|
viewModel =
|
||||||
BookmarkViewModel(libkiwixBookMarks, zimReaderContainer, sharedPreferenceUtil).apply {
|
BookmarkViewModel(libkiwixBookMarks, zimReaderContainer, sharedPreferenceUtil).apply {
|
||||||
alertDialogShower = dialogShower
|
alertDialogShower = dialogShower
|
||||||
@ -106,13 +103,24 @@ internal class BookmarkViewModelTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `offerUpdateToShowAllToggle offers UpdateAllBookmarksPreference`() {
|
fun `offerUpdateToShowAllToggle offers UpdateAllBookmarksPreference`() = runTest {
|
||||||
viewModel.effects.test().also {
|
testFlow(
|
||||||
|
flow = viewModel.effects,
|
||||||
|
triggerAction = {
|
||||||
viewModel.offerUpdateToShowAllToggle(
|
viewModel.offerUpdateToShowAllToggle(
|
||||||
Action.UserClickedShowAllToggle(false),
|
Action.UserClickedShowAllToggle(false),
|
||||||
bookmarkState()
|
bookmarkState()
|
||||||
)
|
)
|
||||||
}.assertValues(UpdateAllBookmarksPreference(sharedPreferenceUtil, false))
|
},
|
||||||
|
assert = {
|
||||||
|
assertThat(awaitItem()).isEqualTo(
|
||||||
|
UpdateAllBookmarksPreference(
|
||||||
|
sharedPreferenceUtil,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -22,9 +22,9 @@ import io.mockk.every
|
|||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.slot
|
import io.mockk.slot
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import io.reactivex.processors.PublishProcessor
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||||
@ -40,7 +40,7 @@ import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.DeleteSelectedBookmar
|
|||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
internal class ShowDeleteBookmarksDialogTest {
|
internal class ShowDeleteBookmarksDialogTest {
|
||||||
val effects = mockk<PublishProcessor<SideEffect<*>>>(relaxed = true)
|
val effects = mockk<MutableSharedFlow<SideEffect<*>>>(relaxed = true)
|
||||||
private val newBookmarksDao = mockk<NewBookmarksDao>()
|
private val newBookmarksDao = mockk<NewBookmarksDao>()
|
||||||
val activity = mockk<CoreMainActivity>()
|
val activity = mockk<CoreMainActivity>()
|
||||||
private val dialogShower = mockk<DialogShower>(relaxed = true)
|
private val dialogShower = mockk<DialogShower>(relaxed = true)
|
||||||
@ -61,7 +61,7 @@ internal class ShowDeleteBookmarksDialogTest {
|
|||||||
showDeleteBookmarksDialog.invokeWith(activity)
|
showDeleteBookmarksDialog.invokeWith(activity)
|
||||||
verify { dialogShower.show(any(), capture(lambdaSlot)) }
|
verify { dialogShower.show(any(), capture(lambdaSlot)) }
|
||||||
lambdaSlot.captured.invoke()
|
lambdaSlot.captured.invoke()
|
||||||
verify { effects.offer(DeletePageItems(bookmarkState(), newBookmarksDao, viewModelScope)) }
|
verify { effects.tryEmit(DeletePageItems(bookmarkState(), newBookmarksDao, viewModelScope)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mockkActivityInjection(showDeleteBookmarksDialog: ShowDeleteBookmarksDialog) {
|
private fun mockkActivityInjection(showDeleteBookmarksDialog: ShowDeleteBookmarksDialog) {
|
||||||
|
@ -3,12 +3,9 @@ package org.kiwix.kiwixmobile.core.page.history.viewmodel
|
|||||||
import io.mockk.clearAllMocks
|
import io.mockk.clearAllMocks
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.reactivex.plugins.RxJavaPlugins
|
|
||||||
import io.reactivex.processors.PublishProcessor
|
|
||||||
import io.reactivex.schedulers.Schedulers
|
|
||||||
import io.reactivex.schedulers.TestScheduler
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
@ -27,8 +24,8 @@ import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
|
|||||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
|
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
|
||||||
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
|
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
|
||||||
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
|
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
|
||||||
|
import org.kiwix.kiwixmobile.core.utils.files.testFlow
|
||||||
import org.kiwix.sharedFunctions.InstantExecutorExtension
|
import org.kiwix.sharedFunctions.InstantExecutorExtension
|
||||||
import org.kiwix.sharedFunctions.setScheduler
|
|
||||||
|
|
||||||
@ExtendWith(InstantExecutorExtension::class)
|
@ExtendWith(InstantExecutorExtension::class)
|
||||||
internal class HistoryViewModelTest {
|
internal class HistoryViewModelTest {
|
||||||
@ -39,16 +36,10 @@ internal class HistoryViewModelTest {
|
|||||||
private val viewModelScope = CoroutineScope(Dispatchers.IO)
|
private val viewModelScope = CoroutineScope(Dispatchers.IO)
|
||||||
|
|
||||||
private lateinit var viewModel: HistoryViewModel
|
private lateinit var viewModel: HistoryViewModel
|
||||||
private val testScheduler = TestScheduler()
|
|
||||||
private val zimReaderSource: ZimReaderSource = mockk()
|
private val zimReaderSource: ZimReaderSource = mockk()
|
||||||
|
|
||||||
init {
|
private val itemsFromDb: MutableSharedFlow<List<Page>> =
|
||||||
setScheduler(testScheduler)
|
MutableSharedFlow<List<Page>>(0)
|
||||||
RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private val itemsFromDb: PublishProcessor<List<Page>> =
|
|
||||||
PublishProcessor.create()
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun init() {
|
fun init() {
|
||||||
@ -89,13 +80,21 @@ internal class HistoryViewModelTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `offerUpdateToShowAllToggle offers UpdateAllHistoryPreference`() {
|
fun `offerUpdateToShowAllToggle offers UpdateAllHistoryPreference`() = runTest {
|
||||||
viewModel.effects.test().also {
|
testFlow(
|
||||||
|
flow = viewModel.effects,
|
||||||
|
triggerAction = {
|
||||||
viewModel.offerUpdateToShowAllToggle(
|
viewModel.offerUpdateToShowAllToggle(
|
||||||
UserClickedShowAllToggle(false),
|
UserClickedShowAllToggle(false),
|
||||||
historyState()
|
historyState()
|
||||||
)
|
)
|
||||||
}.assertValues(UpdateAllHistoryPreference(sharedPreferenceUtil, false))
|
},
|
||||||
|
assert = {
|
||||||
|
assertThat(awaitItem()).isEqualTo(
|
||||||
|
UpdateAllHistoryPreference(sharedPreferenceUtil, false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -4,9 +4,9 @@ import io.mockk.every
|
|||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.slot
|
import io.mockk.slot
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import io.reactivex.processors.PublishProcessor
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
import org.kiwix.kiwixmobile.core.base.SideEffect
|
||||||
@ -20,7 +20,7 @@ import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.DeleteAllHistory
|
|||||||
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.DeleteSelectedHistory
|
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.DeleteSelectedHistory
|
||||||
|
|
||||||
internal class ShowDeleteHistoryDialogTest {
|
internal class ShowDeleteHistoryDialogTest {
|
||||||
val effects = mockk<PublishProcessor<SideEffect<*>>>(relaxed = true)
|
val effects = mockk<MutableSharedFlow<SideEffect<*>>>(relaxed = true)
|
||||||
private val historyDao = mockk<HistoryDao>()
|
private val historyDao = mockk<HistoryDao>()
|
||||||
val activity = mockk<CoreMainActivity>()
|
val activity = mockk<CoreMainActivity>()
|
||||||
private val dialogShower = mockk<DialogShower>(relaxed = true)
|
private val dialogShower = mockk<DialogShower>(relaxed = true)
|
||||||
@ -42,7 +42,7 @@ internal class ShowDeleteHistoryDialogTest {
|
|||||||
showDeleteHistoryDialog.invokeWith(activity)
|
showDeleteHistoryDialog.invokeWith(activity)
|
||||||
verify { dialogShower.show(any(), capture(lambdaSlot)) }
|
verify { dialogShower.show(any(), capture(lambdaSlot)) }
|
||||||
lambdaSlot.captured.invoke()
|
lambdaSlot.captured.invoke()
|
||||||
verify { effects.offer(DeletePageItems(historyState(), historyDao, viewModelScope)) }
|
verify { effects.tryEmit(DeletePageItems(historyState(), historyDao, viewModelScope)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -18,18 +18,16 @@
|
|||||||
|
|
||||||
package org.kiwix.kiwixmobile.core.page.viewmodel
|
package org.kiwix.kiwixmobile.core.page.viewmodel
|
||||||
|
|
||||||
import com.jraska.livedata.test
|
|
||||||
import io.mockk.clearAllMocks
|
import io.mockk.clearAllMocks
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.reactivex.plugins.RxJavaPlugins
|
|
||||||
import io.reactivex.processors.PublishProcessor
|
|
||||||
import io.reactivex.schedulers.Schedulers
|
|
||||||
import io.reactivex.schedulers.TestScheduler
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||||
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
import kotlinx.coroutines.test.resetMain
|
import kotlinx.coroutines.test.resetMain
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
import kotlinx.coroutines.test.setMain
|
import kotlinx.coroutines.test.setMain
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.jupiter.api.AfterEach
|
import org.junit.jupiter.api.AfterEach
|
||||||
@ -40,22 +38,22 @@ import org.kiwix.kiwixmobile.core.dao.PageDao
|
|||||||
import org.kiwix.kiwixmobile.core.page.PageImpl
|
import org.kiwix.kiwixmobile.core.page.PageImpl
|
||||||
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
import org.kiwix.kiwixmobile.core.page.adapter.Page
|
||||||
import org.kiwix.kiwixmobile.core.page.pageState
|
import org.kiwix.kiwixmobile.core.page.pageState
|
||||||
|
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.UpdatePages
|
||||||
|
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.Filter
|
||||||
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.Exit
|
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.Exit
|
||||||
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.ExitActionModeMenu
|
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.ExitActionModeMenu
|
||||||
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.Filter
|
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.UserClickedShowAllToggle
|
||||||
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.OnItemClick
|
|
||||||
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.OnItemLongClick
|
|
||||||
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.UpdatePages
|
|
||||||
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.UserClickedDeleteButton
|
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.UserClickedDeleteButton
|
||||||
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.UserClickedDeleteSelectedPages
|
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.UserClickedDeleteSelectedPages
|
||||||
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.UserClickedShowAllToggle
|
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.OnItemClick
|
||||||
|
import org.kiwix.kiwixmobile.core.page.viewmodel.Action.OnItemLongClick
|
||||||
import org.kiwix.kiwixmobile.core.page.viewmodel.effects.OpenPage
|
import org.kiwix.kiwixmobile.core.page.viewmodel.effects.OpenPage
|
||||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
|
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
|
||||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
|
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
|
||||||
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.PopFragmentBackstack
|
import org.kiwix.kiwixmobile.core.search.viewmodel.effects.PopFragmentBackstack
|
||||||
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
|
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
|
||||||
|
import org.kiwix.kiwixmobile.core.utils.files.testFlow
|
||||||
import org.kiwix.sharedFunctions.InstantExecutorExtension
|
import org.kiwix.sharedFunctions.InstantExecutorExtension
|
||||||
import org.kiwix.sharedFunctions.setScheduler
|
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@ExtendWith(InstantExecutorExtension::class)
|
@ExtendWith(InstantExecutorExtension::class)
|
||||||
@ -65,14 +63,8 @@ internal class PageViewModelTest {
|
|||||||
private val sharedPreferenceUtil: SharedPreferenceUtil = mockk()
|
private val sharedPreferenceUtil: SharedPreferenceUtil = mockk()
|
||||||
|
|
||||||
private lateinit var viewModel: TestablePageViewModel
|
private lateinit var viewModel: TestablePageViewModel
|
||||||
private val testScheduler = TestScheduler()
|
private val itemsFromDb: MutableSharedFlow<List<Page>> =
|
||||||
private val itemsFromDb: PublishProcessor<List<Page>> =
|
MutableSharedFlow<List<Page>>(0)
|
||||||
PublishProcessor.create()
|
|
||||||
|
|
||||||
init {
|
|
||||||
setScheduler(testScheduler)
|
|
||||||
RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun init() {
|
fun init() {
|
||||||
@ -91,73 +83,139 @@ internal class PageViewModelTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `initial state is Initialising`() {
|
fun `initial state is Initialising`() = runTest {
|
||||||
viewModel.state.test().assertValue(pageState())
|
testFlow(
|
||||||
|
flow = viewModel.state,
|
||||||
|
triggerAction = {},
|
||||||
|
assert = { assertThat(awaitItem()).isEqualTo(pageState()) }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Exit calls PopFragmentBackstack`() {
|
fun `Exit calls PopFragmentBackstack`() = runTest {
|
||||||
viewModel.effects.test().also { viewModel.actions.offer(Exit) }
|
testFlow(
|
||||||
.assertValue(PopFragmentBackstack)
|
flow = viewModel.effects,
|
||||||
viewModel.state.test().assertValue(pageState())
|
triggerAction = { viewModel.actions.tryEmit(Exit) },
|
||||||
|
assert = { assertThat(awaitItem()).isEqualTo(PopFragmentBackstack) }
|
||||||
|
)
|
||||||
|
testFlow(
|
||||||
|
flow = viewModel.state,
|
||||||
|
triggerAction = {},
|
||||||
|
assert = { assertThat(awaitItem()).isEqualTo(pageState()) }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `ExitActionModeMenu calls deslectAllPages`() {
|
fun `ExitActionModeMenu calls deslectAllPages`() = runTest {
|
||||||
viewModel.actions.offer(ExitActionModeMenu)
|
testFlow(
|
||||||
viewModel.state.test().assertValue(TestablePageState(searchTerm = "deselectAllPagesCalled"))
|
flow = viewModel.state,
|
||||||
|
triggerAction = { viewModel.actions.tryEmit(ExitActionModeMenu) },
|
||||||
|
assert = {
|
||||||
|
assertThat(awaitItem()).isEqualTo(TestablePageState(searchTerm = ""))
|
||||||
|
assertThat(awaitItem())
|
||||||
|
.isEqualTo(TestablePageState(searchTerm = "deselectAllPagesCalled"))
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `UserClickedShowAllToggle calls offerUpdateToShowAllToggle`() {
|
fun `UserClickedShowAllToggle calls offerUpdateToShowAllToggle`() = runTest {
|
||||||
val action = UserClickedShowAllToggle(true)
|
testFlow(
|
||||||
viewModel.actions.offer(action)
|
flow = viewModel.state,
|
||||||
viewModel.state.test()
|
triggerAction = {
|
||||||
.assertValue(TestablePageState(searchTerm = "offerUpdateToShowAllToggleCalled"))
|
viewModel.actions.tryEmit(UserClickedShowAllToggle(true))
|
||||||
|
},
|
||||||
|
assert = {
|
||||||
|
assertThat(awaitItem()).isEqualTo(TestablePageState(searchTerm = ""))
|
||||||
|
assertThat(awaitItem())
|
||||||
|
.isEqualTo(TestablePageState(searchTerm = "offerUpdateToShowAllToggleCalled"))
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `UserClickedDeleteButton calls createDeletePageDialogEffect`() {
|
fun `UserClickedDeleteButton calls createDeletePageDialogEffect`() = runTest {
|
||||||
viewModel.actions.offer(UserClickedDeleteButton)
|
viewModel.actions.tryEmit(UserClickedDeleteButton)
|
||||||
|
advanceUntilIdle()
|
||||||
assertThat(viewModel.createDeletePageDialogEffectCalled).isEqualTo(true)
|
assertThat(viewModel.createDeletePageDialogEffectCalled).isEqualTo(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `UserClickedDeleteSelectedPages calls createDeletePageDialogEffect`() {
|
fun `UserClickedDeleteSelectedPages calls createDeletePageDialogEffect`() = runTest {
|
||||||
viewModel.actions.offer(UserClickedDeleteSelectedPages)
|
viewModel.actions.tryEmit(UserClickedDeleteSelectedPages)
|
||||||
|
advanceUntilIdle()
|
||||||
assertThat(viewModel.createDeletePageDialogEffectCalled).isEqualTo(true)
|
assertThat(viewModel.createDeletePageDialogEffectCalled).isEqualTo(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
internal fun `OnItemClick selects item if one is selected`() {
|
internal fun `OnItemClick selects item if one is selected`() = runTest {
|
||||||
val zimReaderSource: ZimReaderSource = mockk()
|
val zimReaderSource: ZimReaderSource = mockk()
|
||||||
|
testFlow(
|
||||||
|
viewModel.state,
|
||||||
|
triggerAction = {
|
||||||
val page = PageImpl(isSelected = true, zimReaderSource = zimReaderSource)
|
val page = PageImpl(isSelected = true, zimReaderSource = zimReaderSource)
|
||||||
viewModel.state.postValue(TestablePageState(listOf(page)))
|
viewModel.getMutableStateForTestCases().value = TestablePageState(listOf(page))
|
||||||
viewModel.actions.offer(OnItemClick(page))
|
viewModel.actions.tryEmit(OnItemClick(page))
|
||||||
viewModel.state.test()
|
},
|
||||||
.assertValue(TestablePageState(listOf(PageImpl(zimReaderSource = zimReaderSource))))
|
assert = {
|
||||||
|
assertThat(awaitItem()).isEqualTo(TestablePageState())
|
||||||
|
assertThat(awaitItem())
|
||||||
|
.isEqualTo(
|
||||||
|
TestablePageState(
|
||||||
|
listOf(PageImpl(zimReaderSource = zimReaderSource))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
internal fun `OnItemClick offers OpenPage if none is selected`() {
|
internal fun `OnItemClick offers OpenPage if none is selected`() = runTest {
|
||||||
val zimReaderSource: ZimReaderSource = mockk()
|
val zimReaderSource: ZimReaderSource = mockk()
|
||||||
viewModel.state.postValue(
|
testFlow(
|
||||||
|
viewModel.effects,
|
||||||
|
triggerAction = {
|
||||||
|
viewModel.getMutableStateForTestCases().value =
|
||||||
|
TestablePageState(listOf(PageImpl(zimReaderSource = zimReaderSource)))
|
||||||
|
viewModel.actions.tryEmit(OnItemClick(PageImpl(zimReaderSource = zimReaderSource)))
|
||||||
|
},
|
||||||
|
assert = {
|
||||||
|
assertThat(awaitItem()).isEqualTo(
|
||||||
|
OpenPage(
|
||||||
|
PageImpl(zimReaderSource = zimReaderSource),
|
||||||
|
zimReaderContainer
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
testFlow(
|
||||||
|
viewModel.state,
|
||||||
|
triggerAction = {
|
||||||
|
viewModel.getMutableStateForTestCases().value =
|
||||||
|
TestablePageState(listOf(PageImpl(zimReaderSource = zimReaderSource)))
|
||||||
|
viewModel.actions.tryEmit(OnItemClick(PageImpl(zimReaderSource = zimReaderSource)))
|
||||||
|
},
|
||||||
|
assert = {
|
||||||
|
assertThat(awaitItem()).isEqualTo(
|
||||||
TestablePageState(listOf(PageImpl(zimReaderSource = zimReaderSource)))
|
TestablePageState(listOf(PageImpl(zimReaderSource = zimReaderSource)))
|
||||||
)
|
)
|
||||||
viewModel.effects.test()
|
}
|
||||||
.also { viewModel.actions.offer(OnItemClick(PageImpl(zimReaderSource = zimReaderSource))) }
|
)
|
||||||
.assertValue(OpenPage(PageImpl(zimReaderSource = zimReaderSource), zimReaderContainer))
|
|
||||||
viewModel.state.test()
|
|
||||||
.assertValue(TestablePageState(listOf(PageImpl(zimReaderSource = zimReaderSource))))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
internal fun `OnItemLongClick selects item if none is selected`() {
|
internal fun `OnItemLongClick selects item if none is selected`() = runTest {
|
||||||
val zimReaderSource: ZimReaderSource = mockk()
|
val zimReaderSource: ZimReaderSource = mockk()
|
||||||
val page = PageImpl(zimReaderSource = zimReaderSource)
|
val page = PageImpl(zimReaderSource = zimReaderSource)
|
||||||
viewModel.state.postValue(TestablePageState(listOf(page)))
|
testFlow(
|
||||||
viewModel.actions.offer(OnItemLongClick(page))
|
viewModel.state,
|
||||||
viewModel.state.test().assertValue(
|
triggerAction = {
|
||||||
|
viewModel.getMutableStateForTestCases().value = TestablePageState(listOf(page))
|
||||||
|
viewModel.actions.tryEmit(OnItemLongClick(page))
|
||||||
|
},
|
||||||
|
assert = {
|
||||||
|
assertThat(awaitItem()).isEqualTo(TestablePageState())
|
||||||
|
assertThat(awaitItem()).isEqualTo(
|
||||||
TestablePageState(
|
TestablePageState(
|
||||||
listOf(
|
listOf(
|
||||||
PageImpl(
|
PageImpl(
|
||||||
@ -168,18 +226,30 @@ internal class PageViewModelTest {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
@Test
|
|
||||||
fun `Filter calls updatePagesBasedOnFilter`() {
|
|
||||||
viewModel.actions.offer(Filter("Called"))
|
|
||||||
viewModel.state.test()
|
|
||||||
.assertValue(TestablePageState(searchTerm = "updatePagesBasedOnFilterCalled"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `UpdatePages calls updatePages`() {
|
fun `Filter calls updatePagesBasedOnFilter`() = runTest {
|
||||||
viewModel.actions.offer(UpdatePages(emptyList()))
|
testFlow(
|
||||||
viewModel.state.test()
|
viewModel.state,
|
||||||
.assertValue(TestablePageState(searchTerm = "updatePagesCalled"))
|
triggerAction = { viewModel.actions.tryEmit(Filter("Called")) },
|
||||||
|
assert = {
|
||||||
|
assertThat(awaitItem()).isEqualTo(TestablePageState())
|
||||||
|
assertThat(awaitItem()).isEqualTo(TestablePageState(searchTerm = "updatePagesBasedOnFilterCalled"))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `UpdatePages calls updatePages`() = runTest {
|
||||||
|
testFlow(
|
||||||
|
viewModel.state,
|
||||||
|
triggerAction = { viewModel.actions.tryEmit(UpdatePages(emptyList())) },
|
||||||
|
assert = {
|
||||||
|
assertThat(awaitItem()).isEqualTo(TestablePageState())
|
||||||
|
assertThat(awaitItem()).isEqualTo(TestablePageState(searchTerm = "updatePagesCalled"))
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,6 @@ import io.mockk.clearMocks
|
|||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.mockkStatic
|
import io.mockk.mockkStatic
|
||||||
import io.reactivex.schedulers.Schedulers
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.test.TestScope
|
import kotlinx.coroutines.test.TestScope
|
||||||
@ -41,8 +40,6 @@ import org.junit.jupiter.api.AfterAll
|
|||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Nested
|
import org.junit.jupiter.api.Nested
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.kiwix.sharedFunctions.resetSchedulers
|
|
||||||
import org.kiwix.sharedFunctions.setScheduler
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class FileSearchTest {
|
class FileSearchTest {
|
||||||
@ -54,10 +51,6 @@ class FileSearchTest {
|
|||||||
private val storageDevice: StorageDevice = mockk()
|
private val storageDevice: StorageDevice = mockk()
|
||||||
private val scanningProgressListener: ScanningProgressListener = mockk()
|
private val scanningProgressListener: ScanningProgressListener = mockk()
|
||||||
|
|
||||||
init {
|
|
||||||
setScheduler(Schedulers.trampoline())
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun init() {
|
fun init() {
|
||||||
clearMocks(context, externalStorageDirectory, contentResolver, storageDevice)
|
clearMocks(context, externalStorageDirectory, contentResolver, storageDevice)
|
||||||
@ -78,7 +71,6 @@ class FileSearchTest {
|
|||||||
@AfterAll
|
@AfterAll
|
||||||
fun teardown() {
|
fun teardown() {
|
||||||
deleteTempDirectory()
|
deleteTempDirectory()
|
||||||
resetSchedulers()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
|
Loading…
x
Reference in New Issue
Block a user