refactor: remove legacy folder sync migration and enhance and add cache maintenance tools to release builds (#83)
This commit is contained in:
parent
93f03941b2
commit
3509059d1f
2 changed files with 88 additions and 66 deletions
|
|
@ -158,6 +158,9 @@ fun HomeScreen(
|
||||||
var showSignOutConfirmDialog by remember { mutableStateOf(false) }
|
var showSignOutConfirmDialog by remember { mutableStateOf(false) }
|
||||||
var showAboutDialog by remember { mutableStateOf(false) }
|
var showAboutDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
var showClearBookCacheDialog by remember { mutableStateOf(false) }
|
||||||
|
var showClearReflowCacheDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val feedbackResult =
|
val feedbackResult =
|
||||||
navController.currentBackStackEntry?.savedStateHandle?.getLiveData<String>("banner_message")
|
navController.currentBackStackEntry?.savedStateHandle?.getLiveData<String>("banner_message")
|
||||||
?.observeAsState()
|
?.observeAsState()
|
||||||
|
|
@ -291,7 +294,7 @@ fun HomeScreen(
|
||||||
DefaultTopAppBar(
|
DefaultTopAppBar(
|
||||||
uiState = uiState,
|
uiState = uiState,
|
||||||
onRenderModeChange = viewModel::setRenderMode,
|
onRenderModeChange = viewModel::setRenderMode,
|
||||||
onClearCache = viewModel::clearBookCache,
|
onClearCache = { showClearBookCacheDialog = true },
|
||||||
onClearCloudData = { showClearAllDataDialog = true },
|
onClearCloudData = { showClearAllDataDialog = true },
|
||||||
onAboutClick = { showAboutDialog = true },
|
onAboutClick = { showAboutDialog = true },
|
||||||
onDrawerClick = {
|
onDrawerClick = {
|
||||||
|
|
@ -301,7 +304,7 @@ fun HomeScreen(
|
||||||
},
|
},
|
||||||
onShowDeviceManagement = viewModel::showDeviceManagementForDebug,
|
onShowDeviceManagement = viewModel::showDeviceManagementForDebug,
|
||||||
onFolderSyncToggle = viewModel::setFolderSyncEnabled,
|
onFolderSyncToggle = viewModel::setFolderSyncEnabled,
|
||||||
onClearReflowCache = viewModel::clearReflowCache
|
onClearReflowCache = { showClearReflowCacheDialog = true }
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ContextualTopAppBar(
|
ContextualTopAppBar(
|
||||||
|
|
@ -391,6 +394,30 @@ fun HomeScreen(
|
||||||
navController.navigate(AppDestinations.PRO_SCREEN_ROUTE)
|
navController.navigate(AppDestinations.PRO_SCREEN_ROUTE)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showClearBookCacheDialog) {
|
||||||
|
DangerousFolderActionDialog(
|
||||||
|
title = "Clear Book Cache",
|
||||||
|
message = "This will clear all processed page in pagination mode. This helps fix layout issues but will require books to be re-processed next time you open them.",
|
||||||
|
onConfirm = {
|
||||||
|
viewModel.clearBookCache()
|
||||||
|
showClearBookCacheDialog = false
|
||||||
|
},
|
||||||
|
onDismiss = { showClearBookCacheDialog = false }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showClearReflowCacheDialog) {
|
||||||
|
DangerousFolderActionDialog(
|
||||||
|
title = "Clear Reflow Cache",
|
||||||
|
message = "This will delete all generated 'Text View' versions of your PDFs and clear their associated images/HTML cache. Your original PDFs will remain untouched.",
|
||||||
|
onConfirm = {
|
||||||
|
viewModel.clearReflowCache()
|
||||||
|
showClearReflowCacheDialog = false
|
||||||
|
},
|
||||||
|
onDismiss = { showClearReflowCacheDialog = false }
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (showAboutDialog) {
|
if (showAboutDialog) {
|
||||||
|
|
@ -427,11 +454,6 @@ fun HomeScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (uiState.showFolderMigrationDialog) {
|
|
||||||
FolderMigrationDialog(
|
|
||||||
onConfirm = { viewModel.completeFolderMigration() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -693,7 +715,7 @@ fun DefaultTopAppBar(
|
||||||
onRenderModeChange: (RenderMode) -> Unit,
|
onRenderModeChange: (RenderMode) -> Unit,
|
||||||
onClearCache: () -> Unit,
|
onClearCache: () -> Unit,
|
||||||
onClearCloudData: () -> Unit,
|
onClearCloudData: () -> Unit,
|
||||||
onClearReflowCache: () -> Unit, // Add this parameter
|
onClearReflowCache: () -> Unit,
|
||||||
onDrawerClick: () -> Unit,
|
onDrawerClick: () -> Unit,
|
||||||
onAboutClick: () -> Unit,
|
onAboutClick: () -> Unit,
|
||||||
onShowDeviceManagement: () -> Unit,
|
onShowDeviceManagement: () -> Unit,
|
||||||
|
|
@ -725,20 +747,22 @@ fun DefaultTopAppBar(
|
||||||
showOptionsMenu = false
|
showOptionsMenu = false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
HorizontalDivider()
|
||||||
|
DropdownMenuItem(text = { Text("Clear Book Cache") }, onClick = {
|
||||||
|
onClearCache()
|
||||||
|
showOptionsMenu = false
|
||||||
|
})
|
||||||
|
DropdownMenuItem(text = { Text("Clear Reflow Cache") }, onClick = {
|
||||||
|
onClearReflowCache()
|
||||||
|
showOptionsMenu = false
|
||||||
|
})
|
||||||
|
|
||||||
if (BuildConfig.DEBUG && BuildConfig.FLAVOR != "oss") {
|
if (BuildConfig.DEBUG && BuildConfig.FLAVOR != "oss") {
|
||||||
HorizontalDivider()
|
HorizontalDivider()
|
||||||
DropdownMenuItem(text = { Text("[Debug] Show Device Management") }, onClick = {
|
DropdownMenuItem(text = { Text("[Debug] Show Device Management") }, onClick = {
|
||||||
onShowDeviceManagement()
|
onShowDeviceManagement()
|
||||||
showOptionsMenu = false
|
showOptionsMenu = false
|
||||||
})
|
})
|
||||||
DropdownMenuItem(text = { Text("[Debug] Clear Book Cache") }, onClick = {
|
|
||||||
onClearCache()
|
|
||||||
showOptionsMenu = false
|
|
||||||
})
|
|
||||||
DropdownMenuItem(text = { Text("[Debug] Clear Reflow Cache") }, onClick = {
|
|
||||||
onClearReflowCache()
|
|
||||||
showOptionsMenu = false
|
|
||||||
})
|
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text("[Debug] Clear Cloud & Local Data") },
|
text = { Text("[Debug] Clear Cloud & Local Data") },
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|
@ -1193,27 +1217,41 @@ fun FpsMonitor(modifier: Modifier = Modifier) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun FolderMigrationDialog(onConfirm: () -> Unit) {
|
fun DangerousFolderActionDialog(
|
||||||
|
title: String,
|
||||||
|
message: String,
|
||||||
|
onConfirm: () -> Unit,
|
||||||
|
onDismiss: () -> Unit
|
||||||
|
) {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = { },
|
onDismissRequest = onDismiss,
|
||||||
icon = { Icon(Icons.Default.FolderSpecial, contentDescription = null) },
|
icon = {
|
||||||
title = { Text("Folder Sync Update") },
|
Icon(
|
||||||
text = {
|
imageVector = Icons.Default.Info,
|
||||||
Column {
|
contentDescription = null,
|
||||||
Text(
|
tint = MaterialTheme.colorScheme.error
|
||||||
"We've improved Folder Sync! Books are now read directly from your folder without duplicating files."
|
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
},
|
||||||
|
title = {
|
||||||
Text(
|
Text(
|
||||||
"To keep your reading progress safe, your previously synced books have been converted to standard local books. You may see duplicates once the folder resyncs; you can safely delete the old copies at your convenience.",
|
text = title,
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
color = MaterialTheme.colorScheme.error
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
text = { Text(message) },
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = onConfirm,
|
||||||
|
colors = ButtonDefaults.textButtonColors(
|
||||||
|
contentColor = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text("Confirm & Clear")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
confirmButton = {
|
dismissButton = {
|
||||||
TextButton(onClick = onConfirm) {
|
TextButton(onClick = onDismiss) {
|
||||||
Text("Got it")
|
Text("Cancel")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,6 @@ import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
private const val KEY_RENDER_MODE = "render_mode"
|
private const val KEY_RENDER_MODE = "render_mode"
|
||||||
private const val KEY_FOLDER_SYNC_ENABLED = "folder_sync_enabled"
|
private const val KEY_FOLDER_SYNC_ENABLED = "folder_sync_enabled"
|
||||||
private const val KEY_FOLDER_MIGRATION_COMPLETED = "folder_migration_completed_v2"
|
|
||||||
|
|
||||||
private const val KEY_FILTER_FILE_TYPES = "filter_file_types"
|
private const val KEY_FILTER_FILE_TYPES = "filter_file_types"
|
||||||
private const val KEY_FILTER_FOLDERS = "filter_folders"
|
private const val KEY_FILTER_FOLDERS = "filter_folders"
|
||||||
|
|
@ -221,7 +220,6 @@ data class ReaderScreenState(
|
||||||
val hasUnreadFeedback: Boolean = false,
|
val hasUnreadFeedback: Boolean = false,
|
||||||
val searchQuery: String = "",
|
val searchQuery: String = "",
|
||||||
val isSearchActive: Boolean = false,
|
val isSearchActive: Boolean = false,
|
||||||
val showFolderMigrationDialog: Boolean = false,
|
|
||||||
val isRefreshing: Boolean = false,
|
val isRefreshing: Boolean = false,
|
||||||
val reflowProgress: Float? = null,
|
val reflowProgress: Float? = null,
|
||||||
val recentFiles: List<RecentFileItem> = emptyList(),
|
val recentFiles: List<RecentFileItem> = emptyList(),
|
||||||
|
|
@ -597,14 +595,8 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
|
||||||
|
|
||||||
remoteConfigRepository.init()
|
remoteConfigRepository.init()
|
||||||
|
|
||||||
val isMigrationCompleted = prefs.getBoolean(KEY_FOLDER_MIGRATION_COMPLETED, false)
|
|
||||||
|
|
||||||
if (_internalState.value.syncedFolders.isNotEmpty()) {
|
if (_internalState.value.syncedFolders.isNotEmpty()) {
|
||||||
if (isMigrationCompleted) {
|
|
||||||
syncFolderMetadata()
|
syncFolderMetadata()
|
||||||
} else {
|
|
||||||
Timber.d("App Start: Skipping sync. Waiting for migration/detachment logic.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModelScope.launch { billingClientWrapper.initializeConnection() }
|
viewModelScope.launch { billingClientWrapper.initializeConnection() }
|
||||||
|
|
@ -656,27 +648,6 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val migrationCompleted = prefs.getBoolean(KEY_FOLDER_MIGRATION_COMPLETED, false)
|
|
||||||
|
|
||||||
val hasFolders = _internalState.value.syncedFolders.isNotEmpty()
|
|
||||||
if (hasFolders && !migrationCompleted) {
|
|
||||||
Timber.tag("FolderSync").d("First time after refactor: Showing migration dialog.")
|
|
||||||
_internalState.update { it.copy(showFolderMigrationDialog = true) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun completeFolderMigration() {
|
|
||||||
Timber.tag("FolderSync")
|
|
||||||
.d("User acknowledged update. Detaching old books and starting fresh scan.")
|
|
||||||
|
|
||||||
viewModelScope.launch {
|
|
||||||
recentFilesRepository.detachAllFolderBooks()
|
|
||||||
|
|
||||||
prefs.edit { putBoolean(KEY_FOLDER_MIGRATION_COMPLETED, true) }
|
|
||||||
_internalState.update { it.copy(showFolderMigrationDialog = false) }
|
|
||||||
|
|
||||||
scanSyncedFolder()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDisplayPathFromUri(context: Context, uriString: String): String {
|
private fun getDisplayPathFromUri(context: Context, uriString: String): String {
|
||||||
|
|
@ -1482,7 +1453,7 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
|
||||||
|
|
||||||
_internalState.update {
|
_internalState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
syncedFolders = newStats, showFolderMigrationDialog = false
|
syncedFolders = newStats
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2623,7 +2594,6 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val existing = recentFilesRepository.getFileByBookId(reflowBookId)
|
val existing = recentFilesRepository.getFileByBookId(reflowBookId)
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
showBanner("Opening existing text view...")
|
|
||||||
if (autoOpenPage != null) {
|
if (autoOpenPage != null) {
|
||||||
switchToFileSeamlessly(existing, autoOpenPage)
|
switchToFileSeamlessly(existing, autoOpenPage)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -3809,8 +3779,22 @@ open class MainViewModel(application: Application) : AndroidViewModel(applicatio
|
||||||
imagesDir.deleteRecursively()
|
imagesDir.deleteRecursively()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val allFiles = recentFilesRepository.getAllFilesForSync()
|
||||||
|
val reflowBooks = allFiles.filter { it.bookId.endsWith("_reflow") }
|
||||||
|
|
||||||
|
if (reflowBooks.isNotEmpty()) {
|
||||||
|
val reflowBookIds = reflowBooks.map { it.bookId }
|
||||||
|
|
||||||
|
reflowBookIds.forEach { bookId ->
|
||||||
|
clearImportedFileCache(bookId)
|
||||||
|
pdfTextRepository.clearBookText(bookId)
|
||||||
|
}
|
||||||
|
|
||||||
|
recentFilesRepository.deleteFilePermanently(reflowBookIds)
|
||||||
|
}
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
showBanner("Reflow cache & images cleared.")
|
showBanner("Reflow cache & generated text views cleared.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue