Add native file dialog fs for tauri build#10
Open
CodeWithMa wants to merge 38 commits into
Open
Conversation
- Add storage adapter pattern with build-time provider selection - Implement LocalStorageAdapter for web builds (localStorage) - Implement TauriFileStorageAdapter for desktop builds (plugin-fs) - Implement native file dialogs via @tauri-apps/plugin-dialog - Add dialog and fs Rust plugins to Tauri backend - Update capabilities for dialog and fs permissions - Convert sync service methods to async for Tauri compatibility - Web build continues to use localStorage + blob download - Tauri build uses file system in AppData directory
The constructor was calling async loadData() without awaiting, causing data to be null when components tried to access it during initialization. Using APP_INITIALIZER ensures storage is loaded before any component is created, providing a clean Angular pattern for initialization.
The hidden file input was causing a second dialog to open because the adapter now handles its own file picking. Direct button click to importData() now delegates to the adapter.
The confirmation was being shown at the same time as file dialog because it was in the component while the adapter also opened a dialog. Now the confirm dialog is shown in the adapter before the file picker.
Now the flow is: select file -> confirm -> import The confirm is shown in the service after the adapter returns the data, not before the file picker opens.
- await updateSettings() in settings component to handle rejections - add setTimeout delay after confirm to ensure dialog is processed
Now returns true only when import actually completes. Component shows success message only when importData returns true, not when it silently returns on user cancellation.
The signal now holds the updated data with fresh timestamp, not the stale original data that was passed in.
StorageService now handles lastModifiedAt and ensureUngroupedGroup. Adapters are now simple pass-through for persistence, ensuring signal and persisted data are identical.
Parse and validation errors are now propagated instead of being swallowed, allowing the component to display error messages. User cancellation still returns null/false as expected.
The onchange handler's async function was throwing errors into a disconnected promise, causing the outer Promise to hang forever. Now uses reject(error) to properly reject the outer Promise.
Added 'cancel' event listener to resolve Promise with null when user dismisses the file dialog without selecting a file.
Interface now returns boolean (true=success, false=cancelled). Tauri adapter returns false when user cancels save dialog. Local adapter always returns true (download always succeeds). Service propagates boolean to component. Component only shows success when true is returned.
Create separate config files to avoid importing Tauri adapters in web builds: - app.config.ts: base config without adapter imports - app.config.web.ts: imports only local adapters - app.config.tauri.ts: imports only tauri adapters - angular.json: use fileReplacements to swap configs This ensures web builds don't bundle Tauri plugin dependencies.
Now both environment and app.config are replaced per build target, ensuring isTauri and enableServiceWorker settings are correct.
Single app.config.ts now uses environment to get adapter classes. Environment files contain only the adapter class references that differ: - base: no adapters (fallback for dev) - web: local adapters only - tauri: tauri adapters only This removes need for separate config files per build target.
The development configuration now uses environment.web.ts, matching the pattern used by development-tauri.
- Add LocalStorageAdapter and LocalImportExportAdapter to environment.ts - Make properties required (not optional) in Environment interface - Remove non-null assertions from app.config.ts - Remove fileReplacements from development config (base is sufficient)
Phase 1: Create shared async action helper - createAsyncAction() returns busy/error signals - withAsyncAction() wraps async functions with try/catch/finally - Supports parameterized functions Phase 2: Update storage service with resource() - Replace APP_INITIALIZER with resource() for initial load - dataResource provides loading/error/value signals - Remove manual loadData() method Phase 3: Update components with busy/error handling - home component: uses resource reload after writes - item-list component: async action for mark operations - item-card component: add disabled input for buttons Phase 4: Update base environment with default adapters - Base environment now has local adapters as defaults - Enables direct use without fileReplacements - development config no longer needs fileReplacements
Components were calling synchronous methods in constructors before the resource had loaded, causing empty data on initial render. Added effect() calls in: - HomeComponent: reactively update next items when data loads - GroupManagerComponent: reactively load groups when data loads - SettingsComponent: reactively load settings when data loads Also removed OnInit implementations that only called constructor logic.
Using reload() after save caused a brief loading state where data() returned undefined, creating a visual flicker. Now uses dataResource.set() to directly update the signal synchronously, preserving the current data while persisting. Effect() still fires to update derived state in components.
The busy signal still disables buttons to prevent concurrent operations, but the loading message is no longer shown. This prevents the visual flicker when operations complete quickly.
The old code called groupService.getAllGroups() in the constructor, which only had default data (ungrouped) at construction time. Now uses effect() that tracks this.groups() computed signal, so when storage data loads asynchronously, the effect fires and expands all groups.
The message now includes 'success' to properly trigger green styling.
Added error display in template and disabled state to buttons. Methods now wrapped: createGroup, saveEdit, deleteGroup, moveUp, moveDown. This provides error handling and prevents concurrent operations.
Adapters are instantiated with new in app.config.ts, not via Angular DI. The decorator was misleading.
Classes are instantiated with new in app.config.ts, not via Angular DI, so no decorator needed.
- getData() now throws during loading instead of returning default data - saveData() blocks and throws if data is still loading - Removed getDefaultData() - was a data corruption vector This ensures no writes can overwrite real data with empty default data during the async resource loading phase.
Effects now check if data exists before calling methods that depend on getData(). This prevents 'Data not loaded' errors during the initial async loading phase.
…-manager Styles mirror the settings component for consistent message appearance.
Now shows errors when settings save fails instead of silently swallowing them with showError: false.
- Add withAsyncAction to ItemDetailComponent (saveChanges, markWatched, markCompleted, deleteItem) - Add effect-based data loading to guard against async loading states - Add onError callback to withAsyncAction for rollback on failure - Fix updateShowCompleted to revert checkbox on save failure and show error message
Move validateStorageDataStructure, validateGroup, validateItem, validateWatchHistoryEntry, validateMigratedData, migrateDataOnly, ensureUngroupedGroup, and createDefaultData into shared/data-validation.ts and shared/data-migration.ts to eliminate duplication across import-export adapters, storage adapters, and services.
Remove redundant async/await wrapper and IIFE pattern from withAsyncAction calls in: - GroupManagerComponent (createGroup, saveEdit, deleteGroup, moveUp, moveDown) - AddItemComponent (onSubmit already correct) - ItemListComponent (markWatched, markCompleted already correct) This makes the async handling more direct and readable while preserving identical functionality.
… async error handling
- Rename error signal to message with {text, type} shape to eliminate
fragile .includes('success') string matching in templates
- Unify SettingsComponent to use withAsyncAction consistently instead
of manual try/catch with setTimeout
- Remove redundant try/catch in Tauri save and import adapters that
only logged and re-threw
- Remove duplicate adapter-level validation in import adapters (the
service already validates more thoroughly)
…data, and fix hanging promise on parse error - Move effect() from ngOnInit into constructor in ItemDetailComponent (Angular effect() requires an injection context) - Guard groups computed in ItemListComponent with null check to prevent 'Data not loaded' throw during initial async resource load - Add try/catch with reject() in LocalImportExportAdapter so JSON parse errors reject the promise instead of hanging indefinitely
CodeWithMa
added a commit
that referenced
this pull request
Apr 16, 2026
Replace all inline styles and CSS files with Tailwind utility classes. Configure Tailwind v4 with custom theme tokens preserving the existing color palette, class-based dark mode, and PostCSS integration.
CodeWithMa
added a commit
that referenced
this pull request
Apr 16, 2026
* Migrate from plain CSS to Tailwind CSS v4 (#10) Replace all inline styles and CSS files with Tailwind utility classes. Configure Tailwind v4 with custom theme tokens preserving the existing color palette, class-based dark mode, and PostCSS integration.
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.