Merge branch 'master' into retire-legacy-notification
This commit is contained in:
@@ -13,7 +13,7 @@ This guard is in `android/app/build.gradle`. Do NOT change this conditional to a
|
|||||||
|
|
||||||
`google-services.json` is intentionally excluded from git (`android/.gitignore`). Never commit it.
|
`google-services.json` is intentionally excluded from git (`android/.gitignore`). Never commit it.
|
||||||
|
|
||||||
Full details, incident history, and F-Droid notes: `doc/development/android-firebase-gms.md`
|
Full details, incident history, and F-Droid notes: `doc/android-firebase-gms.md`
|
||||||
|
|
||||||
## Android Build — MLKit Barcode Scanner
|
## Android Build — MLKit Barcode Scanner
|
||||||
|
|
||||||
|
|||||||
+44
-22
@@ -1084,6 +1084,11 @@ npm run build:ios:assets # Generate assets only
|
|||||||
npm run build:ios:deploy # Build and deploy to connected device
|
npm run build:ios:deploy # Build and deploy to connected device
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The iOS asset command reads source artwork from `resources/` explicitly. It
|
||||||
|
generates the default 1024×1024 App Store icon, from which Xcode derives the
|
||||||
|
required device sizes, and installs any available appearance variants under
|
||||||
|
`ios/App/App/Assets.xcassets/AppIcon.appiconset/`.
|
||||||
|
|
||||||
#### Automated Build Script
|
#### Automated Build Script
|
||||||
|
|
||||||
The recommended way to build for iOS is using the automated build script:
|
The recommended way to build for iOS is using the automated build script:
|
||||||
@@ -1127,7 +1132,7 @@ If you need to build manually or want to understand the individual steps:
|
|||||||
|
|
||||||
##### 0. First time (or if dependencies change)
|
##### 0. First time (or if dependencies change)
|
||||||
|
|
||||||
- `pkgx +rubygems.org +pod zsh`
|
- `pkgx +rubygems.org +pod /bin/zsh`
|
||||||
|
|
||||||
- ... and you may have to fix these, especially with pkgx:
|
- ... and you may have to fix these, especially with pkgx:
|
||||||
|
|
||||||
@@ -1141,7 +1146,7 @@ export GEM_PATH=$shortened_path
|
|||||||
##### 1. Bump the version in package.json & CHANGELOG.md for `MARKETING_VERSION`, then `grep CURRENT_PROJECT_VERSION ios/App/App.xcodeproj/project.pbxproj` and add 1 for the numbered version here:
|
##### 1. Bump the version in package.json & CHANGELOG.md for `MARKETING_VERSION`, then `grep CURRENT_PROJECT_VERSION ios/App/App.xcodeproj/project.pbxproj` and add 1 for the numbered version here:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ios/App && xcrun agvtool new-version 70 && perl -p -i -e "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 1.4.4;/g" App.xcodeproj/project.pbxproj && cd -
|
cd ios/App && xcrun agvtool new-version 72 && perl -p -i -e "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 1.4.6;/g" App.xcodeproj/project.pbxproj && cd -
|
||||||
# Unfortunately this edits Info.plist directly.
|
# Unfortunately this edits Info.plist directly.
|
||||||
#xcrun agvtool new-marketing-version 0.4.5
|
#xcrun agvtool new-marketing-version 0.4.5
|
||||||
```
|
```
|
||||||
@@ -1162,14 +1167,20 @@ npm run build:ios:prod
|
|||||||
|
|
||||||
3.2. Use Xcode to release.
|
3.2. Use Xcode to release.
|
||||||
|
|
||||||
- Someday: Under "General" we want to rename a bunch of things to "Time Safari"
|
- Someday: Under "General" we want to rename a bunch of things to "Giftopia"
|
||||||
- Choose Product -> Destination -> Any iOS Device
|
- Choose Product -> Destination -> Any iOS Device
|
||||||
- Choose Product -> Archive
|
- Choose Product -> Archive
|
||||||
- This will trigger a build and take time, needing user's "login" keychain
|
- This will trigger a build and take time, needing user's "login" keychain
|
||||||
password (user's login password), repeatedly.
|
password (user's login password), repeatedly.
|
||||||
|
- Required password entry 26 times.
|
||||||
|
- It can get stuck on uploading SPI; try canceling and restarting.
|
||||||
- If it fails with `building for 'iOS', but linking in dylib
|
- If it fails with `building for 'iOS', but linking in dylib
|
||||||
(.../.pkgx/zlib.net/v1.3.0/lib/libz.1.3.dylib) built for 'macOS'` then run
|
(.../.pkgx/zlib.net/v1.3.0/lib/libz.1.3.dylib) built for 'macOS'` then run
|
||||||
XCode outside that terminal (ie. not with `npx cap open ios`).
|
XCode outside that terminal (ie. not with `npx cap open ios`).
|
||||||
|
- Before distributing, confirm the archive carries production server
|
||||||
|
defaults. Xcode packages whatever web bundle is in `ios/App/App/public`,
|
||||||
|
even one from a `--test` build:
|
||||||
|
`./scripts/check-prod-bundle.sh ios/App/App/public`
|
||||||
- Click Distribute -> App Store Connect
|
- Click Distribute -> App Store Connect
|
||||||
- In AppStoreConnect, add the build to the distribution. You may have to remove
|
- In AppStoreConnect, add the build to the distribution. You may have to remove
|
||||||
the current build with the "-" when you hover over it, then "Add Build" with the
|
the current build with the "-" when you hover over it, then "Add Build" with the
|
||||||
@@ -1319,6 +1330,7 @@ npm run build:android:prod # Production build (builds for production e
|
|||||||
# Auto-run builds
|
# Auto-run builds
|
||||||
npm run build:android:test:run # Test build with auto-run (builds then runs on emulator)
|
npm run build:android:test:run # Test build with auto-run (builds then runs on emulator)
|
||||||
npm run build:android:prod:run # Production build with auto-run (builds then runs on emulator)
|
npm run build:android:prod:run # Production build with auto-run (builds then runs on emulator)
|
||||||
|
# Auto-run targets the one connected device; with none or several it prompts, or exits 9 without a terminal
|
||||||
|
|
||||||
# Debug and release builds
|
# Debug and release builds
|
||||||
npm run build:android:debug # Debug build (builds debug APK)
|
npm run build:android:debug # Debug build (builds debug APK)
|
||||||
@@ -1422,8 +1434,8 @@ The recommended way to build for Android is using the automated build script:
|
|||||||
##### 1. Bump the version in package.json, then update these versions & run:
|
##### 1. Bump the version in package.json, then update these versions & run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
perl -p -i -e 's/versionCode .*/versionCode 70/g' android/app/build.gradle
|
perl -p -i -e 's/versionCode .*/versionCode 72/g' android/app/build.gradle
|
||||||
perl -p -i -e 's/versionName .*/versionName "1.4.4"/g' android/app/build.gradle
|
perl -p -i -e 's/versionName .*/versionName "1.4.6"/g' android/app/build.gradle
|
||||||
```
|
```
|
||||||
|
|
||||||
##### 2. Build
|
##### 2. Build
|
||||||
@@ -1442,35 +1454,38 @@ npx cap open android
|
|||||||
|
|
||||||
##### 4. Use Android Studio to build and run on emulator or device
|
##### 4. Use Android Studio to build and run on emulator or device
|
||||||
|
|
||||||
## Android Build from the console
|
#### Android Build from the console
|
||||||
|
|
||||||
```bash
|
`cd android`
|
||||||
cd android
|
|
||||||
./gradlew clean
|
|
||||||
./gradlew build -Dlint.baselines.continue=true
|
|
||||||
cd -
|
|
||||||
```
|
|
||||||
|
|
||||||
... or, to create the `aab` file, `bundle` instead of `build`:
|
* Note that this currently fails: `./gradlew build`
|
||||||
|
|
||||||
|
To create the `aab` file, `bundle` instead of `build` (but not signed and not usable in Play Store):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./gradlew bundleDebug -Dlint.baselines.continue=true
|
./gradlew bundleDebug -Dlint.baselines.continue=true
|
||||||
```
|
```
|
||||||
|
|
||||||
... or, to create a signed release:
|
So, to create a signed `aab` release:
|
||||||
|
|
||||||
- Setup by adding the app/gradle.properties.secrets file (see properties at top
|
- Setup by adding the app/gradle.properties.secrets file (see properties at top
|
||||||
of app/build.gradle) and the app/time-safari-upload-key-pkcs12.jks file
|
of app/build.gradle) and the app/time-safari-upload-key-pkcs12.jks file
|
||||||
- Then `bundleRelease`:
|
- Then `bundleRelease` or `bundleDebug`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd android
|
# if you use pkgx: pkgx +openjdk.org /bin/zsh
|
||||||
./gradlew bundleRelease -Dlint.baselines.continue=true -PfirebaseEnabled
|
./gradlew bundleRelease -Dlint.baselines.continue=true -PfirebaseEnabled
|
||||||
cd -
|
|
||||||
```
|
```
|
||||||
|
|
||||||
... and find your `aab` file at app/build/outputs/bundle/release
|
... and find your `aab` file at app/build/outputs/bundle/release
|
||||||
|
|
||||||
|
* Gradle packages whatever web bundle was last synced into
|
||||||
|
`android/app/src/main/assets/public`, even one from a `--test` build. Before
|
||||||
|
uploading, confirm it has production server defaults:
|
||||||
|
`./scripts/check-prod-bundle.sh android/app/build/outputs/bundle/release/app-release.aab`
|
||||||
|
* `build-android.sh` runs `./gradlew clean`, which deletes any earlier `aab`, so
|
||||||
|
run `bundleRelease` after the build script finishes.
|
||||||
|
|
||||||
* Note that F-Droid builds should omit `-PfirebaseEnabled`.
|
* Note that F-Droid builds should omit `-PfirebaseEnabled`.
|
||||||
|
|
||||||
At play.google.com/console:
|
At play.google.com/console:
|
||||||
@@ -1607,10 +1622,13 @@ Use the commands above to check and fix code quality issues.
|
|||||||
4. **Native Build**: Platform-specific compilation
|
4. **Native Build**: Platform-specific compilation
|
||||||
5. **Package Creation**: APK/IPA generation
|
5. **Package Creation**: APK/IPA generation
|
||||||
|
|
||||||
`resources/` is the canonical source for app icons and splash screens.
|
`resources/` is the canonical source for app icons and splash screens. Build
|
||||||
Do not keep a legacy top-level `assets/` directory unless it is intentionally
|
scripts select it explicitly with `--assetPath resources`. Use the same option
|
||||||
used: `@capacitor/assets` prioritizes `assets/` over `resources/`, which can
|
when invoking the generator directly:
|
||||||
prevent the canonical assets from being discovered.
|
|
||||||
|
```bash
|
||||||
|
npx capacitor-assets generate --assetPath resources
|
||||||
|
```
|
||||||
|
|
||||||
## Architecture Environment Configuration
|
## Architecture Environment Configuration
|
||||||
|
|
||||||
@@ -1822,7 +1840,7 @@ npm run build:android:assets
|
|||||||
- Configure signing keys
|
- Configure signing keys
|
||||||
- Check device/emulator setup
|
- Check device/emulator setup
|
||||||
|
|
||||||
## Additional Resources
|
## Additional Documentation
|
||||||
|
|
||||||
- [Electron Build Patterns](doc/electron-build-patterns.md)
|
- [Electron Build Patterns](doc/electron-build-patterns.md)
|
||||||
- [iOS Build Scripts](doc/ios-build-scripts.md)
|
- [iOS Build Scripts](doc/ios-build-scripts.md)
|
||||||
@@ -2212,6 +2230,8 @@ command chaining, following DRY principles.
|
|||||||
- `7` - Asset generation failed
|
- `7` - Asset generation failed
|
||||||
- `8` - Android Studio launch failed
|
- `8` - Android Studio launch failed
|
||||||
- `9` - Resource check failed
|
- `9` - Resource check failed
|
||||||
|
- `10` - Production bundle has test/dev server defaults (`--prod` only; see
|
||||||
|
`scripts/check-prod-bundle.sh`)
|
||||||
|
|
||||||
### A.4 build-ios.sh
|
### A.4 build-ios.sh
|
||||||
|
|
||||||
@@ -2272,6 +2292,8 @@ command chaining, following DRY principles.
|
|||||||
- **Clean Build**: Removes Xcode build artifacts and DerivedData
|
- **Clean Build**: Removes Xcode build artifacts and DerivedData
|
||||||
- **Asset Generation**: Creates platform-specific assets
|
- **Asset Generation**: Creates platform-specific assets
|
||||||
- **Simulator Support**: Launches iOS Simulator for testing
|
- **Simulator Support**: Launches iOS Simulator for testing
|
||||||
|
- **Production Bundle Check**: With `--prod`, exits with code `10` if the synced
|
||||||
|
web bundle has test/dev server defaults (`scripts/check-prod-bundle.sh`)
|
||||||
|
|
||||||
### A.5 common.sh
|
### A.5 common.sh
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -6,13 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
|
||||||
## [?] - 2026
|
## [1.4.6] - 2026.09.27
|
||||||
|
### Fixed
|
||||||
|
- Incorrect 'test' server URLs on production.
|
||||||
|
|
||||||
|
|
||||||
|
## [1.4.5] - 2026.09.24
|
||||||
### Added
|
### Added
|
||||||
- Full flow for setting up SMS notifications: phone registration and code
|
- Full flow for setting up SMS notifications: phone registration and code
|
||||||
verification, delegated alertSearch JWT batches with a send hour, and
|
verification, delegated alertSearch JWT batches with a send hour, and
|
||||||
revocation to stop the texts
|
revocation to stop the texts
|
||||||
- Notification debug panel action to mint and upload a push-channel alertSearch
|
- Notification debug panel action to mint and upload a push-channel alertSearch
|
||||||
authorization, built on the same batch minter as the SMS channel
|
authorization, built on the same batch minter as the SMS channel (hidden by dev flag)
|
||||||
### Fixed
|
### Fixed
|
||||||
- Native build scripts set NODE_ENV, so `import.meta.env.DEV` matches the build
|
- Native build scripts set NODE_ENV, so `import.meta.env.DEV` matches the build
|
||||||
mode instead of always reporting a production build
|
mode instead of always reporting a production build
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ android {
|
|||||||
applicationId "app.timesafari.app"
|
applicationId "app.timesafari.app"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 70
|
versionCode 72
|
||||||
versionName "1.4.4"
|
versionName "1.4.6"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
aaptOptions {
|
aaptOptions {
|
||||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||||
|
|||||||
@@ -54,5 +54,9 @@
|
|||||||
{
|
{
|
||||||
"pkg": "SharedImage",
|
"pkg": "SharedImage",
|
||||||
"classpath": "app.timesafari.sharedimage.SharedImagePlugin"
|
"classpath": "app.timesafari.sharedimage.SharedImagePlugin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pkg": "NotificationInspector",
|
||||||
|
"classpath": "app.timesafari.notifications.NotificationInspectorPlugin"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
+17
-1
@@ -1,5 +1,6 @@
|
|||||||
package app.timesafari.notifications;
|
package app.timesafari.notifications;
|
||||||
|
|
||||||
|
import com.getcapacitor.JSObject;
|
||||||
import com.getcapacitor.Plugin;
|
import com.getcapacitor.Plugin;
|
||||||
import com.getcapacitor.PluginCall;
|
import com.getcapacitor.PluginCall;
|
||||||
import com.getcapacitor.PluginMethod;
|
import com.getcapacitor.PluginMethod;
|
||||||
@@ -12,5 +13,20 @@ public class NotificationInspectorPlugin extends Plugin {
|
|||||||
call.unimplemented(
|
call.unimplemented(
|
||||||
"Pending notification inspection is currently implemented on iOS only");
|
"Pending notification inspection is currently implemented on iOS only");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this build carries a Firebase config, i.e. was built with
|
||||||
|
* -PfirebaseEnabled. Without it, PushNotifications.register() throws on the
|
||||||
|
* Capacitor plugin thread and kills the app, so JS must check this first.
|
||||||
|
* The google-services Gradle plugin generates the google_app_id resource,
|
||||||
|
* which is what Firebase's own auto-initialization looks for.
|
||||||
|
*/
|
||||||
|
@PluginMethod
|
||||||
|
public void isFirebaseConfigured(PluginCall call) {
|
||||||
|
int resId = getContext().getResources().getIdentifier(
|
||||||
|
"google_app_id", "string", getContext().getPackageName());
|
||||||
|
JSObject ret = new JSObject();
|
||||||
|
ret.put("configured", resId != 0);
|
||||||
|
call.resolve(ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -42,6 +42,16 @@ This means a single Play Store AAB (`bundleRelease -PfirebaseEnabled`) covers bo
|
|||||||
|
|
||||||
**F-Droid** is stricter: their build policy rejects any APK with GMS dependencies at the binary level, even with graceful degradation. F-Droid submission would require a separate `assembleRelease` build (no flag) and a dedicated F-Droid listing.
|
**F-Droid** is stricter: their build policy rejects any APK with GMS dependencies at the binary level, even with graceful degradation. F-Droid submission would require a separate `assembleRelease` build (no flag) and a dedicated F-Droid listing.
|
||||||
|
|
||||||
|
## Push registration in builds without Firebase
|
||||||
|
|
||||||
|
A build without `-PfirebaseEnabled` contains the `@capacitor/push-notifications` plugin, but no Firebase config. In that build, `PushNotifications.register()` throws `Default FirebaseApp is not initialized` on the Capacitor plugin thread, which kills the app; a JS `try/catch` cannot intercept it.
|
||||||
|
|
||||||
|
The JS therefore asks native code first. `NotificationInspector.isFirebaseConfigured()` (`android/app/src/main/java/app/timesafari/notifications/NotificationInspectorPlugin.java`) reports whether the `google_app_id` string resource exists; the google-services Gradle plugin generates that resource only when Firebase is enabled, and it is the same signal Firebase's auto-initialization uses. `firebaseMessagingClient.ts` skips `register()` on Android when it is absent (or when the check fails), logging `No Firebase config in this Android build; skipping push registration`. Listener setup and the notification permission request run either way, since local daily notifications need that permission.
|
||||||
|
|
||||||
|
`NotificationInspector` must be listed in `scripts/restore-local-plugins.js`: `MainActivity` registers plugins after the Capacitor bridge is built, so a plugin missing from `capacitor.plugins.json` reports "not implemented on android" and the check falls back to skipping push.
|
||||||
|
|
||||||
|
Any code that calls `PushNotifications.register()` or other `FirebaseMessaging` APIs on Android must go through the same check.
|
||||||
|
|
||||||
## The `google-services.json` file
|
## The `google-services.json` file
|
||||||
|
|
||||||
- Gitignored (`android/.gitignore` line 80) — never commit it
|
- Gitignored (`android/.gitignore` line 80) — never commit it
|
||||||
@@ -64,3 +74,9 @@ This is an accepted trade-off. Removing it would require either forking the plug
|
|||||||
Jose Olarte III's `notify-api` branch placed a production `google-services.json` in `android/` to test Firebase Cloud Messaging. The branch was never merged to `master`, but because the file is gitignored it persisted on disk after switching branches. At the time, the Gradle conditional activated Firebase based on file presence alone (no opt-in flag), so all subsequent local builds embedded Firebase and required Google Play Services. This silently broke APK/Aurora/Zapstore distribution.
|
Jose Olarte III's `notify-api` branch placed a production `google-services.json` in `android/` to test Firebase Cloud Messaging. The branch was never merged to `master`, but because the file is gitignored it persisted on disk after switching branches. At the time, the Gradle conditional activated Firebase based on file presence alone (no opt-in flag), so all subsequent local builds embedded Firebase and required Google Play Services. This silently broke APK/Aurora/Zapstore distribution.
|
||||||
|
|
||||||
**Fix applied:** deleted `google-services.json` from disk, changed the Gradle conditional to require `-PfirebaseEnabled`, and documented the rule in `AGENTS.md`.
|
**Fix applied:** deleted `google-services.json` from disk, changed the Gradle conditional to require `-PfirebaseEnabled`, and documented the rule in `AGENTS.md`.
|
||||||
|
|
||||||
|
## Incident: September 2026
|
||||||
|
|
||||||
|
After the June fix, local builds without `-PfirebaseEnabled` (including every `scripts/build-android.sh` build and the FOSS APKs from v1.4.4 on) crashed at launch as soon as notification permission was granted, because startup code called `PushNotifications.register()` unconditionally. Found while running `npm run build:android:prod:run` on an emulator.
|
||||||
|
|
||||||
|
**Fix applied:** the `isFirebaseConfigured()` gate described in "Push registration in builds without Firebase", plus adding `NotificationInspector` to `scripts/restore-local-plugins.js` so the check is reachable on Android.
|
||||||
|
|||||||
@@ -72,7 +72,7 @@
|
|||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||||
C86585E32ED456DE00824752 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
|
C86585E32ED456DE00824752 /* Exceptions for "TimeSafariShareExtension" folder in "TimeSafariShareExtension" target */ = {
|
||||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||||
membershipExceptions = (
|
membershipExceptions = (
|
||||||
Info.plist,
|
Info.plist,
|
||||||
@@ -82,7 +82,18 @@
|
|||||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||||
|
|
||||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||||
C86585D62ED456DE00824752 /* TimeSafariShareExtension */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (C86585E32ED456DE00824752 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = TimeSafariShareExtension; sourceTree = "<group>"; };
|
C86585D62ED456DE00824752 /* TimeSafariShareExtension */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
exceptions = (
|
||||||
|
C86585E32ED456DE00824752 /* Exceptions for "TimeSafariShareExtension" folder in "TimeSafariShareExtension" target */,
|
||||||
|
);
|
||||||
|
explicitFileTypes = {
|
||||||
|
};
|
||||||
|
explicitFolders = (
|
||||||
|
);
|
||||||
|
path = TimeSafariShareExtension;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -530,7 +541,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = App/AppDebug.entitlements;
|
CODE_SIGN_ENTITLEMENTS = App/AppDebug.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 70;
|
CURRENT_PROJECT_VERSION = 72;
|
||||||
DEVELOPMENT_TEAM = GM3FS5JQPH;
|
DEVELOPMENT_TEAM = GM3FS5JQPH;
|
||||||
ENABLE_APP_SANDBOX = NO;
|
ENABLE_APP_SANDBOX = NO;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
@@ -541,7 +552,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.4.4;
|
MARKETING_VERSION = 1.4.6;
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
|
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@@ -560,7 +571,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
|
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 70;
|
CURRENT_PROJECT_VERSION = 72;
|
||||||
DEVELOPMENT_TEAM = GM3FS5JQPH;
|
DEVELOPMENT_TEAM = GM3FS5JQPH;
|
||||||
ENABLE_APP_SANDBOX = NO;
|
ENABLE_APP_SANDBOX = NO;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
@@ -571,7 +582,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.4.4;
|
MARKETING_VERSION = 1.4.6;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
|
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
|
||||||
@@ -589,7 +600,7 @@
|
|||||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = TimeSafariShareExtension/TimeSafariShareExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = TimeSafariShareExtension/TimeSafariShareExtension.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 70;
|
CURRENT_PROJECT_VERSION = 72;
|
||||||
DEVELOPMENT_TEAM = GM3FS5JQPH;
|
DEVELOPMENT_TEAM = GM3FS5JQPH;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@@ -603,7 +614,7 @@
|
|||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MARKETING_VERSION = 1.4.4;
|
MARKETING_VERSION = 1.4.6;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari.TimeSafariShareExtension;
|
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari.TimeSafariShareExtension;
|
||||||
@@ -627,7 +638,7 @@
|
|||||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = TimeSafariShareExtension/TimeSafariShareExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = TimeSafariShareExtension/TimeSafariShareExtension.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 70;
|
CURRENT_PROJECT_VERSION = 72;
|
||||||
DEVELOPMENT_TEAM = GM3FS5JQPH;
|
DEVELOPMENT_TEAM = GM3FS5JQPH;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
@@ -641,7 +652,7 @@
|
|||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MARKETING_VERSION = 1.4.4;
|
MARKETING_VERSION = 1.4.6;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari.TimeSafariShareExtension;
|
PRODUCT_BUNDLE_IDENTIFIER = app.timesafari.TimeSafariShareExtension;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
|||||||
Generated
+677
-1419
File diff suppressed because it is too large
Load Diff
+118
-117
@@ -1,138 +1,139 @@
|
|||||||
{
|
{
|
||||||
"name": "giftopia",
|
"name": "giftopia",
|
||||||
"version": "1.4.4",
|
"version": "1.4.7-beta",
|
||||||
"description": "Giftopia App",
|
"description": "Giftopia App",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Gift Economies Team"
|
"name": "Gift Economies Team"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src",
|
"assets:clean": "rimraf android/app/src/main/res/mipmap-* ios/App/App/Assets.xcassets/**/AppIcon*.png ios/App/App/Assets.xcassets/**/Splash*.png || true",
|
||||||
"lint-fix": "eslint --ext .js,.ts,.vue --ignore-path .gitignore --fix src",
|
|
||||||
"type-check": "tsc --noEmit",
|
|
||||||
"type-check:vue": "vue-tsc --noEmit",
|
|
||||||
"prebuild": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src && node sw_combine.js && node scripts/copy-wasm.js",
|
|
||||||
"test:prerequisites": "node scripts/check-prerequisites.js",
|
|
||||||
"test:unit": "jest",
|
|
||||||
"check:dependencies": "./scripts/check-dependencies.sh",
|
|
||||||
"deps:update-daily-notification-plugin": "npm install @timesafari/daily-notification-plugin@git+https://gitea.anomalistdesign.com/trent_larson/daily-notification-plugin.git#master",
|
|
||||||
"test:all": "npm run lint && npm run type-check && npm run type-check:vue && npm run test:unit && npm run test:web && npm run test:mobile && echo '\n\n\nGotta add the performance tests'",
|
|
||||||
"test:web": "npx playwright test -c playwright.config-local.ts --trace on",
|
|
||||||
"test:mobile": "./scripts/test-mobile.sh",
|
|
||||||
"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)",
|
|
||||||
"auto-run": "./scripts/auto-run.sh",
|
|
||||||
"auto-run:test": "./scripts/auto-run.sh --test",
|
|
||||||
"auto-run:prod": "./scripts/auto-run.sh --prod",
|
|
||||||
"auto-run:ios": "./scripts/auto-run.sh --platform=ios",
|
|
||||||
"auto-run:android": "./scripts/auto-run.sh --platform=android",
|
|
||||||
"auto-run:electron": "./scripts/auto-run.sh --platform=electron",
|
|
||||||
"build:capacitor": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --mode capacitor --config vite.config.capacitor.mts",
|
|
||||||
"build:capacitor:sync": "npm run build:capacitor && npx cap sync && node scripts/restore-local-plugins.js",
|
|
||||||
"build:native": "vite build && npx cap sync && node scripts/restore-local-plugins.js && bash -c 'source scripts/common.sh && ensure_ios_capacitor_asset_directories' && npx capacitor-assets generate",
|
|
||||||
"assets:config": "npx tsx scripts/assets-config.ts",
|
"assets:config": "npx tsx scripts/assets-config.ts",
|
||||||
"assets:validate": "npx tsx scripts/assets-validator.ts",
|
"assets:validate": "npx tsx scripts/assets-validator.ts",
|
||||||
"assets:validate:android": "./scripts/build-android.sh --assets-only",
|
"assets:validate:android": "./scripts/build-android.sh --assets-only",
|
||||||
"assets:clean": "rimraf android/app/src/main/res/mipmap-* ios/App/App/Assets.xcassets/**/AppIcon*.png ios/App/App/Assets.xcassets/**/Splash*.png || true",
|
"auto-run": "./scripts/auto-run.sh",
|
||||||
"build:ios": "./scripts/build-ios.sh",
|
"auto-run:android": "./scripts/auto-run.sh --platform=android",
|
||||||
"build:ios:dev": "./scripts/build-ios.sh --dev",
|
"auto-run:electron": "./scripts/auto-run.sh --platform=electron",
|
||||||
"build:ios:test": "./scripts/build-ios.sh --test",
|
"auto-run:ios": "./scripts/auto-run.sh --platform=ios",
|
||||||
"build:ios:test:run": "./scripts/build-ios.sh --test --auto-run",
|
"auto-run:prod": "./scripts/auto-run.sh --prod",
|
||||||
"build:ios:prod": "./scripts/build-ios.sh --prod",
|
"auto-run:test": "./scripts/auto-run.sh --test",
|
||||||
"build:ios:prod:run": "./scripts/build-ios.sh --prod --auto-run",
|
|
||||||
"build:ios:debug": "./scripts/build-ios.sh --debug",
|
|
||||||
"build:ios:debug:run": "./scripts/build-ios.sh --debug --auto-run",
|
|
||||||
"build:ios:release": "./scripts/build-ios.sh --release",
|
|
||||||
"build:ios:release:run": "./scripts/build-ios.sh --release --auto-run",
|
|
||||||
"build:ios:studio": "./scripts/build-ios.sh --studio",
|
|
||||||
"build:ios:ipa": "./scripts/build-ios.sh --ipa",
|
|
||||||
"build:ios:clean": "./scripts/build-ios.sh --clean",
|
|
||||||
"build:ios:sync": "./scripts/build-ios.sh --sync",
|
|
||||||
"build:ios:assets": "./scripts/build-ios.sh --assets",
|
|
||||||
"build:ios:deploy": "./scripts/build-ios.sh --deploy",
|
|
||||||
"build:ios:dev:custom": "./scripts/build-ios.sh --dev --api-ip",
|
|
||||||
"build:ios:test:custom": "./scripts/build-ios.sh --test --api-ip",
|
|
||||||
"build:ios:dev:run:custom": "./scripts/build-ios.sh --dev --api-ip --auto-run",
|
|
||||||
"build:ios:test:run:custom": "./scripts/build-ios.sh --test --api-ip --auto-run",
|
|
||||||
"build:web": "./scripts/build-web.sh",
|
|
||||||
"build:web:dev": "./scripts/build-web.sh --dev",
|
|
||||||
"build:web:test": "./scripts/build-web.sh --test",
|
|
||||||
"build:web:prod": "./scripts/build-web.sh --prod",
|
|
||||||
"build:web:docker": "./scripts/build-web.sh --docker",
|
|
||||||
"build:web:docker:test": "./scripts/build-web.sh --docker:test",
|
|
||||||
"build:web:docker:prod": "./scripts/build-web.sh --docker:prod",
|
|
||||||
"build:web:serve": "./scripts/build-web.sh --serve",
|
|
||||||
"build:web:serve:test": "./scripts/build-web.sh --serve --test",
|
|
||||||
"build:web:serve:prod": "./scripts/build-web.sh --serve --prod",
|
|
||||||
"docker:up": "docker-compose up",
|
|
||||||
"docker:up:test": "npm run build:web:build -- --mode test && docker-compose up test",
|
|
||||||
"docker:up:prod": "npm run build:web:build -- --mode production && docker-compose up production",
|
|
||||||
"docker:down": "docker-compose down",
|
|
||||||
"docker:logs": "docker-compose logs -f",
|
|
||||||
"build:electron": "./scripts/build-electron.sh",
|
|
||||||
"build:electron:dev": "./scripts/build-electron.sh --dev",
|
|
||||||
"build:electron:dev:run": "./scripts/build-electron.sh --dev --auto-run",
|
|
||||||
"build:electron:test": "./scripts/build-electron.sh --test",
|
|
||||||
"build:electron:test:run": "./scripts/build-electron.sh --test --auto-run",
|
|
||||||
"build:electron:prod": "./scripts/build-electron.sh --prod",
|
|
||||||
"build:electron:prod:run": "./scripts/build-electron.sh --prod --auto-run",
|
|
||||||
"build:electron:windows": "./scripts/build-electron.sh --prod --windows",
|
|
||||||
"build:electron:windows:dev": "./scripts/build-electron.sh --dev --windows",
|
|
||||||
"build:electron:windows:test": "./scripts/build-electron.sh --test --windows",
|
|
||||||
"build:electron:windows:prod": "./scripts/build-electron.sh --prod --windows",
|
|
||||||
"build:electron:mac": "./scripts/build-electron.sh --prod --mac",
|
|
||||||
"build:electron:mac:dev": "./scripts/build-electron.sh --dev --mac",
|
|
||||||
"build:electron:mac:test": "./scripts/build-electron.sh --test --mac",
|
|
||||||
"build:electron:mac:prod": "./scripts/build-electron.sh --prod --mac",
|
|
||||||
"build:electron:linux": "./scripts/build-electron.sh --prod --linux",
|
|
||||||
"build:electron:linux:dev": "./scripts/build-electron.sh --dev --linux",
|
|
||||||
"build:electron:linux:test": "./scripts/build-electron.sh --test --linux",
|
|
||||||
"build:electron:linux:prod": "./scripts/build-electron.sh --prod --linux",
|
|
||||||
"build:electron:appimage": "./scripts/build-electron.sh --prod --appimage",
|
|
||||||
"build:electron:appimage:dev": "./scripts/build-electron.sh --dev --appimage",
|
|
||||||
"build:electron:appimage:test": "./scripts/build-electron.sh --test --appimage",
|
|
||||||
"build:electron:appimage:prod": "./scripts/build-electron.sh --prod --appimage",
|
|
||||||
"build:electron:deb": "./scripts/build-electron.sh --prod --deb",
|
|
||||||
"build:electron:deb:dev": "./scripts/build-electron.sh --dev --deb",
|
|
||||||
"build:electron:deb:test": "./scripts/build-electron.sh --test --deb",
|
|
||||||
"build:electron:deb:prod": "./scripts/build-electron.sh --prod --deb",
|
|
||||||
"build:electron:dmg": "./scripts/build-electron.sh --prod --dmg",
|
|
||||||
"build:electron:dmg:dev": "./scripts/build-electron.sh --dev --dmg",
|
|
||||||
"build:electron:dmg:test": "./scripts/build-electron.sh --test --dmg",
|
|
||||||
"build:electron:dmg:prod": "./scripts/build-electron.sh --prod --dmg",
|
|
||||||
"markdown:fix": "markdownlint-cli2 --fix",
|
|
||||||
"markdown:check": "markdownlint-cli2",
|
|
||||||
"markdown:setup": "./scripts/setup-markdown-hooks.sh",
|
|
||||||
"prepare": "husky",
|
|
||||||
"guard": "bash ./scripts/build-arch-guard.sh",
|
|
||||||
"guard:test": "bash ./scripts/build-arch-guard.sh --staged",
|
|
||||||
"guard:setup": "npm run prepare && echo '✅ Build Architecture Guard is now active!'",
|
|
||||||
"clean:android": "./scripts/uninstall-android.sh",
|
|
||||||
"clean:ios": "rm -rf ios/App/build ios/App/Pods ios/App/output ios/App/App/public ios/DerivedData ios/capacitor-cordova-ios-plugins ios/App/App/capacitor.config.json ios/App/App/config.xml || true",
|
|
||||||
"clean:electron": "./scripts/build-electron.sh --clean",
|
|
||||||
"clean:all": "npm run clean:ios && npm run clean:android && npm run clean:electron",
|
|
||||||
"build:android": "./scripts/build-android.sh",
|
"build:android": "./scripts/build-android.sh",
|
||||||
"build:android:dev": "./scripts/build-android.sh --dev",
|
"build:android:aab": "./scripts/build-android.sh --aab",
|
||||||
"build:android:test": "./scripts/build-android.sh --test",
|
"build:android:apk": "./scripts/build-android.sh --apk",
|
||||||
"build:android:test:run": "./scripts/build-android.sh --test --auto-run",
|
"build:android:assets": "./scripts/build-android.sh --assets",
|
||||||
"build:android:prod": "./scripts/build-android.sh --prod",
|
"build:android:clean": "./scripts/build-android.sh --clean",
|
||||||
"build:android:prod:run": "./scripts/build-android.sh --prod --auto-run",
|
|
||||||
"build:android:debug": "./scripts/build-android.sh --debug",
|
"build:android:debug": "./scripts/build-android.sh --debug",
|
||||||
"build:android:debug:run": "./scripts/build-android.sh --debug --auto-run",
|
"build:android:debug:run": "./scripts/build-android.sh --debug --auto-run",
|
||||||
|
"build:android:deploy": "./scripts/build-android.sh --deploy",
|
||||||
|
"build:android:dev": "./scripts/build-android.sh --dev",
|
||||||
|
"build:android:dev:custom": "./scripts/build-android.sh --dev --api-ip",
|
||||||
|
"build:android:dev:run:custom": "./scripts/build-android.sh --dev --api-ip --auto-run",
|
||||||
|
"build:android:prod": "./scripts/build-android.sh --prod",
|
||||||
|
"build:android:prod:run": "./scripts/build-android.sh --prod --auto-run",
|
||||||
"build:android:release": "./scripts/build-android.sh --release",
|
"build:android:release": "./scripts/build-android.sh --release",
|
||||||
"build:android:release:run": "./scripts/build-android.sh --release --auto-run",
|
"build:android:release:run": "./scripts/build-android.sh --release --auto-run",
|
||||||
"build:android:studio": "./scripts/build-android.sh --studio",
|
"build:android:studio": "./scripts/build-android.sh --studio",
|
||||||
"build:android:apk": "./scripts/build-android.sh --apk",
|
|
||||||
"build:android:aab": "./scripts/build-android.sh --aab",
|
|
||||||
"build:android:clean": "./scripts/build-android.sh --clean",
|
|
||||||
"build:android:sync": "./scripts/build-android.sh --sync",
|
"build:android:sync": "./scripts/build-android.sh --sync",
|
||||||
"build:android:assets": "./scripts/build-android.sh --assets",
|
"build:android:test": "./scripts/build-android.sh --test",
|
||||||
"build:android:deploy": "./scripts/build-android.sh --deploy",
|
|
||||||
"build:android:dev:custom": "./scripts/build-android.sh --dev --api-ip",
|
|
||||||
"build:android:test:custom": "./scripts/build-android.sh --test --api-ip",
|
"build:android:test:custom": "./scripts/build-android.sh --test --api-ip",
|
||||||
"build:android:dev:run:custom": "./scripts/build-android.sh --dev --api-ip --auto-run",
|
"build:android:test:run": "./scripts/build-android.sh --test --auto-run",
|
||||||
"build:android:test:run:custom": "./scripts/build-android.sh --test --api-ip --auto-run"
|
"build:android:test:run:custom": "./scripts/build-android.sh --test --api-ip --auto-run",
|
||||||
|
"build:capacitor": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --mode capacitor --config vite.config.capacitor.mts",
|
||||||
|
"build:capacitor:sync": "npm run build:capacitor && npx cap sync && node scripts/restore-local-plugins.js",
|
||||||
|
"build:electron": "./scripts/build-electron.sh",
|
||||||
|
"build:electron:appimage": "./scripts/build-electron.sh --prod --appimage",
|
||||||
|
"build:electron:appimage:dev": "./scripts/build-electron.sh --dev --appimage",
|
||||||
|
"build:electron:appimage:prod": "./scripts/build-electron.sh --prod --appimage",
|
||||||
|
"build:electron:appimage:test": "./scripts/build-electron.sh --test --appimage",
|
||||||
|
"build:electron:deb": "./scripts/build-electron.sh --prod --deb",
|
||||||
|
"build:electron:deb:dev": "./scripts/build-electron.sh --dev --deb",
|
||||||
|
"build:electron:deb:prod": "./scripts/build-electron.sh --prod --deb",
|
||||||
|
"build:electron:deb:test": "./scripts/build-electron.sh --test --deb",
|
||||||
|
"build:electron:dev": "./scripts/build-electron.sh --dev",
|
||||||
|
"build:electron:dev:run": "./scripts/build-electron.sh --dev --auto-run",
|
||||||
|
"build:electron:dmg": "./scripts/build-electron.sh --prod --dmg",
|
||||||
|
"build:electron:dmg:dev": "./scripts/build-electron.sh --dev --dmg",
|
||||||
|
"build:electron:dmg:prod": "./scripts/build-electron.sh --prod --dmg",
|
||||||
|
"build:electron:dmg:test": "./scripts/build-electron.sh --test --dmg",
|
||||||
|
"build:electron:linux": "./scripts/build-electron.sh --prod --linux",
|
||||||
|
"build:electron:linux:dev": "./scripts/build-electron.sh --dev --linux",
|
||||||
|
"build:electron:linux:prod": "./scripts/build-electron.sh --prod --linux",
|
||||||
|
"build:electron:linux:test": "./scripts/build-electron.sh --test --linux",
|
||||||
|
"build:electron:mac": "./scripts/build-electron.sh --prod --mac",
|
||||||
|
"build:electron:mac:dev": "./scripts/build-electron.sh --dev --mac",
|
||||||
|
"build:electron:mac:prod": "./scripts/build-electron.sh --prod --mac",
|
||||||
|
"build:electron:mac:test": "./scripts/build-electron.sh --test --mac",
|
||||||
|
"build:electron:prod": "./scripts/build-electron.sh --prod",
|
||||||
|
"build:electron:prod:run": "./scripts/build-electron.sh --prod --auto-run",
|
||||||
|
"build:electron:test": "./scripts/build-electron.sh --test",
|
||||||
|
"build:electron:test:run": "./scripts/build-electron.sh --test --auto-run",
|
||||||
|
"build:electron:windows": "./scripts/build-electron.sh --prod --windows",
|
||||||
|
"build:electron:windows:dev": "./scripts/build-electron.sh --dev --windows",
|
||||||
|
"build:electron:windows:prod": "./scripts/build-electron.sh --prod --windows",
|
||||||
|
"build:electron:windows:test": "./scripts/build-electron.sh --test --windows",
|
||||||
|
"build:ios": "./scripts/build-ios.sh",
|
||||||
|
"build:ios:assets": "./scripts/build-ios.sh --assets",
|
||||||
|
"build:ios:clean": "./scripts/build-ios.sh --clean",
|
||||||
|
"build:ios:debug": "./scripts/build-ios.sh --debug",
|
||||||
|
"build:ios:debug:run": "./scripts/build-ios.sh --debug --auto-run",
|
||||||
|
"build:ios:deploy": "./scripts/build-ios.sh --deploy",
|
||||||
|
"build:ios:dev": "./scripts/build-ios.sh --dev",
|
||||||
|
"build:ios:dev:custom": "./scripts/build-ios.sh --dev --api-ip",
|
||||||
|
"build:ios:dev:run:custom": "./scripts/build-ios.sh --dev --api-ip --auto-run",
|
||||||
|
"build:ios:ipa": "./scripts/build-ios.sh --ipa",
|
||||||
|
"build:ios:prod": "./scripts/build-ios.sh --prod",
|
||||||
|
"build:ios:prod:run": "./scripts/build-ios.sh --prod --auto-run",
|
||||||
|
"build:ios:release": "./scripts/build-ios.sh --release",
|
||||||
|
"build:ios:release:run": "./scripts/build-ios.sh --release --auto-run",
|
||||||
|
"build:ios:studio": "./scripts/build-ios.sh --studio",
|
||||||
|
"build:ios:sync": "./scripts/build-ios.sh --sync",
|
||||||
|
"build:ios:test": "./scripts/build-ios.sh --test",
|
||||||
|
"build:ios:test:custom": "./scripts/build-ios.sh --test --api-ip",
|
||||||
|
"build:ios:test:run": "./scripts/build-ios.sh --test --auto-run",
|
||||||
|
"build:ios:test:run:custom": "./scripts/build-ios.sh --test --api-ip --auto-run",
|
||||||
|
"build:native": "vite build && npx cap sync && node scripts/restore-local-plugins.js && bash -c 'source scripts/common.sh && ensure_ios_capacitor_asset_directories' && npx capacitor-assets generate --assetPath resources",
|
||||||
|
"build:web": "./scripts/build-web.sh",
|
||||||
|
"build:web:dev": "./scripts/build-web.sh --dev",
|
||||||
|
"build:web:docker": "./scripts/build-web.sh --docker",
|
||||||
|
"build:web:docker:prod": "./scripts/build-web.sh --docker:prod",
|
||||||
|
"build:web:docker:test": "./scripts/build-web.sh --docker:test",
|
||||||
|
"build:web:prod": "./scripts/build-web.sh --prod",
|
||||||
|
"build:web:serve": "./scripts/build-web.sh --serve",
|
||||||
|
"build:web:serve:prod": "./scripts/build-web.sh --serve --prod",
|
||||||
|
"build:web:serve:test": "./scripts/build-web.sh --serve --test",
|
||||||
|
"build:web:test": "./scripts/build-web.sh --test",
|
||||||
|
"check:android-device": "adb devices | grep -w 'device' || (echo 'No Android device connected' && exit 1)",
|
||||||
|
"check:dependencies": "./scripts/check-dependencies.sh",
|
||||||
|
"check:ios-device": "xcrun xctrace list devices 2>&1 | grep -w 'Booted' || (echo 'No iOS simulator running' && exit 1)",
|
||||||
|
"check:prod-bundle": "./scripts/check-prod-bundle.sh",
|
||||||
|
"clean:all": "npm run clean:ios && npm run clean:android && npm run clean:electron",
|
||||||
|
"clean:android": "./scripts/uninstall-android.sh",
|
||||||
|
"clean:electron": "./scripts/build-electron.sh --clean",
|
||||||
|
"clean:ios": "rm -rf ios/App/build ios/App/Pods ios/App/output ios/App/App/public ios/DerivedData ios/capacitor-cordova-ios-plugins ios/App/App/capacitor.config.json ios/App/App/config.xml || true",
|
||||||
|
"deps:update-daily-notification-plugin": "npm install @timesafari/daily-notification-plugin@git+https://gitea.anomalistdesign.com/trent_larson/daily-notification-plugin.git#master",
|
||||||
|
"docker:down": "docker-compose down",
|
||||||
|
"docker:logs": "docker-compose logs -f",
|
||||||
|
"docker:up": "docker-compose up",
|
||||||
|
"docker:up:prod": "npm run build:web:build -- --mode production && docker-compose up production",
|
||||||
|
"docker:up:test": "npm run build:web:build -- --mode test && docker-compose up test",
|
||||||
|
"guard": "bash ./scripts/build-arch-guard.sh",
|
||||||
|
"guard:setup": "npm run prepare && echo 'Build Architecture Guard is now active!'",
|
||||||
|
"guard:test": "bash ./scripts/build-arch-guard.sh --staged",
|
||||||
|
"lint": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src",
|
||||||
|
"lint-fix": "eslint --ext .js,.ts,.vue --ignore-path .gitignore --fix src",
|
||||||
|
"markdown:check": "markdownlint-cli2",
|
||||||
|
"markdown:fix": "markdownlint-cli2 --fix",
|
||||||
|
"markdown:setup": "./scripts/setup-markdown-hooks.sh",
|
||||||
|
"prebuild": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src && node sw_combine.js && node scripts/copy-wasm.js",
|
||||||
|
"prepare": "husky",
|
||||||
|
"test:all": "npm run lint && npm run type-check && npm run type-check:vue && npm run test:unit && npm run test:web && npm run test:mobile && echo '\n\n\nGotta add the performance tests'",
|
||||||
|
"test:android": "node scripts/test-android.js",
|
||||||
|
"test:ios": "node scripts/test-ios.js",
|
||||||
|
"test:mobile": "./scripts/test-mobile.sh",
|
||||||
|
"test:prerequisites": "node scripts/check-prerequisites.js",
|
||||||
|
"test:unit": "jest",
|
||||||
|
"test:web": "npx playwright test -c playwright.config-local.ts --trace on",
|
||||||
|
"type-check": "tsc --noEmit",
|
||||||
|
"type-check:vue": "vue-tsc --noEmit"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{js,ts,vue,css,json,yml,yaml}": "eslint --fix || true",
|
"*.{js,ts,vue,css,json,yml,yaml}": "eslint --fix || true",
|
||||||
|
|||||||
+7
-8
@@ -46,20 +46,19 @@ Assets are automatically generated during the build process using `capacitor-ass
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Generate assets for all platforms
|
# Generate assets for all platforms
|
||||||
npx capacitor-assets generate
|
npx capacitor-assets generate --assetPath resources
|
||||||
|
|
||||||
# Generate assets for specific platform
|
# Generate assets for specific platform
|
||||||
npx capacitor-assets generate --ios
|
npx capacitor-assets generate --ios --assetPath resources
|
||||||
npx capacitor-assets generate --android
|
npx capacitor-assets generate --android --assetPath resources
|
||||||
npx capacitor-assets generate --web
|
npx capacitor-assets generate --web --assetPath resources
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
`resources/` is this project's canonical asset source. `@capacitor/assets`
|
`resources/` is this project's canonical asset source. Pass it explicitly with
|
||||||
prioritizes a top-level `assets/` directory over `resources/`, so a legacy
|
`--assetPath resources`; `@capacitor/assets` otherwise prioritizes a top-level
|
||||||
`assets/` directory can prevent these assets from being discovered. Remove that
|
`assets/` directory when one exists, even when that directory is empty.
|
||||||
directory when it is empty or obsolete.
|
|
||||||
|
|
||||||
## Version Control
|
## Version Control
|
||||||
|
|
||||||
|
|||||||
+33
-13
@@ -545,7 +545,7 @@ fi
|
|||||||
# Handle assets-only mode
|
# Handle assets-only mode
|
||||||
if [ "$ASSETS_ONLY" = true ]; then
|
if [ "$ASSETS_ONLY" = true ]; then
|
||||||
log_info "Assets-only mode: generating assets"
|
log_info "Assets-only mode: generating assets"
|
||||||
safe_execute "Generating assets" "npx capacitor-assets generate --android" || exit 7
|
safe_execute "Generating assets" "npx capacitor-assets generate --android --assetPath resources" || exit 7
|
||||||
log_success "Assets generation completed successfully!"
|
log_success "Assets generation completed successfully!"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
@@ -612,25 +612,31 @@ elif [ "$BUILD_MODE" = "production" ]; then
|
|||||||
safe_execute "Building Capacitor version (production)" "npm run build:capacitor -- --mode production" || exit 3
|
safe_execute "Building Capacitor version (production)" "npm run build:capacitor -- --mode production" || exit 3
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Step 6: Clean Gradle build
|
# Step 6: Sync with Capacitor (before any Gradle assemble, which packages
|
||||||
|
# whatever web bundle and resources are in android/app/src/main at that moment)
|
||||||
|
safe_execute "Syncing with Capacitor" "npx cap sync android" || exit 6
|
||||||
|
|
||||||
|
# Step 6.5: Restore local plugins (capacitor.plugins.json gets overwritten by cap sync)
|
||||||
|
safe_execute "Restoring local plugins" "node scripts/restore-local-plugins.js" || exit 7
|
||||||
|
|
||||||
|
# Step 6.6: Verify the synced web bundle has production server defaults
|
||||||
|
if [ "$BUILD_MODE" = "production" ]; then
|
||||||
|
safe_execute "Checking production bundle" "./scripts/check-prod-bundle.sh android/app/src/main/assets/public" || exit 10
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 7: Generate assets
|
||||||
|
safe_execute "Generating assets" "npx capacitor-assets generate --android --assetPath resources" || exit 7
|
||||||
|
|
||||||
|
# Step 8: Clean Gradle build
|
||||||
safe_execute "Cleaning Gradle build" "cd android && ./gradlew clean && cd .." || exit 4
|
safe_execute "Cleaning Gradle build" "cd android && ./gradlew clean && cd .." || exit 4
|
||||||
|
|
||||||
# Step 7: Build based on type
|
# Step 9: Build based on type
|
||||||
if [ "$BUILD_TYPE" = "debug" ]; then
|
if [ "$BUILD_TYPE" = "debug" ]; then
|
||||||
safe_execute "Assembling debug build" "cd android && ./gradlew assembleDebug && cd .." || exit 5
|
safe_execute "Assembling debug build" "cd android && ./gradlew assembleDebug && cd .." || exit 5
|
||||||
elif [ "$BUILD_TYPE" = "release" ]; then
|
elif [ "$BUILD_TYPE" = "release" ]; then
|
||||||
safe_execute "Assembling release build" "cd android && ./gradlew assembleRelease && cd .." || exit 5
|
safe_execute "Assembling release build" "cd android && ./gradlew assembleRelease && cd .." || exit 5
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Step 8: Sync with Capacitor
|
|
||||||
safe_execute "Syncing with Capacitor" "npx cap sync android" || exit 6
|
|
||||||
|
|
||||||
# Step 8.5: Restore local plugins (capacitor.plugins.json gets overwritten by cap sync)
|
|
||||||
safe_execute "Restoring local plugins" "node scripts/restore-local-plugins.js" || exit 7
|
|
||||||
|
|
||||||
# Step 9: Generate assets
|
|
||||||
safe_execute "Generating assets" "npx capacitor-assets generate --android" || exit 7
|
|
||||||
|
|
||||||
# Step 10: Build APK/AAB if requested
|
# Step 10: Build APK/AAB if requested
|
||||||
if [ "$BUILD_APK" = true ]; then
|
if [ "$BUILD_APK" = true ]; then
|
||||||
if [ "$BUILD_TYPE" = "debug" ]; then
|
if [ "$BUILD_TYPE" = "debug" ]; then
|
||||||
@@ -642,6 +648,9 @@ fi
|
|||||||
|
|
||||||
if [ "$BUILD_AAB" = true ]; then
|
if [ "$BUILD_AAB" = true ]; then
|
||||||
safe_execute "Building AAB" "cd android && ./gradlew bundleRelease && cd .." || exit 5
|
safe_execute "Building AAB" "cd android && ./gradlew bundleRelease && cd .." || exit 5
|
||||||
|
if [ "$BUILD_MODE" = "production" ]; then
|
||||||
|
safe_execute "Checking production AAB" "./scripts/check-prod-bundle.sh android/app/build/outputs/bundle/release/app-release.aab" || exit 10
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Step 11: Auto-run app if requested
|
# Step 11: Auto-run app if requested
|
||||||
@@ -649,7 +658,18 @@ fi
|
|||||||
# we already synced and ran restore-local-plugins.js above, so skip sync here.
|
# we already synced and ran restore-local-plugins.js above, so skip sync here.
|
||||||
if [ "$AUTO_RUN" = true ]; then
|
if [ "$AUTO_RUN" = true ]; then
|
||||||
log_step "Auto-running Android app..."
|
log_step "Auto-running Android app..."
|
||||||
safe_execute "Launching app" "npx cap run android --no-sync" || {
|
# With exactly one connected device, target it directly. Otherwise cap run
|
||||||
|
# shows an interactive picker, which without a terminal exits 0 having
|
||||||
|
# launched nothing, so fail instead of reporting a false success.
|
||||||
|
run_devices=$(adb devices | awk 'NR > 1 && $2 == "device" { print $1 }')
|
||||||
|
run_target=""
|
||||||
|
if [ "$(printf '%s\n' "$run_devices" | grep -c .)" = "1" ]; then
|
||||||
|
run_target="--target $run_devices"
|
||||||
|
elif [ ! -t 0 ]; then
|
||||||
|
log_error "Auto-run needs exactly one connected device when not run from a terminal (found: ${run_devices:-none})"
|
||||||
|
exit 9
|
||||||
|
fi
|
||||||
|
safe_execute "Launching app" "npx cap run android --no-sync $run_target" || {
|
||||||
log_error "Failed to launch Android app"
|
log_error "Failed to launch Android app"
|
||||||
log_info "You can manually run with: npx cap run android --no-sync"
|
log_info "You can manually run with: npx cap run android --no-sync"
|
||||||
exit 9
|
exit 9
|
||||||
|
|||||||
@@ -295,12 +295,7 @@ apply_ios_app_icon_appearances() {
|
|||||||
# Generate iOS assets (capacitor-assets), then apply optional appearance variants.
|
# Generate iOS assets (capacitor-assets), then apply optional appearance variants.
|
||||||
generate_ios_assets() {
|
generate_ios_assets() {
|
||||||
ensure_ios_capacitor_asset_directories
|
ensure_ios_capacitor_asset_directories
|
||||||
if [ -d "assets" ]; then
|
npx capacitor-assets generate --ios --assetPath resources
|
||||||
log_warn "@capacitor/assets prioritizes the top-level assets/ directory over resources/."
|
|
||||||
log_warn "This project intentionally uses resources/ as the canonical asset source."
|
|
||||||
log_warn "Remove the legacy assets/ directory if it is empty or obsolete."
|
|
||||||
fi
|
|
||||||
npx capacitor-assets generate --ios
|
|
||||||
apply_ios_app_icon_appearances
|
apply_ios_app_icon_appearances
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -685,6 +680,11 @@ safe_execute "Installing CocoaPods dependencies" "run_pod_install_with_workaroun
|
|||||||
# Step 6.6: Sync with Capacitor (uses run_cap_sync_with_workaround defined above for Xcode 26)
|
# Step 6.6: Sync with Capacitor (uses run_cap_sync_with_workaround defined above for Xcode 26)
|
||||||
safe_execute "Syncing with Capacitor" "run_cap_sync_with_workaround" || exit 6
|
safe_execute "Syncing with Capacitor" "run_cap_sync_with_workaround" || exit 6
|
||||||
|
|
||||||
|
# Step 6.7: Verify the synced web bundle has production server defaults
|
||||||
|
if [ "$BUILD_MODE" = "production" ]; then
|
||||||
|
safe_execute "Checking production bundle" "./scripts/check-prod-bundle.sh ios/App/App/public" || exit 10
|
||||||
|
fi
|
||||||
|
|
||||||
# Step 7: Generate assets
|
# Step 7: Generate assets
|
||||||
safe_execute "Generating assets" "generate_ios_assets" || exit 7
|
safe_execute "Generating assets" "generate_ios_assets" || exit 7
|
||||||
|
|
||||||
@@ -751,4 +751,4 @@ fi
|
|||||||
print_footer "iOS Build"
|
print_footer "iOS Build"
|
||||||
|
|
||||||
# Exit with success
|
# Exit with success
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
Executable
+100
@@ -0,0 +1,100 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# check-prod-bundle.sh
|
||||||
|
# Fails if a built web bundle has test/dev server URLs baked in as defaults.
|
||||||
|
#
|
||||||
|
# Native packaging (gradlew bundleRelease, Xcode archive) ships whatever web
|
||||||
|
# bundle was last synced into the native project, whether it came from a
|
||||||
|
# --test, --dev, or --prod build. Run this on the bundle before uploading.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./scripts/check-prod-bundle.sh [PATH...]
|
||||||
|
#
|
||||||
|
# Each PATH is a directory (e.g. android/app/src/main/assets/public,
|
||||||
|
# ios/App/App/public, an .xcarchive) or an .aab, .apk, or .ipa file.
|
||||||
|
# With no PATH, checks the synced Android and iOS web assets plus the
|
||||||
|
# release AAB if it exists.
|
||||||
|
#
|
||||||
|
# Detection: the canary URLs below appear in src/ only as AppString.TEST_*
|
||||||
|
# enum values, which minify to `x.TEST_..._SERVER="<url>"`. Any other quoted
|
||||||
|
# occurrence is a VITE_* default compiled in from .env.test or
|
||||||
|
# .env.development (.env.development also uses the test image server).
|
||||||
|
#
|
||||||
|
# Exit codes: 0 = clean, 1 = test defaults found, 2 = bad input
|
||||||
|
|
||||||
|
source "$(dirname "$0")/common.sh"
|
||||||
|
|
||||||
|
CANARY_REGEX='"https://test(-image-api|-notify-api)?\.timesafari\.app"'
|
||||||
|
ENUM_REGEX='TEST[0-9]*_[A-Z_]+="https'
|
||||||
|
|
||||||
|
# Prints offending snippets from the JS files under a directory; returns 1 if any.
|
||||||
|
check_js_dir() {
|
||||||
|
local dir="$1"
|
||||||
|
local js_count
|
||||||
|
js_count=$(find "$dir" -name "*.js" -path "*assets*" | wc -l | tr -d ' ')
|
||||||
|
if [ "$js_count" = "0" ]; then
|
||||||
|
log_error "No JS assets found under $dir"
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
local hits
|
||||||
|
hits=$(find "$dir" -name "*.js" -path "*assets*" -print0 \
|
||||||
|
| xargs -0 grep -ohE ".{0,40}${CANARY_REGEX}" \
|
||||||
|
| grep -vE "$ENUM_REGEX")
|
||||||
|
if [ -n "$hits" ]; then
|
||||||
|
log_error "Test/dev server defaults found in bundle:"
|
||||||
|
echo "$hits" | sed 's/^/ /' >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
check_path() {
|
||||||
|
local path="$1"
|
||||||
|
local result
|
||||||
|
|
||||||
|
if [ -d "$path" ]; then
|
||||||
|
check_js_dir "$path"
|
||||||
|
result=$?
|
||||||
|
elif [ -f "$path" ]; then
|
||||||
|
case "$path" in
|
||||||
|
*.aab|*.apk|*.ipa) ;;
|
||||||
|
*)
|
||||||
|
log_error "Unsupported file type: $path"
|
||||||
|
return 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
local tmp
|
||||||
|
tmp=$(mktemp -d)
|
||||||
|
unzip -q "$path" '*public/assets/*.js' -d "$tmp" 2>/dev/null
|
||||||
|
check_js_dir "$tmp"
|
||||||
|
result=$?
|
||||||
|
rm -rf "$tmp"
|
||||||
|
else
|
||||||
|
log_error "Not found: $path"
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$result" = "0" ]; then
|
||||||
|
log_success "Production defaults OK: $path"
|
||||||
|
else
|
||||||
|
log_error "Not a production bundle: $path"
|
||||||
|
fi
|
||||||
|
return $result
|
||||||
|
}
|
||||||
|
|
||||||
|
paths=("$@")
|
||||||
|
if [ ${#paths[@]} -eq 0 ]; then
|
||||||
|
for p in android/app/src/main/assets/public \
|
||||||
|
ios/App/App/public \
|
||||||
|
android/app/build/outputs/bundle/release/app-release.aab; do
|
||||||
|
[ -e "$p" ] && paths+=("$p")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
status=0
|
||||||
|
for p in "${paths[@]}"; do
|
||||||
|
check_path "$p"
|
||||||
|
rc=$?
|
||||||
|
[ $rc -gt $status ] && status=$rc
|
||||||
|
done
|
||||||
|
exit $status
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
/**
|
/**
|
||||||
* Restore Local Capacitor Plugins
|
* Restore Local Capacitor Plugins
|
||||||
*
|
*
|
||||||
* This script ensures that local custom plugins (SafeArea and SharedImage)
|
* This script ensures that local custom plugins (SafeArea, SharedImage, NotificationInspector)
|
||||||
* are present in capacitor.plugins.json after `npx cap sync` runs.
|
* are present in capacitor.plugins.json after `npx cap sync` runs.
|
||||||
*
|
*
|
||||||
* The capacitor.plugins.json file is auto-generated by Capacitor and gets
|
* The capacitor.plugins.json file is auto-generated by Capacitor and gets
|
||||||
@@ -28,6 +28,10 @@ const LOCAL_PLUGINS = [
|
|||||||
{
|
{
|
||||||
pkg: 'SharedImage',
|
pkg: 'SharedImage',
|
||||||
classpath: 'app.timesafari.sharedimage.SharedImagePlugin'
|
classpath: 'app.timesafari.sharedimage.SharedImagePlugin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pkg: 'NotificationInspector',
|
||||||
|
classpath: 'app.timesafari.notifications.NotificationInspectorPlugin'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ const MIGRATIONS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "008_remove_iViewContent_from_contacts",
|
name: "008_remove_iViewContent_from_contacts",
|
||||||
// Recreate contacts without iViewContent: backup, drop, recreate, restore
|
// Recreate contacts without iViewContent (since we use hideTheirContent instead): backup, drop, recreate, restore
|
||||||
sql: `
|
sql: `
|
||||||
PRAGMA foreign_keys = OFF;
|
PRAGMA foreign_keys = OFF;
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ export type PendingNotificationInfo = {
|
|||||||
|
|
||||||
export interface NotificationInspectorPlugin {
|
export interface NotificationInspectorPlugin {
|
||||||
getPendingNotifications(): Promise<{ pending: PendingNotificationInfo[] }>;
|
getPendingNotifications(): Promise<{ pending: PendingNotificationInfo[] }>;
|
||||||
|
/** Android only: whether the build has a Firebase config (-PfirebaseEnabled). */
|
||||||
|
isFirebaseConfigured(): Promise<{ configured: boolean }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NotificationInspector =
|
export const NotificationInspector =
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
onMessage,
|
onMessage,
|
||||||
} from "firebase/messaging";
|
} from "firebase/messaging";
|
||||||
import { logger } from "@/utils/logger";
|
import { logger } from "@/utils/logger";
|
||||||
|
import { NotificationInspector } from "@/plugins/NotificationInspectorPlugin";
|
||||||
import { handleCapacitorPushNotificationReceived } from "./NativeNotificationService";
|
import { handleCapacitorPushNotificationReceived } from "./NativeNotificationService";
|
||||||
import { getNotificationApiHeaders } from "./notificationApiAuth";
|
import { getNotificationApiHeaders } from "./notificationApiAuth";
|
||||||
import { deferFcmRegistration } from "./notificationAuthLifecycle";
|
import { deferFcmRegistration } from "./notificationAuthLifecycle";
|
||||||
@@ -67,6 +68,24 @@ async function registerRetrievedToken(
|
|||||||
lastRegisteredFcmToken = trimmed;
|
lastRegisteredFcmToken = trimmed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether PushNotifications.register() is safe to call. Android builds without
|
||||||
|
* -PfirebaseEnabled (FOSS/F-Droid/APK) have no Firebase config, and register()
|
||||||
|
* there throws natively, crashing the app before any JS catch can run.
|
||||||
|
*/
|
||||||
|
async function canRegisterNativePush(): Promise<boolean> {
|
||||||
|
if (Capacitor.getPlatform() !== "android") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const { configured } = await NotificationInspector.isFirebaseConfigured();
|
||||||
|
return configured;
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn(`${LOG} Could not check Firebase config; skipping push`, err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Most recent FCM token from native/web push registration (for debug UI). */
|
/** Most recent FCM token from native/web push registration (for debug UI). */
|
||||||
export function getLastKnownFcmToken(): string | null {
|
export function getLastKnownFcmToken(): string | null {
|
||||||
return lastSeenFcmToken ?? lastRegisteredFcmToken;
|
return lastSeenFcmToken ?? lastRegisteredFcmToken;
|
||||||
@@ -80,6 +99,12 @@ export async function reregisterFcmTokenNow(): Promise<string> {
|
|||||||
throw new Error("FCM registration is only available on native platforms");
|
throw new Error("FCM registration is only available on native platforms");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(await canRegisterNativePush())) {
|
||||||
|
throw new Error(
|
||||||
|
"Push is unavailable: this Android build has no Firebase config (built without -PfirebaseEnabled)",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
lastRegisteredFcmToken = null;
|
lastRegisteredFcmToken = null;
|
||||||
|
|
||||||
const cached = lastSeenFcmToken?.trim();
|
const cached = lastSeenFcmToken?.trim();
|
||||||
@@ -290,6 +315,13 @@ async function initializeNativePushAndFirebaseMessagingImpl(): Promise<void> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(await canRegisterNativePush())) {
|
||||||
|
logger.info(
|
||||||
|
`${LOG} No Firebase config in this Android build; skipping push registration`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await PushNotifications.register();
|
await PushNotifications.register();
|
||||||
|
|
||||||
if (app) {
|
if (app) {
|
||||||
|
|||||||
Reference in New Issue
Block a user