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:
parent
a3cd6cf9f4
commit
1441855eb1
4 changed files with 246 additions and 182 deletions
349
ZAPSTORE.md
349
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:**
|
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
40
capacitor.config.ts
Normal 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;
|
||||||
|
|
@ -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
33
zapstore.yaml
Normal 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
|
||||||
Loading…
Reference in a new issue