Home and Library screen update (#101)

* Added support for filtering file types in synced folders

* Added support for custom file names and enhanced file information dialog

* added feedback option in the homescreen drawer

* Implemented a limit for number of recent files displayed on the home screen
This commit is contained in:
Aryan 2026-03-20 21:51:03 +05:30 committed by GitHub
parent 9842aea9b1
commit ddcd253c7b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 637 additions and 734 deletions

View file

@ -148,7 +148,7 @@ data class DeviceLimitReachedState(
)
data class SyncedFolder(
val uriString: String, val name: String, val lastScanTime: Long
val uriString: String, val name: String, val lastScanTime: Long, val allowedFileTypes: Set<FileType> = FileType.entries.toSet()
)
data class Shelf(val name: String, val books: List<RecentFileItem>) {
@ -227,6 +227,7 @@ data class ReaderScreenState(
val pinnedHomeBookIds: Set<String> = emptySet(),
val pinnedLibraryBookIds: Set<String> = emptySet(),
val libraryFilters: LibraryFilters = LibraryFilters(),
val recentFilesLimit: Int = 0,
)
open class MainViewModel(application: Application) : AndroidViewModel(application) {
@ -324,7 +325,8 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
)
else null,
pinnedHomeBookIds = prefs.getStringSet(KEY_PINNED_HOME, emptySet()) ?: emptySet(),
pinnedLibraryBookIds = prefs.getStringSet(KEY_PINNED_LIBRARY, emptySet()) ?: emptySet()
pinnedLibraryBookIds = prefs.getStringSet(KEY_PINNED_LIBRARY, emptySet()) ?: emptySet(),
recentFilesLimit = prefs.getInt(KEY_RECENT_FILES_LIMIT, 0)
)
)
@ -377,7 +379,8 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
val visibleRecentFiles = sortFiles(baseVisibleFiles.filter { it.isRecent }).let { list ->
val pinned = list.filter { it.bookId in internalState.pinnedHomeBookIds }
val unpinned = list.filter { it.bookId !in internalState.pinnedHomeBookIds }
pinned + unpinned
val combined = pinned + unpinned
if (internalState.recentFilesLimit > 0) combined.take(internalState.recentFilesLimit) else combined
}
val validContextualItems = internalState.contextualActionItems.filter { contextItem ->
@ -783,7 +786,7 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
fun deleteBookPermanently(bookId: String, onDeleted: () -> Unit = {}) {
viewModelScope.launch {
val item = recentFilesRepository.getFileByBookId(bookId) ?: return@launch
@Suppress("UnusedVariable", "Unused") val item = recentFilesRepository.getFileByBookId(bookId) ?: return@launch
Timber.d("Deleting book permanently from reader: $bookId")
@ -1384,7 +1387,7 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
val oldTime = prefs.getLong(KEY_LAST_FOLDER_SCAN_TIME, 0L)
if (oldUri != null) {
val name = getDisplayPathFromUri(appContext, oldUri)
val migrated = SyncedFolder(oldUri, name, oldTime)
val migrated = SyncedFolder(oldUri, name, oldTime, FileType.entries.toSet())
folders.add(migrated)
saveSyncedFoldersToPrefs(folders)
@ -1398,11 +1401,25 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
val jsonArray = JSONArray(jsonString)
for (i in 0 until jsonArray.length()) {
val obj = jsonArray.getJSONObject(i)
val allowedFileTypes = mutableSetOf<FileType>()
if (obj.has("allowedFileTypes")) {
val typesArray = obj.getJSONArray("allowedFileTypes")
for (j in 0 until typesArray.length()) {
try {
allowedFileTypes.add(FileType.valueOf(typesArray.getString(j)))
} catch (_: Exception) {}
}
} else {
allowedFileTypes.addAll(FileType.entries)
}
folders.add(
SyncedFolder(
uriString = obj.getString("uri"),
name = obj.getString("name"),
lastScanTime = obj.optLong("lastScanTime", 0L)
lastScanTime = obj.optLong("lastScanTime", 0L),
allowedFileTypes = allowedFileTypes
)
)
}
@ -1420,6 +1437,9 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
obj.put("uri", folder.uriString)
obj.put("name", folder.name)
obj.put("lastScanTime", folder.lastScanTime)
val typesArray = JSONArray()
folder.allowedFileTypes.forEach { typesArray.put(it.name) }
obj.put("allowedFileTypes", typesArray)
jsonArray.put(obj)
}
prefs.edit { putString(KEY_SYNCED_FOLDERS_JSON, jsonArray.toString()) }
@ -1446,7 +1466,7 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
)
val name = getDisplayPathFromUri(appContext, folderUri.toString())
val newFolder = SyncedFolder(folderUri.toString(), name, 0L)
val newFolder = SyncedFolder(folderUri.toString(), name, 0L, FileType.entries.toSet())
val newStats = currentFolders + newFolder
saveSyncedFoldersToPrefs(newStats)
@ -1578,6 +1598,37 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
}
}
fun updateFolderFilters(folder: SyncedFolder, newFilters: Set<FileType>) {
viewModelScope.launch(Dispatchers.IO) {
val currentFolders = _internalState.value.syncedFolders.toMutableList()
val index = currentFolders.indexOfFirst { it.uriString == folder.uriString }
if (index != -1) {
val updatedFolder = folder.copy(allowedFileTypes = newFilters)
currentFolders[index] = updatedFolder
saveSyncedFoldersToPrefs(currentFolders)
_internalState.update { it.copy(syncedFolders = currentFolders) }
val filesToRemove = recentFilesRepository.getFilesBySourceFolder(folder.uriString)
.filter { it.type !in newFilters }
if (filesToRemove.isNotEmpty()) {
Timber.d("Removing ${filesToRemove.size} files that no longer match the filter for folder ${folder.name}")
val idsToRemove = filesToRemove.map { it.bookId }
idsToRemove.forEach { bookId ->
pdfTextRepository.clearBookText(bookId)
clearImportedFileCache(bookId)
}
recentFilesRepository.deleteFilePermanently(idsToRemove)
}
withContext(Dispatchers.Main) {
scanSyncedFolder()
}
}
}
}
fun disconnectAllSyncedFolders() {
viewModelScope.launch {
val folders = _internalState.value.syncedFolders
@ -2374,6 +2425,11 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
}
}
fun setRecentFilesLimit(limit: Int) {
_internalState.update { it.copy(recentFilesLimit = limit) }
prefs.edit { putInt(KEY_RECENT_FILES_LIMIT, limit) }
}
fun setSortOrder(sortOrder: SortOrder) {
_internalState.update { it.copy(sortOrder = sortOrder) }
prefs.edit { putString(KEY_SORT_ORDER, sortOrder.name) }
@ -3799,6 +3855,26 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
}
}
fun updateCustomName(bookId: String, newName: String?) {
viewModelScope.launch {
val item = recentFilesRepository.getFileByBookId(bookId)
if (item != null) {
val updatedItem = item.copy(customName = newName, lastModifiedTimestamp = System.currentTimeMillis())
recentFilesRepository.addRecentFile(updatedItem)
if (uiState.value.isSyncEnabled) {
uploadSingleBookMetadata(updatedItem)
}
if (updatedItem.sourceFolderUri != null) {
launch(Dispatchers.IO) {
recentFilesRepository.syncLocalMetadataToFolder(bookId)
}
}
}
}
}
companion object {
private const val KEY_SORT_ORDER = "sort_order"
internal const val KEY_SHELVES = "shelf_names"
@ -3817,5 +3893,6 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
private const val MAX_FOLDER_LIMIT = 3
internal const val KEY_PINNED_HOME = "pinned_home_books"
internal const val KEY_PINNED_LIBRARY = "pinned_library_books"
private const val KEY_RECENT_FILES_LIMIT = "recent_files_limit"
}
}