Compare commits

...

7 Commits

Author SHA1 Message Date
Matthew Raymer 3b7a872ae1 refactor: update nostr-tools imports for better tree shaking 9 hours ago
Matthew Raymer a8e15804a6 WIP: certificate view and dependency updates 12 hours ago
Matthew Raymer cee7a6ded3 feat(logging): enhance debug logging across app 1 day ago
Matthew Raymer d2157a7d8c feat(mobile): add deep linking support for Capacitor apps 2 days ago
Matthew Raymer fbdf72557c fix: disable PWA for Capacitor builds 3 days ago
Matthew Raymer 74a412745a refactor: reorganize Vite config into modular files 3 days ago
Matthew Raymer eaf0b76e9e chore: cleanup console logs and test directories 4 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. 17
      index.html
  9. 9
      ios/App/App/Info.plist
  10. 2
      ios/App/Podfile
  11. 29
      main.js
  12. 3862
      package-lock.json
  13. 36
      package.json
  14. 168
      src/lib/fontawesome.ts
  15. 10
      src/libs/endorserServer.ts
  16. 68
      src/main.capacitor.ts
  17. 60
      src/main.common.ts
  18. 4
      src/main.electron.ts
  19. 4
      src/main.pywebview.ts
  20. 166
      src/main.ts
  21. 5
      src/main.web.ts
  22. 24
      src/services/api.ts
  23. 80
      src/services/plan.ts
  24. 2
      src/views/AccountViewView.vue
  25. 3
      src/views/ClaimView.vue
  26. 14
      src/views/NewEditProjectView.vue
  27. 4
      vite.config.capacitor.mts
  28. 73
      vite.config.common.mts
  29. 4
      vite.config.dev.mts
  30. 29
      vite.config.electron.mts
  31. 120
      vite.config.mjs
  32. 4
      vite.config.pywebview.mts
  33. 64
      vite.config.utils.mts
  34. 27
      vite.config.web.mts

5
.gitignore

@ -36,4 +36,7 @@ pnpm-debug.log*
/playwright/.cache/ /playwright/.cache/
/dist-electron-build/ /dist-electron-build/
/dist-capacitor/ /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 ## Initial Setup
1. Clone the repository: 1. Clone the repository:
```bash ```bash
git clone [repository-url] git clone [repository-url]
cd TimeSafari cd TimeSafari
``` ```
2. Install dependencies: 2. Install dependencies:
```bash ```bash
npm install npm install
``` ```
@ -29,6 +31,7 @@ This guide explains how to build TimeSafari for different platforms.
To build for web deployment: To build for web deployment:
1. Run the production build: 1. Run the production build:
```bash ```bash
npm run build npm run build
``` ```
@ -36,6 +39,7 @@ To build for web deployment:
2. The built files will be in the `dist` directory. 2. The built files will be in the `dist` directory.
3. To test the production build locally: 3. To test the production build locally:
```bash ```bash
npm run serve npm run serve
``` ```
@ -45,11 +49,13 @@ To build for web deployment:
### Building for Linux ### Building for Linux
1. Build the electron app in production mode: 1. Build the electron app in production mode:
```bash ```bash
npm run build:electron-prod npm run build:electron-prod
``` ```
2. Package the Electron app for Linux: 2. Package the Electron app for Linux:
```bash ```bash
# For AppImage (recommended) # For AppImage (recommended)
npm run electron:build-linux npm run electron:build-linux
@ -65,12 +71,14 @@ To build for web deployment:
### Running the Packaged App ### Running the Packaged App
- AppImage: Make executable and run - AppImage: Make executable and run
```bash ```bash
chmod +x dist-electron-packages/TimeSafari-*.AppImage chmod +x dist-electron-packages/TimeSafari-*.AppImage
./dist-electron-packages/TimeSafari-*.AppImage ./dist-electron-packages/TimeSafari-*.AppImage
``` ```
- DEB: Install and run - DEB: Install and run
```bash ```bash
sudo dpkg -i dist-electron-packages/timesafari_*_amd64.deb sudo dpkg -i dist-electron-packages/timesafari_*_amd64.deb
timesafari timesafari
@ -95,21 +103,25 @@ npm run build:electron-prod && npm run electron:start
Prerequisites: macOS with Xcode installed Prerequisites: macOS with Xcode installed
1. Build the web assets: 1. Build the web assets:
```bash ```bash
npm run build -- --mode capacitor npm run build -- --mode capacitor
``` ```
2. Add iOS platform if not already added: 2. Add iOS platform if not already added:
```bash ```bash
npx cap add ios npx cap add ios
``` ```
3. Update iOS project with latest build: 3. Update iOS project with latest build:
```bash ```bash
npx cap sync ios npx cap sync ios
``` ```
4. Open the project in Xcode: 4. Open the project in Xcode:
```bash ```bash
npx cap open ios npx cap open ios
``` ```
@ -121,21 +133,25 @@ Prerequisites: macOS with Xcode installed
Prerequisites: Android Studio with SDK installed Prerequisites: Android Studio with SDK installed
1. Build the web assets: 1. Build the web assets:
```bash ```bash
npm run build -- --mode capacitor npm run build -- --mode capacitor
``` ```
2. Add Android platform if not already added: 2. Add Android platform if not already added:
```bash ```bash
npx cap add android npx cap add android
``` ```
3. Update Android project with latest build: 3. Update Android project with latest build:
```bash ```bash
npx cap sync android npx cap sync android
``` ```
4. Open the project in Android Studio: 4. Open the project in Android Studio:
```bash ```bash
npx cap open android npx cap open android
``` ```
@ -147,25 +163,29 @@ Prerequisites: Android Studio with SDK installed
To run the application in development mode: To run the application in development mode:
1. Start the development server: 1. Start the development server:
```bash ```bash
npm run dev npm run dev
``` ```
## PyWebView Desktop Build ## PyWebView Desktop Build
### Prerequisites ### Prerequisites for PyWebView
- Python 3.8 or higher - Python 3.8 or higher
- pip (Python package manager) - pip (Python package manager)
- virtualenv (recommended) - virtualenv (recommended)
- System dependencies: - System dependencies:
```bash ```bash
# For Ubuntu/Debian # For Ubuntu/Debian
sudo apt-get install python3-webview sudo apt-get install python3-webview
# or # or
sudo apt-get install python3-gi python3-gi-cairo gir1.2-gtk-3.0 gir1.2-webkit2-4.0 sudo apt-get install python3-gi python3-gi-cairo gir1.2-gtk-3.0 gir1.2-webkit2-4.0
# For Arch Linux # For Arch Linux
sudo pacman -S webkit2gtk python-gobject python-cairo sudo pacman -S webkit2gtk python-gobject python-cairo
# For Fedora # For Fedora
sudo dnf install python3-webview sudo dnf install python3-webview
# or # or
@ -173,7 +193,9 @@ To run the application in development mode:
``` ```
### Setup ### Setup
1. Create and activate a virtual environment (recommended): 1. Create and activate a virtual environment (recommended):
```bash ```bash
python -m venv .venv python -m venv .venv
source .venv/bin/activate # On Linux/macOS source .venv/bin/activate # On Linux/macOS
@ -182,6 +204,7 @@ To run the application in development mode:
``` ```
2. Install Python dependencies: 2. Install Python dependencies:
```bash ```bash
pip install -r requirements.txt pip install -r requirements.txt
``` ```
@ -189,13 +212,16 @@ To run the application in development mode:
### Troubleshooting ### Troubleshooting
If encountering PyInstaller version errors: If encountering PyInstaller version errors:
```bash ```bash
# Try installing the latest stable version # Try installing the latest stable version
pip install --upgrade pyinstaller pip install --upgrade pyinstaller
``` ```
### Development ### Development of PyWebView
1. Start the PyWebView development build: 1. Start the PyWebView development build:
```bash ```bash
npm run pywebview:dev npm run pywebview:dev
``` ```
@ -203,31 +229,39 @@ pip install --upgrade pyinstaller
### Building for Distribution ### Building for Distribution
#### Linux #### Linux
```bash ```bash
npm run pywebview:package-linux npm run pywebview:package-linux
``` ```
The packaged application will be in `dist/TimeSafari` The packaged application will be in `dist/TimeSafari`
#### Windows #### Windows
```bash ```bash
npm run pywebview:package-win npm run pywebview:package-win
``` ```
The packaged application will be in `dist/TimeSafari` The packaged application will be in `dist/TimeSafari`
#### macOS #### macOS
```bash ```bash
npm run pywebview:package-mac npm run pywebview:package-mac
``` ```
The packaged application will be in `dist/TimeSafari` The packaged application will be in `dist/TimeSafari`
## Testing ## Testing
Run local tests: Run local tests:
```bash ```bash
npm run test-local npm run test-local
``` ```
Run all tests (includes building): Run all tests (includes building):
```bash ```bash
npm run test-all npm run test-all
``` ```
@ -235,11 +269,13 @@ npm run test-all
## Linting ## Linting
Check code style: Check code style:
```bash ```bash
npm run lint npm run lint
``` ```
Fix code style issues: Fix code style issues:
```bash ```bash
npm run lint-fix npm run lint-fix
``` ```
@ -260,16 +296,20 @@ See `.env.*` files for configuration.
## Deployment ## Deployment
### Version Management ### Version Management
1. Update CHANGELOG.md with new changes 1. Update CHANGELOG.md with new changes
2. Update version in package.json 2. Update version in package.json
3. Commit changes and tag release: 3. Commit changes and tag release:
```bash ```bash
git tag <VERSION_TAG> git tag <VERSION_TAG>
git push origin <VERSION_TAG> git push origin <VERSION_TAG>
``` ```
4. After deployment, update package.json with next version + "-beta" 4. After deployment, update package.json with next version + "-beta"
### Test Server ### Test Server
```bash ```bash
# Build using staging environment # Build using staging environment
npm run build -- --mode staging 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 ### Production Server
```bash ```bash
# On the production server: # On the production server:
pkgx +npm sh 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/ mv time-safari/dist time-safari-dist-prev.0 && mv crowd-funder-for-time-pwa/dist time-safari/
``` ```
## Troubleshooting ## Troubleshooting Builds
### Common Build Issues ### 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 iOS: Xcode command line tools must be installed
- For Android: Correct SDK version must be installed - For Android: Correct SDK version must be installed
- Check Capacitor configuration in capacitor.config.ts - 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/), 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). 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. - 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 ## [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 ## [0.4.2] - 2025.02.17
### Added ### Added
- Capacitor build to Android
- Capacitor on iOS and Android
### Fixed ### Fixed
- Path issues
- Path issues
## [0.4.1] - 2025.02.16 ## [0.4.1] - 2025.02.16
### Fixed
### Fixed in 0.4.1
- nostr build issue - nostr build issue
- Linting - Linting
## [0.4.0] - 2025.02.14 ## [0.4.0] - 2025.02.14
### Changed ### Changed
- Images in the home feed now take up the full width of the card. - 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. - 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. - 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. - The lightbox view includes a download button for the image in mobile view.
## [0.3.57] - 2025.02.11 ## [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 ## [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 ## [0.3.54] - 2025.02.06
### Added
- Group onboarding meetings
### Added in 0.3.54
- Group onboarding meetings
## [0.3.53] - 2025.01.30 ## [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 ## [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 ## [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 ## [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 ## [0.3.49] - 2025.01.09 - 36301ed238ff84df25bb11a8d44a295ee7eaf0f8
### Changed
### Changed in 0.3.49
- Make all external contact links direct to the contact-import page. - 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. - 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) ## [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 ## [0.3.47] - 2025.01.06 - 5bf6dd1ee32ca7cc46d39bd7afca58365b422f93
### Added
### Added in 0.3.47
- Notes on contacts page with new contact-edit page - Notes on contacts page with new contact-edit page
- Contact methods (only on contact-edit page and under DID details) - Contact methods (only on contact-edit page and under DID details)
- DID view with no DID shows user's info. - 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). - 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. - Extended details (eg. full claim) is beneath details link on claim page.
## [0.3.46] - 2025.01.03 - 9e7056616b5e5acc51e5a8cf7354d408029fefb3 ## [0.3.46] - 2025.01.03 - 9e7056616b5e5acc51e5a8cf7354d408029fefb3
### Added
### Added in 0.3.46
- More action-oriented questions for the gift prompts - 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 ## [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 ## [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 ## [0.3.42] - 2024.12.27 - 9751934bc24a1040415a8cfeacbae59ed91f92a5
### Added
### Added in 0.3.42
- Link from certificate page to the claim - Link from certificate page to the claim
### Changed
### Changed in 0.3.42
- Contact data sharing is now a verified JWT. - Contact data sharing is now a verified JWT.
- Feed pictures are larger. - Feed pictures are larger.
## [0.3.41] - 2024.12.21 - ff6d14138f26daea6216b051562f0a04681f69fc ## [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 ## [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 ## [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 ## [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 ## [0.3.37] - 2024.12.13 - 4d805b43cd25eed73cdd6651f36ad1ec8c109555
### Added
### Added in 0.3.37
- Record a give from a project on the project page. - Record a give from a project on the project page.
- New button on home page opens the gifted dialog. - New button on home page opens the gifted dialog.
- On confirmation buttons on the project page gives, mark when unavailable and explain why. - 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. - 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. - 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 showing claim issuer name
- Problem going "back" from a project page - Problem going "back" from a project page
## [0.3.36] - 2024.11.24 - c8d23647d165016f8a8f575e13d32583242e53ac ## [0.3.36] - 2024.11.24 - c8d23647d165016f8a8f575e13d32583242e53ac
### Changed
### Changed in 0.3.36
- More friendly default reminder message - More friendly default reminder message
- Blue borders around people to indicate clickability - Blue borders around people to indicate clickability
## [0.3.35] - 2024.11.24 - bff7d0a6320b70349185e26bfac72e3bb17f76df ## [0.3.35] - 2024.11.24 - bff7d0a6320b70349185e26bfac72e3bb17f76df
### Added
### Added in 0.3.35
- Daily reliable, hard-coded notification message - Daily reliable, hard-coded notification message
- Setting to change the partner API server - Setting to change the partner API server
## [0.3.33] - 2024.11.07 - adb7b16ecf1343c39cba71a7d6bb0e7a973e1102 ## [0.3.33] - 2024.11.07 - adb7b16ecf1343c39cba71a7d6bb0e7a973e1102
### Fixed
### Fixed in 0.3.33
- Affirm Delivery button on offer claim page didn't work. - Affirm Delivery button on offer claim page didn't work.
- Plans were not showing by default on project page. - Plans were not showing by default on project page.
## [0.3.32] - 2024.11.06 - 9a3fa38a3fd28f977e06f0265fc39e635c9c5ccd ## [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 ## [0.3.31] - 2024.10.25 - 07c02ab98a09d293dd90d9289a7872e7d681d296
### Changed
- Onboarding messages about offers
### Changed in 0.3.31
- Onboarding messages about offers
## [0.3.30] ## [0.3.30]
### Added
- Onboarding messages
### Added in 0.3.30
- Onboarding messages
## [0.3.29] - 2024.10.09 - babd3832bdfe0c40eaa3869de1b41399a51713c1 ## [0.3.29] - 2024.10.09 - babd3832bdfe0c40eaa3869de1b41399a51713c1
### Added
### Added in 0.3.29
- Invite for a contact to join immediately - Invite for a contact to join immediately
### Changed
### Changed in 0.3.29
- Send signed data to nostr endpoints to verify public key ownership. - Send signed data to nostr endpoints to verify public key ownership.
- Enhanced help & help onboarding. - Enhanced help & help onboarding.
### Changed in DB or environment ### 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 ## [0.3.28] - 2024.09.30 - 84720b94049d29cc0ddd99c50cef2e7176130133
### Added
### Added in 0.3.28
- Posting to nostr apps Trustroots & TripHopping - Posting to nostr apps Trustroots & TripHopping
- Display of providers on claim view page - 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. - 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 ## [0.3.27] - 2024.09.22 - ee23e6f005e47f5bd6f04d804599f6395371b0e4
### Fixed
### Fixed in 0.3.27
- Error loading BVC claims to confirm - Error loading BVC claims to confirm
- Really allow visibility of bulk-imported contacts - Really allow visibility of bulk-imported contacts
## [0.3.26] - 2024.09.16 - 8263ed2b29947b3ccc6f3133bbc9454c222bce28 ## [0.3.26] - 2024.09.16 - 8263ed2b29947b3ccc6f3133bbc9454c222bce28
### Added
### Added in 0.3.26
- Separate 'isRegistered' flag for each account - Separate 'isRegistered' flag for each account
### Fixed
### Fixed in 0.3.26
- Failure to assign offers to their project - Failure to assign offers to their project
- Alert when looking at one's own activity if not in contacts. - Alert when looking at one's own activity if not in contacts.
## [0.3.25] - 2024.08.30 - dcbe02d877aecb4cdef2643d90e6595d246a9f82 ## [0.3.25] - 2024.08.30 - dcbe02d877aecb4cdef2643d90e6595d246a9f82
### Added
### Added in 0.3.25
- "Ideas" now jumps directly to giving prompt or contact list. - "Ideas" now jumps directly to giving prompt or contact list.
### Fixed
### Fixed in 0.3.25
- Empty giver name on gifted-details view - Empty giver name on gifted-details view
- Previously visited project would show up on the giving-details page. - 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 ## [0.3.23] - 2024.08.30
### Added
### Added in 0.3.23
- Sections in Help for different kinds of users - Sections in Help for different kinds of users
- Discovery page parameters so that links with search text work - Discovery page parameters so that links with search text work
- Message when no projects are found - Message when no projects are found
## [0.3.21] - 2024.08.24 - a7b89f4bb6da928d56daeffaae7741fa74cc80bf ## [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. - 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. - 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 ## [0.3.20] - 2024.08.18 - 4064eb75a9743ca268bf00016fa0a5fc5dec4e30
### Fixed
### Fixed in 0.3.20
- Bad "give" verbiage on offer page - Bad "give" verbiage on offer page
- Failing offer test - Failing offer test
## [0.3.19] - 2024.08.18 - ee9c14942ceba993bf21a11249601f205158ec71 ## [0.3.19] - 2024.08.18 - ee9c14942ceba993bf21a11249601f205158ec71
### Added
### Added in 0.3.19
- Update of an offer - Update of an offer
- Recipient description in offer list - Recipient description in offer list
### Fixed
### Fixed in 0.3.19
- List of offers wasn't showing. - List of offers wasn't showing.
- Destination page after sharing photo was wrong. - Destination page after sharing photo was wrong.
## [0.3.17] - 2024.07.11 - cefa384ff1a2d922848c370640c096c529920fab ## [0.3.17] - 2024.07.11 - cefa384ff1a2d922848c370640c096c529920fab
### Added
### Added in 0.3.17
- Photos on more screens - Photos on more screens
### Fixed
### Fixed in 0.3.17
- Share of a photo, including sharing a photo from webkit/Safari which never worked - 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 ## [0.3.15] - 2024.08.04 - c8f0f2c2b16b9f0b4b47d40f7bf29058c7baa68e
### Added
### Added in 0.3.15
- Edit gives - Edit gives
- Page to edit claim JSON before submitting - Page to edit claim JSON before submitting
- Update of imported contacts - 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 - Cache signatures for reports for passkey-signed requests
- Refactor: consolidate alternative signing, eg. for passkeys & did:peer - Refactor: consolidate alternative signing, eg. for passkeys & did:peer
- Playwright tests - Playwright tests
### Changed
### Changed in 0.3.15
- Linked projects display below description (instead of at bottom) - Linked projects display below description (instead of at bottom)
### Fixed
### Fixed in 0.3.15
- Visibility toggle appearance - 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 ## [0.3.14] - 2024.06.22 - 1611d22892f683f43856d2503eee7f391b6bbce8
### Added
### Added in 0.3.14
- Clearer give-confirmation screen - Clearer give-confirmation screen
- BX currency https://thebx.medium.com/ - BX currency <https://thebx.medium.com/>
- Deselection of project on gifted details page - 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 - 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 ## [0.3.13] - 2024.05.24 - 08b67984e443c58d9178ad3776013b0bce7afddc
### Added
### Added in 0.3.13
- Photos on projects - 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 ## [0.3.12] - 2024.05.19 - 141fb39ad19c44d82fe1a33bf85115beacf50870
### Fixed
### Fixed in 0.3.12
- Photo share (share_target) failed because requests were sent to server - 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 ## [0.3.11] - 2024.05.19 - 567bcad88dfb7e9ac8fea72530d1163985e4a7cc
### Added
### Added in 0.3.11
- Choose a file for gifts, and a URL for gifts & profiles - 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 - 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 ## [0.3.10] - 2024.05.11 - 03ac31d98110f7828cf9acb366db8d01b185f64c
### Added
### Added in 0.3.10
- Share an image - Share an image
- Choose a file on the device for a profile 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 ## [0.3.9] - 2024.04.28 - 874e717e698b93a1ace9f588e675b8a3dccd7617
### Added
### Added in 0.3.9
- Offers on contacts page - Offers on contacts page
- Checks on front page until they show as registered - Checks on front page until they show as registered
### Changed
### Changed in 0.3.9
- Scanned contacts now add immediately and prompt for registration. - Scanned contacts now add immediately and prompt for registration.
- Better UI for gives on contact page - Better UI for gives on contact page
- Better UI for all confirmation messages - Better UI for all confirmation messages
### Fixed
### Fixed in 0.3.9
- Repeated elements at top of main feed - 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 ## [0.3.8] - 2024.04.20 - 15c026c80ce03a26cae3ff80b0888934c101c7e2
### Added
### Added in 0.3.8
- Profile image for user - Profile image for user
### Fixed
### Fixed in 0.3.8
- Slow loading of home page feed - 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 ## [0.3.7] - 2024.04.10 - cf18f1543a700d62a5f9e764905a4aafe1fb229b
### Added
### Added in 0.3.7
- Filter on home page feed - Filter on home page feed
- Ability to set time of daily notification - Ability to set time of daily notification
- Jump to app on click of notification - Jump to app on click of notification
### Changed
### Changed in 0.3.7
- Built with vite - Built with vite
- Descriptions on home page to include projects - 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 ## [0.3.6] - 2024.03.24 - 3a07e31d6313ab95711265562d9023c42916e141
### Added
### Added in 0.3.6
- Button to mirror photo during video - Button to mirror photo during video
- More detailed onboarding help screen - More detailed onboarding help screen
- Public-data blurb - 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 ## [0.3.5] - 2024.03.23 - 28754bdfb1e11aa221dd49a5dce4219b69cf6a9d
### Added
### Added in 0.3.5
- Photo on gift records - Photo on gift records
### Fixed
### Fixed in 0.3.5
- Environment variable for BVC meetings project - Environment variable for BVC meetings project
- Environment variables and build enhancements for test vs prod - 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 - 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 get the right default APIs.
- Test that a new browser session will send the right BVC meetings project. - Test that a new browser session will send the right BVC meetings project.
## [0.2.17] - 2024.03.01 - 3612ea42240c5e1b7d7eff29a39ff18f1b869b36 ## [0.2.17] - 2024.03.01 - 3612ea42240c5e1b7d7eff29a39ff18f1b869b36
### Added
### Added in 0.2.17
- Shortcut page for Bountiful Voluntaryist Community - Shortcut page for Bountiful Voluntaryist Community
### Changed
### Changed in 0.2.17
- More readable, targeted summaries in home-page feed items - More readable, targeted summaries in home-page feed items
### Changed in DB ### Changed in DB
- Nothing
- Nothing
## [0.2.14] - 2024.02.14 - 5f9edea1167dbfb64e16648764eed8c09b24eaeb ## [0.2.14] - 2024.02.14 - 5f9edea1167dbfb64e16648764eed8c09b24eaeb
### Changed
### Changed in 0.2.14
- Combine all service worker scripts into a single file. - 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 ## [0.2.13] - 2024.02.07
### Added
### Added in 0.2.13
- Display of user's offers - Display of user's offers
- Check for valid DIDs - Check for valid DIDs
### Fixed
### Fixed in 0.2.13
- Name display on give prompt - Name display on give prompt
- Non-numbers on number input & autocapitalize on URL input - 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 ## [0.2.12] - 2024.02.01
### Added
- Prompts for gratitude
### Added in 0.2.12
- Prompts for gratitude
## [0.2.11] - 2024.01.28 ## [0.2.11] - 2024.01.28
### Added
### Added in 0.2.11
- Actions to share claim data with contacts - Actions to share claim data with contacts
- Bulk CSV import from Endorser Mobile export - Bulk CSV import from Endorser Mobile export
- Dates on give summaries - Dates on give summaries
## [0.2.10] - 2024.01.18 - 667e1e8890b42de59cd939caca1a01c7a7a702be ## [0.2.10] - 2024.01.18 - 667e1e8890b42de59cd939caca1a01c7a7a702be
### Added
### Added in 0.2.10
- Person identicons for contacts - Person identicons for contacts
- Confirmation & delivery directly from project page - Confirmation & delivery directly from project page
- Offer dialog now allows units - Offer dialog now allows units
- Links from claim detail page to the fulfilled project or offer - Links from claim detail page to the fulfilled project or offer
- Link to project from home feed - Link to project from home feed
- Copy to clipboard in more places - 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 ## [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 ## [0.2.8] - 2024.01.14
### Added
### Added in 0.2.8
- Automatic ID creation from home page - Automatic ID creation from home page
- Agent who can also edit a project - 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 ## [0.2.7] - 2024.01.12
### Added
### Added in 0.2.7
- Give to fulfill a particular offer - Give to fulfill a particular offer
- Give as part of a trade as opposed to a donation - Give as part of a trade as opposed to a donation
- Error notifications on import - Error notifications on import
### Changed
### Changed in 0.2.7
- Library security updates - Library security updates
- Visibility of actions & confirmations on claim page - 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 ## [0.2.2] - 2024.01.05
### Added
### Added in 0.2.2
- Check for notification capability on front screen - Check for notification capability on front screen
- Contact next-public-key-hash in manual textual input - Contact next-public-key-hash in manual textual input
- Confirmation for contact visibility change - Confirmation for contact visibility change
- YAML rendering of full claim details - YAML rendering of full claim details
- Hints for onboarding on the contact screen - Hints for onboarding on the contact screen
## [0.2.0] - 2024.01.04 ## [0.2.0] - 2024.01.04
### Added
### Added in 0.2.0
- Contact next-public-key-hash - Contact next-public-key-hash
- Icon for Android - Icon for Android
- More thorough messaging and testing for notifications - More thorough messaging and testing for notifications
## [0.1.9] - 2024.01.01 ## [0.1.9] - 2024.01.01
### Added
### Added in 0.1.9
- Import for contacts and settings - Import for contacts and settings
- Second download button for DuckDuckGo - 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 ## [0.1.8] - 2023.12.27- d26d1d360152a7d0e559b68486e85b72b88bd9ff
### Added
### Added in 0.1.8
- DB logging for service-worker events - DB logging for service-worker events
- Help page for notifications - Help page for notifications
- Test notification & web-push triggers inside app - Test notification & web-push triggers inside app
- Check that the app is installed - 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 ## [0.1.7] - 2023.12.19 - 91c6c7c11c71f96006cc876fc946f1f98a274ba2
### Changed
### Changed in 0.1.7
- Icons - Icons
### Fixed
### Fixed in 0.1.7
- Notification switch now shows message - Notification switch now shows message
- Prod/test server warning message at top of page - Prod/test server warning message at top of page
## [0.1.6] - 2023.12.17 - b445b1234fbfcf6b37d695373f259aab0eda1118 ## [0.1.6] - 2023.12.17 - b445b1234fbfcf6b37d695373f259aab0eda1118
### Added
### Added in 0.1.6
- Infinite scroll on home page - Infinite scroll on home page
### Changed
### Changed in 0.1.6
- UI improvements - UI improvements
- Show web-push subscription info - Show web-push subscription info
- Icon - Icon
## [0.1.5] - 2023.12.09 - 9c36bb509a9bae9bb3306d3bd9eeb144b67aa8ad ## [0.1.5] - 2023.12.09 - 9c36bb509a9bae9bb3306d3bd9eeb144b67aa8ad
### Added
### Added in 0.1.5
- Web push notifications (though not finalized) - Web push notifications (though not finalized)
- Credentials details page - Credentials details page
- See more data without an ID - See more data without an ID
- Change units of a give - Change units of a give
## [0.1.4] - 2023.11.20 - 7311d36726f3667ec4c68f241f91d404273ad4db ## [0.1.4] - 2023.11.20 - 7311d36726f3667ec4c68f241f91d404273ad4db
### Added
### Added in 0.1.4
- Offer on a project - 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 ## [0.1.3] - 2023.11.08 - 910f57ec7d2e50803ae3d04f4b927e0f5219fbde
### Added
### Added in 0.1.3
- Contact name editing - Contact name editing
### Changed
### Changed in 0.1.3
- Don't show actions on front page if not registered. - 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 ## [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. - 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" apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies { dependencies {
implementation project(':capacitor-app')
} }

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

@ -22,6 +22,13 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </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> </activity>
<provider <provider

3
android/capacitor.settings.gradle

@ -1,3 +1,6 @@
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN // DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
include ':capacitor-android' include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor') 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: { server: {
cleartext: true, cleartext: true,
}, },
plugins: {
App: {
appUrlOpen: {
handlers: [
{
url: "timesafari://*",
autoVerify: true
}
]
}
}
}
}; };
export default config; export default config;

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> <strong>We're sorry but TimeSafari doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript> </noscript>
<div id="app"></div> <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> </body>
</html> </html>

9
ios/App/App/Info.plist

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

2
ios/App/Podfile

@ -11,7 +11,7 @@ install! 'cocoapods', :disable_input_output_paths => true
def capacitor_pods def capacitor_pods
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios' pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios' pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
end end
target 'App' do 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();
}
});

3862
package-lock.json

File diff suppressed because it is too large

36
package.json

@ -6,7 +6,7 @@
"name": "TimeSafari Team" "name": "TimeSafari Team"
}, },
"scripts": { "scripts": {
"dev": "vite", "dev": "vite --config vite.config.dev.mts",
"serve": "vite preview", "serve": "vite preview",
"build": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build", "build": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build",
"lint": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src", "lint": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src",
@ -15,23 +15,25 @@
"test-local": "npx playwright test -c playwright.config-local.ts --trace on", "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", "test-all": "npm run build && npx playwright test -c playwright.config-local.ts --trace on",
"clean:electron": "rimraf dist-electron", "clean:electron": "rimraf dist-electron",
"build:electron": "npm run clean:electron && vite build --mode electron && node scripts/build-electron.js", "build:pywebview": "vite build --config vite.config.pywebview.mts",
"build:capacitor": "vite build --mode capacitor", "build:electron": "npm run clean:electron && vite build --config vite.config.electron.mts && node scripts/build-electron.js",
"build:web": "vite build", "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:dev": "npm run build && electron dist-electron",
"electron:start": "electron dist-electron", "electron:start": "electron dist-electron",
"electron:build-linux": "electron-builder --linux AppImage", "electron:build-linux": "npm run build:electron && electron-builder --linux AppImage",
"electron:build-linux-deb": "electron-builder --linux deb", "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", "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 --config vite.config.pywebview.mts && .venv/bin/python src/pywebview/main.py",
"pywebview:dev": "vite build --mode pywebview && .venv/bin/python src/pywebview/main.py", "pywebview:build": "vite build --config vite.config.pywebview.mts && .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 --config vite.config.pywebview.mts && .venv/bin/python -m PyInstaller --name TimeSafari --add-data 'dist:www' 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 --config vite.config.pywebview.mts && .venv/Scripts/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 --config vite.config.pywebview.mts && .venv/bin/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"
}, },
"dependencies": { "dependencies": {
"@capacitor/android": "^6.2.0", "@capacitor/android": "^6.2.0",
"@capacitor/app": "^6.0.0",
"@capacitor/cli": "^6.2.0", "@capacitor/cli": "^6.2.0",
"@capacitor/core": "^6.2.0", "@capacitor/core": "^6.2.0",
"@capacitor/ios": "^6.2.0", "@capacitor/ios": "^6.2.0",
@ -64,7 +66,7 @@
"cbor-x": "^1.5.9", "cbor-x": "^1.5.9",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"dexie": "^3.2.7", "dexie": "^3.2.7",
"dexie-export-import": "^4.1.1", "dexie-export-import": "^4.1.4",
"did-jwt": "^7.4.7", "did-jwt": "^7.4.7",
"did-resolver": "^4.1.0", "did-resolver": "^4.1.0",
"ethereum-cryptography": "^2.1.3", "ethereum-cryptography": "^2.1.3",
@ -116,6 +118,7 @@
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"concurrently": "^8.2.2", "concurrently": "^8.2.2",
"electron": "^33.2.1", "electron": "^33.2.1",
"electron-builder": "^25.1.8",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1", "eslint-plugin-prettier": "^5.2.1",
@ -139,12 +142,13 @@
}, },
"files": [ "files": [
"dist-electron/**/*", "dist-electron/**/*",
"src/electron/**/*" "src/electron/**/*",
"main.js"
], ],
"extraResources": [ "extraResources": [
{ {
"from": "dist-electron/www", "from": "dist-electron",
"to": "www" "to": "."
} }
], ],
"linux": { "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]; cred = resp.data.data[0];
planCache.set(handleId, cred); planCache.set(handleId, cred);
} else { } else {
console.error( console.info(
"Failed to load plan with handle", "[EndorserServer] Plan cache is empty for handle",
handleId, handleId,
" Got data:", " Got data:",
resp.data, JSON.stringify(resp.data),
); );
} }
} catch (error) { } catch (error) {
console.error( console.error(
"Failed to load plan with handle", "[EndorserServer] Failed to load plan with handle",
handleId, handleId,
" Got error:", " Got error:",
error, JSON.stringify(error),
); );
} }
} }

68
src/main.capacitor.ts

@ -0,0 +1,68 @@
import { initializeApp } from "./main.common";
import { App } from "@capacitor/app";
import router from "./router";
import { handleApiError } from "./services/api";
console.log("[Capacitor] Starting initialization");
console.log("[Capacitor] Platform:", process.env.VITE_PLATFORM);
const app = initializeApp();
// 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");

60
src/main.common.ts

@ -0,0 +1,60 @@
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("font-awesome", 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 axios from "axios";
import VueAxios from "vue-axios"; import VueAxios from "vue-axios";
import Notifications from "notiwind"; import Notifications from "notiwind";
import "./assets/styles/tailwind.css"; import "./assets/styles/tailwind.css";
import { FontAwesomeIcon } from "./lib/fontawesome";
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 Camera from "simple-vue-camera"; import Camera from "simple-vue-camera";
// Can trigger this with a 'throw' inside some top-level function, eg. on the HomeView // 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) const app = createApp(App)
.component("fa", FontAwesomeIcon) .component("fa", FontAwesomeIcon)
.component("camera", Camera) .component("camera", Camera)
@ -209,4 +48,3 @@ const app = createApp(App)
setupGlobalErrorHandler(app); setupGlobalErrorHandler(app);
app.mount("#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");

24
src/services/api.ts

@ -0,0 +1,24 @@
import { AxiosError } from "axios";
export const handleApiError = (error: AxiosError, 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;
};

80
src/services/plan.ts

@ -0,0 +1,80 @@
import axios from "axios";
interface PlanResponse {
data?: unknown;
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: unknown) {
console.error(`[Plan Service] Error loading plan ${handle}:`, {
message: (error as Error).message,
status: (error as { response?: { status?: number } })?.response?.status,
statusText: (error as { response?: { statusText?: string } })?.response
?.statusText,
data: (error as { response?: { data?: unknown } })?.response?.data,
headers: (error as { response?: { headers?: unknown } })?.response
?.headers,
config: {
url: (error as { config?: { url?: string } })?.config?.url,
method: (error as { config?: { method?: string } })?.config?.method,
baseURL: (error as { config?: { baseURL?: string } })?.config?.baseURL,
headers: (error as { config?: { headers?: unknown } })?.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 as Error).message}`,
status: (error as { response?: { status?: number } })?.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: unknown) {
console.error(`[Plan Service] API request failed for ${handle}:`, {
endpoint,
error: (error as Error).message,
response: (error as { response?: { data?: unknown } })?.response?.data,
});
throw error;
}
};

2
src/views/AccountViewView.vue

@ -895,7 +895,7 @@ import { AxiosError } from "axios";
import { Buffer } from "buffer/"; import { Buffer } from "buffer/";
import Dexie from "dexie"; import Dexie from "dexie";
import "dexie-export-import"; import "dexie-export-import";
import { ImportProgress } from "dexie-export-import/dist/import"; import { ImportProgress } from "dexie-export-import";
import { LeafletMouseEvent } from "leaflet"; import { LeafletMouseEvent } from "leaflet";
import * as R from "ramda"; import * as R from "ramda";
import { IIdentifier } from "@veramo/core"; import { IIdentifier } from "@veramo/core";

3
src/views/ClaimView.vue

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

14
src/views/NewEditProjectView.vue

@ -229,10 +229,8 @@
import "leaflet/dist/leaflet.css"; import "leaflet/dist/leaflet.css";
import { AxiosError, AxiosRequestHeaders } from "axios"; import { AxiosError, AxiosRequestHeaders } from "axios";
import { DateTime } from "luxon"; import { DateTime } from "luxon";
import { finalizeEvent, serializeEvent } from "nostr-tools"; import { finalizeEvent } from "nostr-tools/lib/esm/index.js";
// these core imports could also be included as "import type ..." import { accountFromExtendedKey, extendedKeysFromSeedWords } from "nostr-tools/lib/esm/nip06.js";
import { EventTemplate, UnsignedEvent, VerifiedEvent } from "nostr-tools/core";
import * as nip06 from "nostr-tools/nip06";
import { Component, Vue } from "vue-facing-decorator"; import { Component, Vue } from "vue-facing-decorator";
import { LMap, LMarker, LTileLayer } from "@vue-leaflet/vue-leaflet"; import { LMap, LMarker, LTileLayer } from "@vue-leaflet/vue-leaflet";
import { RouteLocationNormalizedLoaded, Router } from "vue-router"; import { RouteLocationNormalizedLoaded, Router } from "vue-router";
@ -254,6 +252,8 @@ import {
retrieveAccountCount, retrieveAccountCount,
retrieveFullyDecryptedAccount, retrieveFullyDecryptedAccount,
} from "../libs/util"; } from "../libs/util";
import { EventTemplate, UnsignedEvent, VerifiedEvent } from "nostr-tools/lib/esm/index.js";
import { finalizeEvent as nostrToolsFinalizeEvent, serializeEvent } from "nostr-tools/lib/esm/index.js";
@Component({ @Component({
components: { ImageMethodDialog, LMap, LMarker, LTileLayer, QuickNav }, components: { ImageMethodDialog, LMap, LMarker, LTileLayer, QuickNav },
@ -668,7 +668,7 @@ export default class NewEditProjectView extends Vue {
// remove any trailing ' // remove any trailing '
const finalDerNumNoApostrophe = finalDerNum?.replace(/'/g, ""); const finalDerNumNoApostrophe = finalDerNum?.replace(/'/g, "");
const accountNum = Number(finalDerNumNoApostrophe || 0); const accountNum = Number(finalDerNumNoApostrophe || 0);
const extPubPri = nip06.extendedKeysFromSeedWords( const extPubPri = extendedKeysFromSeedWords(
account?.mnemonic as string, account?.mnemonic as string,
"", "",
accountNum, accountNum,
@ -676,7 +676,7 @@ export default class NewEditProjectView extends Vue {
const publicExtendedKey: string = extPubPri?.publicExtendedKey; const publicExtendedKey: string = extPubPri?.publicExtendedKey;
const privateExtendedKey = extPubPri?.privateExtendedKey; const privateExtendedKey = extPubPri?.privateExtendedKey;
const privateBytes: Uint8Array = const privateBytes: Uint8Array =
nip06.accountFromExtendedKey(privateExtendedKey).privateKey; accountFromExtendedKey(privateExtendedKey).privateKey;
// No real content is necessary, we just want something signed, // No real content is necessary, we just want something signed,
// so we might as well use nostr libs for nostr functions. // so we might as well use nostr libs for nostr functions.
// Besides: someday we may create real content that we can relay. // Besides: someday we may create real content that we can relay.
@ -711,7 +711,7 @@ export default class NewEditProjectView extends Vue {
const timeSafariUrl = window.location.origin + "/claim/" + jwtId; const timeSafariUrl = window.location.origin + "/claim/" + jwtId;
const content = this.fullClaim.name + " - see " + timeSafariUrl; const content = this.fullClaim.name + " - see " + timeSafariUrl;
const publicKeyHex = const publicKeyHex =
nip06.accountFromExtendedKey(publicExtendedKey).publicKey; accountFromExtendedKey(publicExtendedKey).publicKey;
const unsignedPayload: UnsignedEvent = { const unsignedPayload: UnsignedEvent = {
// why doesn't "...signedPayload" work? // why doesn't "...signedPayload" work?
kind: signedPayload.kind, kind: signedPayload.kind,

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'));

73
vite.config.common.mts

@ -0,0 +1,73 @@
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,
rollupOptions: {
external: isCapacitor ? ['@capacitor/app'] : []
}
},
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,
'nostr-tools/nip06': mode === 'development'
? 'nostr-tools/nip06'
: path.resolve(__dirname, 'node_modules/nostr-tools/nip06'),
'nostr-tools/core': mode === 'development'
? 'nostr-tools'
: path.resolve(__dirname, 'node_modules/nostr-tools'),
'nostr-tools': path.resolve(__dirname, 'node_modules/nostr-tools'),
'dexie-export-import': path.resolve(__dirname, 'node_modules/dexie-export-import')
}
},
optimizeDeps: {
include: ['nostr-tools', 'nostr-tools/nip06', 'nostr-tools/core', 'dexie-export-import'],
exclude: isElectron ? [
'register-service-worker',
'workbox-window',
'web-push',
'serviceworker-webpack-plugin'
] : []
}
};
}
export default defineConfig(async () => createBuildConfig('web'));

4
vite.config.dev.mts

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

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 * as path from "path";
import { fileURLToPath } from 'url';
import { promises as fs } from "fs"; 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 packageJson = await loadPackageJson();
const appName = process.env.TIME_SAFARI_APP_TITLE || packageJson.name; const appName = process.env.TIME_SAFARI_APP_TITLE || packageJson.name;
@ -14,6 +64,8 @@ export async function loadAppConfig() {
manifest: { manifest: {
name: appName, name: appName,
short_name: appName, short_name: appName,
theme_color: "#4a90e2",
background_color: "#ffffff",
icons: [ icons: [
{ {
src: "./img/icons/android-chrome-192x192.png", src: "./img/icons/android-chrome-192x192.png",
@ -54,16 +106,10 @@ export async function loadAppConfig() {
}, },
}, },
aliasConfig: { aliasConfig: {
"@": path.resolve(__dirname, ".."), "@": path.resolve(__dirname, "src"),
buffer: path.resolve(__dirname, "node_modules", "buffer"), buffer: path.resolve(__dirname, "node_modules", "buffer"),
"dexie-export-import/dist/import": "dexie-export-import/dist/import":
"dexie-export-import/dist/import/index.js", "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