feat: add Capacitor + Zapstore config for Android APK distribution

- 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 <assistant@shakespeare.diy>
This commit is contained in:
shakespeare.diy 2026-06-17 16:11:28 +07:00
parent a3cd6cf9f4
commit 1441855eb1
4 changed files with 246 additions and 182 deletions

View file

@ -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:** 1. **Node.js 18+** — https://nodejs.org
- ✅ A Progressive Web App (PWA) 2. **Java 17** — https://adoptium.net
- ✅ Installable on Android, iOS, and Desktop 3. **Android Studio** — https://developer.android.com/studio
- ✅ Built with React/Vite (web technologies) - After install: open Android Studio → SDK Manager → install Android SDK 34
- ❌ NOT a native Android APK 4. **Go** (for `zsp` CLI) — https://go.dev/dl/
**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".
--- ---
**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`

40
capacitor.config.ts Normal file
View file

@ -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;

View file

@ -8,7 +8,11 @@
"dev": "npm i --silent && vite", "dev": "npm i --silent && vite",
"build": "npm i --silent && vite build -l error && cp dist/index.html dist/404.html && echo 'Project built successfully!'", "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!'", "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": { "dependencies": {
"@bitcoinerlab/secp256k1": "^1.2.0", "@bitcoinerlab/secp256k1": "^1.2.0",

33
zapstore.yaml Normal file
View file

@ -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