feat: Add the possibility to export and import the database#2
Merged
feat: Add the possibility to export and import the database#2
Conversation
- Add DatabaseExporter interface and implementation - Create platform-specific FileSaver for Android, iOS, and WASM - Add export/import UI in Settings screen with warning dialog - Support JSON format for human-readable backups - Add string resources in English and French - Add Upload/Download icons to GameIcons - Add SQL queries for bulk delete operations - Implement destructive import with user warning
- Create FeedbackMessage data class with FeedbackType enum (SUCCESS/ERROR) - Update SettingsViewModel to emit typed feedback messages using string resource keys - Add SnackbarHostState and LaunchedEffect to SettingsScreen - Display success/error snackbars with proper localization - Use app's existing snackbar infrastructure (AppSnackbarHost) - Success messages: green, auto-dismiss after 3 seconds - Error messages: red with dismiss button, auto-dismiss after 5 seconds - Resolve string resources in composable context for proper localization
- Wrap snackbar display in scope.launch to ensure proper coroutine context - Add 100ms delay before clearing feedback message - Move clearFeedbackMessage inside scope.launch to prevent premature clearing - Add missing imports: kotlinx.coroutines.launch and kotlinx.coroutines.delay This fixes the issue where snackbars weren't appearing on WASM due to: 1. Browser modal dialogs blocking UI thread during file operations 2. Synchronous file download triggering immediate success response 3. Feedback message being cleared before snackbar could render The fix follows the same pattern used in HomeNavigationTemplate and ensures the snackbar has time to render before the feedback state is cleared.
- Remove unnecessary scope.launch wrapper inside LaunchedEffect - LaunchedEffect already provides coroutine context for suspend functions - Follows the pattern used in YahtzeeScoringScreen and TarotScoringScreen - Fixes snackbars not appearing on WASM platform - Keeps 100ms delay for safety (harmless, may help with timing) Previous commit (384aade) incorrectly added nested scope thinking it was a timing issue, but this actually caused the bug by creating nested coroutine contexts that WASM's event loop doesn't handle well.
- Create rememberFileSaver() Composable for all platforms - Android: Get Activity from LocalContext.current - iOS/WASM: Return singleton FileSaver in remember block - Remove FileSaver field from SettingsViewModel - Add FileSaver parameter to exportDatabase() and importDatabase() - Update SettingsScreen to call rememberFileSaver() and pass to ViewModel This fixes the Android crash: UnsupportedOperationException: getFileSaver() cannot be called directly on Android. Use getFileSaver(activity: AppCompatActivity) instead. Root cause: SettingsViewModel tried to initialize FileSaver at construction time, but Android FileSaver requires Activity context which is only available during composition. Solution: Follow the pattern used by ShakeDetector and HapticFeedback - Move platform dependency to UI layer (Composable) - ViewModel receives FileSaver as function parameter (dependency injection) - Clean architecture: ViewModel stays platform-agnostic and testable Related files: - FileSaver.kt:32 - Added rememberFileSaver() expect declaration - FileSaver.android.kt:109-140 - Android implementation with LocalContext - FileSaver.ios.kt:131-134 - iOS implementation - FileSaver.wasmJs.kt:40-43 - WASM implementation - SettingsViewModel.kt:62,102 - Added fileSaver parameter - SettingsScreen.kt:76,163,220 - Use rememberFileSaver()
…rash - Replace activity.registerForActivityResult() with rememberLauncherForActivityResult() - Move FileSaver implementation from class to anonymous object in Composable - Use Channels for coordinating between launcher callbacks and suspend functions - Keep AndroidFileSaver class for backwards compatibility This fixes the Android crash: IllegalStateException: LifecycleOwner MainActivity is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED. Root cause: activity.registerForActivityResult() must be called BEFORE Activity reaches STARTED state, but we were calling it during composition when Activity was already RESUMED. Solution: rememberLauncherForActivityResult() is the Compose-native way to register activity result launchers. Compose runtime handles lifecycle timing automatically, ensuring registration happens at the correct moment. Technical changes: - rememberFileSaver() now creates anonymous FileSaver object - Uses rememberLauncherForActivityResult() for both CreateDocument and OpenDocument - Channels (createDocumentChannel, openDocumentChannel) coordinate async results - Same external API (saveJsonFile, pickJsonFile) - no breaking changes - Context from LocalContext.current used for ContentResolver operations Benefits: - Lifecycle-safe: No more registration timing issues - Compose best practices: Uses official Compose Activity integration - Simpler: No need to find Activity from Context hierarchy - Testable: Logic is self-contained in Composable Files changed: - FileSaver.android.kt:118-213 - Refactored rememberFileSaver() - Removed findActivity() extension (no longer needed) - Added import: rememberLauncherForActivityResult, Uri
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.