forked from jsnbuchanan/crowd-funder-for-time-pwa
- Add electron platform section to capacitor.config.json - Configure deep linking with timesafari:// scheme - Set up build options for macOS, Windows, and Linux - Configure output directory and file inclusion - Add platform-specific build targets (DMG, NSIS, AppImage) - Support both x64 and arm64 architectures for macOS - Set appropriate app categories for each platform This enables building TimeSafari as a native desktop application using Capacitor's Electron platform while maintaining existing mobile and web functionality.
667 lines
16 KiB
Markdown
667 lines
16 KiB
Markdown
# Building TimeSafari
|
|
|
|
This guide explains how to build TimeSafari for different platforms using our unified build scripts.
|
|
|
|
## Prerequisites
|
|
|
|
For a quick dev environment setup, use [pkgx](https://pkgx.dev).
|
|
|
|
- Node.js (LTS version recommended)
|
|
- npm (comes with Node.js)
|
|
- Git
|
|
- For mobile builds: Android Studio (Android) or Xcode (iOS)
|
|
- For desktop builds: Capacitor Electron platform
|
|
|
|
## Unified Build Scripts
|
|
|
|
TimeSafari now uses unified build scripts that automatically handle environment variables, logging, error handling, and timing. All scripts are located in the `scripts/` directory and use a common utilities library.
|
|
|
|
### Script Features
|
|
|
|
- **Automatic Environment Setup**: Each script sets the correct environment variables for its build type
|
|
- **Rich Logging**: Colored, timestamped output with different log levels
|
|
- **Error Handling**: Proper exit codes and graceful failure recovery
|
|
- **Timing**: Automatic execution time tracking for each step
|
|
- **Validation**: Checks for required dependencies and files
|
|
- **CLI Options**: `--help`, `--verbose`, `--env` flags for all scripts
|
|
|
|
### Available Scripts
|
|
|
|
| Script | Purpose | Command |
|
|
|--------|---------|---------|
|
|
| `capacitor-dev.sh` | Capacitor development | `./scripts/capacitor-dev.sh` |
|
|
| `capacitor-build.sh` | Capacitor build | `./scripts/build-capacitor.sh` |
|
|
| `web-dev.sh` | Web development | `./scripts/web-dev.sh` |
|
|
| `web-build.sh` | Web build | `./scripts/build-web.sh` |
|
|
|
|
### Environment Variables
|
|
|
|
All scripts automatically set the correct environment variables for their build type:
|
|
|
|
| Build Type | VITE_PLATFORM | VITE_PWA_ENABLED | VITE_DISABLE_PWA | NODE_ENV |
|
|
|------------|---------------|------------------|------------------|----------|
|
|
| `capacitor` | capacitor | false | true | - |
|
|
| `web` | web | true | false | - |
|
|
|
|
### CLI Options
|
|
|
|
All scripts support these options:
|
|
|
|
```bash
|
|
# Show help
|
|
./scripts/build-capacitor.sh --help
|
|
|
|
# Enable verbose logging
|
|
./scripts/build-capacitor.sh --verbose
|
|
|
|
# Show environment variables
|
|
./scripts/build-capacitor.sh --env
|
|
```
|
|
|
|
## Forks
|
|
|
|
If you have forked this to make your own app, you'll want to customize the iOS & Android files. You can either edit existing ones, or you can remove the `ios` and `android` directories and regenerate them before the `npx cap sync` step in each setup.
|
|
|
|
```bash
|
|
npx cap add android
|
|
npx cap add ios
|
|
```
|
|
|
|
You'll also want to edit the deep link configuration (see below).
|
|
|
|
## Initial Setup
|
|
|
|
Install dependencies:
|
|
|
|
```bash
|
|
npm install
|
|
```
|
|
|
|
## Package Management
|
|
|
|
TimeSafari uses a mixed package management approach, combining npm and JSR (JavaScript Registry) for optimal dependency management.
|
|
|
|
### JSR Integration
|
|
|
|
Some packages are installed via JSR for better ESM support and modern TypeScript compatibility:
|
|
|
|
```bash
|
|
# Install JSR packages
|
|
npx jsr add @nostr/tools
|
|
```
|
|
|
|
### Package Migration History
|
|
|
|
#### nostr-tools → @nostr/tools
|
|
|
|
**Date**: June 2025
|
|
**Reason**: Resolved Vite/Rollup build issues with deep imports
|
|
|
|
**Before** (npm):
|
|
|
|
```typescript
|
|
import { finalizeEvent } from "nostr-tools/lib/cjs/index.js";
|
|
import { accountFromExtendedKey } from "nostr-tools/lib/cjs/nip06.js";
|
|
```
|
|
|
|
**After** (JSR):
|
|
|
|
```typescript
|
|
import { finalizeEvent } from "@nostr/tools";
|
|
import { accountFromExtendedKey } from "@nostr/tools/nip06";
|
|
```
|
|
|
|
**Benefits**:
|
|
- ✅ Proper ESM support
|
|
- ✅ No deep import issues with Vite/Rollup
|
|
- ✅ Better TypeScript compatibility
|
|
- ✅ Modern package structure
|
|
|
|
### Current Package Strategy
|
|
|
|
- **npm**: Primary package manager for most dependencies
|
|
- **JSR**: Used for packages with better ESM support or modern alternatives
|
|
- **Mixed approach**: Allows using the best package for each dependency
|
|
|
|
### When to Use JSR
|
|
|
|
Consider using JSR for:
|
|
- Packages with ESM/CJS compatibility issues in npm
|
|
- Modern TypeScript-first packages
|
|
- Packages that work better with modern bundlers
|
|
- New dependencies where JSR has a better alternative
|
|
|
|
### Vite Configuration
|
|
|
|
The build system is configured to handle both npm and JSR packages:
|
|
|
|
```typescript
|
|
// vite.config.common.mts
|
|
resolve: {
|
|
alias: {
|
|
'@nostr/tools': path.resolve(__dirname, 'node_modules/@nostr/tools'),
|
|
'@nostr/tools/nip06': path.resolve(__dirname, 'node_modules/@nostr/tools/nip06'),
|
|
}
|
|
}
|
|
```
|
|
|
|
### Troubleshooting Package Issues
|
|
|
|
1. **Build failures with deep imports**
|
|
- Check if package has ESM/CJS compatibility issues
|
|
- Consider JSR alternative if available
|
|
- Update Vite configuration if needed
|
|
|
|
2. **TypeScript errors**
|
|
- Ensure proper type definitions are available
|
|
- Check package exports in package.json
|
|
- Verify import paths match package structure
|
|
|
|
3. **Mixed package manager issues**
|
|
- Keep package.json and node_modules in sync
|
|
- Use `npm install` after JSR package additions
|
|
- Check for conflicting package versions
|
|
|
|
## Web Development
|
|
|
|
### Local Development
|
|
|
|
```bash
|
|
npm run dev
|
|
```
|
|
|
|
### Web Build for Server
|
|
|
|
1. Run the production build:
|
|
|
|
```bash
|
|
npm run build:web
|
|
```
|
|
|
|
The built files will be in the `dist` directory.
|
|
|
|
2. To test the production build locally:
|
|
|
|
```bash
|
|
npm run serve
|
|
```
|
|
|
|
### Environment Configuration
|
|
|
|
For different environments, create `.env` files:
|
|
|
|
```bash
|
|
# .env.development
|
|
VITE_APP_SERVER=https://dev.timesafari.app
|
|
VITE_DEFAULT_ENDORSER_API_SERVER=https://dev-api.endorser.ch
|
|
VITE_DEFAULT_IMAGE_API_SERVER=https://dev-image-api.timesafari.app
|
|
VITE_DEFAULT_PARTNER_API_SERVER=https://dev-partner-api.endorser.ch
|
|
VITE_DEFAULT_PUSH_SERVER=https://dev.timesafari.app
|
|
VITE_PASSKEYS_ENABLED=true
|
|
|
|
# .env.production
|
|
VITE_APP_SERVER=https://timesafari.app
|
|
VITE_DEFAULT_ENDORSER_API_SERVER=https://api.endorser.ch
|
|
VITE_DEFAULT_IMAGE_API_SERVER=https://image-api.timesafari.app
|
|
VITE_DEFAULT_PARTNER_API_SERVER=https://partner-api.endorser.ch
|
|
VITE_DEFAULT_PUSH_SERVER=https://timesafari.app
|
|
VITE_PASSKEYS_ENABLED=true
|
|
```
|
|
|
|
## Desktop Build (Capacitor Electron)
|
|
|
|
### Prerequisites
|
|
|
|
1. Install Capacitor CLI:
|
|
|
|
```bash
|
|
npm install -g @capacitor/cli
|
|
```
|
|
|
|
2. Add Electron platform:
|
|
|
|
```bash
|
|
npx cap add electron
|
|
```
|
|
|
|
### Development
|
|
|
|
For development with automatic environment setup:
|
|
|
|
```bash
|
|
# Build web assets
|
|
npm run build:capacitor
|
|
|
|
# Sync with Capacitor
|
|
npx cap sync electron
|
|
|
|
# Open in Electron
|
|
npx cap open electron
|
|
```
|
|
|
|
### Production Build
|
|
|
|
For production builds:
|
|
|
|
```bash
|
|
# Build web assets
|
|
npm run build:capacitor
|
|
|
|
# Sync with Capacitor
|
|
npx cap sync electron
|
|
|
|
# Build Electron app
|
|
npx cap build electron
|
|
```
|
|
|
|
### Packaging
|
|
|
|
Capacitor Electron uses electron-builder for packaging. Configure the build in `capacitor.config.json`:
|
|
|
|
```json
|
|
{
|
|
"plugins": {
|
|
"ElectronBuilder": {
|
|
"buildOptions": {
|
|
"appId": "app.timesafari.app",
|
|
"productName": "TimeSafari",
|
|
"directories": {
|
|
"output": "dist-electron-packages"
|
|
},
|
|
"files": [
|
|
"dist/**/*",
|
|
"electron/**/*"
|
|
],
|
|
"linux": {
|
|
"target": ["AppImage", "deb"],
|
|
"category": "Office"
|
|
},
|
|
"mac": {
|
|
"target": ["dmg", "zip"],
|
|
"category": "public.app-category.productivity"
|
|
},
|
|
"win": {
|
|
"target": ["nsis", "portable"]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Running the Packaged App
|
|
|
|
- **Linux**: AppImage files are self-contained executables
|
|
- **macOS**: `.app` bundles can be dragged to Applications folder
|
|
- **Windows**: `.exe` installers or portable executables
|
|
|
|
## Mobile Builds (Capacitor)
|
|
|
|
### Android Build
|
|
|
|
Prerequisites: Android Studio with Java SDK installed
|
|
|
|
#### Complete Build Process
|
|
|
|
Use the unified Android build script:
|
|
|
|
```bash
|
|
./scripts/build-android.sh
|
|
```
|
|
|
|
This script automatically:
|
|
1. Sets up environment variables for Capacitor
|
|
2. Cleans previous builds
|
|
3. Builds web assets
|
|
4. Builds Capacitor version
|
|
5. Cleans and builds Gradle project
|
|
6. Syncs with Capacitor
|
|
7. Generates assets
|
|
8. Opens Android Studio
|
|
|
|
#### Manual Steps (if needed)
|
|
|
|
If you need to run individual steps:
|
|
|
|
1. Build the web assets:
|
|
```bash
|
|
npm run build:web
|
|
npm run build:capacitor
|
|
```
|
|
|
|
2. Update Android project:
|
|
```bash
|
|
npx cap sync android
|
|
```
|
|
|
|
3. Generate assets:
|
|
```bash
|
|
npx capacitor-assets generate --android
|
|
```
|
|
|
|
4. Open in Android Studio:
|
|
```bash
|
|
npx cap open android
|
|
```
|
|
|
|
#### Console Build
|
|
|
|
For building from the console:
|
|
|
|
```bash
|
|
cd android
|
|
./gradlew clean
|
|
./gradlew build -Dlint.baselines.continue=true
|
|
cd -
|
|
```
|
|
|
|
For creating an `aab` file:
|
|
|
|
```bash
|
|
cd android
|
|
./gradlew bundleDebug -Dlint.baselines.continue=true
|
|
cd -
|
|
```
|
|
|
|
For creating a signed release:
|
|
|
|
1. Setup signing configuration in `app/gradle.properties.secrets`
|
|
2. Add signing key file `app/time-safari-upload-key-pkcs12.jks`
|
|
3. Update version in `app/build.gradle`
|
|
4. Build release:
|
|
```bash
|
|
cd android
|
|
./gradlew bundleRelease -Dlint.baselines.continue=true
|
|
cd -
|
|
```
|
|
|
|
The `aab` file will be at `app/build/outputs/bundle/release`.
|
|
|
|
### iOS Build
|
|
|
|
Prerequisites: macOS with Xcode installed
|
|
|
|
#### First-time iOS Configuration
|
|
|
|
- Generate certificates inside Xcode
|
|
- Right-click on App and under Signing & Capabilities set the Team
|
|
|
|
#### Build Process
|
|
|
|
1. Build the web assets & update iOS:
|
|
```bash
|
|
npm run build:web
|
|
npm run build:capacitor
|
|
npx cap sync ios
|
|
```
|
|
|
|
2. Generate assets:
|
|
```bash
|
|
# Create required directories
|
|
mkdir -p ios/App/App/Assets.xcassets/AppIcon.appiconset
|
|
echo '{"images":[]}' > ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json
|
|
mkdir -p ios/App/App/Assets.xcassets/Splash.imageset
|
|
echo '{"images":[]}' > ios/App/App/Assets.xcassets/Splash.imageset/Contents.json
|
|
|
|
# Generate assets
|
|
npx capacitor-assets generate --ios
|
|
```
|
|
|
|
3. Update version to match Android & package.json:
|
|
```bash
|
|
cd ios/App
|
|
xcrun agvtool new-version 35
|
|
perl -p -i -e "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 1.0.2;/g" App.xcodeproj/project.pbxproj
|
|
cd -
|
|
```
|
|
|
|
4. Open in Xcode:
|
|
```bash
|
|
npx cap open ios
|
|
```
|
|
|
|
5. Build and run on simulator or device using Xcode
|
|
|
|
#### Release Process
|
|
|
|
1. Choose Product -> Destination -> Any iOS Device
|
|
2. Choose Product -> Archive
|
|
3. Click Distribute -> App Store Connect
|
|
4. In App Store Connect, add the build to the distribution
|
|
|
|
## Testing
|
|
|
|
### Complete Test Suite
|
|
|
|
Run all tests with automatic environment setup:
|
|
|
|
```bash
|
|
./scripts/test-all.sh
|
|
```
|
|
|
|
### Mobile Tests
|
|
|
|
Run mobile-specific tests:
|
|
|
|
```bash
|
|
./scripts/test-mobile.sh
|
|
```
|
|
|
|
### Environment Testing
|
|
|
|
Test environment variable handling:
|
|
|
|
```bash
|
|
./scripts/test-env.sh
|
|
```
|
|
|
|
## Docker Deployment
|
|
|
|
The application can be containerized using Docker for consistent deployment across environments.
|
|
|
|
### Prerequisites
|
|
|
|
- Docker installed on your system
|
|
- Docker Compose (optional, for multi-container setups)
|
|
|
|
### Building the Docker Image
|
|
|
|
1. Build the Docker image:
|
|
|
|
```bash
|
|
docker build -t timesafari:latest .
|
|
```
|
|
|
|
2. For development builds with specific environment variables:
|
|
|
|
```bash
|
|
docker build --build-arg NODE_ENV=development -t timesafari:dev .
|
|
```
|
|
|
|
### Running the Container
|
|
|
|
1. Run the container:
|
|
|
|
```bash
|
|
docker run -d -p 80:80 timesafari:latest
|
|
```
|
|
|
|
2. For development with hot-reloading:
|
|
|
|
```bash
|
|
docker run -d -p 80:80 -v $(pwd):/app timesafari:dev
|
|
```
|
|
|
|
### Using Docker Compose
|
|
|
|
Create a `docker-compose.yml` file:
|
|
|
|
```yaml
|
|
version: '3.8'
|
|
services:
|
|
timesafari:
|
|
build: .
|
|
ports:
|
|
- "80:80"
|
|
environment:
|
|
- NODE_ENV=production
|
|
restart: unless-stopped
|
|
```
|
|
|
|
Run with Docker Compose:
|
|
|
|
```bash
|
|
docker-compose up -d
|
|
```
|
|
|
|
### Production Deployment
|
|
|
|
For production deployment, consider the following:
|
|
|
|
1. Use specific version tags instead of 'latest'
|
|
2. Implement health checks
|
|
3. Configure proper logging
|
|
4. Set up reverse proxy with SSL termination
|
|
5. Use Docker secrets for sensitive data
|
|
|
|
Example production deployment:
|
|
|
|
```bash
|
|
# Build with specific version
|
|
docker build -t timesafari:1.0.0 .
|
|
|
|
# Run with production settings
|
|
docker run -d \
|
|
--name timesafari \
|
|
-p 80:80 \
|
|
--restart unless-stopped \
|
|
-e NODE_ENV=production \
|
|
timesafari:1.0.0
|
|
```
|
|
|
|
### Troubleshooting Docker
|
|
|
|
1. **Container fails to start**
|
|
- Check logs: `docker logs <container_id>`
|
|
- Verify port availability
|
|
- Check environment variables
|
|
|
|
2. **Build fails**
|
|
- Ensure all dependencies are in package.json
|
|
- Check Dockerfile syntax
|
|
- Verify build context
|
|
|
|
3. **Performance issues**
|
|
- Monitor container resources: `docker stats`
|
|
- Check nginx configuration
|
|
- Verify caching settings
|
|
|
|
## Configuration
|
|
|
|
### Deep Links
|
|
|
|
#### Android Configuration
|
|
|
|
You must add the following intent filter to the `android/app/src/main/AndroidManifest.xml` file:
|
|
|
|
```xml
|
|
<intent-filter android:autoVerify="true">
|
|
<action android:name="android.intent.action.VIEW" />
|
|
<category android:name="android.intent.category.DEFAULT" />
|
|
<category android:name="android.intent.category.BROWSABLE" />
|
|
<data android:scheme="timesafari" />
|
|
</intent-filter>
|
|
```
|
|
|
|
Note: When using `timesafari://` scheme, you may encounter build errors about missing http(s) scheme and host attributes. This is expected for custom URL schemes.
|
|
|
|
#### iOS Configuration
|
|
|
|
For iOS deep links, configure the URL scheme in Xcode:
|
|
|
|
1. Open the project in Xcode
|
|
2. Select your app target
|
|
3. Go to Info tab
|
|
4. Add URL Types with scheme `timesafari`
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
1. **Environment Variables Not Set**
|
|
- Use `--env` flag to check current environment: `./scripts/build-capacitor.sh --env`
|
|
- Verify `.env` file exists and is properly formatted
|
|
- Check script output for environment setup messages
|
|
|
|
2. **Build Failures**
|
|
- Use `--verbose` flag for detailed logging: `./scripts/build-capacitor.sh --verbose`
|
|
- Check prerequisites are installed
|
|
- Verify all dependencies are installed: `npm install`
|
|
|
|
3. **Permission Issues**
|
|
- Make scripts executable: `chmod +x scripts/*.sh`
|
|
- Check file permissions on build directories
|
|
|
|
4. **Platform-Specific Issues**
|
|
- **Android**: Verify Android Studio and SDK are properly configured
|
|
- **iOS**: Ensure Xcode and certificates are set up correctly
|
|
- **Electron**: Check Capacitor Electron platform installation
|
|
|
|
### Getting Help
|
|
|
|
- Check script help: `./scripts/build-capacitor.sh --help`
|
|
- Review script documentation in `scripts/README.md`
|
|
- Test environment setup: `./scripts/test-env.sh`
|
|
- Test common utilities: `./scripts/test-common.sh`
|
|
|
|
### Platform Support Matrix
|
|
|
|
| Platform | Mode | PWA Enabled | Native Features | Notes |
|
|
|----------|------|-------------|-----------------|-------|
|
|
| `web` | web | true | false | Standard web browser |
|
|
| `capacitor` | capacitor | false | true | Mobile app (iOS/Android) |
|
|
| `electron` | capacitor | false | true | Desktop app (via Capacitor Electron) |
|
|
|
|
## Platform Service Architecture
|
|
|
|
TimeSafari uses a unified platform service architecture that works across all platforms:
|
|
|
|
### Platform Detection
|
|
|
|
The `CapacitorPlatformService` automatically detects the platform and adjusts capabilities:
|
|
|
|
```typescript
|
|
getCapabilities(): PlatformCapabilities {
|
|
const platform = Capacitor.getPlatform();
|
|
const isElectron = platform === "electron";
|
|
|
|
return {
|
|
hasFileSystem: true,
|
|
hasCamera: true,
|
|
isMobile: !isElectron, // false for Electron, true for mobile
|
|
isIOS: platform === "ios",
|
|
hasFileDownload: isElectron, // Electron can download files directly
|
|
needsFileHandlingInstructions: !isElectron, // Mobile needs instructions
|
|
isNativeApp: true,
|
|
};
|
|
}
|
|
```
|
|
|
|
### Unified Database Layer
|
|
|
|
All platforms use the same SQLite database through Capacitor plugins:
|
|
|
|
- **Mobile**: `@capacitor-community/sqlite` plugin
|
|
- **Desktop**: Same plugin via Capacitor Electron
|
|
- **Web**: IndexedDB fallback with absurd-sql
|
|
|
|
### Feature Parity
|
|
|
|
The same Capacitor plugins work across all platforms:
|
|
|
|
- File system operations
|
|
- Camera access
|
|
- SQLite database
|
|
- Deep linking
|
|
- Sharing functionality
|