Compare commits

...

372 Commits

Author SHA1 Message Date
9b69c0b22c bump to version 0.5.9 2025-06-20 10:41:48 -06:00
ab2270d8b2 IndexedDB migration: fix where the existing settings (eg. master) were not updated 2025-06-20 06:51:33 -06:00
0cf5cf266d IndexedDB migration: don't run activeDid migration twice, include warnings in output, don't automatically compare afterward 2025-06-20 06:26:56 -06:00
Matthew Raymer
4d01f64fe7 feat: Implement activeDid migration from Dexie to SQLite
- Add migrateActiveDid() function for dedicated activeDid migration
- Enhance migrateSettings() to handle activeDid extraction and validation
- Update migrateAll() to include activeDid migration step
- Add comprehensive error handling and validation
- Update migration documentation with activeDid migration details
- Ensure user identity continuity during migration process

Files changed:
- src/services/indexedDBMigrationService.ts (153 lines added)
- doc/migration-to-wa-sqlite.md (documentation updated)

Migration order: Accounts -> Settings -> ActiveDid -> Contacts
2025-06-20 04:48:51 +00:00
Matthew Raymer
d1f61e3530 docs: Update migration documentation with fence definition and security checklist - Add comprehensive migration fence definition with clear boundaries - Update migration guide to reflect current Phase 2 status - Create security audit checklist for migration process - Update README with migration status and architecture details - Document migration fence enforcement and guidelines - Add security considerations and compliance requirements 2025-06-20 03:30:43 +00:00
4162208b7f fix linting 2025-06-19 19:06:37 -06:00
731605e244 IndexedDB migration: add a blurb about backing up, and move around messaging 2025-06-19 18:55:07 -06:00
5dd2bf4c6e IndexedDB migration: enhance comparison for contacts 2025-06-19 18:47:26 -06:00
75a4a1d901 IndexedDB migration: fix settings comparison 2025-06-19 18:24:49 -06:00
8e605d04d7 IndexedDB migration: fix settings update 2025-06-19 18:12:56 -06:00
da0b244bae IndexedDB migration: implement the migrations differently 2025-06-19 17:26:30 -06:00
6136cafd11 IndexedDB migration: fix loading of data, fix object comparisons, add unmodified, etc 2025-06-19 16:19:00 -06:00
a248e9a5a3 IndexedDB migration: ensure output is printed during comparison (no logic changes) 2025-06-19 12:55:01 -06:00
452ae555bb IndexedDB migration: add ability to see mnemonic and download settings & contacts 2025-06-19 12:37:41 -06:00
3df5e19d9d IndexedDB migration: extract IndexedDB code away from the ongoing SQLite migrations 2025-06-19 11:11:59 -06:00
8eff407a9c IndexedDB migration: reorder the sections to accounts then settings then contacts 2025-06-19 09:54:09 -06:00
e759e4785b IndexedDB migration: set USE_DEXIE_DB to false, remove unused functions, add raw display of data 2025-06-19 09:47:18 -06:00
Matthew Raymer
9d054074e4 fix(migration): update UI to handle transformed JSON format
The DatabaseMigration view has been updated to properly handle both live
comparison data and exported JSON format, fixing count mismatches and
field name differences.

Changes:
- Added helper methods in DatabaseMigration.vue to handle both data formats:
  - getSettingDisplayName() for settings with type/did or activeDid/accountDid
  - getAccountHasIdentity() and getAccountHasMnemonic() for boolean fields
- Updated template to use new helper methods for consistent display
- Added exportComparison() method to handle JSON export format
- Fixed settings count display to match actual data state

Technical Details:
- Settings now handle both 'type'/'did' (JSON) and 'activeDid'/'accountDid' (live)
- Account display properly shows boolean values from either format
- Export functionality preserves data structure while maintaining readability

Resolves count mismatch between UI (showing 1 SQLite setting) and JSON data
(showing 0 SQLite settings).

Testing:
- Verified UI displays correct counts from both live and exported data
- Confirmed settings display works with both data formats
- Validated account boolean fields display correctly
2025-06-19 14:45:58 +00:00
Matthew Raymer
30de30e709 fix: maintain separate master/account settings in SQLite migration
- Update settings migration to maintain separate master and account records
- Use activeDid/accountDid pattern to differentiate between settings types:
  * Master settings: activeDid set, accountDid empty
  * Account settings: accountDid set, activeDid empty
- Add detailed logging for settings migration process
- Focus on migrating key fields: firstName, isRegistered, profileImageUrl,
  showShortcutBvc, and searchBoxes
- Fix issue where settings were being incorrectly merged into a single record

This change ensures the SQLite database maintains the same settings structure
as Dexie, which is required by the existing codebase.
2025-06-19 14:11:11 +00:00
Matthew Raymer
6cbd32af94 feat: improve database migration comparison and UI display
- Enhanced migration service to filter empty/default records from comparisons
- Updated comparison output to exclude records without meaningful data
- Improved UI display with better visual hierarchy and count indicators
- Added security improvements to prevent sensitive data exposure
- Fixed settings migration to properly handle MASTER_SETTINGS_KEY vs account DIDs
- Updated verification display to show filtered counts and detailed differences
- Improved data formatting for contacts, settings, and accounts sections
- Added proper filtering for missing records to avoid counting empty entries

Changes:
- Filter SQLite records to only include those with actual DIDs/data
- Update comparison counts to reflect meaningful differences only
- Enhance UI with count indicators and better visual organization
- Replace sensitive data display with boolean flags (hasIdentity, hasMnemonic)
- Fix settings migration logic for proper DID field handling
- Improve verification message to show detailed breakdown by type
- Add proper filtering for missing records in all data types

Security: Prevents exposure of mnemonics, private keys, and identity data
UI/UX: Cleaner display with better information hierarchy and counts
Migration: More accurate comparison results and better debugging visibility
2025-06-19 12:44:32 +00:00
Matthew Raymer
30c8b73041 feat: implement single-step migration with proper foreign key order
- Add migrateAll() function that handles complete migration in correct order:
  Accounts → Settings → Contacts to avoid foreign key constraint issues
- Add prominent "Migrate All (Recommended)" button to migration UI
- Add informational section explaining migration order and rationale
- Add info icon to icon set for UI clarity
- Improve migration logic to handle overwriteExisting parameter properly:
  - New records are always migrated regardless of checkbox setting
  - Existing records are only updated when overwriteExisting=true
  - Clear warning messages when records are skipped
- Maintain backward compatibility with individual migration buttons
- All code linted and formatted according to project standards

Co-authored-by: Matthew Raymer
2025-06-19 08:52:55 +00:00
Matthew Raymer
2f9ab14c88 fix: resolve migration service import and function signature conflicts
- Fix import in src/db-sql/migration.ts to use named imports and alias runMigrations to avoid naming conflict
- Add missing migration management functions (registerMigration, runMigrations) to migrationService with full typing and logging
- Update function signatures to accept SQL parameters for compatibility with AbsurdSqlDatabaseService
- Clean up Prettier formatting issues in migrationService and migration.ts
- Confirmed dev server and linter run cleanly

Co-authored-by: Matthew Raymer
2025-06-19 08:32:14 +00:00
Matthew Raymer
8a7f142cb7 feat: integrate importFromMnemonic utility into migration service and UI
- Add account migration support to migrationService with importFromMnemonic integration
- Extend DataComparison and MigrationResult interfaces to include accounts
- Add getDexieAccounts() and getSqliteAccounts() functions for account retrieval
- Implement compareAccounts() and migrateAccounts() functions with proper error handling
- Update DatabaseMigration.vue UI to support account migration:
  - Add "Migrate Accounts" button with lock icon
  - Extend summary cards grid to show Dexie/SQLite account counts
  - Add Account Differences section with added/modified/missing indicators
  - Update success message to include account migration counts
  - Enhance grid layouts to accommodate 6 summary cards and 3 difference sections

The migration service now provides complete data migration capabilities
for contacts, settings, and accounts, with enhanced reliability through
the importFromMnemonic utility for mnemonic-based account handling.
2025-06-19 06:13:25 +00:00
Matthew Raymer
f375a4e11a feat: move database migration link from account view to start view
- Remove database migration link from AccountViewView.vue
- Add new "Database Tools" section to StartView.vue
- Improve user flow by making database tools accessible from start page
- Maintain consistent styling and functionality
- Clean up account view to focus on account-specific settings

The database migration feature is now logically grouped with other
identity-related operations and more discoverable for users.
2025-06-19 05:51:59 +00:00
Matthew Raymer
40a2491d68 feat: Add comprehensive Database Migration UI component
- Create DatabaseMigration.vue with vue-facing-decorator and Tailwind CSS
- Add complete UI for comparing and migrating data between Dexie and SQLite
- Implement real-time loading states, error handling, and success feedback
- Add navigation link to Account page for easy access
- Include export functionality for comparison data
- Create comprehensive documentation in doc/database-migration-guide.md
- Fix all linting issues and ensure code quality standards
- Support both contact and settings migration with overwrite options
- Add visual difference analysis with summary cards and detailed breakdowns

The component provides a professional interface for the migrationService.ts,
enabling users to safely transfer data between database systems during
the transition from Dexie to SQLite storage.
2025-06-18 11:19:34 +00:00
Matthew Raymer
25c1d6ef4e feat: Add comprehensive database migration service for Dexie to SQLite
- Add migrationService.ts with functions to compare and transfer data between Dexie and SQLite
- Implement data comparison with detailed difference analysis (added/modified/missing)
- Add contact migration with overwrite options and error handling
- Add settings migration focusing on key user fields (firstName, isRegistered, profileImageUrl, showShortcutBvc, searchBoxes)
- Include YAML export functionality for data inspection
- Add comprehensive JSDoc documentation with examples and usage instructions
- Support both INSERT and UPDATE operations with parameterized SQL generation
- Include detailed logging and error reporting for migration operations

This service enables safe migration of user data from the legacy Dexie (IndexedDB)
database to the new SQLite implementation, with full comparison capabilities
and rollback safety through detailed reporting.
2025-06-18 10:54:32 +00:00
a5c5c2b9dd bump to build 33 and version 0.5.7 2025-06-18 02:34:18 -06:00
cf33a39fbc fix the invite-one setup, fix adeep link, and tweak other verbiage & error info 2025-06-18 02:32:47 -06:00
8629cefa13 bump to build 32 & version 0.5.6 2025-06-17 05:25:45 -06:00
5e851e442f shrink the contents of the QR code so people can scan it 2025-06-16 15:38:11 -06:00
4a43bc9c6c bump build to 31 and version to 0.5.5 2025-06-16 07:38:16 -06:00
60de8cee62 reword some of the help-page introduction (no code changes) 2025-06-16 07:24:37 -06:00
Jose Olarte III
bb2a4ab76e URL scheme config for iOS
- Registers the timesafari:// URL scheme
- Sets the bundle URL name to app.timesafari
2025-06-16 16:09:08 +08:00
Matthew Raymer
048dded278 fix: resolve deep link route mismatch for project links
- Fix schema validation mismatch between "project-details" and "project"
- Update VALID_DEEP_LINK_ROUTES to include "project" instead of "project-details"
- Update deepLinkSchemas to use "project" route name
- Update documentation to reflect correct route name
- Resolves "Invalid route path: project" errors in deep link handling

The deep link timesafari://project/01JWH0YAB3MAGBD751VAJAXQ17 now works correctly
and routes to the ProjectViewView component as expected.

Fixes: Deep link validation errors for project routes
2025-06-16 05:48:13 +00:00
e240c2940a remove unused deep links and add another 2025-06-15 13:54:12 -06:00
54dca9e745 fix project deep-link (and reorder alphabetically) 2025-06-15 11:02:16 -06:00
9f0fed0a60 update ios check to work, and add links to app stores 2025-06-14 22:10:49 -06:00
0d152adbf2 remove the deep-link autoVerify because it caused a build failure 2025-06-14 22:06:12 -06:00
cead308800 incorporate one of the BUILDING steps directly into the file 2025-06-13 22:37:03 -06:00
676a301331 bump to build 30 version 0.5.4 2025-06-13 22:36:28 -06:00
d6db81cc36 fix some result types and refactor types themselves 2025-06-13 21:58:57 -06:00
Matthew Raymer
f2ddcd2541 feat: add conditional rendering for claim certificate link and update gitignore
- Add v-if directive to show claim certificate link only when veriClaim.id exists
- Update .gitignore to exclude android app resource directory
- Prevents broken links when claim data is not fully loaded
- Improves build process by ignoring generated Android resources

This change ensures the certificate link is only displayed when there's
valid claim data available, preventing navigation errors and improving
user experience. The gitignore update helps keep the repository clean
by excluding Android-specific generated files.
2025-06-14 03:31:12 +00:00
fb81f7b96e fix problems with :href links causing the app to reload for DB errors on mobile 2025-06-13 20:39:12 -06:00
a23416ead1 fix optional message at top to not overflow 2025-06-12 20:10:31 -06:00
530c7c1a13 fix problem with user-profile page, and bump to build 29 & version 0.5.3 2025-06-12 19:16:02 -06:00
f255ea389b bump to build 26 and version 0.5.1 2025-06-11 00:46:46 -06:00
0d343b9877 Merge pull request 'fix creation of did-specific settings (with a rename)' (#138) from fix-did-specifics into master
Reviewed-on: #138
2025-06-11 02:14:41 -04:00
df06100c32 remove more debugging 2025-06-10 23:49:14 -06:00
Matthew Raymer
ac5ddfc6f2 style: fix line length in ContactsView ternary operator
- Break long CSS class strings into multiple concatenated lines
- Ensure all lines are under 100 characters for better readability
- Maintain same functionality and styling behavior
- Improve code maintainability and readability

Fixes: Long lines in conditional CSS class assignment
2025-06-11 05:45:58 +00:00
Matthew Raymer
89b3f30466 fix: debug and clean up GiftedPrompts contact retrieval logic
- Add comprehensive debug logging to identify contact list population issues
- Fix array indexing bug in contact mapping (someContactDbIndex -> 0)
- Clean up all console.log statements for production readiness
- Improve contact retrieval debugging for SQLite and Dexie databases
- Maintain core functionality while adding diagnostic capabilities

Debugging: Contact list population issues in GiftedPrompts component
Cleanup: Remove debug console.log statements
2025-06-11 05:40:05 +00:00
Matthew Raymer
3cb5cc096b refactor: use databaseUtil.updateDefaultSettings for feed filter settings
- Replace direct platform service calls with databaseUtil.updateDefaultSettings
- Remove manual SQL query construction in favor of centralized utility
- Improve code consistency and maintainability
- Add proper error handling through databaseUtil's built-in mechanisms
- Remove unused PlatformServiceFactory import
- Fix SQL syntax errors in clearAll and setAll methods (AND -> comma)
- Ensure both SQLite and Dexie databases are updated consistently

Improves: FeedFilters component architecture and error handling
Fixes: isNearby and filterFeedByVisible settings not being saved properly
2025-06-11 05:19:15 +00:00
Matthew Raymer
5df560154f fix: resolve cross-platform contactMethods JSON parsing inconsistencies
- Add platform-agnostic parseJsonField utility for contactMethods handling
- Update contact export functions (contactsToExportJson, contactToCsvLine)
- Fix contact storage in QR scan views (ContactQRScanShowView, ContactQRScanFullView)
- Ensure consistent JSON string storage across web SQLite and Capacitor SQLite
- Prevents "[object Object] is not valid JSON" errors when switching platforms
- Maintains compatibility between auto-parsing web SQLite and raw string Capacitor SQLite

Fixes: contactMethods parsing errors in export and QR scan functionality
Related: searchBoxes field had similar issue (already fixed)
2025-06-11 04:17:38 +00:00
Matthew Raymer
c1aa522e6c fix: resolve cross-platform SQLite JSON parsing inconsistencies
- Add platform-agnostic parseJsonField utility to handle different SQLite implementations
- Web SQLite (wa-sqlite/absurd-sql) auto-parses JSON strings to objects
- Capacitor SQLite returns raw strings requiring manual parsing
- Update searchBoxes parsing to use new utility for consistent behavior
- Fixes "[object Object] is not valid JSON" error when switching platforms
- Ensures compatibility between web and mobile SQLite implementations

Fixes: searchBoxes parsing errors in databaseUtil.ts
Related: contactMethods field has similar issue (needs same treatment)
2025-06-11 03:44:28 +00:00
a082469a01 fix creation of did-specific settings (with a rename) 2025-06-10 20:51:22 -06:00
Jose Olarte III
3544d7278d Optimized item actions
- Edited button labels for brevity
- Repositioned Totals toggle
- Restyled note about recent hours
- Various text size and spacing changes
2025-06-10 19:54:05 +08:00
Jose Olarte III
d3110506ea Optimized per-item layout
- Stacked contact name and DID
- Text truncates to leave room for action buttons when visible
- Separated "from / to" heading from buttons to minimize width
- Various spacing and alignment adjustments
2025-06-10 18:42:49 +08:00
8609f8458d bump to build 25 & version 0.5.0 2025-06-09 09:26:21 -06:00
8f5c34bc5f fix linting 2025-06-09 09:09:54 -06:00
b0d61b95ea Merge branch 'ui-fixes-2025-06-w2' 2025-06-09 08:44:42 -06:00
af7bd236a3 fix check for successful gift submission 2025-06-09 08:41:47 -06:00
d719338bcc fix problem setting 'loading' flag 2025-06-09 08:37:42 -06:00
6ddf2d1012 fix problem switching IDs (creating too many settings) 2025-06-09 08:33:33 -06:00
Jose Olarte III
1b2d4b623a Turned off automatic safe area in iOS
- Safe area implementations will solely depend on CSS styles for full control
- Eliminates doubled top and bottom padding in iOS
2025-06-09 20:20:17 +08:00
Jose Olarte III
16d5c917d2 Updated QR scanner call
- Searched for all other (outdated) calls to QR scanner dialog and updated them
- Fixed vite HTML spec warning
2025-06-09 19:36:06 +08:00
5976a4995e fix problem clicking on offer-delivery, plus some other hardening and phrasing 2025-06-08 20:13:32 -06:00
dcd0cc4c20 fix import for derived accounts and hopefully make other account-access code more robust 2025-06-08 19:14:41 -06:00
b3ca6c9d91 remove relative URL references in different target because mobile chokes 2025-06-08 16:55:03 -06:00
e9d800f601 fix a web test (all passing now) 2025-06-07 21:41:43 -06:00
b939a5e592 bump build to 23 and version to 0.4.8 2025-06-07 18:54:56 -06:00
aa62037fae bump to build 22 version 0.4.7 (though I think the android capacitor.config.json appId is wrong) 2025-06-07 18:45:24 -06:00
722020ea86 fix linting 2025-06-07 18:09:04 -06:00
96aa3f4a54 add Python dependency for electron on Mac 2025-06-07 17:54:31 -06:00
c0c5f9842b fix some errors and correct recent type duplications & bloat (cherry-picked from d8f2587d1c) 2025-06-07 17:53:36 -06:00
be27ca1855 fix more logic for tests (cherry-picked from 83acb028c7) 2025-06-07 17:42:06 -06:00
92e4570672 fix some incorrect logic & things AI hallucinated 2025-06-07 17:39:10 -06:00
820ae727ed fix linting 2025-06-07 17:19:01 -06:00
dbeb1c6b4b Merge branch 'sql-absurd-sql-back' 2025-06-07 17:18:10 -06:00
573e4b206a Merge branch 'search-map-fix' 2025-06-07 16:43:49 -06:00
abc05d426e update messaging for unknown icons on home feed 2025-06-07 16:23:07 -06:00
2ea7479d75 fix verbiage and fix the contact actions to be on the right-hand side 2025-06-07 16:03:47 -06:00
9ac9713172 fix linting 2025-06-07 15:40:52 -06:00
41dad3254d fix a non-existent description, move the description right below the image 2025-06-07 15:33:29 -06:00
485eac59a0 remove unnecessary data element from export 2025-06-07 14:02:22 -06:00
Matthew Raymer
73fc32b75d fix(import): ensure contact import works for both Dexie and absurd-sql backends
- Refactor importContacts to handle both Dexie and absurd-sql (SQLite) storage
- Add ContactDbRecord interface with all string fields strictly typed (never null)
- Add helper functions to coerce null/undefined to empty string for all string fields
- Guarantee contactMethods is always stored as a JSON string (never null)
- Add runtime validation for required fields (e.g., did)
- Ensure imported/updated contacts are type-safe and compatible with both backends
- Improve code documentation and maintainability

Security:
- No sensitive data exposed
- All fields validated and sanitized before database write
- Consistent data structure across storage backends

Testing:
- Import tested with both Dexie and absurd-sql backends
- Null/undefined fields correctly handled and coerced
- No linter/type errors remain
2025-06-07 06:01:17 +00:00
Matthew Raymer
3d8e40e92b feat(export): Replace CSV export with standardized JSON format
- Add contactsToExportJson utility function for standardized data export
- Replace CSV export with JSON format in DataExportSection
- Update file extension and MIME type to application/json
- Remove Dexie-specific export logic in favor of unified SQLite/Dexie approach
- Update success notifications to reflect JSON format
- Add TypeScript interfaces for export data structure

This change improves data portability and standardization by:
- Using a consistent JSON format for data export/import
- Supporting both SQLite and Dexie databases
- Including all contact fields in export
- Properly handling contactMethods as stringified JSON
- Maintaining backward compatibility with existing import tools

Security: No sensitive data exposure, maintains existing access controls
2025-06-07 05:02:33 +00:00
38e67f3533 update a DB save to match others, ie. first SQL then maybe Dexie 2025-06-06 19:50:16 -06:00
7f63ee7c80 add another way to fix the privacy policy manifest for third parties like GoogleToolboxForMac 2025-06-06 19:45:41 -06:00
6a47f0d3e7 format total numbers better 2025-06-06 19:40:53 -06:00
fc50a9d4c6 fix problem finding offer identifiers 2025-06-06 19:06:29 -06:00
Jose Olarte III
45f43ff363 Updated icon and splash assets 2025-06-06 18:15:42 +08:00
Jose Olarte III
7b1d4c4849 Adjusted iOS-specific paddings
- Switched to CSS max() for proper conditional padding when dealing with screens that have a notch, dynamic island, gesture bar, etc.
- Top padding should now appear more compact in iOS
2025-06-06 18:14:56 +08:00
Matthew Raymer
c1f2c3951a feat(db): improve settings retrieval resilience and logging
Enhance retrieveSettingsForActiveAccount with better error handling and logging
while maintaining core functionality. Changes focus on making the system more
debuggable and resilient without overcomplicating the logic.

Key improvements:
- Add structured error handling with specific try-catch blocks
- Implement detailed logging with [databaseUtil] prefix for easy filtering
- Add graceful fallbacks for searchBoxes parsing and missing settings
- Improve error recovery paths with safe defaults
- Maintain existing security model and data integrity

Security:
- No sensitive data in logs
- Safe JSON parsing with fallbacks
- Proper error boundaries
- Consistent state management
- Clear fallback paths

Testing:
- Verify settings retrieval works with/without active DID
- Check error handling for invalid searchBoxes
- Confirm logging provides clear debugging context
- Validate fallback to default settings works
2025-06-06 09:22:35 +00:00
9d4f726c31 bump to build # 19 version 0.4.7 for mobile packages 2025-06-05 20:30:27 -06:00
1d7f626645 fix SQL references to bad "key" -> "id" 2025-06-05 20:11:32 -06:00
c5228ba7ec fix retrieval of column names -- so now most ops are working (but not all, eg. set name) 2025-06-05 20:06:32 -06:00
6e1fcd8dee remove unused DB methods (for now) 2025-06-05 20:00:51 -06:00
5bb563d694 fix extraction of values from SQLite queries 2025-06-05 19:57:59 -06:00
a3951c9d66 refactor for clarity (no logic changes) 2025-06-05 18:30:25 -06:00
aa177a9b8c fix extraction of migration names for SQLite via Capacitor 2025-06-05 18:13:33 -06:00
03cb4720b8 fix Capacitor to use the same migrations (migrations run but accounts aren't created) 2025-06-04 22:01:14 -06:00
Jose Olarte III
0e65431f43 Map z-index fix + adjustments
- Set map z-index lower than nav
- Relocated search box to account for conditional visibility
- Various conditional fixes
- Spacing adjustments
2025-06-04 18:41:49 +08:00
297c5a2dbb disable SQLite in Java & Swift (since they don't compile) & add SQL queueing on startup
At this point, the app compiles and runs in Android & iOS but DB operations fail.
2025-06-03 19:59:28 -06:00
Jose Olarte III
92b9c9334c Clickable person & project icons
- Known entities get routed to their corresponding detail views
- Unknown entities pop up a notification
2025-06-02 21:35:15 +08:00
Jose Olarte III
706182ca0c Icon for hidden DID entity
- Display EntityIcon for known entities, eye-slash icon for hidden entities, and the person-question icon for unknown entities
- Design tweaks (spacings, mostly)
2025-06-02 17:44:37 +08:00
Matthew Raymer
68e0fc4976 merge(master): big merge for qrcode-reboot 2025-06-02 03:57:00 +00:00
504056eb90 add some time to test 30 (but shrink the per-loop timeout) 2025-06-01 15:08:32 -06:00
5a1007c49c add iOS development team ID 2025-06-01 14:29:32 -06:00
Jose Olarte III
cbc14e21ec Look in .own.did for DID, as well 2025-05-30 17:34:50 +08:00
ef3bfcdbd2 fix linting 2025-05-28 20:30:00 -06:00
ec1f27bab1 fix more logging cleanup errors 2025-05-28 20:20:09 -06:00
01c33069c4 fix more of the logging & log display 2025-05-28 20:08:09 -06:00
c637d39dc9 fix log cleanup check to actually pay attention to limit 2025-05-28 19:44:16 -06:00
3e90bafbd1 correct & simplify the DB logging 2025-05-28 19:37:01 -06:00
Matthew Raymer
d2c3e5db05 fix: one lint that got past me 2025-05-28 14:03:21 +00:00
Matthew Raymer
e824fcce2e fix: linting issues 2025-05-28 14:02:02 +00:00
Matthew Raymer
f2c49872a6 fix: resolve TypeScript errors in database service implementation
- Remove unnecessary generic type parameter from AbsurdSqlDatabaseService
- Fix type handling in operation queue and result processing
- Correct WebPlatformService dbGetOneRow implementation to use imported databaseService
- Add proper type annotations for database operation results

This commit improves type safety and fixes several TypeScript errors that were
preventing proper type checking in the database service layer.
2025-05-28 13:20:01 +00:00
Matthew Raymer
229d9184b2 WIP: BROKEN FOR ELECTRON: Fixes in progress 2025-05-28 13:09:51 +00:00
Matthew Raymer
29908b77e3 feat(types): add comprehensive type definitions for @jlongster/sql.js
- Add FileSystem and FileStream interfaces for filesystem operations

- Update Database interface with proper Promise-based return types

- Add QueryExecResult interface for structured query results

- Include FS and register_for_idb in initialization result

- Fix Database constructor to support path and options parameters

- Add proper JSDoc documentation with author and description

This change resolves TypeScript compilation errors in AbsurdSqlDatabaseService by providing complete type coverage for the SQL.js WASM module with filesystem support.
2025-05-28 11:16:56 +00:00
Jose Olarte III
3e02b3924a Look for DID in .iss field instead of .own.did 2025-05-28 19:08:15 +08:00
Matthew Raymer
16cad04e5c WIP: fix(AbsurdSqlDatabaseService) fixes to typing and other curious beasts 2025-05-28 10:56:27 +00:00
Matthew Raymer
e4f859a116 fix: update offerGiverDid to use credentialSubject.offeredBy
The offerGiverDid function was looking for offeredBy at the root level of the
OfferVerifiableCredential, but it was moved to credentialSubject in our interface
changes. This fix updates the function to look in the correct location while
maintaining the same fallback behavior of using the issuer if offeredBy is not
available.

- Update path from claim.offeredBy to claim.credentialSubject.offeredBy
- Remove unnecessary string type cast
- Keep issuer fallback behavior unchanged
2025-05-28 10:41:39 +00:00
Matthew Raymer
7f17a3d9c7 refactor: remove unused imports and parameters in passkeyDidPeer.ts
- Remove unused imports:
  - DIDResolutionResult from did-resolver
  - sha256 from ethereum-cryptography/sha256.js
- Remove unused parameters from verifyJwtP256 and verifyJwtWebCrypto:
  - credIdHex
  - clientDataJsonBase64Url
  - credId

This change improves code cleanliness by removing unused code while
maintaining the core passkey authentication functionality.
2025-05-28 10:37:01 +00:00
Matthew Raymer
2d4d9691ca fix: use challenge parameter in verifyJwtWebCrypto preimage
- Remove unused client data hashing in verifyJwtWebCrypto
- Use challenge parameter directly in preimage construction
- Fix TS6133 error for unused challenge parameter
- Make verification logic consistent with verifyJwtP256

This change maintains the same verification logic while properly
utilizing the challenge parameter in the signature verification.
2025-05-28 10:28:57 +00:00
Matthew Raymer
63575b36ed fix: use challenge parameter in verifyJwtP256 preimage
- Remove unused client data hashing in verifyJwtP256
- Use challenge parameter directly in preimage construction
- Fix TS6133 error for unused challenge parameter

This change maintains the same verification logic while properly
utilizing the challenge parameter in the signature verification.
2025-05-28 10:27:19 +00:00
Matthew Raymer
2eb46367bc fix: resolve TypeScript errors in passkeyDidPeer.ts
- Fix import path for VerifyAuthenticationResponseOpts to use main package
- Add proper type assertions for credential response using AuthenticatorAssertionResponse
- Add back p256 import from @noble/curves/p256
- Remove unused functions marked with @typescript-eslint/no-unused-vars:
  - peerDidToDidDocument
  - COSEtoPEM
  - base64urlDecodeArrayBuffer
  - base64urlEncodeArrayBuffer
  - pemToCryptoKey

This change improves type safety and removes dead code while maintaining
the core passkey authentication functionality.
2025-05-28 10:24:17 +00:00
Matthew Raymer
cea0456148 fix: resolve type conflicts in AccountKeyInfo and KeyMeta imports
- Update AccountKeyInfo interface to handle derivationPath type conflict
- Fix circular dependency by importing KeyMeta directly from interfaces/common
- Use Omit utility type to properly merge Account and KeyMeta types
- Make derivationPath optional in AccountKeyInfo to match Account type

This change resolves type compatibility issues while maintaining
the intended functionality of account metadata handling.
2025-05-28 10:17:20 +00:00
Matthew Raymer
6f5db13a49 fix: consolidate KeyMeta interface and improve type safety
- Remove duplicate KeyMeta interface from crypto/vc/index.ts
- Import KeyMeta from common.ts as single source of truth
- Add missing fields to KeyMeta interface (identity, passkeyCredIdHex)
- Remove unused ErrorResponse interface from endorserServer.ts
- Fix import path for KeyMeta in crypto/vc/index.ts

This change resolves type compatibility issues and ensures consistent
KeyMeta type usage across the codebase.
2025-05-28 10:13:01 +00:00
Matthew Raymer
068662625d fix: resolve type compatibility in offerGiverDid
- Update offerGiverDid to use GenericVerifiableCredential as base type
- Add type assertion for OfferVerifiableCredential inside function
- Remove unnecessary type assertion in canFulfillOffer
2025-05-28 10:09:13 +00:00
Matthew Raymer
23627835f9 refactor: improve type safety in endorser server and common interfaces
- Add proper type definitions for AxiosErrorResponse with detailed error structure
- Make KeyMeta fields required where needed (publicKeyHex, mnemonic, derivationPath)
- Add QuantitativeValue type for consistent handling of numeric values
- Fix type assertions and compatibility between GenericVerifiableCredential and its extensions
- Improve error handling with proper type guards and assertions
- Update VerifiableCredentialClaim interface with required fields
- Add proper type assertions for claim objects in claimSummary and claimSpecialDescription
- Fix BLANK_GENERIC_SERVER_RECORD to include required @context field

Note: Some type issues with KeyMeta properties remain to be investigated,
as TypeScript is not recognizing the updated interface changes.
2025-05-28 09:29:19 +00:00
Matthew Raymer
f1ba6f9231 fix(endorserServer): improve type safety in claim handling
- Fix type conversion in claimSpecialDescription to properly handle GenericVerifiableCredential
- Update claim type checking to use 'claim' in claim for proper type narrowing
- Add type assertions for claim.object to ensure type safety
- Remove incorrect GenericCredWrapper type cast

This change resolves the type conversion error by properly handling
different claim types and ensuring type safety when passing objects
to claimSummary. The code now correctly distinguishes between
GenericVerifiableCredential and GenericCredWrapper types.
2025-05-28 09:04:15 +00:00
Matthew Raymer
137fce3e30 fix(endorserServer): improve type safety and error handling
- Update claimSummary to handle both GenericVerifiableCredential and GenericCredWrapper types
- Fix logger error handling in register function to properly stringify response data
- Add type narrowing with 'claim' in claim check for safer type handling
- Improve error message formatting for registration errors

This change improves type safety by properly handling different claim types
and ensures consistent error logging. The registration error handling now
properly stringifies response data for better debugging.
2025-05-28 09:00:46 +00:00
Matthew Raymer
7166dadbc0 fix(types): correct type imports and improve null safety
- Move type imports to their correct source locations:
  - GenericCredWrapper, GenericVerifiableCredential from interfaces/common
  - GiveSummaryRecord from interfaces/records
  - OfferVerifiableCredential from interfaces/claims
- Add null safety check for dbResult in retrieveAccountCount
- Initialize result variable to prevent undefined access

This change fixes TypeScript errors by ensuring types are imported from their
proper source files and improves code safety by adding proper null checks.
The type system can now correctly validate the usage of these types across
the codebase.
2025-05-28 08:56:54 +00:00
Matthew Raymer
bc274bdf7f fix(types): improve account type safety and metadata handling
- Change retrieveAllAccountsMetadata to return Account[] instead of AccountEncrypted[]
  to better reflect its purpose of returning non-sensitive metadata
- Update ImportDerivedAccountView to use Account type and group by derivation path
- Update retrieveAllFullyDecryptedAccounts to use AccountEncrypted type for encrypted data
- Fix import path for Account type in ImportDerivedAccountView

This change improves type safety by making it explicit which functions handle
encrypted data vs metadata, and ensures consistent handling of account data
across the application. The metadata functions now correctly strip sensitive
fields while functions that need encrypted data maintain access to those fields.
2025-05-28 08:52:09 +00:00
Matthew Raymer
082f8c0126 feat(QRScanner): implement QRScannerOptions in WebInlineQRScanner
- Add proper handling of QRScannerOptions in startScan method
- Implement camera selection (front/back) via options.camera
- Add video preview toggle via options.showPreview
- Store options as class property for persistence
- Improve logging with options context
- Fix TypeScript error for unused options parameter

This change makes the QR scanner more configurable and properly
implements the QRScannerService interface contract.
2025-05-28 08:43:43 +00:00
Matthew Raymer
fd09c7e426 fix(deepLinks): improve route validation and type safety
- Add early validation for route paths to prevent undefined access
- Introduce INVALID_ROUTE error type with detailed error information
- Simplify parameter mapping using nullish coalescing operator
- Improve type safety in parseDeepLink method
- Add better error details for invalid route paths

This change prevents potential runtime errors from undefined route access
and provides clearer error messages for invalid deep links.
2025-05-28 08:40:49 +00:00
Matthew Raymer
be40643379 fix: unused element 2025-05-28 08:38:10 +00:00
Matthew Raymer
835a270e65 feat(qr-scanner): Add camera state management to CapacitorQRScanner
- Add camera state tracking and listener management
- Implement addCameraStateListener and removeCameraStateListener methods
- Add state transitions during scanning operations
- Improve error handling with state updates
- Add proper type imports for CameraState and CameraStateListener

This change ensures CapacitorQRScanner fully implements the QRScannerService
interface and provides proper camera state feedback to consumers. Camera state
is now tracked through the entire lifecycle of scanning operations, with
appropriate state transitions for initialization, active scanning, errors,
and cleanup.
2025-05-28 08:37:02 +00:00
Jose Olarte III
8b03789941 Change heading based on crop flag 2025-05-28 16:32:41 +08:00
Jose Olarte III
b4a6b99301 Better error handling for image upload 2025-05-28 16:17:49 +08:00
Matthew Raymer
13682a1930 fix(db): add type declarations for SQL.js and absurd-sql modules
- Create type declarations in interfaces/absurd-sql.d.ts
- Import and use proper QueryExecResult and SqlValue types
- Add declarations for all required modules:
  - @jlongster/sql.js
  - absurd-sql
  - absurd-sql/dist/indexeddb-backend
  - absurd-sql/dist/indexeddb-main-thread
- Ensure type safety for database operations

This change resolves TypeScript errors about missing type declarations
while maintaining proper type safety for database operations. The
declarations are placed in the interfaces folder to match the project's
type organization.
2025-05-28 06:21:20 +00:00
Matthew Raymer
669a66c24c fix(db): improve type safety in AbsurdSqlDatabaseService
- Move external module type declarations to dedicated .d.ts file
- Make SQL.js types compatible with our database interface
- Fix type compatibility between operation queue and database results
- Add proper typing for database operations and results

This change improves type safety by:
1. Properly declaring types for external modules
2. Ensuring database operation results match our interface
3. Making the operation queue type-safe with generics
4. Removing duplicate type definitions

The remaining module resolution warnings can be safely ignored as they
don't affect runtime behavior and our type declarations are working.
2025-05-28 06:07:50 +00:00
Matthew Raymer
13505b539e fix(db): resolve type compatibility in AbsurdSqlDatabaseService
- Make QueuedOperation interface generic to properly handle operation types
- Fix type compatibility between queue operations and their resolvers
- Use explicit typing for operation queue to handle generic types
- Maintain type safety while allowing different operation return types

This fixes a TypeScript error where the operation queue's resolve function
was not properly typed to handle generic return types from database operations.
2025-05-28 06:02:55 +00:00
Matthew Raymer
07ac340733 feat(capacitor): implement storage permission checks for file operations
- Add permission checks before writeFile and writeAndShareFile operations
- Reuse existing checkStoragePermissions method for Android devices
- Maintain iOS-specific handling (early return for iOS permission model)
- Improve error handling and logging for permission-related issues

This change ensures proper storage permission handling on Android devices
while maintaining the existing iOS behavior. The permission checks run
before any file write operations, providing better error handling and
user experience.
2025-05-28 05:56:58 +00:00
Matthew Raymer
ba2b2fc543 fix: placeholder for PyWebViewPlatformService writeAndShareFile 2025-05-28 05:06:31 +00:00
21184e7625 fix spelling of SQLite module for iOS 2025-05-27 21:11:07 -06:00
8d1511e38f convert all remaining DB writes & reads to SQL (with successful registration & claim) 2025-05-27 21:07:24 -06:00
Matthew Raymer
b18112b869 WIP: disabling absurd-sql when using Capacitor SQLite 2025-05-27 13:15:41 +00:00
Matthew Raymer
a228a9b1c0 fix: add requirements for capacitor/sqlite 2025-05-27 12:42:27 +00:00
Matthew Raymer
1560ff0829 feature: fleshed out capacitor and electron database operators 2025-05-27 11:23:52 +00:00
Jose Olarte III
e839997f91 TEST: platform- and camera-specific mirroring 2025-05-27 18:58:35 +08:00
Jose Olarte III
d8d054a0e1 Streamlined QR scanner web camera
- No need to stop and start camera preview
2025-05-27 18:57:12 +08:00
Jose Olarte III
efc720e47f Mobile native to use web camera
- Ensure consistent UI experience for uploading photos across mobile web and native
2025-05-27 17:46:19 +08:00
Jose Olarte III
0a85bea533 Feature: context-based default camera
- Specify the default camera (front / back) to use
2025-05-27 15:37:45 +08:00
7de4125eb7 add SQL DB access to everywhere we are using the DB, up to the "C" files 2025-05-27 01:27:04 -06:00
Matthew Raymer
81d4f0c762 fix: resolve PWA build issues with SQL.js worker files
- Update worker format to ESM in Vite config to fix IIFE format error

- Increase PWA precache file size limit to 10MB to accommodate SQL.js files

- Fix type declarations for worker configuration

- Add proper type annotations for Vite config

- Add type declarations for absurd-sql module
2025-05-27 06:54:29 +00:00
4c1b4fe651 fix linting 2025-05-26 22:56:00 -06:00
Matthew Raymer
e63541ef53 fix: update ESLint and VS Code settings
- Configure ESLint to ignore node_modules
- Add VS Code settings for Java diagnostics

This fixes the Android build issues and improves the development
environment by properly ignoring node_modules in linting and
diagnostics.
2025-05-27 04:43:52 +00:00
0bfc18c385 add encryption & decryption for the sensitive identity & mnemonic in SQL DB 2025-05-26 22:42:20 -06:00
Matthew Raymer
35f5df6b6b fix: move lexical declarations outside case blocks
fix: missing logger import
Move variable declarations outside switch statement in AbsurdSqlDatabaseService
to fix 'no-case-declarations' linter errors.
2025-05-27 03:21:55 +00:00
Matthew Raymer
0f1ac2b230 fix: move lexical declarations outside case blocks in AbsurdSqlDatabaseService
- Move queryResult and allResult declarations outside switch statement
- Change const declarations to let since they're now in outer scope
- Remove const declarations from inside case blocks

This fixes the 'no-case-declarations' linter errors by ensuring variables
are declared in a scope that encompasses all case blocks, preventing
potential scoping issues.

Note: Type definition errors for external modules remain and should be
addressed separately.
2025-05-27 03:14:02 +00:00
3c0bdeaed3 remove debugging console statements 2025-05-26 20:43:06 -06:00
11f2527b04 start adding the SQL approach to files, also using the Dexie approach if desired 2025-05-26 20:26:28 -06:00
5d8175aeeb add encryption for the two SQL columns, replace basic DB utils, add USE_DEXIE_DB flag, and start adding SQL everywhere 2025-05-26 19:03:20 -06:00
b6b95cb0d0 remove unused redirect to start page (now that we're creating an ID up front) 2025-05-26 16:29:10 -06:00
655c5188a4 add queueing to the DB service so that requests get processed in order 2025-05-26 16:28:33 -06:00
8b7451330f remove possibility of failing a migration script and then succeeding on later ones 2025-05-26 15:50:37 -06:00
b8fbc3f7a6 fix console error about "window" unavailable due to service worker 2025-05-26 15:49:44 -06:00
92dadba1cb rename the absurd-sql-specific items for clarity 2025-05-26 14:52:39 -06:00
3a6f585de0 adjust so DB calls go to the factory 2025-05-26 13:59:34 -06:00
Jose Olarte III
47501ae917 Linting 2025-05-26 19:23:41 +08:00
Jose Olarte III
28634839ec Feature: front/back camera toggle
- Added to gifting and profile dialog camera for now. Toggle button is hidden in desktop.
- WIP: same feature for QR scanner camera.
- WIP: ability to specify default camera depending on where it's called.
2025-05-26 19:23:28 +08:00
2647c5a77d fix migrations logging error 2025-05-25 21:52:27 -06:00
Matt Raymer
682fceb1c6 Merge remote-tracking branch 'refs/remotes/origin/sql-absurd-sql' into sql-absurd-sql 2025-05-25 23:37:43 -04:00
Matt Raymer
e0013008b4 refactor: improve type safety and browser compatibility - Replace any types with SqlValue[] in migration system - Add browser-compatible implementations of Node.js modules (crypto, fs, path) - Update Vite config to handle Node.js module polyfills - Remove outdated migration documentation files 2025-05-25 23:37:08 -04:00
0674d98670 fix BUILDING instructions 2025-05-25 21:29:57 -06:00
Matt Raymer
ee441d1aea refactor(db): improve type safety in migration system
- Replace any[] with SqlValue[] type for SQL parameters in runMigrations
- Update import to use QueryExecResult from interfaces/database
- Add proper typing for SQL parameter values (string | number | null | Uint8Array)

This change improves type safety and helps catch potential SQL parameter
type mismatches at compile time, reducing the risk of runtime errors
or data corruption.
2025-05-25 23:09:53 -04:00
Matt Raymer
75f6e99200 chore: update migration documents and move to new home 2025-05-25 22:50:32 -04:00
Matt Raymer
52c9e57ef4 Merge remote-tracking branch 'refs/remotes/origin/sql-absurd-sql' into sql-absurd-sql 2025-05-25 22:47:36 -04:00
603823d808 add to build instructions for electron on mac 2025-05-25 20:48:51 -06:00
5f24f4975d fix linting 2025-05-25 20:48:33 -06:00
5057d7d07f don't always apply the camera-implementation cursor rules 2025-05-25 20:37:16 -06:00
946e88d903 add a input area for arbitrary SQL on the test page 2025-05-25 20:27:06 -06:00
Matt Raymer
cbfb1ebf57 Merge branch 'new-storage' into sql-absurd-sql 2025-05-25 22:25:56 -04:00
a38934e38d fix problems with race conditions and multiple DatabaseService instances 2025-05-25 19:46:15 -06:00
a3bdcfd168 fix problem with initialization & refactor types 2025-05-25 18:32:41 -06:00
83771caee1 add more to the inital migration, and refactor the locations of types 2025-05-25 17:55:04 -06:00
da35b225cd remove unused setting 2025-05-25 15:49:36 -06:00
8c3920e108 add DB setup with migrations 2025-05-25 11:06:30 -06:00
54f269054f fix error loading WASM file 2025-05-25 07:45:07 -06:00
Matt Raymer
574520d9b3 feat(db): Implement SQLite database layer with migration support
Add SQLite database implementation with comprehensive features:

- Core database functionality:
  - Connection management and pooling
  - Schema creation and validation
  - Transaction support with rollback
  - Backup and restore capabilities
  - Health checks and integrity verification

- Data migration:
  - Migration utilities from Dexie to SQLite
  - Data transformation and validation
  - Migration verification and rollback
  - Backup before migration

- CRUD operations for all entities:
  - Accounts, contacts, and contact methods
  - Settings and secrets
  - Logging and audit trails

- Type safety and error handling:
  - Full TypeScript type definitions
  - Runtime data validation
  - Comprehensive error handling
  - Transaction safety

Note: Requires @wa-sqlite/sql.js package to be installed
2025-05-25 04:52:16 -04:00
6556eb55a3 add the other pieces for the previous commit 2025-05-25 01:18:58 -06:00
Matt Raymer
28e848e386 docs: add comprehensive migration guide for Dexie to wa-sqlite
- Add detailed migration process documentation including preparation, data migration, and rollback strategies\n- Include TypeScript implementation examples for MigrationService, DataMigration, and RollbackService\n- Add Vue component for migration progress tracking with error handling\n- Document testing strategy with unit and integration test examples\n- Define clear success criteria and timeline for migration\n- Include platform-specific considerations and prerequisites\n- Add post-migration verification and monitoring guidelines
2025-05-25 03:18:12 -04:00
Matt Raymer
55f56174a5 docs: enhance secure storage implementation documentation
- Add comprehensive platform-specific implementations for web and native platforms

- Include detailed error handling and recovery strategies

- Add complete testing strategy with platform-specific tests

- Add practical before/after usage examples

- Add appendix with schema, error codes, and platform capabilities

- Improve documentation structure and readability

- Add migration strategy for web platform

- Include platform-specific security features and optimizations
2025-05-25 03:16:12 -04:00
634e2bb2fb try absurd-sql, which fails in browser with: SyntaxError: Cannot use import statement outside a module (at registerSQLWorker.js... 2025-05-25 01:06:31 -06:00
Matt Raymer
30fb3aee8e docs: enhance secure storage implementation documentation
Add detailed platform implementations, usage examples, and error handling: - Add comprehensive platform-specific implementations for Web (Dexie) and Capacitor (SQLite) - Include detailed database initialization and security features - Add practical usage examples for account and settings management - Document error handling strategies and edge cases - Add concurrency management and data integrity checks - Include platform transition handling

This update provides a complete reference for implementing secure storage across different platforms while maintaining backward compatibility.
2025-05-24 23:37:09 -04:00
e254837951 tweak messages & commentary 2025-05-24 20:55:54 -06:00
8417cad2f3 add documentation for some new work: storage 2025-05-24 18:22:05 -06:00
0f56b659c1 adjust privacy notice with the name 2025-05-23 13:07:56 -06:00
a8bfcb720a adjust the messaging for setting one's name 2025-05-23 13:04:49 -06:00
c23e30c431 remove duplicate prompt to get registered 2025-05-23 12:59:57 -06:00
1129a13e20 add more error handling and messaging when there are bad DB errors 2025-05-23 12:35:16 -06:00
5b6c59c232 show an error if the import goes badly 2025-05-23 11:09:22 -06:00
295a2d9f63 don't export 0s for undefined values! 2025-05-23 11:06:37 -06:00
Jose Olarte III
6e14ccdbbc Fix: mirror camera view
- Always when using a desktop browser
- NEEDS TESTING: Conditionally in mobile
2025-05-23 19:21:20 +08:00
Jose Olarte III
d636b21744 Fix: limit image crop stage height
- Addresses issue when extra-tall portrait images are uploaded for cropping
2025-05-23 17:25:22 +08:00
37b7c4ed36 various instructions (and their timeouts) 2025-05-22 12:27:37 -06:00
Jose Olarte III
f7728aadf0 Revert iOS app ID 2025-05-22 22:36:16 +08:00
Jose Olarte III
ce34257ba1 De-coupled web and mobile QR scanner views
- Separate scanner views for web and mobile platforms: different libraries, similar layouts
- Mobile: QR code overlaid on top of full-screen camera view
- Mobile: added framing box + instruction text
- Mobile: increased debounce time to compensate for behavior of MLkit scanner
- Web: removed Capacitor-related code and platform-specific conditions
- Web: adjusted max-size of QR code and camera view to better fit newer iOS device screens
- Web + mobile: camera view remains active when a QR scan is triggered
2025-05-22 19:46:19 +08:00
Matt Raymer
190c972f57 Remove ContactScanView and rename ContactQRScanView to ContactQRScanFullView
- Deleted ContactScanView.vue and its route from the router.
- Renamed ContactQRScanView.vue to ContactQRScanFullView.vue.
- Updated all router paths, names, and references for consistency.
- Fixed related links and imports to use the new view/component name.
2025-05-21 05:17:25 -04:00
Jose Olarte III
831df4b253 Restored diagnostics repositioning 2025-05-21 16:39:21 +08:00
Matt Raymer
55176ed5db Remove ContactScanView and update QR scan view naming for consistency
- Deleted ContactScanView.vue and removed its route from the router
- Renamed ContactQRScanView.vue to ContactQRScanFullView.vue
- Updated all router paths, names, and references to use 'contact-qr-scan-full'
- Updated related router links in ContactQRScanShowView.vue for consistency
- Ensured all naming and routing is consolidated and matches the new view/component name
2025-05-21 04:16:58 -04:00
Matt Raymer
b491262bef Only request camera permissions on user gesture in ImageMethodDialog
- Removed automatic call to startCameraPreview() from mounted() lifecycle hook
- Camera preview (and permission prompt) now only starts in open(), triggered by user action
- Prevents unnecessary permission prompts on page load and improves UX
2025-05-21 03:48:57 -04:00
Jose Olarte III
a1c18458e7 Repositioned diagnostics over camera preview 2025-05-21 15:02:30 +08:00
Jose Olarte III
995af4e576 Restrict camera height in portrait mode 2025-05-21 14:49:29 +08:00
Matt Raymer
8ac728d488 Remove temporary alert() debug calls from ImageMethodDialog camera preview
- Cleaned up all alert() calls used for diagnosing camera access issues on mobile browsers
- Camera preview now starts without pop-up interruptions
- Retained logging and user notifications for error handling and diagnostics
2025-05-21 02:23:52 -04:00
Matt Raymer
913f11b66c Merge remote-tracking branch 'refs/remotes/origin/qrcode-reboot' into qrcode-reboot 2025-05-20 23:08:37 -04:00
Matt Raymer
79882715d8 fix: update Alpine version to 3.20 for stable package repositories
- Change base image from node:22-alpine to node:22-alpine3.20
- Resolves package installation issues with Alpine 3.21 repositories
- Ensures stable access to build dependencies (python3, gcc, etc.)

This change fixes the Docker build process by using a more stable
version of Alpine Linux that has reliable package repositories.
2025-05-20 23:07:49 -04:00
22978a1eda bump to build 18 version 0.4.7 to match the latest upload to ios 2025-05-20 20:27:38 -06:00
79b2218129 add a privacy-fixer project that may have fixed the GoogleToolboxForMac privacy manifext problem
https://github.com/crasowas/app_privacy_manifest_fixer
2025-05-20 20:24:21 -06:00
52685702c1 Merge pull request 'trent-tweaks' (#133) from trent-tweaks into qrcode-reboot
Reviewed-on: #133
2025-05-20 10:52:33 -04:00
d555bc3e9c Merge branch 'qrcode-reboot' into trent-tweaks 2025-05-20 08:52:08 -06:00
Jose Olarte III
141415977e Fix linting errors 2025-05-20 21:35:42 +08:00
Jose Olarte III
981ccbf269 iOS photo library permission 2025-05-20 21:33:15 +08:00
Jose Olarte III
b74ec8ecbb Design: polished dialog UI 2025-05-20 20:54:42 +08:00
Matt Raymer
7b3b1c930e refactor: consolidate type system and improve documentation
- Move type definitions from src/types/ to src/interfaces/ for better organization
- Enhance deep linking type system documentation with detailed examples
- Update package dependencies to latest versions
- Improve code organization in README.md
- Fix formatting in WebPlatformService.ts

This change consolidates all type definitions into the interfaces folder,
improves type safety documentation, and updates dependencies for better
maintainability. The deep linking system now has clearer documentation
about its type system and validation approach.

Breaking: Removes src/types/ directory in favor of src/interfaces/
2025-05-20 03:15:23 -04:00
Matt Raymer
85aa2981ad docs: add comprehensive camera switching implementation guide
Add detailed documentation for camera switching functionality across web and mobile platforms:

- Add camera management interfaces to QRScannerService
- Document MLKit Barcode Scanner configuration for Capacitor
- Add platform-specific implementations for iOS and Android
- Include camera state management and error handling
- Add performance optimization guidelines
- Document testing requirements and scenarios

Key additions:
- Camera switching implementation for both platforms
- Platform-specific considerations (iOS/Android)
- Battery and memory optimization strategies
- Comprehensive testing guidelines
- Error handling and state management
- Security and permission considerations

This update provides a complete reference for implementing robust
camera switching functionality in the QR code scanner.
2025-05-20 02:50:56 -04:00
Matt Raymer
a86e577127 style: improve code formatting and readability
- Format Vue template attributes and event handlers for better readability
- Reorganize component props and event bindings
- Improve error handling and state management in QR scanner
- Add proper aria labels and accessibility attributes
- Refactor camera state handling in WebInlineQRScanner
- Clean up promise handling in WebPlatformService
- Standardize string quotes to double quotes
- Improve component structure and indentation

No functional changes, purely code style and maintainability improvements.
2025-05-20 01:15:47 -04:00
Matt Raymer
788d162b1c refactor: move lib directory to libs for consistency
- Move src/lib/capacitor to src/libs/capacitor
- Move src/lib/fontawesome.ts to src/libs/fontawesome.ts
- Update import paths in main.capacitor.ts and main.common.ts
- Remove empty src/lib directory

This change standardizes the project structure by using the 'libs'
directory consistently throughout the codebase.
2025-05-19 22:25:12 -04:00
Matt Raymer
616a69b7fd chore: update capacitor config and script paths
- Update capacitor.config.json:
  - Change appId from com.brownspank.timesafari to app.timesafari
  - Add server configuration with cleartext enabled
  - Add plugins configuration for App URL handling
- Update script documentation paths:
  - Change ./openssl_signing_console.sh to /scripts/openssl_signing_console.sh
  - Change ./openssl_signing_console.rst to /doc/openssl_signing_console.rst

This change standardizes the app identifier and adds necessary
capacitor configurations for development, while also fixing script
documentation paths to use absolute references.
2025-05-19 22:18:24 -04:00
Jose Olarte III
efab9b968c Merge branch 'qrcode-reboot' of ssh://173.199.124.46:222/trent_larson/crowd-funder-for-time-pwa into qrcode-reboot 2025-05-19 18:50:27 +08:00
Jose Olarte III
70174aea93 Fix: current photo dialog 2025-05-19 18:50:08 +08:00
Matt Raymer
7f12595c91 docs: consolidate QR code implementation documentation
Merge multiple QR code documentation files into a single comprehensive guide
that accurately reflects the current implementation. The consolidated guide:

- Combines information from qr-code-implementation-guide.mdc,
  qr-code-handling-rule.mdc, and camera-implementation.md
- Clarifies the relationship between ContactQRScanView and ContactQRScanShowView
- Streamlines build configuration documentation
- Adds detailed sections on error handling, security, and best practices
- Improves organization and readability of implementation details
- Removes redundant information while preserving critical details

This change improves documentation maintainability and provides a single
source of truth for QR code implementation details.
2025-05-19 06:28:46 -04:00
Matt Raymer
8f0d09e480 chore: cleanup documents 2025-05-19 05:44:12 -04:00
Matt Raymer
cfc0730e75 feat: implement comprehensive camera state management
- Add CameraState type and CameraStateListener interface for standardized state handling
- Implement camera state tracking in WebInlineQRScanner:
  - Add state management properties and methods
  - Update state transitions during camera operations
  - Add proper error state handling for different scenarios
- Enhance QR scanner UI with improved state feedback:
  - Add color-coded status indicators
  - Implement state-specific messages and notifications
  - Add user-friendly error notifications for common issues
- Improve error handling with specific states for:
  - Camera in use by another application
  - Permission denied
  - Camera not found
  - General errors

This change improves the user experience by providing clear visual
feedback about the camera's state and better error handling with
actionable notifications.
2025-05-19 04:40:18 -04:00
bfbb9a933d add a pod dependency to environment 2025-05-18 20:22:48 -06:00
674bbfa00c bump version to match attempts in app stores 2025-05-18 20:22:24 -06:00
80b754246e add more ios tweaks for app store 2025-05-18 20:20:41 -06:00
5fcf6a1f90 add documentation, especially for build processes 2025-05-18 20:19:29 -06:00
9da12e76fd refactor files that should be ignored 2025-05-18 20:15:50 -06:00
Matt Raymer
04193f61c7 Merging 2025-05-16 06:23:46 -04:00
Matt Raymer
0ca4916a05 chore: update camera documentation 2025-05-16 06:17:10 -04:00
925ce830b4 remove duplicate instructions 2025-05-15 20:48:06 -06:00
Jose Olarte III
d14635c44d UI tweaks 2025-05-15 18:17:58 +08:00
Matt Raymer
eb5c9565a6 fix: remove duplicate Advanced heading and improve UX
- Remove redundant Advanced heading from advanced settings section\n- Make clickable text more descriptive (Show/Hide Advanced Settings)\n- Add cursor-pointer class for better UX\n- Make section heading screen-reader only for accessibility
2025-05-15 03:37:01 -04:00
Matthew Raymer
ea108b754e feat(accessibility): enhance AccountViewView and document test suite
- Add ARIA annotations and roles to AccountViewView for better screen reader support
  - Add role="tooltip" to API server description
  - Improve input control accessibility with proper ARIA attributes
  - Add descriptive labels and aria-labels for interactive elements

- Create comprehensive README.md for Playwright test suite
  - Document test structure and organization
  - Add setup instructions and prerequisites
  - Include troubleshooting guide and contribution guidelines
  - Link to related documentation

This change improves accessibility compliance and makes the test suite
more maintainable for contributors.
2025-05-15 06:19:36 +00:00
Matt Raymer
e4155e1a20 feat(ui): disable all photo upload actions for unregistered users
- Updated ImageMethodDialog.vue to accept an isRegistered prop and show "Register to Upload a Photo" message instead of upload UI when not registered.
- Updated AccountViewView.vue to pass isRegistered to ImageMethodDialog and replace all profile photo add/upload buttons with the same message for unregistered users.
- Ensures consistent UX and prevents unregistered users from accessing any photo upload features.
2025-05-14 05:39:03 -04:00
Matt Raymer
7e9682ce67 feat(web): enable desktop webcam capture in WebPlatformService
- Updated WebPlatformService.takePicture() to use getUserMedia for webcam capture on desktop browsers, providing a live video preview and capture button in an overlay.
- Retained file input with capture attribute for mobile browsers and as a fallback if webcam access fails.
- Ensured interface and factory pattern compatibility; no changes required in PhotoDialog.vue or PlatformServiceFactory.
- Added a stub for writeAndShareFile to satisfy the PlatformService interface on web.
2025-05-14 04:35:33 -04:00
c7f1148fe4 add logger import where needed 2025-05-13 22:22:19 -06:00
ae9f1ee09f update package-lock with the latest build 2025-05-13 20:11:47 -06:00
4d0463f7f7 update with lint-fix 2025-05-13 20:11:34 -06:00
1b7c96ed9b don't highlight profile Advanced link in blue 2025-05-13 19:37:47 -06:00
41365fab8f add projectLink to onboarding meeting, plus enhancements to setup usability 2025-05-13 19:36:23 -06:00
748c4c7a50 add documentation 2025-05-12 19:11:18 -06:00
35bb9d2207 remove ability to mark a 'trade', ensuring this only sends & retrieves gifts 2025-05-09 21:37:48 -06:00
Jose Olarte III
fd914aa46c Removed unneeded elements 2025-05-09 19:56:45 +08:00
Jose Olarte III
ba1453104f UI tweaks to QR scanner
- Removed QR code border
- Changed QR code size to eliminate whitespace baked into image
- Removed scanning frame
- Removed camera selector button
- Restyled camera stop buttons in both web and Capacitor for consistency
- Added iOS safe area to Capacitor camera overlay
2025-05-09 19:53:06 +08:00
Jose Olarte III
3c7f13d604 Merge branch 'qrcode-reboot' of ssh://173.199.124.46:222/trent_larson/crowd-funder-for-time-pwa into qrcode-reboot 2025-05-08 20:14:24 +08:00
Jose Olarte III
8e8eef2ab5 Safe area implementation for iOS
Dynamic padding to clear certain iOS UI elements such as the notch, dynamic island and gesture bar, to ensure they don't overlap with our own UI elements.
2025-05-08 20:11:45 +08:00
Matt Raymer
ea17ef930c chore: adjusting file location 2025-05-08 04:50:27 -04:00
Jose Olarte III
5242a24110 Web: trigger camera start on view load 2025-05-08 15:51:00 +08:00
Matt Raymer
93e860e0ac feat(qr-scanner): implement WebInlineQRScanner with jsQR integration
- Add jsQR library for QR code detection and scanning
- Implement WebInlineQRScanner class with comprehensive camera handling
- Add detailed logging throughout scanner lifecycle
- Include error handling and cleanup procedures
- Add blur detection for QR codes
- Implement FPS throttling for performance optimization
- Add device compatibility checks and permission handling

The scanner now provides:
- Camera stream management
- QR code detection with blur prevention
- Performance optimized scanning (15 FPS target)
- Detailed logging for debugging
- Proper cleanup of resources
2025-05-08 01:58:39 -04:00
Jose Olarte III
f874973bfa Improvements to contact QR scanner UI
Plus some narrow-screen fixes to NotificationGroup
2025-05-07 21:23:12 +08:00
Matt Raymer
74b9caa94f chore: updates for qr code reader rules, linting, and cleanup 2025-05-07 01:57:18 -04:00
Jose Olarte III
fdd1ff80ad Complete: unified QR display + capture 2025-05-06 21:35:24 +08:00
Matt Raymer
5d195d06ba style: improve code formatting and type safety
- Add proper type annotation for onDetect result parameter
- Fix indentation and line wrapping in template
- Remove unused WebInlineQRScanner import
- Clean up button attribute ordering
- Fix whitespace and formatting issues
- Remove unused empty divs
2025-05-06 02:45:24 -04:00
Jose Olarte III
79707d2811 WIP: Unified contact QR code display + capture 2025-05-05 20:52:20 +08:00
Jose Olarte III
9b73e05bdb Merge branch 'qrcode-reboot' of ssh://173.199.124.46:222/trent_larson/crowd-funder-for-time-pwa into qrcode-reboot 2025-05-02 21:31:58 +08:00
Jose Olarte III
1b7c5decd3 Stop scanner when cancelling 2025-05-02 21:29:08 +08:00
Jose Olarte III
8c8fb6fe7d De-coupled web and mobile scanners 2025-05-02 21:28:46 +08:00
Matthew Raymer
29983f11a9 doc: camera system details 2025-05-02 10:30:09 +00:00
Matthew Raymer
5c559606df docs: add macOS build and packaging instructions
- Add detailed macOS build procedure for Electron app
- Include instructions for Intel and Universal builds
- Add code signing and notarization requirements
- Document running instructions for .app, .dmg, and .zip formats
- Add security warning handling instructions
2025-05-02 03:22:07 -07:00
Matthew Raymer
37166fc141 docs(PhotoDialog): improve component documentation and error handling
- Add comprehensive JSDoc documentation for component features and capabilities
- Fix line wrapping in file-level documentation header
- Improve error message formatting for better readability
- Remove unused PhotoResult interface
- Maintain consistent documentation style with project standards

The documentation improvements help developers better understand the component's
cross-platform photo handling capabilities and error management approach.
2025-05-02 08:39:29 +00:00
Jose Olarte III
01ef7c1fa9 Merge branch 'qrcode-reboot' of ssh://173.199.124.46:222/trent_larson/crowd-funder-for-time-pwa into qrcode-reboot 2025-05-01 21:32:48 +08:00
Jose Olarte III
2bb71653ac New contact QR scan view for Capacitor version 2025-05-01 21:32:15 +08:00
Matthew Raymer
7baae7ea7a chore: lint fix 2025-05-01 09:47:20 +00:00
Matthew Raymer
cb1d979431 refactor(electron): improve build process and configuration
- Enhance electron build configuration with proper asset handling
- Add comprehensive logging and error tracking
- Implement CSP headers for security
- Fix module exports for logger compatibility
- Update TypeScript and Vite configs for better build support
- Improve development workflow with better dev tools integration
2025-05-01 09:30:02 +00:00
Matt Raymer
b999a04595 cursor(ADR): attempt to keep changes between the lines when making platform level changes 2025-04-30 02:45:30 -04:00
Matt Raymer
0f9826a39d refactor: replace console.log with logger utility
- Replace all console.log statements with appropriate logger methods in QRScannerDialog.vue
- Replace console.log statements with logger methods in WebDialogQRScanner.ts
- Fix TypeScript type for failsafeTimeout from 'any' to 'unknown'
- Update LogCollector.ts to use 'unknown' type instead of 'any'
- Add eslint-disable comments for console overrides in LogCollector

This change improves logging consistency across the application by using the centralized logger utility, which provides better error handling, log persistence, and environment-aware logging.
2025-04-29 23:22:10 -04:00
Jose Olarte III
8cc17bd09d iOS camera usage description 2025-04-30 09:47:41 +08:00
Matt Raymer
9dc9878472 fix(qr-scanner): robustly handle array/object detection results and guarantee dialog dismissal
- Update QRScannerDialog.vue to handle both array and object detection results in onDetect fallback logic (supports vue-qrcode-reader returning arrays).
- Ensure dialog closes and scan is processed for all detection result shapes.
- Use arrow function for close() to guarantee correct binding with vue-facing-decorator.
- Add enhanced logging for all dialog lifecycle and close/cleanup events.
- In WebDialogQRScanner, use direct mount result (not $refs) for dialogComponent to ensure correct instance.
- Add sessionId and improved logging for dialog open/close/cleanup lifecycle.
2025-04-29 06:10:12 -04:00
Jose Olarte III
22283e32f2 Merge branch 'qrcode-reboot' of ssh://173.199.124.46:222/trent_larson/crowd-funder-for-time-pwa into qrcode-reboot 2025-04-28 21:59:15 +08:00
Jose Olarte III
99863ec186 iOS Capacitor setup 2025-04-28 21:59:00 +08:00
Matthew Raymer
8d2dffb012 fix: lint 2025-04-28 12:31:56 +00:00
Matthew Raymer
538cbef701 feat(qr): improve camera error feedback and robustness in QR scanner
- Display prominent, actionable error banners in QR scanner dialog for camera access issues
- Add troubleshooting tips for common camera errors (no device, denied, in use, HTTPS)
- Enhance error handling and logging in WebDialogQRScanner for device detection and permissions
- Use proper type narrowing for promise handling in QRScannerDialog to resolve linter errors
- Improve user experience and clarity when camera access fails or is unavailable
2025-04-28 11:58:15 +00:00
Matthew Raymer
7b7940189e chore(qr): add unconditional debug panel and simplify onInit for event binding test
- Add always-visible debug panel to QRScannerDialog to confirm template updates
- Simplify onInit signature and add alert to verify @init event is firing
- Refine error handling in onInit for clarity during debugging
2025-04-28 10:18:15 +00:00
Matthew Raymer
35b038036a chore(qr): add visible debug output and version bump for device-side troubleshooting
- Bump version string in QRScannerDialog to include build number for cache-busting and verification
- Add debugMessage UI panel to display internal state and debug info directly in the dialog
- Add alert() and debugMessage updates at key points in QR scanner initialization for device-visible feedback
2025-04-28 09:07:27 +00:00
Matthew Raymer
b9cafbe269 debug: add an old-school alert 2025-04-28 08:49:16 +00:00
Matthew Raymer
559f52e6d6 fix(qr): add timeout fallback for QR scanner initialization
- Adds a 4-second timeout to force initialization complete if the QR scanner promise never resolves
- Prevents UI from being stuck on "Checking camera access..." when camera is active but init promise hangs
- Retains retry logic for transient initialization failures
2025-04-28 08:01:21 +00:00
Matthew Raymer
eb44b624d6 fix(qr): add retry logic to QR scanner initialization
- Retries QR scanner initialization up to 3 times if it fails, with a delay between attempts
- Improves user experience on slow or delayed camera hardware/browser permission responses
- Updates status message to reflect retry attempts
2025-04-28 07:25:25 +00:00
Matthew Raymer
6fdbc7f588 debug: comment out promise 2025-04-28 05:48:00 +00:00
Matthew Raymer
7e8caae69a Merge branch 'qrcode-reboot' of ssh://173.199.124.46:222/trent_larson/crowd-funder-for-time-pwa into qrcode-reboot 2025-04-28 04:35:07 +00:00
Matthew Raymer
7b29232b2c style: fix max-len warnings in QRScannerDialog.vue SVG paths
- Break long SVG path 'd' attributes into multiple lines for readability
- Ensure all lines comply with max-len linting rule
2025-04-28 04:22:34 +00:00
Matthew Raymer
e7cb5ffd33 docs: add Docker deployment instructions to BUILDING.md
- Add comprehensive Docker deployment section
- Include build and run instructions for development and production
- Add Docker Compose configuration example
- Include troubleshooting guide for common Docker issues
- Document best practices for production deployment
2025-04-26 10:42:32 +00:00
Matt Raymer
272f2a91a6 refactor(QRScanner): improve camera handling and UI feedback
- Add detailed camera status and initialization feedback\n- Implement proper error handling with specific error messages\n- Add camera switching functionality with visual indicator\n- Improve TypeScript types with DetectionResult interface\n- Fix duplicate onError method with consolidated error handling\n- Add version display (v1.1.0)\n- Enhance UI with better status indicators and debug info\n- Clean up code formatting and improve maintainability
2025-04-25 06:31:18 -04:00
Matt Raymer
f750ea5d10 feat(qr-scanner): Enhance QR scanner dialog with user feedback
- Add status messages for different scanning states (initializing, scanning, error)
- Add visual feedback with color-coded scanning frame and animations
- Add camera switch button for toggling between front/back cameras
- Add scanning instructions and tips in the footer
- Add retry button for error recovery
- Improve error handling and state management
- Add browser compatibility message for unsupported browsers

This change improves the user experience by providing clear visual feedback
and guidance during the QR code scanning process.
2025-04-25 04:55:36 -04:00
Matt Raymer
78116329d4 feat(qr-scanner): Add detailed logging for QR code scanning process
- Add browser capability detection logging (userAgent, mediaDevices, getUserMedia)
- Add detailed error logging with stack traces and error names
- Add new event handlers for detect and error events
- Add logging for key scanning events (init, detect, decode, close)
- Improve error handling with structured error objects

This change will help diagnose QR code registration issues by providing
more detailed information about the scanning process and any errors that occur.
2025-04-25 04:32:35 -04:00
Matt Raymer
2753e142cf feature: adding Dockerfile for online testing or deployment to docker 2025-04-25 04:20:20 -04:00
9a840ab74a ran lint-fix 2025-04-24 09:35:45 -06:00
Matthew Raymer
c6c49260ef fix: add HTTPS requirement check for camera access
- Check for secure context before attempting camera access
- Show clear user feedback when HTTPS is required
- Prevent confusing permission errors on insecure connections
2025-04-24 09:55:01 +00:00
Matthew Raymer
87438e7b6b fix: improve camera permission error feedback
- Add user-visible notification when camera access is denied
- Keep error message in UI for better visibility
- Pass notification timeout as second argument
2025-04-24 09:48:58 +00:00
Matthew Raymer
3ce2ea9b4e fix: standardize FontAwesome usage and improve error handling
- Change <fa> to <font-awesome> for consistent component naming
- Add structured error logging in QR scanner services
- Fix cacheImage event handling in ActivityListItem
- Improve code formatting and error wrapping
2025-04-24 09:34:01 +00:00
Matthew Raymer
8e6ba68560 fix: correct import paths and add host flag for dev server
- Update import path for GiveRecordWithContactInfo to use relative path
- Add --host flag to dev script for network access during development
2025-04-24 09:03:04 +00:00
Matthew Raymer
ca9ca5fca7 fix: prevent duplicate contacts during QR code scanning
- Add explicit duplicate contact check before database insertion
- Show warning notification when duplicate contact is detected
- Return early to prevent duplicate database entries
- Improve user feedback for duplicate contact scenarios

This change ensures users get clear feedback when scanning an already-added contact and prevents duplicate entries in the contacts database.
2025-04-24 08:42:44 +00:00
Matthew Raymer
4abb188da3 refactor(qr): improve QR scanner robustness and lifecycle management
- Add cleanup promise to prevent concurrent cleanup operations
- Add proper component lifecycle tracking with isMounted flag
- Add isCleaningUp flag to prevent operations during cleanup
- Add debug level logging for better diagnostics
- Add structured error logging with stack traces
- Add proper error handling in component initialization
- Add proper cleanup of event listeners and camera resources
- Add proper handling of app pause/resume events
- Add proper error boundaries around camera operations
- Improve error message formatting and consistency

The QR scanner now properly handles lifecycle events, cleans up resources,
and provides better error diagnostics. This improves reliability on mobile
devices and prevents potential memory leaks.
2025-04-22 11:26:27 +00:00
Matthew Raymer
30e448faf8 refactor(qr): improve QR code scanning robustness and error handling
- Enhance JWT extraction with unified path handling and validation
- Add debouncing to prevent duplicate scans
- Improve error handling and logging throughout QR flow
- Add proper TypeScript interfaces for QR scan results
- Implement mobile app lifecycle handlers (pause/resume)
- Enhance logging with structured data and consistent levels
- Clean up scanner resources properly on component destroy
- Split contact handling into separate method for better organization
- Add proper type for UserNameDialog ref

This commit improves the reliability and maintainability of the QR code
scanning functionality while adding better error handling and logging.
2025-04-22 11:04:56 +00:00
Matthew Raymer
a8812714a3 fix(qr): improve QR scanner implementation and error handling
- Implement robust QR scanner factory with platform detection
- Add proper camera permissions to Android manifest
- Improve error handling and logging across scanner implementations
- Add continuous scanning mode for Capacitor/MLKit scanner
- Enhance UI feedback during scanning process
- Fix build configuration for proper platform detection
- Clean up resources properly in scanner components
- Add TypeScript improvements and error wrapping

The changes include:
- Adding CAMERA permission to AndroidManifest.xml
- Setting proper build flags (__IS_MOBILE__, __USE_QR_READER__)
- Implementing continuous scanning mode for better UX
- Adding proper cleanup of scanner resources
- Improving error handling and type safety
- Enhancing UI with loading states and error messages
2025-04-22 10:00:37 +00:00
Matthew Raymer
2855d4b8d5 chore: cleanup and test 2025-04-22 07:48:04 +00:00
Matthew Raymer
b85e6d2958 Merge branch 'qrcode-reboot' of ssh://173.199.124.46:222/trent_larson/crowd-funder-for-time-pwa into qrcode-reboot 2025-04-22 07:06:13 +00:00
Matthew Raymer
7d260365be fix(deep-links): standardize DID parameter name and add route mapping docs
- Change DID schema parameter from 'id' to 'did' for consistency
- Add documentation for deep link route mapping functionality
2025-04-22 06:57:14 +00:00
Matthew Raymer
72de271f6c feat: Add MLKit barcode scanning plugin for Android
- Added @capacitor-mlkit/barcode-scanning@6.0.0 dependency
- Integrated MLKit plugin into Android project configuration
- Updated capacitor.settings.gradle to include MLKit module
- Added MLKit implementation to app dependencies

This change enables native QR code scanning capabilities on Android
devices using Google's MLKit barcode scanning technology.

Security:
- Uses official Google MLKit implementation
- Properly handles camera permissions
- Implements secure barcode scanning

BREAKING CHANGE: Requires Android API level 21+ for MLKit support
2025-04-21 13:05:01 +00:00
Matthew Raymer
2055097cf2 feature(qrcode): reboot qrcode reader 2025-04-21 10:13:12 +00:00
Matthew Raymer
6b38b1a347 test: increase timeout for record offer test to 60s
The record offer test occasionally hits the 45s timeout limit.
Increasing to 60s provides more headroom, aligning with actual
test durations seen in Firefox (up to 43.1s for similar operations).
2025-04-21 06:14:34 +00:00
ca455e9593 modify files to make the ios build & distribution work 2025-04-18 20:40:41 -06:00
5ada70b05e fix the reference to the secrets file 2025-04-15 21:24:03 -06:00
Matthew Raymer
4f9b146a66 fix: improve file sharing on Android using app-private storage
- Replace direct file writing with app-private storage + share dialog
- Add Share plugin for cross-platform file sharing
- Update file paths configuration for Android
- Fix permission issues by using Directory.Data instead of Documents
- Simplify file export flow in DataExportSection
2025-04-11 07:13:07 +00:00
Matthew Raymer
2b638ce2a7 chore: add an android build script to simplify creation of versions 2025-04-10 10:55:40 +00:00
Matthew Raymer
0b528af2a6 WIP: Fix Android file writing permissions and path handling
- Refactor writeFile method to properly handle Android Storage Access Framework (SAF) URIs
- Update path construction for Android to use Download directory with correct permissions
- Enhance error handling and logging throughout file operations
- Add detailed logging for debugging file system operations
- Fix permission checking logic to handle "File does not exist" case correctly
- Improve error messages and stack traces in logs
- Add timestamp to all log entries for better debugging
- Use proper directory types (ExternalStorage for Android, Documents for iOS)
- Add UTF-8 encoding specification for file writes

This is a work in progress as we're still seeing permission issues with Android file writing.
2025-04-09 13:05:42 +00:00
Matthew Raymer
008211bc21 feat(android): implement file picker for data export
- Add @capawesome/capacitor-file-picker dependency
- Update DataExportSection UI text to reflect new file picker behavior
- Implement file picker in CapacitorPlatformService
- Add debug logging for path handling
- Fix logger to show messages in Capacitor environment

WIP: File path handling still needs refinement
2025-04-09 10:04:03 +00:00
Matthew Raymer
6955a36458 chore: clean up lock file 2025-04-09 08:25:20 +00:00
Matthew Raymer
ba079ea983 chore: remove generated index.html from git repo 2025-04-09 07:53:01 +00:00
Matthew Raymer
d7b3c5ec9d fix: remove last "any" lint messages 2025-04-09 07:37:54 +00:00
Matthew Raymer
d83a25f47e chore: linted with auto-fix 2025-04-09 07:17:21 +00:00
Matthew Raymer
fb40dc0ff7 feat(android): update Capacitor assets and fix Xcode project version
- Generate new launcher icons for all densities (mdpi, hdpi, xhdpi, xxhdpi, xxxhdpi)
- Update both regular and round icon variants
- Fix Xcode project version numbers (0920 -> 920)
- Add missing file provider paths meta-data
2025-04-09 07:10:30 +00:00
5cc42be58a fix some test scripts 2025-04-08 20:31:47 -06:00
3d1a2eeb8d adjust to app.timesafari.app in more places 2025-04-08 20:29:08 -06:00
Matthew Raymer
d03fa55001 refactor(platform): replace platform checks with capability-based system
- Add PlatformCapabilities interface to define available features
- Remove isWeb(), isCapacitor(), isElectron(), isPyWebView() methods
- Update platform services to implement getCapabilities()
- Refactor DataExportSection to use capability checks instead of platform checks
- Improve platform abstraction and separation of concerns
- Make platform-specific logic more maintainable and extensible

This change decouples components from specific platform implementations,
making the codebase more maintainable and easier to extend with new platforms.
2025-04-08 11:29:39 +00:00
Matthew Raymer
c8eff4d39e chore(deps): update React Native and Metro dependencies
- Update react-native from 0.78.2 to 0.79.0
- Update metro and related packages from 0.81.4 to 0.82.1
- Update @react-native/virtualized-lists to 0.79.0
- Update @react-native/normalize-colors to 0.79.0
- Update @react-native/codegen to 0.79.0
- Remove unused dependencies and their related code
- Update debug configuration to use v4.4.0 instead of v2.6.9
- Update sudo-prompt to 9.1.1
- Update synckit to 0.11.3
- Gradle updated from 8.9.0 to 8.9.1

This update improves build stability and removes deprecated dependencies.
2025-04-08 11:14:23 +00:00
Matthew Raymer
b8a7771edf feat(export): adapt DataExportSection for platform-specific file handling
Integrate PlatformServiceFactory to provide platform-specific data export:
- Add platform-specific file saving for Capacitor and other platforms
- Use web download mechanism only in web platform
- Conditionally show platform-specific save instructions
- Add iOS/Android detection for targeted guidance
- Update success messages based on platform context
- Improve download link visibility logic for web platform

This change ensures proper file handling across web, mobile, and desktop
platforms while maintaining a consistent user experience.
2025-04-07 08:50:09 +00:00
Matthew Raymer
5d845fb112 docs: add comprehensive JSDoc documentation to service layer
Add detailed TypeScript JSDoc documentation to core service modules:
- api.ts: Document error handling utilities and platform-specific logging
- plan.ts: Document plan/claim loading with retry mechanism
- deepLinks.ts: Document URL parsing and routing functionality
- Platform services:
  - CapacitorPlatformService: Document mobile platform capabilities
  - ElectronPlatformService: Document desktop placeholder implementation
  - PyWebViewPlatformService: Document Python bridge placeholder
  - WebPlatformService: Document web platform limitations and features

Key improvements:
- Add detailed @remarks sections explaining implementation details
- Include usage examples with TypeScript code snippets
- Document error handling and platform-specific behaviors
- Add @todo tags for unimplemented features
- Fix PlanResponse interface to include headers property

This documentation enhances code maintainability and developer experience
by providing clear guidance on service layer functionality and usage.
2025-04-07 07:49:39 +00:00
Matthew Raymer
660f2170de fix: improve error handling in photo upload
- Add proper unknown type for error handling in PhotoDialog
- Remove any type in favor of unknown for better type safety
- Fix error message access with type guards
2025-04-07 07:29:52 +00:00
Matthew Raymer
94bd649003 refactor: improve camera controls and modularize data export
- Add detailed error logging for image upload failures in PhotoDialog and SharedPhotoView
- Extract DataExportSection into standalone component with proper prop handling
- Fix Backup Identifier Seed visibility by passing activeDid prop
2025-04-07 07:17:43 +00:00
Matthew Raymer
b2d628cfeb chore: commit gitignore 2025-04-07 06:43:56 +00:00
Matthew Raymer
00e52f8dca feat: enhance error logging and upgrade Android build tools
- Add detailed error logging for image upload failures
- Upgrade Gradle to 8.11.1 and Android build tools to 8.9.0
- Add Capacitor camera and filesystem modules to Android build
2025-04-07 06:37:14 +00:00
7b0ee2e44e more ios folders to ignore (until we figure out the right way to dance with capacitor-assets) 2025-04-06 19:52:44 -06:00
ac018997e8 adjust instructions for capacitor-assets and more files 2025-04-06 19:48:45 -06:00
6f449e9c1f restore important file from previous cleanup 2025-04-06 19:15:00 -06:00
543599a6a1 remove icon files that are generated by capacitor-assets 2025-04-06 19:02:01 -06:00
Matthew Raymer
073ce24f43 chore(deps): Add Capacitor camera and filesystem plugins
- Add @capacitor/camera@6.0.0 for cross-platform photo capture
- Add @capacitor/filesystem@6.0.0 for file system operations
- Maintain compatibility with existing Capacitor core v6.2.1

These plugins enable native camera access and file system operations
for the Capacitor platform implementation.
2025-04-06 13:28:50 +00:00
Matthew Raymer
2c84bb50b3 **refactor(PhotoDialog, PlatformService): Implement cross-platform photo capture and encapsulated image processing**
- Replace direct camera library with platform-agnostic `PlatformService`
- Move platform-specific image processing logic to respective platform implementations
- Introduce `ImageResult` interface for consistent image handling across platforms
- Add support for native camera and image picker across all platforms
- Simplify `PhotoDialog` by removing platform-specific logic
- Maintain existing cropping and upload functionality
- Improve error handling and logging throughout
- Clean up UI for better user experience
- Add comprehensive documentation for usage and architecture

**BREAKING CHANGE:** Removes direct camera library dependency in favor of `PlatformService`

This change improves separation of concerns, enhances maintainability, and standardizes cross-platform image handling.
2025-04-06 13:04:26 +00:00
Matthew Raymer
abf18835f6 feat: update TypeScript config for platform services
- Add useDefineForClassFields for class field initialization
- Remove test-playwright from includes
- Add tsconfig.node.json reference
- Remove redundant node_modules exclude
2025-04-06 06:58:25 +00:00
Matthew Raymer
f72562804d feat: update TypeScript config for platform services
- Add useDefineForClassFields for class field initialization
- Remove test-playwright from includes
- Add tsconfig.node.json reference
- Remove redundant node_modules exclude
2025-04-06 06:58:14 +00:00
Matthew Raymer
bdc5ffafc1 baseline for this branch 2025-04-06 05:41:12 +00:00
634395ff38 fix instructions & app name 2025-04-05 17:23:29 -06:00
da1f08ebaa move the android secrets files in proximity to where they're used 2025-04-05 16:25:17 -06:00
4ee3ce0061 make changes that must have been done by XCode 2025-04-05 16:08:12 -06:00
654c67af72 add important ios files that aren't regenerated 2025-04-05 16:03:44 -06:00
b244f609b3 fix linting 2025-04-05 15:16:01 -06:00
9c84302c2e consolidate build & test instructions 2025-04-05 15:04:28 -06:00
ca37c30180 add instructions for the release build 2025-04-04 20:10:09 -06:00
130139e2af fix the build config to allow signing, either with a secrets file or with env vars 2025-04-04 20:01:23 -06:00
9802deb17c Merge pull request 'Adjustments to source-destination graphic' (#129) from homeview-card-design-2025-04 into master
Reviewed-on: #129
2025-04-03 21:50:30 -04:00
76c983ea3e replace with real designed icon 2025-04-03 17:55:59 -06:00
114ef440b8 add more build instructions for iOS 2025-04-03 17:52:03 -06:00
b58d510f24 make Advanced links explicit and use "project" instead of "idea" in project page 2025-04-03 17:50:49 -06:00
Matthew Raymer
da6a5ee83e fix(ui): resolve duplicate attributes and improve code style
- Remove duplicate class attributes in ProjectsView and ClaimView
- Fix attribute ordering for better readability
- Replace this references with direct variable names in templates
- Update icon-size prop to use kebab-case
- Remove unnecessary comments and improve formatting
- Fix import organization in ProjectsView

This commit resolves Vue template errors and improves code consistency.
2025-04-02 00:39:38 -07:00
Matthew Raymer
7af39d322f Merge branch 'ui-fixes-2025-03' 2025-04-02 06:48:07 +00:00
Matthew Raymer
bab802160f docs: add call graph and chain documentation to remaining methods
Add comprehensive JSDoc documentation to methods in HomeView.vue:

- latLongInAnySearchBox: Add call chain from shouldIncludeRecord
- giveDescription: Document template usage and displayAmount calls
- displayAmount: Add currency formatting chain
- currencyShortWordForCode: Document amount formatting flow
- openDialog: Document template and openGiftedPrompts usage
- openGiftedPrompts: Add dialog opening chain
- showNameThenIdDialog: Document template usage and prompt flow
- promptForShareMethod: Add sharing flow documentation

Each method now includes:
- @callGraph showing caller/callee relationships
- @chain showing complete execution paths
- @requires listing dependencies
- Enhanced parameter documentation

This completes the standardized documentation pattern across all methods,
making method relationships and dependencies explicit.
2025-04-01 12:30:46 +00:00
Matthew Raymer
01d7bc9e27 docs: enhance method documentation with standardized patterns
Add comprehensive JSDoc documentation to methods in HomeView.vue using standardized patterns:

- Add @callGraph sections to document method relationships and dependencies
- Add @chain sections to show complete call chains
- Add @requires sections to list state and parameter dependencies
- Add @modifies sections to document state changes
- Enhance parameter and return type documentation
- Standardize documentation format across all methods

Key methods enhanced:
- processRecord()
- extractClaim()
- extractGiverDid()
- getFulfillsPlan()
- shouldIncludeRecord()
- createFeedRecord()

This improves code maintainability by:
- Making method relationships explicit
- Documenting state dependencies
- Clarifying call chains
- Standardizing documentation format
2025-04-01 11:04:19 +00:00
Matthew Raymer
fa20360d87 docs: enhance component documentation with usage and reference tracking
- Add comprehensive JSDoc comments to HomeView and InfiniteScroll components
- Document method visibility (@public/@internal) and usage contexts
- Add clear references to where methods are called from (template, components, lifecycle)
- Include file-level documentation with component descriptions
- Document component dependencies and template usage
- Add parameter and return type documentation
- Clarify method call chains and dependencies
- Document event emissions and component interactions

This commit improves code maintainability by making method usage and
component relationships more explicit in the documentation.
2025-04-01 10:57:41 +00:00
Jose Olarte III
770c0fa77c Adjustments to source-destination graphic 2025-03-31 21:46:50 +08:00
Matthew Raymer
0709d0c726 fix: resolve strict mode violation in gift recording test
- Update selector to target specific gift text link instead of generic anchor
- Add filter to ensure unique element selection
- Fix test reliability by being explicit about element selection
2025-03-31 09:32:58 +00:00
Matthew Raymer
d943983bf8 refactor: improve feed loading and infinite scroll reliability
- Add debouncing and loading state to InfiniteScroll component to prevent duplicate entries
- Refactor updateAllFeed into smaller, focused functions for better maintainability
- Add proper error handling and type assertions
- Optimize test performance with networkidle waits and better selectors
- Fix strict mode violations in Playwright tests
- Clean up test configuration by commenting out unused browser targets
2025-03-31 09:17:15 +00:00
be9465e9f8 fix spelling & empty message, rename middle button to one word "yours" 2025-03-30 19:32:29 -06:00
5606f2a18a bump test timeout to 45 seconds, mostly for #33 with many records 2025-03-29 17:56:33 -06:00
Jose Olarte III
06e9950e53 Homeview activity card design tweaks 2025-03-27 00:02:43 +08:00
Jose Olarte III
5dbd66e51b Nav tweaks 2025-03-12 23:12:04 +08:00
Jose Olarte III
312b4aaaa3 Padding adjustments 2025-03-12 17:54:18 +08:00
Jose Olarte III
3a6a24d923 Contact list tweaks 2025-03-12 16:50:13 +08:00
Jose Olarte III
d7afb80a07 Pointer cursor 2025-03-12 15:51:39 +08:00
Jose Olarte III
751df09fe5 Button style tweaks + consistency 2025-03-12 15:51:15 +08:00
Jose Olarte III
8858495f73 Larger contact image
ClickUp task 86b3dgv2f
2025-03-10 19:55:57 +08:00
Jose Olarte III
ecb088bee2 Recolored confirm button to gray
ClickUp task 86b3y8f95
2025-03-10 19:08:49 +08:00
280 changed files with 30582 additions and 29624 deletions

View File

@@ -0,0 +1,153 @@
---
description:
globs:
alwaysApply: true
---
# Absurd SQL - Cursor Development Guide
## Project Overview
Absurd SQL is a backend implementation for sql.js that enables persistent SQLite databases in the browser by using IndexedDB as a block storage system. This guide provides rules and best practices for developing with this project in Cursor.
## Project Structure
```
absurd-sql/
├── src/ # Source code
├── dist/ # Built files
├── package.json # Dependencies and scripts
├── rollup.config.js # Build configuration
└── jest.config.js # Test configuration
```
## Development Rules
### 1. Worker Thread Requirements
- All SQL operations MUST be performed in a worker thread
- Main thread should only handle worker initialization and communication
- Never block the main thread with database operations
### 2. Code Organization
- Keep worker code in separate files (e.g., `*.worker.js`)
- Use ES modules for imports/exports
- Follow the project's existing module structure
### 3. Required Headers
When developing locally or deploying, ensure these headers are set:
```
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
```
### 4. Browser Compatibility
- Primary target: Modern browsers with SharedArrayBuffer support
- Fallback mode: Safari (with limitations)
- Always test in both modes
### 5. Database Configuration
Recommended database settings:
```sql
PRAGMA journal_mode=MEMORY;
PRAGMA page_size=8192; -- Optional, but recommended
```
### 6. Development Workflow
1. Install dependencies:
```bash
yarn add @jlongster/sql.js absurd-sql
```
2. Development commands:
- `yarn build` - Build the project
- `yarn jest` - Run tests
- `yarn serve` - Start development server
### 7. Testing Guidelines
- Write tests for both SharedArrayBuffer and fallback modes
- Use Jest for testing
- Include performance benchmarks for critical operations
### 8. Performance Considerations
- Use bulk operations when possible
- Monitor read/write performance
- Consider using transactions for multiple operations
- Avoid unnecessary database connections
### 9. Error Handling
- Implement proper error handling for:
- Worker initialization failures
- Database connection issues
- Concurrent access conflicts (in fallback mode)
- Storage quota exceeded scenarios
### 10. Security Best Practices
- Never expose database operations directly to the client
- Validate all SQL queries
- Implement proper access controls
- Handle sensitive data appropriately
### 11. Code Style
- Follow ESLint configuration
- Use async/await for asynchronous operations
- Document complex database operations
- Include comments for non-obvious optimizations
### 12. Debugging
- Use `jest-debug` for debugging tests
- Monitor IndexedDB usage in browser dev tools
- Check worker communication in console
- Use performance monitoring tools
## Common Patterns
### Worker Initialization
```javascript
// Main thread
import { initBackend } from 'absurd-sql/dist/indexeddb-main-thread';
function init() {
let worker = new Worker(new URL('./index.worker.js', import.meta.url));
initBackend(worker);
}
```
### Database Setup
```javascript
// Worker thread
import initSqlJs from '@jlongster/sql.js';
import { SQLiteFS } from 'absurd-sql';
import IndexedDBBackend from 'absurd-sql/dist/indexeddb-backend';
async function setupDatabase() {
let SQL = await initSqlJs({ locateFile: file => file });
let sqlFS = new SQLiteFS(SQL.FS, new IndexedDBBackend());
SQL.register_for_idb(sqlFS);
SQL.FS.mkdir('/sql');
SQL.FS.mount(sqlFS, {}, '/sql');
return new SQL.Database('/sql/db.sqlite', { filename: true });
}
```
## Troubleshooting
### Common Issues
1. SharedArrayBuffer not available
- Check COOP/COEP headers
- Verify browser support
- Test fallback mode
2. Worker initialization failures
- Check file paths
- Verify module imports
- Check browser console for errors
3. Performance issues
- Monitor IndexedDB usage
- Check for unnecessary operations
- Verify transaction usage
## Resources
- [Project Demo](https://priceless-keller-d097e5.netlify.app/)
- [Example Project](https://github.com/jlongster/absurd-example-project)
- [Blog Post](https://jlongster.com/future-sql-web)
- [SQL.js Documentation](https://github.com/sql-js/sql.js/)

View File

@@ -0,0 +1,292 @@
---
description:
globs:
alwaysApply: true
---
# TimeSafari Cross-Platform Architecture Guide
## 1. Platform Support Matrix
| Feature | Web (PWA) | Capacitor (Mobile) | Electron (Desktop) | PyWebView (Desktop) |
|---------|-----------|-------------------|-------------------|-------------------|
| QR Code Scanning | WebInlineQRScanner | @capacitor-mlkit/barcode-scanning | Not Implemented | Not Implemented |
| Deep Linking | URL Parameters | App URL Open Events | Not Implemented | Not Implemented |
| File System | Limited (Browser API) | Capacitor Filesystem | Electron fs | PyWebView Python Bridge |
| Camera Access | MediaDevices API | Capacitor Camera | Not Implemented | Not Implemented |
| Platform Detection | Web APIs | Capacitor.isNativePlatform() | process.env checks | process.env checks |
## 2. Project Structure
### 2.1 Core Directories
```
src/
├── components/ # Vue components
├── services/ # Platform services and business logic
├── views/ # Page components
├── router/ # Vue router configuration
├── types/ # TypeScript type definitions
├── utils/ # Utility functions
├── lib/ # Core libraries
├── platforms/ # Platform-specific implementations
├── electron/ # Electron-specific code
├── constants/ # Application constants
├── db/ # Database related code
├── interfaces/ # TypeScript interfaces and type definitions
└── assets/ # Static assets
```
### 2.2 Entry Points
```
src/
├── main.ts # Base entry
├── main.common.ts # Shared initialization
├── main.capacitor.ts # Mobile entry
├── main.electron.ts # Electron entry
├── main.pywebview.ts # PyWebView entry
└── main.web.ts # Web/PWA entry
```
### 2.3 Build Configurations
```
root/
├── vite.config.common.mts # Shared config
├── vite.config.capacitor.mts # Mobile build
├── vite.config.electron.mts # Electron build
├── vite.config.pywebview.mts # PyWebView build
├── vite.config.web.mts # Web/PWA build
└── vite.config.utils.mts # Build utilities
```
## 3. Service Architecture
### 3.1 Service Organization
```
services/
├── QRScanner/ # QR code scanning service
│ ├── WebInlineQRScanner.ts
│ └── interfaces.ts
├── platforms/ # Platform-specific services
│ ├── WebPlatformService.ts
│ ├── CapacitorPlatformService.ts
│ ├── ElectronPlatformService.ts
│ └── PyWebViewPlatformService.ts
└── factory/ # Service factories
└── PlatformServiceFactory.ts
```
### 3.2 Service Factory Pattern
```typescript
// PlatformServiceFactory.ts
export class PlatformServiceFactory {
private static instance: PlatformService | null = null;
public static getInstance(): PlatformService {
if (!PlatformServiceFactory.instance) {
const platform = process.env.VITE_PLATFORM || "web";
PlatformServiceFactory.instance = createPlatformService(platform);
}
return PlatformServiceFactory.instance;
}
}
```
## 4. Feature Implementation Guidelines
### 4.1 QR Code Scanning
1. **Service Interface**
```typescript
interface QRScannerService {
checkPermissions(): Promise<boolean>;
requestPermissions(): Promise<boolean>;
isSupported(): Promise<boolean>;
startScan(): Promise<void>;
stopScan(): Promise<void>;
addListener(listener: ScanListener): void;
onStream(callback: (stream: MediaStream | null) => void): void;
cleanup(): Promise<void>;
}
```
2. **Platform-Specific Implementation**
```typescript
// WebInlineQRScanner.ts
export class WebInlineQRScanner implements QRScannerService {
private scanListener: ScanListener | null = null;
private isScanning = false;
private stream: MediaStream | null = null;
private events = new EventEmitter();
// Implementation of interface methods
}
```
### 4.2 Deep Linking
1. **URL Structure**
```typescript
// Format: timesafari://<route>[/<param>][?queryParam1=value1]
interface DeepLinkParams {
route: string;
params?: Record<string, string>;
query?: Record<string, string>;
}
```
2. **Platform Handlers**
```typescript
// Capacitor
App.addListener("appUrlOpen", handleDeepLink);
// Web
router.beforeEach((to, from, next) => {
handleWebDeepLink(to.query);
});
```
## 5. Build Process
### 5.1 Environment Configuration
```typescript
// vite.config.common.mts
export function createBuildConfig(mode: string) {
return {
define: {
'process.env.VITE_PLATFORM': JSON.stringify(mode),
'process.env.VITE_PWA_ENABLED': JSON.stringify(!isNative),
__IS_MOBILE__: JSON.stringify(isCapacitor),
__USE_QR_READER__: JSON.stringify(!isCapacitor)
}
};
}
```
### 5.2 Platform-Specific Builds
```bash
# Build commands from package.json
"build:web": "vite build --config vite.config.web.mts",
"build:capacitor": "vite build --config vite.config.capacitor.mts",
"build:electron": "vite build --config vite.config.electron.mts",
"build:pywebview": "vite build --config vite.config.pywebview.mts"
```
## 6. Testing Strategy
### 6.1 Test Configuration
```typescript
// playwright.config-local.ts
const config: PlaywrightTestConfig = {
projects: [
{
name: 'web',
use: { browserName: 'chromium' }
},
{
name: 'mobile',
use: { ...devices['Pixel 5'] }
}
]
};
```
### 6.2 Platform-Specific Tests
```typescript
test('QR scanning works on mobile', async ({ page }) => {
test.skip(!process.env.MOBILE_TEST, 'Mobile-only test');
// Test implementation
});
```
## 7. Error Handling
### 7.1 Global Error Handler
```typescript
function setupGlobalErrorHandler(app: VueApp) {
app.config.errorHandler = (err, instance, info) => {
logger.error("[App Error]", {
error: err,
info,
component: instance?.$options.name
});
};
}
```
### 7.2 Platform-Specific Error Handling
```typescript
// API error handling for Capacitor
if (process.env.VITE_PLATFORM === 'capacitor') {
logger.error(`[Capacitor API Error] ${endpoint}:`, {
message: error.message,
status: error.response?.status
});
}
```
## 8. Best Practices
### 8.1 Code Organization
- Use platform-specific directories for unique implementations
- Share common code through service interfaces
- Implement feature detection before using platform capabilities
- Keep platform-specific code isolated in dedicated directories
- Use TypeScript interfaces for cross-platform compatibility
### 8.2 Platform Detection
```typescript
const platformService = PlatformServiceFactory.getInstance();
const capabilities = platformService.getCapabilities();
if (capabilities.hasCamera) {
// Implement camera features
}
```
### 8.3 Feature Implementation
1. Define platform-agnostic interface
2. Create platform-specific implementations
3. Use factory pattern for instantiation
4. Implement graceful fallbacks
5. Add comprehensive error handling
6. Use dependency injection for better testability
## 9. Dependency Management
### 9.1 Platform-Specific Dependencies
```json
{
"dependencies": {
"@capacitor/core": "^6.2.0",
"electron": "^33.2.1",
"vue": "^3.4.0"
}
}
```
### 9.2 Conditional Loading
```typescript
if (process.env.VITE_PLATFORM === 'capacitor') {
await import('@capacitor/core');
}
```
## 10. Security Considerations
### 10.1 Permission Handling
```typescript
async checkPermissions(): Promise<boolean> {
if (platformService.isCapacitor()) {
return await checkNativePermissions();
}
return await checkWebPermissions();
}
```
### 10.2 Data Storage
- Use secure storage mechanisms for sensitive data
- Implement proper encryption for stored data
- Follow platform-specific security guidelines
- Regular security audits and updates
This document should be updated as new features are added or platform-specific implementations change. Regular reviews ensure it remains current with the codebase.

View File

@@ -0,0 +1,222 @@
---
description:
globs:
alwaysApply: false
---
# Camera Implementation Documentation
## Overview
This document describes how camera functionality is implemented across the TimeSafari application. The application uses cameras for two main purposes:
1. QR Code scanning
2. Photo capture
## Components
### QRScannerDialog.vue
Primary component for QR code scanning in web browsers.
**Key Features:**
- Uses `qrcode-stream` for web-based QR scanning
- Supports both front and back cameras
- Provides real-time camera status feedback
- Implements error handling with user-friendly messages
- Includes camera switching functionality
**Camera Access Flow:**
1. Checks for camera API availability
2. Enumerates available video devices
3. Requests camera permissions
4. Initializes camera stream with preferred settings
5. Handles various error conditions with specific messages
### PhotoDialog.vue
Component for photo capture and selection.
**Key Features:**
- Cross-platform photo capture interface
- Image cropping capabilities
- File selection fallback
- Unified interface for different platforms
## Services
### QRScanner Services
#### WebDialogQRScanner
Web-based implementation of QR scanning.
**Key Methods:**
- `checkPermissions()`: Verifies camera permission status
- `requestPermissions()`: Requests camera access
- `isSupported()`: Checks for camera API support
- Handles various error conditions with specific messages
#### CapacitorQRScanner
Native implementation using Capacitor's MLKit.
**Key Features:**
- Uses `@capacitor-mlkit/barcode-scanning`
- Supports both front and back cameras
- Implements permission management
- Provides continuous scanning capability
### Platform Services
#### WebPlatformService
Web-specific implementation of platform features.
**Camera Capabilities:**
- Uses HTML5 file input with capture attribute
- Falls back to file selection if camera unavailable
- Processes captured images for consistent format
#### CapacitorPlatformService
Native implementation using Capacitor.
**Camera Features:**
- Uses `Camera.getPhoto()` for native camera access
- Supports image editing
- Configures high-quality image capture
- Handles base64 image processing
#### ElectronPlatformService
Desktop implementation (currently unimplemented).
**Status:**
- Camera functionality not yet implemented
- Planned to use Electron's media APIs
## Platform-Specific Considerations
### iOS
- Requires `NSCameraUsageDescription` in Info.plist
- Supports both front and back cameras
- Implements proper permission handling
### Android
- Requires camera permissions in manifest
- Supports both front and back cameras
- Handles permission requests through Capacitor
### Web
- Requires HTTPS for camera access
- Implements fallback mechanisms
- Handles browser compatibility issues
## Error Handling
### Common Error Scenarios
1. No camera found
2. Permission denied
3. Camera in use by another application
4. HTTPS required
5. Browser compatibility issues
### Error Response
- User-friendly error messages
- Troubleshooting tips
- Clear instructions for resolution
- Platform-specific guidance
## Security Considerations
### Permission Management
- Explicit permission requests
- Permission state tracking
- Graceful handling of denied permissions
### Data Handling
- Secure image processing
- Proper cleanup of camera resources
- No persistent storage of camera data
## Best Practices
### Camera Access
1. Always check for camera availability
2. Request permissions explicitly
3. Handle all error conditions
4. Provide clear user feedback
5. Implement proper cleanup
### Performance
1. Optimize camera resolution
2. Implement proper resource cleanup
3. Handle camera switching efficiently
4. Manage memory usage
### User Experience
1. Clear status indicators
2. Intuitive camera controls
3. Helpful error messages
4. Smooth camera switching
5. Responsive UI feedback
## Future Improvements
### Planned Enhancements
1. Implement Electron camera support
2. Add advanced camera features
3. Improve error handling
4. Enhance user feedback
5. Optimize performance
### Known Issues
1. Electron camera implementation pending
2. Some browser compatibility limitations
3. Platform-specific quirks to address
## Dependencies
### Key Packages
- `@capacitor-mlkit/barcode-scanning`
- `qrcode-stream`
- `vue-picture-cropper`
- Platform-specific camera APIs
## Testing
### Test Scenarios
1. Permission handling
2. Camera switching
3. Error conditions
4. Platform compatibility
5. Performance metrics
### Test Environment
- Multiple browsers
- iOS and Android devices
- Desktop platforms
- Various network conditions

View File

@@ -0,0 +1,276 @@
---
description:
globs:
alwaysApply: true
---
---
description:
globs:
alwaysApply: true
---
# Time Safari Context
## Project Overview
Time Safari is an application designed to foster community building through gifts, gratitude, and collaborative projects. The app should make it extremely easy and intuitive for users of any age and capability to recognize contributions, build trust networks, and organize collective action. It is built on services that preserve privacy and data sovereignty.
The ultimate goals of Time Safari are two-fold:
1. **Connect** Make it easy, rewarding, and non-threatening for people to connect with others who have similar interests, and to initiate activities together. This helps people accomplish and learn from other individuals in less-structured environments; moreover, it helps them discover who they want to continue to support and with whom they want to maintain relationships.
2. **Reveal** Widely advertise the great support and rewards that are being given and accepted freely, especially non-monetary ones. Using visuals and text, display the kind of impact that gifts are making in the lives of others. Also show useful and engaging reports of project statistics and personal accomplishments.
## Core Approaches
Time Safari should help everyday users build meaningful connections and organize collective efforts by:
1. **Recognizing Contributions**: Creating permanent, verifiable records of gifts and contributions people give to each other and their communities.
2. **Facilitating Collaboration**: Making it ridiculously easy for people to ask for or propose help on projects and interests that matter to them.
3. **Building Trust Networks**: Enabling users to maintain their network and activity visibility. Developing reputation through verified contributions and references, which can be selectively shown to others outside the network.
4. **Preserving Privacy**: Ensuring personal identifiers are only shared with explicitly authorized contacts, allowing private individuals including children to participate safely.
5. **Engaging Content**: Displaying people's records in compelling stories, and highlighting those projects that are lifting people's lives long-term, both in physical support and in emotional-spiritual-creative thriving.
## Technical Foundation
This application is built on a privacy-preserving claims architecture (via endorser.ch) with these key characteristics:
- **Decentralized Identifiers (DIDs)**: User identities are based on public/private key pairs stored on their devices
- **Cryptographic Verification**: All claims and confirmations are cryptographically signed
- **User-Controlled Visibility**: Users explicitly control who can see their identifiers and data
- **Merkle-Chained Claims**: Claims are cryptographically chained for verification and integrity
- **Native and Web App**: Works on Capacitor (iOS, Android), Desktop (Electron and CEFPython), and web browsers
## User Journey
The typical progression of usage follows these stages:
1. **Gratitude & Recognition**: Users begin by expressing and recording gratitude for gifts received, building a foundation of acknowledgment.
2. **Project Proposals**: Users propose projects and ideas, reaching out to connect with others who share similar interests.
3. **Action Triggers**: Offers of help serve as triggers and motivations to execute proposed projects, moving from ideas to action.
## Context for LLM Development
When developing new functionality for Time Safari, consider these design principles:
1. **Accessibility First**: Features should be usable by non-technical users with minimal learning curve.
2. **Privacy by Design**: All features must respect user privacy and data sovereignty.
3. **Progressive Enhancement**: Core functionality should work across all devices, with richer experiences where supported.
4. **Voluntary Collaboration**: The system should enable but never coerce participation.
5. **Trust Building**: Features should help build verifiable trust between users.
6. **Network Effects**: Consider how features scale as more users join the platform.
7. **Low Resource Requirements**: The system should be lightweight enough to run on inexpensive devices users already own.
## Use Cases to Support
LLM development should focus on enhancing these key use cases:
1. **Community Building**: Tools that help people find others with shared interests and values.
2. **Project Coordination**: Features that make it easy to propose collaborative projects and to submit suggestions and offers to existing ones.
3. **Reputation Building**: Methods for users to showcase their contributions and reliability, in contexts where they explicitly reveal that information.
4. **Governance Experimentation**: Features that facilitate decision-making and collective governance.
## Constraints
When developing new features, be mindful of these constraints:
1. **Privacy Preservation**: User identifiers must remain private except when explicitly shared.
2. **Platform Limitations**: Features must work within the constraints of the target app platforms, while aiming to leverage the best platform technology available.
3. **Endorser API Limitations**: Backend features are constrained by the endorser.ch API capabilities.
4. **Performance on Low-End Devices**: The application should remain performant on older/simpler devices.
5. **Offline-First When Possible**: Key functionality should work offline when feasible.
## Project Technologies
- Typescript using ES6 classes using vue-facing-decorator
- TailwindCSS
- Vite Build Tool
- Playwright E2E testing
- IndexDB
- Camera, Image uploads, QR Code reader, ...
## Mobile Features
- Deep Linking
- Local Notifications via a custom Capacitor plugin
## Project Architecture
- The application must work on web browser, PWA (Progressive Web Application), desktop via Electron, and mobile via Capacitor
- Building for each platform is managed via Vite
## Core Development Principles
### DRY development
- **Code Reuse**
- Extract common functionality into utility functions
- Create reusable components for UI patterns
- Implement service classes for shared business logic
- Use mixins for cross-cutting concerns
- Leverage TypeScript interfaces for shared type definitions
- **Component Patterns**
- Create base components for common UI elements
- Implement higher-order components for shared behavior
- Use slot patterns for flexible component composition
- Create composable services for business logic
- Implement factory patterns for component creation
- **State Management**
- Centralize state in Pinia stores
- Use computed properties for derived state
- Implement shared state selectors
- Create reusable state mutations
- Use action creators for common operations
- **Error Handling**
- Implement centralized error handling
- Create reusable error components
- Use error boundary components
- Implement consistent error logging
- Create error type definitions
- **Type Definitions**
- Create shared interfaces for common data structures
- Use type aliases for complex types
- Implement generic types for reusable components
- Create utility types for common patterns
- Use discriminated unions for state management
- **API Integration**
- Create reusable API client classes
- Implement request/response interceptors
- Use consistent error handling patterns
- Create type-safe API endpoints
- Implement caching strategies
- **Platform Services**
- Abstract platform-specific code behind interfaces
- Create platform-agnostic service layers
- Implement feature detection
- Use dependency injection for services
- Create service factories
- **Testing**
- Create reusable test utilities
- Implement test factories
- Use shared test configurations
- Create reusable test helpers
- Implement consistent test patterns
### SOLID Principles
- **Single Responsibility**: Each class/component should have only one reason to change
- Components should focus on one specific feature (e.g., QR scanning, DID management)
- Services should handle one type of functionality (e.g., platform services, crypto services)
- Utilities should provide focused helper functions
- **Open/Closed**: Software entities should be open for extension but closed for modification
- Use interfaces for service definitions
- Implement plugin architecture for platform-specific features
- Allow component behavior extension through props and events
- **Liskov Substitution**: Objects should be replaceable with their subtypes
- Platform services should work consistently across web/mobile
- Authentication providers should be interchangeable
- Storage implementations should be swappable
- **Interface Segregation**: Clients shouldn't depend on interfaces they don't use
- Break down large service interfaces into smaller, focused ones
- Component props should be minimal and purposeful
- Event emissions should be specific and targeted
- **Dependency Inversion**: High-level modules shouldn't depend on low-level modules
- Use dependency injection for services
- Abstract platform-specific code behind interfaces
- Implement factory patterns for component creation
### Law of Demeter
- Components should only communicate with immediate dependencies
- Avoid chaining method calls (e.g., `this.service.getUser().getProfile().getName()`)
- Use mediator patterns for complex component interactions
- Implement facade patterns for subsystem access
- Keep component communication through defined events and props
### Composition over Inheritance
- Prefer building components through composition
- Use mixins for shared functionality
- Implement feature toggles through props
- Create higher-order components for common patterns
- Use service composition for complex features
### Interface Segregation
- Define clear interfaces for services
- Keep component APIs minimal and focused
- Split large interfaces into smaller, specific ones
- Use TypeScript interfaces for type definitions
- Implement role-based interfaces for different use cases
### Fail Fast
- Validate inputs early in the process
- Use TypeScript strict mode
- Implement comprehensive error handling
- Add runtime checks for critical operations
- Use assertions for development-time validation
### Principle of Least Astonishment
- Follow Vue.js conventions consistently
- Use familiar naming patterns
- Implement predictable component behaviors
- Maintain consistent error handling
- Keep UI interactions intuitive
### Information Hiding
- Encapsulate implementation details
- Use private class members
- Implement proper access modifiers
- Hide complex logic behind simple interfaces
- Use TypeScript's access modifiers effectively
### Single Source of Truth
- Use Pinia for state management
- Maintain one source for user data
- Centralize configuration management
- Use computed properties for derived state
- Implement proper state synchronization
### Principle of Least Privilege
- Implement proper access control
- Use minimal required permissions
- Follow privacy-by-design principles
- Restrict component access to necessary data
- Implement proper authentication/authorization
### Continuous Integration/Continuous Deployment (CI/CD)
- Automated testing on every commit
- Consistent build process across platforms
- Automated deployment pipelines
- Quality gates for code merging
- Environment-specific configurations
This expanded documentation provides:
1. Clear principles for development
2. Practical implementation guidelines
3. Real-world examples
4. TypeScript integration
5. Best practices for Time Safari

267
.cursor/rules/wa-sqlite.mdc Normal file
View File

@@ -0,0 +1,267 @@
---
description:
globs:
alwaysApply: true
---
# wa-sqlite Usage Guide
## Table of Contents
- [1. Overview](#1-overview)
- [2. Installation](#2-installation)
- [3. Basic Setup](#3-basic-setup)
- [3.1 Import and Initialize](#31-import-and-initialize)
- [3.2 Basic Database Operations](#32-basic-database-operations)
- [4. Virtual File Systems (VFS)](#4-virtual-file-systems-vfs)
- [4.1 Available VFS Options](#41-available-vfs-options)
- [4.2 Using a VFS](#42-using-a-vfs)
- [5. Best Practices](#5-best-practices)
- [5.1 Error Handling](#51-error-handling)
- [5.2 Transaction Management](#52-transaction-management)
- [5.3 Prepared Statements](#53-prepared-statements)
- [6. Performance Considerations](#6-performance-considerations)
- [7. Common Issues and Solutions](#7-common-issues-and-solutions)
- [8. TypeScript Support](#8-typescript-support)
## 1. Overview
wa-sqlite is a WebAssembly build of SQLite that enables SQLite database operations in web browsers and JavaScript environments. It provides both synchronous and asynchronous builds, with support for custom virtual file systems (VFS) for persistent storage.
## 2. Installation
```bash
npm install wa-sqlite
# or
yarn add wa-sqlite
```
## 3. Basic Setup
### 3.1 Import and Initialize
```javascript
// Choose one of these imports based on your needs:
// - wa-sqlite.mjs: Synchronous build
// - wa-sqlite-async.mjs: Asynchronous build (required for async VFS)
// - wa-sqlite-jspi.mjs: JSPI-based async build (experimental, Chromium only)
import SQLiteESMFactory from 'wa-sqlite/dist/wa-sqlite.mjs';
import * as SQLite from 'wa-sqlite';
async function initDatabase() {
// Initialize SQLite module
const module = await SQLiteESMFactory();
const sqlite3 = SQLite.Factory(module);
// Open database (returns a Promise)
const db = await sqlite3.open_v2('myDatabase');
return { sqlite3, db };
}
```
### 3.2 Basic Database Operations
```javascript
async function basicOperations() {
const { sqlite3, db } = await initDatabase();
try {
// Create a table
await sqlite3.exec(db, `
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE
)
`);
// Insert data
await sqlite3.exec(db, `
INSERT INTO users (name, email)
VALUES ('John Doe', 'john@example.com')
`);
// Query data
const results = [];
await sqlite3.exec(db, 'SELECT * FROM users', (row, columns) => {
results.push({ row, columns });
});
return results;
} finally {
// Always close the database when done
await sqlite3.close(db);
}
}
```
## 4. Virtual File Systems (VFS)
### 4.1 Available VFS Options
wa-sqlite provides several VFS implementations for persistent storage:
1. **IDBBatchAtomicVFS** (Recommended for general use)
- Uses IndexedDB with batch atomic writes
- Works in all contexts (Window, Worker, Service Worker)
- Supports WAL mode
- Best performance with `PRAGMA synchronous=normal`
2. **IDBMirrorVFS**
- Keeps files in memory, persists to IndexedDB
- Works in all contexts
- Good for smaller databases
3. **OPFS-based VFS** (Origin Private File System)
- Various implementations available:
- AccessHandlePoolVFS
- OPFSAdaptiveVFS
- OPFSCoopSyncVFS
- OPFSPermutedVFS
- Better performance but limited to Worker contexts
### 4.2 Using a VFS
```javascript
import { IDBBatchAtomicVFS } from 'wa-sqlite/src/examples/IDBBatchAtomicVFS.js';
import SQLiteESMFactory from 'wa-sqlite/dist/wa-sqlite-async.mjs';
import * as SQLite from 'wa-sqlite';
async function initDatabaseWithVFS() {
const module = await SQLiteESMFactory();
const sqlite3 = SQLite.Factory(module);
// Register VFS
const vfs = await IDBBatchAtomicVFS.create('myApp', module);
sqlite3.vfs_register(vfs, true);
// Open database with VFS
const db = await sqlite3.open_v2('myDatabase');
// Configure for better performance
await sqlite3.exec(db, 'PRAGMA synchronous = normal');
await sqlite3.exec(db, 'PRAGMA journal_mode = WAL');
return { sqlite3, db };
}
```
## 5. Best Practices
### 5.1 Error Handling
```javascript
async function safeDatabaseOperation() {
const { sqlite3, db } = await initDatabase();
try {
await sqlite3.exec(db, 'SELECT * FROM non_existent_table');
} catch (error) {
if (error.code === SQLite.SQLITE_ERROR) {
console.error('SQL error:', error.message);
} else {
console.error('Database error:', error);
}
} finally {
await sqlite3.close(db);
}
}
```
### 5.2 Transaction Management
```javascript
async function transactionExample() {
const { sqlite3, db } = await initDatabase();
try {
await sqlite3.exec(db, 'BEGIN TRANSACTION');
// Perform multiple operations
await sqlite3.exec(db, 'INSERT INTO users (name) VALUES (?)', ['Alice']);
await sqlite3.exec(db, 'INSERT INTO users (name) VALUES (?)', ['Bob']);
await sqlite3.exec(db, 'COMMIT');
} catch (error) {
await sqlite3.exec(db, 'ROLLBACK');
throw error;
} finally {
await sqlite3.close(db);
}
}
```
### 5.3 Prepared Statements
```javascript
async function preparedStatementExample() {
const { sqlite3, db } = await initDatabase();
try {
// Prepare statement
const stmt = await sqlite3.prepare(db, 'SELECT * FROM users WHERE id = ?');
// Execute with different parameters
await sqlite3.bind(stmt, 1, 1);
while (await sqlite3.step(stmt) === SQLite.SQLITE_ROW) {
const row = sqlite3.row(stmt);
console.log(row);
}
// Reset and reuse
await sqlite3.reset(stmt);
await sqlite3.bind(stmt, 1, 2);
// ... execute again
await sqlite3.finalize(stmt);
} finally {
await sqlite3.close(db);
}
}
```
## 6. Performance Considerations
1. **VFS Selection**
- Use IDBBatchAtomicVFS for general-purpose applications
- Consider OPFS-based VFS for better performance in Worker contexts
- Use MemoryVFS for temporary databases
2. **Configuration**
- Set appropriate page size (default is usually fine)
- Use WAL mode for better concurrency
- Consider `PRAGMA synchronous=normal` for better performance
- Adjust cache size based on your needs
3. **Concurrency**
- Use transactions for multiple operations
- Be aware of VFS-specific concurrency limitations
- Consider using Web Workers for heavy database operations
## 7. Common Issues and Solutions
1. **Database Locking**
- Use appropriate transaction isolation levels
- Implement retry logic for busy errors
- Consider using WAL mode
2. **Storage Limitations**
- Be aware of browser storage quotas
- Implement cleanup strategies
- Monitor database size
3. **Cross-Context Access**
- Use appropriate VFS for your context
- Consider message passing for cross-context communication
- Be aware of storage access limitations
## 8. TypeScript Support
wa-sqlite includes TypeScript definitions. The main types are:
```typescript
type SQLiteCompatibleType = number | string | Uint8Array | Array<number> | bigint | null;
interface SQLiteAPI {
open_v2(filename: string, flags?: number, zVfs?: string): Promise<number>;
exec(db: number, sql: string, callback?: (row: any[], columns: string[]) => void): Promise<number>;
close(db: number): Promise<number>;
// ... other methods
}
```
## Additional Resources
- [Official GitHub Repository](https://github.com/rhashimoto/wa-sqlite)
- [Online Demo](https://rhashimoto.github.io/wa-sqlite/demo/)
- [API Reference](https://rhashimoto.github.io/wa-sqlite/docs/)
- [FAQ](https://github.com/rhashimoto/wa-sqlite/issues?q=is%3Aissue+label%3Afaq+)
- [Discussion Forums](https://github.com/rhashimoto/wa-sqlite/discussions)

View File

@@ -2,11 +2,12 @@
# iOS doesn't like spaces in the app title.
TIME_SAFARI_APP_TITLE="TimeSafari_Dev"
VITE_APP_SERVER=http://localhost:3000
VITE_APP_SERVER=http://localhost:8080
# This is the claim ID for actions in the BVC project, with the JWT ID on this environment (not production).
VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F
VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000
# Using shared server by default to ease setup, which works for shared test users.
VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app
VITE_DEFAULT_PARTNER_API_SERVER=http://localhost:3000
#VITE_DEFAULT_PUSH_SERVER... can't be set up with localhost domain
VITE_PASSKEYS_ENABLED=true

View File

@@ -1,6 +0,0 @@
# Admin DID credentials
ADMIN_DID=did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F
ADMIN_PRIVATE_KEY=2b6472c026ec2aa2c4235c994a63868fc9212d18b58f6cbfe861b52e71330f5b
# API Configuration
ENDORSER_API_URL=https://test-api.endorser.ch/api/v2/claim

View File

@@ -9,3 +9,4 @@ VITE_DEFAULT_ENDORSER_API_SERVER=https://api.endorser.ch
VITE_DEFAULT_IMAGE_API_SERVER=https://image-api.timesafari.app
VITE_DEFAULT_PARTNER_API_SERVER=https://partner-api.endorser.ch
VITE_DEFAULT_PUSH_SERVER=https://timesafari.app

View File

@@ -9,4 +9,5 @@ VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch
VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app
VITE_DEFAULT_PARTNER_API_SERVER=https://test-partner-api.endorser.ch
VITE_DEFAULT_PUSH_SERVER=https://test.timesafari.app
VITE_PASSKEYS_ENABLED=true

View File

@@ -4,6 +4,12 @@ module.exports = {
node: true,
es2022: true,
},
ignorePatterns: [
'node_modules/',
'dist/',
'dist-electron/',
'*.d.ts'
],
extends: [
"plugin:vue/vue3-recommended",
"eslint:recommended",
@@ -26,6 +32,7 @@ module.exports = {
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "warn",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-unnecessary-type-constraint": "off"
"@typescript-eslint/no-unnecessary-type-constraint": "off",
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }]
},
};

19
.gitignore vendored
View File

@@ -38,30 +38,21 @@ pnpm-debug.log*
/dist-capacitor/
/test-playwright-results/
playwright-tests
test-playwright
dist-electron-packages
ios
.ruby-version
+.env
# Generated test files
# Test files generated by scripts test-ios.js & test-android.js
.generated/
# Fastlane
ios/fastlane/report.xml
ios/fastlane/Preview.html
ios/fastlane/screenshots
ios/fastlane/test_output
android/fastlane/report.xml
android/fastlane/Preview.html
android/fastlane/screenshots
android/fastlane/test_output
.env.default
vendor/
# Build logs
build_logs/
# Android generated assets
android/app/src/main/assets/public/assets/
# PWA icon files generated by capacitor-assets
icons
android/app/src/main/res/

View File

@@ -9,8 +9,6 @@ For a quick dev environment setup, use [pkgx](https://pkgx.dev).
- Node.js (LTS version recommended)
- npm (comes with Node.js)
- Git
- For iOS builds: macOS with Xcode installed
- For Android builds: Android Studio with SDK installed
- For desktop builds: Additional build tools based on your OS
## Forks
@@ -22,44 +20,188 @@ If you have forked this to make your own app, you'll want to customize the iOS &
npx cap add ios
```
You'll also want to edit the deep link configuration.
You'll also want to edit the deep link configuration (see below).
## Initial Setup
1. Clone the repository:
```bash
git clone [repository-url]
cd TimeSafari
```
2. Install dependencies:
Install dependencies:
```bash
npm install
```
## Web Build
## Web Dev Locally
To build for web deployment:
```bash
npm run dev
```
## Web Build for Server
1. Run the production build:
```bash
npm run build
npm run build:web
```
2. The built files will be in the `dist` directory.
The built files will be in the `dist` directory.
3. To test the production build locally:
2. To test the production build locally:
You'll likely want to use test locations for the Endorser & image & partner servers; see "DEFAULT_ENDORSER_API_SERVER" & "DEFAULT_IMAGE_API_SERVER" & "DEFAULT_PARTNER_API_SERVER" below.
```bash
npm run serve
```
### Compile and minify for test & production
* If there are DB changes: before updating the test server, open browser(s) with current version to test DB migrations.
* `npx prettier --write ./sw_scripts/`
* Update the ClickUp tasks & CHANGELOG.md & the version in package.json, run `npm install`.
* Commit everything (since the commit hash is used the app).
* Put the commit hash in the changelog (which will help you remember to bump the version later).
* Tag with the new version, [online](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/releases) or `git tag 0.5.8 && git push origin 0.5.8`.
* For test, build the app (because test server is not yet set up to build):
```bash
TIME_SAFARI_APP_TITLE="TimeSafari_Test" VITE_APP_SERVER=https://test.timesafari.app VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app VITE_DEFAULT_PARTNER_API_SERVER=https://test-partner-api.endorser.ch VITE_DEFAULT_PUSH_SERVER=https://test.timesafari.app VITE_PASSKEYS_ENABLED=true npm run build:web
```
... and transfer to the test server:
```bash
rsync -azvu -e "ssh -i ~/.ssh/..." dist ubuntutest@test.timesafari.app:time-safari
```
(Let's replace that with a .env.development or .env.staging file.)
(Note: The test BVC_MEETUPS_PROJECT_CLAIM_ID does not resolve as a URL because it's only in the test DB and the prod redirect won't redirect there.)
* For prod, get on the server and run the correct build:
... and log onto the server:
* `pkgx +npm sh`
* `cd crowd-funder-for-time-pwa && git checkout master && git pull && git checkout 0.5.8 && npm install && npm run build:web && cd -`
(The plain `npm run build:web` uses the .env.production file.)
* Back up the time-safari/dist folder & deploy: `mv time-safari/dist time-safari-dist-prev.0 && mv crowd-funder-for-time-pwa/dist time-safari/`
* Record the new hash in the changelog. Edit package.json to increment version & add "-beta", `npm install`, and commit. Also record what version is on production.
## Docker Deployment
The application can be containerized using Docker for consistent deployment across environments.
### Prerequisites
- Docker installed on your system
- Docker Compose (optional, for multi-container setups)
### Building the Docker Image
1. Build the Docker image:
```bash
docker build -t timesafari:latest .
```
2. For development builds with specific environment variables:
```bash
docker build --build-arg NODE_ENV=development -t timesafari:dev .
```
### Running the Container
1. Run the container:
```bash
docker run -d -p 80:80 timesafari:latest
```
2. For development with hot-reloading:
```bash
docker run -d -p 80:80 -v $(pwd):/app timesafari:dev
```
### Using Docker Compose
Create a `docker-compose.yml` file:
```yaml
version: '3.8'
services:
timesafari:
build: .
ports:
- "80:80"
environment:
- NODE_ENV=production
restart: unless-stopped
```
Run with Docker Compose:
```bash
docker-compose up -d
```
### Production Deployment
For production deployment, consider the following:
1. Use specific version tags instead of 'latest'
2. Implement health checks
3. Configure proper logging
4. Set up reverse proxy with SSL termination
5. Use Docker secrets for sensitive data
Example production deployment:
```bash
# Build with specific version
docker build -t timesafari:1.0.0 .
# Run with production settings
docker run -d \
--name timesafari \
-p 80:80 \
--restart unless-stopped \
-e NODE_ENV=production \
timesafari:1.0.0
```
### Troubleshooting Docker
1. **Container fails to start**
- Check logs: `docker logs <container_id>`
- Verify port availability
- Check environment variables
2. **Build fails**
- Ensure all dependencies are in package.json
- Check Dockerfile syntax
- Verify build context
3. **Performance issues**
- Monitor container resources: `docker stats`
- Check nginx configuration
- Verify caching settings
## Desktop Build (Electron)
### Building for Linux
### Linux Build
1. Build the electron app in production mode:
@@ -81,21 +223,77 @@ To build for web deployment:
- AppImage: `dist-electron-packages/TimeSafari-x.x.x.AppImage`
- DEB: `dist-electron-packages/timesafari_x.x.x_amd64.deb`
### macOS Build
1. Build the electron app in production mode:
```bash
npm run build:web
npm run build:electron
npm run electron:build-mac
```
2. Package the Electron app for macOS:
```bash
# For Intel Macs
npm run electron:build-mac
# For Universal build (Intel + Apple Silicon)
npm run electron:build-mac-universal
```
3. The packaged applications will be in `dist-electron-packages/`:
- `.app` bundle: `TimeSafari.app`
- `.dmg` installer: `TimeSafari-x.x.x.dmg`
- `.zip` archive: `TimeSafari-x.x.x-mac.zip`
### Code Signing and Notarization (macOS)
For public distribution on macOS, you need to code sign and notarize your app:
1. Set up environment variables:
```bash
export CSC_LINK=/path/to/your/certificate.p12
export CSC_KEY_PASSWORD=your_certificate_password
export APPLE_ID=your_apple_id
export APPLE_ID_PASSWORD=your_app_specific_password
```
2. Build with signing:
```bash
npm run electron:build-mac
```
### Running the Packaged App
- AppImage: Make executable and run
- **Linux**:
- AppImage: Make executable and run
```bash
chmod +x dist-electron-packages/TimeSafari-*.AppImage
./dist-electron-packages/TimeSafari-*.AppImage
```
- DEB: Install and run
```bash
sudo dpkg -i dist-electron-packages/timesafari_*_amd64.deb
timesafari
```
```bash
chmod +x dist-electron-packages/TimeSafari-*.AppImage
./dist-electron-packages/TimeSafari-*.AppImage
```
- **macOS**:
- `.app` bundle: Double-click `TimeSafari.app` in Finder
- `.dmg` installer:
1. Double-click the `.dmg` file
2. Drag the app to your Applications folder
3. Launch from Applications
- `.zip` archive:
1. Extract the `.zip` file
2. Move `TimeSafari.app` to your Applications folder
3. Launch from Applications
- DEB: Install and run
```bash
sudo dpkg -i dist-electron-packages/timesafari_*_amd64.deb
timesafari
```
Note: If you get a security warning when running the app:
1. Right-click the app
2. Select "Open"
3. Click "Open" in the security dialog
### Development Testing
@@ -115,36 +313,87 @@ npm run build:electron-prod && npm run electron:start
Prerequisites: macOS with Xcode installed
1. Build the web assets:
#### First-time iOS Configuration
- Generate certificates inside XCode.
- Right-click on App and under Signing & Capabilities set the Team.
#### Each Release
0. First time (or if dependencies change):
- `pkgx +rubygems.org sh`
- ... and you may have to fix these, especially with pkgx:
```bash
npm run build:capacitor
gem_path=$(which gem)
shortened_path="${gem_path:h:h}"
export GEM_HOME=$shortened_path
export GEM_PATH=$shortened_path
```
2. Update iOS project with latest build:
1. Build the web assets & update ios:
```bash
rm -rf dist
npm run build:web
npm run build:capacitor
npx cap sync ios
```
- If that fails with "Could not find..." then look at the "gem_path" instructions above.
3. Copy the assets:
```bash
# It makes no sense why capacitor-assets will not run without these but it actually changes the contents.
mkdir -p ios/App/App/Assets.xcassets/AppIcon.appiconset
echo '{"images":[]}' > ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json
mkdir -p ios/App/App/Assets.xcassets/Splash.imageset
echo '{"images":[]}' > ios/App/App/Assets.xcassets/Splash.imageset/Contents.json
npx capacitor-assets generate --ios
```
3. Open the project in Xcode:
4. Bump the version to match Android & package.json:
```
cd ios/App
xcrun agvtool new-version 33
# Unfortunately this edits Info.plist directly.
#xcrun agvtool new-marketing-version 0.4.5
cat App.xcodeproj/project.pbxproj | sed "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 0.5.7;/g" > temp && mv temp App.xcodeproj/project.pbxproj
cd -
```
5. Open the project in Xcode:
```bash
npx cap open ios
```
4. Use Xcode to build and run on simulator or device.
6. Use Xcode to build and run on simulator or device.
* Select Product -> Destination with some Simulator version. Then click the run arrow.
7. Release
* Someday: Under "General" we want to rename a bunch of things to "Time Safari"
* Choose Product -> Destination -> Any iOS Device
* Choose Product -> Archive
* This will trigger a build and take time, needing user's "login" keychain password (user's login password), repeatedly.
* If it fails with `building for 'iOS', but linking in dylib (.../.pkgx/zlib.net/v1.3.0/lib/libz.1.3.dylib) built for 'macOS'` then run XCode outside that terminal (ie. not with `npx cap open ios`).
* Click Distribute -> App Store Connect
* In AppStoreConnect, add the build to the distribution: remove the current build with the "-" when you hover over it, then "Add Build" with the new build.
* May have to go to App Review, click Submission, then hover over the build and click "-".
* It can take 15 minutes for the build to show up in the list of builds.
* You'll probably have to "Manage" something about encryption, disallowed in France.
* Then "Save" and "Add to Review" and "Resubmit to App Review".
### Android Build
Prerequisites: Android Studio with SDK installed
Prerequisites: Android Studio with Java SDK installed
1. Build the web assets:
@@ -166,32 +415,57 @@ Prerequisites: Android Studio with SDK installed
npx capacitor-assets generate --android
```
4. Open the project in Android Studio:
4. Bump version to match iOS & package.json: android/app/build.gradle
5. Open the project in Android Studio:
```bash
npx cap open android
```
5. Use Android Studio to build and run on emulator or device.
6. Use Android Studio to build and run on emulator or device.
## Building Android from the console
## Android Build from the console
```bash
cd android
./gradlew clean
./gradlew build -Dlint.baselines.continue=true
cd ..
npx cap run android
cd -
```
... or, to create the `aab` file, `bundle` instead of `build`:
```bash
./gradlew bundle -Dlint.baselines.continue=true
./gradlew bundleDebug -Dlint.baselines.continue=true
```
... or, to create a signed release:
## Configuring Android for deep links
* Setup by adding the app/gradle.properties.secrets file (see properties at top of app/build.gradle) and the app/time-safari-upload-key-pkcs12.jks file
* In app/build.gradle, bump the versionCode and maybe the versionName
* Then `bundleRelease`:
```bash
cd android
./gradlew bundleRelease -Dlint.baselines.continue=true
cd -
```
... and find your `aab` file at app/build/outputs/bundle/release
At play.google.com/console:
- Go to the Testing Track (eg. Closed).
- Click "Create new release".
- Upload the `aab` file.
- Hit "Next".
- Save, go to "Publishing Overview" as prompted, and click "Send changes for review".
- Note that if you add testers, you have to go to "Publishing Overview" and send those changes or your (closed) testers won't see it.
## Android Configuration for deep links
You must add the following intent filter to the `android/app/src/main/AndroidManifest.xml` file:
@@ -204,251 +478,4 @@ You must add the following intent filter to the `android/app/src/main/AndroidMan
</intent-filter>
```
You must also add the following to the `android/app/build.gradle` file:
```gradle
android {
// ... existing config ...
lintOptions {
disable 'UnsanitizedFilenameFromContentProvider'
abortOnError false
baseline file("lint-baseline.xml")
// Ignore Capacitor module issues
ignore 'DefaultLocale'
ignore 'UnsanitizedFilenameFromContentProvider'
ignore 'LintBaseline'
ignore 'LintBaselineFixed'
}
}
```
Modify `/android/build.gradle` to use a stable version of AGP and make sure Kotlin version is compatible.
```gradle
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
// Use a stable version of AGP
classpath 'com.android.tools.build:gradle:8.1.0'
// Make sure Kotlin version is compatible
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
// Add this to handle version conflicts
configurations.all {
resolutionStrategy {
force 'org.jetbrains.kotlin:kotlin-stdlib:1.8.0'
force 'org.jetbrains.kotlin:kotlin-stdlib-common:1.8.0'
}
}
```
## PyWebView Desktop Build
### Prerequisites for PyWebView
- Python 3.8 or higher
- pip (Python package manager)
- virtualenv (recommended)
- System dependencies:
```bash
# For Ubuntu/Debian
sudo apt-get install python3-webview
# or
sudo apt-get install python3-gi python3-gi-cairo gir1.2-gtk-3.0 gir1.2-webkit2-4.0
# For Arch Linux
sudo pacman -S webkit2gtk python-gobject python-cairo
# For Fedora
sudo dnf install python3-webview
# or
sudo dnf install python3-gobject python3-cairo webkit2gtk3
```
### Setup
1. Create and activate a virtual environment (recommended):
```bash
python -m venv .venv
source .venv/bin/activate # On Linux/macOS
# or
.venv\Scripts\activate # On Windows
```
2. Install Python dependencies:
```bash
pip install -r requirements.txt
```
### Troubleshooting
If encountering PyInstaller version errors:
```bash
# Try installing the latest stable version
pip install --upgrade pyinstaller
```
### Development of PyWebView
1. Start the PyWebView development build:
```bash
npm run pywebview:dev
```
### Building for Distribution
#### Linux
```bash
npm run pywebview:package-linux
```
The packaged application will be in `dist/TimeSafari`
#### Windows
```bash
npm run pywebview:package-win
```
The packaged application will be in `dist/TimeSafari`
#### macOS
```bash
npm run pywebview:package-mac
```
The packaged application will be in `dist/TimeSafari`
## Testing
Run all tests (requires XCode and Android Studio/device):
```bash
npm run test:all
```
See [TESTING.md](test-playwright/TESTING.md) for more details.
## Linting
Check code style:
```bash
npm run lint
```
Fix code style issues:
```bash
npm run lint-fix
```
## Environment Configuration
See `.env.*` files for configuration.
## Notes
- The application uses PWA (Progressive Web App) features for web builds
- Electron builds disable PWA features automatically
- Build output directories:
- Web: `dist/`
- Electron: `dist-electron/`
- Capacitor: `dist-capacitor/`
## Deployment
### Version Management
1. Update CHANGELOG.md with new changes
2. Update version in package.json
3. Commit changes and tag release:
```bash
git tag <VERSION_TAG>
git push origin <VERSION_TAG>
```
4. After deployment, update package.json with next version + "-beta"
### Test Server
```bash
# Build using staging environment
npm run build -- --mode staging
# Deploy to test server
rsync -azvu -e "ssh -i ~/.ssh/<YOUR_KEY>" dist ubuntutest@test.timesafari.app:time-safari/
```
### Production Server
```bash
# On the production server:
pkgx +npm sh
cd crowd-funder-for-time-pwa
git checkout master && git pull
git checkout <VERSION_TAG>
npm install
npm run build
cd -
# Backup and deploy
mv time-safari/dist time-safari-dist-prev.0 && mv crowd-funder-for-time-pwa/dist time-safari/
```
## Troubleshooting Builds
### Common Build Issues
1. **Missing Environment Variables**
- Check that all required variables are set in your .env file
- For development, ensure local services are running on correct ports
2. **Electron Build Failures**
- Verify Node.js version compatibility
- Check that all required dependencies are installed
- Ensure proper paths in electron/main.js
3. **Mobile Build Issues**
- For iOS: Xcode command line tools must be installed
- For Android: Correct SDK version must be installed
- Check Capacitor configuration in capacitor.config.ts
# List all installed packages
adb shell pm list packages | grep timesafari
# Force stop the app (if it's running)
adb shell am force-stop app.timesafari
# Clear app data (if you don't want to fully uninstall)
adb shell pm clear app.timesafari
# Uninstall for all users
adb shell pm uninstall -k --user 0 app.timesafari
# Check if app is installed
adb shell pm path app.timesafari
... though when we tried that most recently it failed to 'build' the APK with: http(s) scheme and host attribute are missing, but are required for Android App Links [AppLinkUrlError]

View File

@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [0.5.9]
### Added
- Migration from IndexedDB to SQLite
## [0.4.7]
### Fixed
- Cameras everywhere
### Changed
- IndexedDB -> SQLite
## [0.4.5] - 2025.02.23
### Added
- Total amounts of gives on project page

36
Dockerfile Normal file
View File

@@ -0,0 +1,36 @@
# Build stage
FROM node:22-alpine3.20 AS builder
# Install build dependencies
RUN apk add --no-cache bash git python3 py3-pip py3-setuptools make g++ gcc
# Set working directory
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci
# Copy source code
COPY . .
# Build the application
RUN npm run build:web
# Production stage
FROM nginx:alpine
# Copy built assets from builder stage
COPY --from=builder /app/dist /usr/share/nginx/html
# Copy nginx configuration if needed
# COPY nginx.conf /etc/nginx/conf.d/default.conf
# Expose port 80
EXPOSE 80
# Start nginx
CMD ["nginx", "-g", "daemon off;"]

124
README.md
View File

@@ -3,6 +3,32 @@
[Time Safari](https://timesafari.org/) allows people to ease into collaboration: start with expressions of gratitude
and expand to crowd-fund with time & money, then record and see the impact of contributions.
## Database Migration Status
**Current Status**: The application is undergoing a migration from Dexie (IndexedDB) to SQLite using absurd-sql. This migration is in **Phase 2** with a well-defined migration fence in place.
### Migration Progress
-**SQLite Database Service**: Fully implemented with absurd-sql
-**Platform Service Layer**: Unified database interface across platforms
-**Settings Migration**: Core user settings transferred
-**Account Migration**: Identity and key management
- 🔄 **Contact Migration**: User contact data (via import interface)
- 📋 **Code Cleanup**: Remove unused Dexie imports
### Migration Fence
The migration is controlled by a **migration fence** that separates legacy Dexie code from the new SQLite implementation. See [Migration Fence Definition](doc/migration-fence-definition.md) for complete details.
**Key Points**:
- Legacy Dexie database is disabled by default (`USE_DEXIE_DB = false`)
- All database operations go through `PlatformService`
- Migration tools provide controlled access to both databases
- Clear separation between legacy and new code
### Migration Documentation
- [Migration Guide](doc/migration-to-wa-sqlite.md) - Complete migration process
- [Migration Fence Definition](doc/migration-fence-definition.md) - Fence boundaries and rules
- [Database Migration Guide](doc/database-migration-guide.md) - User-facing migration tools
## Roadmap
See [project.task.yaml](project.task.yaml) for current priorities.
@@ -12,6 +38,8 @@ See [project.task.yaml](project.task.yaml) for current priorities.
Quick start:
* For setup, we recommend [pkgx](https://pkgx.dev), which installs what you need (either automatically or with the `dev` command). Core dependencies are typescript & npm; when building for other platforms, you'll need other things such as those in the pkgx.yaml & BUILDING.md files.
```bash
npm install
npm run dev
@@ -19,72 +47,15 @@ npm run dev
See [BUILDING.md](BUILDING.md) for more details.
See the test locations for "IMAGE_API_SERVER" or "PARTNER_API_SERVER" below, or use http://localhost:3000 for local endorser.ch
### Run all UI tests
Look at [BUILDING.md](BUILDING.md) for the "test-all" instructions and [TESTING.md](test-playwright/TESTING.md) for more details.
### Compile and minify for test & production
* If there are DB changes: before updating the test server, open browser(s) with current version to test DB migrations.
* `npx prettier --write ./sw_scripts/`
* Update the ClickUp tasks & CHANGELOG.md & the version in package.json, run `npm install`.
* Commit everything (since the commit hash is used the app).
* Put the commit hash in the changelog (which will help you remember to bump the version later).
* Tag with the new version, [online](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/releases) or `git tag 0.3.55 && git push origin 0.3.55`.
* For test, build the app (because test server is not yet set up to build):
```bash
TIME_SAFARI_APP_TITLE="TimeSafari_Test" VITE_APP_SERVER=https://test.timesafari.app VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app VITE_DEFAULT_PARTNER_API_SERVER=https://test-partner-api.endorser.ch VITE_PASSKEYS_ENABLED=true npm run build
```
... and transfer to the test server:
```bash
rsync -azvu -e "ssh -i ~/.ssh/..." dist ubuntutest@test.timesafari.app:time-safari
```
(Let's replace that with a .env.development or .env.staging file.)
(Note: The test BVC_MEETUPS_PROJECT_CLAIM_ID does not resolve as a URL because it's only in the test DB and the prod redirect won't redirect there.)
* For prod, get on the server and run the correct build:
... and log onto the server:
* `pkgx +npm sh`
* `cd crowd-funder-for-time-pwa && git checkout master && git pull && git checkout 0.3.55 && npm install && npm run build && cd -`
(The plain `npm run build` uses the .env.production file.)
* Back up the time-safari/dist folder & deploy: `mv time-safari/dist time-safari-dist-prev.0 && mv crowd-funder-for-time-pwa/dist time-safari/`
* Record the new hash in the changelog. Edit package.json to increment version & add "-beta", `npm install`, and commit. Also record what version is on production.
## Tests
See [TESTING.md](test-playwright/TESTING.md) for detailed test instructions.
## Icons
To add an icon, add to main.ts and reference with `fa` element and `icon` attribute with the hyphenated name.
Application icons are in the `assets` directory, processed by the `capacitor-assets` command.
To add a Font Awesome icon, add to main.ts and reference with `font-awesome` element and `icon` attribute with the hyphenated name.
## Other
@@ -97,6 +68,39 @@ To add an icon, add to main.ts and reference with `fa` element and `icon` attrib
* If you are deploying in a subdirectory, add it to `publicPath` in vue.config.js, eg: `publicPath: "/app/time-tracker/",`
### Code Organization
The project uses a centralized approach to type definitions and interfaces:
* `src/interfaces/` - Contains all TypeScript interfaces and type definitions
* `deepLinks.ts` - Deep linking type system and Zod validation schemas
* `give.ts` - Give-related interfaces and type definitions
* `claims.ts` - Claim-related interfaces and verifiable credentials
* `common.ts` - Shared interfaces and utility types
* Other domain-specific interface files
Key principles:
- All interfaces and types are defined in the interfaces folder
- Zod schemas are used for runtime validation and type generation
- Domain-specific interfaces are separated into their own files
- Common interfaces are shared through `common.ts`
- Type definitions are generated from Zod schemas where possible
### Database Architecture
The application uses a platform-agnostic database layer:
* `src/services/PlatformService.ts` - Database interface definition
* `src/services/PlatformServiceFactory.ts` - Platform-specific service factory
* `src/services/AbsurdSqlDatabaseService.ts` - SQLite implementation
* `src/db/` - Legacy Dexie database (migration in progress)
**Development Guidelines**:
- Always use `PlatformService` for database operations
- Never import Dexie directly in application code
- Test with `USE_DEXIE_DB = false` for new features
- Use migration tools for data transfer between systems
### Kudos
Gifts make the world go 'round!

84
TASK_storage.md Normal file
View File

@@ -0,0 +1,84 @@
# What to do about storage for native apps?
## Problem
We can't trust iOS IndexedDB to persist. I want to start delivering an app to people now, in preparation for presentations mid-June: Rotary on June 12 and Porcfest on June 17.
* Apple WebKit puts a [7-day cap on IndexedDB](https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/).
* The web standards expose a `persist` method to mark memory as persistent, and [supposedly WebView supports it](https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/persisted), but too many other things indicate it's not reliable. I've talked with [ChatGPT](https://chatgpt.com/share/68322f40-84c8-8007-b213-855f7962989a) & Venice & Claude (in Cursor); [this answer from Perplexity](https://www.perplexity.ai/search/which-platforms-prompt-the-use-HUQLqy4qQD2cRbkmO4CgHg) says that most platforms don't prompt and Safari doesn't support it; I don't know if that means WebKit as well.
* Capacitor says [not to trust it on iOS](https://capacitorjs.com/docs/v6/guides/storage).
Also, with sensitive data, the accounts info should be encrypted.
# Options
* There is a community [SQLite plugin for Capacitor](https://github.com/capacitor-community/sqlite) with encryption by [SQLCipher](https://github.com/sqlcipher/sqlcipher).
* [This tutorial](https://jepiqueau.github.io/2023/09/05/Ionic7Vue-SQLite-CRUD-App.html#part-1---web---table-of-contents) shows how that plugin works for web as well as native.
* Capacitor abstracts [user preferences in an API](https://capacitorjs.com/docs/apis/preferences), which uses different underlying libraries on iOS & Android. Unfortunately, it won't do any filtering or searching, and is only meant for small amounts of data. (It could be used for settings and for identifiers, but contacts will grow and image blobs won't work.)
* There are hints that Capacitor offers another custom storage API but all I could find was that Preferences API.
* [Ionic Storage](https://ionic.io/docs/secure-storage) is an enterprise solution, which also supports encryption.
* Not an option yet: Dexie may support SQLite in [a future version](https://dexie.org/roadmap/dexie5.0).
# Current Plan
* Implement SQLite for Capacitor & web, with encryption. That will allow us to test quickly and keep the same interface for native & web, but we don't deal with migrations for current web users.
* After that is delivered, write a migration for current web users from IndexedDB to SQLite.
# Current method calls
... which is not 100% complete because the AI that generated thus claimed no usage of 'temp' DB.
### Secret Database (secretDB) - Used for storing the encryption key
secretDB.open() - Opens the database
secretDB.secret.get(MASTER_SECRET_KEY) - Retrieves the secret key
secretDB.secret.add({ id: MASTER_SECRET_KEY, secret }) - Adds a new secret key
### Accounts Database (accountsDB) - Used for storing sensitive account information
accountsDB.open() - Opens the database
accountsDB.accounts.count() - Counts number of accounts
accountsDB.accounts.toArray() - Gets all accounts
accountsDB.accounts.where("did").equals(did).first() - Gets a specific account by DID
accountsDB.accounts.add(account) - Adds a new account
### Non-sensitive Database (db) - Used for settings, contacts, logs, and temp data
Settings operations:
export all settings (Dexie format)
db.settings.get(MASTER_SETTINGS_KEY) - Gets default settings
db.settings.where("accountDid").equals(did).first() - Gets account-specific settings
db.settings.where("accountDid").equals(did).modify(settingsChanges) - Updates account settings
db.settings.add(settingsChanges) - Adds new settings
db.settings.count() - Counts number of settings
db.settings.update(key, changes) - Updates settings
Contacts operations:
export all contacts (Dexie format)
db.contacts.toArray() - Gets all contacts
db.contacts.add(contact) - Adds a new contact
db.contacts.update(did, contactData) - Updates a contact
db.contacts.delete(did) - Deletes a contact
db.contacts.where("did").equals(did).first() - Gets a specific contact by DID
Logs operations:
db.logs.get(todayKey) - Gets logs for a specific day
db.logs.update(todayKey, { message: fullMessage }) - Updates logs
db.logs.clear() - Clears all logs

23
android/.gitignore vendored
View File

@@ -1,5 +1,20 @@
# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore
app/build/*
!app/build/.npmkeep
# Copied web assets
app/src/main/assets/public
# Generated Config files
app/src/main/assets/capacitor.config.json
app/src/main/assets/capacitor.plugins.json
app/src/main/res/xml/config.xml
# secrets
app/gradle.properties.secrets
app/time-safari-upload-key-pkcs12.jks
# Built application files
*.apk
*.aar
@@ -91,11 +106,3 @@ lint/tmp/
# Cordova plugins for Capacitor
capacitor-cordova-android-plugins
# Copied web assets
app/src/main/assets/public
# Generated Config files
app/src/main/assets/capacitor.config.json
app/src/main/assets/capacitor.plugins.json
app/src/main/res/xml/config.xml

View File

@@ -1,2 +0,0 @@
#Fri Mar 21 07:27:50 UTC 2025
gradle.version=8.2.1

Binary file not shown.

View File

@@ -1,2 +0,0 @@
/build/*
!/build/.npmkeep

View File

@@ -1,14 +1,38 @@
apply plugin: 'com.android.application'
// These are sample values to set in gradle.properties.secrets
// MY_KEYSTORE_FILE=time-safari-upload-key-pkcs12.jks
// MY_KEYSTORE_PASSWORD=...
// MY_KEY_ALIAS=time-safari-key-alias
// MY_KEY_PASSWORD=...
// Try to load from environment variables first
project.ext.MY_KEYSTORE_FILE = System.getenv('ANDROID_KEYSTORE_FILE') ?: ""
project.ext.MY_KEYSTORE_PASSWORD = System.getenv('ANDROID_KEYSTORE_PASSWORD') ?: ""
project.ext.MY_KEY_ALIAS = System.getenv('ANDROID_KEY_ALIAS') ?: ""
project.ext.MY_KEY_PASSWORD = System.getenv('ANDROID_KEY_PASSWORD') ?: ""
// If no environment variables, try to load from secrets file
if (!project.ext.MY_KEYSTORE_FILE) {
def secretsPropertiesFile = rootProject.file("app/gradle.properties.secrets")
if (secretsPropertiesFile.exists()) {
Properties secretsProperties = new Properties()
secretsProperties.load(new FileInputStream(secretsPropertiesFile))
secretsProperties.each { name, value ->
project.ext[name] = value
}
}
}
android {
namespace 'app.timesafari'
compileSdk rootProject.ext.compileSdkVersion
defaultConfig {
applicationId "app.timesafari"
applicationId "app.timesafari.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
versionCode 33
versionName "0.5.7"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
@@ -16,10 +40,41 @@ android {
ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
}
}
signingConfigs {
release {
if (project.ext.MY_KEYSTORE_FILE &&
project.ext.MY_KEYSTORE_PASSWORD &&
project.ext.MY_KEY_ALIAS &&
project.ext.MY_KEY_PASSWORD) {
storeFile file(project.ext.MY_KEYSTORE_FILE)
storePassword project.ext.MY_KEYSTORE_PASSWORD
keyAlias project.ext.MY_KEY_ALIAS
keyPassword project.ext.MY_KEY_PASSWORD
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// Only sign if we have the signing config
if (signingConfigs.release.storeFile != null) {
signingConfig signingConfigs.release
}
}
}
// Enable bundle builds (without which it doesn't work right for bundleDebug vs bundleRelease)
bundle {
language {
enableSplit = true
}
density {
enableSplit = true
}
abi {
enableSplit = true
}
}
}
@@ -36,6 +91,8 @@ dependencies {
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
implementation project(':capacitor-android')
implementation project(':capacitor-community-sqlite')
implementation "androidx.biometric:biometric:1.2.0-alpha05"
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"

View File

@@ -9,7 +9,13 @@ android {
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {
implementation project(':capacitor-community-sqlite')
implementation project(':capacitor-mlkit-barcode-scanning')
implementation project(':capacitor-app')
implementation project(':capacitor-camera')
implementation project(':capacitor-filesystem')
implementation project(':capacitor-share')
implementation project(':capawesome-capacitor-file-picker')
}

View File

@@ -0,0 +1,28 @@
{
"project_info": {
"project_number": "123456789000",
"project_id": "timesafari-app",
"storage_bucket": "timesafari-app.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:123456789000:android:1234567890abcdef",
"android_client_info": {
"package_name": "app.timesafari.app"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyDummyKeyForBuildPurposesOnly12345"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
]
}

View File

@@ -21,6 +21,6 @@ public class ExampleInstrumentedTest {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("app.timesafari", appContext.getPackageName());
assertEquals("app.timesafari.app", appContext.getPackageName());
}
}

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
@@ -8,7 +7,6 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
@@ -16,7 +14,6 @@
android:label="@string/title_activity_main"
android:launchMode="singleTask"
android:theme="@style/AppTheme.NoActionBarLaunch">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -28,7 +25,6 @@
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="timesafari" />
</intent-filter>
</activity>
<provider
@@ -36,13 +32,15 @@
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"></meta-data>
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" />
</provider>
</application>
<!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
</manifest>

View File

@@ -16,6 +16,41 @@
}
]
}
},
"SQLite": {
"iosDatabaseLocation": "Library/CapacitorDatabase",
"iosIsEncryption": true,
"iosBiometric": {
"biometricAuth": true,
"biometricTitle": "Biometric login for TimeSafari"
},
"androidIsEncryption": true,
"androidBiometric": {
"biometricAuth": true,
"biometricTitle": "Biometric login for TimeSafari"
}
}
},
"ios": {
"contentInset": "never",
"allowsLinkPreview": true,
"scrollEnabled": true,
"limitsNavigationsToAppBoundDomains": true,
"backgroundColor": "#ffffff",
"allowNavigation": [
"*.timesafari.app",
"*.jsdelivr.net",
"api.endorser.ch"
]
},
"android": {
"allowMixedContent": false,
"captureInput": true,
"webContentsDebuggingEnabled": false,
"allowNavigation": [
"*.timesafari.app",
"*.jsdelivr.net",
"api.endorser.ch"
]
}
}

View File

@@ -1,6 +1,30 @@
[
{
"pkg": "@capacitor-community/sqlite",
"classpath": "com.getcapacitor.community.database.sqlite.CapacitorSQLitePlugin"
},
{
"pkg": "@capacitor-mlkit/barcode-scanning",
"classpath": "io.capawesome.capacitorjs.plugins.mlkit.barcodescanning.BarcodeScannerPlugin"
},
{
"pkg": "@capacitor/app",
"classpath": "com.capacitorjs.plugins.app.AppPlugin"
},
{
"pkg": "@capacitor/camera",
"classpath": "com.capacitorjs.plugins.camera.CameraPlugin"
},
{
"pkg": "@capacitor/filesystem",
"classpath": "com.capacitorjs.plugins.filesystem.FilesystemPlugin"
},
{
"pkg": "@capacitor/share",
"classpath": "com.capacitorjs.plugins.share.SharePlugin"
},
{
"pkg": "@capawesome/capacitor-file-picker",
"classpath": "io.capawesome.capacitorjs.plugins.filepicker.FilePickerPlugin"
}
]

View File

@@ -1,17 +0,0 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="/favicon.ico">
<title>TimeSafari</title>
<script type="module" crossorigin src="/assets/index-CZMUlUNO.js"></script>
</head>
<body>
<noscript>
<strong>We're sorry but TimeSafari doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
</body>
</html>

View File

@@ -1,7 +1,15 @@
package app.timesafari;
import android.os.Bundle;
import com.getcapacitor.BridgeActivity;
//import com.getcapacitor.community.sqlite.SQLite;
public class MainActivity extends BridgeActivity {
// ... existing code ...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initialize SQLite
//registerPlugin(SQLite.class);
}
}

View File

@@ -1,5 +0,0 @@
package timesafari.app;
import com.getcapacitor.BridgeActivity;
public class MainActivity extends BridgeActivity {}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<background>
<inset android:drawable="@mipmap/ic_launcher_background" android:inset="16.7%" />
</background>
<foreground>
<inset android:drawable="@mipmap/ic_launcher_foreground" android:inset="16.7%" />
</foreground>
</adaptive-icon>

View File

@@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<background>
<inset android:drawable="@mipmap/ic_launcher_background" android:inset="16.7%" />
</background>
<foreground>
<inset android:drawable="@mipmap/ic_launcher_foreground" android:inset="16.7%" />
</foreground>
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -2,4 +2,5 @@
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="." />
<cache-path name="my_cache_images" path="." />
<files-path name="my_files" path="." />
</paths>

View File

@@ -7,7 +7,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.2.1'
classpath 'com.android.tools.build:gradle:8.9.1'
classpath 'com.google.gms:google-services:4.4.0'
// NOTE: Do not place your application dependencies here; they belong

View File

@@ -2,5 +2,23 @@
include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
include ':capacitor-community-sqlite'
project(':capacitor-community-sqlite').projectDir = new File('../node_modules/@capacitor-community/sqlite/android')
include ':capacitor-mlkit-barcode-scanning'
project(':capacitor-mlkit-barcode-scanning').projectDir = new File('../node_modules/@capacitor-mlkit/barcode-scanning/android')
include ':capacitor-app'
project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android')
include ':capacitor-camera'
project(':capacitor-camera').projectDir = new File('../node_modules/@capacitor/camera/android')
include ':capacitor-filesystem'
project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android')
include ':capacitor-share'
project(':capacitor-share').projectDir = new File('../node_modules/@capacitor/share/android')
include ':capawesome-capacitor-file-picker'
project(':capawesome-capacitor-file-picker').projectDir = new File('../node_modules/@capawesome/capacitor-file-picker/android')

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

2
assets/README.md Normal file
View File

@@ -0,0 +1,2 @@
Application icons are here. They are processed for android & ios by the `capacitor-assets` command, as indicated in the BUILDING.md file.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

BIN
assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

BIN
assets/splash-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

BIN
assets/splash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

4
build.sh Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/bash
export IMAGENAME="$(basename $PWD):1.0"
docker build . --network=host -t $IMAGENAME --no-cache

56
capacitor.config.json Normal file
View File

@@ -0,0 +1,56 @@
{
"appId": "app.timesafari",
"appName": "TimeSafari",
"webDir": "dist",
"bundledWebRuntime": false,
"server": {
"cleartext": true
},
"plugins": {
"App": {
"appUrlOpen": {
"handlers": [
{
"url": "timesafari://*",
"autoVerify": true
}
]
}
},
"SQLite": {
"iosDatabaseLocation": "Library/CapacitorDatabase",
"iosIsEncryption": true,
"iosBiometric": {
"biometricAuth": true,
"biometricTitle": "Biometric login for TimeSafari"
},
"androidIsEncryption": true,
"androidBiometric": {
"biometricAuth": true,
"biometricTitle": "Biometric login for TimeSafari"
}
}
},
"ios": {
"contentInset": "never",
"allowsLinkPreview": true,
"scrollEnabled": true,
"limitsNavigationsToAppBoundDomains": true,
"backgroundColor": "#ffffff",
"allowNavigation": [
"*.timesafari.app",
"*.jsdelivr.net",
"api.endorser.ch"
]
},
"android": {
"allowMixedContent": false,
"captureInput": true,
"webContentsDebuggingEnabled": false,
"allowNavigation": [
"*.timesafari.app",
"*.jsdelivr.net",
"api.endorser.ch"
]
}
}

View File

@@ -1,25 +0,0 @@
import { CapacitorConfig } from '@capacitor/cli';
const config: CapacitorConfig = {
appId: 'app.timesafari',
appName: 'TimeSafari',
webDir: 'dist',
bundledWebRuntime: false,
server: {
cleartext: true,
},
plugins: {
App: {
appUrlOpen: {
handlers: [
{
url: "timesafari://*",
autoVerify: true
}
]
}
}
}
};
export default config;

View File

@@ -9,21 +9,95 @@ The deep linking system uses a multi-layered type safety approach:
- Enforces parameter requirements
- Sanitizes input data
- Provides detailed validation errors
- Generates TypeScript types automatically
2. **TypeScript Types**
- Generated from Zod schemas
- Generated from Zod schemas using `z.infer`
- Ensures compile-time type safety
- Provides IDE autocompletion
- Catches type errors during development
- Maintains single source of truth for types
3. **Router Integration**
- Type-safe parameter passing
- Route-specific parameter validation
- Query parameter type checking
- Automatic type inference for route parameters
## Type System Implementation
### Zod Schema to TypeScript Type Generation
```typescript
// Define the schema
const claimSchema = z.object({
id: z.string(),
view: z.enum(["details", "certificate", "raw"]).optional()
});
// TypeScript type is automatically generated
type ClaimParams = z.infer<typeof claimSchema>;
// Equivalent to:
// type ClaimParams = {
// id: string;
// view?: "details" | "certificate" | "raw";
// }
```
### Type Safety Layers
1. **Schema Definition**
```typescript
// src/interfaces/deepLinks.ts
export const deepLinkSchemas = {
claim: z.object({
id: z.string(),
view: z.enum(["details", "certificate", "raw"]).optional()
}),
// Other route schemas...
};
```
2. **Type Generation**
```typescript
// Types are automatically generated from schemas
export type DeepLinkParams = {
[K in keyof typeof deepLinkSchemas]: z.infer<(typeof deepLinkSchemas)[K]>;
};
```
3. **Runtime Validation**
```typescript
// In DeepLinkHandler
const result = deepLinkSchemas.claim.safeParse(params);
if (!result.success) {
// Handle validation errors
console.error(result.error);
}
```
### Error Handling Types
```typescript
export interface DeepLinkError extends Error {
code: string;
details?: unknown;
}
// Usage in error handling
try {
await handler.handleDeepLink(url);
} catch (error) {
if (error instanceof DeepLinkError) {
// Type-safe error handling
console.error(error.code, error.message);
}
}
```
## Implementation Files
- `src/types/deepLinks.ts`: Type definitions and validation schemas
- `src/interfaces/deepLinks.ts`: Type definitions and validation schemas
- `src/services/deepLinks.ts`: Deep link processing service
- `src/main.capacitor.ts`: Capacitor integration

View File

@@ -0,0 +1,295 @@
# Database Migration Guide
## Overview
The Database Migration feature allows you to compare and migrate data between Dexie (IndexedDB) and SQLite databases in the TimeSafari application. This is particularly useful during the transition from the old Dexie-based storage system to the new SQLite-based system.
## Features
### 1. Database Comparison
- Compare data between Dexie and SQLite databases
- View detailed differences in contacts and settings
- Identify added, modified, and missing records
- Export comparison results for analysis
### 2. Data Migration
- Migrate contacts from Dexie to SQLite
- Migrate settings from Dexie to SQLite
- Option to overwrite existing records or skip them
- Comprehensive error handling and reporting
### 3. User Interface
- Modern, responsive UI built with Tailwind CSS
- Real-time loading states and progress indicators
- Clear success and error messaging
- Export functionality for comparison data
## Prerequisites
### Enable Dexie Database
Before using the migration features, you must enable the Dexie database by setting:
```typescript
// In constants/app.ts
export const USE_DEXIE_DB = true;
```
**Note**: This should only be enabled temporarily during migration. Remember to set it back to `false` after migration is complete.
## Accessing the Migration Interface
1. Navigate to the **Account** page in the TimeSafari app
2. Scroll down to find the **Database Migration** link
3. Click the link to open the migration interface
## Using the Migration Interface
### Step 1: Compare Databases
1. Click the **"Compare Databases"** button
2. The system will retrieve data from both Dexie and SQLite databases
3. Review the comparison results showing:
- Summary counts for each database
- Detailed differences (added, modified, missing records)
- Specific records that need attention
### Step 2: Review Differences
The comparison results are displayed in several sections:
#### Summary Cards
- **Dexie Contacts**: Number of contacts in Dexie database
- **SQLite Contacts**: Number of contacts in SQLite database
- **Dexie Settings**: Number of settings in Dexie database
- **SQLite Settings**: Number of settings in SQLite database
#### Contact Differences
- **Added**: Contacts in Dexie but not in SQLite
- **Modified**: Contacts that differ between databases
- **Missing**: Contacts in SQLite but not in Dexie
#### Settings Differences
- **Added**: Settings in Dexie but not in SQLite
- **Modified**: Settings that differ between databases
- **Missing**: Settings in SQLite but not in Dexie
### Step 3: Configure Migration Options
Before migrating data, configure the migration options:
- **Overwrite existing records**: When enabled, existing records in SQLite will be updated with data from Dexie. When disabled, existing records will be skipped.
### Step 4: Migrate Data
#### Migrate Contacts
1. Click the **"Migrate Contacts"** button
2. The system will transfer contacts from Dexie to SQLite
3. Review the migration results showing:
- Number of contacts successfully migrated
- Any warnings or errors encountered
#### Migrate Settings
1. Click the **"Migrate Settings"** button
2. The system will transfer settings from Dexie to SQLite
3. Review the migration results showing:
- Number of settings successfully migrated
- Any warnings or errors encountered
### Step 5: Export Comparison (Optional)
1. Click the **"Export Comparison"** button
2. A JSON file will be downloaded containing the complete comparison data
3. This file can be used for analysis or backup purposes
## Migration Process Details
### Contact Migration
The contact migration process:
1. **Retrieves** all contacts from Dexie database
2. **Checks** for existing contacts in SQLite by DID
3. **Inserts** new contacts or **updates** existing ones (if overwrite is enabled)
4. **Handles** complex fields like `contactMethods` (JSON arrays)
5. **Reports** success/failure for each contact
### Settings Migration
The settings migration process:
1. **Retrieves** all settings from Dexie database
2. **Focuses** on key user-facing settings:
- `firstName`
- `isRegistered`
- `profileImageUrl`
- `showShortcutBvc`
- `searchBoxes`
3. **Preserves** other settings in SQLite
4. **Reports** success/failure for each setting
## Error Handling
### Common Issues
#### Dexie Database Not Enabled
**Error**: "Dexie database is not enabled"
**Solution**: Set `USE_DEXIE_DB = true` in `constants/app.ts`
#### Database Connection Issues
**Error**: "Failed to retrieve Dexie contacts"
**Solution**: Check that the Dexie database is properly initialized and accessible
#### SQLite Query Errors
**Error**: "Failed to retrieve SQLite contacts"
**Solution**: Verify that the SQLite database is properly set up and the platform service is working
#### Migration Failures
**Error**: "Migration failed: [specific error]"
**Solution**: Review the error details and check data integrity in both databases
### Error Recovery
1. **Review** the error messages carefully
2. **Check** the browser console for additional details
3. **Verify** database connectivity and permissions
4. **Retry** the operation if appropriate
5. **Export** comparison data for manual review if needed
## Best Practices
### Before Migration
1. **Backup** your data if possible
2. **Test** the migration on a small dataset first
3. **Verify** that both databases are accessible
4. **Review** the comparison results before migrating
### During Migration
1. **Don't** interrupt the migration process
2. **Monitor** the progress and error messages
3. **Note** any warnings or skipped records
4. **Export** comparison data for reference
### After Migration
1. **Verify** that data was migrated correctly
2. **Test** the application functionality
3. **Disable** Dexie database (`USE_DEXIE_DB = false`)
4. **Clean up** any temporary files or exports
## Technical Details
### Database Schema
The migration handles the following data structures:
#### Contacts Table
```typescript
interface Contact {
did: string; // Decentralized Identifier
name: string; // Contact name
contactMethods: ContactMethod[]; // Array of contact methods
nextPubKeyHashB64: string; // Next public key hash
notes: string; // Contact notes
profileImageUrl: string; // Profile image URL
publicKeyBase64: string; // Public key in base64
seesMe: boolean; // Visibility flag
registered: boolean; // Registration status
}
```
#### Settings Table
```typescript
interface Settings {
id: number; // Settings ID
accountDid: string; // Account DID
activeDid: string; // Active DID
firstName: string; // User's first name
isRegistered: boolean; // Registration status
profileImageUrl: string; // Profile image URL
showShortcutBvc: boolean; // UI preference
searchBoxes: any[]; // Search configuration
// ... other fields
}
```
### Migration Logic
The migration service uses sophisticated comparison logic:
1. **Primary Key Matching**: Uses DID for contacts, ID for settings
2. **Deep Comparison**: Compares all fields including complex objects
3. **JSON Handling**: Properly handles JSON fields like `contactMethods` and `searchBoxes`
4. **Conflict Resolution**: Provides options for handling existing records
### Performance Considerations
- **Batch Processing**: Processes records one by one for reliability
- **Error Isolation**: Individual record failures don't stop the entire migration
- **Memory Management**: Handles large datasets efficiently
- **Progress Reporting**: Provides real-time feedback during migration
## Troubleshooting
### Migration Stuck
If the migration appears to be stuck:
1. **Check** the browser console for errors
2. **Refresh** the page and try again
3. **Verify** database connectivity
4. **Check** for large datasets that might take time
### Incomplete Migration
If migration doesn't complete:
1. **Review** error messages
2. **Check** data integrity in both databases
3. **Export** comparison data for manual review
4. **Consider** migrating in smaller batches
### Data Inconsistencies
If you notice data inconsistencies:
1. **Export** comparison data
2. **Review** the differences carefully
3. **Manually** verify critical records
4. **Consider** selective migration of specific records
## Support
For issues with the Database Migration feature:
1. **Check** this documentation first
2. **Review** the browser console for error details
3. **Export** comparison data for analysis
4. **Contact** the development team with specific error details
## Security Considerations
- **Data Privacy**: Migration data is processed locally and not sent to external servers
- **Access Control**: Only users with access to the account can perform migration
- **Data Integrity**: Migration preserves data integrity and handles conflicts gracefully
- **Audit Trail**: Export functionality provides an audit trail of migration operations
---
**Note**: This migration tool is designed for the transition period between database systems. Once migration is complete and verified, the Dexie database should be disabled to avoid confusion and potential data conflicts.

View File

@@ -0,0 +1,418 @@
# Dexie to absurd-sql Mapping Guide
## Schema Mapping
### Current Dexie Schema
```typescript
// Current Dexie schema
const db = new Dexie('TimeSafariDB');
db.version(1).stores({
accounts: 'did, publicKeyHex, createdAt, updatedAt',
settings: 'key, value, updatedAt',
contacts: 'id, did, name, createdAt, updatedAt'
});
```
### New SQLite Schema
```sql
-- New SQLite schema
CREATE TABLE accounts (
did TEXT PRIMARY KEY,
public_key_hex TEXT NOT NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE TABLE settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
updated_at INTEGER NOT NULL
);
CREATE TABLE contacts (
id TEXT PRIMARY KEY,
did TEXT NOT NULL,
name TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (did) REFERENCES accounts(did)
);
-- Indexes for performance
CREATE INDEX idx_accounts_created_at ON accounts(created_at);
CREATE INDEX idx_contacts_did ON contacts(did);
CREATE INDEX idx_settings_updated_at ON settings(updated_at);
```
## Query Mapping
### 1. Account Operations
#### Get Account by DID
```typescript
// Dexie
const account = await db.accounts.get(did);
// absurd-sql
const result = await db.exec(`
SELECT * FROM accounts WHERE did = ?
`, [did]);
const account = result[0]?.values[0];
```
#### Get All Accounts
```typescript
// Dexie
const accounts = await db.accounts.toArray();
// absurd-sql
const result = await db.exec(`
SELECT * FROM accounts ORDER BY created_at DESC
`);
const accounts = result[0]?.values || [];
```
#### Add Account
```typescript
// Dexie
await db.accounts.add({
did,
publicKeyHex,
createdAt: Date.now(),
updatedAt: Date.now()
});
// absurd-sql
await db.run(`
INSERT INTO accounts (did, public_key_hex, created_at, updated_at)
VALUES (?, ?, ?, ?)
`, [did, publicKeyHex, Date.now(), Date.now()]);
```
#### Update Account
```typescript
// Dexie
await db.accounts.update(did, {
publicKeyHex,
updatedAt: Date.now()
});
// absurd-sql
await db.run(`
UPDATE accounts
SET public_key_hex = ?, updated_at = ?
WHERE did = ?
`, [publicKeyHex, Date.now(), did]);
```
### 2. Settings Operations
#### Get Setting
```typescript
// Dexie
const setting = await db.settings.get(key);
// absurd-sql
const result = await db.exec(`
SELECT * FROM settings WHERE key = ?
`, [key]);
const setting = result[0]?.values[0];
```
#### Set Setting
```typescript
// Dexie
await db.settings.put({
key,
value,
updatedAt: Date.now()
});
// absurd-sql
await db.run(`
INSERT INTO settings (key, value, updated_at)
VALUES (?, ?, ?)
ON CONFLICT(key) DO UPDATE SET
value = excluded.value,
updated_at = excluded.updated_at
`, [key, value, Date.now()]);
```
### 3. Contact Operations
#### Get Contacts by Account
```typescript
// Dexie
const contacts = await db.contacts
.where('did')
.equals(accountDid)
.toArray();
// absurd-sql
const result = await db.exec(`
SELECT * FROM contacts
WHERE did = ?
ORDER BY created_at DESC
`, [accountDid]);
const contacts = result[0]?.values || [];
```
#### Add Contact
```typescript
// Dexie
await db.contacts.add({
id: generateId(),
did: accountDid,
name,
createdAt: Date.now(),
updatedAt: Date.now()
});
// absurd-sql
await db.run(`
INSERT INTO contacts (id, did, name, created_at, updated_at)
VALUES (?, ?, ?, ?, ?)
`, [generateId(), accountDid, name, Date.now(), Date.now()]);
```
## Transaction Mapping
### Batch Operations
```typescript
// Dexie
await db.transaction('rw', [db.accounts, db.contacts], async () => {
await db.accounts.add(account);
await db.contacts.bulkAdd(contacts);
});
// absurd-sql
await db.exec('BEGIN TRANSACTION;');
try {
await db.run(`
INSERT INTO accounts (did, public_key_hex, created_at, updated_at)
VALUES (?, ?, ?, ?)
`, [account.did, account.publicKeyHex, account.createdAt, account.updatedAt]);
for (const contact of contacts) {
await db.run(`
INSERT INTO contacts (id, did, name, created_at, updated_at)
VALUES (?, ?, ?, ?, ?)
`, [contact.id, contact.did, contact.name, contact.createdAt, contact.updatedAt]);
}
await db.exec('COMMIT;');
} catch (error) {
await db.exec('ROLLBACK;');
throw error;
}
```
## Migration Helper Functions
### 1. Data Export (Dexie to JSON)
```typescript
async function exportDexieData(): Promise<MigrationData> {
const db = new Dexie('TimeSafariDB');
return {
accounts: await db.accounts.toArray(),
settings: await db.settings.toArray(),
contacts: await db.contacts.toArray(),
metadata: {
version: '1.0.0',
timestamp: Date.now(),
dexieVersion: Dexie.version
}
};
}
```
### 2. Data Import (JSON to absurd-sql)
```typescript
async function importToAbsurdSql(data: MigrationData): Promise<void> {
await db.exec('BEGIN TRANSACTION;');
try {
// Import accounts
for (const account of data.accounts) {
await db.run(`
INSERT INTO accounts (did, public_key_hex, created_at, updated_at)
VALUES (?, ?, ?, ?)
`, [account.did, account.publicKeyHex, account.createdAt, account.updatedAt]);
}
// Import settings
for (const setting of data.settings) {
await db.run(`
INSERT INTO settings (key, value, updated_at)
VALUES (?, ?, ?)
`, [setting.key, setting.value, setting.updatedAt]);
}
// Import contacts
for (const contact of data.contacts) {
await db.run(`
INSERT INTO contacts (id, did, name, created_at, updated_at)
VALUES (?, ?, ?, ?, ?)
`, [contact.id, contact.did, contact.name, contact.createdAt, contact.updatedAt]);
}
await db.exec('COMMIT;');
} catch (error) {
await db.exec('ROLLBACK;');
throw error;
}
}
```
### 3. Verification
```typescript
async function verifyMigration(dexieData: MigrationData): Promise<boolean> {
// Verify account count
const accountResult = await db.exec('SELECT COUNT(*) as count FROM accounts');
const accountCount = accountResult[0].values[0][0];
if (accountCount !== dexieData.accounts.length) {
return false;
}
// Verify settings count
const settingsResult = await db.exec('SELECT COUNT(*) as count FROM settings');
const settingsCount = settingsResult[0].values[0][0];
if (settingsCount !== dexieData.settings.length) {
return false;
}
// Verify contacts count
const contactsResult = await db.exec('SELECT COUNT(*) as count FROM contacts');
const contactsCount = contactsResult[0].values[0][0];
if (contactsCount !== dexieData.contacts.length) {
return false;
}
// Verify data integrity
for (const account of dexieData.accounts) {
const result = await db.exec(
'SELECT * FROM accounts WHERE did = ?',
[account.did]
);
const migratedAccount = result[0]?.values[0];
if (!migratedAccount ||
migratedAccount[1] !== account.publicKeyHex) { // public_key_hex is second column
return false;
}
}
return true;
}
```
## Performance Considerations
### 1. Indexing
- Dexie automatically creates indexes based on the schema
- absurd-sql requires explicit index creation
- Added indexes for frequently queried fields
- Use `PRAGMA journal_mode=MEMORY;` for better performance
### 2. Batch Operations
- Dexie has built-in bulk operations
- absurd-sql uses transactions for batch operations
- Consider chunking large datasets
- Use prepared statements for repeated queries
### 3. Query Optimization
- Dexie uses IndexedDB's native indexing
- absurd-sql requires explicit query optimization
- Use prepared statements for repeated queries
- Consider using `PRAGMA synchronous=NORMAL;` for better performance
## Error Handling
### 1. Common Errors
```typescript
// Dexie errors
try {
await db.accounts.add(account);
} catch (error) {
if (error instanceof Dexie.ConstraintError) {
// Handle duplicate key
}
}
// absurd-sql errors
try {
await db.run(`
INSERT INTO accounts (did, public_key_hex, created_at, updated_at)
VALUES (?, ?, ?, ?)
`, [account.did, account.publicKeyHex, account.createdAt, account.updatedAt]);
} catch (error) {
if (error.message.includes('UNIQUE constraint failed')) {
// Handle duplicate key
}
}
```
### 2. Transaction Recovery
```typescript
// Dexie transaction
try {
await db.transaction('rw', db.accounts, async () => {
// Operations
});
} catch (error) {
// Dexie automatically rolls back
}
// absurd-sql transaction
try {
await db.exec('BEGIN TRANSACTION;');
// Operations
await db.exec('COMMIT;');
} catch (error) {
await db.exec('ROLLBACK;');
throw error;
}
```
## Migration Strategy
1. **Preparation**
- Export all Dexie data
- Verify data integrity
- Create SQLite schema
- Setup indexes
2. **Migration**
- Import data in transactions
- Verify each batch
- Handle errors gracefully
- Maintain backup
3. **Verification**
- Compare record counts
- Verify data integrity
- Test common queries
- Validate relationships
4. **Cleanup**
- Remove Dexie database
- Clear IndexedDB storage
- Update application code
- Remove old dependencies

View File

@@ -0,0 +1,272 @@
# Migration Fence Definition: Dexie to SQLite
## Overview
This document defines the **migration fence** - the boundary between the legacy Dexie (IndexedDB) storage system and the new SQLite-based storage system in TimeSafari. The fence ensures controlled migration while maintaining data integrity and application stability.
## Current Migration Status
### ✅ Completed Components
- **SQLite Database Service**: Fully implemented with absurd-sql
- **Platform Service Layer**: Unified database interface across platforms
- **Migration Tools**: Data comparison and transfer utilities
- **Schema Migration**: Complete table structure migration
- **Data Export/Import**: Backup and restore functionality
### 🔄 Active Migration Components
- **Settings Migration**: Core user settings transferred
- **Account Migration**: Identity and key management
- **Contact Migration**: User contact data (via import interface)
### ❌ Legacy Components (Fence Boundary)
- **Dexie Database**: Legacy IndexedDB storage (disabled by default)
- **Dexie-Specific Code**: Direct database access patterns
- **Legacy Migration Paths**: Old data transfer methods
## Migration Fence Definition
### 1. Configuration Boundary
```typescript
// src/constants/app.ts
export const USE_DEXIE_DB = false; // FENCE: Controls legacy database access
```
**Fence Rule**: When `USE_DEXIE_DB = false`:
- All new data operations use SQLite
- Legacy Dexie database is not initialized
- Migration tools are the only path to legacy data
**Fence Rule**: When `USE_DEXIE_DB = true`:
- Legacy database is available for migration
- Dual-write operations may be enabled
- Migration tools can access both databases
### 2. Service Layer Boundary
```typescript
// src/services/PlatformServiceFactory.ts
export class PlatformServiceFactory {
public static getInstance(): PlatformService {
// FENCE: All database operations go through platform service
// No direct Dexie access outside migration tools
}
}
```
**Fence Rule**: All database operations must use:
- `PlatformService.dbQuery()` for read operations
- `PlatformService.dbExec()` for write operations
- No direct `db.` or `accountsDBPromise` access in application code
### 3. Data Access Patterns
#### ✅ Allowed (Inside Fence)
```typescript
// Use platform service for all database operations
const platformService = PlatformServiceFactory.getInstance();
const contacts = await platformService.dbQuery(
"SELECT * FROM contacts WHERE did = ?",
[accountDid]
);
```
#### ❌ Forbidden (Outside Fence)
```typescript
// Direct Dexie access (legacy pattern)
const contacts = await db.contacts.where('did').equals(accountDid).toArray();
// Direct database reference
const result = await accountsDBPromise;
```
### 4. Migration Tool Boundary
```typescript
// src/services/indexedDBMigrationService.ts
// FENCE: Only migration tools can access both databases
export async function compareDatabases(): Promise<DataComparison> {
// This is the ONLY place where both databases are accessed
}
```
**Fence Rule**: Migration tools are the exclusive interface between:
- Legacy Dexie database
- New SQLite database
- Data comparison and transfer operations
## Migration Fence Guidelines
### 1. Code Development Rules
#### New Feature Development
- **Always** use `PlatformService` for database operations
- **Never** import or reference Dexie directly
- **Always** test with `USE_DEXIE_DB = false`
#### Legacy Code Maintenance
- **Only** modify Dexie code for migration purposes
- **Always** add migration tests for schema changes
- **Never** add new Dexie-specific features
### 2. Data Integrity Rules
#### Migration Safety
- **Always** create backups before migration
- **Always** verify data integrity after migration
- **Never** delete legacy data until verified
#### Rollback Strategy
- **Always** maintain ability to rollback to Dexie
- **Always** preserve migration logs
- **Never** assume migration is irreversible
### 3. Testing Requirements
#### Migration Testing
```typescript
// Required test pattern for migration
describe('Database Migration', () => {
it('should migrate data without loss', async () => {
// 1. Enable Dexie
// 2. Create test data
// 3. Run migration
// 4. Verify data integrity
// 5. Disable Dexie
});
});
```
#### Application Testing
```typescript
// Required test pattern for application features
describe('Feature with Database', () => {
it('should work with SQLite only', async () => {
// Test with USE_DEXIE_DB = false
// Verify all operations use PlatformService
});
});
```
## Migration Fence Enforcement
### 1. Static Analysis
#### ESLint Rules
```json
{
"rules": {
"no-restricted-imports": [
"error",
{
"patterns": [
{
"group": ["../db/index"],
"message": "Use PlatformService instead of direct Dexie access"
}
]
}
]
}
}
```
#### TypeScript Rules
```json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true
}
}
```
### 2. Runtime Checks
#### Development Mode Validation
```typescript
// Development-only fence validation
if (import.meta.env.DEV && USE_DEXIE_DB) {
console.warn('⚠️ Dexie is enabled - migration mode active');
}
```
#### Production Safety
```typescript
// Production fence enforcement
if (import.meta.env.PROD && USE_DEXIE_DB) {
throw new Error('Dexie cannot be enabled in production');
}
```
## Migration Fence Timeline
### Phase 1: Fence Establishment ✅
- [x] Define migration fence boundaries
- [x] Implement PlatformService layer
- [x] Create migration tools
- [x] Set `USE_DEXIE_DB = false` by default
### Phase 2: Data Migration 🔄
- [x] Migrate core settings
- [x] Migrate account data
- [ ] Complete contact migration
- [ ] Verify all data integrity
### Phase 3: Code Cleanup 📋
- [ ] Remove unused Dexie imports
- [ ] Clean up legacy database code
- [ ] Update all documentation
- [ ] Remove migration tools
### Phase 4: Fence Removal 🎯
- [ ] Remove `USE_DEXIE_DB` constant
- [ ] Remove Dexie dependencies
- [ ] Remove migration service
- [ ] Finalize SQLite-only architecture
## Security Considerations
### 1. Data Protection
- **Encryption**: Maintain encryption standards across migration
- **Access Control**: Preserve user privacy during migration
- **Audit Trail**: Log all migration operations
### 2. Error Handling
- **Graceful Degradation**: Handle migration failures gracefully
- **User Communication**: Clear messaging about migration status
- **Recovery Options**: Provide rollback mechanisms
## Performance Considerations
### 1. Migration Performance
- **Batch Operations**: Use transactions for bulk data transfer
- **Progress Indicators**: Show migration progress to users
- **Background Processing**: Non-blocking migration operations
### 2. Application Performance
- **Query Optimization**: Optimize SQLite queries for performance
- **Indexing Strategy**: Maintain proper database indexes
- **Memory Management**: Efficient memory usage during migration
## Documentation Requirements
### 1. Code Documentation
- **Migration Fence Comments**: Document fence boundaries in code
- **API Documentation**: Update all database API documentation
- **Migration Guides**: Comprehensive migration documentation
### 2. User Documentation
- **Migration Instructions**: Clear user migration steps
- **Troubleshooting**: Common migration issues and solutions
- **Rollback Instructions**: How to revert if needed
## Conclusion
The migration fence provides a controlled boundary between legacy and new database systems, ensuring:
- **Data Integrity**: No data loss during migration
- **Application Stability**: Consistent behavior across platforms
- **Development Clarity**: Clear guidelines for code development
- **Migration Safety**: Controlled and reversible migration process
This fence will remain in place until all data is successfully migrated and verified, at which point the legacy system can be safely removed.

View File

@@ -0,0 +1,355 @@
# Database Migration Security Audit Checklist
## Overview
This document provides a comprehensive security audit checklist for the Dexie to SQLite migration in TimeSafari. The checklist ensures that data protection, privacy, and security are maintained throughout the migration process.
## Pre-Migration Security Assessment
### 1. Data Classification and Sensitivity
- [ ] **Data Inventory**
- [ ] Identify all sensitive data types (DIDs, private keys, personal information)
- [ ] Document data retention requirements
- [ ] Map data relationships and dependencies
- [ ] Assess data sensitivity levels (public, internal, confidential, restricted)
- [ ] **Encryption Assessment**
- [ ] Verify current encryption methods for sensitive data
- [ ] Document encryption keys and their management
- [ ] Assess encryption strength and compliance
- [ ] Plan encryption migration strategy
### 2. Access Control Review
- [ ] **User Access Rights**
- [ ] Audit current user permissions and roles
- [ ] Document access control mechanisms
- [ ] Verify principle of least privilege
- [ ] Plan access control migration
- [ ] **System Access**
- [ ] Review database access patterns
- [ ] Document authentication mechanisms
- [ ] Assess session management
- [ ] Plan authentication migration
### 3. Compliance Requirements
- [ ] **Regulatory Compliance**
- [ ] Identify applicable regulations (GDPR, CCPA, etc.)
- [ ] Document data processing requirements
- [ ] Assess privacy impact
- [ ] Plan compliance verification
- [ ] **Industry Standards**
- [ ] Review security standards compliance
- [ ] Document security controls
- [ ] Assess audit requirements
- [ ] Plan standards compliance
## Migration Security Controls
### 1. Data Protection During Migration
- [ ] **Encryption in Transit**
- [ ] Verify all data transfers are encrypted
- [ ] Use secure communication protocols (TLS 1.3+)
- [ ] Implement secure API endpoints
- [ ] Monitor encryption status
- [ ] **Encryption at Rest**
- [ ] Maintain encryption for stored data
- [ ] Verify encryption key management
- [ ] Test encryption/decryption processes
- [ ] Document encryption procedures
### 2. Access Control During Migration
- [ ] **Authentication**
- [ ] Maintain user authentication during migration
- [ ] Verify session management
- [ ] Implement secure token handling
- [ ] Monitor authentication events
- [ ] **Authorization**
- [ ] Preserve user permissions during migration
- [ ] Verify role-based access control
- [ ] Implement audit logging
- [ ] Monitor access patterns
### 3. Data Integrity
- [ ] **Data Validation**
- [ ] Implement input validation for all data
- [ ] Verify data format consistency
- [ ] Test data transformation processes
- [ ] Document validation rules
- [ ] **Data Verification**
- [ ] Implement checksums for data integrity
- [ ] Verify data completeness after migration
- [ ] Test data consistency checks
- [ ] Document verification procedures
## Migration Process Security
### 1. Backup Security
- [ ] **Backup Creation**
- [ ] Create encrypted backups before migration
- [ ] Verify backup integrity
- [ ] Store backups securely
- [ ] Test backup restoration
- [ ] **Backup Access**
- [ ] Limit backup access to authorized personnel
- [ ] Implement backup access logging
- [ ] Verify backup encryption
- [ ] Document backup procedures
### 2. Migration Tool Security
- [ ] **Tool Authentication**
- [ ] Implement secure authentication for migration tools
- [ ] Verify tool access controls
- [ ] Monitor tool usage
- [ ] Document tool security
- [ ] **Tool Validation**
- [ ] Verify migration tool integrity
- [ ] Test tool security features
- [ ] Validate tool outputs
- [ ] Document tool validation
### 3. Error Handling
- [ ] **Error Security**
- [ ] Implement secure error handling
- [ ] Avoid information disclosure in errors
- [ ] Log security-relevant errors
- [ ] Document error procedures
- [ ] **Recovery Security**
- [ ] Implement secure recovery procedures
- [ ] Verify recovery data protection
- [ ] Test recovery processes
- [ ] Document recovery security
## Post-Migration Security
### 1. Data Verification
- [ ] **Data Completeness**
- [ ] Verify all data was migrated successfully
- [ ] Check for data corruption
- [ ] Validate data relationships
- [ ] Document verification results
- [ ] **Data Accuracy**
- [ ] Verify data accuracy after migration
- [ ] Test data consistency
- [ ] Validate data integrity
- [ ] Document accuracy checks
### 2. Access Control Verification
- [ ] **User Access**
- [ ] Verify user access rights after migration
- [ ] Test authentication mechanisms
- [ ] Validate authorization rules
- [ ] Document access verification
- [ ] **System Access**
- [ ] Verify system access controls
- [ ] Test API security
- [ ] Validate session management
- [ ] Document system security
### 3. Security Testing
- [ ] **Penetration Testing**
- [ ] Conduct security penetration testing
- [ ] Test for common vulnerabilities
- [ ] Verify security controls
- [ ] Document test results
- [ ] **Vulnerability Assessment**
- [ ] Scan for security vulnerabilities
- [ ] Assess security posture
- [ ] Identify security gaps
- [ ] Document assessment results
## Monitoring and Logging
### 1. Security Monitoring
- [ ] **Access Monitoring**
- [ ] Monitor database access patterns
- [ ] Track user authentication events
- [ ] Monitor system access
- [ ] Document monitoring procedures
- [ ] **Data Monitoring**
- [ ] Monitor data access patterns
- [ ] Track data modification events
- [ ] Monitor data integrity
- [ ] Document data monitoring
### 2. Security Logging
- [ ] **Audit Logging**
- [ ] Implement comprehensive audit logging
- [ ] Log all security-relevant events
- [ ] Secure log storage and access
- [ ] Document logging procedures
- [ ] **Log Analysis**
- [ ] Implement log analysis tools
- [ ] Monitor for security incidents
- [ ] Analyze security trends
- [ ] Document analysis procedures
## Incident Response
### 1. Security Incident Planning
- [ ] **Incident Response Plan**
- [ ] Develop security incident response plan
- [ ] Define incident response procedures
- [ ] Train incident response team
- [ ] Document response procedures
- [ ] **Incident Detection**
- [ ] Implement incident detection mechanisms
- [ ] Monitor for security incidents
- [ ] Establish incident reporting procedures
- [ ] Document detection procedures
### 2. Recovery Procedures
- [ ] **Data Recovery**
- [ ] Develop data recovery procedures
- [ ] Test recovery processes
- [ ] Verify recovery data integrity
- [ ] Document recovery procedures
- [ ] **System Recovery**
- [ ] Develop system recovery procedures
- [ ] Test system recovery
- [ ] Verify system security after recovery
- [ ] Document recovery procedures
## Compliance Verification
### 1. Regulatory Compliance
- [ ] **Privacy Compliance**
- [ ] Verify GDPR compliance
- [ ] Check CCPA compliance
- [ ] Assess other privacy regulations
- [ ] Document compliance status
- [ ] **Security Compliance**
- [ ] Verify security standard compliance
- [ ] Check industry requirements
- [ ] Assess security certifications
- [ ] Document compliance status
### 2. Audit Requirements
- [ ] **Audit Trail**
- [ ] Maintain comprehensive audit trail
- [ ] Verify audit log integrity
- [ ] Test audit log accessibility
- [ ] Document audit procedures
- [ ] **Audit Reporting**
- [ ] Generate audit reports
- [ ] Verify report accuracy
- [ ] Distribute reports securely
- [ ] Document reporting procedures
## Documentation and Training
### 1. Security Documentation
- [ ] **Security Procedures**
- [ ] Document security procedures
- [ ] Update security policies
- [ ] Create security guidelines
- [ ] Maintain documentation
- [ ] **Security Training**
- [ ] Develop security training materials
- [ ] Train staff on security procedures
- [ ] Verify training effectiveness
- [ ] Document training procedures
### 2. Ongoing Security
- [ ] **Security Maintenance**
- [ ] Establish security maintenance procedures
- [ ] Schedule security updates
- [ ] Monitor security trends
- [ ] Document maintenance procedures
- [ ] **Security Review**
- [ ] Conduct regular security reviews
- [ ] Update security controls
- [ ] Assess security effectiveness
- [ ] Document review procedures
## Risk Assessment
### 1. Risk Identification
- [ ] **Security Risks**
- [ ] Identify potential security risks
- [ ] Assess risk likelihood and impact
- [ ] Prioritize security risks
- [ ] Document risk assessment
- [ ] **Mitigation Strategies**
- [ ] Develop risk mitigation strategies
- [ ] Implement risk controls
- [ ] Monitor risk status
- [ ] Document mitigation procedures
### 2. Risk Monitoring
- [ ] **Risk Tracking**
- [ ] Track identified risks
- [ ] Monitor risk status
- [ ] Update risk assessments
- [ ] Document risk tracking
- [ ] **Risk Reporting**
- [ ] Generate risk reports
- [ ] Distribute risk information
- [ ] Update risk documentation
- [ ] Document reporting procedures
## Conclusion
This security audit checklist ensures that the database migration maintains the highest standards of data protection, privacy, and security. Regular review and updates of this checklist are essential to maintain security throughout the migration process and beyond.
### Security Checklist Summary
- [ ] **Pre-Migration Assessment**: Complete
- [ ] **Migration Controls**: Complete
- [ ] **Process Security**: Complete
- [ ] **Post-Migration Verification**: Complete
- [ ] **Monitoring and Logging**: Complete
- [ ] **Incident Response**: Complete
- [ ] **Compliance Verification**: Complete
- [ ] **Documentation and Training**: Complete
- [ ] **Risk Assessment**: Complete
**Overall Security Status**: [ ] Secure [ ] Needs Attention [ ] Critical Issues
**Next Review Date**: _______________
**Reviewed By**: _______________
**Approved By**: _______________

View File

@@ -0,0 +1,226 @@
# Migration Guide: Dexie to absurd-sql
## Overview
This document outlines the migration process from Dexie.js to absurd-sql for the TimeSafari app's storage implementation. The migration aims to provide a consistent SQLite-based storage solution across all platforms while maintaining data integrity and ensuring a smooth transition for users.
**Current Status**: The migration is in **Phase 2** with a well-defined migration fence in place. Core settings and account data have been migrated, with contact migration in progress. **ActiveDid migration has been implemented** to ensure user identity continuity.
## Migration Goals
1. **Data Integrity**
- Preserve all existing data
- Maintain data relationships
- Ensure data consistency
- **Preserve user's active identity**
2. **Performance**
- Improve query performance
- Reduce storage overhead
- Optimize for platform-specific capabilities
3. **User Experience**
- Seamless transition with no data loss
- Maintain user's active identity and preferences
- Preserve application state
## Migration Architecture
### Migration Fence
The migration fence is defined by the `USE_DEXIE_DB` constant in `src/constants/app.ts`:
- `USE_DEXIE_DB = false` (default): Uses SQLite database
- `USE_DEXIE_DB = true`: Uses Dexie database (for migration purposes)
### Migration Order
The migration follows a specific order to maintain data integrity:
1. **Accounts** (foundational - contains DIDs)
2. **Settings** (references accountDid, activeDid)
3. **ActiveDid** (depends on accounts and settings) ⭐ **NEW**
4. **Contacts** (independent, but migrated after accounts for consistency)
## ActiveDid Migration ⭐ **NEW FEATURE**
### Problem Solved
Previously, the `activeDid` setting was not migrated from Dexie to SQLite, causing users to lose their active identity after migration.
### Solution Implemented
The migration now includes a dedicated step for migrating the `activeDid`:
1. **Detection**: Identifies the `activeDid` from Dexie master settings
2. **Validation**: Verifies the `activeDid` exists in SQLite accounts
3. **Migration**: Updates SQLite master settings with the `activeDid`
4. **Error Handling**: Graceful handling of missing accounts
### Implementation Details
#### New Function: `migrateActiveDid()`
```typescript
export async function migrateActiveDid(): Promise<MigrationResult> {
// 1. Get Dexie settings to find the activeDid
const dexieSettings = await getDexieSettings();
const masterSettings = dexieSettings.find(setting => !setting.accountDid);
// 2. Verify the activeDid exists in SQLite accounts
const accountExists = await platformService.dbQuery(
"SELECT did FROM accounts WHERE did = ?",
[dexieActiveDid],
);
// 3. Update SQLite master settings
await updateDefaultSettings({ activeDid: dexieActiveDid });
}
```
#### Enhanced `migrateSettings()` Function
The settings migration now includes activeDid handling:
- Extracts `activeDid` from Dexie master settings
- Validates account existence in SQLite
- Updates SQLite master settings with the `activeDid`
#### Updated `migrateAll()` Function
The complete migration now includes a dedicated step for activeDid:
```typescript
// Step 3: Migrate ActiveDid (depends on accounts and settings)
logger.info("[MigrationService] Step 3: Migrating activeDid...");
const activeDidResult = await migrateActiveDid();
```
### Benefits
-**User Identity Preservation**: Users maintain their active identity
-**Seamless Experience**: No need to manually select identity after migration
-**Data Consistency**: Ensures all identity-related settings are preserved
-**Error Resilience**: Graceful handling of edge cases
## Migration Process
### Phase 1: Preparation ✅
- [x] Enable Dexie database access
- [x] Implement data comparison tools
- [x] Create migration service structure
### Phase 2: Core Migration ✅
- [x] Account migration with `importFromMnemonic`
- [x] Settings migration (excluding activeDid)
- [x] **ActiveDid migration****COMPLETED**
- [x] Contact migration framework
### Phase 3: Validation and Cleanup 🔄
- [ ] Comprehensive data validation
- [ ] Performance testing
- [ ] User acceptance testing
- [ ] Dexie removal
## Usage
### Manual Migration
```typescript
import { migrateAll, migrateActiveDid } from '../services/indexedDBMigrationService';
// Complete migration
const result = await migrateAll();
// Or migrate just the activeDid
const activeDidResult = await migrateActiveDid();
```
### Migration Verification
```typescript
import { compareDatabases } from '../services/indexedDBMigrationService';
const comparison = await compareDatabases();
console.log('Migration differences:', comparison.differences);
```
## Error Handling
### ActiveDid Migration Errors
- **Missing Account**: If the `activeDid` from Dexie doesn't exist in SQLite accounts
- **Database Errors**: Connection or query failures
- **Settings Update Failures**: Issues updating SQLite master settings
### Recovery Strategies
1. **Automatic Recovery**: Migration continues even if activeDid migration fails
2. **Manual Recovery**: Users can manually select their identity after migration
3. **Fallback**: System creates new identity if none exists
## Security Considerations
### Data Protection
- All sensitive data (mnemonics, private keys) are encrypted
- Migration preserves encryption standards
- No plaintext data exposure during migration
### Identity Verification
- ActiveDid migration validates account existence
- Prevents setting non-existent identities as active
- Maintains cryptographic integrity
## Testing
### Migration Testing
```bash
# Enable Dexie for testing
# Set USE_DEXIE_DB = true in constants/app.ts
# Run migration
npm run migrate
# Verify results
npm run test:migration
```
### ActiveDid Testing
```typescript
// Test activeDid migration specifically
const result = await migrateActiveDid();
expect(result.success).toBe(true);
expect(result.warnings).toContain('Successfully migrated activeDid');
```
## Troubleshooting
### Common Issues
1. **ActiveDid Not Found**
- Ensure accounts were migrated before activeDid migration
- Check that the Dexie activeDid exists in SQLite accounts
2. **Migration Failures**
- Verify Dexie database is accessible
- Check SQLite database permissions
- Review migration logs for specific errors
3. **Data Inconsistencies**
- Use `compareDatabases()` to identify differences
- Re-run migration if necessary
- Check for duplicate or conflicting records
### Debugging
```typescript
// Enable detailed logging
logger.setLevel('debug');
// Check migration status
const comparison = await compareDatabases();
console.log('Settings differences:', comparison.differences.settings);
```
## Future Enhancements
### Planned Improvements
1. **Batch Processing**: Optimize for large datasets
2. **Incremental Migration**: Support partial migrations
3. **Rollback Capability**: Ability to revert migration
4. **Progress Tracking**: Real-time migration progress
### Performance Optimizations
1. **Parallel Processing**: Migrate independent data concurrently
2. **Memory Management**: Optimize for large datasets
3. **Transaction Batching**: Reduce database round trips
## Conclusion
The Dexie to SQLite migration provides a robust, secure, and user-friendly transition path. The addition of activeDid migration ensures that users maintain their identity continuity throughout the migration process, significantly improving the user experience.
The migration fence architecture allows for controlled, reversible migration while maintaining application stability and data integrity.

View File

@@ -1,6 +1,6 @@
JWT Creation & Verification
To run this in a script, see ./openssl_signing_console.sh
To run this in a script, see /scripts/openssl_signing_console.sh
Prerequisites: openssl, jq

View File

@@ -0,0 +1,805 @@
# QR Code Implementation Guide
## Overview
This document describes the QR code scanning and generation implementation in the TimeSafari application. The system uses a platform-agnostic design with specific implementations for web and mobile platforms.
## Architecture
### Directory Structure
```
src/
├── services/
│ └── QRScanner/
│ ├── types.ts # Core interfaces and types
│ ├── QRScannerFactory.ts # Factory for creating scanner instances
│ ├── CapacitorQRScanner.ts # Mobile implementation using MLKit
│ ├── WebInlineQRScanner.ts # Web implementation using MediaDevices API
│ └── interfaces.ts # Additional interfaces
├── components/
│ └── QRScanner/
│ └── QRScannerDialog.vue # Shared UI component
└── views/
├── ContactQRScanView.vue # Dedicated scanning view
└── ContactQRScanShowView.vue # Combined QR display and scanning view
```
### Core Components
1. **Factory Pattern**
- `QRScannerFactory` - Creates appropriate scanner instance based on platform
- Common interface `QRScannerService` implemented by all scanners
- Platform detection via Capacitor and build flags
2. **Platform-Specific Implementations**
- `CapacitorQRScanner` - Native mobile implementation using MLKit
- `WebInlineQRScanner` - Web browser implementation using MediaDevices API
- `QRScannerDialog.vue` - Shared UI component
3. **View Components**
- `ContactQRScanView` - Dedicated view for scanning QR codes
- `ContactQRScanShowView` - Combined view for displaying and scanning QR codes
## Implementation Details
### Core Interfaces
```typescript
interface QRScannerService {
checkPermissions(): Promise<boolean>;
requestPermissions(): Promise<boolean>;
isSupported(): Promise<boolean>;
startScan(options?: QRScannerOptions): Promise<void>;
stopScan(): Promise<void>;
addListener(listener: ScanListener): void;
onStream(callback: (stream: MediaStream | null) => void): void;
cleanup(): Promise<void>;
getAvailableCameras(): Promise<MediaDeviceInfo[]>;
switchCamera(deviceId: string): Promise<void>;
getCurrentCamera(): Promise<MediaDeviceInfo | null>;
}
interface ScanListener {
onScan: (result: string) => void;
onError?: (error: Error) => void;
}
interface QRScannerOptions {
camera?: "front" | "back";
showPreview?: boolean;
playSound?: boolean;
}
```
### Platform-Specific Implementations
#### Mobile (Capacitor)
- Uses `@capacitor-mlkit/barcode-scanning`
- Native camera access through platform APIs
- Optimized for mobile performance
- Supports both iOS and Android
- Real-time QR code detection
- Back camera preferred for scanning
Configuration:
```typescript
// capacitor.config.ts
const config: CapacitorConfig = {
plugins: {
MLKitBarcodeScanner: {
formats: ['QR_CODE'],
detectorSize: 1.0,
lensFacing: 'back',
googleBarcodeScannerModuleInstallState: true,
// Additional camera options
cameraOptions: {
quality: 0.8,
allowEditing: false,
resultType: 'uri',
sourceType: 'CAMERA',
saveToGallery: false
}
}
}
};
```
#### Web
- Uses browser's MediaDevices API
- Vue.js components for UI
- EventEmitter for stream management
- Browser-based camera access
- Inline camera preview
- Responsive design
- Cross-browser compatibility
### View Components
#### ContactQRScanView
- Dedicated view for scanning QR codes
- Full-screen camera interface
- Simple UI focused on scanning
- Used primarily on native platforms
- Streamlined scanning experience
#### ContactQRScanShowView
- Combined view for QR code display and scanning
- Shows user's own QR code
- Handles user registration status
- Provides options to copy contact information
- Platform-specific scanning implementation:
- Native: Button to navigate to ContactQRScanView
- Web: Built-in scanning functionality
### QR Code Workflow
1. **Initiation**
- User selects "Scan QR Code" option
- Platform-specific scanner is initialized
- Camera permissions are verified
- Appropriate scanner component is loaded
2. **Platform-Specific Implementation**
- Web: Uses `qrcode-stream` for real-time scanning
- Native: Uses `@capacitor-mlkit/barcode-scanning`
3. **Scanning Process**
- Camera stream initialization
- Real-time frame analysis
- QR code detection and decoding
- Validation of QR code format
- Processing of contact information
4. **Contact Processing**
- Decryption of contact data
- Validation of user information
- Verification of timestamp
- Check for duplicate contacts
- Processing of shared data
## Build Configuration
### Common Vite Configuration
```typescript
// vite.config.common.mts
export async function createBuildConfig(mode: string) {
const isCapacitor = mode === "capacitor";
return defineConfig({
define: {
'process.env.VITE_PLATFORM': JSON.stringify(mode),
'process.env.VITE_PWA_ENABLED': JSON.stringify(!isNative),
__IS_MOBILE__: JSON.stringify(isCapacitor),
__USE_QR_READER__: JSON.stringify(!isCapacitor)
},
optimizeDeps: {
include: [
'@capacitor-mlkit/barcode-scanning',
'vue-qrcode-reader'
]
}
});
}
```
### Platform-Specific Builds
```json
{
"scripts": {
"build:web": "vite build --config vite.config.web.mts",
"build:capacitor": "vite build --config vite.config.capacitor.mts",
"build:all": "npm run build:web && npm run build:capacitor"
}
}
```
## Error Handling
### Common Error Scenarios
1. No camera found
2. Permission denied
3. Camera in use by another application
4. HTTPS required
5. Browser compatibility issues
6. Invalid QR code format
7. Expired QR codes
8. Duplicate contact attempts
9. Network connectivity issues
### Error Response
- User-friendly error messages
- Troubleshooting tips
- Clear instructions for resolution
- Platform-specific guidance
## Security Considerations
### QR Code Security
- Encryption of contact data
- Timestamp validation
- Version checking
- User verification
- Rate limiting for scans
### Data Protection
- Secure transmission of contact data
- Validation of QR code authenticity
- Prevention of duplicate scans
- Protection against malicious codes
- Secure storage of contact information
## Best Practices
### Camera Access
1. Always check for camera availability
2. Request permissions explicitly
3. Handle all error conditions
4. Provide clear user feedback
5. Implement proper cleanup
### Performance
1. Optimize camera resolution
2. Implement proper resource cleanup
3. Handle camera switching efficiently
4. Manage memory usage
5. Battery usage optimization
### User Experience
1. Clear visual feedback
2. Camera preview
3. Scanning status indicators
4. Error messages
5. Success confirmations
6. Intuitive camera controls
7. Smooth camera switching
8. Responsive UI feedback
## Testing
### Test Scenarios
1. Permission handling
2. Camera switching
3. Error conditions
4. Platform compatibility
5. Performance metrics
6. QR code detection
7. Contact processing
8. Security validation
### Test Environment
- Multiple browsers
- iOS and Android devices
- Various network conditions
- Different camera configurations
## Dependencies
### Key Packages
- `@capacitor-mlkit/barcode-scanning`
- `qrcode-stream`
- `vue-qrcode-reader`
- Platform-specific camera APIs
## Maintenance
### Regular Updates
- Keep dependencies updated
- Monitor platform changes
- Update documentation
- Review security patches
### Performance Monitoring
- Track memory usage
- Monitor camera performance
- Check error rates
- Analyze user feedback
## Camera Handling
### Camera Switching Implementation
The QR scanner supports camera switching on both mobile and desktop platforms through a unified interface.
#### Platform-Specific Implementations
1. **Mobile (Capacitor)**
- Uses `@capacitor-mlkit/barcode-scanning`
- Supports front/back camera switching
- Native camera access through platform APIs
- Optimized for mobile performance
```typescript
// CapacitorQRScanner.ts
async startScan(options?: QRScannerOptions): Promise<void> {
const scanOptions: StartScanOptions = {
formats: [BarcodeFormat.QrCode],
lensFacing: options?.camera === "front" ?
LensFacing.Front : LensFacing.Back
};
await BarcodeScanner.startScan(scanOptions);
}
```
2. **Web (Desktop)**
- Uses browser's MediaDevices API
- Supports multiple camera devices
- Dynamic camera enumeration
- Real-time camera switching
```typescript
// WebInlineQRScanner.ts
async getAvailableCameras(): Promise<MediaDeviceInfo[]> {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter(device => device.kind === 'videoinput');
}
async switchCamera(deviceId: string): Promise<void> {
// Stop current stream
await this.stopScan();
// Start new stream with selected camera
this.stream = await navigator.mediaDevices.getUserMedia({
video: {
deviceId: { exact: deviceId },
width: { ideal: 1280 },
height: { ideal: 720 }
}
});
// Update video and restart scanning
if (this.video) {
this.video.srcObject = this.stream;
await this.video.play();
}
this.scanQRCode();
}
```
### Core Interfaces
```typescript
interface QRScannerService {
// ... existing methods ...
/** Get available cameras */
getAvailableCameras(): Promise<MediaDeviceInfo[]>;
/** Switch to a specific camera */
switchCamera(deviceId: string): Promise<void>;
/** Get current camera info */
getCurrentCamera(): Promise<MediaDeviceInfo | null>;
}
interface QRScannerOptions {
/** Camera to use ('front' or 'back' for mobile) */
camera?: "front" | "back";
/** Whether to show a preview of the camera feed */
showPreview?: boolean;
/** Whether to play a sound on successful scan */
playSound?: boolean;
}
```
### UI Components
The camera switching UI adapts to the platform:
1. **Mobile Interface**
- Simple toggle button for front/back cameras
- Positioned in bottom-right corner
- Clear visual feedback during switching
- Native camera controls
```vue
<button
v-if="isNativePlatform"
@click="toggleMobileCamera"
class="camera-switch-btn"
>
<font-awesome icon="camera-rotate" />
Switch Camera
</button>
```
2. **Desktop Interface**
- Dropdown menu with all available cameras
- Camera labels and device IDs
- Real-time camera switching
- Responsive design
```vue
<select
v-model="selectedCameraId"
@change="onCameraChange"
class="camera-select-dropdown"
>
<option
v-for="camera in availableCameras"
:key="camera.deviceId"
:value="camera.deviceId"
>
{{ camera.label || `Camera ${camera.deviceId.slice(0, 4)}` }}
</option>
</select>
```
### Error Handling
The camera switching implementation includes comprehensive error handling:
1. **Common Error Scenarios**
- Camera in use by another application
- Permission denied during switch
- Device not available
- Stream initialization failure
- Camera switch timeout
2. **Error Response**
```typescript
private async handleCameraSwitch(deviceId: string): Promise<void> {
try {
this.updateCameraState("initializing", "Switching camera...");
await this.switchCamera(deviceId);
this.updateCameraState("active", "Camera switched successfully");
} catch (error) {
this.updateCameraState("error", "Failed to switch camera");
throw error;
}
}
```
3. **User Feedback**
- Visual indicators during switching
- Error notifications
- Camera state updates
- Permission request dialogs
### State Management
The camera system maintains several states:
1. **Camera States**
```typescript
type CameraState =
| "initializing" // Camera is being initialized
| "ready" // Camera is ready to use
| "active" // Camera is actively streaming
| "in_use" // Camera is in use by another application
| "permission_denied" // Camera permission was denied
| "not_found" // No camera found on device
| "error" // Generic error state
| "off"; // Camera is off
```
2. **State Transitions**
- Initialization → Ready
- Ready → Active
- Active → Switching
- Switching → Active/Error
- Any state → Off (on cleanup)
### Best Practices
1. **Camera Access**
- Always check permissions before switching
- Handle camera busy states
- Implement proper cleanup
- Monitor camera state changes
2. **Performance**
- Optimize camera resolution
- Handle stream switching efficiently
- Manage memory usage
- Implement proper cleanup
3. **User Experience**
- Clear visual feedback
- Smooth camera transitions
- Intuitive camera controls
- Responsive UI updates
- Accessible camera selection
4. **Security**
- Secure camera access
- Permission management
- Device validation
- Stream security
### Testing
1. **Test Scenarios**
- Camera switching on both platforms
- Permission handling
- Error conditions
- Multiple camera devices
- Camera busy states
- Stream initialization
- UI responsiveness
2. **Test Environment**
- Multiple mobile devices
- Various desktop browsers
- Different camera configurations
- Network conditions
- Permission states
### Capacitor Implementation Details
#### MLKit Barcode Scanner Configuration
1. **Plugin Setup**
```typescript
// capacitor.config.ts
const config: CapacitorConfig = {
plugins: {
MLKitBarcodeScanner: {
formats: ['QR_CODE'],
detectorSize: 1.0,
lensFacing: 'back',
googleBarcodeScannerModuleInstallState: true,
// Additional camera options
cameraOptions: {
quality: 0.8,
allowEditing: false,
resultType: 'uri',
sourceType: 'CAMERA',
saveToGallery: false
}
}
}
};
```
2. **Camera Management**
```typescript
// CapacitorQRScanner.ts
export class CapacitorQRScanner implements QRScannerService {
private currentLensFacing: LensFacing = LensFacing.Back;
async getAvailableCameras(): Promise<MediaDeviceInfo[]> {
// On mobile, we have two fixed cameras
return [
{
deviceId: 'back',
label: 'Back Camera',
kind: 'videoinput'
},
{
deviceId: 'front',
label: 'Front Camera',
kind: 'videoinput'
}
] as MediaDeviceInfo[];
}
async switchCamera(deviceId: string): Promise<void> {
if (!this.isScanning) return;
const newLensFacing = deviceId === 'front' ?
LensFacing.Front : LensFacing.Back;
// Stop current scan
await this.stopScan();
// Update lens facing
this.currentLensFacing = newLensFacing;
// Restart scan with new camera
await this.startScan({
camera: deviceId as 'front' | 'back'
});
}
async getCurrentCamera(): Promise<MediaDeviceInfo | null> {
return {
deviceId: this.currentLensFacing === LensFacing.Front ? 'front' : 'back',
label: this.currentLensFacing === LensFacing.Front ?
'Front Camera' : 'Back Camera',
kind: 'videoinput'
} as MediaDeviceInfo;
}
}
```
3. **Camera State Management**
```typescript
// CapacitorQRScanner.ts
private async handleCameraState(): Promise<void> {
try {
// Check if camera is available
const { camera } = await BarcodeScanner.checkPermissions();
if (camera === 'denied') {
this.updateCameraState('permission_denied');
return;
}
// Check if camera is in use
const isInUse = await this.isCameraInUse();
if (isInUse) {
this.updateCameraState('in_use');
return;
}
this.updateCameraState('ready');
} catch (error) {
this.updateCameraState('error', error.message);
}
}
private async isCameraInUse(): Promise<boolean> {
try {
// Try to start a test scan
await BarcodeScanner.startScan({
formats: [BarcodeFormat.QrCode],
lensFacing: this.currentLensFacing
});
// If successful, stop it immediately
await BarcodeScanner.stopScan();
return false;
} catch (error) {
return error.message.includes('camera in use');
}
}
```
4. **Error Handling**
```typescript
// CapacitorQRScanner.ts
private async handleCameraError(error: Error): Promise<void> {
switch (error.name) {
case 'CameraPermissionDenied':
this.updateCameraState('permission_denied');
break;
case 'CameraInUse':
this.updateCameraState('in_use');
break;
case 'CameraUnavailable':
this.updateCameraState('not_found');
break;
default:
this.updateCameraState('error', error.message);
}
}
```
#### Platform-Specific Considerations
1. **iOS Implementation**
- Camera permissions in Info.plist
- Privacy descriptions
- Camera usage description
- Background camera access
```xml
<!-- ios/App/App/Info.plist -->
<key>NSCameraUsageDescription</key>
<string>We need access to your camera to scan QR codes</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need access to save scanned QR codes</string>
```
2. **Android Implementation**
- Camera permissions in AndroidManifest.xml
- Runtime permission handling
- Camera features declaration
- Hardware feature requirements
```xml
<!-- android/app/src/main/AndroidManifest.xml -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
```
3. **Platform-Specific Features**
- iOS: Camera orientation handling
- Android: Camera resolution optimization
- Both: Battery usage optimization
- Both: Memory management
```typescript
// Platform-specific optimizations
private getPlatformSpecificOptions(): StartScanOptions {
const baseOptions: StartScanOptions = {
formats: [BarcodeFormat.QrCode],
lensFacing: this.currentLensFacing
};
if (Capacitor.getPlatform() === 'ios') {
return {
...baseOptions,
// iOS-specific options
cameraOptions: {
quality: 0.7, // Lower quality for better performance
allowEditing: false,
resultType: 'uri'
}
};
} else if (Capacitor.getPlatform() === 'android') {
return {
...baseOptions,
// Android-specific options
cameraOptions: {
quality: 0.8,
allowEditing: false,
resultType: 'uri',
saveToGallery: false
}
};
}
return baseOptions;
}
```
#### Performance Optimization
1. **Battery Usage**
```typescript
// CapacitorQRScanner.ts
private optimizeBatteryUsage(): void {
// Reduce scan frequency when battery is low
if (this.isLowBattery()) {
this.scanInterval = 2000; // 2 seconds between scans
} else {
this.scanInterval = 1000; // 1 second between scans
}
}
private isLowBattery(): boolean {
// Check battery level if available
if (Capacitor.isPluginAvailable('Battery')) {
const { level } = await Battery.getBatteryLevel();
return level < 0.2; // 20% or lower
}
return false;
}
```
2. **Memory Management**
```typescript
// CapacitorQRScanner.ts
private async cleanupResources(): Promise<void> {
// Stop scanning
await this.stopScan();
// Clear any stored camera data
this.currentLensFacing = LensFacing.Back;
// Remove listeners
this.listenerHandles.forEach(handle => handle());
this.listenerHandles = [];
// Reset state
this.isScanning = false;
this.updateCameraState('off');
}
```
#### Testing on Capacitor
1. **Device Testing**
- Test on multiple iOS devices
- Test on multiple Android devices
- Test different camera configurations
- Test with different screen sizes
- Test with different OS versions
2. **Camera Testing**
- Test front camera switching
- Test back camera switching
- Test camera permissions
- Test camera in use scenarios
- Test low light conditions
- Test different QR code sizes
- Test different QR code distances
3. **Performance Testing**
- Battery usage monitoring
- Memory usage monitoring
- Camera switching speed
- QR code detection speed
- App responsiveness
- Background/foreground transitions

View File

@@ -0,0 +1,339 @@
# Secure Storage Implementation Guide for TimeSafari App
## Overview
This document outlines the implementation of secure storage for the TimeSafari app. The implementation focuses on:
1. **Platform-Specific Storage Solutions**:
- Web: SQLite with IndexedDB backend (absurd-sql)
- Electron: SQLite with Node.js backend
- Native: (Planned) SQLCipher with platform-specific secure storage
2. **Key Features**:
- SQLite-based storage using absurd-sql for web
- Platform-specific service factory pattern
- Consistent API across platforms
- Migration support from Dexie.js
## Quick Start
### 1. Installation
```bash
# Core dependencies
npm install @jlongster/sql.js
npm install absurd-sql
# Platform-specific dependencies (for future native support)
npm install @capacitor/preferences
npm install @capacitor-community/biometric-auth
```
### 2. Basic Usage
```typescript
// Using the platform service
import { PlatformServiceFactory } from '../services/PlatformServiceFactory';
// Get platform-specific service instance
const platformService = PlatformServiceFactory.getInstance();
// Example database operations
async function example() {
try {
// Query example
const result = await platformService.dbQuery(
"SELECT * FROM accounts WHERE did = ?",
[did]
);
// Execute example
await platformService.dbExec(
"INSERT INTO accounts (did, public_key_hex) VALUES (?, ?)",
[did, publicKeyHex]
);
} catch (error) {
console.error('Database operation failed:', error);
}
}
```
### 3. Platform Detection
```typescript
// src/services/PlatformServiceFactory.ts
export class PlatformServiceFactory {
static getInstance(): PlatformService {
if (process.env.ELECTRON) {
// Electron platform
return new ElectronPlatformService();
} else {
// Web platform (default)
return new AbsurdSqlDatabaseService();
}
}
}
```
### 4. Current Implementation Details
#### Web Platform (AbsurdSqlDatabaseService)
The web platform uses absurd-sql with IndexedDB backend:
```typescript
// src/services/AbsurdSqlDatabaseService.ts
export class AbsurdSqlDatabaseService implements PlatformService {
private static instance: AbsurdSqlDatabaseService | null = null;
private db: AbsurdSqlDatabase | null = null;
private initialized: boolean = false;
// Singleton pattern
static getInstance(): AbsurdSqlDatabaseService {
if (!AbsurdSqlDatabaseService.instance) {
AbsurdSqlDatabaseService.instance = new AbsurdSqlDatabaseService();
}
return AbsurdSqlDatabaseService.instance;
}
// Database operations
async dbQuery(sql: string, params: unknown[] = []): Promise<QueryExecResult[]> {
await this.waitForInitialization();
return this.queueOperation<QueryExecResult[]>("query", sql, params);
}
async dbExec(sql: string, params: unknown[] = []): Promise<void> {
await this.waitForInitialization();
await this.queueOperation<void>("run", sql, params);
}
}
```
Key features:
- Uses absurd-sql for SQLite in the browser
- Implements operation queuing for thread safety
- Handles initialization and connection management
- Provides consistent API across platforms
### 5. Migration from Dexie.js
The current implementation supports gradual migration from Dexie.js:
```typescript
// Example of dual-storage pattern
async function getAccount(did: string): Promise<Account | undefined> {
// Try SQLite first
const platform = PlatformServiceFactory.getInstance();
let account = await platform.dbQuery(
"SELECT * FROM accounts WHERE did = ?",
[did]
);
// Fallback to Dexie if needed
if (USE_DEXIE_DB) {
account = await db.accounts.get(did);
}
return account;
}
```
#### A. Modifying Code
When converting from Dexie.js to SQL-based implementation, follow these patterns:
1. **Database Access Pattern**
```typescript
// Before (Dexie)
const result = await db.table.where("field").equals(value).first();
// After (SQL)
const platform = PlatformServiceFactory.getInstance();
let result = await platform.dbQuery(
"SELECT * FROM table WHERE field = ?",
[value]
);
result = databaseUtil.mapQueryResultToValues(result);
// Fallback to Dexie if needed
if (USE_DEXIE_DB) {
result = await db.table.where("field").equals(value).first();
}
```
2. **Update Operations**
```typescript
// Before (Dexie)
await db.table.where("id").equals(id).modify(changes);
// After (SQL)
// For settings updates, use the utility methods:
await databaseUtil.updateDefaultSettings(changes);
// OR
await databaseUtil.updateAccountSettings(did, changes);
// For other tables, use direct SQL:
const platform = PlatformServiceFactory.getInstance();
await platform.dbExec(
"UPDATE table SET field1 = ?, field2 = ? WHERE id = ?",
[changes.field1, changes.field2, id]
);
// Fallback to Dexie if needed
if (USE_DEXIE_DB) {
await db.table.where("id").equals(id).modify(changes);
}
```
3. **Insert Operations**
```typescript
// Before (Dexie)
await db.table.add(item);
// After (SQL)
const platform = PlatformServiceFactory.getInstance();
const columns = Object.keys(item);
const values = Object.values(item);
const placeholders = values.map(() => '?').join(', ');
const sql = `INSERT INTO table (${columns.join(', ')}) VALUES (${placeholders})`;
await platform.dbExec(sql, values);
// Fallback to Dexie if needed
if (USE_DEXIE_DB) {
await db.table.add(item);
}
```
4. **Delete Operations**
```typescript
// Before (Dexie)
await db.table.where("id").equals(id).delete();
// After (SQL)
const platform = PlatformServiceFactory.getInstance();
await platform.dbExec("DELETE FROM table WHERE id = ?", [id]);
// Fallback to Dexie if needed
if (USE_DEXIE_DB) {
await db.table.where("id").equals(id).delete();
}
```
5. **Result Processing**
```typescript
// Before (Dexie)
const items = await db.table.toArray();
// After (SQL)
const platform = PlatformServiceFactory.getInstance();
let items = await platform.dbQuery("SELECT * FROM table");
items = databaseUtil.mapQueryResultToValues(items);
// Fallback to Dexie if needed
if (USE_DEXIE_DB) {
items = await db.table.toArray();
}
```
6. **Using Utility Methods**
When working with settings or other common operations, use the utility methods in `db/index.ts`:
```typescript
// Settings operations
await databaseUtil.updateDefaultSettings(settings);
await databaseUtil.updateAccountSettings(did, settings);
const settings = await databaseUtil.retrieveSettingsForDefaultAccount();
const settings = await databaseUtil.retrieveSettingsForActiveAccount();
// Logging operations
await databaseUtil.logToDb(message);
await databaseUtil.logConsoleAndDb(message, showInConsole);
```
Key Considerations:
- Always use `databaseUtil.mapQueryResultToValues()` to process SQL query results
- Use utility methods from `db/index.ts` when available instead of direct SQL
- Keep Dexie fallbacks wrapped in `if (USE_DEXIE_DB)` checks
- For queries that return results, use `let` variables to allow Dexie fallback to override
- For updates/inserts/deletes, execute both SQL and Dexie operations when `USE_DEXIE_DB` is true
Example Migration:
```typescript
// Before (Dexie)
export async function updateSettings(settings: Settings): Promise<void> {
await db.settings.put(settings);
}
// After (SQL)
export async function updateSettings(settings: Settings): Promise<void> {
const platform = PlatformServiceFactory.getInstance();
const { sql, params } = generateUpdateStatement(
settings,
"settings",
"id = ?",
[settings.id]
);
await platform.dbExec(sql, params);
}
```
Remember to:
- Create database access code to use the platform service, putting it in front of the Dexie version
- Instead of removing Dexie-specific code, keep it.
- For creates & updates & deletes, the duplicate code is fine.
- For queries where we use the results, make the setting from SQL into a 'let' variable, then wrap the Dexie code in a check for USE_DEXIE_DB from app.ts and if
it's true then use that result instead of the SQL code's result.
- Consider data migration needs, and warn if there are any potential migration problems
## Success Criteria
1. **Functionality**
- [x] Basic CRUD operations work correctly
- [x] Platform service factory pattern implemented
- [x] Error handling in place
- [ ] Native platform support (planned)
2. **Performance**
- [x] Database operations complete within acceptable time
- [x] Operation queuing for thread safety
- [x] Proper initialization handling
- [ ] Performance monitoring (planned)
3. **Security**
- [x] Basic data integrity
- [ ] Encryption (planned for native platforms)
- [ ] Secure key storage (planned)
- [ ] Platform-specific security features (planned)
4. **Testing**
- [x] Basic unit tests
- [ ] Comprehensive integration tests (planned)
- [ ] Platform-specific tests (planned)
- [ ] Migration tests (planned)
## Next Steps
1. **Native Platform Support**
- Implement SQLCipher for iOS/Android
- Add platform-specific secure storage
- Implement biometric authentication
2. **Enhanced Security**
- Add encryption for sensitive data
- Implement secure key storage
- Add platform-specific security features
3. **Testing and Monitoring**
- Add comprehensive test coverage
- Implement performance monitoring
- Add error tracking and analytics
4. **Documentation**
- Add API documentation
- Create migration guides
- Document security measures

View File

@@ -0,0 +1,329 @@
# Storage Implementation Checklist
## Core Services
### 1. Storage Service Layer
- [x] Create base `PlatformService` interface
- [x] Define common methods for all platforms
- [x] Add platform-specific method signatures
- [x] Include error handling types
- [x] Add migration support methods
- [x] Implement platform-specific services
- [x] `AbsurdSqlDatabaseService` (web)
- [x] Database initialization
- [x] VFS setup with IndexedDB backend
- [x] Connection management
- [x] Operation queuing
- [ ] `NativeSQLiteService` (iOS/Android) (planned)
- [ ] SQLCipher integration
- [ ] Native bridge setup
- [ ] File system access
- [ ] `ElectronSQLiteService` (planned)
- [ ] Node SQLite integration
- [ ] IPC communication
- [ ] File system access
### 2. Migration Services
- [x] Implement basic migration support
- [x] Dual-storage pattern (SQLite + Dexie)
- [x] Basic data verification
- [ ] Rollback procedures (planned)
- [ ] Progress tracking (planned)
- [ ] Create `MigrationUI` components (planned)
- [ ] Progress indicators
- [ ] Error handling
- [ ] User notifications
- [ ] Manual triggers
### 3. Security Layer
- [x] Basic data integrity
- [ ] Implement `EncryptionService` (planned)
- [ ] Key management
- [ ] Encryption/decryption
- [ ] Secure storage
- [ ] Add `BiometricService` (planned)
- [ ] Platform detection
- [ ] Authentication flow
- [ ] Fallback mechanisms
## Platform-Specific Implementation
### Web Platform
- [x] Setup absurd-sql
- [x] Install dependencies
```json
{
"@jlongster/sql.js": "^1.8.0",
"absurd-sql": "^1.8.0"
}
```
- [x] Configure VFS with IndexedDB backend
- [x] Setup worker threads
- [x] Implement operation queuing
- [x] Configure database pragmas
```sql
PRAGMA journal_mode=MEMORY;
PRAGMA synchronous=NORMAL;
PRAGMA foreign_keys=ON;
PRAGMA busy_timeout=5000;
```
- [x] Update build configuration
- [x] Modify `vite.config.ts`
- [x] Add worker configuration
- [x] Update chunk splitting
- [x] Configure asset handling
- [x] Implement IndexedDB backend
- [x] Create database service
- [x] Add operation queuing
- [x] Handle initialization
- [x] Implement atomic operations
### iOS Platform (Planned)
- [ ] Setup SQLCipher
- [ ] Install pod dependencies
- [ ] Configure encryption
- [ ] Setup keychain access
- [ ] Implement secure storage
- [ ] Update Capacitor config
- [ ] Modify `capacitor.config.ts`
- [ ] Add iOS permissions
- [ ] Configure backup
- [ ] Setup app groups
### Android Platform (Planned)
- [ ] Setup SQLCipher
- [ ] Add Gradle dependencies
- [ ] Configure encryption
- [ ] Setup keystore
- [ ] Implement secure storage
- [ ] Update Capacitor config
- [ ] Modify `capacitor.config.ts`
- [ ] Add Android permissions
- [ ] Configure backup
- [ ] Setup file provider
### Electron Platform (Planned)
- [ ] Setup Node SQLite
- [ ] Install dependencies
- [ ] Configure IPC
- [ ] Setup file system access
- [ ] Implement secure storage
- [ ] Update Electron config
- [ ] Modify `electron.config.ts`
- [ ] Add security policies
- [ ] Configure file access
- [ ] Setup auto-updates
## Data Models and Types
### 1. Database Schema
- [x] Define tables
```sql
-- Accounts table
CREATE TABLE accounts (
did TEXT PRIMARY KEY,
public_key_hex TEXT NOT NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
-- Settings table
CREATE TABLE settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
updated_at INTEGER NOT NULL
);
-- Contacts table
CREATE TABLE contacts (
id TEXT PRIMARY KEY,
did TEXT NOT NULL,
name TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (did) REFERENCES accounts(did)
);
-- Indexes for performance
CREATE INDEX idx_accounts_created_at ON accounts(created_at);
CREATE INDEX idx_contacts_did ON contacts(did);
CREATE INDEX idx_settings_updated_at ON settings(updated_at);
```
- [x] Create indexes
- [x] Define constraints
- [ ] Add triggers (planned)
- [ ] Setup migrations (planned)
### 2. Type Definitions
- [x] Create interfaces
```typescript
interface Account {
did: string;
publicKeyHex: string;
createdAt: number;
updatedAt: number;
}
interface Setting {
key: string;
value: string;
updatedAt: number;
}
interface Contact {
id: string;
did: string;
name?: string;
createdAt: number;
updatedAt: number;
}
```
- [x] Add validation
- [x] Create DTOs
- [x] Define enums
- [x] Add type guards
## UI Components
### 1. Migration UI (Planned)
- [ ] Create components
- [ ] `MigrationProgress.vue`
- [ ] `MigrationError.vue`
- [ ] `MigrationSettings.vue`
- [ ] `MigrationStatus.vue`
### 2. Settings UI (Planned)
- [ ] Update components
- [ ] Add storage settings
- [ ] Add migration controls
- [ ] Add backup options
- [ ] Add security settings
### 3. Error Handling UI (Planned)
- [ ] Create components
- [ ] `StorageError.vue`
- [ ] `QuotaExceeded.vue`
- [ ] `MigrationFailed.vue`
- [ ] `RecoveryOptions.vue`
## Testing
### 1. Unit Tests
- [x] Basic service tests
- [x] Platform service tests
- [x] Database operation tests
- [ ] Security service tests (planned)
- [ ] Platform detection tests (planned)
### 2. Integration Tests (Planned)
- [ ] Test migrations
- [ ] Web platform tests
- [ ] iOS platform tests
- [ ] Android platform tests
- [ ] Electron platform tests
### 3. E2E Tests (Planned)
- [ ] Test workflows
- [ ] Account management
- [ ] Settings management
- [ ] Contact management
- [ ] Migration process
## Documentation
### 1. Technical Documentation
- [x] Update architecture docs
- [x] Add API documentation
- [ ] Create migration guides (planned)
- [ ] Document security measures (planned)
### 2. User Documentation (Planned)
- [ ] Update user guides
- [ ] Add troubleshooting guides
- [ ] Create FAQ
- [ ] Document new features
## Deployment
### 1. Build Process
- [x] Update build scripts
- [x] Add platform-specific builds
- [ ] Configure CI/CD (planned)
- [ ] Setup automated testing (planned)
### 2. Release Process (Planned)
- [ ] Create release checklist
- [ ] Add version management
- [ ] Setup rollback procedures
- [ ] Configure monitoring
## Monitoring and Analytics (Planned)
### 1. Error Tracking
- [ ] Setup error logging
- [ ] Add performance monitoring
- [ ] Configure alerts
- [ ] Create dashboards
### 2. Usage Analytics
- [ ] Add storage metrics
- [ ] Track migration success
- [ ] Monitor performance
- [ ] Collect user feedback
## Security Audit (Planned)
### 1. Code Review
- [ ] Review encryption
- [ ] Check access controls
- [ ] Verify data handling
- [ ] Audit dependencies
### 2. Penetration Testing
- [ ] Test data access
- [ ] Verify encryption
- [ ] Check authentication
- [ ] Review permissions
## Success Criteria
### 1. Performance
- [x] Query response time < 100ms
- [x] Operation queuing for thread safety
- [x] Proper initialization handling
- [ ] Migration time < 5s per 1000 records (planned)
- [ ] Storage overhead < 10% (planned)
- [ ] Memory usage < 50MB (planned)
### 2. Reliability
- [x] Basic data integrity
- [x] Operation queuing
- [ ] Automatic recovery (planned)
- [ ] Backup verification (planned)
- [ ] Transaction atomicity (planned)
- [ ] Data consistency (planned)
### 3. Security
- [x] Basic data integrity
- [ ] AES-256 encryption (planned)
- [ ] Secure key storage (planned)
- [ ] Access control (planned)
- [ ] Audit logging (planned)
### 4. User Experience
- [x] Basic database operations
- [ ] Smooth migration (planned)
- [ ] Clear error messages (planned)
- [ ] Progress indicators (planned)
- [ ] Recovery options (planned)

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<meta name="viewport" content="width=device-width,initial-scale=1.0,viewport-fit=cover">
<link rel="icon" href="/favicon.ico">
<title>TimeSafari</title>
</head>

26
ios/.gitignore vendored Normal file
View File

@@ -0,0 +1,26 @@
App/build
App/Pods
App/output
App/App/public
DerivedData
xcuserdata
# Cordova plugins for Capacitor
capacitor-cordova-ios-plugins
# Generated Config files
App/App/capacitor.config.json
App/App/config.xml
# User-specific Xcode files
App/App.xcodeproj/xcuserdata/*.xcuserdatad/
App/App.xcodeproj/*.xcuserstate
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
# Generated Icons from capacitor-assets (also Contents.json which is confusing; see BUILDING.md)
App/App/Assets.xcassets/AppIcon.appiconset
App/App/Assets.xcassets/Splash.imageset

View File

@@ -0,0 +1,477 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; };
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; };
504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; };
504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; };
504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; };
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };
50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
97EF2DC6FD76C3643D680B8D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 90DCAFB4D8948F7A50C13800 /* Pods_App.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = "<group>"; };
50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = "<group>"; };
504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; };
504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
90DCAFB4D8948F7A50C13800 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E2E9297D5D02C549106C77F9 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
EAEC6436E595F7CD3A1C9E96 /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
504EC3011FED79650016851F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
97EF2DC6FD76C3643D680B8D /* Pods_App.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
4B546315E668C7A13939F417 /* Frameworks */ = {
isa = PBXGroup;
children = (
90DCAFB4D8948F7A50C13800 /* Pods_App.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
504EC2FB1FED79650016851F = {
isa = PBXGroup;
children = (
504EC3061FED79650016851F /* App */,
504EC3051FED79650016851F /* Products */,
BA325FFCDCE8D334E5C7AEBE /* Pods */,
4B546315E668C7A13939F417 /* Frameworks */,
);
sourceTree = "<group>";
};
504EC3051FED79650016851F /* Products */ = {
isa = PBXGroup;
children = (
504EC3041FED79650016851F /* App.app */,
);
name = Products;
sourceTree = "<group>";
};
504EC3061FED79650016851F /* App */ = {
isa = PBXGroup;
children = (
50379B222058CBB4000EE86E /* capacitor.config.json */,
504EC3071FED79650016851F /* AppDelegate.swift */,
504EC30B1FED79650016851F /* Main.storyboard */,
504EC30E1FED79650016851F /* Assets.xcassets */,
504EC3101FED79650016851F /* LaunchScreen.storyboard */,
504EC3131FED79650016851F /* Info.plist */,
2FAD9762203C412B000D30F8 /* config.xml */,
50B271D01FEDC1A000F3C39B /* public */,
);
path = App;
sourceTree = "<group>";
};
BA325FFCDCE8D334E5C7AEBE /* Pods */ = {
isa = PBXGroup;
children = (
EAEC6436E595F7CD3A1C9E96 /* Pods-App.debug.xcconfig */,
E2E9297D5D02C549106C77F9 /* Pods-App.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
504EC3031FED79650016851F /* App */ = {
isa = PBXNativeTarget;
buildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */;
buildPhases = (
92977BEA1068CC097A57FC77 /* [CP] Check Pods Manifest.lock */,
504EC3001FED79650016851F /* Sources */,
504EC3011FED79650016851F /* Frameworks */,
504EC3021FED79650016851F /* Resources */,
012076E8FFE4BF260A79B034 /* Fix Privacy Manifest */,
3525031ED1C96EF4CF6E9959 /* [CP] Embed Pods Frameworks */,
96A7EF592DF3366D00084D51 /* Fix Privacy Manifest */,
);
buildRules = (
);
dependencies = (
);
name = App;
productName = App;
productReference = 504EC3041FED79650016851F /* App.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
504EC2FC1FED79650016851F /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 920;
LastUpgradeCheck = 1630;
TargetAttributes = {
504EC3031FED79650016851F = {
CreatedOnToolsVersion = 9.2;
LastSwiftMigration = 1100;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */;
compatibilityVersion = "Xcode 8.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 504EC2FB1FED79650016851F;
productRefGroup = 504EC3051FED79650016851F /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
504EC3031FED79650016851F /* App */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
504EC3021FED79650016851F /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */,
50B271D11FEDC1A000F3C39B /* public in Resources */,
504EC30F1FED79650016851F /* Assets.xcassets in Resources */,
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */,
504EC30D1FED79650016851F /* Main.storyboard in Resources */,
2FAD9763203C412B000D30F8 /* config.xml in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
012076E8FFE4BF260A79B034 /* Fix Privacy Manifest */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Fix Privacy Manifest";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PROJECT_DIR}/app_privacy_manifest_fixer/fixer.sh\" \n";
showEnvVarsInLog = 0;
};
3525031ED1C96EF4CF6E9959 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-App/Pods-App-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
92977BEA1068CC097A57FC77 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-App-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
96A7EF592DF3366D00084D51 /* Fix Privacy Manifest */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Fix Privacy Manifest";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "$PROJECT_DIR/app_privacy_manifest_fixer/fixer.sh\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
504EC3001FED79650016851F /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
504EC3081FED79650016851F /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
504EC30B1FED79650016851F /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
504EC30C1FED79650016851F /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
504EC3101FED79650016851F /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
504EC3111FED79650016851F /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
504EC3141FED79650016851F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 7XVXYPEQYJ;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
504EC3151FED79650016851F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 7XVXYPEQYJ;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
504EC3171FED79650016851F /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = EAEC6436E595F7CD3A1C9E96 /* Pods-App.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 33;
DEVELOPMENT_TEAM = GM3FS5JQPH;
ENABLE_APP_SANDBOX = NO;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 0.5.7;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
504EC3181FED79650016851F /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = E2E9297D5D02C549106C77F9 /* Pods-App.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 33;
DEVELOPMENT_TEAM = GM3FS5JQPH;
ENABLE_APP_SANDBOX = NO;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 0.5.7;
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */ = {
isa = XCConfigurationList;
buildConfigurations = (
504EC3141FED79650016851F /* Debug */,
504EC3151FED79650016851F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */ = {
isa = XCConfigurationList;
buildConfigurations = (
504EC3171FED79650016851F /* Debug */,
504EC3181FED79650016851F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 504EC2FC1FED79650016851F /* Project object */;
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:App.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,54 @@
import UIKit
import Capacitor
import CapacitorCommunitySqlite
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize SQLite
//let sqlite = SQLite()
//sqlite.initialize()
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
// Called when the app was launched with a url. Feel free to add additional processing here,
// but if you want the App API to support tracking app url opens, make sure to keep this call
return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
}
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
// Called when the app was launched with an activity, including Universal Links.
// Feel free to add additional processing here, but if you want the App API to support
// tracking app url opens, make sure to keep this call
return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)
}
}

View File

@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17132" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17105"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<imageView key="view" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Splash" id="snD-IY-ifK">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</imageView>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="Splash" width="1366" height="1366"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14111" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
</dependencies>
<scenes>
<!--Bridge View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="CAPBridgeViewController" customModule="Capacitor" sceneMemberID="viewController"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

64
ios/App/App/Info.plist Normal file
View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>TimeSafari</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>Time Safari allows you to take photos, and also scan QR codes from contacts.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Time Safari allows you to upload photos.</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>app.timesafari</string>
<key>CFBundleURLSchemes</key>
<array>
<string>timesafari</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.debugger</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.personal-information.addressbook</key>
<true/>
<key>com.apple.security.personal-information.calendars</key>
<true/>
</dict>
</plist>

35
ios/App/Podfile Normal file
View File

@@ -0,0 +1,35 @@
require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers'
platform :ios, '13.0'
use_frameworks!
# workaround to avoid Xcode caching of Pods that requires
# Product -> Clean Build Folder after new Cordova plugins installed
# Requires CocoaPods 1.6 or newer
install! 'cocoapods', :disable_input_output_paths => true
def capacitor_pods
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorCommunitySqlite', :path => '../../node_modules/@capacitor-community/sqlite'
pod 'CapacitorMlkitBarcodeScanning', :path => '../../node_modules/@capacitor-mlkit/barcode-scanning'
pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
pod 'CapacitorCamera', :path => '../../node_modules/@capacitor/camera'
pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem'
pod 'CapacitorShare', :path => '../../node_modules/@capacitor/share'
pod 'CapawesomeCapacitorFilePicker', :path => '../../node_modules/@capawesome/capacitor-file-picker'
end
target 'App' do
capacitor_pods
# Add your Pods here
end
post_install do |installer|
assertDeploymentTarget(installer)
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64'
end
end
end

162
ios/App/Podfile.lock Normal file
View File

@@ -0,0 +1,162 @@
PODS:
- Capacitor (6.2.1):
- CapacitorCordova
- CapacitorApp (6.0.2):
- Capacitor
- CapacitorCamera (6.1.2):
- Capacitor
- CapacitorCommunitySqlite (6.0.2):
- Capacitor
- SQLCipher
- ZIPFoundation
- CapacitorCordova (6.2.1)
- CapacitorFilesystem (6.0.3):
- Capacitor
- CapacitorMlkitBarcodeScanning (6.2.0):
- Capacitor
- GoogleMLKit/BarcodeScanning (= 5.0.0)
- CapacitorShare (6.0.3):
- Capacitor
- CapawesomeCapacitorFilePicker (6.2.0):
- Capacitor
- GoogleDataTransport (9.4.1):
- GoogleUtilities/Environment (~> 7.7)
- nanopb (< 2.30911.0, >= 2.30908.0)
- PromisesObjC (< 3.0, >= 1.2)
- GoogleMLKit/BarcodeScanning (5.0.0):
- GoogleMLKit/MLKitCore
- MLKitBarcodeScanning (~> 4.0.0)
- GoogleMLKit/MLKitCore (5.0.0):
- MLKitCommon (~> 10.0.0)
- GoogleToolboxForMac/DebugUtils (2.3.2):
- GoogleToolboxForMac/Defines (= 2.3.2)
- GoogleToolboxForMac/Defines (2.3.2)
- GoogleToolboxForMac/Logger (2.3.2):
- GoogleToolboxForMac/Defines (= 2.3.2)
- "GoogleToolboxForMac/NSData+zlib (2.3.2)":
- GoogleToolboxForMac/Defines (= 2.3.2)
- "GoogleToolboxForMac/NSDictionary+URLArguments (2.3.2)":
- GoogleToolboxForMac/DebugUtils (= 2.3.2)
- GoogleToolboxForMac/Defines (= 2.3.2)
- "GoogleToolboxForMac/NSString+URLArguments (= 2.3.2)"
- "GoogleToolboxForMac/NSString+URLArguments (2.3.2)"
- GoogleUtilities/Environment (7.13.3):
- GoogleUtilities/Privacy
- PromisesObjC (< 3.0, >= 1.2)
- GoogleUtilities/Logger (7.13.3):
- GoogleUtilities/Environment
- GoogleUtilities/Privacy
- GoogleUtilities/Privacy (7.13.3)
- GoogleUtilities/UserDefaults (7.13.3):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- GoogleUtilitiesComponents (1.1.0):
- GoogleUtilities/Logger
- GTMSessionFetcher/Core (3.5.0)
- MLImage (1.0.0-beta5)
- MLKitBarcodeScanning (4.0.0):
- MLKitCommon (~> 10.0)
- MLKitVision (~> 6.0)
- MLKitCommon (10.0.0):
- GoogleDataTransport (~> 9.0)
- GoogleToolboxForMac/Logger (~> 2.1)
- "GoogleToolboxForMac/NSData+zlib (~> 2.1)"
- "GoogleToolboxForMac/NSDictionary+URLArguments (~> 2.1)"
- GoogleUtilities/UserDefaults (~> 7.0)
- GoogleUtilitiesComponents (~> 1.0)
- GTMSessionFetcher/Core (< 4.0, >= 1.1)
- MLKitVision (6.0.0):
- GoogleToolboxForMac/Logger (~> 2.1)
- "GoogleToolboxForMac/NSData+zlib (~> 2.1)"
- GTMSessionFetcher/Core (< 4.0, >= 1.1)
- MLImage (= 1.0.0-beta5)
- MLKitCommon (~> 10.0)
- nanopb (2.30910.0):
- nanopb/decode (= 2.30910.0)
- nanopb/encode (= 2.30910.0)
- nanopb/decode (2.30910.0)
- nanopb/encode (2.30910.0)
- PromisesObjC (2.4.0)
- SQLCipher (4.9.0):
- SQLCipher/standard (= 4.9.0)
- SQLCipher/common (4.9.0)
- SQLCipher/standard (4.9.0):
- SQLCipher/common
- ZIPFoundation (0.9.19)
DEPENDENCIES:
- "Capacitor (from `../../node_modules/@capacitor/ios`)"
- "CapacitorApp (from `../../node_modules/@capacitor/app`)"
- "CapacitorCamera (from `../../node_modules/@capacitor/camera`)"
- "CapacitorCommunitySqlite (from `../../node_modules/@capacitor-community/sqlite`)"
- "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
- "CapacitorFilesystem (from `../../node_modules/@capacitor/filesystem`)"
- "CapacitorMlkitBarcodeScanning (from `../../node_modules/@capacitor-mlkit/barcode-scanning`)"
- "CapacitorShare (from `../../node_modules/@capacitor/share`)"
- "CapawesomeCapacitorFilePicker (from `../../node_modules/@capawesome/capacitor-file-picker`)"
SPEC REPOS:
trunk:
- GoogleDataTransport
- GoogleMLKit
- GoogleToolboxForMac
- GoogleUtilities
- GoogleUtilitiesComponents
- GTMSessionFetcher
- MLImage
- MLKitBarcodeScanning
- MLKitCommon
- MLKitVision
- nanopb
- PromisesObjC
- SQLCipher
- ZIPFoundation
EXTERNAL SOURCES:
Capacitor:
:path: "../../node_modules/@capacitor/ios"
CapacitorApp:
:path: "../../node_modules/@capacitor/app"
CapacitorCamera:
:path: "../../node_modules/@capacitor/camera"
CapacitorCommunitySqlite:
:path: "../../node_modules/@capacitor-community/sqlite"
CapacitorCordova:
:path: "../../node_modules/@capacitor/ios"
CapacitorFilesystem:
:path: "../../node_modules/@capacitor/filesystem"
CapacitorMlkitBarcodeScanning:
:path: "../../node_modules/@capacitor-mlkit/barcode-scanning"
CapacitorShare:
:path: "../../node_modules/@capacitor/share"
CapawesomeCapacitorFilePicker:
:path: "../../node_modules/@capawesome/capacitor-file-picker"
SPEC CHECKSUMS:
Capacitor: c95400d761e376be9da6be5a05f226c0e865cebf
CapacitorApp: e1e6b7d05e444d593ca16fd6d76f2b7c48b5aea7
CapacitorCamera: 9bc7b005d0e6f1d5f525b8137045b60cffffce79
CapacitorCommunitySqlite: 0299d20f4b00c2e6aa485a1d8932656753937b9b
CapacitorCordova: 8d93e14982f440181be7304aa9559ca631d77fff
CapacitorFilesystem: 59270a63c60836248812671aa3b15df673fbaf74
CapacitorMlkitBarcodeScanning: 7652be9c7922f39203a361de735d340ae37e134e
CapacitorShare: d2a742baec21c8f3b92b361a2fbd2401cdd8288e
CapawesomeCapacitorFilePicker: c40822f0a39f86855321943c7829d52bca7f01bd
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
GoogleMLKit: 90ba06e028795a50261f29500d238d6061538711
GoogleToolboxForMac: 8bef7c7c5cf7291c687cf5354f39f9db6399ad34
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
GoogleUtilitiesComponents: 679b2c881db3b615a2777504623df6122dd20afe
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
MLImage: 1824212150da33ef225fbd3dc49f184cf611046c
MLKitBarcodeScanning: 9cb0ec5ec65bbb5db31de4eba0a3289626beab4e
MLKitCommon: afcd11b6c0735066a0dde8b4bf2331f6197cbca2
MLKitVision: 90922bca854014a856f8b649d1f1f04f63fd9c79
nanopb: 438bc412db1928dac798aa6fd75726007be04262
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
SQLCipher: 31878d8ebd27e5c96db0b7cb695c96e9f8ad77da
ZIPFoundation: b8c29ea7ae353b309bc810586181fd073cb3312c
PODFILE CHECKSUM: f987510f7383b04a1b09ea8472bdadcd88b6c924
COCOAPODS: 1.16.2

View File

@@ -0,0 +1,5 @@
# macOS
.DS_Store
# Build
/Build/

View File

@@ -0,0 +1,58 @@
## 1.4.1
- Fix macOS app re-signing issue.
- Automatically enable Hardened Runtime in macOS codesign.
- Add clean script.
## 1.4.0
- Support for macOS app ([#9](https://github.com/crasowas/app_privacy_manifest_fixer/issues/9)).
## 1.3.11
- Fix install issue by skipping `PBXAggregateTarget` ([#4](https://github.com/crasowas/app_privacy_manifest_fixer/issues/4)).
## 1.3.10
- Fix app re-signing issue.
- Enhance Build Phases script robustness.
## 1.3.9
- Add log file output.
## 1.3.8
- Add version info to privacy access report.
- Remove empty tables from privacy access report.
## 1.3.7
- Enhance API symbols analysis with strings tool.
- Improve performance of API usage analysis.
## 1.3.5
- Fix issue with inaccurate privacy manifest search.
- Disable dependency analysis to force the script to run on every build.
- Add placeholder for privacy access report.
- Update build output directory naming convention.
- Add examples for privacy access report.
## 1.3.0
- Add privacy access report generation.
## 1.2.3
- Fix issue with relative path parameter.
- Add support for all application targets.
## 1.2.1
- Fix backup issue with empty user templates directory.
## 1.2.0
- Add uninstall script.
## 1.1.2
- Remove `Templates/.gitignore` to track `UserTemplates`.
- Fix incorrect use of `App.xcprivacy` template in `App.framework`.
## 1.1.0
- Add logs for latest release fetch failure.
- Fix issue with converting published time to local time.
- Disable showing environment variables in the build log.
- Add `--install-builds-only` command line option.
## 1.0.0
- Initial version.

View File

@@ -0,0 +1,80 @@
#!/bin/bash
# Copyright (c) 2025, crasowas.
#
# Use of this source code is governed by a MIT-style license
# that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT.
set -e
# Prevent duplicate loading
if [ -n "$CONSTANTS_SH_LOADED" ]; then
return
fi
readonly CONSTANTS_SH_LOADED=1
# File name of the privacy manifest
readonly PRIVACY_MANIFEST_FILE_NAME="PrivacyInfo.xcprivacy"
# Common privacy manifest template file names
readonly APP_TEMPLATE_FILE_NAME="AppTemplate.xcprivacy"
readonly FRAMEWORK_TEMPLATE_FILE_NAME="FrameworkTemplate.xcprivacy"
# Universal delimiter
readonly DELIMITER=":"
# Space escape symbol for handling space in path
readonly SPACE_ESCAPE="\u0020"
# Default value when the version cannot be retrieved
readonly UNKNOWN_VERSION="unknown"
# Categories of required reason APIs
readonly API_CATEGORIES=(
"NSPrivacyAccessedAPICategoryFileTimestamp"
"NSPrivacyAccessedAPICategorySystemBootTime"
"NSPrivacyAccessedAPICategoryDiskSpace"
"NSPrivacyAccessedAPICategoryActiveKeyboards"
"NSPrivacyAccessedAPICategoryUserDefaults"
)
# Symbol of the required reason APIs and their categories
#
# See also:
# * https://developer.apple.com/documentation/bundleresources/describing-use-of-required-reason-api
# * https://github.com/Wooder/ios_17_required_reason_api_scanner/blob/main/required_reason_api_binary_scanner.sh
readonly API_SYMBOLS=(
# NSPrivacyAccessedAPICategoryFileTimestamp
"NSPrivacyAccessedAPICategoryFileTimestamp${DELIMITER}getattrlist"
"NSPrivacyAccessedAPICategoryFileTimestamp${DELIMITER}getattrlistbulk"
"NSPrivacyAccessedAPICategoryFileTimestamp${DELIMITER}fgetattrlist"
"NSPrivacyAccessedAPICategoryFileTimestamp${DELIMITER}stat"
"NSPrivacyAccessedAPICategoryFileTimestamp${DELIMITER}fstat"
"NSPrivacyAccessedAPICategoryFileTimestamp${DELIMITER}fstatat"
"NSPrivacyAccessedAPICategoryFileTimestamp${DELIMITER}lstat"
"NSPrivacyAccessedAPICategoryFileTimestamp${DELIMITER}getattrlistat"
"NSPrivacyAccessedAPICategoryFileTimestamp${DELIMITER}NSFileCreationDate"
"NSPrivacyAccessedAPICategoryFileTimestamp${DELIMITER}NSFileModificationDate"
"NSPrivacyAccessedAPICategoryFileTimestamp${DELIMITER}NSURLContentModificationDateKey"
"NSPrivacyAccessedAPICategoryFileTimestamp${DELIMITER}NSURLCreationDateKey"
# NSPrivacyAccessedAPICategorySystemBootTime
"NSPrivacyAccessedAPICategorySystemBootTime${DELIMITER}systemUptime"
"NSPrivacyAccessedAPICategorySystemBootTime${DELIMITER}mach_absolute_time"
# NSPrivacyAccessedAPICategoryDiskSpace
"NSPrivacyAccessedAPICategoryDiskSpace${DELIMITER}statfs"
"NSPrivacyAccessedAPICategoryDiskSpace${DELIMITER}statvfs"
"NSPrivacyAccessedAPICategoryDiskSpace${DELIMITER}fstatfs"
"NSPrivacyAccessedAPICategoryDiskSpace${DELIMITER}fstatvfs"
"NSPrivacyAccessedAPICategoryDiskSpace${DELIMITER}NSFileSystemFreeSize"
"NSPrivacyAccessedAPICategoryDiskSpace${DELIMITER}NSFileSystemSize"
"NSPrivacyAccessedAPICategoryDiskSpace${DELIMITER}NSURLVolumeAvailableCapacityKey"
"NSPrivacyAccessedAPICategoryDiskSpace${DELIMITER}NSURLVolumeAvailableCapacityForImportantUsageKey"
"NSPrivacyAccessedAPICategoryDiskSpace${DELIMITER}NSURLVolumeAvailableCapacityForOpportunisticUsageKey"
"NSPrivacyAccessedAPICategoryDiskSpace${DELIMITER}NSURLVolumeTotalCapacityKey"
# NSPrivacyAccessedAPICategoryActiveKeyboards
"NSPrivacyAccessedAPICategoryActiveKeyboards${DELIMITER}activeInputModes"
# NSPrivacyAccessedAPICategoryUserDefaults
"NSPrivacyAccessedAPICategoryUserDefaults${DELIMITER}NSUserDefaults"
)

View File

@@ -0,0 +1,125 @@
#!/bin/bash
# Copyright (c) 2025, crasowas.
#
# Use of this source code is governed by a MIT-style license
# that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT.
set -e
# Prevent duplicate loading
if [ -n "$UTILS_SH_LOADED" ]; then
return
fi
readonly UTILS_SH_LOADED=1
# Absolute path of the script and the tool's root directory
script_path="$(realpath "${BASH_SOURCE[0]}")"
tool_root_path="$(dirname "$(dirname "$script_path")")"
# Load common constants
source "$tool_root_path/Common/constants.sh"
# Print the elements of an array along with their indices
function print_array() {
local -a array=("$@")
for ((i=0; i<${#array[@]}; i++)); do
echo "[$i] $(decode_path "${array[i]}")"
done
}
# Split a string into substrings using a specified delimiter
function split_string_by_delimiter() {
local string="$1"
local -a substrings=()
IFS="$DELIMITER" read -ra substrings <<< "$string"
echo "${substrings[@]}"
}
# Encode a path string by replacing space with an escape character
function encode_path() {
echo "$1" | sed "s/ /$SPACE_ESCAPE/g"
}
# Decode a path string by replacing encoded character with space
function decode_path() {
echo "$1" | sed "s/$SPACE_ESCAPE/ /g"
}
# Get the dependency name by removing common suffixes
function get_dependency_name() {
local path="$1"
local dir_name="$(basename "$path")"
# Remove `.app`, `.framework`, and `.xcframework` suffixes
local dep_name="${dir_name%.*}"
echo "$dep_name"
}
# Get the executable name from the specified `Info.plist` file
function get_plist_executable() {
local plist_file="$1"
if [ ! -f "$plist_file" ]; then
echo ""
else
/usr/libexec/PlistBuddy -c "Print :CFBundleExecutable" "$plist_file" 2>/dev/null || echo ""
fi
}
# Get the version from the specified `Info.plist` file
function get_plist_version() {
local plist_file="$1"
if [ ! -f "$plist_file" ]; then
echo "$UNKNOWN_VERSION"
else
/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$plist_file" 2>/dev/null || echo "$UNKNOWN_VERSION"
fi
}
# Get the path of the specified framework version
function get_framework_path() {
local path="$1"
local version_path="$2"
if [ -z "$version_path" ]; then
echo "$path"
else
echo "$path/$version_path"
fi
}
# Search for privacy manifest files in the specified directory
function search_privacy_manifest_files() {
local path="$1"
local -a privacy_manifest_files=()
# Create a temporary file to store search results
local temp_file="$(mktemp)"
# Ensure the temporary file is deleted on script exit
trap "rm -f $temp_file" EXIT
# Find privacy manifest files within the specified directory and store the results in the temporary file
find "$path" -type f -name "$PRIVACY_MANIFEST_FILE_NAME" -print0 2>/dev/null > "$temp_file"
while IFS= read -r -d '' file; do
privacy_manifest_files+=($(encode_path "$file"))
done < "$temp_file"
echo "${privacy_manifest_files[@]}"
}
# Get the privacy manifest file with the shortest path
function get_privacy_manifest_file() {
local privacy_manifest_file="$(printf "%s\n" "$@" | awk '{print length, $0}' | sort -n | head -n1 | cut -d ' ' -f2-)"
echo "$(decode_path "$privacy_manifest_file")"
}

View File

@@ -0,0 +1,80 @@
# Copyright (c) 2024, crasowas.
#
# Use of this source code is governed by a MIT-style license
# that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT.
require 'xcodeproj'
RUN_SCRIPT_PHASE_NAME = 'Fix Privacy Manifest'
if ARGV.length < 2
puts "Usage: ruby xcode_install_helper.rb <project_path> <script_content> [install_builds_only (true|false)]"
exit 1
end
project_path = ARGV[0]
run_script_content = ARGV[1]
install_builds_only = ARGV[2] == 'true'
# Find the first .xcodeproj file in the project directory
xcodeproj_path = Dir.glob(File.join(project_path, "*.xcodeproj")).first
# Validate the .xcodeproj file existence
unless xcodeproj_path
puts "Error: No .xcodeproj file found in the specified directory."
exit 1
end
# Open the Xcode project file
begin
project = Xcodeproj::Project.open(xcodeproj_path)
rescue StandardError => e
puts "Error: Unable to open the project file - #{e.message}"
exit 1
end
# Process all targets in the project
project.targets.each do |target|
# Skip PBXAggregateTarget
if target.is_a?(Xcodeproj::Project::Object::PBXAggregateTarget)
puts "Skipping aggregate target: #{target.name}."
next
end
# Check if the target is a native application target
if target.product_type == 'com.apple.product-type.application'
puts "Processing target: #{target.name}..."
# Check for an existing Run Script phase with the specified name
existing_phase = target.shell_script_build_phases.find { |phase| phase.name == RUN_SCRIPT_PHASE_NAME }
# Remove the existing Run Script phase if found
if existing_phase
puts " - Removing existing Run Script."
target.build_phases.delete(existing_phase)
end
# Add the new Run Script phase at the end
puts " - Adding new Run Script."
new_phase = target.new_shell_script_build_phase(RUN_SCRIPT_PHASE_NAME)
new_phase.shell_script = run_script_content
# Disable showing environment variables in the build log
new_phase.show_env_vars_in_log = '0'
# Run only for deployment post-processing if install_builds_only is true
new_phase.run_only_for_deployment_postprocessing = install_builds_only ? '1' : '0'
# Disable dependency analysis to force the script to run on every build, unless restricted to deployment builds by post-processing setting
new_phase.always_out_of_date = '1'
else
puts "Skipping non-application target: #{target.name}."
end
end
# Save the project file
begin
project.save
puts "Successfully added the Run Script phase: '#{RUN_SCRIPT_PHASE_NAME}'."
rescue StandardError => e
puts "Error: Unable to save the project file - #{e.message}"
exit 1
end

Some files were not shown because too many files have changed in this diff Show More