remove the remainder of PWA functionality that wasn't being used, and fix the web "share target"
This commit is contained in:
@@ -63,7 +63,7 @@ follow proper review, testing, and documentation procedures.
|
||||
|
||||
- **CI/CD Pipeline**: GitHub Actions, build validation scripts
|
||||
|
||||
- **Service Worker Assembly**: `sw_scripts/`, `sw_combine.js`, WASM copy steps
|
||||
- **WASM Assembly**: WASM copy steps
|
||||
|
||||
## Change Authorization Requirements
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
- **Development**: `npm run dev` - serve and load app
|
||||
|
||||
- **Production**: `npm run build:web:prod` - verify SW and WASM present
|
||||
- **Production**: `npm run build:web:prod` - verify WASM present
|
||||
|
||||
### Mobile Platforms
|
||||
|
||||
@@ -98,13 +98,11 @@
|
||||
verify single-instance,
|
||||
icons, signing
|
||||
|
||||
### Service Worker / WASM
|
||||
### WASM
|
||||
|
||||
- **Trigger**: `sw_combine.js`, WASM copy path
|
||||
- **Trigger**: WASM copy path
|
||||
|
||||
- **Validation**: Verify combined SW exists and is injected; page loads offline;
|
||||
|
||||
WASM present
|
||||
- **Validation**: WASM present
|
||||
|
||||
### Docker
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ dist-electon-build
|
||||
node_modules
|
||||
dist
|
||||
signature.bin
|
||||
# generated during `npm run build`
|
||||
sw_scripts-combined.js
|
||||
*.pem
|
||||
verified.txt
|
||||
myenv
|
||||
|
||||
+35
-201
@@ -1860,34 +1860,17 @@ npm run build:android:assets
|
||||
|
||||
### Build Process Overview
|
||||
|
||||
TimeSafari's build system follows a multi-stage process that prepares assets,
|
||||
combines scripts, and generates platform-specific outputs.
|
||||
TimeSafari's build system follows a multi-stage process that prepares assets
|
||||
and generates platform-specific outputs.
|
||||
|
||||
#### Pre-Build Preparation
|
||||
|
||||
##### 1. Service Worker Script Preparation
|
||||
|
||||
```bash
|
||||
# Optional: Format third-party service worker scripts
|
||||
npx prettier --write ./sw_scripts/
|
||||
```
|
||||
|
||||
**What this does:**
|
||||
|
||||
- Formats cryptographic libraries (`nacl.js`, `noble-curves.js`,
|
||||
`noble-hashes.js`, etc.)
|
||||
- These scripts are automatically combined during the build process
|
||||
- Improves readability and makes version control diffs cleaner
|
||||
- **Note**: This is optional and only needed when updating third-party scripts
|
||||
|
||||
**2. Automatic Pre-Build Steps**
|
||||
The `prebuild` script automatically runs before any build:
|
||||
#### Pre-Build Script
|
||||
|
||||
```json
|
||||
"prebuild": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src && node scripts/copy-wasm.js"
|
||||
```
|
||||
|
||||
npm runs `prebuild` only before a script named `build`. This project defines no `build` script, so none of the `build:*` commands trigger it.
|
||||
npm runs `prebuild` only before a script named `build`. This project defines
|
||||
no `build` script, so none of the `build:*` commands trigger it.
|
||||
|
||||
**What it does when run:**
|
||||
|
||||
@@ -1899,8 +1882,8 @@ npm runs `prebuild` only before a script named `build`. This project defines no
|
||||
**Web Build Process:**
|
||||
|
||||
```text
|
||||
1. Pre-Build: ESLint + Script Combination + WASM Copy
|
||||
2. Environment Setup: Load .env files, set NODE_ENV
|
||||
1. Environment Setup: Load .env files, set NODE_ENV
|
||||
2. Type Check: tsc --noEmit (test and production modes)
|
||||
3. Vite Build: Bundle web assets
|
||||
4. Output: Production-ready files in dist/
|
||||
```
|
||||
@@ -1925,34 +1908,6 @@ npm runs `prebuild` only before a script named `build`. This project defines no
|
||||
5. Package Creation: APK/IPA generation
|
||||
```
|
||||
|
||||
#### Service Worker Architecture
|
||||
|
||||
**Script Organization:**
|
||||
|
||||
- `sw_scripts/` - Individual third-party scripts
|
||||
- `sw_combine.js` - Combines scripts into single file
|
||||
- `sw_scripts-combined.js` - Combined service worker (317KB, 10K+ lines)
|
||||
- `vite.config.utils.mts` - PWA configuration using combined script
|
||||
|
||||
**PWA Integration:**
|
||||
|
||||
```typescript
|
||||
// vite.config.utils.mts
|
||||
pwaConfig: {
|
||||
strategies: "injectManifest",
|
||||
filename: "sw_scripts-combined.js", // Uses our combined script
|
||||
// ... manifest configuration
|
||||
}
|
||||
```
|
||||
|
||||
**What Gets Combined:**
|
||||
|
||||
- `nacl.js` - NaCl cryptographic library
|
||||
- `noble-curves.js` - Elliptic curve cryptography (177KB)
|
||||
- `noble-hashes.js` - Cryptographic hash functions (91KB)
|
||||
- `safari-notifications.js` - Safari-specific notifications
|
||||
- `additional-scripts.js` - Additional service worker functionality
|
||||
|
||||
#### Process Environment Configuration
|
||||
|
||||
**Environment Files:**
|
||||
@@ -2005,10 +1960,13 @@ device)
|
||||
```folders
|
||||
dist/
|
||||
├── index.html # Main HTML file
|
||||
├── assets/ # Bundled JavaScript/CSS
|
||||
├── sw.js # Service worker (injected)
|
||||
├── manifest.webmanifest # PWA manifest
|
||||
└── wasm/ # SQLite WASM files
|
||||
├── assets/ # Bundled JavaScript, CSS, and SQLite WASM
|
||||
├── img/ # Static images (copied from public/)
|
||||
├── models/ # Static model files (copied from public/)
|
||||
├── favicon.ico
|
||||
├── manifest.webmanifest # Web app manifest, incl. share_target (copied from public/)
|
||||
├── robots.txt
|
||||
└── sw.js # Service worker for the Web Share Target (copied from public/)
|
||||
```
|
||||
|
||||
**Electron Build:**
|
||||
@@ -2030,17 +1988,14 @@ electron/
|
||||
**Manual Steps (Developer Responsibility):**
|
||||
|
||||
- Database migration testing
|
||||
- Service worker script formatting (optional)
|
||||
- Version updates and changelog
|
||||
- Environment-specific builds
|
||||
|
||||
**Automatic Steps (Build System):**
|
||||
|
||||
- Code linting and formatting
|
||||
- Script combination
|
||||
- Type checking (test and production builds)
|
||||
- Asset optimization
|
||||
- Package creation
|
||||
- Service worker injection
|
||||
|
||||
This architecture ensures consistent builds across all platforms while providing
|
||||
flexibility for platform-specific optimizations and manual quality assurance steps.
|
||||
@@ -2445,9 +2400,7 @@ export async function createBuildConfig(platform: string): Promise<UserConfig> {
|
||||
const appConfig = await loadAppConfig();
|
||||
const isCapacitor = platform === "capacitor";
|
||||
const isElectron = platform === "electron";
|
||||
const isNative = isCapacitor || isElectron;
|
||||
|
||||
// Set platform - PWA is always enabled for web platforms
|
||||
process.env.VITE_PLATFORM = platform;
|
||||
|
||||
return {
|
||||
@@ -2463,7 +2416,6 @@ export async function createBuildConfig(platform: string): Promise<UserConfig> {
|
||||
assetsDir: 'assets',
|
||||
chunkSizeWarningLimit: 1000,
|
||||
rollupOptions: {
|
||||
external: isNative ? ['@capacitor/app'] : [],
|
||||
output: {
|
||||
format: 'esm',
|
||||
generatedCode: { preset: 'es2015' },
|
||||
@@ -2495,12 +2447,7 @@ export async function createBuildConfig(platform: string): Promise<UserConfig> {
|
||||
'@jlongster/sql.js',
|
||||
'absurd-sql',
|
||||
// ... additional dependencies
|
||||
],
|
||||
exclude: isNative ? [
|
||||
'register-service-worker',
|
||||
'workbox-window',
|
||||
// ... native exclusions
|
||||
] : []
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -2523,98 +2470,23 @@ export async function createBuildConfig(platform: string): Promise<UserConfig> {
|
||||
|
||||
**File**: `vite.config.web.mts`
|
||||
**Author**: Matthew Raymer
|
||||
**Description**: Vite configuration for web platform with PWA support
|
||||
**Description**: Vite configuration for the web platform
|
||||
|
||||
**Purpose**: Configures Vite for web builds with environment-specific
|
||||
optimizations and PWA features.
|
||||
**Purpose**: Configures Vite for web builds by delegating to the common
|
||||
configuration with the `web` platform.
|
||||
|
||||
**Configuration Structure**:
|
||||
|
||||
```typescript
|
||||
export default defineConfig(async () => createBuildConfig('web'));
|
||||
```
|
||||
|
||||
**Key Features**:
|
||||
|
||||
- **Environment-Specific Configuration**: Different settings for dev/test/prod
|
||||
- **PWA Integration**: Progressive Web App support with service worker
|
||||
- **Build Optimization**: Manual chunk splitting for better caching
|
||||
- **Source Maps**: Environment-specific source map configuration
|
||||
- **Caching Strategies**: Production-specific API caching
|
||||
|
||||
**Environment Configurations**:
|
||||
|
||||
**Development Mode**:
|
||||
|
||||
```typescript
|
||||
{
|
||||
build: {
|
||||
minify: false,
|
||||
sourcemap: true,
|
||||
rollupOptions: { output: { manualChunks: undefined } }
|
||||
},
|
||||
define: {
|
||||
__DEV__: true,
|
||||
__TEST__: false,
|
||||
__PROD__: false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Test Mode**:
|
||||
|
||||
```typescript
|
||||
{
|
||||
build: {
|
||||
minify: false,
|
||||
sourcemap: true,
|
||||
rollupOptions: { output: { manualChunks: undefined } }
|
||||
},
|
||||
define: {
|
||||
__DEV__: false,
|
||||
__TEST__: true,
|
||||
__PROD__: false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Production Mode**:
|
||||
|
||||
```typescript
|
||||
{
|
||||
build: {
|
||||
minify: 'terser',
|
||||
sourcemap: false,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
vendor: ['vue', 'vue-router', 'pinia'],
|
||||
utils: ['luxon', 'ramda', 'zod'],
|
||||
crypto: ['@ethersproject/wallet', '@ethersproject/hdnode'],
|
||||
sql: ['@jlongster/sql.js', 'absurd-sql']
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
define: {
|
||||
__DEV__: false,
|
||||
__TEST__: false,
|
||||
__PROD__: true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**PWA Configuration**:
|
||||
|
||||
```typescript
|
||||
VitePWA({
|
||||
registerType: 'autoUpdate',
|
||||
manifest: appConfig.pwaConfig?.manifest,
|
||||
devOptions: { enabled: true, type: 'classic' },
|
||||
workbox: {
|
||||
cleanupOutdatedCaches: true,
|
||||
skipWaiting: true,
|
||||
clientsClaim: true,
|
||||
sourcemap: mode !== 'production',
|
||||
maximumFileSizeToCacheInBytes: 10 * 1024 * 1024,
|
||||
runtimeCaching: mode === 'production' ? [/* API caching */] : []
|
||||
}
|
||||
})
|
||||
```
|
||||
- **Minimal Configuration**: Inherits all settings from `vite.config.common.mts`
|
||||
- **Mode Selection**: `--mode test` or `--mode production` (passed by
|
||||
`scripts/build-web.sh`) selects the matching `.env.*` file
|
||||
- **Output**: Writes to `dist/`
|
||||
|
||||
### B.3 vite.config.electron.mts
|
||||
|
||||
@@ -2705,7 +2577,6 @@ export default defineConfig(async () => createBuildConfig('capacitor'));
|
||||
|
||||
- **Minimal Configuration**: Inherits all settings from common config
|
||||
- **Mobile Platform**: Automatically configures for mobile-specific settings
|
||||
- **PWA Disabled**: Progressive Web App features disabled for native apps
|
||||
- **Native Dependencies**: Handles Capacitor-specific dependencies
|
||||
|
||||
### B.5 vite.config.utils.mts
|
||||
@@ -2714,51 +2585,15 @@ export default defineConfig(async () => createBuildConfig('capacitor'));
|
||||
**Author**: Matthew Raymer
|
||||
**Description**: Utility functions for Vite configuration
|
||||
|
||||
**Purpose**: Provides shared configuration loading and PWA manifest generation.
|
||||
**Purpose**: Provides the shared alias configuration and strict ESBuild settings.
|
||||
|
||||
**Key Functions**:
|
||||
|
||||
**loadPackageJson()**: Loads and parses package.json for app configuration
|
||||
**loadAppConfig()**: Returns the alias configuration used by the common and
|
||||
optimized configs
|
||||
|
||||
**loadAppConfig()**: Loads complete application configuration including PWA settings
|
||||
|
||||
**PWA Configuration**:
|
||||
|
||||
```typescript
|
||||
interface PWAConfig {
|
||||
registerType: string;
|
||||
strategies: string;
|
||||
srcDir: string;
|
||||
filename: string;
|
||||
manifest: {
|
||||
name: string;
|
||||
short_name: string;
|
||||
theme_color: string;
|
||||
background_color: string;
|
||||
icons: ManifestIcon[];
|
||||
share_target: ShareTarget;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Manifest Icons**:
|
||||
|
||||
- Android Chrome 192x192 PNG
|
||||
- Android Chrome 512x512 PNG
|
||||
- Maskable icons for adaptive UI
|
||||
|
||||
**Share Target Configuration**:
|
||||
|
||||
```typescript
|
||||
share_target: {
|
||||
action: "/share-target",
|
||||
method: "POST",
|
||||
enctype: "multipart/form-data",
|
||||
params: {
|
||||
files: [{ name: "photo", accept: ["image/*"] }]
|
||||
}
|
||||
}
|
||||
```
|
||||
**getStrictESBuildConfig()**: Returns ESBuild settings that fail the build on
|
||||
syntax and duplicate-declaration errors
|
||||
|
||||
**Alias Configuration**:
|
||||
|
||||
@@ -2826,8 +2661,7 @@ All scripts use consistent error handling:
|
||||
|
||||
**Web Platform**:
|
||||
|
||||
- PWA features enabled
|
||||
- Service worker injection
|
||||
- Requires a secure context (HTTPS, `localhost`, `127.0.0.1`, or `[::1]`)
|
||||
- Browser-specific optimizations
|
||||
|
||||
**Electron Platform**:
|
||||
|
||||
+10
-1
@@ -8,7 +8,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
### Changed
|
||||
- Various npm build script inconsistencies were modified
|
||||
- `npm run build:web` builds production into `dist/` instead of starting the dev server.
|
||||
- `npm run build:web:serve` builds production (instead of development) before serving.
|
||||
- `npm run build:web:docker` builds production (instead of development) before creating the image.
|
||||
- `npm run build:native` builds with the Capacitor Vite config instead of a bare `vite build`.
|
||||
### Removed
|
||||
- `npm run build:web:dev` is removed; use `npm run dev` for the dev server.
|
||||
- Leftover service-worker build code (`sw_scripts/`, the unused PWA config, and the `register-service-worker` package) is removed.
|
||||
### Fixed
|
||||
- Sharing a photo into the installed web app (Web Share Target) works again.
|
||||
- `npm run docker:up:test` and `npm run docker:up:prod` run, instead of failing on a missing `build:web:build` script.
|
||||
|
||||
|
||||
## [1.4.9] - 2026.10.11
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
# Plan: Web Push on the Web Build
|
||||
|
||||
## Goal
|
||||
|
||||
A user of the web build (browser tab or installed web app) can turn on the
|
||||
Daily Reminder and New Activity notifications and receive them, matching what
|
||||
native builds deliver.
|
||||
|
||||
Requirements:
|
||||
|
||||
1. A `DIRECT_NOTIFICATION` push shows its message as sent.
|
||||
2. A `DAILY_CHECK` push shows the count of new activity for the active
|
||||
identity, using the same Endorser query the native fetcher uses.
|
||||
3. The service worker never holds a long-lived credential: it holds only
|
||||
day-scoped JWTs, the same pool native builds hand to their background
|
||||
fetcher.
|
||||
4. The Notifications section of the Account view works on the web build.
|
||||
|
||||
## Can FCM replace Web Push?
|
||||
|
||||
No. Firebase Cloud Messaging on the web (the `firebase/messaging` SDK) is a
|
||||
layer over the browser Push API: `getToken()` requires a registered service
|
||||
worker (`firebase-messaging-sw.js` by default), and that worker must display the
|
||||
notification, the same as a plain Web Push worker. FCM changes only the
|
||||
sending side — the push server would call the FCM HTTP v1 API instead of
|
||||
sending VAPID-signed Web Push requests — and it adds a Google dependency and
|
||||
Firebase config to the web build. It removes none of the work below.
|
||||
|
||||
## Current state
|
||||
|
||||
- `public/sw.js` is registered by `src/main.web.ts` on the web platform. It
|
||||
handles only the Web Share Target POST; it has no `push` or
|
||||
`notificationclick` handler.
|
||||
- The Notifications section of the Account view renders only on native
|
||||
(`v-if="isNativePlatform"`, `src/views/AccountViewView.vue:81`), and the
|
||||
"Notification Push Server" setting is hidden with `v-if="false"`
|
||||
(`src/views/AccountViewView.vue:719`). Web users cannot subscribe.
|
||||
- `src/components/PushNotificationPermission.vue` contains the web
|
||||
subscription flow:
|
||||
- GET `{pushUrl}/web-push/vapid` for the VAPID key (`:242`);
|
||||
- `pushManager.subscribe({ userVisibleOnly: true, ... })` (`:538`);
|
||||
- POST of the subscription to the relative URL `/web-push/subscribe`
|
||||
(`:583`), so the web host must proxy that path to the push server;
|
||||
- `askPermission()` sends the worker the placeholder secret
|
||||
`"temporary-secret"` via `SEND_LOCAL_DATA`, which nothing reads.
|
||||
- The push server sends a JSON payload whose `title` is `DAILY_CHECK` (run the
|
||||
new-activity check) or `DIRECT_NOTIFICATION` (show `message` as-is).
|
||||
- Account data lives in SQLite through absurd-sql, which a service worker
|
||||
cannot open. The worker therefore cannot decrypt the identity and sign its
|
||||
own JWTs.
|
||||
- Native builds solve the same problem with `mintBackgroundJwtTokenPool()`
|
||||
(`src/libs/crypto/backgroundJwtPool.ts`): 100 JWTs, each valid for one UTC
|
||||
day, stored at index `epochDay % 100`, handed to the background fetcher by
|
||||
`configureNativeFetcherIfReady()`
|
||||
(`src/services/notifications/nativeFetcherConfig.ts`). Passkey (`did:peer`)
|
||||
identities cannot mint the pool and raise
|
||||
`BackgroundJwtUnsupportedIdentityError`.
|
||||
|
||||
## Work
|
||||
|
||||
### 1. Service worker push handling (`public/sw.js`)
|
||||
|
||||
- `push`: parse the JSON payload.
|
||||
- `DIRECT_NOTIFICATION`: `showNotification("Direct Message", { body: message })`.
|
||||
- `DAILY_CHECK` (and any other title): take the token for today's slot from the
|
||||
pool, GET `{apiServer}/api/v2/report/claims` with it, count claims newer
|
||||
than the stored `lastNotifiedClaimId`, store the newest ID, and show "There
|
||||
is 1 new activity" / "There are N new activities".
|
||||
- `notificationclick`: close the notification, focus an open app window or open
|
||||
`/`.
|
||||
- Worker state (token pool, `apiServer`, `activeDid`, `lastNotifiedClaimId`)
|
||||
lives in the worker's own IndexedDB database, never in the app's SQLite
|
||||
database.
|
||||
|
||||
### 2. Hand the token pool to the worker
|
||||
|
||||
- On app start, identity change, and pool expiry, mint the pool with
|
||||
`mintBackgroundJwtTokenPool()` and `postMessage` it to the worker together
|
||||
with `apiServer` and `activeDid`. Reuse the trigger points in
|
||||
`src/services/notifications/notificationAuthLifecycle.ts`
|
||||
(`onNotificationAuthMayBeReady`).
|
||||
- Delete the `SEND_LOCAL_DATA` / placeholder-secret path in
|
||||
`PushNotificationPermission.vue`.
|
||||
- Passkey identities: do not offer New Activity notifications (the Daily
|
||||
Reminder needs no token).
|
||||
|
||||
### 3. Empty daily checks
|
||||
|
||||
Chrome requires every push received with `userVisibleOnly: true` to show a
|
||||
notification. A `DAILY_CHECK` that finds nothing new must show one anyway, or
|
||||
Chrome eventually shows its own "This site has been updated in the background"
|
||||
notice. Choose one (see Open questions):
|
||||
|
||||
- show a quiet "No new activity" notification and close it after a short delay;
|
||||
- have the push server skip pushes when there is nothing new, which requires
|
||||
the server to query Endorser on the user's behalf.
|
||||
|
||||
### 4. Re-enable the UI on the web build
|
||||
|
||||
- Render the Notifications section on web when `"PushManager" in window` and a
|
||||
service worker is registered, instead of only on native.
|
||||
- Restore the "Notification Push Server" setting for web.
|
||||
- iOS Safari delivers Web Push only to web apps added to the Home Screen
|
||||
(iOS 16.4+). `public/manifest.webmanifest` is linked from `index.html` and
|
||||
makes the app installable; the UI must explain the Home Screen requirement on
|
||||
iOS.
|
||||
|
||||
### 5. Hosting
|
||||
|
||||
- Confirm each web host (test, production, Docker `docker/*.conf`) proxies
|
||||
`/web-push/subscribe` to the push server, or change the client to POST to
|
||||
`{pushUrl}/web-push/subscribe`.
|
||||
- Serve `sw.js` with `Cache-Control: no-cache` so worker updates reach users
|
||||
promptly.
|
||||
|
||||
### 6. Tests
|
||||
|
||||
- Playwright: register the worker, then deliver pushes with the Chrome DevTools
|
||||
Protocol `ServiceWorker.deliverPushMessage` and assert on the shown
|
||||
notification (`registration.getNotifications()`), for both payload types.
|
||||
- Unit-test the claim-counting function, extracted so it runs outside the
|
||||
worker.
|
||||
|
||||
## Estimate
|
||||
|
||||
Two to four days of work, plus push-server and nginx checks (low confidence:
|
||||
the push server's current behavior and routing are not visible from this
|
||||
repository).
|
||||
|
||||
## Open questions
|
||||
|
||||
- Empty daily checks: silent-notification workaround, or server-side filtering?
|
||||
- Does the push server serve `/web-push/vapid` and accept
|
||||
`/web-push/subscribe`, and does it send `DAILY_CHECK` daily?
|
||||
- Should New Activity on web use the same Endorser query as the native fetcher
|
||||
(starred-plan changes) rather than `/api/v2/report/claims`, so both platforms
|
||||
report the same thing?
|
||||
|
||||
## Rejected
|
||||
|
||||
- **FCM for web:** still needs a service worker; adds a Google dependency to the
|
||||
web build and a server-side switch to the FCM API. See "Can FCM replace Web
|
||||
Push?".
|
||||
- **Reviving the old worker scripts (`sw_scripts/safari-notifications.js`):**
|
||||
they read the Dexie `TimeSafari` IndexedDB database, which no longer holds
|
||||
accounts, and decrypted the identity inside the worker using a secret passed
|
||||
from the page, shipping NaCl and noble crypto libraries into the worker. The
|
||||
day-scoped token pool needs neither.
|
||||
|
||||
## History
|
||||
|
||||
- 2025-02-18 (`2746b8fe`): the Vite config reorganization switched the web build
|
||||
to a Workbox-generated worker, so the custom push, notification-click, and
|
||||
share-target handlers stopped shipping.
|
||||
- 2025-07-18 (`8bc1c521`): VitePWA and the generated worker were removed; the
|
||||
web manifest stopped being generated and linked.
|
||||
- The leftover `sw_scripts/` files and PWA config were deleted, and
|
||||
`public/sw.js` was added for the Web Share Target only.
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] Decide the empty-daily-check behavior
|
||||
- [ ] Confirm push-server endpoints and routing per environment
|
||||
- [ ] `push` and `notificationclick` handlers in `public/sw.js`
|
||||
- [ ] Token-pool handoff to the worker; remove the placeholder secret
|
||||
- [ ] Re-enable the web Notifications UI, with the iOS Home Screen note
|
||||
- [ ] Playwright push-delivery tests
|
||||
@@ -104,9 +104,7 @@ Build Scripts:
|
||||
├── scripts/** # All build and automation scripts
|
||||
├── electron/** # Electron build files
|
||||
├── android/** # Android build configuration
|
||||
├── ios/** # iOS build configuration
|
||||
├── sw_scripts/** # Service worker scripts
|
||||
└── sw_combine.js # Service worker combination
|
||||
└── ios/** # iOS build configuration
|
||||
|
||||
Deployment:
|
||||
├── Dockerfile # Docker configuration
|
||||
|
||||
@@ -175,7 +175,6 @@ export async function createBuildConfig(mode: string) {
|
||||
return defineConfig({
|
||||
define: {
|
||||
'process.env.VITE_PLATFORM': JSON.stringify(mode),
|
||||
// PWA is automatically enabled for web platforms via build configuration
|
||||
__IS_MOBILE__: JSON.stringify(isCapacitor),
|
||||
__USE_QR_READER__: JSON.stringify(!isCapacitor)
|
||||
},
|
||||
|
||||
+2
-5
@@ -103,7 +103,6 @@ The Dockerfile supports these build arguments:
|
||||
| `BUILD_MODE` | `production` | Build mode: development, staging, or production |
|
||||
| `NODE_ENV` | `production` | Node.js environment |
|
||||
| `VITE_PLATFORM` | `web` | Vite platform type |
|
||||
| PWA | `enabled` | Automatically enabled for web platforms |
|
||||
|
||||
### Environment Variables
|
||||
|
||||
@@ -114,7 +113,6 @@ Docker Compose supports these environment variables:
|
||||
| `BUILD_MODE` | `production` | Build mode |
|
||||
| `NODE_ENV` | `production` | Node environment |
|
||||
| `VITE_PLATFORM` | `web` | Vite platform |
|
||||
| PWA | `enabled` | Automatically enabled for web platforms |
|
||||
| `DEV_PORT` | `5173` | Development port |
|
||||
| `STAGING_PORT` | `8080` | Staging port |
|
||||
| `PROD_PORT` | `80` | Production port |
|
||||
@@ -219,13 +217,12 @@ BUILD_MODE=staging NODE_ENV=staging ./docker/run.sh custom
|
||||
docker build \
|
||||
--build-arg BUILD_MODE=staging \
|
||||
--build-arg NODE_ENV=staging \
|
||||
|
||||
-t timesafari:staging-no-pwa .
|
||||
-t timesafari:staging .
|
||||
|
||||
# Run with custom configuration
|
||||
docker run -d -p 9000:80 \
|
||||
-e VITE_APP_SERVER=https://test.example.com \
|
||||
timesafari:staging-no-pwa
|
||||
timesafari:staging
|
||||
```
|
||||
|
||||
### Docker Compose with Custom Variables
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
# BUILD_MODE: development, staging, or production
|
||||
# NODE_ENV: node environment
|
||||
# VITE_PLATFORM: vite platform
|
||||
# PWA: automatically enabled for web platforms
|
||||
# PORT: port to expose
|
||||
# ENV_FILE: environment file to use
|
||||
|
||||
@@ -71,7 +70,6 @@ show_usage() {
|
||||
echo " BUILD_MODE: development, staging, or production"
|
||||
echo " NODE_ENV: node environment"
|
||||
echo " VITE_PLATFORM: vite platform"
|
||||
echo " PWA: automatically enabled for web platforms"
|
||||
echo " PORT: port to expose"
|
||||
echo " ENV_FILE: environment file to use"
|
||||
}
|
||||
@@ -86,7 +84,6 @@ show_build_args() {
|
||||
echo " BUILD_MODE: development"
|
||||
echo " NODE_ENV: development"
|
||||
echo " VITE_PLATFORM: web"
|
||||
echo " PWA: enabled (web platform)"
|
||||
echo " Target: development"
|
||||
echo " Port: 5173"
|
||||
;;
|
||||
@@ -94,7 +91,6 @@ show_build_args() {
|
||||
echo " BUILD_MODE: staging"
|
||||
echo " NODE_ENV: staging"
|
||||
echo " VITE_PLATFORM: web"
|
||||
echo " PWA: enabled (web platform)"
|
||||
echo " Target: staging"
|
||||
echo " Port: 80 (mapped to 8080)"
|
||||
;;
|
||||
@@ -102,7 +98,6 @@ show_build_args() {
|
||||
echo " BUILD_MODE: production"
|
||||
echo " NODE_ENV: production"
|
||||
echo " VITE_PLATFORM: web"
|
||||
echo " PWA: enabled (web platform)"
|
||||
echo " Target: production"
|
||||
echo " Port: 80"
|
||||
;;
|
||||
@@ -110,7 +105,6 @@ show_build_args() {
|
||||
echo " BUILD_MODE: \${BUILD_MODE:-production}"
|
||||
echo " NODE_ENV: \${NODE_ENV:-production}"
|
||||
echo " VITE_PLATFORM: \${VITE_PLATFORM:-web}"
|
||||
echo " PWA: enabled for web platforms"
|
||||
echo " Target: \${BUILD_TARGET:-production}"
|
||||
echo " Port: \${CUSTOM_PORT:-8080}:\${CUSTOM_INTERNAL_PORT:-80}"
|
||||
;;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
|
||||
<link rel="manifest" href="/manifest.webmanifest" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover, user-scalable=no, interactive-widget=overlays-content" />
|
||||
|
||||
<!-- CORS headers removed to allow images from any domain -->
|
||||
|
||||
Generated
-5
@@ -87,7 +87,6 @@
|
||||
"ramda": "^0.29.1",
|
||||
"readable-stream": "^4.5.2",
|
||||
"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",
|
||||
@@ -26317,10 +26316,6 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/register-service-worker": {
|
||||
"version": "1.7.2",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/registry-auth-token": {
|
||||
"version": "3.3.2",
|
||||
"dev": true,
|
||||
|
||||
+1
-2
@@ -90,7 +90,7 @@
|
||||
"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": "npm run build:capacitor:sync && bash -c 'source scripts/common.sh && ensure_ios_capacitor_asset_directories' && npx capacitor-assets generate --assetPath resources",
|
||||
"build:native": "npm run build:capacitor:sync && bash -c 'source scripts/common.sh && ensure_ios_capacitor_asset_directories' && npx capacitor-assets generate --ios --android --assetPath resources",
|
||||
"build:web": "./scripts/build-web.sh",
|
||||
"build:web:docker": "./scripts/build-web.sh --docker",
|
||||
"build:web:docker:prod": "./scripts/build-web.sh --docker:prod",
|
||||
@@ -219,7 +219,6 @@
|
||||
"ramda": "^0.29.1",
|
||||
"readable-stream": "^4.5.2",
|
||||
"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",
|
||||
|
||||
+37
-37
@@ -1,46 +1,46 @@
|
||||
{
|
||||
"name": "Giftopia",
|
||||
"short_name": "Giftopia",
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"theme_color": "#4a90e2",
|
||||
"background_color": "#ffffff",
|
||||
"icons": [
|
||||
{
|
||||
"src": "../icons/icon-48.webp",
|
||||
"type": "image/png",
|
||||
"sizes": "48x48",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "../icons/icon-72.webp",
|
||||
"type": "image/png",
|
||||
"sizes": "72x72",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "../icons/icon-96.webp",
|
||||
"type": "image/png",
|
||||
"sizes": "96x96",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "../icons/icon-128.webp",
|
||||
"type": "image/png",
|
||||
"sizes": "128x128",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "../icons/icon-192.webp",
|
||||
"type": "image/png",
|
||||
"src": "/img/icons/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"purpose": "any maskable"
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "../icons/icon-256.webp",
|
||||
"type": "image/png",
|
||||
"sizes": "256x256",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "../icons/icon-512.webp",
|
||||
"type": "image/png",
|
||||
"src": "/img/icons/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"purpose": "any maskable"
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/img/icons/android-chrome-maskable-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "/img/icons/android-chrome-maskable-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
]
|
||||
],
|
||||
"share_target": {
|
||||
"action": "/share-target",
|
||||
"method": "POST",
|
||||
"enctype": "multipart/form-data",
|
||||
"params": {
|
||||
"files": [
|
||||
{
|
||||
"name": "photo",
|
||||
"accept": ["image/*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/* eslint-env serviceworker */
|
||||
/**
|
||||
* Service worker for the web build.
|
||||
*
|
||||
* Its only job is the Web Share Target: the manifest's share_target POSTs a
|
||||
* photo to /share-target, which no static server can accept. This worker
|
||||
* stashes the photo in Cache Storage and redirects to /shared-photo, where
|
||||
* src/libs/webShareTarget.ts picks it up. Every other request goes straight to
|
||||
* the network (no fetch handler response, no precaching).
|
||||
*
|
||||
* It also replaces the Workbox worker that older builds registered at this
|
||||
* same URL, and deletes that worker's precaches, which otherwise keep serving
|
||||
* a stale index.html that points at JavaScript files no longer deployed.
|
||||
*/
|
||||
|
||||
const SHARE_TARGET_CACHE = "share-target";
|
||||
// Keep in sync with src/libs/webShareTarget.ts
|
||||
const SHARED_PHOTO_KEY = "/share-target/photo";
|
||||
const FILE_NAME_HEADER = "X-File-Name";
|
||||
|
||||
self.addEventListener("install", () => {
|
||||
self.skipWaiting();
|
||||
});
|
||||
|
||||
self.addEventListener("activate", (event) => {
|
||||
event.waitUntil(
|
||||
(async () => {
|
||||
const names = await caches.keys();
|
||||
await Promise.all(
|
||||
names
|
||||
.filter((name) => name !== SHARE_TARGET_CACHE)
|
||||
.map((name) => caches.delete(name)),
|
||||
);
|
||||
await self.clients.claim();
|
||||
})(),
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener("fetch", (event) => {
|
||||
const url = new URL(event.request.url);
|
||||
if (
|
||||
event.request.method !== "POST" ||
|
||||
url.origin !== self.location.origin ||
|
||||
url.pathname !== "/share-target"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.respondWith(
|
||||
(async () => {
|
||||
const formData = await event.request.formData();
|
||||
const photo = formData.get("photo");
|
||||
if (photo instanceof File) {
|
||||
const cache = await caches.open(SHARE_TARGET_CACHE);
|
||||
await cache.put(
|
||||
SHARED_PHOTO_KEY,
|
||||
new Response(photo, {
|
||||
headers: {
|
||||
"Content-Type": photo.type || "application/octet-stream",
|
||||
[FILE_NAME_HEADER]: encodeURIComponent(photo.name || ""),
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
return Response.redirect("/shared-photo?source=share-target", 303);
|
||||
})(),
|
||||
);
|
||||
});
|
||||
+4
-6
@@ -23,10 +23,10 @@ All scripts automatically handle environment variables for different build types
|
||||
|
||||
#### Build Types and Environment Variables
|
||||
|
||||
| Platform | Mode | PWA Enabled | Native Features | Build Script |
|
||||
|----------|------|-------------|-----------------|--------------|
|
||||
| `web` | web | true | false | `build-web.sh` |
|
||||
| `capacitor` | capacitor | false | true | `build-capacitor.sh` |
|
||||
| Platform | Mode | Native Features | Build Script |
|
||||
|----------|------|-----------------|--------------|
|
||||
| `web` | web | false | `build-web.sh` |
|
||||
| `capacitor` | capacitor | true | `build-capacitor.sh` |
|
||||
|
||||
#### Automatic Environment Setup
|
||||
|
||||
@@ -183,8 +183,6 @@ All scripts automatically configure the correct environment variables for their
|
||||
```bash
|
||||
# Capacitor builds automatically get:
|
||||
export VITE_PLATFORM=capacitor
|
||||
export VITE_PWA_ENABLED=false
|
||||
export VITE_DISABLE_PWA=true
|
||||
export DEBUG_MIGRATIONS=0
|
||||
export VITE_GIT_HASH=<git-hash>
|
||||
|
||||
|
||||
@@ -23,8 +23,6 @@ SENSITIVE=(
|
||||
"electron/**"
|
||||
"android/**"
|
||||
"ios/**"
|
||||
"sw_scripts/**"
|
||||
"sw_combine.js"
|
||||
"Dockerfile"
|
||||
"docker/**"
|
||||
"capacitor.config.ts"
|
||||
|
||||
@@ -298,7 +298,6 @@ export async function logToDb(
|
||||
}
|
||||
}
|
||||
|
||||
// similar method is in the sw_scripts/additional-scripts.js file
|
||||
export async function logConsoleAndDb(
|
||||
message: string,
|
||||
isError = false,
|
||||
|
||||
@@ -302,7 +302,6 @@ export async function logToDb(message: string): Promise<void> {
|
||||
await db.logs.update(todayKey, { message: fullMessage });
|
||||
}
|
||||
|
||||
// similar method is in the sw_scripts/additional-scripts.js file
|
||||
export async function logConsoleAndDb(
|
||||
message: string,
|
||||
isError = false,
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Web Share Target handoff (web build only).
|
||||
*
|
||||
* public/sw.js receives the POST from the manifest's share_target, stores the
|
||||
* photo in Cache Storage under these keys, and redirects to /shared-photo.
|
||||
* Keep the constants in sync with public/sw.js.
|
||||
*/
|
||||
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
const SHARE_TARGET_CACHE = "share-target";
|
||||
const SHARED_PHOTO_KEY = "/share-target/photo";
|
||||
const FILE_NAME_HEADER = "X-File-Name";
|
||||
|
||||
export interface WebSharedPhoto {
|
||||
blob: Blob;
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers public/sw.js, which handles the share target POST.
|
||||
*/
|
||||
export function registerWebShareTargetWorker(): void {
|
||||
if (!("serviceWorker" in navigator)) {
|
||||
return;
|
||||
}
|
||||
navigator.serviceWorker.register("/sw.js").catch((error) => {
|
||||
// Not fatal: only sharing into the app depends on the worker.
|
||||
logger.warn("[WebShareTarget] Service worker registration failed", error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the photo stashed by the service worker and removes it, or null
|
||||
* when there is none (or Cache Storage is unavailable).
|
||||
*/
|
||||
export async function takeWebSharedPhoto(): Promise<WebSharedPhoto | null> {
|
||||
if (typeof caches === "undefined") {
|
||||
return null;
|
||||
}
|
||||
const cache = await caches.open(SHARE_TARGET_CACHE);
|
||||
const response = await cache.match(SHARED_PHOTO_KEY);
|
||||
if (!response) {
|
||||
return null;
|
||||
}
|
||||
const blob = await response.blob();
|
||||
const fileName = decodeURIComponent(
|
||||
response.headers.get(FILE_NAME_HEADER) || "",
|
||||
);
|
||||
await cache.delete(SHARED_PHOTO_KEY);
|
||||
return { blob, fileName };
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { logger } from "./utils/logger";
|
||||
import { registerWebShareTargetWorker } from "./libs/webShareTarget";
|
||||
|
||||
const platform = process.env.VITE_PLATFORM;
|
||||
|
||||
@@ -47,5 +48,10 @@ if (!window.isSecureContext) {
|
||||
}
|
||||
|
||||
app.mount("#app");
|
||||
|
||||
// Handles the manifest's share_target POST (see public/sw.js)
|
||||
if (platform === "web") {
|
||||
registerWebShareTargetWorker();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -281,8 +281,6 @@ const routes: Array<RouteRecordRaw> = [
|
||||
component: () => import("../views/SharedPhotoView.vue"),
|
||||
},
|
||||
|
||||
// /share-target is also an endpoint in the service worker
|
||||
|
||||
{
|
||||
path: "/start",
|
||||
name: "start",
|
||||
|
||||
@@ -225,14 +225,4 @@ export interface PlatformService {
|
||||
* @throws if the backup copy fails (the original is never deleted without one)
|
||||
*/
|
||||
resetDatabase?(): Promise<string | undefined>;
|
||||
|
||||
// --- PWA/Web-only methods (optional, only implemented on web) ---
|
||||
/**
|
||||
* Registers the service worker for PWA support (web only)
|
||||
*/
|
||||
registerServiceWorker?(): void;
|
||||
|
||||
/**
|
||||
* Returns true if PWA is enabled (web only)
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -1546,9 +1546,6 @@ export class CapacitorPlatformService
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- PWA/Web-only methods (no-op for Capacitor) ---
|
||||
public registerServiceWorker(): void {}
|
||||
|
||||
// Database utility methods - inherited from BaseDatabaseService
|
||||
// generateInsertStatement, updateDefaultSettings, updateActiveDid,
|
||||
// getActiveIdentity, insertNewDidIntoSettings, updateDidSpecificSettings,
|
||||
|
||||
@@ -163,7 +163,4 @@ export class ElectronPlatformService extends CapacitorPlatformService {
|
||||
isWeb(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- PWA/Web-only methods (no-op for Electron) ---
|
||||
public registerServiceWorker(): void {}
|
||||
}
|
||||
|
||||
@@ -136,6 +136,7 @@ import {
|
||||
} from "../constants/app";
|
||||
import { accessToken } from "../libs/crypto";
|
||||
import { base64ToBlob, SHARED_PHOTO_BASE64_KEY } from "../libs/util";
|
||||
import { takeWebSharedPhoto } from "../libs/webShareTarget";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
||||
@@ -221,6 +222,17 @@ export default class SharedPhotoView extends Vue {
|
||||
const activeIdentity = await (this as any).$getActiveIdentity();
|
||||
this.activeDid = activeIdentity.activeDid;
|
||||
|
||||
// Web Share Target: public/sw.js stashes the photo in Cache Storage
|
||||
if (this.$route.query["source"] === "share-target") {
|
||||
const shared = await takeWebSharedPhoto();
|
||||
if (shared) {
|
||||
this.imageBlob = shared.blob;
|
||||
this.imageFileName = shared.fileName;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Native share: main.capacitor.ts stores the photo in the temp table
|
||||
const temp = await this.$getTemp(SHARED_PHOTO_BASE64_KEY);
|
||||
const imageB64 = temp?.blobB64 as string;
|
||||
if (temp) {
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
import * as path from "path";
|
||||
import { promises as fs } from "fs";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
export async function loadAppConfig() {
|
||||
const packageJson = await loadPackageJson();
|
||||
const appName = process.env.TIME_SAFARI_APP_TITLE || packageJson.name;
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
return {
|
||||
pwaConfig: {
|
||||
manifest: {
|
||||
name: appName,
|
||||
short_name: appName,
|
||||
icons: [
|
||||
{
|
||||
src: "./img/icons/android-chrome-192x192.png",
|
||||
sizes: "192x192",
|
||||
type: "image/png",
|
||||
},
|
||||
{
|
||||
src: "./img/icons/android-chrome-512x512.png",
|
||||
sizes: "512x512",
|
||||
type: "image/png",
|
||||
},
|
||||
{
|
||||
src: "./img/icons/android-chrome-maskable-192x192.png",
|
||||
sizes: "192x192",
|
||||
type: "image/png",
|
||||
purpose: "maskable",
|
||||
},
|
||||
{
|
||||
src: "./img/icons/android-chrome-maskable-512x512.png",
|
||||
sizes: "512x512",
|
||||
type: "image/png",
|
||||
purpose: "maskable",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
aliasConfig: {
|
||||
"@": path.resolve(path.dirname(__dirname), "src"),
|
||||
buffer: path.resolve(path.dirname(__dirname), "node_modules", "buffer"),
|
||||
"dexie-export-import/dist/import":
|
||||
"dexie-export-import/dist/import/index.js",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function loadPackageJson() {
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const packageJsonPath = path.resolve(path.dirname(__dirname), "package.json");
|
||||
const packageJsonData = await fs.readFile(packageJsonPath, "utf-8");
|
||||
return JSON.parse(packageJsonData);
|
||||
}
|
||||
-1051
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,7 @@
|
||||
*
|
||||
* Related Files:
|
||||
* - Frontend invite handling: src/libs/endorserServer.ts
|
||||
* - JWT creation: sw_scripts/safari-notifications.js
|
||||
* - JWT creation: src/libs/crypto/vc/index.ts
|
||||
*
|
||||
* @see Documentation on invitation flows in usage-guide.md
|
||||
* @requires @playwright/test
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
*
|
||||
* Related Files:
|
||||
* - Project utilities: ./testUtils
|
||||
* - JWT handling: sw_scripts/safari-notifications.js
|
||||
* - JWT handling: src/libs/crypto/vc/index.ts
|
||||
* - Project view: src/views/ProjectView.vue
|
||||
*
|
||||
* @see Documentation in usage-guide.md for project creation workflows
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
*
|
||||
* Related Files:
|
||||
* - Gift recording view: src/views/RecordGiftView.vue
|
||||
* - JWT creation: sw_scripts/safari-notifications.js
|
||||
* - JWT creation: src/libs/crypto/vc/index.ts
|
||||
* - Endorser API: src/libs/endorserServer.ts
|
||||
*
|
||||
* @see Documentation in usage-guide.md for gift recording workflows
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
*
|
||||
* Related Files:
|
||||
* - Gift recording view: src/views/RecordGiftView.vue
|
||||
* - JWT creation: sw_scripts/safari-notifications.js
|
||||
* - JWT creation: src/libs/crypto/vc/index.ts
|
||||
* - Endorser API: src/libs/endorserServer.ts
|
||||
* - Test utilities: ./testUtils.ts
|
||||
*
|
||||
|
||||
+1
-10
@@ -15,9 +15,7 @@ export async function createBuildConfig(platform: string): Promise<UserConfig> {
|
||||
const appConfig = await loadAppConfig();
|
||||
const isCapacitor = platform === "capacitor";
|
||||
const isElectron = platform === "electron";
|
||||
const isNative = isCapacitor || isElectron;
|
||||
|
||||
// Set platform - PWA is always enabled for web platforms
|
||||
process.env.VITE_PLATFORM = platform;
|
||||
|
||||
// Environment variables are loaded from .env files via dotenv.config() above
|
||||
@@ -74,7 +72,6 @@ export async function createBuildConfig(platform: string): Promise<UserConfig> {
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
||||
'process.env.VITE_PLATFORM': JSON.stringify(platform),
|
||||
'process.env.VITE_LOG_LEVEL': JSON.stringify(process.env.VITE_LOG_LEVEL),
|
||||
// PWA is always enabled for web platforms
|
||||
__dirname: JSON.stringify(process.cwd()),
|
||||
__IS_MOBILE__: JSON.stringify(isCapacitor),
|
||||
__IS_ELECTRON__: JSON.stringify(isElectron),
|
||||
@@ -106,13 +103,7 @@ export async function createBuildConfig(platform: string): Promise<UserConfig> {
|
||||
'absurd-sql',
|
||||
'absurd-sql/dist/indexeddb-main-thread',
|
||||
'absurd-sql/dist/indexeddb-backend'
|
||||
],
|
||||
exclude: isNative ? [
|
||||
'register-service-worker',
|
||||
'workbox-window',
|
||||
'web-push',
|
||||
'serviceworker-webpack-plugin'
|
||||
] : []
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
*
|
||||
* Environment Variables:
|
||||
* - VITE_PLATFORM: Set to "electron"
|
||||
* - PWA: Disabled for desktop (via build exclusion)
|
||||
*/
|
||||
|
||||
import { defineConfig } from "vite";
|
||||
|
||||
@@ -24,7 +24,6 @@ export async function createOptimizedBuildConfig(mode: string): Promise<UserConf
|
||||
const isElectron = mode === "electron";
|
||||
const isNative = isCapacitor || isElectron;
|
||||
|
||||
// Set platform - PWA is always enabled for web platforms
|
||||
process.env.VITE_PLATFORM = mode;
|
||||
|
||||
return {
|
||||
@@ -120,13 +119,7 @@ export async function createOptimizedBuildConfig(mode: string): Promise<UserConf
|
||||
'vue-router',
|
||||
'pinia',
|
||||
'vue-facing-decorator'
|
||||
],
|
||||
exclude: isNative ? [
|
||||
'register-service-worker',
|
||||
'workbox-window',
|
||||
'web-push',
|
||||
'serviceworker-webpack-plugin'
|
||||
] : []
|
||||
]
|
||||
},
|
||||
// Enhanced performance optimizations
|
||||
esbuild: {
|
||||
|
||||
@@ -1,110 +1,16 @@
|
||||
import * as path from "path";
|
||||
import { fileURLToPath } from 'url';
|
||||
import { promises as fs } from "fs";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
async function loadPackageJson() {
|
||||
const packageJsonPath = path.join(__dirname, 'package.json');
|
||||
const packageJsonContent = await fs.readFile(packageJsonPath, 'utf-8');
|
||||
return JSON.parse(packageJsonContent);
|
||||
}
|
||||
|
||||
interface ManifestIcon {
|
||||
src: string;
|
||||
sizes: string;
|
||||
type: string;
|
||||
purpose?: string;
|
||||
}
|
||||
|
||||
interface ShareTarget {
|
||||
action: string;
|
||||
method: "POST";
|
||||
enctype: string;
|
||||
params: {
|
||||
files: Array<{
|
||||
name: string;
|
||||
accept: string[];
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
interface PWAConfig {
|
||||
registerType: string;
|
||||
strategies: string;
|
||||
srcDir: string;
|
||||
filename: string;
|
||||
manifest: {
|
||||
name: string;
|
||||
short_name: string;
|
||||
theme_color: string;
|
||||
background_color: string;
|
||||
icons: ManifestIcon[];
|
||||
share_target: ShareTarget;
|
||||
};
|
||||
}
|
||||
|
||||
interface AppConfig {
|
||||
pwaConfig: PWAConfig;
|
||||
aliasConfig: {
|
||||
[key: string]: string;
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadAppConfig(): Promise<AppConfig> {
|
||||
const packageJson = await loadPackageJson();
|
||||
const appName = process.env.TIME_SAFARI_APP_TITLE || packageJson.name;
|
||||
|
||||
return {
|
||||
pwaConfig: {
|
||||
registerType: "autoUpdate",
|
||||
strategies: "injectManifest",
|
||||
srcDir: ".",
|
||||
filename: "sw_scripts-combined.js",
|
||||
manifest: {
|
||||
name: appName,
|
||||
short_name: appName,
|
||||
theme_color: "#4a90e2",
|
||||
background_color: "#ffffff",
|
||||
icons: [
|
||||
{
|
||||
src: "./img/icons/android-chrome-192x192.png",
|
||||
sizes: "192x192",
|
||||
type: "image/png",
|
||||
},
|
||||
{
|
||||
src: "./img/icons/android-chrome-512x512.png",
|
||||
sizes: "512x512",
|
||||
type: "image/png",
|
||||
},
|
||||
{
|
||||
src: "./img/icons/android-chrome-maskable-192x192.png",
|
||||
sizes: "192x192",
|
||||
type: "image/png",
|
||||
purpose: "maskable",
|
||||
},
|
||||
{
|
||||
src: "./img/icons/android-chrome-maskable-512x512.png",
|
||||
sizes: "512x512",
|
||||
type: "image/png",
|
||||
purpose: "maskable",
|
||||
},
|
||||
],
|
||||
share_target: {
|
||||
action: "/share-target",
|
||||
method: "POST",
|
||||
enctype: "multipart/form-data",
|
||||
params: {
|
||||
files: [
|
||||
{
|
||||
name: "photo",
|
||||
accept: ["image/*"],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
aliasConfig: {
|
||||
"@": path.resolve(__dirname, "src"),
|
||||
buffer: path.resolve(__dirname, "node_modules", "buffer"),
|
||||
|
||||
Reference in New Issue
Block a user