Compare commits

...

5 Commits

Author SHA1 Message Date
Matthew Raymer cee7a6ded3 feat(logging): enhance debug logging across app 9 hours ago
Matthew Raymer d2157a7d8c feat(mobile): add deep linking support for Capacitor apps 1 day ago
Matthew Raymer fbdf72557c fix: disable PWA for Capacitor builds 2 days ago
Matthew Raymer 74a412745a refactor: reorganize Vite config into modular files 2 days ago
Matthew Raymer eaf0b76e9e chore: cleanup console logs and test directories 2 days ago
  1. 5
      .gitignore
  2. 54
      BUILDING.md
  3. 559
      CHANGELOG.md
  4. 2
      android/app/capacitor.build.gradle
  5. 7
      android/app/src/main/AndroidManifest.xml
  6. 3
      android/capacitor.settings.gradle
  7. 12
      capacitor.config.ts
  8. 0
      id:
  9. 17
      index.html
  10. 9
      ios/App/App/Info.plist
  11. 2
      ios/App/Podfile
  12. 29
      main.js
  13. 2701
      package-lock.json
  14. 32
      package.json
  15. 168
      src/lib/fontawesome.ts
  16. 10
      src/libs/endorserServer.ts
  17. 71
      src/main.capacitor.ts
  18. 61
      src/main.common.ts
  19. 4
      src/main.electron.ts
  20. 4
      src/main.pywebview.ts
  21. 166
      src/main.ts
  22. 5
      src/main.web.ts
  23. 22
      src/services/api.ts
  24. 71
      src/services/plan.ts
  25. 3
      src/views/ClaimView.vue
  26. 4
      vite.config.capacitor.mts
  27. 59
      vite.config.common.mts
  28. 29
      vite.config.electron.mts
  29. 120
      vite.config.mjs
  30. 4
      vite.config.pywebview.mts
  31. 64
      vite.config.utils.mts
  32. 27
      vite.config.web.mts

5
.gitignore

@ -36,4 +36,7 @@ pnpm-debug.log*
/playwright/.cache/
/dist-electron-build/
/dist-capacitor/
/test-playwright-results/
/test-playwright-results/
playwright-tests
test-playwright
dist-electron-packages

54
BUILDING.md

@ -14,12 +14,14 @@ This guide explains how to build TimeSafari for different platforms.
## Initial Setup
1. Clone the repository:
```bash
git clone [repository-url]
cd TimeSafari
```
2. Install dependencies:
```bash
npm install
```
@ -29,6 +31,7 @@ This guide explains how to build TimeSafari for different platforms.
To build for web deployment:
1. Run the production build:
```bash
npm run build
```
@ -36,6 +39,7 @@ To build for web deployment:
2. The built files will be in the `dist` directory.
3. To test the production build locally:
```bash
npm run serve
```
@ -45,11 +49,13 @@ To build for web deployment:
### Building for Linux
1. Build the electron app in production mode:
```bash
npm run build:electron-prod
```
2. Package the Electron app for Linux:
```bash
# For AppImage (recommended)
npm run electron:build-linux
@ -65,12 +71,14 @@ To build for web deployment:
### Running the Packaged App
- 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
@ -95,21 +103,25 @@ npm run build:electron-prod && npm run electron:start
Prerequisites: macOS with Xcode installed
1. Build the web assets:
```bash
npm run build -- --mode capacitor
```
2. Add iOS platform if not already added:
```bash
npx cap add ios
```
3. Update iOS project with latest build:
```bash
npx cap sync ios
```
4. Open the project in Xcode:
```bash
npx cap open ios
```
@ -121,21 +133,25 @@ Prerequisites: macOS with Xcode installed
Prerequisites: Android Studio with SDK installed
1. Build the web assets:
```bash
npm run build -- --mode capacitor
```
2. Add Android platform if not already added:
```bash
npx cap add android
```
3. Update Android project with latest build:
```bash
npx cap sync android
```
4. Open the project in Android Studio:
```bash
npx cap open android
```
@ -147,25 +163,29 @@ Prerequisites: Android Studio with SDK installed
To run the application in development mode:
1. Start the development server:
```bash
npm run dev
```
## PyWebView Desktop Build
### Prerequisites
### 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
@ -173,7 +193,9 @@ To run the application in development mode:
```
### Setup
1. Create and activate a virtual environment (recommended):
```bash
python -m venv .venv
source .venv/bin/activate # On Linux/macOS
@ -182,6 +204,7 @@ To run the application in development mode:
```
2. Install Python dependencies:
```bash
pip install -r requirements.txt
```
@ -189,13 +212,16 @@ To run the application in development mode:
### Troubleshooting
If encountering PyInstaller version errors:
```bash
# Try installing the latest stable version
pip install --upgrade pyinstaller
```
### Development
### Development of PyWebView
1. Start the PyWebView development build:
```bash
npm run pywebview:dev
```
@ -203,31 +229,39 @@ pip install --upgrade pyinstaller
### 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
#### macOS
```bash
npm run pywebview:package-mac
```
The packaged application will be in `dist/TimeSafari`
## Testing
Run local tests:
```bash
npm run test-local
```
Run all tests (includes building):
```bash
npm run test-all
```
@ -235,11 +269,13 @@ npm run test-all
## Linting
Check code style:
```bash
npm run lint
```
Fix code style issues:
```bash
npm run lint-fix
```
@ -260,16 +296,20 @@ See `.env.*` files for configuration.
## 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
@ -279,6 +319,7 @@ rsync -azvu -e "ssh -i ~/.ssh/<YOUR_KEY>" dist ubuntutest@test.timesafari.app:ti
```
### Production Server
```bash
# On the production server:
pkgx +npm sh
@ -293,7 +334,7 @@ cd -
mv time-safari/dist time-safari-dist-prev.0 && mv crowd-funder-for-time-pwa/dist time-safari/
```
## Troubleshooting
## Troubleshooting Builds
### Common Build Issues
@ -310,4 +351,3 @@ mv time-safari/dist time-safari-dist-prev.0 && mv crowd-funder-for-time-pwa/dist
- For iOS: Xcode command line tools must be installed
- For Android: Correct SDK version must be installed
- Check Capacitor configuration in capacitor.config.ts

559
CHANGELOG.md

@ -5,272 +5,348 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.4.4] - 2025.02.17
### Fixed in 0.4.4
## [0.4.4] - 2025.02.17
### Fixed
- On production (due to data?) the search results would disappear after scrolling down. Now we don't show any results when going to the people map with a shortcut.
## [0.4.3] - 2025.02.17
### Added
- Discover query parameter searchPeople to go directly to the people map
### Added in 0.4.3
- Discover query parameter searchPeople to go directly to the people map
## [0.4.2] - 2025.02.17
### Added
- Capacitor build to Android
- Capacitor on iOS and Android
### Fixed
- Path issues
- Path issues
## [0.4.1] - 2025.02.16
### Fixed
### Fixed in 0.4.1
- nostr build issue
- Linting
## [0.4.0] - 2025.02.14
### Changed
- Images in the home feed now take up the full width of the card.
- Clicking the image previously, would open the image in a new tab. Now, clicking the image opens the image in a lightbox view.
### Added
### Added in 0.4.0
- Clicking an image also now displays an in-app lightbox view of the image.
- The lightbox view includes a download button for the image in mobile view.
## [0.3.57] - 2025.02.11
### Added
- Automatic user creation in onboarding meetings
### Added in 0.3.57
- Automatic user creation in onboarding meetings
## [0.3.55] - 2025.02.07
### Added
- End time for projects
### Added in 0.3.55
- End time for projects
## [0.3.54] - 2025.02.06
### Added
- Group onboarding meetings
### Added in 0.3.54
- Group onboarding meetings
## [0.3.53] - 2025.01.30
### Added
- Hints for contacting the creator of a project
### Added in 0.3.53
- Hints for contacting the creator of a project
## [0.3.52] - 2025.01.22
### Fixed
- User profile endpoint server for map was broken.
### Fixed in 0.3.52
- User profile endpoint server for map was broken.
## [0.3.51] - 2025.01.22
### Fixed
- User profile map jumped on first zoom.
### Fixed in 0.3.51
- User profile map jumped on first zoom.
## [0.3.50] - 2025.01.20 - b9fedcd3fd3e34c3fb0fc79150d1a81a76eaeb40
### Added
- User public profiles
### Added in 0.3.50
- User public profiles
## [0.3.49] - 2025.01.09 - 36301ed238ff84df25bb11a8d44a295ee7eaf0f8
### Changed
### Changed in 0.3.49
- Make all external contact links direct to the contact-import page.
- Handle all new-single-contact JWTs in the contacts page, and multiple-contact JWTs in the contacts-import page.
## [0.3.48] - 2025.01.08 - 398f3e64a376789f7eb1c400cd886f5a2cacd588 (but app shows 07c4e58)
### Added
- More sanity-checks on contact-import JWT
### Added in 0.3.48
- More sanity-checks on contact-import JWT
## [0.3.47] - 2025.01.06 - 5bf6dd1ee32ca7cc46d39bd7afca58365b422f93
### Added
### Added in 0.3.47
- Notes on contacts page with new contact-edit page
- Contact methods (only on contact-edit page and under DID details)
- DID view with no DID shows user's info.
### Changed
### Changed in 0.3.47
- URL for user's contact info is now URL to this app (not endorser.ch).
- Extended details (eg. full claim) is beneath details link on claim page.
## [0.3.46] - 2025.01.03 - 9e7056616b5e5acc51e5a8cf7354d408029fefb3
### Added
### Added in 0.3.46
- More action-oriented questions for the gift prompts
### Fixed
- Contact-list import set visibility for all, even if not chosen.
### Fixed in 0.3.46
- Contact-list import set visibility for all, even if not chosen.
## [0.3.45] - 2025.01.01 - 65402dc68ce69ccc6cb9aa8d2e7a9249bf4298e0
### Fixed
- Previous project links stayed when following a link.
### Fixed in 0.3.45
- Previous project links stayed when following a link.
## [0.3.44] - 2024.12.31 - 694b22987b05482e4527c2478bbe15e6b6f3b532
### Added
- Project counts on a map
### Added in 0.3.44
- Project counts on a map
## [0.3.42] - 2024.12.27 - 9751934bc24a1040415a8cfeacbae59ed91f92a5
### Added
### Added in 0.3.42
- Link from certificate page to the claim
### Changed
### Changed in 0.3.42
- Contact data sharing is now a verified JWT.
- Feed pictures are larger.
## [0.3.41] - 2024.12.21 - ff6d14138f26daea6216b051562f0a04681f69fc
### Added
- Link from certificate page to the claim
### Added in 0.3.41
- Link from certificate page to the claim
## [0.3.40] - 2024.12.20 - 77290d9fed3c364243793dc3e9bfe2e994a016b8
### Added
- Only show issuer on certificate if it's not the agent.
### Added in 0.3.40
- Only show issuer on certificate if it's not the agent.
## [0.3.39] - 2024.12.20 - d8819155e2acd2b57fdab523168fa5d1d09e80cc
### Added
- Page for a framed claim certificate
### Added in 0.3.39
- Page for a framed claim certificate
## [0.3.38] - 2024.12.14 - f8cae5ad4fee1f114320dcce052299eab12108b2
### Fixed
- Error on BVC confirmation screen (from IndexedDB refactor)
### Fixed in 0.3.38
- Error on BVC confirmation screen (from IndexedDB refactor)
## [0.3.37] - 2024.12.13 - 4d805b43cd25eed73cdd6651f36ad1ec8c109555
### Added
### Added in 0.3.37
- Record a give from a project on the project page.
- New button on home page opens the gifted dialog.
- On confirmation buttons on the project page gives, mark when unavailable and explain why.
### Changed
### Changed in 0.3.37
- Moved the secret into IndexedDB (and out of localStorage) for more reliability.
- New "invite" destination page helps troubleshoot when JWT link doesn't come through.
### Fixed
### Fixed in 0.3.37
- Problem showing claim issuer name
- Problem going "back" from a project page
## [0.3.36] - 2024.11.24 - c8d23647d165016f8a8f575e13d32583242e53ac
### Changed
### Changed in 0.3.36
- More friendly default reminder message
- Blue borders around people to indicate clickability
## [0.3.35] - 2024.11.24 - bff7d0a6320b70349185e26bfac72e3bb17f76df
### Added
### Added in 0.3.35
- Daily reliable, hard-coded notification message
- Setting to change the partner API server
## [0.3.33] - 2024.11.07 - adb7b16ecf1343c39cba71a7d6bb0e7a973e1102
### Fixed
### Fixed in 0.3.33
- Affirm Delivery button on offer claim page didn't work.
- Plans were not showing by default on project page.
## [0.3.32] - 2024.11.06 - 9a3fa38a3fd28f977e06f0265fc39e635c9c5ccd
### Added
- Highlight in green new offers to user & to user's projects on the front page.
### Added in 0.3.32
- Highlight in green new offers to user & to user's projects on the front page.
## [0.3.31] - 2024.10.25 - 07c02ab98a09d293dd90d9289a7872e7d681d296
### Changed
- Onboarding messages about offers
### Changed in 0.3.31
- Onboarding messages about offers
## [0.3.30]
### Added
- Onboarding messages
### Added in 0.3.30
- Onboarding messages
## [0.3.29] - 2024.10.09 - babd3832bdfe0c40eaa3869de1b41399a51713c1
### Added
### Added in 0.3.29
- Invite for a contact to join immediately
### Changed
### Changed in 0.3.29
- Send signed data to nostr endpoints to verify public key ownership.
- Enhanced help & help onboarding.
### Changed in DB or environment
- Uses Endorser.ch version 4.1.1
- Uses Endorser.ch version 4.1.1
## [0.3.28] - 2024.09.30 - 84720b94049d29cc0ddd99c50cef2e7176130133
### Added
### Added in 0.3.28
- Posting to nostr apps Trustroots & TripHopping
- Display of providers on claim view page
### Changed
### Changed in 0.3.28
- Switched BVC-meeting-ending gift to be a gift from the group.
### Changed in DB or environment
- Requires Endorser.ch version 4.1.0
### Changed in DB or environment in 0.3.28
- Requires Endorser.ch version 4.1.0
## [0.3.27] - 2024.09.22 - ee23e6f005e47f5bd6f04d804599f6395371b0e4
### Fixed
### Fixed in 0.3.27
- Error loading BVC claims to confirm
- Really allow visibility of bulk-imported contacts
## [0.3.26] - 2024.09.16 - 8263ed2b29947b3ccc6f3133bbc9454c222bce28
### Added
### Added in 0.3.26
- Separate 'isRegistered' flag for each account
### Fixed
### Fixed in 0.3.26
- Failure to assign offers to their project
- Alert when looking at one's own activity if not in contacts.
## [0.3.25] - 2024.08.30 - dcbe02d877aecb4cdef2643d90e6595d246a9f82
### Added
### Added in 0.3.25
- "Ideas" now jumps directly to giving prompt or contact list.
### Fixed
### Fixed in 0.3.25
- Empty giver name on gifted-details view
- Previously visited project would show up on the giving-details page.
### Removed
- All unnecessary localStorage for project IDs
### Removed in 0.3.25
- All unnecessary localStorage for project IDs
## [0.3.23] - 2024.08.30
### Added
### Added in 0.3.23
- Sections in Help for different kinds of users
- Discovery page parameters so that links with search text work
- Message when no projects are found
## [0.3.21] - 2024.08.24 - a7b89f4bb6da928d56daeffaae7741fa74cc80bf
### Added
### Added in 0.3.21
- Send list of contacts to someone, and move individual contact actions to detail page.
- Prompt for name in pop-up, and send to different contact-sharing screens.
### Changed
- Moved contact actions from list onto detail page
### Changed in 0.3.21
- Moved contact actions from list onto detail page
## [0.3.20] - 2024.08.18 - 4064eb75a9743ca268bf00016fa0a5fc5dec4e30
### Fixed
### Fixed in 0.3.20
- Bad "give" verbiage on offer page
- Failing offer test
## [0.3.19] - 2024.08.18 - ee9c14942ceba993bf21a11249601f205158ec71
### Added
### Added in 0.3.19
- Update of an offer
- Recipient description in offer list
### Fixed
### Fixed in 0.3.19
- List of offers wasn't showing.
- Destination page after sharing photo was wrong.
## [0.3.17] - 2024.07.11 - cefa384ff1a2d922848c370640c096c529920fab
### Added
### Added in 0.3.17
- Photos on more screens
### Fixed
### Fixed in 0.3.17
- Share of a photo, including sharing a photo from webkit/Safari which never worked
### Changed in DB or environment
- Nothing (though there's a new temp field in IndexedDB)
### Changed in DB or environment in 0.3.17
- Nothing (though there's a new temp field in IndexedDB)
## [0.3.15] - 2024.08.04 - c8f0f2c2b16b9f0b4b47d40f7bf29058c7baa68e
### Added
### Added in 0.3.15
- Edit gives
- Page to edit claim JSON before submitting
- Update of imported contacts
@ -281,263 +357,364 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Cache signatures for reports for passkey-signed requests
- Refactor: consolidate alternative signing, eg. for passkeys & did:peer
- Playwright tests
### Changed
### Changed in 0.3.15
- Linked projects display below description (instead of at bottom)
### Fixed
### Fixed in 0.3.15
- Visibility toggle appearance
### Changed in DB or environment
- Nothing
### Changed in DB or environment in 0.3.15
- Nothing
## [0.3.14] - 2024.06.22 - 1611d22892f683f43856d2503eee7f391b6bbce8
### Added
### Added in 0.3.14
- Clearer give-confirmation screen
- BX currency https://thebx.medium.com/
- BX currency <https://thebx.medium.com/>
- Deselection of project on gifted details page
### Fixed
### Fixed in 0.3.14
- Don't show registration pop-up for a new contact that is registered
### Changed in DB or environment
- Nothing
### Changed in DB or environment in 0.3.14
- Nothing
## [0.3.13] - 2024.05.24 - 08b67984e443c58d9178ad3776013b0bce7afddc
### Added
### Added in 0.3.13
- Photos on projects
### Changed in DB or environment
- Nothing
### Changed in DB or environment in 0.3.13
- Nothing
## [0.3.12] - 2024.05.19 - 141fb39ad19c44d82fe1a33bf85115beacf50870
### Fixed
### Fixed in 0.3.12
- Photo share (share_target) failed because requests were sent to server
### Changed in DB or environment
- Nothing
### Changed in DB or environment in 0.3.12
- Nothing
## [0.3.11] - 2024.05.19 - 567bcad88dfb7e9ac8fea72530d1163985e4a7cc
### Added
### Added in 0.3.11
- Choose a file for gifts, and a URL for gifts & profiles
### Fixed
### Fixed in 0.3.11
- Multiple button pushes were required to switch camera
### Changed in DB or environment
- Nothing
### Changed in DB or environment in 0.3.11
- Nothing
## [0.3.10] - 2024.05.11 - 03ac31d98110f7828cf9acb366db8d01b185f64c
### Added
### Added in 0.3.10
- Share an image
- Choose a file on the device for a profile image
### Changed in DB or environment
- Nothing
### Changed in DB or environment in 0.3.10
- Nothing
## [0.3.9] - 2024.04.28 - 874e717e698b93a1ace9f588e675b8a3dccd7617
### Added
### Added in 0.3.9
- Offers on contacts page
- Checks on front page until they show as registered
### Changed
### Changed in 0.3.9
- Scanned contacts now add immediately and prompt for registration.
- Better UI for gives on contact page
- Better UI for all confirmation messages
### Fixed
### Fixed in 0.3.9
- Repeated elements at top of main feed
### Changed in DB or environment
- Nothing
### Changed in DB or environment in 0.3.9
- Nothing
## [0.3.8] - 2024.04.20 - 15c026c80ce03a26cae3ff80b0888934c101c7e2
### Added
### Added in 0.3.8
- Profile image for user
### Fixed
### Fixed in 0.3.8
- Slow loading of home page feed
### Changed in DB or environment
- Nothing
### Changed in DB or environment in 0.3.8
- Nothing
## [0.3.7] - 2024.04.10 - cf18f1543a700d62a5f9e764905a4aafe1fb229b
### Added
### Added in 0.3.7
- Filter on home page feed
- Ability to set time of daily notification
- Jump to app on click of notification
### Changed
### Changed in 0.3.7
- Built with vite
- Descriptions on home page to include projects
### Changed in DB or environment
- Nothing
### Changed in DB or environment in 0.3.7
- Nothing
## [0.3.6] - 2024.03.24 - 3a07e31d6313ab95711265562d9023c42916e141
### Added
### Added in 0.3.6
- Button to mirror photo during video
- More detailed onboarding help screen
- Public-data blurb
### Changed in DB or environment
- Nothing
### Changed in DB or environment in 0.3.6
- Nothing
## [0.3.5] - 2024.03.23 - 28754bdfb1e11aa221dd49a5dce4219b69cf6a9d
### Added
### Added in 0.3.5
- Photo on gift records
### Fixed
### Fixed in 0.3.5
- Environment variable for BVC meetings project
- Environment variables and build enhancements for test vs prod
### Changed in DB or environment
### Changed in DB or environment in 0.3.5
- New environment variable for image API server
- Test that a new browser session will get the right default APIs.
- Test that a new browser session will send the right BVC meetings project.
## [0.2.17] - 2024.03.01 - 3612ea42240c5e1b7d7eff29a39ff18f1b869b36
### Added
### Added in 0.2.17
- Shortcut page for Bountiful Voluntaryist Community
### Changed
### Changed in 0.2.17
- More readable, targeted summaries in home-page feed items
### Changed in DB
- Nothing
- Nothing
## [0.2.14] - 2024.02.14 - 5f9edea1167dbfb64e16648764eed8c09b24eaeb
### Changed
### Changed in 0.2.14
- Combine all service worker scripts into a single file.
### Changed in DB
- Nothing
### Changed in DB in 0.2.14
- Nothing
## [0.2.13] - 2024.02.07
### Added
### Added in 0.2.13
- Display of user's offers
- Check for valid DIDs
### Fixed
### Fixed in 0.2.13
- Name display on give prompt
- Non-numbers on number input & autocapitalize on URL input
### Changed in DB
- Nothing
### Changed in DB in 0.2.13
- Nothing
## [0.2.12] - 2024.02.01
### Added
- Prompts for gratitude
### Added in 0.2.12
- Prompts for gratitude
## [0.2.11] - 2024.01.28
### Added
### Added in 0.2.11
- Actions to share claim data with contacts
- Bulk CSV import from Endorser Mobile export
- Dates on give summaries
## [0.2.10] - 2024.01.18 - 667e1e8890b42de59cd939caca1a01c7a7a702be
### Added
### Added in 0.2.10
- Person identicons for contacts
- Confirmation & delivery directly from project page
- Offer dialog now allows units
- Links from claim detail page to the fulfilled project or offer
- Link to project from home feed
- Copy to clipboard in more places
### Fixed
- "More Contacts" for give on project page now links correctly.
### Fixed in 0.2.10
- "More Contacts" for give on project page now links correctly.
## [0.2.9] - 2024.01.15 - e5e702f8a5a53a6efbed48d35f0bc3cee63024a0
### Fixed
- Set visibility for new contact.
### Fixed in 0.2.9
- Set visibility for new contact.
## [0.2.8] - 2024.01.14
### Added
### Added in 0.2.8
- Automatic ID creation from home page
- Agent who can also edit a project
### Fixed
- Cannot declare anonymous gift
### Fixed in 0.2.8
- Cannot declare anonymous gift
## [0.2.7] - 2024.01.12
### Added
### Added in 0.2.7
- Give to fulfill a particular offer
- Give as part of a trade as opposed to a donation
- Error notifications on import
### Changed
### Changed in 0.2.7
- Library security updates
- Visibility of actions & confirmations on claim page
### Fixed
- Name of offerer
### Fixed in 0.2.7
- Name of offerer
## [0.2.2] - 2024.01.05
### Added
### Added in 0.2.2
- Check for notification capability on front screen
- Contact next-public-key-hash in manual textual input
- Confirmation for contact visibility change
- YAML rendering of full claim details
- Hints for onboarding on the contact screen
## [0.2.0] - 2024.01.04
### Added
### Added in 0.2.0
- Contact next-public-key-hash
- Icon for Android
- More thorough messaging and testing for notifications
## [0.1.9] - 2024.01.01
### Added
### Added in 0.1.9
- Import for contacts and settings
- Second download button for DuckDuckGo
### Changed
- Removed some keys from Dexie's IndexedDB declarations
### Changed in 0.1.9
- Removed some keys from Dexie's IndexedDB declarations
## [0.1.8] - 2023.12.27- d26d1d360152a7d0e559b68486e85b72b88bd9ff
### Added
### Added in 0.1.8
- DB logging for service-worker events
- Help page for notifications
- Test notification & web-push triggers inside app
- Check that the app is installed
### Fixed
- Project issuer display name
### Fixed in 0.1.8
- Project issuer display name
## [0.1.7] - 2023.12.19 - 91c6c7c11c71f96006cc876fc946f1f98a274ba2
### Changed
### Changed in 0.1.7
- Icons
### Fixed
### Fixed in 0.1.7
- Notification switch now shows message
- Prod/test server warning message at top of page
## [0.1.6] - 2023.12.17 - b445b1234fbfcf6b37d695373f259aab0eda1118
### Added
### Added in 0.1.6
- Infinite scroll on home page
### Changed
### Changed in 0.1.6
- UI improvements
- Show web-push subscription info
- Icon
## [0.1.5] - 2023.12.09 - 9c36bb509a9bae9bb3306d3bd9eeb144b67aa8ad
### Added
### Added in 0.1.5
- Web push notifications (though not finalized)
- Credentials details page
- See more data without an ID
- Change units of a give
## [0.1.4] - 2023.11.20 - 7311d36726f3667ec4c68f241f91d404273ad4db
### Added
### Added in 0.1.4
- Offer on a project
### Changed
- Automatically set as visible when importing a contact
### Changed in 0.1.4
- Automatically set as visible when importing a contact
## [0.1.3] - 2023.11.08 - 910f57ec7d2e50803ae3d04f4b927e0f5219fbde
### Added
### Added in 0.1.3
- Contact name editing
### Changed
### Changed in 0.1.3
- Don't show actions on front page if not registered.
### Removed
- Home page Notiwind test buttons
### Removed in 0.1.3
- Home page Notiwind test buttons
## [0.1.2] - 2023.11.01 - 7f6c93802911a030a89fe3706e18b5c17151e5bb
### Added
### Added in 0.1.2
- Basics: create ID, record a give, declare a project, search, and get notifications.

2
android/app/capacitor.build.gradle

@ -9,7 +9,7 @@ android {
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {
implementation project(':capacitor-app')
}

7
android/app/src/main/AndroidManifest.xml

@ -22,6 +22,13 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="timesafari" />
</intent-filter>
</activity>
<provider

3
android/capacitor.settings.gradle

@ -1,3 +1,6 @@
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
include ':capacitor-app'
project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android')

12
capacitor.config.ts

@ -8,6 +8,18 @@ const config: CapacitorConfig = {
server: {
cleartext: true,
},
plugins: {
App: {
appUrlOpen: {
handlers: [
{
url: "timesafari://*",
autoVerify: true
}
]
}
}
}
};
export default config;

0
id:

17
index.html

@ -12,6 +12,21 @@
<strong>We're sorry but TimeSafari doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script type="module">
const platform = process.env.VITE_PLATFORM;
switch (platform) {
case 'capacitor':
import('./src/main.capacitor.ts');
break;
case 'electron':
import('./src/main.electron.ts');
break;
case 'pywebview':
import('./src/main.pywebview.ts');
break;
default:
import('./src/main.web.ts');
}
</script>
</body>
</html>

9
ios/App/App/Info.plist

@ -45,5 +45,14 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>timesafari</string>
</array>
</dict>
</array>
</dict>
</plist>

2
ios/App/Podfile

@ -11,7 +11,7 @@ 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 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
end
target 'App' do

29
main.js

@ -0,0 +1,29 @@
const { app, BrowserWindow } = require('electron');
const path = require('path');
function createWindow() {
const win = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
});
win.loadFile(path.join(__dirname, 'dist-electron/www/index.html'));
}
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});

2701
package-lock.json

File diff suppressed because it is too large

32
package.json

@ -15,23 +15,25 @@
"test-local": "npx playwright test -c playwright.config-local.ts --trace on",
"test-all": "npm run build && npx playwright test -c playwright.config-local.ts --trace on",
"clean:electron": "rimraf dist-electron",
"build:electron": "npm run clean:electron && vite build --mode electron && node scripts/build-electron.js",
"build:capacitor": "vite build --mode capacitor",
"build:web": "vite build",
"build:pywebview": "vite build --config vite.config.pywebview.mts",
"build:electron": "npm run clean:electron && vite build --config vite.config.electron.mts && node scripts/build-electron.js",
"build:capacitor": "vite build --config vite.config.capacitor.mts",
"build:web": "vite build --config vite.config.web.mts",
"electron:dev": "npm run build && electron dist-electron",
"electron:start": "electron dist-electron",
"electron:build-linux": "electron-builder --linux AppImage",
"electron:build-linux-deb": "electron-builder --linux deb",
"electron:build-linux": "npm run build:electron && electron-builder --linux AppImage",
"electron:build-linux-deb": "npm run build:electron && electron-builder --linux deb",
"electron:build-linux-prod": "NODE_ENV=production npm run build:electron && electron-builder --linux AppImage",
"build:electron-prod": "NODE_ENV=production npm run build:electron",
"electron:build-linux-prod": "npm run build:electron-prod && electron-builder --linux AppImage",
"pywebview:dev": "vite build --mode pywebview && .venv/bin/python src/pywebview/main.py",
"pywebview:build": "vite build --mode pywebview && .venv/bin/python src/pywebview/main.py",
"pywebview:package-linux": "vite build --mode pywebview && .venv/bin/python -m PyInstaller --name TimeSafari --add-data 'dist:www' src/pywebview/main.py",
"pywebview:package-win": "vite build --mode pywebview && .venv/Scripts/python -m PyInstaller --name TimeSafari --add-data 'dist;www' src/pywebview/main.py",
"pywebview:package-mac": "vite build --mode pywebview && .venv/bin/python -m PyInstaller --name TimeSafari --add-data 'dist:www' src/pywebview/main.py"
"pywebview:dev": "vite build --config vite.config.pywebview.mts && .venv/bin/python src/pywebview/main.py",
"pywebview:build": "vite build --config vite.config.pywebview.mts && .venv/bin/python src/pywebview/main.py",
"pywebview:package-linux": "vite build --mode pywebview --config vite.config.pywebview.mts && .venv/bin/python -m PyInstaller --name TimeSafari --add-data 'dist:www' src/pywebview/main.py",
"pywebview:package-win": "vite build --mode pywebview --config vite.config.pywebview.mts && .venv/Scripts/python -m PyInstaller --name TimeSafari --add-data 'dist;www' src/pywebview/main.py",
"pywebview:package-mac": "vite build --mode pywebview --config vite.config.pywebview.mts && .venv/bin/python -m PyInstaller --name TimeSafari --add-data 'dist:www' src/pywebview/main.py"
},
"dependencies": {
"@capacitor/android": "^6.2.0",
"@capacitor/app": "^6.0.0",
"@capacitor/cli": "^6.2.0",
"@capacitor/core": "^6.2.0",
"@capacitor/ios": "^6.2.0",
@ -116,6 +118,7 @@
"autoprefixer": "^10.4.19",
"concurrently": "^8.2.2",
"electron": "^33.2.1",
"electron-builder": "^25.1.8",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
@ -139,12 +142,13 @@
},
"files": [
"dist-electron/**/*",
"src/electron/**/*"
"src/electron/**/*",
"main.js"
],
"extraResources": [
{
"from": "dist-electron/www",
"to": "www"
"from": "dist-electron",
"to": "."
}
],
"linux": {

168
src/lib/fontawesome.ts

@ -0,0 +1,168 @@
/**
* @file Font Awesome Icon Library Configuration
* @description Centralizes Font Awesome icon imports and library configuration
* @author Matthew Raymer
*/
import { library } from "@fortawesome/fontawesome-svg-core";
import {
faArrowDown,
faArrowLeft,
faArrowRight,
faArrowRotateBackward,
faArrowUpRightFromSquare,
faArrowUp,
faBan,
faBitcoinSign,
faBurst,
faCalendar,
faCamera,
faCaretDown,
faChair,
faCheck,
faChevronDown,
faChevronLeft,
faChevronRight,
faChevronUp,
faCircle,
faCircleCheck,
faCircleInfo,
faCircleQuestion,
faCircleUser,
faClock,
faCoins,
faComment,
faCopy,
faDollar,
faEllipsis,
faEllipsisVertical,
faEnvelopeOpenText,
faEraser,
faEye,
faEyeSlash,
faFileContract,
faFileLines,
faFilter,
faFloppyDisk,
faFolderOpen,
faForward,
faGift,
faGlobe,
faHammer,
faHand,
faHandHoldingDollar,
faHandHoldingHeart,
faHouseChimney,
faImagePortrait,
faLeftRight,
faLightbulb,
faLink,
faLocationDot,
faLongArrowAltLeft,
faLongArrowAltRight,
faMagnifyingGlass,
faMessage,
faMinus,
faPen,
faPersonCircleCheck,
faPersonCircleQuestion,
faPlus,
faQuestion,
faQrcode,
faRightFromBracket,
faRotate,
faShareNodes,
faSpinner,
faSquare,
faSquareCaretDown,
faSquareCaretUp,
faSquarePlus,
faTrashCan,
faTriangleExclamation,
faUser,
faUsers,
faXmark,
} from "@fortawesome/free-solid-svg-icons";
// Initialize Font Awesome library with all required icons
library.add(
faArrowDown,
faArrowLeft,
faArrowRight,
faArrowRotateBackward,
faArrowUpRightFromSquare,
faArrowUp,
faBan,
faBitcoinSign,
faBurst,
faCalendar,
faCamera,
faCaretDown,
faChair,
faCheck,
faChevronDown,
faChevronLeft,
faChevronRight,
faChevronUp,
faCircle,
faCircleCheck,
faCircleInfo,
faCircleQuestion,
faCircleUser,
faClock,
faCoins,
faComment,
faCopy,
faDollar,
faEllipsis,
faEllipsisVertical,
faEnvelopeOpenText,
faEraser,
faEye,
faEyeSlash,
faFileContract,
faFileLines,
faFilter,
faFloppyDisk,
faFolderOpen,
faForward,
faGift,
faGlobe,
faHammer,
faHand,
faHandHoldingDollar,
faHandHoldingHeart,
faHouseChimney,
faImagePortrait,
faLeftRight,
faLightbulb,
faLink,
faLocationDot,
faLongArrowAltLeft,
faLongArrowAltRight,
faMagnifyingGlass,
faMessage,
faMinus,
faPen,
faPersonCircleCheck,
faPersonCircleQuestion,
faPlus,
faQrcode,
faQuestion,
faRotate,
faRightFromBracket,
faShareNodes,
faSpinner,
faSquare,
faSquareCaretDown,
faSquareCaretUp,
faSquarePlus,
faTrashCan,
faTriangleExclamation,
faUser,
faUsers,
faXmark,
);
// Export the FontAwesomeIcon component for use in other files
export { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";

10
src/libs/endorserServer.ts

@ -649,19 +649,19 @@ export async function getPlanFromCache(
cred = resp.data.data[0];
planCache.set(handleId, cred);
} else {
console.error(
"Failed to load plan with handle",
console.info(
"[EndorserServer] Plan cache is empty for handle",
handleId,
" Got data:",
resp.data,
JSON.stringify(resp.data),
);
}
} catch (error) {
console.error(
"Failed to load plan with handle",
"[EndorserServer] Failed to load plan with handle",
handleId,
" Got error:",
error,
JSON.stringify(error),
);
}
}

71
src/main.capacitor.ts

@ -0,0 +1,71 @@
import { initializeApp } from "./main.common";
import { App } from "@capacitor/app";
import router from "./router";
import { handleApiError } from "./services/api";
import { loadPlanWithRetry } from "./services/plan";
import { Capacitor } from '@capacitor/core';
console.log("[Capacitor] Starting initialization");
console.log("[Capacitor] Platform:", process.env.VITE_PLATFORM);
const app = initializeApp();
// Store initial deep link if app is not ready
let pendingDeepLink: string | null = null;
// Initialize API error handling
window.addEventListener('unhandledrejection', (event) => {
if (event.reason?.response) {
handleApiError(event.reason, event.reason.config?.url || 'unknown');
}
});
// Create reusable handler function
const handleDeepLink = async (data: { url: string }) => {
try {
console.log("[Capacitor Deep Link] START Handler");
console.log("[Capacitor Deep Link] Received URL:", data.url);
// Wait for router to be ready
await router.isReady();
// Parse the custom URL scheme
const parts = data.url.split('://');
if (parts.length !== 2) {
throw new Error('Invalid URL format');
}
const path = parts[1]; // This will be "claim/01JMAAFZRNSRTQ0EBSD70A8E1H"
console.log("[Capacitor Deep Link] Parsed path:", path);
// Map parameterized routes
const paramRoutes = {
'claim': /^claim\/(.+)$/, // Updated pattern without leading slash
};
// Check if path matches any parameterized route
for (const [routeName, pattern] of Object.entries(paramRoutes)) {
const match = path.match(pattern);
if (match) {
console.log(`[Capacitor Deep Link] Matched route: ${routeName}, param: ${match[1]}`);
await router.replace({
name: routeName,
params: { id: match[1] }
});
return;
}
}
await router.replace('/' + path);
} catch (error) {
console.error("[Capacitor Deep Link] Error:", error);
handleApiError(error, 'deep-link');
}
};
// Register listener
App.addListener("appUrlOpen", handleDeepLink);
console.log("[Capacitor] Mounting app");
app.mount("#app");
console.log("[Capacitor] App mounted");

61
src/main.common.ts

@ -0,0 +1,61 @@
import { createPinia } from "pinia";
import { App as VueApp, ComponentPublicInstance, createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import axios from "axios";
import VueAxios from "vue-axios";
import Notifications from "notiwind";
import "./assets/styles/tailwind.css";
import { FontAwesomeIcon } from "./lib/fontawesome";
import Camera from "simple-vue-camera";
// Global Error Handler
function setupGlobalErrorHandler(app: VueApp) {
console.log("[App Init] Setting up global error handler");
app.config.errorHandler = (
err: unknown,
instance: ComponentPublicInstance | null,
info: string
) => {
console.error("[App Error] Global Error Handler:", {
error: err,
info,
component: instance?.$options.name || 'unknown'
});
alert(
(err instanceof Error ? err.message : "Something bad happened") +
" - Try reloading or restarting the app."
);
};
}
// Function to initialize the app
export function initializeApp() {
console.log("[App Init] Starting app initialization");
console.log("[App Init] Platform:", process.env.VITE_PLATFORM);
const app = createApp(App);
console.log("[App Init] Vue app created");
app.component("fa", FontAwesomeIcon)
.component("camera", Camera);
console.log("[App Init] Components registered");
const pinia = createPinia();
app.use(pinia);
console.log("[App Init] Pinia store initialized");
app.use(VueAxios, axios);
console.log("[App Init] Axios initialized");
app.use(router);
console.log("[App Init] Router initialized");
app.use(Notifications);
console.log("[App Init] Notifications initialized");
setupGlobalErrorHandler(app);
console.log("[App Init] App initialization complete");
return app;
}

4
src/main.electron.ts

@ -0,0 +1,4 @@
import { initializeApp } from "./main.common";
const app = initializeApp();
app.mount("#app");

4
src/main.pywebview.ts

@ -0,0 +1,4 @@
import { initializeApp } from "./main.common";
const app = initializeApp();
app.mount("#app");

166
src/main.ts

@ -6,169 +6,8 @@ import router from "./router";
import axios from "axios";
import VueAxios from "vue-axios";
import Notifications from "notiwind";
import "./assets/styles/tailwind.css";
import { library } from "@fortawesome/fontawesome-svg-core";
import {
faArrowDown,
faArrowLeft,
faArrowRight,
faArrowRotateBackward,
faArrowUpRightFromSquare,
faArrowUp,
faBan,
faBitcoinSign,
faBurst,
faCalendar,
faCamera,
faCaretDown,
faChair,
faCheck,
faChevronDown,
faChevronLeft,
faChevronRight,
faChevronUp,
faCircle,
faCircleCheck,
faCircleInfo,
faCircleQuestion,
faCircleUser,
faClock,
faCoins,
faComment,
faCopy,
faDollar,
faEllipsis,
faEllipsisVertical,
faEnvelopeOpenText,
faEraser,
faEye,
faEyeSlash,
faFileContract,
faFileLines,
faFilter,
faFloppyDisk,
faFolderOpen,
faForward,
faGift,
faGlobe,
faHammer,
faHand,
faHandHoldingDollar,
faHandHoldingHeart,
faHouseChimney,
faImagePortrait,
faLeftRight,
faLightbulb,
faLink,
faLocationDot,
faLongArrowAltLeft,
faLongArrowAltRight,
faMagnifyingGlass,
faMessage,
faMinus,
faPen,
faPersonCircleCheck,
faPersonCircleQuestion,
faPlus,
faQuestion,
faQrcode,
faRightFromBracket,
faRotate,
faShareNodes,
faSpinner,
faSquare,
faSquareCaretDown,
faSquareCaretUp,
faSquarePlus,
faTrashCan,
faTriangleExclamation,
faUser,
faUsers,
faXmark,
} from "@fortawesome/free-solid-svg-icons";
library.add(
faArrowDown,
faArrowLeft,
faArrowRight,
faArrowRotateBackward,
faArrowUpRightFromSquare,
faArrowUp,
faBan,
faBitcoinSign,
faBurst,
faCalendar,
faCamera,
faCaretDown,
faChair,
faCheck,
faChevronDown,
faChevronLeft,
faChevronRight,
faChevronUp,
faCircle,
faCircleCheck,
faCircleInfo,
faCircleQuestion,
faCircleUser,
faClock,
faCoins,
faComment,
faCopy,
faDollar,
faEllipsis,
faEllipsisVertical,
faEnvelopeOpenText,
faEraser,
faEye,
faEyeSlash,
faFileContract,
faFileLines,
faFilter,
faFloppyDisk,
faFolderOpen,
faForward,
faGift,
faGlobe,
faHammer,
faHand,
faHandHoldingDollar,
faHandHoldingHeart,
faHouseChimney,
faImagePortrait,
faLeftRight,
faLightbulb,
faLink,
faLocationDot,
faLongArrowAltLeft,
faLongArrowAltRight,
faMagnifyingGlass,
faMessage,
faMinus,
faPen,
faPersonCircleCheck,
faPersonCircleQuestion,
faPlus,
faQrcode,
faQuestion,
faRotate,
faRightFromBracket,
faShareNodes,
faSpinner,
faSquare,
faSquareCaretDown,
faSquareCaretUp,
faSquarePlus,
faTrashCan,
faTriangleExclamation,
faUser,
faUsers,
faXmark,
);
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { FontAwesomeIcon } from "./lib/fontawesome";
import Camera from "simple-vue-camera";
// Can trigger this with a 'throw' inside some top-level function, eg. on the HomeView
@ -197,7 +36,7 @@ function setupGlobalErrorHandler(app: VueApp) {
);
};
}
// console.log("Bootstrapping Vue app...");
const app = createApp(App)
.component("fa", FontAwesomeIcon)
.component("camera", Camera)
@ -209,4 +48,3 @@ const app = createApp(App)
setupGlobalErrorHandler(app);
app.mount("#app");
// console.log("Vue app mounted.");

5
src/main.web.ts

@ -0,0 +1,5 @@
import { initializeApp } from "./main.common";
import "./registerServiceWorker"; // Web PWA support
const app = initializeApp();
app.mount("#app");

22
src/services/api.ts

@ -0,0 +1,22 @@
export const handleApiError = (error: any, endpoint: string) => {
if (process.env.VITE_PLATFORM === 'capacitor') {
console.error(`[Capacitor API Error] ${endpoint}:`, {
message: error.message,
status: error.response?.status,
data: error.response?.data,
config: {
url: error.config?.url,
method: error.config?.method,
headers: error.config?.headers
}
});
}
// Specific handling for rate limits
if (error.response?.status === 400) {
console.warn(`[Rate Limit] ${endpoint}`);
return null;
}
throw error;
};

71
src/services/plan.ts

@ -0,0 +1,71 @@
import axios from 'axios';
interface PlanResponse {
data?: any;
status?: number;
error?: string;
}
export const loadPlanWithRetry = async (handle: string, retries = 3): Promise<PlanResponse> => {
try {
console.log(`[Plan Service] Loading plan ${handle}, attempt 1/${retries}`);
console.log(`[Plan Service] Context: Deep link handle=${handle}, isClaimFlow=${handle.includes('claim')}`);
// Different endpoint if this is a claim flow
const response = await loadPlan(handle);
console.log(`[Plan Service] Plan ${handle} loaded successfully:`, {
status: response?.status,
headers: response?.headers,
data: response?.data
});
return response;
} catch (error: any) {
console.error(`[Plan Service] Error loading plan ${handle}:`, {
message: error.message,
status: error.response?.status,
statusText: error.response?.statusText,
data: error.response?.data,
headers: error.response?.headers,
config: {
url: error.config?.url,
method: error.config?.method,
baseURL: error.config?.baseURL,
headers: error.config?.headers
}
});
if (retries > 1) {
console.log(`[Plan Service] Retrying plan ${handle}, ${retries-1} attempts remaining`);
await new Promise(resolve => setTimeout(resolve, 1000));
return loadPlanWithRetry(handle, retries - 1);
}
return {
error: `Failed to load plan ${handle} after ${4-retries} attempts: ${error.message}`,
status: error.response?.status
};
}
};
export const loadPlan = async (handle: string): Promise<PlanResponse> => {
console.log(`[Plan Service] Making API request for plan ${handle}`);
const endpoint = handle.includes('claim')
? `/api/claims/${handle}`
: `/api/plans/${handle}`;
console.log(`[Plan Service] Using endpoint: ${endpoint}`);
try {
const response = await axios.get(endpoint);
return response;
} catch (error: any) {
console.error(`[Plan Service] API request failed for ${handle}:`, {
endpoint,
error: error.message,
response: error.response?.data
});
throw error;
}
};

3
src/views/ClaimView.vue

@ -585,6 +585,7 @@ export default class ClaimView extends Vue {
}
async created() {
console.log("ClaimView created");
const settings = await retrieveSettingsForActiveAccount();
this.activeDid = settings.activeDid || "";
this.apiServer = settings.apiServer || "";
@ -659,6 +660,7 @@ export default class ClaimView extends Vue {
}
async loadClaim(claimId: string, userDid: string) {
console.log("[ClaimView] loadClaim called with claimId:", claimId);
const urlPath = libsUtil.isGlobalUri(claimId)
? "/api/claim/byHandle/"
: "/api/claim/";
@ -666,6 +668,7 @@ export default class ClaimView extends Vue {
const headers = await serverUtil.getHeaders(userDid);
try {
console.log("[ClaimView] Making API request to:", url);
const resp = await this.axios.get(url, { headers });
if (resp.status === 200) {
this.veriClaim = resp.data;

4
vite.config.capacitor.mts

@ -0,0 +1,4 @@
import { defineConfig } from "vite";
import { createBuildConfig } from "./vite.config.common.mts";
export default defineConfig(async () => createBuildConfig('capacitor'));

59
vite.config.common.mts

@ -0,0 +1,59 @@
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import dotenv from "dotenv";
import { loadAppConfig } from "./vite.config.utils.mts";
import path from "path";
import { fileURLToPath } from 'url';
// Load environment variables
dotenv.config();
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export async function createBuildConfig(mode: string) {
const appConfig = await loadAppConfig();
const isElectron = mode === "electron";
const isCapacitor = mode === "capacitor";
const isPyWebView = mode === "pywebview";
// Explicitly set platform
process.env.VITE_PLATFORM = mode;
if (isElectron || isPyWebView || isCapacitor) {
process.env.VITE_PWA_ENABLED = 'false';
}
return {
base: isElectron || isPyWebView ? "./" : "/",
plugins: [vue()],
server: {
port: parseInt(process.env.VITE_PORT || "8080"),
fs: { strict: false },
},
build: {
outDir: isElectron ? "dist-electron" : "dist",
assetsDir: 'assets',
chunkSizeWarningLimit: 1000
},
define: {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'process.env.VITE_PLATFORM': JSON.stringify(mode),
'process.env.VITE_PWA_ENABLED': JSON.stringify(!(isElectron || isPyWebView || isCapacitor)),
__dirname: isElectron ? JSON.stringify(process.cwd()) : '""',
},
resolve: {
alias: appConfig.aliasConfig
},
optimizeDeps: {
exclude: isElectron ? [
'register-service-worker',
'workbox-window',
'web-push',
'serviceworker-webpack-plugin'
] : []
}
};
}
export default defineConfig(async () => createBuildConfig('web'));

29
vite.config.electron.mts

@ -0,0 +1,29 @@
import { defineConfig, mergeConfig } from "vite";
import { createBuildConfig } from "./vite.config.common.mts";
export default defineConfig(async () => {
const baseConfig = await createBuildConfig('electron');
return mergeConfig(baseConfig, {
plugins: [{
name: 'remove-sw-imports',
transform(code: string, id: string) {
if (
id.includes('registerServiceWorker') ||
id.includes('register-service-worker') ||
id.includes('sw_scripts') ||
id.includes('PushNotificationPermission') ||
code.includes('navigator.serviceWorker')
) {
return {
code: code
.replace(/import.*registerServiceWorker.*$/mg, '')
.replace(/import.*register-service-worker.*$/mg, '')
.replace(/navigator\.serviceWorker/g, 'undefined')
.replace(/if\s*\([^)]*serviceWorker[^)]*\)\s*{[^}]*}/g, '')
};
}
}
}]
});
});

120
vite.config.mjs

@ -1,120 +0,0 @@
import { defineConfig } from "vite";
import { VitePWA } from "vite-plugin-pwa";
import vue from "@vitejs/plugin-vue";
import dotenv from "dotenv";
import { loadAppConfig } from "./vite.config.utils";
import path from "path";
import { fileURLToPath } from 'url';
// Load environment variables from .env file
dotenv.config();
// Get dirname in ESM context
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Load application configuration
const appConfig = loadAppConfig();
export default defineConfig(({ mode }) => {
const isElectron = mode === "electron";
const isCapacitor = mode === "capacitor";
const isPyWebView = mode === "pywebview";
// Disable PWA features for desktop builds
if (isElectron || isPyWebView) {
process.env.VITE_PWA_ENABLED = 'false';
}
return {
base: isElectron || isPyWebView ? "./" : "/",
server: {
port: process.env.VITE_PORT || 8080,
fs: {
strict: false
},
},
build: {
outDir: isElectron ? "dist-electron" : "dist",
assetsDir: 'assets',
rollupOptions: {
external: ['electron', 'path'],
input: {
main: path.resolve(__dirname, 'index.html')
},
output: {
manualChunks(id) {
if (isElectron && (
id.includes('registerServiceWorker') ||
id.includes('register-service-worker') ||
id.includes('workbox') ||
id.includes('sw_scripts') ||
id.includes('PushNotificationPermission')
)) {
return null; // Exclude these modules completely
}
}
}
},
chunkSizeWarningLimit: 1000
},
define: {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'process.env.VITE_PWA_ENABLED': JSON.stringify(!(isElectron || isPyWebView)),
__dirname: isElectron ? JSON.stringify(process.cwd()) : '""',
},
plugins: [
vue(),
{
name: 'remove-sw-imports',
transform(code, id) {
if (isElectron) {
if (
id.includes('registerServiceWorker') ||
id.includes('register-service-worker') ||
id.includes('sw_scripts') ||
id.includes('PushNotificationPermission') ||
code.includes('navigator.serviceWorker')
) {
return {
code: code
.replace(/import.*registerServiceWorker.*$/mg, '')
.replace(/import.*register-service-worker.*$/mg, '')
.replace(/navigator\.serviceWorker/g, 'undefined')
.replace(/if\s*\([^)]*serviceWorker[^)]*\)\s*{[^}]*}/g, '')
};
}
}
}
},
...(!isElectron && !isCapacitor ? [
VitePWA({
disable: true,
registerType: 'autoUpdate',
injectRegister: null,
workbox: {
cleanupOutdatedCaches: true,
skipWaiting: true,
clientsClaim: true,
sourcemap: true
},
manifest: appConfig.pwaConfig?.manifest,
devOptions: {
enabled: false
}
}),
] : []),
],
resolve: {
alias: appConfig.aliasConfig
},
optimizeDeps: {
exclude: isElectron ? [
'register-service-worker',
'workbox-window',
'web-push',
'serviceworker-webpack-plugin'
] : []
}
};
});

4
vite.config.pywebview.mts

@ -0,0 +1,4 @@
import { defineConfig } from "vite";
import { createBuildConfig } from "./vite.config.common.mts";
export default defineConfig(async () => createBuildConfig('pywebview'));

64
vite.config.utils.js → vite.config.utils.mts

@ -1,7 +1,57 @@
import * as path from "path";
import { fileURLToPath } from 'url';
import { promises as fs } from "fs";
export async function loadAppConfig() {
const __dirname = path.dirname(fileURLToPath(import.meta.url));
async function loadPackageJson() {
const packageJsonPath = path.join(__dirname, 'package.json');
const packageJsonContent = await fs.readFile(packageJsonPath, 'utf-8');
return JSON.parse(packageJsonContent);
}
interface ManifestIcon {
src: string;
sizes: string;
type: string;
purpose?: string;
}
interface ShareTarget {
action: string;
method: "POST";
enctype: string;
params: {
files: Array<{
name: string;
accept: string[];
}>;
};
}
interface PWAConfig {
registerType: string;
strategies: string;
srcDir: string;
filename: string;
manifest: {
name: string;
short_name: string;
theme_color: string;
background_color: string;
icons: ManifestIcon[];
share_target: ShareTarget;
};
}
interface AppConfig {
pwaConfig: PWAConfig;
aliasConfig: {
[key: string]: string;
};
}
export async function loadAppConfig(): Promise<AppConfig> {
const packageJson = await loadPackageJson();
const appName = process.env.TIME_SAFARI_APP_TITLE || packageJson.name;
@ -14,6 +64,8 @@ export async function loadAppConfig() {
manifest: {
name: appName,
short_name: appName,
theme_color: "#4a90e2",
background_color: "#ffffff",
icons: [
{
src: "./img/icons/android-chrome-192x192.png",
@ -54,16 +106,10 @@ export async function loadAppConfig() {
},
},
aliasConfig: {
"@": path.resolve(__dirname, ".."),
"@": path.resolve(__dirname, "src"),
buffer: path.resolve(__dirname, "node_modules", "buffer"),
"dexie-export-import/dist/import":
"dexie-export-import/dist/import/index.js",
},
};
}
async function loadPackageJson() {
const packageJsonPath = path.resolve(__dirname, "package.json");
const packageJsonData = await fs.readFile(packageJsonPath, "utf-8");
return JSON.parse(packageJsonData);
}
}

27
vite.config.web.mts

@ -0,0 +1,27 @@
import { defineConfig, mergeConfig } from "vite";
import { VitePWA } from "vite-plugin-pwa";
import { createBuildConfig } from "./vite.config.common.mts";
import { loadAppConfig } from "./vite.config.utils.mts";
export default defineConfig(async () => {
const baseConfig = await createBuildConfig('web');
const appConfig = await loadAppConfig();
return mergeConfig(baseConfig, {
plugins: [
VitePWA({
registerType: 'autoUpdate',
manifest: appConfig.pwaConfig?.manifest,
devOptions: {
enabled: false
},
workbox: {
cleanupOutdatedCaches: true,
skipWaiting: true,
clientsClaim: true,
sourcemap: true
}
})
]
});
});
Loading…
Cancel
Save