# Building TimeSafari

This guide explains how to build TimeSafari for different platforms.

## Prerequisites

For a quick dev environment setup, use [pkgx](https://pkgx.dev).

- Node.js (LTS version recommended)
- npm (comes with Node.js)
- Git
- For Android builds: Android Studio with SDK installed
- For iOS builds: macOS with Xcode and ruby gems & bundle
  - pkgx +rubygems.org sh

  - ... and you may have to fix these, especially with pkgx

   ```bash
   gem_path=$(which gem)
   shortened_path="${gem_path:h:h}"
   export GEM_HOME=$shortened_path
   export GEM_PATH=$shortened_path
   ```

- For desktop builds: Additional build tools based on your OS

## 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
   ```

## Web Dev Locally

   ```bash
   npm run dev
   ```

## Web Build for Server

1. Run the production build:

   ```bash
   npm run build
   ```

   The built files will be in the `dist` directory.

2. To test the production build locally:

   You'll likely want to use test locations for the Endorser & image & partner servers; see "DEFAULT_ENDORSER_API_SERVER" & "DEFAULT_IMAGE_API_SERVER" & "DEFAULT_PARTNER_API_SERVER" below.

   ```bash
   npm run serve
   ```

### Compile and minify for test & production

* If there are DB changes: before updating the test server, open browser(s) with current version to test DB migrations.

* `npx prettier --write ./sw_scripts/`

* Update the ClickUp tasks & CHANGELOG.md & the version in package.json, run `npm install`.

* Commit everything (since the commit hash is used the app).

* Put the commit hash in the changelog (which will help you remember to bump the version later).

* Tag with the new version, [online](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/releases) or `git tag 0.3.55 && git push origin 0.3.55`.

* For test, build the app (because test server is not yet set up to build):

```bash
TIME_SAFARI_APP_TITLE="TimeSafari_Test" VITE_APP_SERVER=https://test.timesafari.app VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app VITE_DEFAULT_PARTNER_API_SERVER=https://test-partner-api.endorser.ch VITE_PASSKEYS_ENABLED=true npm run build
```

   ... and transfer to the test server:

   ```bash
   rsync -azvu -e "ssh -i ~/.ssh/..." dist ubuntutest@test.timesafari.app:time-safari
   ```

(Let's replace that with a .env.development or .env.staging file.)

(Note: The test BVC_MEETUPS_PROJECT_CLAIM_ID does not resolve as a URL because it's only in the test DB and the prod redirect won't redirect there.)

* For prod, get on the server and run the correct build:

  ... and log onto the server:

  * `pkgx +npm sh`

  * `cd crowd-funder-for-time-pwa && git checkout master && git pull && git checkout 0.3.55 && npm install && npm run build && cd -`

  (The plain `npm run build` uses the .env.production file.)

* Back up the time-safari/dist folder & deploy: `mv time-safari/dist time-safari-dist-prev.0 && mv crowd-funder-for-time-pwa/dist time-safari/`

* Record the new hash in the changelog. Edit package.json to increment version & add "-beta", `npm install`, and commit. Also record what version is on production.




## Desktop Build (Electron)

### Linux Build

1. Build the electron app in production mode:

   ```bash
   npm run build:electron-prod
   ```

2. Package the Electron app for Linux:

   ```bash
   # For AppImage (recommended)
   npm run electron:build-linux

   # For .deb package
   npm run electron:build-linux-deb
   ```

3. The packaged applications will be in `dist-electron-packages/`:
   - AppImage: `dist-electron-packages/TimeSafari-x.x.x.AppImage`
   - DEB: `dist-electron-packages/timesafari_x.x.x_amd64.deb`

### Running the Packaged App

- AppImage: Make executable and run

  ```bash
  chmod +x dist-electron-packages/TimeSafari-*.AppImage
  ./dist-electron-packages/TimeSafari-*.AppImage
  ```

- DEB: Install and run

  ```bash
  sudo dpkg -i dist-electron-packages/timesafari_*_amd64.deb
  timesafari
  ```

### Development Testing

For testing the Electron build before packaging:

```bash
# Build and run in development mode (includes DevTools)
npm run electron:dev

# Build in production mode and test
npm run build:electron-prod && npm run electron:start
```

## Mobile Builds (Capacitor)

### iOS Build

Prerequisites: macOS with Xcode installed

1. Build the web assets:

   ```bash
   npm run build:capacitor
   ```

2. Update iOS project with latest build:

   ```bash
   npx cap sync ios
   ```

3. Copy the assets:

   ```bash
   mkdir -p ios/App/App/Assets.xcassets/AppIcon.appiconset
   npx capacitor-assets generate --ios
   ```

3. Open the project in Xcode:

   ```bash
   npx cap open ios
   ```

4. Use Xcode to build and run on simulator or device.

#### First-time iOS Configuration

- Generate certificates inside XCode.

- Right-click on App and under Signing & Capabilities set the Team.

### Android Build

Prerequisites: Android Studio with SDK installed

1. Build the web assets:

   ```bash
   rm -rf dist
   npm run build:web
   npm run build:capacitor
   ```

2. Update Android project with latest build:

   ```bash
   npx cap sync android
   ```

3. Copy the assets

   ```bash
   npx capacitor-assets generate --android
   ```

4. Open the project in Android Studio:

   ```bash
   npx cap open android
   ```

5. Use Android Studio to build and run on emulator or device.

## Android Build from the console

   ```bash
   cd android
   ./gradlew clean
   ./gradlew build -Dlint.baselines.continue=true
   cd ..
   npx cap run android
   ```

... or, to create the `aab` file, `bundle` instead of `build`:

   ```bash
   ./gradlew bundleDebug -Dlint.baselines.continue=true
   ```

... or, to create a signed release, add the app/gradle.properties.secrets file (see properties at top of app/build.gradle) and the app/time-safari-upload-key-pkcs12.jks file, then `bundleRelease`:

   ```bash
   ./gradlew bundleRelease -Dlint.baselines.continue=true
   ```



## First-time Android Configuration for deep links

You must add the following intent filter to the `android/app/src/main/AndroidManifest.xml` file:

   ```xml
               <intent-filter android:autoVerify="true">
                  <action android:name="android.intent.action.VIEW" />
                  <category android:name="android.intent.category.DEFAULT" />
                  <category android:name="android.intent.category.BROWSABLE" />
                  <data android:scheme="timesafari" />
               </intent-filter>
   ```

You must also add the following to the `android/app/build.gradle` file:

   ```gradle
   android {
      // ... existing config ...

      lintOptions {
         disable 'UnsanitizedFilenameFromContentProvider'
         abortOnError false
         baseline file("lint-baseline.xml")
         
         // Ignore Capacitor module issues
         ignore 'DefaultLocale'
         ignore 'UnsanitizedFilenameFromContentProvider'
         ignore 'LintBaseline'
         ignore 'LintBaselineFixed'
      }
   }
   ```

Modify `/android/build.gradle` to use a stable version of AGP and make sure Kotlin version is compatible.

   ```gradle
   buildscript {
      repositories {
         google()
         mavenCentral()
      }
      dependencies {
         // Use a stable version of AGP
         classpath 'com.android.tools.build:gradle:8.1.0'
         
         // Make sure Kotlin version is compatible
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0"
      }
   }

   allprojects {
      repositories {
         google()
         mavenCentral()
      }
   }

   // Add this to handle version conflicts
   configurations.all {
      resolutionStrategy {
         force 'org.jetbrains.kotlin:kotlin-stdlib:1.8.0'
         force 'org.jetbrains.kotlin:kotlin-stdlib-common:1.8.0'
      }
   }
   ```

## PyWebView Desktop Build

### Prerequisites for PyWebView

- Python 3.8 or higher
- pip (Python package manager)
- virtualenv (recommended)
- System dependencies:

  ```bash
  # For Ubuntu/Debian
  sudo apt-get install python3-webview
  # or
  sudo apt-get install python3-gi python3-gi-cairo gir1.2-gtk-3.0 gir1.2-webkit2-4.0

  # For Arch Linux
  sudo pacman -S webkit2gtk python-gobject python-cairo

  # For Fedora
  sudo dnf install python3-webview
  # or
  sudo dnf install python3-gobject python3-cairo webkit2gtk3
  ```

### Setup

1. Create and activate a virtual environment (recommended):

   ```bash
   python -m venv .venv
   source .venv/bin/activate  # On Linux/macOS
   # or
   .venv\Scripts\activate     # On Windows
   ```

2. Install Python dependencies:

   ```bash
   pip install -r requirements.txt
   ```

### Troubleshooting

If encountering PyInstaller version errors:

```bash
# Try installing the latest stable version
pip install --upgrade pyinstaller
```

### Development of PyWebView

1. Start the PyWebView development build:

   ```bash
   npm run pywebview:dev
   ```

### Building for Distribution

#### Linux

```bash
npm run pywebview:package-linux
```

The packaged application will be in `dist/TimeSafari`

#### Windows

```bash
npm run pywebview:package-win
```

The packaged application will be in `dist/TimeSafari`

#### macOS

```bash
npm run pywebview:package-mac
```

The packaged application will be in `dist/TimeSafari`

## Testing

Run all tests (requires XCode and Android Studio/device):

```bash
npm run test:all
```

See [TESTING.md](test-playwright/TESTING.md) for more details.

## Linting

Check code style:

```bash
npm run lint
```

Fix code style issues:

```bash
npm run lint-fix
```

## Environment Configuration

See `.env.*` files for configuration.

## Notes

- The application uses PWA (Progressive Web App) features for web builds
- Electron builds disable PWA features automatically
- Build output directories:
  - Web: `dist/`
  - Electron: `dist-electron/`
  - Capacitor: `dist-capacitor/`

## Deployment

### Version Management

1. Update CHANGELOG.md with new changes
2. Update version in package.json
3. Commit changes and tag release:

   ```bash
   git tag <VERSION_TAG>
   git push origin <VERSION_TAG>
   ```

4. After deployment, update package.json with next version + "-beta"

### Test Server

```bash
# Build using staging environment
npm run build -- --mode staging

# Deploy to test server
rsync -azvu -e "ssh -i ~/.ssh/<YOUR_KEY>" dist ubuntutest@test.timesafari.app:time-safari/
```

### Production Server

```bash
# On the production server:
pkgx +npm sh
cd crowd-funder-for-time-pwa
git checkout master && git pull
git checkout <VERSION_TAG>
npm install
npm run build
cd -

# Backup and deploy
mv time-safari/dist time-safari-dist-prev.0 && mv crowd-funder-for-time-pwa/dist time-safari/
```

## Troubleshooting Builds

### Common Build Issues

1. **Missing Environment Variables**
   - Check that all required variables are set in your .env file
   - For development, ensure local services are running on correct ports

2. **Electron Build Failures**
   - Verify Node.js version compatibility
   - Check that all required dependencies are installed
   - Ensure proper paths in electron/main.js

3. **Mobile Build Issues**
   - For iOS: Xcode command line tools must be installed
   - For Android: Correct SDK version must be installed
   - Check Capacitor configuration in capacitor.config.ts


# List all installed packages
adb shell pm list packages | grep timesafari

# Force stop the app (if it's running)
adb shell am force-stop app.timesafari

# Clear app data (if you don't want to fully uninstall)
adb shell pm clear app.timesafari

# Uninstall for all users
adb shell pm uninstall -k --user 0 app.timesafari

# Check if app is installed
adb shell pm path app.timesafari