Rounds out the phoenix rebrand for assets the earlier logo.svg swap
missed:
- public/icon-192.png / icon-512.png (PWA install icons, referenced by
manifest.webmanifest) still had the old bolt-on-circle mark.
- Android's push-notification status-bar icon (ic_stat_ditto, all
densities) was carrying an even older mark — a ringed-planet icon
from 'Ditto', the project two forks back. Regenerated as a white
phoenix silhouette (status-bar icons must be flat white on
transparent; the OS tints them).
- android/.../splash_icon_vector.xml, the actual live Android 12+
splash icon (wired via styles.xml's windowSplashScreenAnimatedIcon),
had the old double-bolt path hardcoded. Replaced with the phoenix
path and recomputed the scale/center math for its 1446x1246 viewBox.
- ios/.../Splash.imageset (wired into LaunchScreen.storyboard) had an
even older blue crossed-arrow mark predating Agora. Regenerated as
the brand-orange phoenix on white, matching the existing small
centered-mark proportions.
Also deleted the legacy per-density drawable*/splash.png and
drawable/splash_icon.png rasters: confirmed unreferenced by any theme,
manifest, or code (the app fully migrated to the Android 12
SplashScreen API / splash_icon_vector.xml), so they were dead weight
carrying outdated branding no user could ever see.
generate-icons.sh now produces all of the above so future logo changes
regenerate everything in one pass instead of missing assets again.
Note: unlike the web assets, the Android/iOS changes here only take
effect on the next native app build + store release — there's no web
deploy step for them.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the diamond-bolt mark with the phoenix (brand yellow #fcd414).
Regenerate favicons, web logo.png, and Android/iOS launcher icons from
the new public/logo.svg; update the generator scripts for the new
viewBox (1446x1246) and fill color.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rebrand Agora to Eranos and strip the non-Grin rails. Add Grin donations:
a GoblinPay client + GrinPayDialog, on-chain payment-proof verification
(receiver-sig + kernel-on-chain + dedupe), and a proof-verified campaign
tally (kind 3414). Shift the brand from orange to gold. 118 tests green.
The 720x880 logo.svg was force-rendered into a 512x512 square, stretching
the bolt horizontally. Render height-bounded (aspect-preserving) instead
and let the centered composite handle fit. Regenerate all Android, iOS,
and web/PWA/Zapstore icons.
The Android launcher icons and adaptive foreground were the old icon
(orange-circle single bolt / stale Ditto vector), and generate-icons.sh
still sourced the purple Ditto branding. Rewrite the generator to use
the current logo.svg double-bolt glyph in white on the brand orange
(#e9673f) circle, and regenerate:
- Android legacy + adaptive launcher icons (all densities)
- adaptive icon background color -> #e9673f
- iOS AppIcon
- web/PWA/Zapstore icons (logo.png, icon-192, icon-512, apple-touch-icon)
Also remove the unused stale adaptive foreground vector so it can't
shadow the regenerated foreground PNG.
The recipient input on /wallet's Send dialog now has a camera button
that opens a QR scanner. Bitcoin BIP-21 URIs are parsed and the
silent-payment fallback (?sp=) is preferred when present, falling
back to the on-chain address otherwise. Plain addresses, sp1… codes,
npub, and nprofile values are dropped into the input verbatim and
resolved by the existing recipient logic.
QrScannerDialog is a standalone component (ported from Ditto) that
owns the camera lifecycle via getUserMedia and the qr-scanner npm
package. It surfaces failure modes (insecure context, denied
permission, no camera, busy camera, overconstrained, ready timeout)
instead of a silent black screen, and offers a flash toggle when the
device supports it.
Android needed an explicit CAMERA permission in the manifest; iOS's
existing NSCameraUsageDescription string was extended to mention QR
scanning. No Capacitor camera plugin is required — the standard web
APIs work inside WKWebView and Android's WebView.
Renames the Capacitor app identifier from pub.agora.app to
spot.agora.app and cleans up Ditto-branded artifacts that don't refer
to upstream Ditto-the-project or Ditto-stack services.
App identifier (pub.agora.app -> spot.agora.app):
- capacitor.config.ts appId
- android applicationId, namespace, package_name string, custom_url_scheme
- iOS PRODUCT_BUNDLE_IDENTIFIER (Debug + Release)
- public/.well-known/assetlinks.json package_name
- public/.well-known/apple-app-site-association app id
- Info.plist BGTaskSchedulerPermittedIdentifiers and the matching
Swift bgTaskIdentifier (previously mismatched: plist said
pub.agora.app.notification-refresh, Swift said
pub.ditto.app.notification-refresh, so background refresh would
silently fail to register)
- src/lib/helpContent.ts Zapstore URLs
- .gitlab-ci.yml --package_name for fastlane supply
Android Java package (pub.ditto.app -> spot.agora.app):
- Move android/app/src/main/java/pub/ditto/app/ ->
android/app/src/main/java/spot/agora/app/ (4 files: MainActivity,
DittoNotificationPlugin, NostrPoller, NotificationRelayService)
- Update package declarations to match the new Android namespace
(was a hard build failure with namespace = spot.agora.app)
- Update proguard -keep rule
- Update NotificationRelayService ACTION_FETCH intent string
pub.ditto.app.ACTION_FETCH -> spot.agora.app.ACTION_FETCH
Fastlane (pub.ditto.app -> spot.agora.app):
- Appfile, Matchfile, Fastfile provisioning profile specifiers.
Matchfile still points at Soapbox's certificates git repo; a new
match repo with certs for spot.agora.app is required before iOS CI
signing works.
IPA artifact name (Ditto.ipa -> Agora.ipa):
- Fastfile output_name and matching CI artifact paths
- .gitlab-ci.yml: artifacts/Ditto.ipa references and the GitLab
Generic Packages path from /packages/generic/ditto/ ->
/packages/generic/agora/ (matches how APK/AAB are already
published). Existing release artifacts at the old path remain
reachable; new releases land at the new path.
Release-notes script fallback (Ditto vX.Y.Z -> Agora vX.Y.Z):
- scripts/extract-release-notes.mjs fallback used as the App Store /
Play Store 'What's New' blurb when a changelog section has no
summary.
manifest.webmanifest:
- Update related_applications Play Store entry to spot.agora.app.
- Remove the iTunes related_applications entry that pointed at
the existing Ditto App Store listing; not applicable to Agora
until Agora has its own listing.
Capacitor sync incidentals:
- npm run cap:sync picked up @capacitor/barcode-scanner registration
that had been missed in a prior plugin install
(android/app/capacitor.build.gradle, capacitor.settings.gradle,
ios/App/CapApp-SPM/Package.swift).
Intentionally NOT touched:
- ditto.json filename, DittoConfigSchema, DittoConfig, and JSDoc
references to ditto.json. The config-system shape is shared with
upstream Ditto by design.
- relay.ditto.pub, blossom.ditto.pub, ditto.pub/api/* and other
Ditto-stack services Agora actively consumes.
- The DittoNotificationPlugin Android/iOS class name, the
DittoNotification JS bridge name, ditto_notification_config
SharedPreferences keys, ic_stat_ditto drawables, and the
DittoBridgeViewController. Renaming requires a coordinated
JS-side rename plus a SharedPreferences migration or existing
users on the Ditto fork lose their notification config on upgrade.
- Ditto references in skill docs, NIP.md kind comments, README, and
zapstore.yaml attribution \u2014 those correctly describe the upstream
Ditto project that Agora forked from.
Follow-ups required before CI succeeds end-to-end (out of scope here):
- Stand up a new fastlane match git repo containing certs +
provisioning profiles for spot.agora.app, or update Matchfile
git_url to point at it.
- Register spot.agora.app in App Store Connect for team GZLTTH5DLM
and create a new App Store listing.
- Create a new Google Play Console listing for spot.agora.app
(package name is immutable per app on Play; the existing
pub.agora.app listing cannot be reused).
- Re-publish to Zapstore under spot.agora.app so the URLs in
helpContent.ts resolve.
Mirror the existing Android publishing flow for iOS. The pipeline
gains two jobs: build-ipa runs on a self-hosted Mac runner and
produces a signed App Store IPA; publish-app-store runs on a shared
Linux runner and submits the prebuilt IPA to App Store Connect.
Build pipeline (.gitlab-ci.yml):
- build-ipa (Mac, stage build, parallel with build-apk): decodes the
ASC API key, runs match (with api_key, so cert validity is verified
against Apple before xcodebuild starts), builds web assets, syncs
Capacitor, stamps MARKETING_VERSION. Uploads Ditto-${CI_COMMIT_TAG}
.ipa to GitLab's Generic Packages registry.
- publish-app-store (Linux ruby:3.3, needs: [build-ipa]): gem
install fastlane, decode the ASC API key, extract the changelog
section into release_notes.txt, fastlane submit_release with
IPA_PATH pointing at the inherited artifact. No Xcode, no signing,
no keychain \u2014 pure Apple API call.
- release job now needs both build-apk and build-ipa, and links three
assets (APK / AAB / IPA).
fastlane (ios/fastlane/Fastfile, Matchfile, Appfile, metadata/):
- Four lanes: build_ipa (CI build), submit_release (CI publish, reads
IPA_PATH from env), release (single-step convenience for local
dev), submit_only (debug lane to re-submit an already-uploaded
build).
- Match config points at the private gitlab.com/soapbox-pub
/certificates repo. App Store Connect API key is built inline in
the Fastfile to avoid a collision with match's APP_STORE_CONNECT
_API_KEY_PATH env var (match wants a JSON descriptor, the action
writes a raw .p8). CI overrides CODE_SIGN_STYLE=Manual via xcargs
so the Xcode project can stay on Automatic for local development.
Vite config (vite.config.ts):
- Renames the build-time config override env var from CONFIG_FILE to
DITTO_CONFIG_FILE. GitLab Runner sets CONFIG_FILE to its own TOML
config in job env, which broke vite's loader.
App-side changes:
- ios/App/App.xcodeproj/project.pbxproj: team GZLTTH5DLM stamped in;
MARKETING_VERSION gets stamped from the tag at build time.
- public/CHANGELOG.md, package.json: v2.14.3.
Skills + AGENTS.md updated to reflect the six-job pipeline (test /
deploy unchanged, build now has two jobs, release / publish updated)
and to document Mac-runner operations, fastlane match cert rotation,
and local debugging workflows.
When a logged-in user opens an nsite preview, a window.nostr provider is
injected into the sandboxed iframe. The provider proxies signEvent, nip04,
and nip44 calls to the parent signer over the existing JSON-RPC bridge.
A permission system gates each operation:
- getPublicKey is auto-allowed (clicking Run implies consent)
- signEvent prompts are granular per event kind (like Amber)
- encrypt/decrypt prompts are per operation type
- Users can check 'Remember for this site' to persist decisions
- Permissions are scoped to (userPubkey, siteId) in localStorage
The nsite preview nav bar gains a shield icon that opens a popover for
managing stored permissions.
Kind labels for the signer nudge, the permission prompt, and the post-
detail loading title now route through a central KIND_LABELS registry
(src/lib/kindLabels.ts) instead of three divergent inline maps.
The native SandboxPlugin (iOS WKWebView / Android WebView overlay) is
removed; SandboxFrame now always uses iframe.diy, so native behavior
matches web. This drops ~1100 lines of native code, the Android-only
blob prefetch workaround in NsitePreviewDialog, and the createPluginCall
registration in MainActivity and capacitor.config.json.
Install @capacitor/haptics and add a centralized haptics utility
(src/lib/haptics.ts) that uses the native taptic engine on iOS/Android
and falls back to navigator.vibrate() on web.
Haptics added to:
- Switch component (covers 36+ toggle switches app-wide)
- PullToRefresh threshold (covers 15+ pages)
- MobileBottomNav tab taps
- ReactionButton (like/unlike, double-click heart)
- RepostMenu (repost/undo repost)
- ZapDialog button press + payment success (NWC and WebLN)
- FollowButton and ProfilePage follow toggle
- ComposeBox (post, voice message, and poll publish success)
- NoteMoreMenu (bookmark, pin, mute)
- VinesFeedPage reaction and repost buttons
- ProfileReactionButton and ExternalReactionButton
- NoteCard share button
- BlobbiRoomShell swipe navigation
Replaces raw navigator.vibrate() calls in GameControls and
SendAnimation with the new cross-platform haptics utility, fixing
haptic feedback on iOS where the Vibration API is not available.