forked from jsnbuchanan/crowd-funder-for-time-pwa
Merge branch 'deep_linking'
This commit is contained in:
6
.env.example
Normal file
6
.env.example
Normal file
@@ -0,0 +1,6 @@
|
||||
# Admin DID credentials
|
||||
ADMIN_DID=did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F
|
||||
ADMIN_PRIVATE_KEY=2b6472c026ec2aa2c4235c994a63868fc9212d18b58f6cbfe861b52e71330f5b
|
||||
|
||||
# API Configuration
|
||||
ENDORSER_API_URL=https://test-api.endorser.ch/api/v2/claim
|
||||
25
.eslintrc.js
25
.eslintrc.js
@@ -5,30 +5,27 @@ module.exports = {
|
||||
es2022: true,
|
||||
},
|
||||
extends: [
|
||||
"plugin:vue/vue3-essential",
|
||||
"plugin:vue/vue3-recommended",
|
||||
"eslint:recommended",
|
||||
"@vue/typescript/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
// parserOptions: {
|
||||
// ecmaVersion: 2020,
|
||||
// },
|
||||
rules: {
|
||||
"max-len": [
|
||||
"warn",
|
||||
{
|
||||
code: 120,
|
||||
ignoreComments: true, // why does this not make it allow comment of any length?
|
||||
"max-len": ["warn", {
|
||||
code: 100,
|
||||
ignoreComments: true,
|
||||
ignorePattern: '^\\s*class="[^"]*"$',
|
||||
ignoreStrings: true,
|
||||
ignoreTemplateLiterals: true,
|
||||
ignoreTrailingComments: true,
|
||||
ignoreUrls: true,
|
||||
},
|
||||
],
|
||||
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||
// "prettier/prettier": ["warn", { printWidth: 120 }], // removes errors but adds thousands of warnings
|
||||
"@typescript-eslint/no-unnecessary-type-constraint": "off",
|
||||
}],
|
||||
"no-console": process.env.NODE_ENV === "production" ? "error" : "warn",
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "warn",
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/no-unnecessary-type-constraint": "off"
|
||||
},
|
||||
};
|
||||
|
||||
28
.gitignore
vendored
28
.gitignore
vendored
@@ -37,3 +37,31 @@ pnpm-debug.log*
|
||||
/dist-electron-build/
|
||||
/dist-capacitor/
|
||||
/test-playwright-results/
|
||||
playwright-tests
|
||||
test-playwright
|
||||
dist-electron-packages
|
||||
ios
|
||||
.ruby-version
|
||||
+.env
|
||||
|
||||
# Generated test files
|
||||
.generated/
|
||||
|
||||
# Fastlane
|
||||
ios/fastlane/report.xml
|
||||
ios/fastlane/Preview.html
|
||||
ios/fastlane/screenshots
|
||||
ios/fastlane/test_output
|
||||
android/fastlane/report.xml
|
||||
android/fastlane/Preview.html
|
||||
android/fastlane/screenshots
|
||||
android/fastlane/test_output
|
||||
.env.default
|
||||
vendor/
|
||||
|
||||
# Build logs
|
||||
build_logs/
|
||||
|
||||
# Android generated assets
|
||||
android/app/src/main/assets/public/assets/
|
||||
|
||||
|
||||
164
BUILDING.md
164
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,68 +103,155 @@ 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
|
||||
npm run build:capacitor
|
||||
```
|
||||
|
||||
2. Add iOS platform if not already added:
|
||||
```bash
|
||||
npx cap add ios
|
||||
```
|
||||
2. Update iOS project with latest build:
|
||||
|
||||
3. Update iOS project with latest build:
|
||||
```bash
|
||||
npx cap sync ios
|
||||
```
|
||||
|
||||
4. Open the project in Xcode:
|
||||
3. Open the project in Xcode:
|
||||
|
||||
```bash
|
||||
npx cap open ios
|
||||
```
|
||||
|
||||
5. Use Xcode to build and run on simulator or device.
|
||||
4. Use Xcode to build and run on simulator or device.
|
||||
|
||||
If you have forked this to make your own app, you'll want to customize the ios files:
|
||||
|
||||
```bash
|
||||
rm -rf ios
|
||||
npx cap add ios
|
||||
```
|
||||
|
||||
... and then repeat the steps above.
|
||||
|
||||
|
||||
### Android Build
|
||||
|
||||
Prerequisites: Android Studio with SDK installed
|
||||
|
||||
1. Build the web assets:
|
||||
|
||||
```bash
|
||||
npm run build -- --mode capacitor
|
||||
rm -rf dist
|
||||
npm run build:web
|
||||
npm run build:capacitor
|
||||
```
|
||||
|
||||
2. Add Android platform if not already added:
|
||||
```bash
|
||||
npx cap add android
|
||||
```
|
||||
2. Update Android project with latest build:
|
||||
|
||||
3. Update Android project with latest build:
|
||||
```bash
|
||||
npx cap sync android
|
||||
```
|
||||
|
||||
4. Open the project in Android Studio:
|
||||
3. Open the project in Android Studio:
|
||||
|
||||
```bash
|
||||
npx cap open android
|
||||
```
|
||||
|
||||
5. Use Android Studio to build and run on emulator or device.
|
||||
3. Use Android Studio to build and run on emulator or device.
|
||||
|
||||
## Development
|
||||
If you have forked this to make your own app, you'll want to customize the android files:
|
||||
|
||||
To run the application in development mode:
|
||||
|
||||
1. Start the development server:
|
||||
```bash
|
||||
npm run dev
|
||||
rm -rf android
|
||||
npx cap add android
|
||||
```
|
||||
|
||||
... and then: repeat the steps above, and look below for the deep link configuration.
|
||||
|
||||
## Building Android from the console
|
||||
|
||||
```bash
|
||||
cd android
|
||||
./gradlew clean
|
||||
./gradlew build -Dlint.baselines.continue=true
|
||||
cd ..
|
||||
npx cap run android
|
||||
```
|
||||
|
||||
## Configuring Android for deep links
|
||||
|
||||
You must add the following intent filter to the `android/app/src/main/AndroidManifest.xml` file:
|
||||
|
||||
```xml
|
||||
<intent-filter android:autoVerify="true">
|
||||
<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>
|
||||
```
|
||||
|
||||
You must also add the following to the `android/app/build.gradle` file:
|
||||
|
||||
```gradle
|
||||
android {
|
||||
// ... existing config ...
|
||||
|
||||
lintOptions {
|
||||
disable 'UnsanitizedFilenameFromContentProvider'
|
||||
abortOnError false
|
||||
baseline file("lint-baseline.xml")
|
||||
|
||||
// Ignore Capacitor module issues
|
||||
ignore 'DefaultLocale'
|
||||
ignore 'UnsanitizedFilenameFromContentProvider'
|
||||
ignore 'LintBaseline'
|
||||
ignore 'LintBaselineFixed'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Modify `/android/build.gradle` to use a stable version of AGP and make sure Kotlin version is compatible.
|
||||
|
||||
```gradle
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
// Use a stable version of AGP
|
||||
classpath 'com.android.tools.build:gradle:8.1.0'
|
||||
|
||||
// Make sure Kotlin version is compatible
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
// Add this to handle version conflicts
|
||||
configurations.all {
|
||||
resolutionStrategy {
|
||||
force 'org.jetbrains.kotlin:kotlin-stdlib:1.8.0'
|
||||
force 'org.jetbrains.kotlin:kotlin-stdlib-common:1.8.0'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## PyWebView Desktop Build
|
||||
|
||||
### Prerequisites
|
||||
### 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
|
||||
@@ -173,7 +268,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 +279,7 @@ To run the application in development mode:
|
||||
```
|
||||
|
||||
2. Install Python dependencies:
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
@@ -189,13 +287,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,26 +304,33 @@ 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
|
||||
|
||||
```bash
|
||||
npm run pywebview:package-mac
|
||||
```
|
||||
|
||||
The packaged application will be in `dist/TimeSafari`
|
||||
|
||||
## Testing
|
||||
|
||||
Run local tests:
|
||||
|
||||
```bash
|
||||
npm run test-local
|
||||
```
|
||||
@@ -232,11 +340,13 @@ See [TESTING.md](test-playwright/TESTING.md) for more details.
|
||||
## Linting
|
||||
|
||||
Check code style:
|
||||
|
||||
```bash
|
||||
npm run lint
|
||||
```
|
||||
|
||||
Fix code style issues:
|
||||
|
||||
```bash
|
||||
npm run lint-fix
|
||||
```
|
||||
@@ -257,16 +367,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
|
||||
@@ -276,6 +390,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
|
||||
@@ -290,7 +405,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
|
||||
|
||||
@@ -307,4 +422,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
|
||||
|
||||
|
||||
587
CHANGELOG.md
587
CHANGELOG.md
@@ -15,269 +15,347 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
|
||||
## [0.4.4] - 2025.02.17
|
||||
### Fixed
|
||||
|
||||
### Fixed in 0.4.4
|
||||
|
||||
- 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
|
||||
|
||||
### 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
|
||||
|
||||
|
||||
## [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
|
||||
|
||||
### Added in 0.3.57
|
||||
|
||||
- Automatic user creation in onboarding meetings
|
||||
|
||||
|
||||
## [0.3.55] - 2025.02.07
|
||||
### Added
|
||||
|
||||
### Added in 0.3.55
|
||||
|
||||
- End time for projects
|
||||
|
||||
|
||||
## [0.3.54] - 2025.02.06
|
||||
### Added
|
||||
|
||||
### Added in 0.3.54
|
||||
|
||||
- Group onboarding meetings
|
||||
|
||||
|
||||
## [0.3.53] - 2025.01.30
|
||||
### Added
|
||||
|
||||
### Added in 0.3.53
|
||||
|
||||
- Hints for contacting the creator of a project
|
||||
|
||||
|
||||
## [0.3.52] - 2025.01.22
|
||||
### Fixed
|
||||
|
||||
### Fixed in 0.3.52
|
||||
|
||||
- User profile endpoint server for map was broken.
|
||||
|
||||
|
||||
## [0.3.51] - 2025.01.22
|
||||
### Fixed
|
||||
|
||||
### Fixed in 0.3.51
|
||||
|
||||
- User profile map jumped on first zoom.
|
||||
|
||||
|
||||
## [0.3.50] - 2025.01.20 - b9fedcd3fd3e34c3fb0fc79150d1a81a76eaeb40
|
||||
### Added
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
|
||||
### Fixed in 0.3.46
|
||||
|
||||
- Contact-list import set visibility for all, even if not chosen.
|
||||
|
||||
|
||||
## [0.3.45] - 2025.01.01 - 65402dc68ce69ccc6cb9aa8d2e7a9249bf4298e0
|
||||
### Fixed
|
||||
|
||||
### Fixed in 0.3.45
|
||||
|
||||
- Previous project links stayed when following a link.
|
||||
|
||||
|
||||
## [0.3.44] - 2024.12.31 - 694b22987b05482e4527c2478bbe15e6b6f3b532
|
||||
### Added
|
||||
|
||||
### 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
|
||||
|
||||
### Added in 0.3.41
|
||||
|
||||
- Link from certificate page to the claim
|
||||
|
||||
|
||||
## [0.3.40] - 2024.12.20 - 77290d9fed3c364243793dc3e9bfe2e994a016b8
|
||||
### Added
|
||||
|
||||
### Added in 0.3.40
|
||||
|
||||
- Only show issuer on certificate if it's not the agent.
|
||||
|
||||
|
||||
## [0.3.39] - 2024.12.20 - d8819155e2acd2b57fdab523168fa5d1d09e80cc
|
||||
### Added
|
||||
|
||||
### Added in 0.3.39
|
||||
|
||||
- Page for a framed claim certificate
|
||||
|
||||
|
||||
## [0.3.38] - 2024.12.14 - f8cae5ad4fee1f114320dcce052299eab12108b2
|
||||
### Fixed
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
|
||||
### Changed in 0.3.31
|
||||
|
||||
- Onboarding messages about offers
|
||||
|
||||
|
||||
## [0.3.30]
|
||||
### Added
|
||||
|
||||
### 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
|
||||
|
||||
|
||||
## [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
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
@@ -288,263 +366,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
|
||||
- Linked projects display below description (instead of at bottom)
|
||||
### Fixed
|
||||
- Visibility toggle appearance
|
||||
### Changed in DB or environment
|
||||
- Nothing
|
||||
|
||||
### Changed in 0.3.15
|
||||
|
||||
- Linked projects display below description (instead of at bottom)
|
||||
|
||||
### Fixed in 0.3.15
|
||||
|
||||
- Visibility toggle appearance
|
||||
|
||||
### Changed in DB or environment in 0.3.15
|
||||
|
||||
- Nothing
|
||||
|
||||
## [0.3.14] - 2024.06.22 - 1611d22892f683f43856d2503eee7f391b6bbce8
|
||||
### Added
|
||||
- Clearer give-confirmation screen
|
||||
- BX currency https://thebx.medium.com/
|
||||
- Deselection of project on gifted details page
|
||||
### Fixed
|
||||
- Don't show registration pop-up for a new contact that is registered
|
||||
### Changed in DB or environment
|
||||
- Nothing
|
||||
|
||||
### Added in 0.3.14
|
||||
|
||||
- Clearer give-confirmation screen
|
||||
- BX currency <https://thebx.medium.com/>
|
||||
- Deselection of project on gifted details page
|
||||
|
||||
### Fixed in 0.3.14
|
||||
|
||||
- Don't show registration pop-up for a new contact that is registered
|
||||
|
||||
### Changed in DB or environment in 0.3.14
|
||||
|
||||
- Nothing
|
||||
|
||||
## [0.3.13] - 2024.05.24 - 08b67984e443c58d9178ad3776013b0bce7afddc
|
||||
### Added
|
||||
- Photos on projects
|
||||
### Changed in DB or environment
|
||||
- Nothing
|
||||
|
||||
### Added in 0.3.13
|
||||
|
||||
- Photos on projects
|
||||
|
||||
### Changed in DB or environment in 0.3.13
|
||||
|
||||
- Nothing
|
||||
|
||||
## [0.3.12] - 2024.05.19 - 141fb39ad19c44d82fe1a33bf85115beacf50870
|
||||
### Fixed
|
||||
- Photo share (share_target) failed because requests were sent to server
|
||||
### Changed in DB or environment
|
||||
- Nothing
|
||||
|
||||
### Fixed in 0.3.12
|
||||
|
||||
- Photo share (share_target) failed because requests were sent to server
|
||||
|
||||
### Changed in DB or environment in 0.3.12
|
||||
|
||||
- Nothing
|
||||
|
||||
## [0.3.11] - 2024.05.19 - 567bcad88dfb7e9ac8fea72530d1163985e4a7cc
|
||||
### Added
|
||||
- Choose a file for gifts, and a URL for gifts & profiles
|
||||
### Fixed
|
||||
- Multiple button pushes were required to switch camera
|
||||
### Changed in DB or environment
|
||||
- Nothing
|
||||
|
||||
### Added in 0.3.11
|
||||
|
||||
- Choose a file for gifts, and a URL for gifts & profiles
|
||||
|
||||
### Fixed in 0.3.11
|
||||
|
||||
- Multiple button pushes were required to switch camera
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
- Repeated elements at top of main feed
|
||||
### Changed in DB or environment
|
||||
- Nothing
|
||||
|
||||
### Fixed in 0.3.9
|
||||
|
||||
- Repeated elements at top of main feed
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
- Shortcut page for Bountiful Voluntaryist Community
|
||||
### Changed
|
||||
- More readable, targeted summaries in home-page feed items
|
||||
### Changed in DB
|
||||
- Nothing
|
||||
|
||||
### Added in 0.2.17
|
||||
|
||||
- Shortcut page for Bountiful Voluntaryist Community
|
||||
|
||||
### Changed in 0.2.17
|
||||
|
||||
- More readable, targeted summaries in home-page feed items
|
||||
|
||||
### Changed in DB
|
||||
|
||||
- Nothing
|
||||
|
||||
## [0.2.14] - 2024.02.14 - 5f9edea1167dbfb64e16648764eed8c09b24eaeb
|
||||
### Changed
|
||||
- Combine all service worker scripts into a single file.
|
||||
### Changed in DB
|
||||
- Nothing
|
||||
|
||||
### Changed in 0.2.14
|
||||
|
||||
- Combine all service worker scripts into a single file.
|
||||
|
||||
### 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
|
||||
|
||||
### Changed in DB in 0.2.13
|
||||
|
||||
- Nothing
|
||||
|
||||
|
||||
## [0.2.12] - 2024.02.01
|
||||
### Added
|
||||
|
||||
### 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
|
||||
|
||||
### Fixed in 0.2.10
|
||||
|
||||
- "More Contacts" for give on project page now links correctly.
|
||||
|
||||
|
||||
## [0.2.9] - 2024.01.15 - e5e702f8a5a53a6efbed48d35f0bc3cee63024a0
|
||||
### Fixed
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
|
||||
### 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
|
||||
|
||||
### 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.
|
||||
|
||||
5
Gemfile
Normal file
5
Gemfile
Normal file
@@ -0,0 +1,5 @@
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "fastlane"
|
||||
gem "cocoapods"
|
||||
|
||||
321
Gemfile.lock
Normal file
321
Gemfile.lock
Normal file
@@ -0,0 +1,321 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.7)
|
||||
base64
|
||||
nkf
|
||||
rexml
|
||||
activesupport (7.2.2.1)
|
||||
base64
|
||||
benchmark (>= 0.3)
|
||||
bigdecimal
|
||||
concurrent-ruby (~> 1.0, >= 1.3.1)
|
||||
connection_pool (>= 2.2.5)
|
||||
drb
|
||||
i18n (>= 1.6, < 2)
|
||||
logger (>= 1.4.2)
|
||||
minitest (>= 5.1)
|
||||
securerandom (>= 0.3)
|
||||
tzinfo (~> 2.0, >= 2.0.5)
|
||||
addressable (2.8.7)
|
||||
public_suffix (>= 2.0.2, < 7.0)
|
||||
algoliasearch (1.27.5)
|
||||
httpclient (~> 2.8, >= 2.8.3)
|
||||
json (>= 1.5.1)
|
||||
artifactory (3.0.17)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.3.2)
|
||||
aws-partitions (1.1066.0)
|
||||
aws-sdk-core (3.220.1)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.992.0)
|
||||
aws-sigv4 (~> 1.9)
|
||||
base64
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
aws-sdk-kms (1.99.0)
|
||||
aws-sdk-core (~> 3, >= 3.216.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-s3 (1.182.0)
|
||||
aws-sdk-core (~> 3, >= 3.216.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sigv4 (1.11.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
base64 (0.2.0)
|
||||
benchmark (0.4.0)
|
||||
bigdecimal (3.1.9)
|
||||
claide (1.1.0)
|
||||
cocoapods (1.16.2)
|
||||
addressable (~> 2.8)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
cocoapods-core (= 1.16.2)
|
||||
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||
cocoapods-downloader (>= 2.1, < 3.0)
|
||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||
cocoapods-search (>= 1.0.0, < 2.0)
|
||||
cocoapods-trunk (>= 1.6.0, < 2.0)
|
||||
cocoapods-try (>= 1.1.0, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
escape (~> 0.0.4)
|
||||
fourflusher (>= 2.3.0, < 3.0)
|
||||
gh_inspector (~> 1.0)
|
||||
molinillo (~> 0.8.0)
|
||||
nap (~> 1.0)
|
||||
ruby-macho (>= 2.3.0, < 3.0)
|
||||
xcodeproj (>= 1.27.0, < 2.0)
|
||||
cocoapods-core (1.16.2)
|
||||
activesupport (>= 5.0, < 8)
|
||||
addressable (~> 2.8)
|
||||
algoliasearch (~> 1.0)
|
||||
concurrent-ruby (~> 1.1)
|
||||
fuzzy_match (~> 2.0.4)
|
||||
nap (~> 1.0)
|
||||
netrc (~> 0.11)
|
||||
public_suffix (~> 4.0)
|
||||
typhoeus (~> 1.0)
|
||||
cocoapods-deintegrate (1.0.5)
|
||||
cocoapods-downloader (2.1)
|
||||
cocoapods-plugins (1.0.0)
|
||||
nap
|
||||
cocoapods-search (1.0.1)
|
||||
cocoapods-trunk (1.6.0)
|
||||
nap (>= 0.8, < 2.0)
|
||||
netrc (~> 0.11)
|
||||
cocoapods-try (1.2.0)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander (4.6.0)
|
||||
highline (~> 2.0.0)
|
||||
concurrent-ruby (1.3.5)
|
||||
connection_pool (2.5.0)
|
||||
declarative (0.0.20)
|
||||
digest-crc (0.7.0)
|
||||
rake (>= 12.0.0, < 14.0.0)
|
||||
domain_name (0.6.20240107)
|
||||
dotenv (2.8.1)
|
||||
drb (2.2.1)
|
||||
emoji_regex (3.2.3)
|
||||
escape (0.0.4)
|
||||
ethon (0.16.0)
|
||||
ffi (>= 1.15.0)
|
||||
excon (0.112.0)
|
||||
faraday (1.10.4)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-httpclient (~> 1.0)
|
||||
faraday-multipart (~> 1.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.0)
|
||||
faraday-patron (~> 1.0)
|
||||
faraday-rack (~> 1.0)
|
||||
faraday-retry (~> 1.0)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-cookie_jar (0.0.7)
|
||||
faraday (>= 0.8.0)
|
||||
http-cookie (~> 1.0.0)
|
||||
faraday-em_http (1.0.0)
|
||||
faraday-em_synchrony (1.0.0)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-multipart (1.1.0)
|
||||
multipart-post (~> 2.0)
|
||||
faraday-net_http (1.0.2)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
faraday-rack (1.0.0)
|
||||
faraday-retry (1.0.3)
|
||||
faraday_middleware (1.2.1)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.4.0)
|
||||
fastlane (2.227.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.8, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
aws-sdk-s3 (~> 1.0)
|
||||
babosa (>= 1.0.3, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 3.0.0)
|
||||
colored (~> 1.2)
|
||||
commander (~> 4.6)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (>= 0.1, < 4.0)
|
||||
excon (>= 0.71.0, < 1.0.0)
|
||||
faraday (~> 1.0)
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 1.0)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
fastlane-sirp (>= 1.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-apis-androidpublisher_v3 (~> 0.3)
|
||||
google-apis-playcustomapp_v1 (~> 0.1)
|
||||
google-cloud-env (>= 1.6.0, < 2.0.0)
|
||||
google-cloud-storage (~> 1.31)
|
||||
highline (~> 2.0)
|
||||
http-cookie (~> 1.0.5)
|
||||
json (< 3.0.0)
|
||||
jwt (>= 2.1.0, < 3)
|
||||
mini_magick (>= 4.9.4, < 5.0.0)
|
||||
multipart-post (>= 2.0.0, < 3.0.0)
|
||||
naturally (~> 2.2)
|
||||
optparse (>= 0.1.1, < 1.0.0)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
rubyzip (>= 2.0.0, < 3.0.0)
|
||||
security (= 0.1.5)
|
||||
simctl (~> 1.6.3)
|
||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-table (~> 3)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||
xcpretty (~> 0.4.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
|
||||
fastlane-sirp (1.0.0)
|
||||
sysrandom (~> 1.0)
|
||||
ffi (1.17.1)
|
||||
ffi (1.17.1-aarch64-linux-gnu)
|
||||
ffi (1.17.1-aarch64-linux-musl)
|
||||
ffi (1.17.1-arm-linux-gnu)
|
||||
ffi (1.17.1-arm-linux-musl)
|
||||
ffi (1.17.1-arm64-darwin)
|
||||
ffi (1.17.1-x86-linux-gnu)
|
||||
ffi (1.17.1-x86-linux-musl)
|
||||
ffi (1.17.1-x86_64-darwin)
|
||||
ffi (1.17.1-x86_64-linux-gnu)
|
||||
ffi (1.17.1-x86_64-linux-musl)
|
||||
fourflusher (2.3.1)
|
||||
fuzzy_match (2.0.4)
|
||||
gh_inspector (1.1.3)
|
||||
google-apis-androidpublisher_v3 (0.54.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-core (0.11.3)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
httpclient (>= 2.8.1, < 3.a)
|
||||
mini_mime (~> 1.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.a)
|
||||
rexml
|
||||
google-apis-iamcredentials_v1 (0.17.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-playcustomapp_v1 (0.13.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-storage_v1 (0.31.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-cloud-core (1.8.0)
|
||||
google-cloud-env (>= 1.0, < 3.a)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-env (1.6.0)
|
||||
faraday (>= 0.17.3, < 3.0)
|
||||
google-cloud-errors (1.5.0)
|
||||
google-cloud-storage (1.47.0)
|
||||
addressable (~> 2.8)
|
||||
digest-crc (~> 0.4)
|
||||
google-apis-iamcredentials_v1 (~> 0.1)
|
||||
google-apis-storage_v1 (~> 0.31.0)
|
||||
google-cloud-core (~> 1.6)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (1.8.1)
|
||||
faraday (>= 0.17.3, < 3.a)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (>= 0.16, < 2.a)
|
||||
highline (2.0.3)
|
||||
http-cookie (1.0.8)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.9.0)
|
||||
mutex_m
|
||||
i18n (1.14.7)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jmespath (1.6.2)
|
||||
json (2.10.2)
|
||||
jwt (2.10.1)
|
||||
base64
|
||||
logger (1.6.6)
|
||||
mini_magick (4.13.2)
|
||||
mini_mime (1.1.5)
|
||||
minitest (5.25.5)
|
||||
molinillo (0.8.0)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.4.1)
|
||||
mutex_m (0.3.0)
|
||||
nanaimo (0.4.0)
|
||||
nap (1.1.0)
|
||||
naturally (2.2.1)
|
||||
netrc (0.11.0)
|
||||
nkf (0.2.0)
|
||||
optparse (0.6.0)
|
||||
os (1.1.4)
|
||||
plist (3.7.2)
|
||||
public_suffix (4.0.7)
|
||||
rake (13.2.1)
|
||||
representable (3.2.0)
|
||||
declarative (< 0.1.0)
|
||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rexml (3.4.1)
|
||||
rouge (3.28.0)
|
||||
ruby-macho (2.5.1)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.4.1)
|
||||
securerandom (0.4.1)
|
||||
security (0.1.5)
|
||||
signet (0.19.0)
|
||||
addressable (~> 2.8)
|
||||
faraday (>= 0.17.5, < 3.a)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.10)
|
||||
CFPropertyList
|
||||
naturally
|
||||
sysrandom (1.0.5)
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (3.0.2)
|
||||
unicode-display_width (>= 1.1.1, < 3)
|
||||
trailblazer-option (0.1.2)
|
||||
tty-cursor (0.7.1)
|
||||
tty-screen (0.8.2)
|
||||
tty-spinner (0.9.3)
|
||||
tty-cursor (~> 0.7)
|
||||
typhoeus (1.4.1)
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
uber (0.1.0)
|
||||
unicode-display_width (2.6.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.27.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.4.0)
|
||||
rexml (>= 3.3.6, < 4.0)
|
||||
xcpretty (0.4.0)
|
||||
rouge (~> 3.28.0)
|
||||
xcpretty-travis-formatter (1.0.1)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
||||
PLATFORMS
|
||||
aarch64-linux-gnu
|
||||
aarch64-linux-musl
|
||||
arm-linux-gnu
|
||||
arm-linux-musl
|
||||
arm64-darwin
|
||||
ruby
|
||||
x86-linux-gnu
|
||||
x86-linux-musl
|
||||
x86_64-darwin
|
||||
x86_64-linux-gnu
|
||||
x86_64-linux-musl
|
||||
|
||||
DEPENDENCIES
|
||||
cocoapods
|
||||
fastlane
|
||||
|
||||
BUNDLED WITH
|
||||
2.6.5
|
||||
25
README.md
25
README.md
@@ -8,8 +8,6 @@ and expand to crowd-fund with time & money, then record and see the impact of co
|
||||
See [project.task.yaml](project.task.yaml) for current priorities.
|
||||
(Numbers at the beginning of lines are estimated hours. See [taskyaml.org](https://taskyaml.org/) for details.)
|
||||
|
||||
|
||||
|
||||
## Setup & Building
|
||||
|
||||
Quick start:
|
||||
@@ -29,7 +27,6 @@ See the test locations for "IMAGE_API_SERVER" or "PARTNER_API_SERVER" below, or
|
||||
Look at [BUILDING.md](BUILDING.md) for the "test-all" instructions and [TESTING.md](test-playwright/TESTING.md) for more details.
|
||||
|
||||
|
||||
|
||||
### Compile and minify for test & production
|
||||
|
||||
* If there are DB changes: before updating the test server, open browser(s) with current version to test DB migrations.
|
||||
@@ -46,15 +43,19 @@ Look at [BUILDING.md](BUILDING.md) for the "test-all" instructions and [TESTING.
|
||||
|
||||
* For test, build the app (because test server is not yet set up to build):
|
||||
|
||||
```
|
||||
```bash
|
||||
TIME_SAFARI_APP_TITLE="TimeSafari_Test" VITE_APP_SERVER=https://test.timesafari.app VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app VITE_DEFAULT_PARTNER_API_SERVER=https://test-partner-api.endorser.ch VITE_PASSKEYS_ENABLED=true npm run build
|
||||
```
|
||||
|
||||
... and transfer to the test server: `rsync -azvu -e "ssh -i ~/.ssh/..." dist ubuntutest@test.timesafari.app:time-safari`
|
||||
... and transfer to the test server:
|
||||
|
||||
(Let's replace that with a .env.development or .env.staging file.)
|
||||
```bash
|
||||
rsync -azvu -e "ssh -i ~/.ssh/..." dist ubuntutest@test.timesafari.app:time-safari
|
||||
```
|
||||
|
||||
(Note: The test BVC_MEETUPS_PROJECT_CLAIM_ID does not resolve as a URL because it's only in the test DB and the prod redirect won't redirect there.)
|
||||
(Let's replace that with a .env.development or .env.staging file.)
|
||||
|
||||
(Note: The test BVC_MEETUPS_PROJECT_CLAIM_ID does not resolve as a URL because it's only in the test DB and the prod redirect won't redirect there.)
|
||||
|
||||
* For prod, get on the server and run the correct build:
|
||||
|
||||
@@ -74,12 +75,17 @@ TIME_SAFARI_APP_TITLE="TimeSafari_Test" VITE_APP_SERVER=https://test.timesafari.
|
||||
|
||||
|
||||
|
||||
## Tests
|
||||
|
||||
See [TESTING.md](test-playwright/TESTING.md) for detailed test instructions.
|
||||
|
||||
|
||||
|
||||
|
||||
## Icons
|
||||
|
||||
To add an icon, add to main.ts and reference with `fa` element and `icon` attribute with the hyphenated name.
|
||||
|
||||
|
||||
|
||||
## Other
|
||||
|
||||
### Reference Material
|
||||
@@ -91,7 +97,6 @@ To add an icon, add to main.ts and reference with `fa` element and `icon` attrib
|
||||
|
||||
* If you are deploying in a subdirectory, add it to `publicPath` in vue.config.js, eg: `publicPath: "/app/time-tracker/",`
|
||||
|
||||
|
||||
### Kudos
|
||||
|
||||
Gifts make the world go 'round!
|
||||
|
||||
BIN
android/.gradle/8.11.1/checksums/checksums.lock
Normal file
BIN
android/.gradle/8.11.1/checksums/checksums.lock
Normal file
Binary file not shown.
BIN
android/.gradle/8.11.1/checksums/md5-checksums.bin
Normal file
BIN
android/.gradle/8.11.1/checksums/md5-checksums.bin
Normal file
Binary file not shown.
BIN
android/.gradle/8.11.1/checksums/sha1-checksums.bin
Normal file
BIN
android/.gradle/8.11.1/checksums/sha1-checksums.bin
Normal file
Binary file not shown.
BIN
android/.gradle/8.11.1/executionHistory/executionHistory.bin
Normal file
BIN
android/.gradle/8.11.1/executionHistory/executionHistory.bin
Normal file
Binary file not shown.
BIN
android/.gradle/8.11.1/executionHistory/executionHistory.lock
Normal file
BIN
android/.gradle/8.11.1/executionHistory/executionHistory.lock
Normal file
Binary file not shown.
BIN
android/.gradle/8.11.1/fileChanges/last-build.bin
Normal file
BIN
android/.gradle/8.11.1/fileChanges/last-build.bin
Normal file
Binary file not shown.
BIN
android/.gradle/8.11.1/fileHashes/fileHashes.bin
Normal file
BIN
android/.gradle/8.11.1/fileHashes/fileHashes.bin
Normal file
Binary file not shown.
BIN
android/.gradle/8.11.1/fileHashes/fileHashes.lock
Normal file
BIN
android/.gradle/8.11.1/fileHashes/fileHashes.lock
Normal file
Binary file not shown.
BIN
android/.gradle/8.11.1/fileHashes/resourceHashesCache.bin
Normal file
BIN
android/.gradle/8.11.1/fileHashes/resourceHashesCache.bin
Normal file
Binary file not shown.
0
android/.gradle/8.11.1/gc.properties
Normal file
0
android/.gradle/8.11.1/gc.properties
Normal file
BIN
android/.gradle/buildOutputCleanup/buildOutputCleanup.lock
Normal file
BIN
android/.gradle/buildOutputCleanup/buildOutputCleanup.lock
Normal file
Binary file not shown.
2
android/.gradle/buildOutputCleanup/cache.properties
Normal file
2
android/.gradle/buildOutputCleanup/cache.properties
Normal file
@@ -0,0 +1,2 @@
|
||||
#Tue Mar 11 10:01:05 UTC 2025
|
||||
gradle.version=8.10.2
|
||||
BIN
android/.gradle/file-system.probe
Normal file
BIN
android/.gradle/file-system.probe
Normal file
Binary file not shown.
0
android/.gradle/vcs-1/gc.properties
Normal file
0
android/.gradle/vcs-1/gc.properties
Normal file
3
android/Gemfile
Normal file
3
android/Gemfile
Normal file
@@ -0,0 +1,3 @@
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "fastlane"
|
||||
1
android/app/.gitignore
vendored
1
android/app/.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
/build/*
|
||||
!/build/.npmkeep
|
||||
src/main/assets/public/assets/
|
||||
@@ -1,10 +1,10 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
namespace "app.timesafari.app"
|
||||
namespace "app.timesafari"
|
||||
compileSdk rootProject.ext.compileSdkVersion
|
||||
defaultConfig {
|
||||
applicationId "app.timesafari.app"
|
||||
applicationId "app.timesafari"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
@@ -22,6 +22,17 @@ android {
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
lintOptions {
|
||||
disable 'UnsanitizedFilenameFromContentProvider'
|
||||
abortOnError false
|
||||
baseline file("lint-baseline.xml")
|
||||
|
||||
// Ignore Capacitor module issues
|
||||
ignore 'DefaultLocale'
|
||||
ignore 'UnsanitizedFilenameFromContentProvider'
|
||||
ignore 'LintBaseline'
|
||||
ignore 'LintBaselineFixed'
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
||||
@@ -9,7 +9,7 @@ android {
|
||||
|
||||
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
|
||||
dependencies {
|
||||
|
||||
implementation project(':capacitor-app')
|
||||
|
||||
}
|
||||
|
||||
|
||||
398
android/app/lint-baseline.xml
Normal file
398
android/app/lint-baseline.xml
Normal file
@@ -0,0 +1,398 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<issues format="6" by="lint 8.1.0" type="baseline" client="gradle" dependencies="true" name="AGP (8.1.0)" variant="all" version="8.1.0">
|
||||
|
||||
<issue
|
||||
id="UnknownIssueId"
|
||||
message="Unknown issue id "UnsanitizedFilenameFromContentProvider""
|
||||
errorLine1=" disable 'UnsanitizedFilenameFromContentProvider'"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="build.gradle"
|
||||
line="26"
|
||||
column="18"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnknownIssueId"
|
||||
message="Unknown issue id "UnsanitizedFilenameFromContentProvider""
|
||||
errorLine1=" disable 'UnsanitizedFilenameFromContentProvider'"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="build.gradle"
|
||||
line="26"
|
||||
column="18"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnknownIssueId"
|
||||
message="Unknown issue id "LintBaselineFixed""
|
||||
errorLine1=" ignore 'LintBaselineFixed'"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="build.gradle"
|
||||
line="34"
|
||||
column="17"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnknownIssueId"
|
||||
message="Unknown issue id "LintBaselineFixed""
|
||||
errorLine1=" ignore 'LintBaselineFixed'"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="build.gradle"
|
||||
line="34"
|
||||
column="17"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="DefaultLocale"
|
||||
message="Implicitly using the default locale is a common source of bugs: Use `String.format(Locale, ...)` instead"
|
||||
errorLine1=" String msg = String.format("
|
||||
errorLine2=" ^">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/BridgeWebChromeClient.java"
|
||||
line="467"
|
||||
column="26"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="DefaultLocale"
|
||||
message="Implicitly using the default locale is a common source of bugs: Use `toUpperCase(Locale)` instead. For strings meant to be internal use `Locale.ROOT`, otherwise `Locale.getDefault()`."
|
||||
errorLine1=" return mask.toUpperCase().equals(string.toUpperCase());"
|
||||
errorLine2=" ~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/util/HostMask.java"
|
||||
line="110"
|
||||
column="29"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="DefaultLocale"
|
||||
message="Implicitly using the default locale is a common source of bugs: Use `toUpperCase(Locale)` instead. For strings meant to be internal use `Locale.ROOT`, otherwise `Locale.getDefault()`."
|
||||
errorLine1=" return mask.toUpperCase().equals(string.toUpperCase());"
|
||||
errorLine2=" ~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/util/HostMask.java"
|
||||
line="110"
|
||||
column="57"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="DefaultLocale"
|
||||
message="Implicitly using the default locale is a common source of bugs: Use `toLowerCase(Locale)` instead. For strings meant to be internal use `Locale.ROOT`, otherwise `Locale.getDefault()`."
|
||||
errorLine1=" if (header.getKey().equalsIgnoreCase("Accept") && header.getValue().toLowerCase().contains("text/html")) {"
|
||||
errorLine2=" ~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/WebViewLocalServer.java"
|
||||
line="474"
|
||||
column="93"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="SimpleDateFormat"
|
||||
message="To get local formatting use `getDateInstance()`, `getDateTimeInstance()`, or `getTimeInstance()`, or use `new SimpleDateFormat(String template, Locale locale)` with for example `Locale.US` for ASCII dates."
|
||||
errorLine1=" String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/BridgeWebChromeClient.java"
|
||||
line="504"
|
||||
column="28"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="SimpleDateFormat"
|
||||
message="To get local formatting use `getDateInstance()`, `getDateTimeInstance()`, or `getTimeInstance()`, or use `new SimpleDateFormat(String template, Locale locale)` with for example `Locale.US` for ASCII dates."
|
||||
errorLine1=" DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'");"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/PluginResult.java"
|
||||
line="44"
|
||||
column="25"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedAttribute"
|
||||
message="Attribute `usesCleartextTraffic` is only used in API level 23 and higher (current min is 22)"
|
||||
errorLine1="<application android:usesCleartextTraffic="true">"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/AndroidManifest.xml"
|
||||
line="4"
|
||||
column="15"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedAttribute"
|
||||
message="Attribute `autoVerify` is only used in API level 23 and higher (current min is 22)"
|
||||
errorLine1=" <intent-filter android:autoVerify="true">"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/AndroidManifest.xml"
|
||||
line="25"
|
||||
column="28"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="ManifestOrder"
|
||||
message="`<uses-permission>` tag appears after `<application>` tag"
|
||||
errorLine1=" <uses-permission android:name="android.permission.INTERNET" />"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/AndroidManifest.xml"
|
||||
line="47"
|
||||
column="6"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="AndroidGradlePluginVersion"
|
||||
message="A newer version of com.android.tools.build:gradle than 8.2.1 is available: 8.9.0. (There is also a newer version of 8.2.𝑥 available, if upgrading to 8.9.0 is difficult: 8.2.2)"
|
||||
errorLine1=" classpath 'com.android.tools.build:gradle:8.2.1'"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="build.gradle"
|
||||
line="12"
|
||||
column="9"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="AndroidGradlePluginVersion"
|
||||
message="A newer version of com.android.tools.build:gradle than 8.2.1 is available: 8.9.0. (There is also a newer version of 8.2.𝑥 available, if upgrading to 8.9.0 is difficult: 8.2.2)"
|
||||
errorLine1=" classpath 'com.android.tools.build:gradle:8.2.1'"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="build.gradle"
|
||||
line="18"
|
||||
column="9"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="GradleDependency"
|
||||
message="A newer version of androidx.appcompat:appcompat than 1.6.1 is available: 1.7.0"
|
||||
errorLine1=" implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="build.gradle"
|
||||
line="46"
|
||||
column="20"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="GradleDependency"
|
||||
message="A newer version of androidx.appcompat:appcompat than 1.6.1 is available: 1.7.0"
|
||||
errorLine1=" implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="build.gradle"
|
||||
line="46"
|
||||
column="20"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="GradleDependency"
|
||||
message="A newer version of androidx.coordinatorlayout:coordinatorlayout than 1.2.0 is available: 1.3.0"
|
||||
errorLine1=" implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="build.gradle"
|
||||
line="47"
|
||||
column="20"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="GradleDependency"
|
||||
message="A newer version of androidx.test.ext:junit than 1.1.5 is available: 1.2.1"
|
||||
errorLine1=" androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="build.gradle"
|
||||
line="51"
|
||||
column="31"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="GradleDependency"
|
||||
message="A newer version of androidx.test.espresso:espresso-core than 3.5.1 is available: 3.6.1"
|
||||
errorLine1=" androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="build.gradle"
|
||||
line="52"
|
||||
column="31"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="GradleDependency"
|
||||
message="A newer version of androidx.appcompat:appcompat than 1.6.1 is available: 1.7.0"
|
||||
errorLine1=" implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="build.gradle"
|
||||
line="75"
|
||||
column="20"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="GradleDependency"
|
||||
message="A newer version of androidx.test.ext:junit than 1.1.5 is available: 1.2.1"
|
||||
errorLine1=" androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="build.gradle"
|
||||
line="77"
|
||||
column="31"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="GradleDependency"
|
||||
message="A newer version of androidx.test.espresso:espresso-core than 3.5.1 is available: 3.6.1"
|
||||
errorLine1=" androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="build.gradle"
|
||||
line="78"
|
||||
column="31"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="Recycle"
|
||||
message="This `TypedArray` should be recycled after use with `#recycle()`"
|
||||
errorLine1=" TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.bridge_fragment);"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/java/com/getcapacitor/BridgeFragment.java"
|
||||
line="99"
|
||||
column="32"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="Overdraw"
|
||||
message="Possible overdraw: Root element paints background `#F0FF1414` with a theme that also paints a background (inferred theme is `@android:style/Theme.Holo`)"
|
||||
errorLine1=" android:background="#F0FF1414""
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/layout/fragment_bridge.xml"
|
||||
line="5"
|
||||
column="5"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedResources"
|
||||
message="The resource `R.layout.activity_main` appears to be unused"
|
||||
errorLine1="<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android""
|
||||
errorLine2="^">
|
||||
<location
|
||||
file="src/main/res/layout/activity_main.xml"
|
||||
line="2"
|
||||
column="1"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedResources"
|
||||
message="The resource `R.xml.config` appears to be unused"
|
||||
errorLine1="<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">"
|
||||
errorLine2="^">
|
||||
<location
|
||||
file="src/main/res/xml/config.xml"
|
||||
line="2"
|
||||
column="1"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedResources"
|
||||
message="The resource `R.drawable.ic_launcher_background` appears to be unused"
|
||||
errorLine1="<vector xmlns:android="http://schemas.android.com/apk/res/android""
|
||||
errorLine2="^">
|
||||
<location
|
||||
file="src/main/res/drawable/ic_launcher_background.xml"
|
||||
line="2"
|
||||
column="1"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedResources"
|
||||
message="The resource `R.drawable.ic_launcher_foreground` appears to be unused"
|
||||
errorLine1="<vector xmlns:android="http://schemas.android.com/apk/res/android""
|
||||
errorLine2="^">
|
||||
<location
|
||||
file="src/main/res/drawable-v24/ic_launcher_foreground.xml"
|
||||
line="1"
|
||||
column="1"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedResources"
|
||||
message="The resource `R.string.package_name` appears to be unused"
|
||||
errorLine1=" <string name="package_name">app.timesafari.app</string>"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="5"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="UnusedResources"
|
||||
message="The resource `R.string.custom_url_scheme` appears to be unused"
|
||||
errorLine1=" <string name="custom_url_scheme">app.timesafari.app</string>"
|
||||
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||
<location
|
||||
file="src/main/res/values/strings.xml"
|
||||
line="6"
|
||||
column="13"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="MonochromeLauncherIcon"
|
||||
message="The application adaptive icon is missing a monochrome tag"
|
||||
errorLine1="<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">"
|
||||
errorLine2="^">
|
||||
<location
|
||||
file="src/main/res/mipmap-anydpi-v26/ic_launcher.xml"
|
||||
line="2"
|
||||
column="1"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="MonochromeLauncherIcon"
|
||||
message="The application adaptive roundIcon is missing a monochrome tag"
|
||||
errorLine1="<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">"
|
||||
errorLine2="^">
|
||||
<location
|
||||
file="src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml"
|
||||
line="2"
|
||||
column="1"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="IconDipSize"
|
||||
message="The image `splash.png` varies significantly in its density-independent (dip) size across the various density versions: drawable-land-hdpi/splash.png: 533x320 dp (800x480 px), drawable-land-mdpi/splash.png: 480x320 dp (480x320 px), drawable-land-xhdpi/splash.png: 640x360 dp (1280x720 px), drawable-land-xxhdpi/splash.png: 533x320 dp (1600x960 px), drawable-land-xxxhdpi/splash.png: 480x320 dp (1920x1280 px)">
|
||||
<location
|
||||
file="src/main/res/drawable-land-mdpi/splash.png"/>
|
||||
<location
|
||||
file="src/main/res/drawable-land-xxxhdpi/splash.png"/>
|
||||
<location
|
||||
file="src/main/res/drawable-land-hdpi/splash.png"/>
|
||||
<location
|
||||
file="src/main/res/drawable-land-xxhdpi/splash.png"/>
|
||||
<location
|
||||
file="src/main/res/drawable-land-xhdpi/splash.png"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="IconDuplicatesConfig"
|
||||
message="The `splash.png` icon has identical contents in the following configuration folders: drawable-land-mdpi, drawable">
|
||||
<location
|
||||
file="src/main/res/drawable/splash.png"/>
|
||||
<location
|
||||
file="src/main/res/drawable-land-mdpi/splash.png"/>
|
||||
</issue>
|
||||
|
||||
<issue
|
||||
id="IconLocation"
|
||||
message="Found bitmap drawable `res/drawable/splash.png` in densityless folder">
|
||||
<location
|
||||
file="src/main/res/drawable/splash.png"/>
|
||||
</issue>
|
||||
|
||||
</issues>
|
||||
@@ -1,26 +1,20 @@
|
||||
package com.getcapacitor.myapp;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
package app.timesafari.app;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
|
||||
@Test
|
||||
public void useAppContext() throws Exception {
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
|
||||
assertEquals("com.getcapacitor.app", appContext.getPackageName());
|
||||
assertEquals("app.timesafari", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,13 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:autoVerify="true">
|
||||
<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
|
||||
|
||||
21
android/app/src/main/assets/capacitor.config.json
Normal file
21
android/app/src/main/assets/capacitor.config.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"appId": "app.timesafari.app",
|
||||
"appName": "TimeSafari",
|
||||
"webDir": "dist",
|
||||
"bundledWebRuntime": false,
|
||||
"server": {
|
||||
"cleartext": true
|
||||
},
|
||||
"plugins": {
|
||||
"App": {
|
||||
"appUrlOpen": {
|
||||
"handlers": [
|
||||
{
|
||||
"url": "timesafari://*",
|
||||
"autoVerify": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
android/app/src/main/assets/capacitor.plugins.json
Normal file
6
android/app/src/main/assets/capacitor.plugins.json
Normal file
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"pkg": "@capacitor/app",
|
||||
"classpath": "com.capacitorjs.plugins.app.AppPlugin"
|
||||
}
|
||||
]
|
||||
@@ -7,8 +7,9 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.8.1'
|
||||
classpath 'com.android.tools.build:gradle:8.1.0'
|
||||
classpath 'com.google.gms:google-services:4.4.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
@@ -27,3 +28,10 @@ allprojects {
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
resolutionStrategy {
|
||||
force 'org.jetbrains.kotlin:kotlin-stdlib:1.8.0'
|
||||
force 'org.jetbrains.kotlin:kotlin-stdlib-common:1.8.0'
|
||||
}
|
||||
}
|
||||
|
||||
7
android/capacitor-android/build.gradle
Normal file
7
android/capacitor-android/build.gradle
Normal file
@@ -0,0 +1,7 @@
|
||||
android {
|
||||
lintOptions {
|
||||
disable 'UnsanitizedFilenameFromContentProvider'
|
||||
abortOnError false
|
||||
baseline file("lint-baseline.xml")
|
||||
}
|
||||
}
|
||||
59
android/capacitor-cordova-android-plugins/build.gradle
Normal file
59
android/capacitor-cordova-android-plugins/build.gradle
Normal file
@@ -0,0 +1,59 @@
|
||||
ext {
|
||||
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.6.1'
|
||||
cordovaAndroidVersion = project.hasProperty('cordovaAndroidVersion') ? rootProject.ext.cordovaAndroidVersion : '10.1.1'
|
||||
}
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.2.1'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
namespace "capacitor.cordova.android.plugins"
|
||||
compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
|
||||
defaultConfig {
|
||||
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22
|
||||
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
flatDir{
|
||||
dirs 'src/main/libs', 'libs'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'src/main/libs', include: ['*.jar'])
|
||||
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
||||
implementation "org.apache.cordova:framework:$cordovaAndroidVersion"
|
||||
// SUB-PROJECT DEPENDENCIES START
|
||||
|
||||
// SUB-PROJECT DEPENDENCIES END
|
||||
}
|
||||
|
||||
// PLUGIN GRADLE EXTENSIONS START
|
||||
apply from: "cordova.variables.gradle"
|
||||
// PLUGIN GRADLE EXTENSIONS END
|
||||
|
||||
for (def func : cdvPluginPostBuildExtras) {
|
||||
func()
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
|
||||
ext {
|
||||
cdvMinSdkVersion = project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22
|
||||
// Plugin gradle extensions can append to this to have code run at the end.
|
||||
cdvPluginPostBuildExtras = []
|
||||
cordovaConfig = [:]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:amazon="http://schemas.amazon.com/apk/res/android">
|
||||
<application android:usesCleartextTraffic="true">
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -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')
|
||||
|
||||
2
android/fastlane/Appfile
Normal file
2
android/fastlane/Appfile
Normal file
@@ -0,0 +1,2 @@
|
||||
json_key_file("") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
|
||||
package_name("app.timesafari.app") # e.g. com.krausefx.app
|
||||
38
android/fastlane/Fastfile
Normal file
38
android/fastlane/Fastfile
Normal file
@@ -0,0 +1,38 @@
|
||||
# This file contains the fastlane.tools configuration
|
||||
# You can find the documentation at https://docs.fastlane.tools
|
||||
#
|
||||
# For a list of all available actions, check out
|
||||
#
|
||||
# https://docs.fastlane.tools/actions
|
||||
#
|
||||
# For a list of all available plugins, check out
|
||||
#
|
||||
# https://docs.fastlane.tools/plugins/available-plugins
|
||||
#
|
||||
|
||||
# Uncomment the line if you want fastlane to automatically update itself
|
||||
# update_fastlane
|
||||
|
||||
default_platform(:android)
|
||||
|
||||
platform :android do
|
||||
desc "Build and deploy Android app"
|
||||
lane :beta do
|
||||
gradle(
|
||||
task: "clean assembleRelease"
|
||||
)
|
||||
upload_to_play_store(
|
||||
track: 'beta',
|
||||
aab: '../app/build/outputs/bundle/release/app-release.aab'
|
||||
)
|
||||
end
|
||||
|
||||
lane :release do
|
||||
gradle(
|
||||
task: "clean assembleRelease"
|
||||
)
|
||||
upload_to_play_store(
|
||||
aab: '../app/build/outputs/bundle/release/app-release.aab'
|
||||
)
|
||||
end
|
||||
end
|
||||
40
android/fastlane/README.md
Normal file
40
android/fastlane/README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
fastlane documentation
|
||||
----
|
||||
|
||||
# Installation
|
||||
|
||||
Make sure you have the latest version of the Xcode command line tools installed:
|
||||
|
||||
```sh
|
||||
xcode-select --install
|
||||
```
|
||||
|
||||
For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
|
||||
|
||||
# Available Actions
|
||||
|
||||
## Android
|
||||
|
||||
### android beta
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane android beta
|
||||
```
|
||||
|
||||
Build and deploy Android app
|
||||
|
||||
### android release
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane android release
|
||||
```
|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
|
||||
|
||||
More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
|
||||
|
||||
The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
|
||||
@@ -20,3 +20,6 @@ org.gradle.jvmargs=-Xmx1536m
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
android.suppressUnsupportedCompileSdk=34
|
||||
android.suppressUnsupportedCompileSdk=34
|
||||
android.suppressUnsupportedCompileSdk=34
|
||||
|
||||
8
android/local.properties
Normal file
8
android/local.properties
Normal file
@@ -0,0 +1,8 @@
|
||||
## This file must *NOT* be checked into Version Control Systems,
|
||||
# as it contains information specific to your local configuration.
|
||||
#
|
||||
# Location of the SDK. This is only used by Gradle.
|
||||
# For customization when using a Version Control System, please read the
|
||||
# header note.
|
||||
#Sun Mar 09 06:14:41 UTC 2025
|
||||
sdk.dir=/opt/android-sdk
|
||||
@@ -8,6 +8,18 @@ const config: CapacitorConfig = {
|
||||
server: {
|
||||
cleartext: true,
|
||||
},
|
||||
plugins: {
|
||||
App: {
|
||||
appUrlOpen: {
|
||||
handlers: [
|
||||
{
|
||||
url: "timesafari://*",
|
||||
autoVerify: true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
91
docs/DEEP_LINKS.md
Normal file
91
docs/DEEP_LINKS.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# TimeSafari Deep Linking Documentation
|
||||
|
||||
## Type System Overview
|
||||
|
||||
The deep linking system uses a multi-layered type safety approach:
|
||||
|
||||
1. **Runtime Validation (Zod Schemas)**
|
||||
- Validates URL structure
|
||||
- Enforces parameter requirements
|
||||
- Sanitizes input data
|
||||
- Provides detailed validation errors
|
||||
|
||||
2. **TypeScript Types**
|
||||
- Generated from Zod schemas
|
||||
- Ensures compile-time type safety
|
||||
- Provides IDE autocompletion
|
||||
- Catches type errors during development
|
||||
|
||||
3. **Router Integration**
|
||||
- Type-safe parameter passing
|
||||
- Route-specific parameter validation
|
||||
- Query parameter type checking
|
||||
|
||||
## Implementation Files
|
||||
|
||||
- `src/types/deepLinks.ts`: Type definitions and validation schemas
|
||||
- `src/services/deepLinks.ts`: Deep link processing service
|
||||
- `src/main.capacitor.ts`: Capacitor integration
|
||||
|
||||
## Type Safety Examples
|
||||
|
||||
```typescript
|
||||
// Parameter type safety
|
||||
type ClaimParams = DeepLinkParams["claim"];
|
||||
// TypeScript knows this has:
|
||||
// - id: string
|
||||
// - view?: "details" | "certificate" | "raw"
|
||||
// Runtime validation
|
||||
const result = deepLinkSchemas.claim.safeParse({
|
||||
id: "123",
|
||||
view: "details"
|
||||
});
|
||||
// Validates at runtime with detailed error messages
|
||||
```
|
||||
|
||||
## Supported URL Schemes
|
||||
|
||||
All deep links follow the format: `timesafari://<route>/<param>?<query>`
|
||||
|
||||
### Claim Routes
|
||||
|
||||
- `timesafari://claim/:id`
|
||||
- Query params:
|
||||
- `view`: "details" | "certificate" | "raw"
|
||||
|
||||
- `timesafari://claim-cert/:id`
|
||||
- `timesafari://claim-add-raw/:id`
|
||||
- Query params:
|
||||
- `claim`: JSON string of claim data
|
||||
- `claimJwtId`: JWT ID for claim
|
||||
|
||||
### Contact Routes
|
||||
|
||||
- `timesafari://contact-edit/:did`
|
||||
- `timesafari://contact-import/:jwt`
|
||||
- Query params:
|
||||
- `contacts`: JSON array of contacts
|
||||
|
||||
### Project Routes
|
||||
|
||||
- `timesafari://project/:id`
|
||||
- Query params:
|
||||
- `view`: "details" | "edit"
|
||||
|
||||
### Invite Routes
|
||||
|
||||
- `timesafari://invite-one-accept/:jwt`
|
||||
- Query params:
|
||||
- `type`: "one" | "many"
|
||||
|
||||
### Gift Routes
|
||||
|
||||
- `timesafari://confirm-gift/:id`
|
||||
- Query params:
|
||||
- `action`: "confirm" | "details"
|
||||
|
||||
### Offer Routes
|
||||
|
||||
- `timesafari://offer-details/:id`
|
||||
- Query params:
|
||||
- `view`: "details"
|
||||
17
index.html
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>
|
||||
|
||||
13
ios/.gitignore
vendored
13
ios/.gitignore
vendored
@@ -1,13 +0,0 @@
|
||||
App/build
|
||||
App/Pods
|
||||
App/output
|
||||
App/App/public
|
||||
DerivedData
|
||||
xcuserdata
|
||||
|
||||
# Cordova plugins for Capacitor
|
||||
capacitor-cordova-ios-plugins
|
||||
|
||||
# Generated Config files
|
||||
App/App/capacitor.config.json
|
||||
App/App/config.xml
|
||||
@@ -1,408 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 48;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; };
|
||||
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; };
|
||||
504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; };
|
||||
504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; };
|
||||
504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; };
|
||||
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };
|
||||
50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
|
||||
A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = "<group>"; };
|
||||
50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = "<group>"; };
|
||||
504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
|
||||
AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
|
||||
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
504EC3011FED79650016851F /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
27E2DDA53C4D2A4D1A88CE4A /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
504EC2FB1FED79650016851F = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
504EC3061FED79650016851F /* App */,
|
||||
504EC3051FED79650016851F /* Products */,
|
||||
7F8756D8B27F46E3366F6CEA /* Pods */,
|
||||
27E2DDA53C4D2A4D1A88CE4A /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
504EC3051FED79650016851F /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
504EC3041FED79650016851F /* App.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
504EC3061FED79650016851F /* App */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
50379B222058CBB4000EE86E /* capacitor.config.json */,
|
||||
504EC3071FED79650016851F /* AppDelegate.swift */,
|
||||
504EC30B1FED79650016851F /* Main.storyboard */,
|
||||
504EC30E1FED79650016851F /* Assets.xcassets */,
|
||||
504EC3101FED79650016851F /* LaunchScreen.storyboard */,
|
||||
504EC3131FED79650016851F /* Info.plist */,
|
||||
2FAD9762203C412B000D30F8 /* config.xml */,
|
||||
50B271D01FEDC1A000F3C39B /* public */,
|
||||
);
|
||||
path = App;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7F8756D8B27F46E3366F6CEA /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */,
|
||||
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
504EC3031FED79650016851F /* App */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */;
|
||||
buildPhases = (
|
||||
6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */,
|
||||
504EC3001FED79650016851F /* Sources */,
|
||||
504EC3011FED79650016851F /* Frameworks */,
|
||||
504EC3021FED79650016851F /* Resources */,
|
||||
9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = App;
|
||||
productName = App;
|
||||
productReference = 504EC3041FED79650016851F /* App.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
504EC2FC1FED79650016851F /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 0920;
|
||||
LastUpgradeCheck = 0920;
|
||||
TargetAttributes = {
|
||||
504EC3031FED79650016851F = {
|
||||
CreatedOnToolsVersion = 9.2;
|
||||
LastSwiftMigration = 1100;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */;
|
||||
compatibilityVersion = "Xcode 8.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 504EC2FB1FED79650016851F;
|
||||
packageReferences = (
|
||||
);
|
||||
productRefGroup = 504EC3051FED79650016851F /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
504EC3031FED79650016851F /* App */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
504EC3021FED79650016851F /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */,
|
||||
50B271D11FEDC1A000F3C39B /* public in Resources */,
|
||||
504EC30F1FED79650016851F /* Assets.xcassets in Resources */,
|
||||
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */,
|
||||
504EC30D1FED79650016851F /* Main.storyboard in Resources */,
|
||||
2FAD9763203C412B000D30F8 /* config.xml in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-App-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-App/Pods-App-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
504EC3001FED79650016851F /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
504EC3081FED79650016851F /* AppDelegate.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
504EC30B1FED79650016851F /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
504EC30C1FED79650016851F /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
504EC3101FED79650016851F /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
504EC3111FED79650016851F /* Base */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
504EC3141FED79650016851F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
504EC3151FED79650016851F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
504EC3171FED79650016851F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
INFOPLIST_FILE = App/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 1.0;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
504EC3181FED79650016851F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
INFOPLIST_FILE = App/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
504EC3141FED79650016851F /* Debug */,
|
||||
504EC3151FED79650016851F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
504EC3171FED79650016851F /* Debug */,
|
||||
504EC3181FED79650016851F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 504EC2FC1FED79650016851F /* Project object */;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,49 +0,0 @@
|
||||
import UIKit
|
||||
import Capacitor
|
||||
|
||||
@UIApplicationMain
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
return true
|
||||
}
|
||||
|
||||
func applicationWillResignActive(_ application: UIApplication) {
|
||||
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
||||
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
|
||||
}
|
||||
|
||||
func applicationDidEnterBackground(_ application: UIApplication) {
|
||||
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
|
||||
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
||||
}
|
||||
|
||||
func applicationWillEnterForeground(_ application: UIApplication) {
|
||||
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
|
||||
}
|
||||
|
||||
func applicationDidBecomeActive(_ application: UIApplication) {
|
||||
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ application: UIApplication) {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
}
|
||||
|
||||
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
|
||||
// Called when the app was launched with a url. Feel free to add additional processing here,
|
||||
// but if you want the App API to support tracking app url opens, make sure to keep this call
|
||||
return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
|
||||
// Called when the app was launched with an activity, including Universal Links.
|
||||
// Feel free to add additional processing here, but if you want the App API to support
|
||||
// tracking app url opens, make sure to keep this call
|
||||
return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 108 KiB |
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "AppIcon-512@2x.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "splash-2732x2732-2.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "splash-2732x2732-1.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "splash-2732x2732.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 40 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 40 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 40 KiB |
@@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17132" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17105"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<imageView key="view" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Splash" id="snD-IY-ifK">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
</imageView>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="Splash" width="1366" height="1366"/>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14111" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Bridge View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="CAPBridgeViewController" customModule="Capacitor" sceneMemberID="viewController"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -1,49 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>TimeSafari</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(MARKETING_VERSION)</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,24 +0,0 @@
|
||||
require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers'
|
||||
|
||||
platform :ios, '13.0'
|
||||
use_frameworks!
|
||||
|
||||
# workaround to avoid Xcode caching of Pods that requires
|
||||
# Product -> Clean Build Folder after new Cordova plugins installed
|
||||
# Requires CocoaPods 1.6 or newer
|
||||
install! 'cocoapods', :disable_input_output_paths => true
|
||||
|
||||
def capacitor_pods
|
||||
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
|
||||
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
|
||||
|
||||
end
|
||||
|
||||
target 'App' do
|
||||
capacitor_pods
|
||||
# Add your Pods here
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
assertDeploymentTarget(installer)
|
||||
end
|
||||
29
main.js
Normal file
29
main.js
Normal file
@@ -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();
|
||||
}
|
||||
});
|
||||
6076
package-lock.json
generated
6076
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
66
package.json
66
package.json
@@ -6,38 +6,52 @@
|
||||
"name": "TimeSafari Team"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"dev": "vite --config vite.config.dev.mts",
|
||||
"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 --config vite.config.mts",
|
||||
"lint": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src",
|
||||
"lint-fix": "eslint --ext .js,.ts,.vue --ignore-path .gitignore --fix src",
|
||||
"prebuild": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src && node sw_combine.js",
|
||||
"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 test:prerequisites && npm run build && npm run test:web && npm run test:mobile",
|
||||
"test:prerequisites": "node scripts/check-prerequisites.js",
|
||||
"test:web": "npx playwright test -c playwright.config-local.ts --trace on",
|
||||
"test:mobile": "npm run build:capacitor && npm run test:android && npm run test:ios",
|
||||
"test:android": "node scripts/test-android.js",
|
||||
"test:ios": "node scripts/test-ios.js",
|
||||
"check:android-device": "adb devices | grep -w 'device' || (echo 'No Android device connected' && exit 1)",
|
||||
"check:ios-device": "xcrun xctrace list devices 2>&1 | grep -w 'Booted' || (echo 'No iOS simulator running' && exit 1)",
|
||||
"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",
|
||||
"fastlane:ios:beta": "cd ios && fastlane beta",
|
||||
"fastlane:ios:release": "cd ios && fastlane release",
|
||||
"fastlane:android:beta": "cd android && fastlane beta",
|
||||
"fastlane:android:release": "cd android && fastlane release"
|
||||
},
|
||||
"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",
|
||||
"@dicebear/collection": "^5.4.1",
|
||||
"@dicebear/core": "^5.4.1",
|
||||
"@ethersproject/hdnode": "^5.7.0",
|
||||
"@ethersproject/wallet": "^5.8.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.5.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.5.1",
|
||||
"@fortawesome/vue-fontawesome": "^3.0.6",
|
||||
@@ -64,9 +78,10 @@
|
||||
"cbor-x": "^1.5.9",
|
||||
"class-transformer": "^0.5.1",
|
||||
"dexie": "^3.2.7",
|
||||
"dexie-export-import": "^4.1.1",
|
||||
"dexie-export-import": "^4.1.4",
|
||||
"did-jwt": "^7.4.7",
|
||||
"did-resolver": "^4.1.0",
|
||||
"dotenv": "^16.0.3",
|
||||
"ethereum-cryptography": "^2.1.3",
|
||||
"ethereumjs-util": "^7.1.5",
|
||||
"jdenticon": "^3.2.0",
|
||||
@@ -89,24 +104,29 @@
|
||||
"reflect-metadata": "^0.1.14",
|
||||
"register-service-worker": "^1.7.2",
|
||||
"simple-vue-camera": "^1.1.3",
|
||||
"sqlite3": "^5.1.7",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"three": "^0.156.1",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
"util": "^0.12.5",
|
||||
"vue": "^3.5.13",
|
||||
"vue-axios": "^3.5.2",
|
||||
"vue-facing-decorator": "^3.0.4",
|
||||
"vue-picture-cropper": "^0.7.0",
|
||||
"vue-qrcode-reader": "^5.5.3",
|
||||
"vue-router": "^4.5.0",
|
||||
"web-did-resolver": "^2.0.27"
|
||||
"web-did-resolver": "^2.0.27",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.45.2",
|
||||
"@types/dom-webcodecs": "^0.1.7",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/leaflet": "^1.9.8",
|
||||
"@types/luxon": "^3.4.2",
|
||||
"@types/node": "^20.14.11",
|
||||
"@types/node-fetch": "^2.6.12",
|
||||
"@types/ramda": "^0.29.11",
|
||||
"@types/sqlite3": "^3.1.11",
|
||||
"@types/three": "^0.155.1",
|
||||
"@types/ua-parser-js": "^0.7.39",
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
@@ -116,11 +136,14 @@
|
||||
"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",
|
||||
"eslint-plugin-vue": "^9.32.0",
|
||||
"fs-extra": "^11.3.0",
|
||||
"markdownlint": "^0.37.4",
|
||||
"markdownlint-cli": "^0.44.0",
|
||||
"npm-check-updates": "^17.1.13",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.2.5",
|
||||
@@ -132,19 +155,20 @@
|
||||
},
|
||||
"main": "./dist-electron/main.js",
|
||||
"build": {
|
||||
"appId": "org.timesafari.app",
|
||||
"appId": "app.timesafari",
|
||||
"productName": "TimeSafari",
|
||||
"directories": {
|
||||
"output": "dist-electron-packages"
|
||||
},
|
||||
"files": [
|
||||
"dist-electron/**/*",
|
||||
"src/electron/**/*"
|
||||
"src/electron/**/*",
|
||||
"main.js"
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "dist-electron/www",
|
||||
"to": "www"
|
||||
"from": "dist-electron",
|
||||
"to": "."
|
||||
}
|
||||
],
|
||||
"linux": {
|
||||
|
||||
5
pkgx.yaml
Normal file
5
pkgx.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
dependencies:
|
||||
- gradle
|
||||
- java
|
||||
|
||||
# other dependencies are discovered via package.json & requirements.txt & Gemfile (I'm guessing).
|
||||
@@ -75,11 +75,6 @@ export default defineConfig({
|
||||
use: { ...devices['Desktop Firefox'] },
|
||||
},
|
||||
|
||||
{
|
||||
name: "webkit",
|
||||
use: { ...devices["Desktop Safari"] },
|
||||
},
|
||||
|
||||
/* Test against mobile viewports. */
|
||||
|
||||
{
|
||||
@@ -104,8 +99,9 @@ export default defineConfig({
|
||||
],
|
||||
|
||||
/* Configure global timeout; default is 30000 milliseconds */
|
||||
// the image upload will often not succeed at 5 seconds
|
||||
timeout: 30000, // various tests fail at various times with 25000
|
||||
// the image upload will often not succeed in 5 seconds
|
||||
// 33-record-gift-x10.spec.ts:90:5 > Record 9 new gifts will often not succeed in 30 seconds
|
||||
timeout: 35000, // various tests fail at various times with 25000
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
eth_keys
|
||||
pywebview
|
||||
pyinstaller>=6.12.0
|
||||
# For development
|
||||
|
||||
185
scripts/check-prerequisites.js
Normal file
185
scripts/check-prerequisites.js
Normal file
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* @fileoverview Prerequisites checker for mobile development environment
|
||||
*
|
||||
* This script verifies that all necessary tools and configurations are in place
|
||||
* for mobile app development, including both Android and iOS platforms.
|
||||
*
|
||||
* Features:
|
||||
* - Validates development environment setup
|
||||
* - Checks required command-line tools
|
||||
* - Verifies Android SDK and device connectivity
|
||||
* - Confirms iOS development tools and simulator status
|
||||
*
|
||||
* Prerequisites checked:
|
||||
* - Node.js and npm installation
|
||||
* - Gradle for Android builds
|
||||
* - Xcode and command line tools for iOS
|
||||
* - ANDROID_HOME environment variable
|
||||
* - Android platform files
|
||||
* - Connected Android devices/emulators
|
||||
* - iOS platform files
|
||||
* - Running iOS simulators
|
||||
*
|
||||
* Exit codes:
|
||||
* - 0: All checks passed
|
||||
* - 1: One or more checks failed
|
||||
*
|
||||
* @example
|
||||
* // Run directly
|
||||
* node scripts/check-prerequisites.js
|
||||
*
|
||||
* // Run via npm script
|
||||
* npm run test:prerequisites
|
||||
*
|
||||
* @author TimeSafari Team
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
const { existsSync } = require('fs');
|
||||
|
||||
/**
|
||||
* Checks if a command-line tool is available by attempting to run its --version command
|
||||
*
|
||||
* @param {string} command - The command to check (e.g., 'node', 'npm', 'gradle')
|
||||
* @param {string} errorMessage - The error message to display if the command is not available
|
||||
* @returns {boolean} - True if the command exists and is executable, false otherwise
|
||||
*
|
||||
* @example
|
||||
* checkCommand('node', 'Node.js is required')
|
||||
* // Returns true if node is available, false otherwise
|
||||
*/
|
||||
function checkCommand(command, errorMessage) {
|
||||
try {
|
||||
execSync(command + ' --version', { stdio: 'ignore' });
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error(`❌ ${errorMessage}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies Android development environment setup
|
||||
*
|
||||
* Checks for:
|
||||
* 1. ANDROID_HOME environment variable
|
||||
* 2. Android platform files in project
|
||||
* 3. Connected Android devices or running emulators
|
||||
*
|
||||
* @returns {boolean} - True if Android setup is complete and valid, false otherwise
|
||||
*
|
||||
* @example
|
||||
* if (!checkAndroidSetup()) {
|
||||
* console.error('Android prerequisites not met');
|
||||
* }
|
||||
*/
|
||||
function checkAndroidSetup() {
|
||||
// Check ANDROID_HOME environment variable
|
||||
// This is required for Android SDK tools access
|
||||
if (!process.env.ANDROID_HOME) {
|
||||
console.error('❌ ANDROID_HOME environment variable not set');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if Android platform was added to the project
|
||||
// The 'android' directory should exist if platform was added via 'npx cap add android'
|
||||
if (!existsSync('android')) {
|
||||
console.error('❌ Android platform not added. Run: npx cap add android');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for connected devices or running emulators
|
||||
// Uses ADB (Android Debug Bridge) to list connected devices
|
||||
try {
|
||||
const devices = execSync('adb devices').toString();
|
||||
// Parse ADB output - looking for lines ending with 'device' (not 'offline' or 'unauthorized')
|
||||
if (!devices.split('\n').slice(1).some(line => line.includes('device'))) {
|
||||
console.error('❌ No Android devices connected');
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('❌ ADB not available');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies iOS development environment setup
|
||||
*
|
||||
* Checks for:
|
||||
* 1. iOS platform files in project
|
||||
* 2. Running iOS simulators
|
||||
* 3. Xcode command line tools availability
|
||||
*
|
||||
* @returns {boolean} - True if iOS setup is complete and valid, false otherwise
|
||||
*
|
||||
* @example
|
||||
* if (!checkIosSetup()) {
|
||||
* console.error('iOS prerequisites not met');
|
||||
* }
|
||||
*/
|
||||
function checkIosSetup() {
|
||||
// Check if iOS platform was added to the project
|
||||
// The 'ios' directory should exist if platform was added via 'npx cap add ios'
|
||||
if (!existsSync('ios')) {
|
||||
console.error('❌ iOS platform not added. Run: npx cap add ios');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for available and running iOS simulators
|
||||
// Uses xcrun simctl to list simulator devices
|
||||
try {
|
||||
const simulators = execSync('xcrun simctl list devices available').toString();
|
||||
if (!simulators.includes('Booted')) {
|
||||
console.error('❌ No iOS simulator running');
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('❌ Xcode command line tools not available');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function to check all prerequisites for mobile development
|
||||
*
|
||||
* Verifies:
|
||||
* 1. Required command line tools (node, npm, gradle, xcodebuild)
|
||||
* 2. Android development setup
|
||||
* 3. iOS development setup
|
||||
*
|
||||
* Exits with code 1 if any checks fail
|
||||
*
|
||||
* @example
|
||||
* // Run from package.json script:
|
||||
* // "test:prerequisites": "node scripts/check-prerequisites.js"
|
||||
*/
|
||||
function main() {
|
||||
let success = true;
|
||||
|
||||
// Check required command line tools
|
||||
// These are essential for building and testing the application
|
||||
success &= checkCommand('node', 'Node.js is required');
|
||||
success &= checkCommand('npm', 'npm is required');
|
||||
success &= checkCommand('gradle', 'Gradle is required for Android builds');
|
||||
success &= checkCommand('xcodebuild', 'Xcode is required for iOS builds');
|
||||
|
||||
// Check platform-specific development environments
|
||||
success &= checkAndroidSetup();
|
||||
success &= checkIosSetup();
|
||||
|
||||
// Exit with error if any checks failed
|
||||
if (!success) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('✅ All prerequisites met!');
|
||||
}
|
||||
|
||||
// Execute the checks
|
||||
main();
|
||||
132
scripts/run-available-mobile-tests.js
Normal file
132
scripts/run-available-mobile-tests.js
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* @fileoverview Runs mobile tests based on available platforms and devices
|
||||
*
|
||||
* This script intelligently detects available mobile platforms and their
|
||||
* associated devices/simulators, then runs tests only for the available
|
||||
* configurations. This allows for flexible testing across different
|
||||
* development environments without failing when a platform is unavailable.
|
||||
*
|
||||
* Platform detection:
|
||||
* - Android: Checks for SDK and connected devices/emulators
|
||||
* - iOS: Checks for macOS, Xcode, and running simulators
|
||||
*
|
||||
* Features:
|
||||
* - Smart platform detection
|
||||
* - Graceful handling of unavailable platforms
|
||||
* - Clear logging of test execution
|
||||
* - Comprehensive error reporting
|
||||
*
|
||||
* Exit codes:
|
||||
* - 0: Tests completed successfully on available platforms
|
||||
* - 1: Tests failed or no platforms available
|
||||
*
|
||||
* @example
|
||||
* // Run directly
|
||||
* node scripts/run-available-mobile-tests.js
|
||||
*
|
||||
* // Run via npm script
|
||||
* npm run test:mobile:available
|
||||
*
|
||||
* @requires child_process
|
||||
* @requires fs
|
||||
*
|
||||
* @author TimeSafari Team
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
const { existsSync } = require('fs');
|
||||
|
||||
/**
|
||||
* Executes mobile tests on available platforms
|
||||
*
|
||||
* This function performs the following steps:
|
||||
* 1. Checks Android environment and device availability
|
||||
* 2. Checks iOS environment and simulator availability (on macOS)
|
||||
* 3. Runs tests on available platforms
|
||||
* 4. Reports results and handles errors
|
||||
*
|
||||
* Platform-specific checks:
|
||||
* Android:
|
||||
* - ANDROID_HOME environment variable
|
||||
* - Android platform files existence
|
||||
* - Connected devices via ADB
|
||||
*
|
||||
* iOS:
|
||||
* - macOS operating system
|
||||
* - iOS platform files existence
|
||||
* - Running simulators via xcrun
|
||||
*
|
||||
* @async
|
||||
* @throws {Error} If tests fail or no platforms are available
|
||||
*
|
||||
* @example
|
||||
* runAvailableMobileTests().catch(error => {
|
||||
* console.error('Test execution failed:', error);
|
||||
* process.exit(1);
|
||||
* });
|
||||
*/
|
||||
async function runAvailableMobileTests() {
|
||||
try {
|
||||
// Check Android availability
|
||||
// Requires both SDK (ANDROID_HOME) and platform files
|
||||
const androidAvailable = existsSync('android') && process.env.ANDROID_HOME;
|
||||
let androidDeviceAvailable = false;
|
||||
|
||||
if (androidAvailable) {
|
||||
try {
|
||||
// Check for connected devices using ADB
|
||||
const devices = execSync('adb devices').toString();
|
||||
// Parse ADB output for actually connected devices
|
||||
// Filters out unauthorized or offline devices
|
||||
androidDeviceAvailable = devices.split('\n').slice(1).some(line => line.includes('device'));
|
||||
} catch (e) {
|
||||
console.log('⚠️ Android SDK available but no devices connected');
|
||||
}
|
||||
}
|
||||
|
||||
// Check iOS availability
|
||||
// Only possible on macOS with Xcode installed
|
||||
const iosAvailable = process.platform === 'darwin' && existsSync('ios');
|
||||
let iosSimulatorAvailable = false;
|
||||
|
||||
if (iosAvailable) {
|
||||
try {
|
||||
// Check for running simulators using xcrun
|
||||
const simulators = execSync('xcrun simctl list devices available').toString();
|
||||
// Look for 'Booted' state in simulator list
|
||||
iosSimulatorAvailable = simulators.includes('Booted');
|
||||
} catch (e) {
|
||||
console.log('⚠️ iOS platform available but no simulator running');
|
||||
}
|
||||
}
|
||||
|
||||
// Execute tests for available platforms
|
||||
if (androidDeviceAvailable) {
|
||||
console.log('🤖 Running Android tests...');
|
||||
// Run Android tests via npm script
|
||||
execSync('npm run test:android', { stdio: 'inherit' });
|
||||
}
|
||||
|
||||
if (iosSimulatorAvailable) {
|
||||
console.log('🍎 Running iOS tests...');
|
||||
// Run iOS tests via npm script
|
||||
execSync('npm run test:ios', { stdio: 'inherit' });
|
||||
}
|
||||
|
||||
// Error if no platforms are available for testing
|
||||
if (!androidDeviceAvailable && !iosSimulatorAvailable) {
|
||||
console.error('❌ No mobile platforms available for testing');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('✅ Available mobile tests completed successfully');
|
||||
} catch (error) {
|
||||
// Handle any errors during test execution
|
||||
console.error('❌ Mobile tests failed:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the test runner
|
||||
runAvailableMobileTests();
|
||||
299
scripts/test-android.js
Normal file
299
scripts/test-android.js
Normal file
@@ -0,0 +1,299 @@
|
||||
/**
|
||||
* @fileoverview Android test runner for Capacitor-based mobile app
|
||||
*
|
||||
* This script handles the build, installation, and testing of the Android app.
|
||||
* It ensures the app is properly synced, built, installed on a device/emulator,
|
||||
* and runs the test suite.
|
||||
*
|
||||
* Process flow:
|
||||
* 1. Sync Capacitor project with latest web build
|
||||
* 2. Build debug APK
|
||||
* 3. Install APK on connected device/emulator
|
||||
* 4. Run instrumented tests
|
||||
*
|
||||
* Prerequisites:
|
||||
* - Android SDK installed and ANDROID_HOME set
|
||||
* - Gradle installed and in PATH
|
||||
* - Connected Android device or running emulator
|
||||
* - Capacitor Android platform added to project
|
||||
*
|
||||
* Exit codes:
|
||||
* - 0: Tests completed successfully
|
||||
* - 1: Build, installation, or test failure
|
||||
*
|
||||
* @example
|
||||
* // Run directly
|
||||
* node scripts/test-android.js
|
||||
*
|
||||
* // Run via npm script
|
||||
* npm run test:android
|
||||
*
|
||||
* @requires child_process
|
||||
* @requires path
|
||||
*
|
||||
* @author TimeSafari Team
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
const { join } = require('path');
|
||||
const { existsSync, mkdirSync, appendFileSync, readFileSync } = require('fs');
|
||||
|
||||
// Format date as YYYY-MM-DD-HHMMSS
|
||||
const getLogFileName = () => {
|
||||
const now = new Date();
|
||||
const date = now.toISOString().split('T')[0];
|
||||
const time = now.toTimeString().split(' ')[0].replace(/:/g, '');
|
||||
return `build_logs/android-build-${date}-${time}.log`;
|
||||
};
|
||||
|
||||
// Create logger function
|
||||
const createLogger = (logFile) => {
|
||||
return (message) => {
|
||||
const timestamp = new Date().toISOString();
|
||||
const logMessage = `[${timestamp}] ${message}\n`;
|
||||
console.log(message);
|
||||
appendFileSync(logFile, logMessage);
|
||||
};
|
||||
};
|
||||
|
||||
// Check for connected Android devices
|
||||
const checkConnectedDevices = async (log) => {
|
||||
log('🔍 Checking for Android devices...');
|
||||
const devices = execSync('adb devices').toString();
|
||||
const connectedDevices = devices.split('\n')
|
||||
.slice(1)
|
||||
.filter(line => line.includes('device'))
|
||||
.map(line => line.split('\t')[0])
|
||||
.filter(Boolean);
|
||||
|
||||
if (connectedDevices.length === 0) {
|
||||
throw new Error('No Android devices or emulators connected. Please connect a device or start an emulator.');
|
||||
}
|
||||
|
||||
log(`📱 Found ${connectedDevices.length} device(s): ${connectedDevices.join(', ')}`);
|
||||
return connectedDevices;
|
||||
};
|
||||
|
||||
// Verify Java installation
|
||||
const verifyJavaInstallation = (log) => {
|
||||
log('🔍 Checking Java...');
|
||||
const javaHome = process.env.JAVA_HOME;
|
||||
if (!existsSync(javaHome)) {
|
||||
throw new Error(`Required Java not found at ${javaHome}. Please install OpenJDK.`);
|
||||
}
|
||||
log('✅ Java found');
|
||||
};
|
||||
|
||||
// Generate test data using generate_data.ts
|
||||
const generateTestData = async (log) => {
|
||||
log('🔄 Generating test data...');
|
||||
try {
|
||||
execSync('npx ts-node test-scripts/generate_data.ts', { stdio: 'inherit' });
|
||||
log('✅ Test data generated successfully');
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to generate test data: ${error.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
// Parse shell environment file
|
||||
const parseEnvFile = (filePath) => {
|
||||
const content = readFileSync(filePath, 'utf8');
|
||||
const env = {};
|
||||
content.split('\n').forEach(line => {
|
||||
const match = line.match(/^export\s+(\w+)="(.+)"$/);
|
||||
if (match) {
|
||||
env[match[1]] = match[2];
|
||||
}
|
||||
});
|
||||
return env;
|
||||
};
|
||||
|
||||
// Run individual deeplink test
|
||||
const executeDeeplink = async (url, description, log) => {
|
||||
log(`\n🔗 Testing deeplink: ${description}`);
|
||||
log(`URL: ${url}`);
|
||||
|
||||
try {
|
||||
// Stop the app before executing the deep link
|
||||
execSync('adb shell am force-stop app.timesafari.app');
|
||||
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1s
|
||||
|
||||
execSync(`adb shell am start -W -a android.intent.action.VIEW -d "${url}" -c android.intent.category.BROWSABLE`);
|
||||
log(`✅ Successfully executed: ${description}`);
|
||||
|
||||
// Wait between deeplink tests
|
||||
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5s
|
||||
} catch (error) {
|
||||
log(`❌ Failed to execute deeplink: ${description}`);
|
||||
log(`Error: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// Run all deeplink tests
|
||||
const runDeeplinkTests = async (log) => {
|
||||
log('🔗 Starting deeplink tests...');
|
||||
|
||||
try {
|
||||
// Load test data
|
||||
const testEnv = parseEnvFile('.generated/test-env.sh');
|
||||
const claimDetails = JSON.parse(readFileSync('.generated/claim_details.json', 'utf8'));
|
||||
const contacts = JSON.parse(readFileSync('.generated/contacts.json', 'utf8'));
|
||||
|
||||
// Test each deeplink
|
||||
const deeplinkTests = [
|
||||
{
|
||||
url: `timesafari://claim/${claimDetails.claim_id}`,
|
||||
description: 'Claim view'
|
||||
},
|
||||
{
|
||||
url: `timesafari://claim-cert/${claimDetails.claim_id}`,
|
||||
description: 'Claim certificate view'
|
||||
},
|
||||
{
|
||||
url: `timesafari://claim-add-raw/${claimDetails.claim_id}`,
|
||||
description: 'Raw claim addition'
|
||||
},
|
||||
{
|
||||
url: 'timesafari://did/test',
|
||||
description: 'DID view with test identifier'
|
||||
},
|
||||
{
|
||||
url: `timesafari://did/${testEnv.CONTACT1_DID}`,
|
||||
description: 'DID view with contact DID'
|
||||
},
|
||||
{
|
||||
url: `timesafari://contact-edit/${testEnv.CONTACT1_DID}`,
|
||||
description: 'Contact editing'
|
||||
},
|
||||
{
|
||||
url: `timesafari://contacts/import?contacts=${encodeURIComponent(JSON.stringify(contacts))}`,
|
||||
description: 'Contacts import'
|
||||
}
|
||||
];
|
||||
|
||||
// Execute each test
|
||||
for (const test of deeplinkTests) {
|
||||
await executeDeeplink(test.url, test.description, log);
|
||||
}
|
||||
|
||||
let succeeded = true;
|
||||
try {
|
||||
await executeDeeplink('timesafari://contactJunk', 'Non-existent deeplink', log);
|
||||
} catch (error) {
|
||||
log('✅ Non-existent deeplink failed as expected');
|
||||
succeeded = false;
|
||||
} finally {
|
||||
if (succeeded) {
|
||||
throw new Error('Non-existent deeplink should have failed');
|
||||
}
|
||||
}
|
||||
|
||||
log('✅ All deeplink tests completed successfully');
|
||||
} catch (error) {
|
||||
log('❌ Deeplink tests failed');
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// Build web assets
|
||||
const buildWebAssets = async (log) => {
|
||||
log('🌐 Building web assets...');
|
||||
execSync('rm -rf dist', { stdio: 'inherit' });
|
||||
execSync('npm run build:web', { stdio: 'inherit' });
|
||||
execSync('npm run build:capacitor', { stdio: 'inherit' });
|
||||
log('✅ Web assets built successfully');
|
||||
};
|
||||
|
||||
// Configure Android project
|
||||
const configureAndroidProject = async (log) => {
|
||||
log('📱 Syncing Capacitor project...');
|
||||
execSync('npx cap sync android', { stdio: 'inherit' });
|
||||
log('✅ Capacitor sync completed');
|
||||
|
||||
log('⚙️ Configuring Gradle properties...');
|
||||
const gradleProps = 'android/gradle.properties';
|
||||
if (!existsSync(gradleProps) || !execSync(`grep -q "android.suppressUnsupportedCompileSdk=34" ${gradleProps}`)) {
|
||||
execSync('echo "android.suppressUnsupportedCompileSdk=34" >> android/gradle.properties');
|
||||
log('✅ Added SDK suppression to gradle.properties');
|
||||
}
|
||||
};
|
||||
|
||||
// Build and test Android project
|
||||
const buildAndTestAndroid = async (log, env) => {
|
||||
log('🏗️ Building Android project...');
|
||||
execSync('cd android && ./gradlew clean', { stdio: 'inherit', env });
|
||||
log('✅ Gradle clean completed');
|
||||
|
||||
execSync('cd android && ./gradlew build', { stdio: 'inherit', env });
|
||||
log('✅ Gradle build completed');
|
||||
|
||||
log('🧪 Running Android tests...');
|
||||
execSync('cd android && ./gradlew connectedAndroidTest', { stdio: 'inherit', env });
|
||||
log('✅ Android tests completed');
|
||||
};
|
||||
|
||||
// Run the app
|
||||
const runAndroidApp = async (log, env) => {
|
||||
log('📱 Running app on device...');
|
||||
execSync('npx cap run android', { stdio: 'inherit', env });
|
||||
log('✅ App launched successfully');
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs the complete Android test suite including build, installation, and testing
|
||||
*
|
||||
* The function performs the following steps:
|
||||
* 1. Checks for connected devices/emulators
|
||||
* 2. Ensures correct Java version is used
|
||||
* 3. Checks if app is already installed
|
||||
* 4. Syncs the Capacitor project with latest build
|
||||
* 5. Builds and runs instrumented Android tests
|
||||
*
|
||||
* @async
|
||||
* @throws {Error} If any step in the build or test process fails
|
||||
*
|
||||
* @example
|
||||
* runAndroidTests().catch(error => {
|
||||
* console.error('Test execution failed:', error);
|
||||
* process.exit(1);
|
||||
* });
|
||||
*/
|
||||
async function runAndroidTests() {
|
||||
// Create build_logs directory if it doesn't exist
|
||||
if (!existsSync('build_logs')) {
|
||||
mkdirSync('build_logs');
|
||||
}
|
||||
|
||||
const logFile = getLogFileName();
|
||||
const log = createLogger(logFile);
|
||||
|
||||
try {
|
||||
log('🚀 Starting Android build and test process...');
|
||||
|
||||
// Generate test data first
|
||||
await generateTestData(log);
|
||||
|
||||
await checkConnectedDevices(log);
|
||||
await verifyJavaInstallation(log);
|
||||
await buildWebAssets(log);
|
||||
await configureAndroidProject(log);
|
||||
const env = process.env;
|
||||
await buildAndTestAndroid(log, env);
|
||||
await runAndroidApp(log, env);
|
||||
|
||||
// Run deeplink tests after app is installed
|
||||
await runDeeplinkTests(log);
|
||||
|
||||
log('🎉 Android build and test process completed successfully');
|
||||
log(`📝 Full build log available at: ${logFile}`);
|
||||
} catch (error) {
|
||||
log(`❌ Android tests failed: ${error.message}`);
|
||||
log(`📝 Check build log for details: ${logFile}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the test suite
|
||||
runAndroidTests();
|
||||
652
scripts/test-ios.js
Normal file
652
scripts/test-ios.js
Normal file
@@ -0,0 +1,652 @@
|
||||
/**
|
||||
* @fileoverview iOS test runner for Capacitor-based mobile app
|
||||
*
|
||||
* This script handles the build and testing of the iOS app using Xcode's
|
||||
* command-line tools. It ensures the app is properly synced with the latest
|
||||
* web build and runs the test suite on a specified iOS simulator.
|
||||
*
|
||||
* Process flow:
|
||||
* 1. Sync Capacitor project with latest web build
|
||||
* 2. Build app for iOS simulator
|
||||
* 3. Run XCTest suite
|
||||
*
|
||||
* Prerequisites:
|
||||
* - macOS operating system
|
||||
* - Xcode installed with command line tools
|
||||
* - iOS simulator available
|
||||
* - Capacitor iOS platform added to project
|
||||
* - Valid iOS development certificates
|
||||
*
|
||||
* Exit codes:
|
||||
* - 0: Tests completed successfully
|
||||
* - 1: Build or test failure
|
||||
*
|
||||
* @example
|
||||
* // Run directly
|
||||
* node scripts/test-ios.js
|
||||
*
|
||||
* // Run via npm script
|
||||
* npm run test:ios
|
||||
*
|
||||
* @requires child_process
|
||||
* @requires path
|
||||
* @requires fs
|
||||
*
|
||||
* @author TimeSafari Team
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
const { join } = require('path');
|
||||
const { existsSync, mkdirSync, appendFileSync, readFileSync } = require('fs');
|
||||
|
||||
// Format date as YYYY-MM-DD-HHMMSS
|
||||
const getLogFileName = () => {
|
||||
const now = new Date();
|
||||
const date = now.toISOString().split('T')[0];
|
||||
const time = now.toTimeString().split(' ')[0].replace(/:/g, '');
|
||||
return `build_logs/ios-build-${date}-${time}.log`;
|
||||
};
|
||||
|
||||
// Create logger function
|
||||
const createLogger = (logFile) => {
|
||||
return (message) => {
|
||||
const timestamp = new Date().toISOString();
|
||||
const logMessage = `[${timestamp}] ${message}\n`;
|
||||
console.log(message);
|
||||
appendFileSync(logFile, logMessage);
|
||||
};
|
||||
};
|
||||
|
||||
// Check for iOS simulator
|
||||
const checkSimulator = async (log) => {
|
||||
log('🔍 Checking for iOS simulator...');
|
||||
const simulatorsOutput = execSync('xcrun simctl list devices available -j').toString();
|
||||
const simulatorsData = JSON.parse(simulatorsOutput);
|
||||
|
||||
// Get all available devices/simulators with their UDIDs
|
||||
const allDevices = [];
|
||||
|
||||
// Process all runtime groups (iOS versions)
|
||||
Object.entries(simulatorsData.devices).forEach(([runtime, devices]) => {
|
||||
devices.forEach(device => {
|
||||
allDevices.push({
|
||||
name: device.name,
|
||||
udid: device.udid,
|
||||
state: device.state,
|
||||
runtime: runtime,
|
||||
isIphone: device.name.includes('iPhone'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Check for booted simulators first
|
||||
const bootedDevices = allDevices.filter(device => device.state === 'Booted');
|
||||
|
||||
if (bootedDevices.length > 0) {
|
||||
log(`📱 Found ${bootedDevices.length} running simulator(s): ${bootedDevices.map(d => d.name).join(', ')}`);
|
||||
return bootedDevices;
|
||||
}
|
||||
|
||||
// No booted devices found, try to boot one
|
||||
log('⚠️ No running iOS simulator found. Attempting to boot one...');
|
||||
|
||||
// Prefer iPhone devices, especially newer models
|
||||
const preferredDevices = [
|
||||
'iPhone 15', 'iPhone 14', 'iPhone 13', 'iPhone 12', 'iPhone', // Prefer newer iPhones first
|
||||
'iPad' // Then iPads if no iPhones available
|
||||
];
|
||||
|
||||
let deviceToLaunch = null;
|
||||
|
||||
// Try to find a device from our preferred list
|
||||
for (const preferredName of preferredDevices) {
|
||||
const matchingDevices = allDevices.filter(device =>
|
||||
device.name.includes(preferredName) && device.state === 'Shutdown');
|
||||
|
||||
if (matchingDevices.length > 0) {
|
||||
// Sort by runtime to prefer newer iOS versions
|
||||
matchingDevices.sort((a, b) => b.runtime.localeCompare(a.runtime));
|
||||
deviceToLaunch = matchingDevices[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no preferred device found, take any available device
|
||||
if (!deviceToLaunch && allDevices.length > 0) {
|
||||
const availableDevices = allDevices.filter(device => device.state === 'Shutdown');
|
||||
if (availableDevices.length > 0) {
|
||||
deviceToLaunch = availableDevices[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (!deviceToLaunch) {
|
||||
throw new Error('No available iOS simulators found. Please create a simulator in Xcode first.');
|
||||
}
|
||||
|
||||
// Boot the selected simulator
|
||||
log(`🚀 Booting iOS simulator: ${deviceToLaunch.name} (${deviceToLaunch.runtime})`);
|
||||
execSync(`xcrun simctl boot ${deviceToLaunch.udid}`);
|
||||
|
||||
// Wait for simulator to fully boot
|
||||
log('⏳ Waiting for simulator to boot completely...');
|
||||
// Give the simulator time to fully boot before proceeding
|
||||
await new Promise(resolve => setTimeout(resolve, 10000));
|
||||
|
||||
log(`✅ Successfully booted simulator: ${deviceToLaunch.name}`);
|
||||
|
||||
return [{ name: deviceToLaunch.name, udid: deviceToLaunch.udid }];
|
||||
};
|
||||
|
||||
// Verify Xcode installation
|
||||
const verifyXcodeInstallation = (log) => {
|
||||
log('🔍 Checking Xcode installation...');
|
||||
try {
|
||||
execSync('xcode-select -p');
|
||||
log('✅ Xcode command line tools found');
|
||||
} catch (error) {
|
||||
throw new Error('Xcode command line tools not found. Please install Xcode first.');
|
||||
}
|
||||
};
|
||||
|
||||
// Generate test data using generate_data.ts
|
||||
const generateTestData = async (log) => {
|
||||
log('🔄 Generating test data...');
|
||||
|
||||
// Check if test-scripts directory exists
|
||||
if (!existsSync('test-scripts')) {
|
||||
log('⚠️ test-scripts directory not found');
|
||||
log('⚠️ Current directory: ' + process.cwd());
|
||||
|
||||
// List directories to help debug
|
||||
const { readdirSync } = require('fs');
|
||||
log('📂 Directories in current path:');
|
||||
try {
|
||||
const files = readdirSync('.');
|
||||
files.forEach(file => {
|
||||
const isDir = existsSync(file) && require('fs').statSync(file).isDirectory();
|
||||
log(`${isDir ? '📁' : '📄'} ${file}`);
|
||||
});
|
||||
} catch (err) {
|
||||
log(`⚠️ Error listing directory: ${err.message}`);
|
||||
}
|
||||
} else {
|
||||
log('✅ Found test-scripts directory');
|
||||
|
||||
// Check if generate_data.ts exists
|
||||
if (existsSync('test-scripts/generate_data.ts')) {
|
||||
log('✅ Found generate_data.ts');
|
||||
} else {
|
||||
log('⚠️ generate_data.ts not found in test-scripts directory');
|
||||
|
||||
// List files in test-scripts to help debug
|
||||
const { readdirSync } = require('fs');
|
||||
log('📂 Files in test-scripts:');
|
||||
try {
|
||||
const files = readdirSync('test-scripts');
|
||||
files.forEach(file => {
|
||||
log(`📄 ${file}`);
|
||||
});
|
||||
} catch (err) {
|
||||
log(`⚠️ Error listing test-scripts: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create .generated directory if it doesn't exist
|
||||
if (!existsSync('.generated')) {
|
||||
log('📁 Creating .generated directory');
|
||||
mkdirSync('.generated', { recursive: true });
|
||||
}
|
||||
|
||||
try {
|
||||
// Try to generate test data using the script
|
||||
log('🔄 Running test data generation script...');
|
||||
execSync('npx ts-node test-scripts/generate_data.ts', { stdio: 'inherit' });
|
||||
log('✅ Test data generation script completed');
|
||||
|
||||
// Verify the generated files exist
|
||||
const requiredFiles = [
|
||||
'.generated/test-env.json',
|
||||
'.generated/claim_details.json',
|
||||
'.generated/contacts.json'
|
||||
];
|
||||
|
||||
log('🔍 Verifying generated files:');
|
||||
for (const file of requiredFiles) {
|
||||
if (!existsSync(file)) {
|
||||
log(`⚠️ Required file ${file} was not generated`);
|
||||
throw new Error(`Required file ${file} was not generated`);
|
||||
} else {
|
||||
log(`✅ ${file} exists`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
log(`⚠️ Failed to generate test data: ${error.message}`);
|
||||
log('⚠️ Creating fallback test data...');
|
||||
|
||||
// Create minimal fallback test data
|
||||
const fallbackTestEnv = {
|
||||
"CONTACT1_DID": "did:example:123456789",
|
||||
"APP_URL": "https://app.timesafari.example"
|
||||
};
|
||||
|
||||
const fallbackClaimDetails = {
|
||||
"claim_id": "claim_12345",
|
||||
"title": "Test Claim",
|
||||
"description": "This is a test claim"
|
||||
};
|
||||
|
||||
const fallbackContacts = [
|
||||
{
|
||||
"id": "contact1",
|
||||
"name": "Test Contact",
|
||||
"did": "did:example:123456789"
|
||||
}
|
||||
];
|
||||
|
||||
// Use writeFileSync to overwrite any existing files
|
||||
const { writeFileSync } = require('fs');
|
||||
writeFileSync('.generated/test-env.json', JSON.stringify(fallbackTestEnv, null, 2));
|
||||
writeFileSync('.generated/claim_details.json', JSON.stringify(fallbackClaimDetails, null, 2));
|
||||
writeFileSync('.generated/contacts.json', JSON.stringify(fallbackContacts, null, 2));
|
||||
|
||||
log('✅ Fallback test data created');
|
||||
|
||||
// Verify files were created
|
||||
const requiredFiles = [
|
||||
'.generated/test-env.json',
|
||||
'.generated/claim_details.json',
|
||||
'.generated/contacts.json'
|
||||
];
|
||||
|
||||
log('🔍 Verifying fallback files:');
|
||||
for (const file of requiredFiles) {
|
||||
if (!existsSync(file)) {
|
||||
log(`⚠️ Failed to create ${file}`);
|
||||
} else {
|
||||
log(`✅ Created ${file}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Build web assets
|
||||
const buildWebAssets = async (log) => {
|
||||
log('🌐 Building web assets...');
|
||||
execSync('rm -rf dist', { stdio: 'inherit' });
|
||||
execSync('npm run build:web', { stdio: 'inherit' });
|
||||
execSync('npm run build:capacitor', { stdio: 'inherit' });
|
||||
log('✅ Web assets built successfully');
|
||||
};
|
||||
|
||||
// Configure iOS project
|
||||
const configureIosProject = async (log) => {
|
||||
log('📱 Syncing Capacitor project...');
|
||||
try {
|
||||
execSync('npx cap sync ios', { stdio: 'inherit' });
|
||||
log('✅ Capacitor sync completed');
|
||||
} catch (error) {
|
||||
log('⚠️ Capacitor sync encountered issues. Attempting to continue...');
|
||||
}
|
||||
|
||||
// Register URL scheme for deeplink tests
|
||||
log('🔗 Configuring URL scheme for deeplink tests...');
|
||||
if (checkAndRegisterUrlScheme(log)) {
|
||||
log('✅ URL scheme configuration completed');
|
||||
}
|
||||
|
||||
log('⚙️ Installing CocoaPods dependencies...');
|
||||
try {
|
||||
// Try to run pod install normally first
|
||||
execSync('cd ios/App && pod install', { stdio: 'inherit' });
|
||||
} catch (error) {
|
||||
// If that fails, try using sudo (requires password)
|
||||
log('⚠️ CocoaPods installation failed. Trying with sudo...');
|
||||
try {
|
||||
execSync('cd ios/App && sudo pod install', { stdio: 'inherit' });
|
||||
} catch (sudoError) {
|
||||
// If both methods fail, alert the user
|
||||
log('❌ CocoaPods installation failed.');
|
||||
log('Please run one of the following commands manually:');
|
||||
log('1. cd ios/App && pod install');
|
||||
log('2. cd ios/App && sudo pod install');
|
||||
log('3. Install CocoaPods through Homebrew: brew install cocoapods');
|
||||
throw new Error('CocoaPods installation failed. See log for details.');
|
||||
}
|
||||
}
|
||||
log('✅ CocoaPods installation completed');
|
||||
};
|
||||
|
||||
// Build and test iOS project
|
||||
const buildAndTestIos = async (log, simulator) => {
|
||||
const simulatorName = simulator[0].name;
|
||||
log('🏗️ Building iOS project...');
|
||||
execSync('cd ios/App && xcodebuild clean -workspace App.xcworkspace -scheme App', { stdio: 'inherit' });
|
||||
log('✅ Xcode clean completed');
|
||||
|
||||
log(`🏗️ Building for simulator: ${simulatorName}`);
|
||||
execSync(`cd ios/App && xcodebuild build -workspace App.xcworkspace -scheme App -destination "platform=iOS Simulator,name=${simulatorName}"`, { stdio: 'inherit' });
|
||||
log('✅ Xcode build completed');
|
||||
|
||||
// Check if the project is configured for testing by querying the scheme capabilities
|
||||
try {
|
||||
log(`🧪 Checking if scheme is configured for testing`);
|
||||
const schemeInfo = execSync(`cd ios/App && xcodebuild -scheme App -showBuildSettings | grep TEST`).toString();
|
||||
|
||||
if (schemeInfo.includes('ENABLE_TESTABILITY = YES')) {
|
||||
log(`🧪 Attempting to run tests on simulator: ${simulatorName}`);
|
||||
try {
|
||||
execSync(`cd ios/App && xcodebuild test -workspace App.xcworkspace -scheme App -destination "platform=iOS Simulator,name=${simulatorName}"`, { stdio: 'inherit' });
|
||||
log('✅ iOS tests completed successfully');
|
||||
} catch (testError) {
|
||||
log(`⚠️ Tests failed or scheme not properly configured for testing: ${testError.message}`);
|
||||
log('⚠️ This is normal if no test targets have been added to the project');
|
||||
log('⚠️ Skipping test step and continuing with the app launch');
|
||||
}
|
||||
} else {
|
||||
log('⚠️ Project does not have testing enabled in build settings');
|
||||
log('⚠️ Skipping test step and continuing with the app launch');
|
||||
}
|
||||
} catch (error) {
|
||||
log('⚠️ Unable to determine if testing is configured');
|
||||
log('⚠️ Skipping test step and continuing with the app launch');
|
||||
}
|
||||
};
|
||||
|
||||
// Run the app
|
||||
const runIosApp = async (log, simulator) => {
|
||||
const simulatorName = simulator[0].name;
|
||||
const simulatorUdid = simulator[0].udid;
|
||||
|
||||
log(`📱 Running app in simulator: ${simulatorName} (${simulatorUdid})...`);
|
||||
// Use the --target parameter to specify the device directly, avoiding the UI prompt
|
||||
execSync(`npx cap run ios --target="${simulatorUdid}"`, { stdio: 'inherit' });
|
||||
log('✅ App launched successfully');
|
||||
};
|
||||
|
||||
/**
|
||||
* Run deeplink tests
|
||||
* Optionally tests deeplinks if the test data is available
|
||||
*
|
||||
* @param {function} log - Logging function
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const runDeeplinkTests = async (log) => {
|
||||
log('🔗 Starting deeplink tests...');
|
||||
|
||||
// Register URL scheme if needed
|
||||
checkAndRegisterUrlScheme(log);
|
||||
|
||||
// Check if test data files exist first
|
||||
const requiredFiles = [
|
||||
'.generated/test-env.json',
|
||||
'.generated/claim_details.json',
|
||||
'.generated/contacts.json'
|
||||
];
|
||||
|
||||
for (const file of requiredFiles) {
|
||||
if (!existsSync(file)) {
|
||||
log(`⚠️ Required file ${file} does not exist`);
|
||||
log('⚠️ Skipping deeplink tests');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Load test data
|
||||
log('📂 Loading test data from .generated directory');
|
||||
let testEnv, claimDetails, contacts;
|
||||
|
||||
try {
|
||||
const testEnvContent = readFileSync('.generated/test-env.json', 'utf8');
|
||||
testEnv = JSON.parse(testEnvContent);
|
||||
log('✅ Loaded test-env.json');
|
||||
} catch (error) {
|
||||
log(`⚠️ Failed to load test-env.json: ${error.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const claimDetailsContent = readFileSync('.generated/claim_details.json', 'utf8');
|
||||
claimDetails = JSON.parse(claimDetailsContent);
|
||||
log('✅ Loaded claim_details.json');
|
||||
} catch (error) {
|
||||
log(`⚠️ Failed to load claim_details.json: ${error.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const contactsContent = readFileSync('.generated/contacts.json', 'utf8');
|
||||
contacts = JSON.parse(contactsContent);
|
||||
log('✅ Loaded contacts.json');
|
||||
} catch (error) {
|
||||
log(`⚠️ Failed to load contacts.json: ${error.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the app URL scheme is registered in the simulator
|
||||
log('🔍 Checking if URL scheme is registered in simulator...');
|
||||
try {
|
||||
// Attempt to open a simple URL with the scheme
|
||||
execSync(`xcrun simctl openurl booted "timesafari://test"`, { stdio: 'pipe' });
|
||||
log('✅ URL scheme is registered and working');
|
||||
} catch (error) {
|
||||
const errorMessage = error.message || '';
|
||||
|
||||
// Check for the specific error code that indicates an unregistered URL scheme
|
||||
if (errorMessage.includes('OSStatus error -10814') || errorMessage.includes('NSOSStatusErrorDomain, code=-10814')) {
|
||||
log('⚠️ URL scheme "timesafari://" is not registered in the app or app is not running');
|
||||
log('⚠️ The scheme was added to Info.plist but the app may need to be rebuilt');
|
||||
log('⚠️ Trying to continue with tests, but they may fail');
|
||||
}
|
||||
}
|
||||
|
||||
// Test URLs
|
||||
const deeplinkTests = [
|
||||
{
|
||||
url: `timesafari://claim/${claimDetails.claim_id}`,
|
||||
description: 'Claim view'
|
||||
},
|
||||
{
|
||||
url: `timesafari://claim-cert/${claimDetails.claim_id}`,
|
||||
description: 'Claim certificate view'
|
||||
},
|
||||
{
|
||||
url: `timesafari://claim-add-raw/${claimDetails.claim_id}`,
|
||||
description: 'Raw claim addition'
|
||||
},
|
||||
{
|
||||
url: 'timesafari://did/test',
|
||||
description: 'DID view with test identifier'
|
||||
},
|
||||
{
|
||||
url: `timesafari://did/${testEnv.CONTACT1_DID}`,
|
||||
description: 'DID view with contact DID'
|
||||
},
|
||||
{
|
||||
url: `timesafari://contact-edit/${testEnv.CONTACT1_DID}`,
|
||||
description: 'Contact editing'
|
||||
},
|
||||
{
|
||||
url: `timesafari://contacts/import?contacts=${encodeURIComponent(JSON.stringify(contacts))}`,
|
||||
description: 'Contacts import'
|
||||
}
|
||||
];
|
||||
|
||||
// Execute each test
|
||||
let testsCompleted = 0;
|
||||
let testsSkipped = 0;
|
||||
|
||||
for (const test of deeplinkTests) {
|
||||
try {
|
||||
log(`\n🔗 Testing deeplink: ${test.description}`);
|
||||
log(`URL: ${test.url}`);
|
||||
|
||||
execSync(`xcrun simctl openurl booted "${test.url}"`, { stdio: 'pipe' });
|
||||
log(`✅ Successfully executed: ${test.description}`);
|
||||
testsCompleted++;
|
||||
|
||||
// Wait between tests
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
} catch (deeplinkError) {
|
||||
const errorMessage = deeplinkError.message || '';
|
||||
|
||||
// Handle specific error for URL scheme not registered
|
||||
if (errorMessage.includes('OSStatus error -10814') || errorMessage.includes('NSOSStatusErrorDomain, code=-10814')) {
|
||||
log(`⚠️ URL scheme not properly handled: ${test.description}`);
|
||||
testsSkipped++;
|
||||
} else {
|
||||
log(`⚠️ Failed to execute deeplink test: ${test.description}`);
|
||||
log(`⚠️ Error: ${errorMessage}`);
|
||||
}
|
||||
log('⚠️ Continuing with next test...');
|
||||
}
|
||||
}
|
||||
|
||||
log(`✅ Deeplink tests completed: ${testsCompleted} successful, ${testsSkipped} skipped`);
|
||||
|
||||
if (testsSkipped > 0) {
|
||||
log('\n📝 Note about skipped tests:');
|
||||
log('1. The app needs to have the URL scheme registered in Info.plist');
|
||||
log('2. The app needs to be rebuilt after registering the URL scheme');
|
||||
log('3. The app must be running in the foreground for deeplink tests to work');
|
||||
log('4. If these conditions are met and tests still fail, check URL handling in the app code');
|
||||
}
|
||||
} catch (error) {
|
||||
log(`❌ Deeplink tests setup failed: ${error.message}`);
|
||||
log('⚠️ Deeplink tests might be unavailable or test data is missing');
|
||||
// Don't rethrow the error to prevent halting the process
|
||||
}
|
||||
};
|
||||
|
||||
// Check and register URL scheme if needed
|
||||
const checkAndRegisterUrlScheme = (log) => {
|
||||
log('🔍 Checking if URL scheme is registered in Info.plist...');
|
||||
|
||||
const infoPlistPath = 'ios/App/App/Info.plist';
|
||||
|
||||
// Check if Info.plist exists
|
||||
if (!existsSync(infoPlistPath)) {
|
||||
log('⚠️ Info.plist not found at: ' + infoPlistPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read Info.plist content
|
||||
const infoPlistContent = readFileSync(infoPlistPath, 'utf8');
|
||||
|
||||
// Check if URL scheme is already registered
|
||||
if (infoPlistContent.includes('<string>timesafari</string>')) {
|
||||
log('✅ URL scheme "timesafari://" is already registered in Info.plist');
|
||||
return true;
|
||||
}
|
||||
|
||||
log('⚠️ URL scheme "timesafari://" is not registered in Info.plist');
|
||||
log('⚠️ Attempting to register the URL scheme automatically...');
|
||||
|
||||
try {
|
||||
// Look for the closing dict tag to insert our URL types
|
||||
const closingDictIndex = infoPlistContent.lastIndexOf('</dict>');
|
||||
if (closingDictIndex === -1) {
|
||||
log('⚠️ Could not find closing dict tag in Info.plist');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create URL types entry
|
||||
const urlTypesEntry = `
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>app.timesafari.app</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>timesafari</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>`;
|
||||
|
||||
// Insert URL types entry before closing dict
|
||||
const updatedPlistContent =
|
||||
infoPlistContent.substring(0, closingDictIndex) +
|
||||
urlTypesEntry +
|
||||
infoPlistContent.substring(closingDictIndex);
|
||||
|
||||
// Write updated content back to Info.plist
|
||||
const { writeFileSync } = require('fs');
|
||||
writeFileSync(infoPlistPath, updatedPlistContent, 'utf8');
|
||||
|
||||
log('✅ URL scheme "timesafari://" registered in Info.plist');
|
||||
log('⚠️ You will need to rebuild the app for changes to take effect');
|
||||
return true;
|
||||
} catch (error) {
|
||||
log(`⚠️ Failed to register URL scheme: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs the complete iOS test suite including build and testing
|
||||
*
|
||||
* The function performs the following steps:
|
||||
* 1. Syncs the Capacitor project with latest web build
|
||||
* 2. Builds the app using xcodebuild
|
||||
* 3. Optionally runs tests if configured
|
||||
* 4. Launches the app in the simulator
|
||||
*
|
||||
* If no simulator is running, it automatically selects and boots one.
|
||||
*
|
||||
* @async
|
||||
* @throws {Error} If any step in the build process fails
|
||||
*
|
||||
* @example
|
||||
* runIosTests().catch(error => {
|
||||
* console.error('Test execution failed:', error);
|
||||
* process.exit(1);
|
||||
* });
|
||||
*/
|
||||
async function runIosTests() {
|
||||
// Create build_logs directory if it doesn't exist
|
||||
if (!existsSync('build_logs')) {
|
||||
mkdirSync('build_logs');
|
||||
}
|
||||
|
||||
const logFile = getLogFileName();
|
||||
const log = createLogger(logFile);
|
||||
|
||||
try {
|
||||
log('🚀 Starting iOS build and test process...');
|
||||
|
||||
// Generate test data first
|
||||
await generateTestData(log);
|
||||
|
||||
// Verify Xcode installation
|
||||
verifyXcodeInstallation(log);
|
||||
|
||||
// Check for simulator or boot one if needed
|
||||
const simulator = await checkSimulator(log);
|
||||
|
||||
// Build web assets and configure iOS project
|
||||
await buildWebAssets(log);
|
||||
await configureIosProject(log);
|
||||
|
||||
// Build and test using the selected simulator
|
||||
await buildAndTestIos(log, simulator);
|
||||
|
||||
// Run the app in the simulator
|
||||
await runIosApp(log, simulator);
|
||||
|
||||
// Run deeplink tests after app is installed
|
||||
await runDeeplinkTests(log);
|
||||
|
||||
log('🎉 iOS build and test process completed successfully');
|
||||
log(`📝 Full build log available at: ${logFile}`);
|
||||
} catch (error) {
|
||||
log(`❌ iOS tests failed: ${error.message}`);
|
||||
log(`📝 Check build log for details: ${logFile}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the test suite
|
||||
runIosTests();
|
||||
118
src/App.vue
118
src/App.vue
@@ -40,7 +40,10 @@
|
||||
<div
|
||||
class="flex items-center justify-center w-12 bg-slate-600 text-slate-100"
|
||||
>
|
||||
<fa icon="circle-info" class="fa-fw fa-xl"></fa>
|
||||
<font-awesome
|
||||
icon="circle-info"
|
||||
class="fa-fw fa-xl"
|
||||
></font-awesome>
|
||||
</div>
|
||||
|
||||
<div class="relative w-full pl-4 pr-8 py-2 text-slate-900">
|
||||
@@ -48,10 +51,10 @@
|
||||
<p class="text-sm">{{ truncateLongWords(notification.text) }}</p>
|
||||
|
||||
<button
|
||||
@click="close(notification.id)"
|
||||
class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-slate-200 text-slate-600"
|
||||
@click="close(notification.id)"
|
||||
>
|
||||
<fa icon="xmark" class="fa-fw"></fa>
|
||||
<font-awesome icon="xmark" class="fa-fw"></font-awesome>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -63,7 +66,10 @@
|
||||
<div
|
||||
class="flex items-center justify-center w-12 bg-emerald-600 text-emerald-100"
|
||||
>
|
||||
<fa icon="circle-info" class="fa-fw fa-xl"></fa>
|
||||
<font-awesome
|
||||
icon="circle-info"
|
||||
class="fa-fw fa-xl"
|
||||
></font-awesome>
|
||||
</div>
|
||||
|
||||
<div class="relative w-full pl-4 pr-8 py-2 text-emerald-900">
|
||||
@@ -71,10 +77,10 @@
|
||||
<p class="text-sm">{{ truncateLongWords(notification.text) }}</p>
|
||||
|
||||
<button
|
||||
@click="close(notification.id)"
|
||||
class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-emerald-200 text-emerald-600"
|
||||
@click="close(notification.id)"
|
||||
>
|
||||
<fa icon="xmark" class="fa-fw"></fa>
|
||||
<font-awesome icon="xmark" class="fa-fw"></font-awesome>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -86,7 +92,10 @@
|
||||
<div
|
||||
class="flex items-center justify-center w-12 bg-amber-600 text-amber-100"
|
||||
>
|
||||
<fa icon="triangle-exclamation" class="fa-fw fa-xl"></fa>
|
||||
<font-awesome
|
||||
icon="triangle-exclamation"
|
||||
class="fa-fw fa-xl"
|
||||
></font-awesome>
|
||||
</div>
|
||||
|
||||
<div class="relative w-full pl-4 pr-8 py-2 text-amber-900">
|
||||
@@ -94,10 +103,10 @@
|
||||
<p class="text-sm">{{ truncateLongWords(notification.text) }}</p>
|
||||
|
||||
<button
|
||||
@click="close(notification.id)"
|
||||
class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-amber-200 text-amber-600"
|
||||
@click="close(notification.id)"
|
||||
>
|
||||
<fa icon="xmark" class="fa-fw"></fa>
|
||||
<font-awesome icon="xmark" class="fa-fw"></font-awesome>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -109,7 +118,10 @@
|
||||
<div
|
||||
class="flex items-center justify-center w-12 bg-rose-600 text-rose-100"
|
||||
>
|
||||
<fa icon="triangle-exclamation" class="fa-fw fa-xl"></fa>
|
||||
<font-awesome
|
||||
icon="triangle-exclamation"
|
||||
class="fa-fw fa-xl"
|
||||
></font-awesome>
|
||||
</div>
|
||||
|
||||
<div class="relative w-full pl-4 pr-8 py-2 text-rose-900">
|
||||
@@ -117,10 +129,10 @@
|
||||
<p class="text-sm">{{ truncateLongWords(notification.text) }}</p>
|
||||
|
||||
<button
|
||||
@click="close(notification.id)"
|
||||
class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-rose-200 text-rose-600"
|
||||
@click="close(notification.id)"
|
||||
>
|
||||
<fa icon="xmark" class="fa-fw"></fa>
|
||||
<font-awesome icon="xmark" class="fa-fw"></font-awesome>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -131,7 +143,8 @@
|
||||
|
||||
<!--
|
||||
This "group" of "modal" is the prompt for an answer.
|
||||
Set "type" as follows: "confirm" for yes/no, and "notification" ones: "-permission", "-mute", "-off"
|
||||
Set "type" as follows: "confirm" for yes/no, and "notification" ones:
|
||||
"-permission", "-mute", "-off"
|
||||
-->
|
||||
<NotificationGroup group="modal">
|
||||
<div class="fixed z-[100] top-0 inset-x-0 w-full">
|
||||
@@ -174,11 +187,11 @@
|
||||
|
||||
<button
|
||||
v-if="notification.onYes"
|
||||
class="block w-full text-center text-md font-bold uppercase bg-blue-600 text-white px-2 py-2 rounded-md mb-2"
|
||||
@click="
|
||||
notification.onYes();
|
||||
close(notification.id);
|
||||
"
|
||||
class="block w-full text-center text-md font-bold uppercase bg-blue-600 text-white px-2 py-2 rounded-md mb-2"
|
||||
>
|
||||
Yes{{
|
||||
notification.yesText ? ", " + notification.yesText : ""
|
||||
@@ -187,12 +200,12 @@
|
||||
|
||||
<button
|
||||
v-if="notification.onNo"
|
||||
class="block w-full text-center text-md font-bold uppercase bg-yellow-600 text-white px-2 py-2 rounded-md mb-2"
|
||||
@click="
|
||||
notification.onNo(stopAsking);
|
||||
close(notification.id);
|
||||
stopAsking = false; // reset value
|
||||
"
|
||||
class="block w-full text-center text-md font-bold uppercase bg-yellow-600 text-white px-2 py-2 rounded-md mb-2"
|
||||
>
|
||||
No{{ notification.noText ? ", " + notification.noText : "" }}
|
||||
</button>
|
||||
@@ -209,8 +222,8 @@
|
||||
<div class="relative ml-2">
|
||||
<!-- input -->
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="stopAsking"
|
||||
type="checkbox"
|
||||
name="stopAsking"
|
||||
class="sr-only"
|
||||
/>
|
||||
@@ -224,6 +237,7 @@
|
||||
</label>
|
||||
|
||||
<button
|
||||
class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md"
|
||||
@click="
|
||||
notification.onCancel
|
||||
? notification.onCancel(stopAsking)
|
||||
@@ -231,7 +245,6 @@
|
||||
close(notification.id);
|
||||
stopAsking = false; // reset value for next time they open this modal
|
||||
"
|
||||
class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md"
|
||||
>
|
||||
{{ notification.onYes ? "Cancel" : "Close" }}
|
||||
</button>
|
||||
@@ -270,8 +283,8 @@
|
||||
Until I turn it back on
|
||||
</button>
|
||||
<button
|
||||
@click="close(notification.id)"
|
||||
class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md"
|
||||
@click="close(notification.id)"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
@@ -292,17 +305,17 @@
|
||||
</p>
|
||||
|
||||
<button
|
||||
class="block w-full text-center text-md font-bold uppercase bg-rose-600 text-white px-2 py-2 rounded-md mb-2"
|
||||
@click="
|
||||
close(notification.id);
|
||||
turnOffNotifications(notification);
|
||||
"
|
||||
class="block w-full text-center text-md font-bold uppercase bg-rose-600 text-white px-2 py-2 rounded-md mb-2"
|
||||
>
|
||||
Turn Off Notification
|
||||
</button>
|
||||
<button
|
||||
@click="close(notification.id)"
|
||||
class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md"
|
||||
@click="close(notification.id)"
|
||||
>
|
||||
Leave it On
|
||||
</button>
|
||||
@@ -315,12 +328,11 @@
|
||||
</NotificationGroup>
|
||||
</template>
|
||||
|
||||
<style></style>
|
||||
|
||||
<script lang="ts">
|
||||
import { Vue, Component } from "vue-facing-decorator";
|
||||
import { logConsoleAndDb, retrieveSettingsForActiveAccount } from "./db/index";
|
||||
import { NotificationIface } from "./constants/app";
|
||||
import { logger } from "./utils/logger";
|
||||
|
||||
interface Settings {
|
||||
notifyingNewActivityTime?: string;
|
||||
@@ -334,38 +346,38 @@ export default class App extends Vue {
|
||||
stopAsking = false;
|
||||
|
||||
// created() {
|
||||
// console.log(
|
||||
// logger.log(
|
||||
// "Component created: Reactivity set up.",
|
||||
// window.location.pathname,
|
||||
// );
|
||||
// }
|
||||
|
||||
// beforeCreate() {
|
||||
// console.log("Component beforeCreate: Instance initialized.");
|
||||
// logger.log("Component beforeCreate: Instance initialized.");
|
||||
// }
|
||||
|
||||
// beforeMount() {
|
||||
// console.log("Component beforeMount: Template is about to be rendered.");
|
||||
// logger.log("Component beforeMount: Template is about to be rendered.");
|
||||
// }
|
||||
|
||||
// mounted() {
|
||||
// console.log("Component mounted: Template is now rendered.");
|
||||
// logger.log("Component mounted: Template is now rendered.");
|
||||
// }
|
||||
|
||||
// beforeUpdate() {
|
||||
// console.log("Component beforeUpdate: DOM is about to be updated.");
|
||||
// logger.log("Component beforeUpdate: DOM is about to be updated.");
|
||||
// }
|
||||
|
||||
// updated() {
|
||||
// console.log("Component updated: DOM has been updated.");
|
||||
// logger.log("Component updated: DOM has been updated.");
|
||||
// }
|
||||
|
||||
// beforeUnmount() {
|
||||
// console.log("Component beforeUnmount: Cleaning up before removal.");
|
||||
// logger.log("Component beforeUnmount: Cleaning up before removal.");
|
||||
// }
|
||||
|
||||
// unmounted() {
|
||||
// console.log("Component unmounted: Component removed from the DOM.");
|
||||
// logger.log("Component unmounted: Component removed from the DOM.");
|
||||
// }
|
||||
|
||||
truncateLongWords(sentence: string) {
|
||||
@@ -378,42 +390,42 @@ export default class App extends Vue {
|
||||
async turnOffNotifications(
|
||||
notification: NotificationIface,
|
||||
): Promise<boolean> {
|
||||
console.log("Starting turnOffNotifications...");
|
||||
logger.log("Starting turnOffNotifications...");
|
||||
let subscription: PushSubscriptionJSON | null = null;
|
||||
let allGoingOff = false;
|
||||
|
||||
try {
|
||||
console.log("Retrieving settings for the active account...");
|
||||
logger.log("Retrieving settings for the active account...");
|
||||
const settings: Settings = await retrieveSettingsForActiveAccount();
|
||||
console.log("Retrieved settings:", settings);
|
||||
logger.log("Retrieved settings:", settings);
|
||||
|
||||
const notifyingNewActivity = !!settings?.notifyingNewActivityTime;
|
||||
const notifyingReminder = !!settings?.notifyingReminderTime;
|
||||
|
||||
if (!notifyingNewActivity || !notifyingReminder) {
|
||||
allGoingOff = true;
|
||||
console.log("Both notifications are being turned off.");
|
||||
logger.log("Both notifications are being turned off.");
|
||||
}
|
||||
|
||||
console.log("Checking service worker readiness...");
|
||||
logger.log("Checking service worker readiness...");
|
||||
await navigator.serviceWorker?.ready
|
||||
.then((registration) => {
|
||||
console.log("Service worker is ready. Fetching subscription...");
|
||||
logger.log("Service worker is ready. Fetching subscription...");
|
||||
return registration.pushManager.getSubscription();
|
||||
})
|
||||
.then(async (subscript: PushSubscription | null) => {
|
||||
if (subscript) {
|
||||
subscription = subscript.toJSON();
|
||||
console.log("PushSubscription retrieved:", subscription);
|
||||
logger.log("PushSubscription retrieved:", subscription);
|
||||
|
||||
if (allGoingOff) {
|
||||
console.log("Unsubscribing from push notifications...");
|
||||
logger.log("Unsubscribing from push notifications...");
|
||||
await subscript.unsubscribe();
|
||||
console.log("Successfully unsubscribed.");
|
||||
logger.log("Successfully unsubscribed.");
|
||||
}
|
||||
} else {
|
||||
logConsoleAndDb("Subscription object is not available.");
|
||||
console.log("No subscription found.");
|
||||
logger.log("No subscription found.");
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -422,11 +434,11 @@ export default class App extends Vue {
|
||||
JSON.stringify(error),
|
||||
true,
|
||||
);
|
||||
console.error("Error during subscription fetch:", error);
|
||||
logger.error("Error during subscription fetch:", error);
|
||||
});
|
||||
|
||||
if (!subscription) {
|
||||
console.log("No subscription available. Notifying user...");
|
||||
logger.log("No subscription available. Notifying user...");
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
@@ -436,7 +448,7 @@ export default class App extends Vue {
|
||||
},
|
||||
5000,
|
||||
);
|
||||
console.log("Exiting as there is no subscription to process.");
|
||||
logger.log("Exiting as there is no subscription to process.");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -445,12 +457,12 @@ export default class App extends Vue {
|
||||
};
|
||||
if (!allGoingOff) {
|
||||
serverSubscription["notifyType"] = notification.title;
|
||||
console.log(
|
||||
logger.log(
|
||||
`Server subscription updated with notifyType: ${notification.title}`,
|
||||
);
|
||||
}
|
||||
|
||||
console.log("Sending unsubscribe request to the server...");
|
||||
logger.log("Sending unsubscribe request to the server...");
|
||||
const pushServerSuccess = await fetch("/web-push/unsubscribe", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -465,9 +477,9 @@ export default class App extends Vue {
|
||||
`Push server failed: ${response.status} ${errorBody}`,
|
||||
true,
|
||||
);
|
||||
console.error("Push server error response:", errorBody);
|
||||
logger.error("Push server error response:", errorBody);
|
||||
}
|
||||
console.log(`Server response status: ${response.status}`);
|
||||
logger.log(`Server response status: ${response.status}`);
|
||||
return response.ok;
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -475,14 +487,14 @@ export default class App extends Vue {
|
||||
"Push server communication failed: " + JSON.stringify(error),
|
||||
true,
|
||||
);
|
||||
console.error("Error during server communication:", error);
|
||||
logger.error("Error during server communication:", error);
|
||||
return false;
|
||||
});
|
||||
|
||||
const message = pushServerSuccess
|
||||
? "Notification is off."
|
||||
: "Notification is still on. Try to turn it off again.";
|
||||
console.log("Server response processed. Message:", message);
|
||||
logger.log("Server response processed. Message:", message);
|
||||
|
||||
this.$notify(
|
||||
{
|
||||
@@ -495,11 +507,11 @@ export default class App extends Vue {
|
||||
);
|
||||
|
||||
if (notification.callback) {
|
||||
console.log("Executing notification callback...");
|
||||
logger.log("Executing notification callback...");
|
||||
notification.callback(pushServerSuccess);
|
||||
}
|
||||
|
||||
console.log(
|
||||
logger.log(
|
||||
"Completed turnOffNotifications with success:",
|
||||
pushServerSuccess,
|
||||
);
|
||||
@@ -509,7 +521,7 @@ export default class App extends Vue {
|
||||
"Error turning off notifications: " + JSON.stringify(error),
|
||||
true,
|
||||
);
|
||||
console.error("Critical error in turnOffNotifications:", error);
|
||||
logger.error("Critical error in turnOffNotifications:", error);
|
||||
|
||||
this.$notify(
|
||||
{
|
||||
@@ -526,3 +538,5 @@ export default class App extends Vue {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
|
||||
@@ -29,29 +29,29 @@
|
||||
<p class="text-sm mb-2">{{ text }}</p>
|
||||
|
||||
<button
|
||||
@click="handleOption1(close)"
|
||||
class="block w-full text-center text-md font-bold capitalize bg-blue-800 text-white px-2 py-2 rounded-md mb-2"
|
||||
@click="handleOption1(close)"
|
||||
>
|
||||
{{ option1Text }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
@click="handleOption2(close)"
|
||||
class="block w-full text-center text-md font-bold capitalize bg-blue-700 text-white px-2 py-2 rounded-md mb-2"
|
||||
@click="handleOption2(close)"
|
||||
>
|
||||
{{ option2Text }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
@click="handleOption3(close)"
|
||||
class="block w-full text-center text-md font-bold capitalize bg-blue-600 text-white px-2 py-2 rounded-md mb-2"
|
||||
@click="handleOption3(close)"
|
||||
>
|
||||
{{ option3Text }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
@click="handleCancel(close)"
|
||||
class="block w-full text-center text-md font-bold capitalize bg-slate-600 text-white px-2 py-2 rounded-md"
|
||||
@click="handleCancel(close)"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
{{ message }}
|
||||
Note that their name is only stored on this device.
|
||||
<input
|
||||
v-model="newText"
|
||||
type="text"
|
||||
placeholder="Name"
|
||||
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
||||
v-model="newText"
|
||||
/>
|
||||
|
||||
<div class="mt-8">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div v-html="generateIcon()" class="w-fit"></div>
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<div class="w-fit" v-html="generateIcon()"></div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { createAvatar, StyleOptions } from "@dicebear/core";
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
<div class="relative ml-2">
|
||||
<!-- input -->
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="hasVisibleDid"
|
||||
type="checkbox"
|
||||
name="toggleFilterFromMyContacts"
|
||||
class="sr-only"
|
||||
/>
|
||||
@@ -46,8 +46,8 @@
|
||||
<div v-if="hasSearchBox" class="relative ml-2">
|
||||
<!-- input -->
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="isNearby"
|
||||
type="checkbox"
|
||||
name="toggleFilterNearby"
|
||||
class="sr-only"
|
||||
/>
|
||||
@@ -98,7 +98,7 @@ import {
|
||||
LRectangle,
|
||||
LTileLayer,
|
||||
} from "@vue-leaflet/vue-leaflet";
|
||||
|
||||
import { Router } from "vue-router";
|
||||
import { MASTER_SETTINGS_KEY } from "../db/tables/settings";
|
||||
import { db, retrieveSettingsForActiveAccount } from "../db/index";
|
||||
|
||||
@@ -111,6 +111,7 @@ import { db, retrieveSettingsForActiveAccount } from "../db/index";
|
||||
},
|
||||
})
|
||||
export default class FeedFilters extends Vue {
|
||||
$router!: Router;
|
||||
onCloseIfChanged = () => {};
|
||||
hasSearchBox = false;
|
||||
hasVisibleDid = false;
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
{{ customTitle }}
|
||||
</h1>
|
||||
<input
|
||||
v-model="description"
|
||||
type="text"
|
||||
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
|
||||
:placeholder="prompt || 'What was given?'"
|
||||
v-model="description"
|
||||
/>
|
||||
<div class="flex flex-row justify-center">
|
||||
<span
|
||||
@@ -21,19 +21,19 @@
|
||||
class="border border-r-0 border-slate-400 bg-slate-200 px-4 py-2"
|
||||
@click="amountInput === '0' ? null : decrement()"
|
||||
>
|
||||
<fa icon="chevron-left" />
|
||||
<font-awesome icon="chevron-left" />
|
||||
</div>
|
||||
<input
|
||||
id="inputGivenAmount"
|
||||
v-model="amountInput"
|
||||
type="number"
|
||||
class="border border-r-0 border-slate-400 px-2 py-2 text-center w-20"
|
||||
v-model="amountInput"
|
||||
/>
|
||||
<div
|
||||
class="rounded-r border border-slate-400 bg-slate-200 px-4 py-2"
|
||||
@click="increment()"
|
||||
>
|
||||
<fa icon="chevron-right" />
|
||||
<font-awesome icon="chevron-right" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 flex justify-center">
|
||||
@@ -62,7 +62,7 @@
|
||||
</div>
|
||||
<p class="text-center mb-2 mt-6 italic">
|
||||
Sign & Send to publish to the world
|
||||
<fa
|
||||
<font-awesome
|
||||
icon="circle-info"
|
||||
class="pl-2 text-blue-500 cursor-pointer"
|
||||
@click="explainData()"
|
||||
@@ -162,7 +162,7 @@ export default class GiftedDialog extends Vue {
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (err: any) {
|
||||
console.error("Error retrieving settings from database:", err);
|
||||
logger.error("Error retrieving settings from database:", err);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
@@ -311,7 +311,7 @@ export default class GiftedDialog extends Vue {
|
||||
this.isGiveCreationError(result.response)
|
||||
) {
|
||||
const errorMessage = this.getGiveCreationErrorMessage(result);
|
||||
console.error("Error with give creation result:", result);
|
||||
logger.error("Error with give creation result:", result);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
@@ -337,7 +337,7 @@ export default class GiftedDialog extends Vue {
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
console.error("Error with give recordation caught:", error);
|
||||
logger.error("Error with give recordation caught:", error);
|
||||
const errorMessage =
|
||||
error.userMessage ||
|
||||
serverMessageForUser(error) ||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
class="text-lg text-center p-2 leading-none absolute right-0 -top-1"
|
||||
@click="cancel"
|
||||
>
|
||||
<fa icon="xmark" class="w-[1em]"></fa>
|
||||
<font-awesome icon="xmark" class="w-[1em]"></font-awesome>
|
||||
</div>
|
||||
</h1>
|
||||
<span class="mt-2 flex justify-between">
|
||||
@@ -16,7 +16,7 @@
|
||||
class="rounded-l border border-slate-400 bg-slate-200 px-4 py-2 flex"
|
||||
@click="prevIdea()"
|
||||
>
|
||||
<fa icon="chevron-left" class="m-auto" />
|
||||
<font-awesome icon="chevron-left" class="m-auto" />
|
||||
</span>
|
||||
|
||||
<div class="m-2">
|
||||
@@ -45,7 +45,7 @@
|
||||
class="text-center bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mt-4"
|
||||
@click="nextIdeaPastContacts()"
|
||||
>
|
||||
Skip Contacts <fa icon="forward" />
|
||||
Skip Contacts <font-awesome icon="forward" />
|
||||
</button>
|
||||
</span>
|
||||
</span>
|
||||
@@ -57,7 +57,7 @@
|
||||
class="rounded-r border border-slate-400 bg-slate-200 px-4 py-2 flex"
|
||||
@click="nextIdea()"
|
||||
>
|
||||
<fa icon="chevron-right" class="m-auto" />
|
||||
<font-awesome icon="chevron-right" class="m-auto" />
|
||||
</span>
|
||||
</span>
|
||||
<button
|
||||
@@ -82,6 +82,7 @@ import { GiverReceiverInputInfo } from "../libs/util";
|
||||
@Component
|
||||
export default class GivenPrompts extends Vue {
|
||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||
$router!: Router;
|
||||
|
||||
CATEGORY_CONTACTS = 1;
|
||||
CATEGORY_IDEAS = 0;
|
||||
@@ -145,7 +146,7 @@ export default class GivenPrompts extends Vue {
|
||||
// proceed with logic but don't change values (just in case some actions are added later)
|
||||
this.visible = false;
|
||||
if (this.currentCategory === this.CATEGORY_IDEAS) {
|
||||
(this.$router as Router).push({
|
||||
this.$router.push({
|
||||
name: "contact-gift",
|
||||
query: {
|
||||
prompt: this.IDEAS[this.currentIdeaIndex],
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
<!-- Header -->
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="text-xl font-bold capitalize">{{ roleName }} Details</h2>
|
||||
<button @click="close" class="text-gray-500 hover:text-gray-700">
|
||||
<fa icon="times" />
|
||||
<button class="text-gray-500 hover:text-gray-700" @click="close">
|
||||
<font-awesome icon="times" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -53,7 +53,10 @@
|
||||
target="_blank"
|
||||
class="text-blue-500"
|
||||
>
|
||||
<fa icon="arrow-up-right-from-square" class="fa-fw" />
|
||||
<font-awesome
|
||||
icon="arrow-up-right-from-square"
|
||||
class="fa-fw"
|
||||
/>
|
||||
</a>
|
||||
</span>
|
||||
</span>
|
||||
@@ -66,7 +69,7 @@
|
||||
<div class="mt-4">
|
||||
<span v-if="canShare">
|
||||
If you'd like an introduction,
|
||||
<a @click="onClickShareClaim()" class="text-blue-500"
|
||||
<a class="text-blue-500" @click="onClickShareClaim()"
|
||||
>click here to share the information with them and ask if they'll
|
||||
tell you more about the {{ roleName }}.</a
|
||||
>
|
||||
@@ -74,8 +77,8 @@
|
||||
<span v-else>
|
||||
If you'd like an introduction,
|
||||
<a
|
||||
@click="copyToClipboard('A link to this page', windowLocation)"
|
||||
class="text-blue-500"
|
||||
@click="copyToClipboard('A link to this page', windowLocation)"
|
||||
>click here to copy this page, paste it into a message, and ask if
|
||||
they'll tell you more about the {{ roleName }}.</a
|
||||
>
|
||||
@@ -86,8 +89,8 @@
|
||||
<!-- Footer -->
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
@click="close"
|
||||
class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
|
||||
@click="close"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
class="text-lg text-center px-2 py-0.5 leading-none absolute right-0 top-0 text-white"
|
||||
@click="close()"
|
||||
>
|
||||
<fa icon="xmark" class="w-[1em]"></fa>
|
||||
<font-awesome icon="xmark" class="w-[1em]"></font-awesome>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="text-center mt-8">
|
||||
<div>
|
||||
<fa
|
||||
<font-awesome
|
||||
icon="camera"
|
||||
class="bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-2 rounded-md"
|
||||
@click="openPhotoDialog()"
|
||||
@@ -31,17 +31,21 @@
|
||||
<div class="mt-4">
|
||||
<span class="mt-2">
|
||||
... or paste a URL:
|
||||
<input type="text" v-model="imageUrl" class="border-2" />
|
||||
<input v-model="imageUrl" type="text" class="border-2" />
|
||||
</span>
|
||||
<span class="ml-2">
|
||||
<fa
|
||||
<font-awesome
|
||||
v-if="imageUrl"
|
||||
icon="check"
|
||||
class="bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-2 rounded-md cursor-pointer"
|
||||
@click="acceptUrl"
|
||||
/>
|
||||
<!-- so that there's no shifting when it becomes visible -->
|
||||
<fa v-else icon="check" class="text-white bg-white px-2 py-2" />
|
||||
<font-awesome
|
||||
v-else
|
||||
icon="check"
|
||||
class="text-white bg-white px-2 py-2"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
class="text-white text-2xl p-2 rounded-full hover:bg-white/10"
|
||||
@click="close"
|
||||
>
|
||||
<fa icon="xmark" />
|
||||
<font-awesome icon="xmark" />
|
||||
</button>
|
||||
|
||||
<!-- Mobile share button -->
|
||||
@@ -17,7 +17,7 @@
|
||||
class="text-white text-xl p-2 rounded-full hover:bg-white/10"
|
||||
@click="handleShare"
|
||||
>
|
||||
<fa icon="ellipsis" />
|
||||
<font-awesome icon="ellipsis" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
<img
|
||||
:src="imageUrl"
|
||||
class="max-h-[calc(100vh-5rem)] w-full h-full object-contain"
|
||||
@click.stop
|
||||
alt="expanded shared content"
|
||||
@click.stop
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -74,7 +74,7 @@ export default class ImageViewer extends Vue {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Share failed, opening in new tab:", error);
|
||||
logger.warn("Share failed, opening in new tab:", error);
|
||||
window.open(this.imageUrl, "_blank");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,18 +8,18 @@
|
||||
If you want to store your own way, the invitation ID is:
|
||||
{{ inviteIdentifier }}
|
||||
<input
|
||||
v-model="text"
|
||||
type="text"
|
||||
placeholder="Notes"
|
||||
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
||||
v-model="text"
|
||||
/>
|
||||
|
||||
<!-- Add date selection element -->
|
||||
Expiration
|
||||
<input
|
||||
v-model="expiresAt"
|
||||
type="date"
|
||||
class="block rounded border border-slate-400 mb-4 px-3 py-2"
|
||||
v-model="expiresAt"
|
||||
/>
|
||||
|
||||
<div class="mt-8">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
v-if="isLoading"
|
||||
class="mt-16 text-center text-4xl bg-slate-400 text-white w-14 py-2.5 rounded-full mx-auto"
|
||||
>
|
||||
<fa icon="spinner" class="fa-spin-pulse" />
|
||||
<font-awesome icon="spinner" class="fa-spin-pulse" />
|
||||
</div>
|
||||
|
||||
<!-- Members List -->
|
||||
@@ -33,13 +33,13 @@
|
||||
<span
|
||||
class="mx-2 min-w-[24px] min-h-[24px] w-6 h-6 flex items-center justify-center rounded-full bg-blue-100 text-blue-600"
|
||||
>
|
||||
<fa icon="plus" class="text-sm" />
|
||||
<font-awesome icon="plus" class="text-sm" />
|
||||
</span>
|
||||
/
|
||||
<span
|
||||
class="mx-2 min-w-[24px] min-h-[24px] w-6 h-6 flex items-center justify-center rounded-full bg-blue-100 text-blue-600"
|
||||
>
|
||||
<fa icon="minus" class="text-sm" />
|
||||
<font-awesome icon="minus" class="text-sm" />
|
||||
</span>
|
||||
to add/remove them to/from the meeting.
|
||||
</span>
|
||||
@@ -54,20 +54,23 @@
|
||||
<span
|
||||
class="mx-2 w-8 h-8 flex items-center justify-center rounded-full bg-green-100 text-green-600"
|
||||
>
|
||||
<fa icon="circle-user" class="text-xl" />
|
||||
<font-awesome icon="circle-user" class="text-xl" />
|
||||
</span>
|
||||
to add them to your contacts.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center">
|
||||
<!-- always have at least one refresh button even without members in case the organizer changes the password -->
|
||||
<!--
|
||||
always have at least one refresh button even without members in case the organizer
|
||||
changes the password
|
||||
-->
|
||||
<button
|
||||
@click="fetchMembers"
|
||||
class="w-8 h-8 flex items-center justify-center rounded-full bg-blue-100 text-blue-600 hover:bg-blue-200 hover:text-blue-800 transition-colors"
|
||||
title="Refresh members list"
|
||||
@click="fetchMembers"
|
||||
>
|
||||
<fa icon="rotate" :class="{ 'fa-spin': isLoading }" />
|
||||
<font-awesome icon="rotate" :class="{ 'fa-spin': isLoading }" />
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
@@ -83,24 +86,24 @@
|
||||
class="flex justify-end"
|
||||
>
|
||||
<button
|
||||
@click="addAsContact(member)"
|
||||
class="ml-2 w-8 h-8 flex items-center justify-center rounded-full bg-green-100 text-green-600 hover:bg-green-200 hover:text-green-800 transition-colors"
|
||||
title="Add as contact"
|
||||
@click="addAsContact(member)"
|
||||
>
|
||||
<fa icon="circle-user" class="text-xl" />
|
||||
<font-awesome icon="circle-user" class="text-xl" />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
v-if="member.did !== activeDid"
|
||||
class="ml-2 mb-2 w-6 h-6 flex items-center justify-center rounded-full bg-slate-100 text-slate-500 hover:bg-slate-200 hover:text-slate-800 transition-colors"
|
||||
title="Contact info"
|
||||
@click="
|
||||
informAboutAddingContact(
|
||||
getContactFor(member.did) !== undefined,
|
||||
)
|
||||
"
|
||||
class="ml-2 mb-2 w-6 h-6 flex items-center justify-center rounded-full bg-slate-100 text-slate-500 hover:bg-slate-200 hover:text-slate-800 transition-colors"
|
||||
title="Contact info"
|
||||
>
|
||||
<fa icon="circle-info" class="text-base" />
|
||||
<font-awesome icon="circle-info" class="text-base" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex">
|
||||
@@ -111,23 +114,23 @@
|
||||
class="flex items-center"
|
||||
>
|
||||
<button
|
||||
@click="checkWhetherContactBeforeAdmitting(member)"
|
||||
class="mr-2 w-6 h-6 flex items-center justify-center rounded-full bg-blue-100 text-blue-600 hover:bg-blue-200 hover:text-blue-800 transition-colors"
|
||||
:title="
|
||||
member.member.admitted ? 'Remove member' : 'Admit member'
|
||||
"
|
||||
@click="checkWhetherContactBeforeAdmitting(member)"
|
||||
>
|
||||
<fa
|
||||
<font-awesome
|
||||
:icon="member.member.admitted ? 'minus' : 'plus'"
|
||||
class="text-sm"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
@click="informAboutAdmission()"
|
||||
class="mr-2 mb-2 w-6 h-6 flex items-center justify-center rounded-full bg-slate-100 text-slate-500 hover:bg-slate-200 hover:text-slate-800 transition-colors"
|
||||
title="Admission info"
|
||||
@click="informAboutAdmission()"
|
||||
>
|
||||
<fa icon="circle-info" class="text-base" />
|
||||
<font-awesome icon="circle-info" class="text-base" />
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
@@ -138,11 +141,11 @@
|
||||
</div>
|
||||
<div v-if="membersToShow().length > 0" class="flex justify-center mt-4">
|
||||
<button
|
||||
@click="fetchMembers"
|
||||
class="w-8 h-8 flex items-center justify-center rounded-full bg-blue-100 text-blue-600 hover:bg-blue-200 hover:text-blue-800 transition-colors"
|
||||
title="Refresh members list"
|
||||
@click="fetchMembers"
|
||||
>
|
||||
<fa icon="rotate" :class="{ 'fa-spin': isLoading }" />
|
||||
<font-awesome icon="rotate" :class="{ 'fa-spin': isLoading }" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<div class="dialog">
|
||||
<h1 class="text-xl font-bold text-center mb-4">Offer Help</h1>
|
||||
<input
|
||||
v-model="description"
|
||||
type="text"
|
||||
data-testId="inputDescription"
|
||||
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
|
||||
placeholder="Description of what is offered"
|
||||
v-model="description"
|
||||
/>
|
||||
<div class="flex flex-row mt-2">
|
||||
<span
|
||||
@@ -17,23 +17,23 @@
|
||||
{{ libsUtil.UNIT_SHORT[amountUnitCode] }}
|
||||
</span>
|
||||
<div
|
||||
v-if="amountInput !== '0'"
|
||||
class="border border-r-0 border-slate-400 bg-slate-200 px-4 py-2"
|
||||
@click="decrement()"
|
||||
v-if="amountInput !== '0'"
|
||||
>
|
||||
<fa icon="chevron-left" />
|
||||
<font-awesome icon="chevron-left" />
|
||||
</div>
|
||||
<input
|
||||
v-model="amountInput"
|
||||
data-testId="inputOfferAmount"
|
||||
type="number"
|
||||
class="w-full border border-r-0 border-slate-400 px-2 py-2 text-center"
|
||||
v-model="amountInput"
|
||||
/>
|
||||
<div
|
||||
class="rounded-r border border-slate-400 bg-slate-200 px-4 py-2"
|
||||
@click="increment()"
|
||||
>
|
||||
<fa icon="chevron-right" />
|
||||
<font-awesome icon="chevron-right" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 flex justify-center">
|
||||
@@ -89,6 +89,7 @@ import {
|
||||
} from "../libs/endorserServer";
|
||||
import * as libsUtil from "../libs/util";
|
||||
import { retrieveSettingsForActiveAccount } from "../db/index";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
@Component
|
||||
export default class OfferDialog extends Vue {
|
||||
@@ -121,7 +122,7 @@ export default class OfferDialog extends Vue {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (err: any) {
|
||||
console.error("Error retrieving settings from database:", err);
|
||||
logger.error("Error retrieving settings from database:", err);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
@@ -249,7 +250,7 @@ export default class OfferDialog extends Vue {
|
||||
this.isOfferCreationError(result.response)
|
||||
) {
|
||||
const errorMessage = this.getOfferCreationErrorMessage(result);
|
||||
console.error("Error with offer creation result:", result);
|
||||
logger.error("Error with offer creation result:", result);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
@@ -272,7 +273,7 @@ export default class OfferDialog extends Vue {
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
console.error("Error with offer recordation caught:", error);
|
||||
logger.error("Error with offer recordation caught:", error);
|
||||
const message =
|
||||
error.userMessage ||
|
||||
error.response?.data?.error?.message ||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
class="text-lg text-center leading-none absolute right-0 -top-1"
|
||||
@click="onClickClose(true)"
|
||||
>
|
||||
<fa icon="xmark" class="w-[1em]" />
|
||||
<font-awesome icon="xmark" class="w-[1em]" />
|
||||
</div>
|
||||
</h1>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</span>
|
||||
click on the
|
||||
<span class="bg-green-600 text-white rounded-full">
|
||||
<fa icon="plus" class="fa-fw" />
|
||||
<font-awesome icon="plus" class="fa-fw" />
|
||||
</span>
|
||||
button to express your appreciation for... whatever -- maybe thanks for
|
||||
showing you all these fascinating stories of
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
<p class="mt-4 flex items-center">
|
||||
The
|
||||
<fa
|
||||
<font-awesome
|
||||
icon="house-chimney"
|
||||
class="ml-1 mr-1 text-lg text-white bg-slate-400 px-2 py-2 rounded"
|
||||
/>
|
||||
@@ -84,7 +84,7 @@
|
||||
class="text-lg text-center leading-none absolute right-0 -top-1"
|
||||
@click="onClickClose(true)"
|
||||
>
|
||||
<fa icon="xmark" class="w-[1em]" />
|
||||
<font-awesome icon="xmark" class="w-[1em]" />
|
||||
</div>
|
||||
</h1>
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
|
||||
<p class="mt-4 flex items-center">
|
||||
The
|
||||
<fa
|
||||
<font-awesome
|
||||
icon="magnifying-glass"
|
||||
class="ml-1 mr-1 text-lg text-white bg-slate-400 px-2 py-2 rounded"
|
||||
/>
|
||||
@@ -141,14 +141,14 @@
|
||||
class="text-lg text-center leading-none absolute right-0 -top-1"
|
||||
@click="onClickClose(true)"
|
||||
>
|
||||
<fa icon="xmark" class="w-[1em]" />
|
||||
<font-awesome icon="xmark" class="w-[1em]" />
|
||||
</div>
|
||||
</h1>
|
||||
|
||||
<p class="relative">
|
||||
Now you can take a turn: click on the
|
||||
<span class="bg-green-600 text-white rounded-full">
|
||||
<fa icon="plus" class="fa-fw" />
|
||||
<font-awesome icon="plus" class="fa-fw" />
|
||||
</span>
|
||||
button to throw out projects of your own... anything you'd like to see
|
||||
happen. If your first idea doesn't catch anyone, try, try again... and
|
||||
@@ -157,7 +157,7 @@
|
||||
|
||||
<p class="mt-4 flex items-center">
|
||||
The
|
||||
<fa
|
||||
<font-awesome
|
||||
icon="hand"
|
||||
class="ml-1 mr-1 text-lg text-white bg-slate-400 px-2 py-2 rounded"
|
||||
/>
|
||||
@@ -168,7 +168,7 @@
|
||||
By the way, one good way to get to know your neighbors and their
|
||||
interests is to offer time directly to them. You can do this on the
|
||||
contacts screen
|
||||
<fa icon="users" class="text-slate-500" />
|
||||
<font-awesome icon="users" class="text-slate-500" />
|
||||
which is a great way to get to know a neighbor's interests.
|
||||
</p>
|
||||
|
||||
@@ -219,6 +219,7 @@ import { OnboardPage } from "../libs/util";
|
||||
})
|
||||
export default class OnboardingDialog extends Vue {
|
||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||
$router!: Router;
|
||||
|
||||
activeDid = "";
|
||||
firstContactName = null;
|
||||
@@ -254,7 +255,7 @@ export default class OnboardingDialog extends Vue {
|
||||
finishedOnboarding: true,
|
||||
});
|
||||
if (goHome) {
|
||||
(this.$router as Router).push({ name: "home" });
|
||||
this.$router.push({ name: "home" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
class="text-lg text-center px-2 py-0.5 leading-none absolute right-0 top-0 text-white"
|
||||
@click="close()"
|
||||
>
|
||||
<fa icon="xmark" class="w-[1em]"></fa>
|
||||
<font-awesome icon="xmark" class="w-[1em]"></font-awesome>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="uploading" class="flex justify-center">
|
||||
<fa
|
||||
<font-awesome
|
||||
icon="spinner"
|
||||
class="fa-spin fa-3x text-center block px-12 py-12"
|
||||
/>
|
||||
@@ -28,7 +28,7 @@
|
||||
<div v-else-if="blob">
|
||||
<div v-if="crop">
|
||||
<VuePictureCropper
|
||||
:boxStyle="{
|
||||
:box-style="{
|
||||
backgroundColor: '#f8f8f8',
|
||||
margin: 'auto',
|
||||
}"
|
||||
@@ -56,8 +56,8 @@
|
||||
</div>
|
||||
<div class="absolute bottom-[1rem] left-[1rem] px-2 py-1">
|
||||
<button
|
||||
@click="uploadImage"
|
||||
class="bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white py-1 px-2 rounded-md"
|
||||
@click="uploadImage"
|
||||
>
|
||||
<span>Upload</span>
|
||||
</button>
|
||||
@@ -67,8 +67,8 @@
|
||||
class="absolute bottom-[1rem] right-[1rem] px-2 py-1"
|
||||
>
|
||||
<button
|
||||
@click="retryImage"
|
||||
class="bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white py-1 px-2 rounded-md"
|
||||
@click="retryImage"
|
||||
>
|
||||
<span>Retry</span>
|
||||
</button>
|
||||
@@ -81,37 +81,37 @@
|
||||
:resolution="{ width: 375, height: 812 }"
|
||||
-->
|
||||
<camera
|
||||
facingMode="environment"
|
||||
autoplay
|
||||
ref="camera"
|
||||
facing-mode="environment"
|
||||
autoplay
|
||||
@started="cameraStarted()"
|
||||
>
|
||||
<div
|
||||
class="absolute portrait:bottom-0 portrait:left-0 portrait:right-0 portrait:pb-2 landscape:right-0 landscape:top-0 landscape:bottom-0 landscape:pr-4 flex landscape:flex-row justify-center items-center"
|
||||
>
|
||||
<button
|
||||
@click="takeImage()"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-3 rounded-full text-2xl leading-none"
|
||||
@click="takeImage()"
|
||||
>
|
||||
<fa icon="camera" class="w-[1em]"></fa>
|
||||
<font-awesome icon="camera" class="w-[1em]"></font-awesome>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="absolute portrait:bottom-2 portrait:right-16 landscape:right-0 landscape:bottom-16 landscape:pr-4 flex justify-center items-center"
|
||||
>
|
||||
<button
|
||||
@click="swapMirrorClass()"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-3 rounded-full text-2xl leading-none"
|
||||
@click="swapMirrorClass()"
|
||||
>
|
||||
<fa icon="left-right" class="w-[1em]"></fa>
|
||||
<font-awesome icon="left-right" class="w-[1em]"></font-awesome>
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="numDevices > 1" class="absolute bottom-2 right-4">
|
||||
<button
|
||||
@click="switchCamera()"
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold p-3 rounded-full text-2xl leading-none"
|
||||
@click="switchCamera()"
|
||||
>
|
||||
<fa icon="rotate" class="w-[1em]"></fa>
|
||||
<font-awesome icon="rotate" class="w-[1em]"></font-awesome>
|
||||
</button>
|
||||
</div>
|
||||
</camera>
|
||||
@@ -129,6 +129,7 @@ import VuePictureCropper, { cropper } from "vue-picture-cropper";
|
||||
import { DEFAULT_IMAGE_API_SERVER, NotificationIface } from "../constants/app";
|
||||
import { retrieveSettingsForActiveAccount } from "../db/index";
|
||||
import { accessToken } from "../libs/crypto";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
@Component({ components: { Camera, VuePictureCropper } })
|
||||
export default class PhotoDialog extends Vue {
|
||||
@@ -155,7 +156,7 @@ export default class PhotoDialog extends Vue {
|
||||
this.activeDid = settings.activeDid || "";
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (err: any) {
|
||||
console.error("Error retrieving settings from database:", err);
|
||||
logger.error("Error retrieving settings from database:", err);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
@@ -373,7 +374,7 @@ export default class PhotoDialog extends Vue {
|
||||
window.location.hostname === "localhost" &&
|
||||
!DEFAULT_IMAGE_API_SERVER.includes("localhost")
|
||||
) {
|
||||
console.log(
|
||||
logger.log(
|
||||
"Using shared image API server, so only users on that server can play with images.",
|
||||
);
|
||||
}
|
||||
@@ -387,7 +388,7 @@ export default class PhotoDialog extends Vue {
|
||||
this.close();
|
||||
this.setImageCallback(response.data.url as string);
|
||||
} catch (error) {
|
||||
console.error("Error uploading the image", error);
|
||||
logger.error("Error uploading the image", error);
|
||||
this.$notify(
|
||||
{
|
||||
group: "alert",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<template>
|
||||
<a
|
||||
v-if="linkToFull && imageUrl"
|
||||
@@ -5,12 +6,12 @@
|
||||
target="_blank"
|
||||
class="h-full w-full object-contain"
|
||||
>
|
||||
<div v-html="generateIdenticon()" class="h-full w-full object-contain" />
|
||||
<div class="h-full w-full object-contain" v-html="generateIdenticon()" />
|
||||
</a>
|
||||
<div
|
||||
v-else
|
||||
v-html="generateIdenticon()"
|
||||
class="h-full w-full object-contain"
|
||||
v-html="generateIdenticon()"
|
||||
/>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</p>
|
||||
<p v-else class="text-lg mb-4">
|
||||
Waiting for system initialization, which may take up to 5 seconds...
|
||||
<fa icon="spinner" spin />
|
||||
<font-awesome icon="spinner" spin />
|
||||
</p>
|
||||
|
||||
<div v-if="serviceWorkerReady && vapidKey">
|
||||
@@ -54,23 +54,25 @@
|
||||
<span class="flex flex-row justify-center">
|
||||
<span class="mt-2">... at: </span>
|
||||
<input
|
||||
type="number"
|
||||
@change="checkHourInput"
|
||||
class="rounded-l border border-r-0 border-slate-400 ml-2 mt-2 px-2 py-2 text-center w-20"
|
||||
v-model="hourInput"
|
||||
type="number"
|
||||
class="rounded-l border border-r-0 border-slate-400 ml-2 mt-2 px-2 py-2 text-center w-20"
|
||||
@change="checkHourInput"
|
||||
/>
|
||||
<input
|
||||
type="number"
|
||||
@change="checkMinuteInput"
|
||||
class="border border-slate-400 mt-2 px-2 py-2 text-center w-20"
|
||||
v-model="minuteInput"
|
||||
type="number"
|
||||
class="border border-slate-400 mt-2 px-2 py-2 text-center w-20"
|
||||
@change="checkMinuteInput"
|
||||
/>
|
||||
<span
|
||||
class="rounded-r border border-slate-400 bg-slate-200 text-center text-blue-500 mt-2 px-2 py-2 w-20"
|
||||
@click="hourAm = !hourAm"
|
||||
>
|
||||
<span v-if="hourAm"> AM <fa icon="chevron-down" /> </span>
|
||||
<span v-else> PM <fa icon="chevron-up" /> </span>
|
||||
<span v-if="hourAm">
|
||||
AM <font-awesome icon="chevron-down" />
|
||||
</span>
|
||||
<span v-else> PM <font-awesome icon="chevron-up" /> </span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@@ -86,8 +88,8 @@
|
||||
</div>
|
||||
|
||||
<button
|
||||
@click="close()"
|
||||
class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white mt-4 px-2 py-2 rounded-md"
|
||||
@click="close()"
|
||||
>
|
||||
No, Not Now
|
||||
</button>
|
||||
@@ -109,6 +111,7 @@ import {
|
||||
import { MASTER_SECRET_KEY } from "../db/tables/secret";
|
||||
import { urlBase64ToUint8Array } from "../libs/crypto/vc/util";
|
||||
import * as libsUtil from "../libs/util";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
// Example interface for error
|
||||
interface ErrorResponse {
|
||||
@@ -493,13 +496,13 @@ export default class PushNotificationPermission extends Vue {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
if (!("serviceWorker" in navigator && "PushManager" in window)) {
|
||||
const errorMsg = "Push messaging is not supported";
|
||||
console.warn(errorMsg);
|
||||
logger.warn(errorMsg);
|
||||
return reject(new Error(errorMsg));
|
||||
}
|
||||
|
||||
if (window.Notification.permission !== "granted") {
|
||||
const errorMsg = "Notification permission not granted";
|
||||
console.warn(errorMsg);
|
||||
logger.warn(errorMsg);
|
||||
return reject(new Error(errorMsg));
|
||||
}
|
||||
|
||||
@@ -560,7 +563,7 @@ export default class PushNotificationPermission extends Vue {
|
||||
body: JSON.stringify(subscription),
|
||||
}).then((response) => {
|
||||
if (!response.ok) {
|
||||
console.error("Bad response subscribing to web push: ", response);
|
||||
logger.error("Bad response subscribing to web push: ", response);
|
||||
throw new Error("Failed to send push subscription to server");
|
||||
}
|
||||
logConsoleAndDb("Push subscription sent to server successfully.");
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
>
|
||||
<router-link :to="{ name: 'home' }" class="block text-center py-2 px-1">
|
||||
<div class="flex flex-col items-center">
|
||||
<fa icon="house-chimney" class="fa-fw" />
|
||||
<font-awesome icon="house-chimney" class="fa-fw" />
|
||||
<span class="text-xs mt-1">feed</span>
|
||||
</div>
|
||||
</router-link>
|
||||
@@ -32,7 +32,7 @@
|
||||
class="block text-center py-2 px-1"
|
||||
>
|
||||
<div class="flex flex-col items-center">
|
||||
<fa icon="magnifying-glass" class="fa-fw" />
|
||||
<font-awesome icon="magnifying-glass" class="fa-fw" />
|
||||
<span class="text-xs mt-1">search</span>
|
||||
</div>
|
||||
</router-link>
|
||||
@@ -51,7 +51,7 @@
|
||||
class="block text-center py-2 px-1"
|
||||
>
|
||||
<div class="flex flex-col items-center">
|
||||
<fa icon="hand" class="fa-fw" />
|
||||
<font-awesome icon="hand" class="fa-fw" />
|
||||
<span class="text-xs mt-1">your work</span>
|
||||
</div>
|
||||
</router-link>
|
||||
@@ -70,7 +70,7 @@
|
||||
class="block text-center py-2 px-1"
|
||||
>
|
||||
<div class="flex flex-col items-center">
|
||||
<fa icon="users" class="fa-fw" />
|
||||
<font-awesome icon="users" class="fa-fw" />
|
||||
<span class="text-xs mt-1">contacts</span>
|
||||
</div>
|
||||
</router-link>
|
||||
@@ -89,12 +89,13 @@
|
||||
class="block text-center py-2 px-1"
|
||||
>
|
||||
<div class="flex flex-col items-center">
|
||||
<fa icon="circle-user" class="fa-fw" />
|
||||
<font-awesome icon="circle-user" class="fa-fw" />
|
||||
<!--
|
||||
We used to say "account", so we'll keep that in the code,
|
||||
but it isn't accurate because we don't hold anything for them.
|
||||
We'll say "profile" to the users.
|
||||
(Or: settings, face, registry, cache, repo, vault... or separate preferences from identity.)
|
||||
(Or: settings, face, registry, cache, repo, vault... or separate
|
||||
preferences from identity.)
|
||||
-->
|
||||
<span class="text-xs mt-1">profile</span>
|
||||
</div>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
|
||||
{{ sharingExplanation }}
|
||||
<input
|
||||
v-model="givenName"
|
||||
type="text"
|
||||
placeholder="Name"
|
||||
class="block w-full rounded border border-slate-400 mb-4 px-3 py-2"
|
||||
v-model="givenName"
|
||||
/>
|
||||
|
||||
<div class="mt-8">
|
||||
|
||||
@@ -5,6 +5,7 @@ import * as SkeletonUtils from "three/addons/utils/SkeletonUtils";
|
||||
import * as TWEEN from "@tweenjs/tween.js";
|
||||
import { retrieveSettingsForActiveAccount } from "../../../../db";
|
||||
import { getHeaders } from "../../../../libs/endorserServer";
|
||||
import { logger } from "../../../../utils/logger";
|
||||
|
||||
const ANIMATION_DURATION_SECS = 10;
|
||||
const ENDORSER_ENTITY_PREFIX = "https://endorser.ch/entity/";
|
||||
@@ -82,7 +83,7 @@ export async function loadLandmarks(vue, world, scene, loop) {
|
||||
},
|
||||
undefined,
|
||||
function (error) {
|
||||
console.error(error);
|
||||
logger.error(error);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -117,7 +118,7 @@ export async function loadLandmarks(vue, world, scene, loop) {
|
||||
world.lights = [...world.lights, light];
|
||||
}
|
||||
} else {
|
||||
console.error(
|
||||
logger.error(
|
||||
"Got bad server response status & data of",
|
||||
resp.status,
|
||||
resp.data,
|
||||
@@ -128,7 +129,7 @@ export async function loadLandmarks(vue, world, scene, loop) {
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Got exception contacting server:", error);
|
||||
logger.error("Got exception contacting server:", error);
|
||||
vue.setAlert(
|
||||
"Error With Server",
|
||||
"There was a problem retrieving your claims from the server.",
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from "./tables/settings";
|
||||
import { Temp, TempSchema } from "./tables/temp";
|
||||
import { DEFAULT_ENDORSER_API_SERVER } from "../constants/app";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
// Define types for tables that hold sensitive and non-sensitive data
|
||||
type SecretTable = { secret: Table<Secret> };
|
||||
@@ -219,9 +220,9 @@ export async function logConsoleAndDb(
|
||||
isError = false,
|
||||
): Promise<void> {
|
||||
if (isError) {
|
||||
console.error(`${new Date().toISOString()} ${message}`);
|
||||
logger.error(`${new Date().toISOString()} ${message}`);
|
||||
} else {
|
||||
console.log(`${new Date().toISOString()} ${message}`);
|
||||
logger.log(`${new Date().toISOString()} ${message}`);
|
||||
}
|
||||
|
||||
await db.open();
|
||||
|
||||
@@ -20,7 +20,7 @@ export type Settings = {
|
||||
// active Decentralized ID
|
||||
activeDid?: string; // only used in the MASTER_SETTINGS_KEY entry
|
||||
|
||||
apiServer: string; // API server URL
|
||||
apiServer?: string; // API server URL
|
||||
|
||||
filterFeedByNearby?: boolean; // filter by nearby
|
||||
filterFeedByVisible?: boolean; // filter by visible users ie. anyone not hidden
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const { app, BrowserWindow } = require("electron");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const logger = require("../utils/logger");
|
||||
|
||||
// Check if running in dev mode
|
||||
const isDev = process.argv.includes("--inspect");
|
||||
@@ -8,8 +9,8 @@ const isDev = process.argv.includes("--inspect");
|
||||
function createWindow() {
|
||||
// Add before createWindow function
|
||||
const preloadPath = path.join(__dirname, "preload.js");
|
||||
console.log("Checking preload path:", preloadPath);
|
||||
console.log("Preload exists:", fs.existsSync(preloadPath));
|
||||
logger.log("Checking preload path:", preloadPath);
|
||||
logger.log("Preload exists:", fs.existsSync(preloadPath));
|
||||
|
||||
// Create the browser window.
|
||||
const mainWindow = new BrowserWindow({
|
||||
@@ -65,25 +66,25 @@ function createWindow() {
|
||||
|
||||
if (isDev) {
|
||||
// Debug info
|
||||
console.log("Debug Info:");
|
||||
console.log("Running in dev mode:", isDev);
|
||||
console.log("App is packaged:", app.isPackaged);
|
||||
console.log("Process resource path:", process.resourcesPath);
|
||||
console.log("App path:", app.getAppPath());
|
||||
console.log("__dirname:", __dirname);
|
||||
console.log("process.cwd():", process.cwd());
|
||||
logger.log("Debug Info:");
|
||||
logger.log("Running in dev mode:", isDev);
|
||||
logger.log("App is packaged:", app.isPackaged);
|
||||
logger.log("Process resource path:", process.resourcesPath);
|
||||
logger.log("App path:", app.getAppPath());
|
||||
logger.log("__dirname:", __dirname);
|
||||
logger.log("process.cwd():", process.cwd());
|
||||
}
|
||||
|
||||
const indexPath = path.join(__dirname, "www", "index.html");
|
||||
|
||||
if (isDev) {
|
||||
console.log("Loading index from:", indexPath);
|
||||
console.log("www path:", path.join(__dirname, "www"));
|
||||
console.log("www assets path:", path.join(__dirname, "www", "assets"));
|
||||
logger.log("Loading index from:", indexPath);
|
||||
logger.log("www path:", path.join(__dirname, "www"));
|
||||
logger.log("www assets path:", path.join(__dirname, "www", "assets"));
|
||||
}
|
||||
|
||||
if (!fs.existsSync(indexPath)) {
|
||||
console.error(`Index file not found at: ${indexPath}`);
|
||||
logger.error(`Index file not found at: ${indexPath}`);
|
||||
throw new Error("Index file not found");
|
||||
}
|
||||
|
||||
@@ -110,38 +111,38 @@ function createWindow() {
|
||||
mainWindow
|
||||
.loadFile(indexPath)
|
||||
.then(() => {
|
||||
console.log("Successfully loaded index.html");
|
||||
logger.log("Successfully loaded index.html");
|
||||
if (isDev) {
|
||||
mainWindow.webContents.openDevTools();
|
||||
console.log("DevTools opened - running in dev mode");
|
||||
logger.log("DevTools opened - running in dev mode");
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Failed to load index.html:", err);
|
||||
console.error("Attempted path:", indexPath);
|
||||
logger.error("Failed to load index.html:", err);
|
||||
logger.error("Attempted path:", indexPath);
|
||||
});
|
||||
|
||||
// Listen for console messages from the renderer
|
||||
mainWindow.webContents.on("console-message", (_event, level, message) => {
|
||||
console.log("Renderer Console:", message);
|
||||
logger.log("Renderer Console:", message);
|
||||
});
|
||||
|
||||
// Add right after creating the BrowserWindow
|
||||
mainWindow.webContents.on(
|
||||
"did-fail-load",
|
||||
(event, errorCode, errorDescription) => {
|
||||
console.error("Page failed to load:", errorCode, errorDescription);
|
||||
logger.error("Page failed to load:", errorCode, errorDescription);
|
||||
},
|
||||
);
|
||||
|
||||
mainWindow.webContents.on("preload-error", (event, preloadPath, error) => {
|
||||
console.error("Preload script error:", preloadPath, error);
|
||||
logger.error("Preload script error:", preloadPath, error);
|
||||
});
|
||||
|
||||
mainWindow.webContents.on(
|
||||
"console-message",
|
||||
(event, level, message, line, sourceId) => {
|
||||
console.log("Renderer Console:", line, sourceId, message);
|
||||
logger.log("Renderer Console:", line, sourceId, message);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -169,5 +170,5 @@ app.on("activate", () => {
|
||||
|
||||
// Handle any errors
|
||||
process.on("uncaughtException", (error) => {
|
||||
console.error("Uncaught Exception:", error);
|
||||
logger.error("Uncaught Exception:", error);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
const { contextBridge, ipcRenderer } = require("electron");
|
||||
|
||||
const logger = {
|
||||
log: (message, ...args) => {
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
/* eslint-disable no-console */
|
||||
console.log(message, ...args);
|
||||
/* eslint-enable no-console */
|
||||
}
|
||||
},
|
||||
warn: (message, ...args) => {
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
/* eslint-disable no-console */
|
||||
console.warn(message, ...args);
|
||||
/* eslint-enable no-console */
|
||||
}
|
||||
},
|
||||
error: (message, ...args) => {
|
||||
/* eslint-disable no-console */
|
||||
console.error(message, ...args); // Errors should always be logged
|
||||
/* eslint-enable no-console */
|
||||
},
|
||||
};
|
||||
|
||||
// Use a more direct path resolution approach
|
||||
const getPath = (pathType) => {
|
||||
switch (pathType) {
|
||||
@@ -19,7 +41,7 @@ const getPath = (pathType) => {
|
||||
}
|
||||
};
|
||||
|
||||
console.log("Preload script starting...");
|
||||
logger.log("Preload script starting...");
|
||||
|
||||
try {
|
||||
contextBridge.exposeInMainWorld("electronAPI", {
|
||||
@@ -50,7 +72,7 @@ try {
|
||||
},
|
||||
});
|
||||
|
||||
console.log("Preload script completed successfully");
|
||||
logger.log("Preload script completed successfully");
|
||||
} catch (error) {
|
||||
console.error("Error in preload script:", error);
|
||||
logger.error("Error in preload script:", error);
|
||||
}
|
||||
|
||||
58
src/interfaces/claims-result.ts
Normal file
58
src/interfaces/claims-result.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { AxiosResponse } from "axios";
|
||||
import { GiverReceiverInputInfo } from "../libs/util";
|
||||
import { ErrorResult, ResultWithType } from "./common";
|
||||
|
||||
export interface GiverOutputInfo {
|
||||
action: string;
|
||||
giver?: GiverReceiverInputInfo;
|
||||
description?: string;
|
||||
amount?: number;
|
||||
unitCode?: string;
|
||||
}
|
||||
|
||||
export interface ClaimResult {
|
||||
success: { claimId: string; handleId: string };
|
||||
error: { code: string; message: string };
|
||||
}
|
||||
|
||||
export interface VerifiableCredential {
|
||||
exp?: number;
|
||||
iat: number;
|
||||
iss: string;
|
||||
vc: {
|
||||
"@context": string[];
|
||||
type: string[];
|
||||
credentialSubject: VerifiableCredentialSubject;
|
||||
};
|
||||
}
|
||||
|
||||
export interface VerifiableCredentialSubject {
|
||||
"@context": string;
|
||||
"@type": string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface WorldProperties {
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
}
|
||||
|
||||
export interface ProviderInfo {
|
||||
/**
|
||||
* Could be a DID or a handleId that identifies the provider
|
||||
*/
|
||||
identifier: string;
|
||||
/**
|
||||
* Indicates if the provider link has been confirmed
|
||||
*/
|
||||
linkConfirmed: boolean;
|
||||
}
|
||||
|
||||
// Type for createAndSubmitClaim result
|
||||
export type CreateAndSubmitClaimResult = SuccessResult | ErrorResult;
|
||||
|
||||
// Update SuccessResult to use ClaimResult
|
||||
export interface SuccessResult extends ResultWithType {
|
||||
type: "success";
|
||||
response: AxiosResponse<ClaimResult>;
|
||||
}
|
||||
68
src/interfaces/claims.ts
Normal file
68
src/interfaces/claims.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { GenericVerifiableCredential } from "./common";
|
||||
|
||||
export interface AgreeVerifiableCredential {
|
||||
"@context": string;
|
||||
"@type": string;
|
||||
object: Record<string, unknown>;
|
||||
}
|
||||
|
||||
// Note that previous VCs may have additional fields.
|
||||
// https://endorser.ch/doc/html/transactions.html#id4
|
||||
export interface GiveVerifiableCredential extends GenericVerifiableCredential {
|
||||
"@context"?: string;
|
||||
"@type": "GiveAction";
|
||||
agent?: { identifier: string };
|
||||
description?: string;
|
||||
fulfills?: { "@type": string; identifier?: string; lastClaimId?: string }[];
|
||||
identifier?: string;
|
||||
image?: string;
|
||||
object?: { amountOfThisGood: number; unitCode: string };
|
||||
provider?: GenericVerifiableCredential;
|
||||
recipient?: { identifier: string };
|
||||
}
|
||||
|
||||
// Note that previous VCs may have additional fields.
|
||||
// https://endorser.ch/doc/html/transactions.html#id8
|
||||
export interface OfferVerifiableCredential extends GenericVerifiableCredential {
|
||||
"@context"?: string;
|
||||
"@type": "Offer";
|
||||
description?: string;
|
||||
includesObject?: { amountOfThisGood: number; unitCode: string };
|
||||
itemOffered?: {
|
||||
description?: string;
|
||||
isPartOf?: {
|
||||
identifier?: string;
|
||||
lastClaimId?: string;
|
||||
"@type"?: string;
|
||||
name?: string;
|
||||
};
|
||||
};
|
||||
offeredBy?: { identifier: string };
|
||||
recipient?: { identifier: string };
|
||||
validThrough?: string;
|
||||
}
|
||||
|
||||
// Note that previous VCs may have additional fields.
|
||||
// https://endorser.ch/doc/html/transactions.html#id7
|
||||
export interface PlanVerifiableCredential extends GenericVerifiableCredential {
|
||||
"@context": "https://schema.org";
|
||||
"@type": "PlanAction";
|
||||
name: string;
|
||||
agent?: { identifier: string };
|
||||
description?: string;
|
||||
identifier?: string;
|
||||
lastClaimId?: string;
|
||||
location?: {
|
||||
geo: { "@type": "GeoCoordinates"; latitude: number; longitude: number };
|
||||
};
|
||||
}
|
||||
|
||||
// AKA Registration & RegisterAction
|
||||
export interface RegisterVerifiableCredential {
|
||||
"@context": string;
|
||||
"@type": "RegisterAction";
|
||||
agent: { identifier: string };
|
||||
identifier?: string;
|
||||
object: string;
|
||||
participant?: { identifier: string };
|
||||
}
|
||||
36
src/interfaces/common.ts
Normal file
36
src/interfaces/common.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
// similar to VerifiableCredentialSubject... maybe rename this
|
||||
export interface GenericVerifiableCredential {
|
||||
"@context"?: string;
|
||||
"@type": string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface GenericCredWrapper<T extends GenericVerifiableCredential> {
|
||||
claim: T;
|
||||
claimType?: string;
|
||||
handleId: string;
|
||||
id: string;
|
||||
issuedAt: string;
|
||||
issuer: string;
|
||||
publicUrls?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface ResultWithType {
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface ErrorResponse {
|
||||
error?: {
|
||||
message?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface InternalError {
|
||||
error: string;
|
||||
userMessage?: string;
|
||||
}
|
||||
|
||||
export interface ErrorResult extends ResultWithType {
|
||||
type: "error";
|
||||
error: InternalError;
|
||||
}
|
||||
13
src/interfaces/deepLinks.ts
Normal file
13
src/interfaces/deepLinks.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @file Deep Link Interface Definitions
|
||||
* @author Matthew Raymer
|
||||
*
|
||||
* Defines the core interfaces for the deep linking system.
|
||||
* These interfaces are used across the deep linking implementation
|
||||
* to ensure type safety and consistent error handling.
|
||||
*/
|
||||
|
||||
export interface DeepLinkError extends Error {
|
||||
code: string;
|
||||
details?: unknown;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user