From 1441855eb141b5e5749337f28c9bca47fd86f2ad Mon Sep 17 00:00:00 2001 From: "shakespeare.diy" Date: Wed, 17 Jun 2026 16:11:28 +0700 Subject: [PATCH] feat: add Capacitor + Zapstore config for Android APK distribution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - capacitor.config.ts: Capacitor config with dark theme, WebSocket support, splash screen, status bar, keyboard plugins - zapstore.yaml: Zapstore publish config with npub identity verification - ZAPSTORE.md: full step-by-step guide (clone โ†’ build APK โ†’ publish to Zapstore) - package.json: android:init, android:sync, android:open, android:build scripts - App ID: org.skatespots.app Co-authored-by: shakespeare.diy --- ZAPSTORE.md | 349 +++++++++++++++++++++----------------------- capacitor.config.ts | 40 +++++ package.json | 6 +- zapstore.yaml | 33 +++++ 4 files changed, 246 insertions(+), 182 deletions(-) create mode 100644 capacitor.config.ts create mode 100644 zapstore.yaml diff --git a/ZAPSTORE.md b/ZAPSTORE.md index 6f17005..25caa5c 100644 --- a/ZAPSTORE.md +++ b/ZAPSTORE.md @@ -1,187 +1,174 @@ -# Publishing SkateSpots to Zapstore +# SkateSpots โ€” Zapstore Publishing Guide -Zapstore is a permissionless, Nostr-based app store for Android. However, **SkateSpots is a web app (PWA)**, not a native Android APK, so direct Zapstore publishing isn't applicable in the traditional sense. +Get SkateSpots on [Zapstore](https://zapstore.dev) as a native Android APK. -## ๐Ÿค” Current Situation +## Prerequisites (install on your machine) -**SkateSpots is:** -- โœ… A Progressive Web App (PWA) -- โœ… Installable on Android, iOS, and Desktop -- โœ… Built with React/Vite (web technologies) -- โŒ NOT a native Android APK - -**Zapstore is designed for:** -- โœ… Native Android APK files (.apk) -- โœ… Apps compiled for Android (Java/Kotlin) -- โŒ NOT for web apps or PWAs - -## ๐Ÿš€ Options to Get SkateSpots on Zapstore - -### Option 1: Create a Native Android Wrapper (Recommended for Zapstore) - -Convert the PWA to a native Android app using one of these tools: - -**A. Capacitor** (Recommended) -```bash -npm install @capacitor/core @capacitor/cli -npx cap init -npx cap add android -npm run build -npx cap copy -npx cap open android -``` - -Then build APK in Android Studio and publish to Zapstore. - -**B. PWA Builder** -- Visit https://www.pwabuilder.com/ -- Enter your deployed URL: https://skate-spots.com -- Generate Android APK -- Sign and publish to Zapstore - -**C. Trusted Web Activity (TWA)** -- Create a minimal Android app that wraps your PWA -- Requires Android development setup -- More control but more complex - -### Option 2: Promote PWA Installation (Current Best Option) - -Since SkateSpots is already a PWA, you can: - -**On Android:** -1. Open https://skate-spots.com in Chrome -2. Tap menu โ†’ "Add to Home Screen" -3. App installs like a native app -4. Works offline, has app icon, full-screen experience - -**Advantages:** -- โœ… Works NOW without any changes -- โœ… No app store approval needed -- โœ… Cross-platform (Android, iOS, Desktop) -- โœ… Instant updates (no app store review) -- โœ… Users already have it available - -**Promote this in your marketing:** -- "Install SkateSpots PWA from your browser" -- Add installation instructions to homepage -- QR code linking to https://skate-spots.com - -### Option 3: Publish as Nostr Event (Alternative Distribution) - -Even without an APK, you could publish app metadata to Nostr: - -**Use Zapstore NIPs for visibility:** -- Kind 32267: App metadata -- Include your PWA URL -- Tag as "web app" or "PWA" -- Zapstore users might discover it even if they can't install via Zapstore - -This wouldn't enable Zapstore installation but would list your app in the Nostr ecosystem. - -## ๐Ÿ“ If You Want to Proceed with Native Android (Zapstore-ready) - -### Step 1: Install Zapstore CLI -```bash -npm install -g zapstore-cli -``` - -### Step 2: Create `zapstore.yaml` in project root - -```yaml -# Repository (optional - for open source apps) -repository: nostr://npub1yrnuj56rnen08zp2h9h7p74ghgjx6ma39spmpj6w9hzxywutevsst7k5cx/git.shakespeare.diy/skate-spots - -# Name of the app -name: SkateSpots - -# App identifier (reverse domain notation) -identifier: com.skatespots.app - -# Description -summary: Decentralized skate spot discovery on Nostr - -# Icon (path to icon file) -icon: public/images/icon-512.png - -# Screenshots -images: - - public/images/screenshot-1.png - - public/images/screenshot-2.png - -# APK files to publish (regex pattern) -assets: - - output/.*.apk - -# Categories/tags -tags: - - skateboarding - - nostr - - social - - sports - - maps - -# Remote metadata sources -remote_metadata: - - github -``` - -### Step 3: Build Android APK -Using Capacitor, PWA Builder, or manual Android development. - -### Step 4: Publish to Zapstore -```bash -zapstore publish -``` - -You'll be prompted to sign with: -- Your nsec (secret key) -- Browser extension (NIP-07) -- Nostr Connect bunker - -### Step 5: Published! -- App goes live immediately (no approval process) -- Listed on relay.zapstore.dev -- Discoverable via Nostr social graph -- Users can zap you directly - -## ๐ŸŽฏ My Recommendation - -**For NOW:** -1. โœ… Keep promoting PWA installation (works great!) -2. โœ… Add PWA install instructions to homepage -3. โœ… Create installation guide in docs/FAQ - -**For FUTURE (if you want native Android):** -1. Use **Capacitor** to wrap SkateSpots as native Android app -2. Build signed APK -3. Create `zapstore.yaml` config -4. Publish to Zapstore with `zapstore publish` - -## ๐Ÿ”— Useful Links - -- **Zapstore Website**: https://zapstore.dev -- **Zapstore Docs**: https://zapstore.dev/docs -- **Zapstore CLI**: https://zapstore.dev/docs/publish -- **GitHub**: https://github.com/zapstore/zapstore -- **Download Zapstore App**: https://zapstore.dev (for Android users) - -## โ“ Questions? - -**Q: Can I publish a PWA to Zapstore?** -A: Not directly. Zapstore expects .apk files. You'd need to wrap your PWA as a native Android app first. - -**Q: Do I need to pay to publish?** -A: No! Publishing is free and permissionless. Anyone with a Nostr pubkey can publish. - -**Q: Will users pay for the app?** -A: Apps can be free or paid. Users can zap (tip) developers directly via Lightning. - -**Q: How do updates work?** -A: Run `zapstore publish` again with new APK. Users get notified of updates. - -**Q: Can iOS users use Zapstore?** -A: No, Zapstore is Android-only. iOS users can install your PWA via Safari's "Add to Home Screen". +1. **Node.js 18+** โ€” https://nodejs.org +2. **Java 17** โ€” https://adoptium.net +3. **Android Studio** โ€” https://developer.android.com/studio + - After install: open Android Studio โ†’ SDK Manager โ†’ install Android SDK 34 +4. **Go** (for `zsp` CLI) โ€” https://go.dev/dl/ --- -**Bottom line:** SkateSpots is currently a PWA, which is already installable on Android (and iOS/Desktop). To publish to Zapstore, you'd need to create a native Android APK wrapper first. The PWA approach is working great for now! ๐Ÿ›นโšก +## Step 1 โ€” Clone & install + +```bash +git clone nostr://npub1yrnuj56rnen08zp2h9h7p74ghgjx6ma39spmpj6w9hzxywutevsst7k5cx/git.shakespeare.diy/SkateSpots +cd SkateSpots +npm install +``` + +--- + +## Step 2 โ€” Install Capacitor + +```bash +npm install @capacitor/core @capacitor/cli @capacitor/android +npm install @capacitor/splash-screen @capacitor/status-bar @capacitor/keyboard +``` + +--- + +## Step 3 โ€” Initialize Android project + +```bash +npm run android:init +``` + +This: +- Builds the web app into `dist/` +- Creates the `android/` folder with a full Android project +- Copies your web build into the Android WebView + +--- + +## Step 4 โ€” Add app icons + +Copy your icon files into the Android resources: + +```bash +# The app already has icons in public/images/ +# After android:init, copy them: +cp public/images/icon-1024.png android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +``` + +Or use Android Studio: right-click `app` โ†’ New โ†’ Image Asset โ†’ import `public/images/icon-1024.png` + +--- + +## Step 5 โ€” Generate a signing keystore (one-time) + +```bash +keytool -genkey -v \ + -keystore skatespots-release.jks \ + -alias skatespots \ + -keyalg RSA \ + -keysize 2048 \ + -validity 10000 \ + -dname "CN=SkateSpots, OU=SkateSpots, O=SkateSpots, L=Bangkok, S=Bangkok, C=TH" +``` + +**Save the keystore file and passwords somewhere safe โ€” you need them for every future release.** + +--- + +## Step 6 โ€” Build the release APK + +```bash +npm run android:build +``` + +Or manually with signing: + +```bash +npm run build +npx cap sync android +cd android +./gradlew assembleRelease \ + -Pandroid.injected.signing.store.file=../skatespots-release.jks \ + -Pandroid.injected.signing.store.password=YOUR_STORE_PASSWORD \ + -Pandroid.injected.signing.key.alias=skatespots \ + -Pandroid.injected.signing.key.password=YOUR_KEY_PASSWORD +``` + +APK output: `android/app/build/outputs/apk/release/app-release.apk` + +--- + +## Step 7 โ€” Install zsp (Zapstore CLI) + +```bash +go install github.com/zapstore/zsp@latest +export PATH=$PATH:$(go env GOPATH)/bin +``` + +--- + +## Step 8 โ€” Publish to Zapstore + +```bash +SIGN_WITH=nsec1skg6sa4eqxgy6g4tauhfhf5vs4v89ja76mxgajc770jqf7jquqjq2pt8z7 zsp publish --wizard +``` + +The wizard will: +1. Ask for your APK path โ†’ point to `android/app/build/outputs/apk/release/app-release.apk` +2. Link your APK signing certificate to your Nostr identity (one-time NIP-C1 proof) +3. Publish the app event to Zapstore's relay + +**Note:** `zapstore.yaml` is already committed to the repo โ€” Zapstore will use it to verify your identity automatically. + +--- + +## Step 9 โ€” Verify on Zapstore + +Visit: https://zapstore.dev/app/org.skatespots.app + +Or search "SkateSpots" in the Zapstore app. + +--- + +## Updating the app + +For future releases: + +```bash +# 1. Build new APK +npm run android:build + +# 2. Publish new version +SIGN_WITH=nsec1... zsp publish -r git.shakespeare.diy/skatespots/SkateSpots +``` + +--- + +## Android project structure + +``` +android/ +โ”œโ”€โ”€ app/ +โ”‚ โ”œโ”€โ”€ src/main/ +โ”‚ โ”‚ โ”œโ”€โ”€ assets/public/ โ† your web app lives here (auto-synced) +โ”‚ โ”‚ โ”œโ”€โ”€ res/ โ† icons, splash screens +โ”‚ โ”‚ โ””โ”€โ”€ AndroidManifest.xml +โ”‚ โ””โ”€โ”€ build/outputs/apk/release/app-release.apk โ† built APK +โ”œโ”€โ”€ capacitor.settings.gradle +โ””โ”€โ”€ build.gradle +``` + +## Troubleshooting + +**`ANDROID_HOME` not set:** +```bash +export ANDROID_HOME=$HOME/Library/Android/sdk # macOS +export ANDROID_HOME=$HOME/Android/Sdk # Linux +export PATH=$PATH:$ANDROID_HOME/platform-tools +``` + +**Gradle build fails:** +- Make sure Java 17 is active: `java -version` +- Switch with: `export JAVA_HOME=$(/usr/libexec/java_home -v 17)` + +**WebSocket connections blocked:** +- Already handled โ€” `capacitor.config.ts` sets `allowMixedContent: true` diff --git a/capacitor.config.ts b/capacitor.config.ts new file mode 100644 index 0000000..b854eca --- /dev/null +++ b/capacitor.config.ts @@ -0,0 +1,40 @@ +import type { CapacitorConfig } from '@capacitor/cli'; + +const config: CapacitorConfig = { + appId: 'org.skatespots.app', + appName: 'SkateSpots', + webDir: 'dist', + server: { + // In production the app loads from the bundled dist/ โ€” no remote URL + // androidScheme controls the scheme used for local file loading + androidScheme: 'https', + }, + android: { + // Allow mixed content for Nostr WebSocket connections + allowMixedContent: true, + // Capture input for hardware back button + captureInput: true, + // Use the app's dark background for the splash + backgroundColor: '#000000', + }, + plugins: { + SplashScreen: { + launchShowDuration: 1500, + backgroundColor: '#000000', + androidSplashResourceName: 'splash', + androidScaleType: 'CENTER_CROP', + showSpinner: false, + }, + StatusBar: { + style: 'Dark', + backgroundColor: '#000000', + }, + Keyboard: { + resize: 'body', + style: 'dark', + resizeOnFullScreen: true, + }, + }, +}; + +export default config; diff --git a/package.json b/package.json index db63369..7dcec62 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,11 @@ "dev": "npm i --silent && vite", "build": "npm i --silent && vite build -l error && cp dist/index.html dist/404.html && echo 'Project built successfully!'", "test": "npm i --silent && tsc --noEmit && eslint && vitest run --reporter=dot --silent && vite build -l error && cp dist/index.html dist/404.html && echo 'All tests passed!'", - "deploy": "npm i --silent && npm run build && npx -y nostr-deploy-cli deploy --skip-setup" + "deploy": "npm i --silent && npm run build && npx -y nostr-deploy-cli deploy --skip-setup", + "android:init": "npm run build && npx cap add android && npx cap sync android", + "android:sync": "npm run build && npx cap sync android", + "android:open": "npx cap open android", + "android:build": "npm run build && npx cap sync android && cd android && ./gradlew assembleRelease" }, "dependencies": { "@bitcoinerlab/secp256k1": "^1.2.0", diff --git a/zapstore.yaml b/zapstore.yaml new file mode 100644 index 0000000..9c352fd --- /dev/null +++ b/zapstore.yaml @@ -0,0 +1,33 @@ +# Zapstore publish config for SkateSpots +# https://zapstore.dev/docs/publish +# +# This file must be committed to the repository root. +# Zapstore's relay fetches it to verify the publisher's Nostr identity. + +repository: https://git.shakespeare.diy/skatespots/SkateSpots +pubkey: npub1yrnuj56rnen08zp2h9h7p74ghgjx6ma39spmpj6w9hzxywutevsst7k5cx + +# App metadata +name: SkateSpots +summary: Decentralized skate map & community on Nostr +description: | + Discover skate spots worldwide, find coaches and filmers in The Lab, + join events, buy/sell gear, and connect with the skateboarding community. + Built on Nostr โ€” decentralized, censorship-resistant, no ads, no tracking. + Powered by Bitcoin Lightning zaps. + +website: https://skate-spots.org +license: custom + +# Categories (Zapstore uses F-Droid categories) +categories: + - Sports & Health + - Navigation + +# Where to find the APK once built +# Update this to your actual release APK path after building +release_source: ./android/app/build/outputs/apk/release/app-release.apk + +# Pull extra metadata from the GitHub/git repo +metadata_sources: + - github