* Add performance and stylus debugging logs * Refactor and decouple UI models from `MainViewModel` * Refactor library state management and projection logic * Implement desktop shell using Compose Multiplatform * Implement desktop shell using Compose Multiplatform * Implement desktop shell using Compose Multiplatform * Introduce ReaderEngine and enhance EPUB reader features in windows app * Move core paginated reader logic to a Kotlin Multiplatform `shared` module and introduce experimental desktop support. * Implement PDF rendering and text extraction for desktop using Pdfium * Add `NonReaderScreens.kt` and UI dependencies * Refactor and centralize library state management and models to improve cross-platform consistency * Implement JSON persistence for desktop library and enhance library management features including shelf CRUD, tagging, and metadata editing * Implement PDF annotation system and enhanced zoom controls for the desktop viewer * Implement WebView-based EPUB rendering for desktop using CEF and embedded resources * Optimize UI state projection, navigation state handling, and main screen pager performance * Implement Bring Your Own Key (BYOK) support for AI features in OSS version * Support Gemini-based Cloud TTS with BYOK support for OSS builds * Refactor table cell image sizing in `PaginatedReader` and improve `MobiParser` native library loading and error handling. * crash fixes * Enhance navigation stability with lifecycle-aware safety checks and update `navigation-compose` to 2.9.6 * Implement dynamic bottom padding for the page info bar to account for device rounded corners * Implement bidirectional jump history navigation and replace the jump-back pill with a dedicated `PdfJumpHistoryBar` * Optimize PDF tiling performance and refine pan-and-fling gesture handling * Implement customizable toolbars with drag-and-drop reordering and placement for PDF and EPUB readers * Updated UI for customize toolbar * Refine drag-and-drop reordering and section assignment for PDF and EPUB reader controls * restructure PDF viewer UI component hierarchy to fix verifier crash * Implement separate text dimming factors for light and dark themes * Synchronize Pdfium access and improve resource lifecycle safety across Kotlin and native layers * Enhance image alignment in paginated and EPUB readers through anchor detection and style-based positioning * Centralize file type resolution logic and implement HTML sanitization during import * Introduce vertical margin customization and configurable progress bar positioning * texture support in epub reader * Enhance TTS session management, progress tracking, and diagnostic logging * Optimize library state projection and folder synchronization performance by refactoring collection lookups and refining metadata extraction logic. * Refine TTS page mapping for PDF and overhaul TTS control UI * Implement natural session completion logic in `TtsPlaybackManager` for cloud tts * Replace Snackbar with `CustomTopBanner` for notifications in `PdfViewerScreen` * Refine TTS playback continuity across PDF pages and improve state management for session transitions * Implement global texture transparency and enhance textured theme support across PDF and EPUB readers. * Update reader themes and improve texture rendering in page animations, EPUB UI, and immersive mode * Add Support Project screen * Optimize library performance via projection caching, batch database updates, and scoped folder synchronization. * Enhance folder synchronization with fallback query mechanisms and refactor annotation sidecar importing logic * Bump version to 1.0.47 (51)
220 lines
8.2 KiB
Kotlin
220 lines
8.2 KiB
Kotlin
package com.aryan.reader
|
|
|
|
import com.aryan.reader.data.BookShelfCrossRef
|
|
import com.aryan.reader.data.BookTagCrossRef
|
|
import com.aryan.reader.data.RecentFileItem
|
|
import com.aryan.reader.data.ShelfEntity
|
|
import com.aryan.reader.data.TagEntity
|
|
import com.aryan.reader.shared.AddBooksSource as SharedAddBooksSource
|
|
import com.aryan.reader.shared.AppContrastOption as SharedAppContrastOption
|
|
import com.aryan.reader.shared.AppThemeMode as SharedAppThemeMode
|
|
import com.aryan.reader.shared.BannerMessage as SharedBannerMessage
|
|
import com.aryan.reader.shared.BookItem as SharedBookItem
|
|
import com.aryan.reader.shared.BookShelfRef as SharedBookShelfRef
|
|
import com.aryan.reader.shared.CustomAppTheme as SharedCustomAppTheme
|
|
import com.aryan.reader.shared.FileType as SharedFileType
|
|
import com.aryan.reader.shared.LibraryFilters as SharedLibraryFilters
|
|
import com.aryan.reader.shared.ReadStatusFilter as SharedReadStatusFilter
|
|
import com.aryan.reader.shared.RenderMode as SharedRenderMode
|
|
import com.aryan.reader.shared.SharedLibraryProjectionInput
|
|
import com.aryan.reader.shared.SharedReaderScreenState
|
|
import com.aryan.reader.shared.ShelfRecord
|
|
import com.aryan.reader.shared.SortOrder as SharedSortOrder
|
|
import com.aryan.reader.shared.SyncedFolder as SharedSyncedFolder
|
|
import com.aryan.reader.shared.Tag as SharedTag
|
|
|
|
fun RecentFileItem.toSharedBookItem(): SharedBookItem {
|
|
return SharedBookItem(
|
|
id = bookId,
|
|
path = uriString,
|
|
type = type.toSharedFileType(),
|
|
displayName = customName ?: displayName,
|
|
timestamp = timestamp,
|
|
title = title,
|
|
author = author,
|
|
progressPercentage = progressPercentage,
|
|
isRecent = isRecent,
|
|
fileSize = fileSize,
|
|
sourceFolder = sourceFolderUri,
|
|
seriesName = seriesName,
|
|
seriesIndex = seriesIndex,
|
|
tags = tags.map { it.toSharedTag() }
|
|
)
|
|
}
|
|
|
|
fun TagEntity.toSharedTag(): SharedTag {
|
|
return SharedTag(
|
|
id = id,
|
|
name = name,
|
|
color = color
|
|
)
|
|
}
|
|
|
|
fun ShelfEntity.toSharedShelfRecord(): ShelfRecord {
|
|
return ShelfRecord(
|
|
id = id,
|
|
name = name,
|
|
isSmart = isSmart,
|
|
smartRulesJson = smartRulesJson
|
|
)
|
|
}
|
|
|
|
fun BookShelfCrossRef.toSharedBookShelfRef(): SharedBookShelfRef {
|
|
return SharedBookShelfRef(
|
|
bookId = bookId,
|
|
shelfId = shelfId,
|
|
addedAt = addedAt
|
|
)
|
|
}
|
|
|
|
fun ReaderScreenState.toSharedReaderScreenState(
|
|
rawBooks: List<RecentFileItem> = rawLibraryFiles,
|
|
dbTags: List<TagEntity> = allTags
|
|
): SharedReaderScreenState {
|
|
return SharedReaderScreenState(
|
|
selectedBookId = selectedBookId,
|
|
selectedUriString = selectedPdfUri?.toString() ?: selectedEpubUri?.toString(),
|
|
selectedFileType = selectedFileType?.toSharedFileType(),
|
|
isLoading = isLoading,
|
|
errorMessage = errorMessage,
|
|
renderMode = renderMode.toSharedRenderMode(),
|
|
sortOrder = sortOrder.toSharedSortOrder(),
|
|
viewingShelfId = viewingShelfId,
|
|
isAddingBooksToShelf = isAddingBooksToShelf,
|
|
showCreateShelfDialog = showCreateShelfDialog,
|
|
mainScreenStartPage = mainScreenStartPage,
|
|
libraryScreenStartPage = libraryScreenStartPage,
|
|
showRenameShelfDialogFor = showRenameShelfDialogFor,
|
|
showDeleteShelfDialogFor = showDeleteShelfDialogFor,
|
|
addBooksSource = addBooksSource.toSharedAddBooksSource(),
|
|
booksSelectedForAdding = booksSelectedForAdding,
|
|
selectedBookIds = contextualActionItems.mapTo(mutableSetOf()) { it.bookId },
|
|
selectedShelfIds = contextualActionShelfIds,
|
|
isProUser = isProUser,
|
|
credits = credits,
|
|
isSyncEnabled = isSyncEnabled,
|
|
isFolderSyncEnabled = isFolderSyncEnabled,
|
|
bannerMessage = bannerMessage?.toSharedBannerMessage(),
|
|
downloadingBookIds = downloadingBookIds,
|
|
uploadingBookIds = uploadingBookIds,
|
|
syncedFolders = syncedFolders.map { it.toSharedSyncedFolder() },
|
|
lastFolderScanTime = lastFolderScanTime,
|
|
hasUnreadFeedback = hasUnreadFeedback,
|
|
searchQuery = searchQuery,
|
|
isSearchActive = isSearchActive,
|
|
isRefreshing = isRefreshing,
|
|
reflowProgress = reflowProgress,
|
|
recentBooks = recentFiles.map { it.toSharedBookItem() },
|
|
libraryBooks = allRecentFiles.map { it.toSharedBookItem() },
|
|
rawLibraryBooks = rawBooks.map { it.toSharedBookItem() },
|
|
pinnedHomeBookIds = pinnedHomeBookIds,
|
|
pinnedLibraryBookIds = pinnedLibraryBookIds,
|
|
libraryFilters = libraryFilters.toSharedLibraryFilters(),
|
|
recentFilesLimit = recentFilesLimit,
|
|
isTabsEnabled = isTabsEnabled,
|
|
openTabIds = openTabIds,
|
|
openTabs = openTabs.map { it.toSharedBookItem() },
|
|
activeTabBookId = activeTabBookId,
|
|
showExternalFileSavePromptFor = showExternalFileSavePromptFor,
|
|
externalFileBehavior = externalFileBehavior,
|
|
useStrictFileFilter = useStrictFileFilter,
|
|
appThemeMode = appThemeMode.toSharedAppThemeMode(),
|
|
appContrastOption = appContrastOption.toSharedAppContrastOption(),
|
|
appTextDimFactorLight = appTextDimFactorLight,
|
|
appTextDimFactorDark = appTextDimFactorDark,
|
|
appSeedColor = appSeedColor,
|
|
customAppThemes = customAppThemes.map { it.toSharedCustomAppTheme() },
|
|
allTags = dbTags.map { it.toSharedTag() },
|
|
showTagSelectionDialogFor = showTagSelectionDialogFor
|
|
)
|
|
}
|
|
|
|
fun ReaderScreenState.toSharedLibraryProjectionInput(
|
|
recentFilesFromDb: List<RecentFileItem>,
|
|
dbShelves: List<ShelfEntity>,
|
|
shelfRefs: List<BookShelfCrossRef>,
|
|
dbTags: List<TagEntity>,
|
|
tagRefs: List<BookTagCrossRef>
|
|
): SharedLibraryProjectionInput {
|
|
val tagsById = dbTags.associateBy { it.id }
|
|
val bookTagsMap = tagRefs.groupBy { it.bookId }.mapValues { entry ->
|
|
entry.value.mapNotNull { tagsById[it.tagId] }
|
|
}
|
|
val taggedBooks = recentFilesFromDb.map { item ->
|
|
item.copy(tags = bookTagsMap[item.bookId].orEmpty())
|
|
}
|
|
return SharedLibraryProjectionInput(
|
|
state = toSharedReaderScreenState(
|
|
rawBooks = taggedBooks,
|
|
dbTags = dbTags
|
|
),
|
|
booksFromStore = taggedBooks
|
|
.filterNot { it.bookId.endsWith("_reflow") }
|
|
.map { it.toSharedBookItem() },
|
|
shelfRecords = dbShelves.map { it.toSharedShelfRecord() },
|
|
shelfRefs = shelfRefs.map { it.toSharedBookShelfRef() },
|
|
tags = dbTags.map { it.toSharedTag() }
|
|
)
|
|
}
|
|
|
|
fun FileType.toSharedFileType(): SharedFileType {
|
|
return runCatching { SharedFileType.valueOf(name) }.getOrDefault(SharedFileType.UNKNOWN)
|
|
}
|
|
|
|
private fun RenderMode.toSharedRenderMode(): SharedRenderMode {
|
|
return SharedRenderMode.valueOf(name)
|
|
}
|
|
|
|
private fun AddBooksSource.toSharedAddBooksSource(): SharedAddBooksSource {
|
|
return SharedAddBooksSource.valueOf(name)
|
|
}
|
|
|
|
private fun SortOrder.toSharedSortOrder(): SharedSortOrder {
|
|
return SharedSortOrder.valueOf(name)
|
|
}
|
|
|
|
private fun ReadStatusFilter.toSharedReadStatusFilter(): SharedReadStatusFilter {
|
|
return SharedReadStatusFilter.valueOf(name)
|
|
}
|
|
|
|
private fun LibraryFilters.toSharedLibraryFilters(): SharedLibraryFilters {
|
|
return SharedLibraryFilters(
|
|
fileTypes = fileTypes.mapTo(mutableSetOf()) { it.toSharedFileType() },
|
|
sourceFolders = sourceFolders,
|
|
readStatus = readStatus.toSharedReadStatusFilter(),
|
|
tagIds = tagIds
|
|
)
|
|
}
|
|
|
|
private fun SyncedFolder.toSharedSyncedFolder(): SharedSyncedFolder {
|
|
return SharedSyncedFolder(
|
|
uriString = uriString,
|
|
name = name,
|
|
lastScanTime = lastScanTime,
|
|
allowedFileTypes = allowedFileTypes.mapTo(mutableSetOf()) { it.toSharedFileType() }
|
|
)
|
|
}
|
|
|
|
private fun BannerMessage.toSharedBannerMessage(): SharedBannerMessage {
|
|
return SharedBannerMessage(
|
|
message = message,
|
|
isError = isError,
|
|
isPersistent = isPersistent
|
|
)
|
|
}
|
|
|
|
private fun AppThemeMode.toSharedAppThemeMode(): SharedAppThemeMode {
|
|
return SharedAppThemeMode.valueOf(name)
|
|
}
|
|
|
|
private fun AppContrastOption.toSharedAppContrastOption(): SharedAppContrastOption {
|
|
return SharedAppContrastOption.valueOf(name)
|
|
}
|
|
|
|
private fun CustomAppTheme.toSharedCustomAppTheme(): SharedCustomAppTheme {
|
|
return SharedCustomAppTheme(
|
|
id = id,
|
|
name = name,
|
|
seedColor = seedColor
|
|
)
|
|
}
|