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.
The FundraiserLayout overhaul dropped the bottom nav along with the
rest of the Twitter-style chrome. Bring it back and unhide it above
the 900px sidebar breakpoint so the Search / Communities / Feed /
Notifications / World row is available on every viewport.
- Mount <MobileBottomNav /> in FundraiserLayoutInner, outside the
flex column so its fixed positioning behaves normally.
- Drop the 'sidebar:hidden' class on the nav element.
- Pad the layout root by --bottom-nav-height + safe-area-inset-bottom
so the SiteFooter still clears the fixed bar.
Regression-of: 704cb42e
The layout outlet had no max-width, so pages without their own `max-w-*`
wrapper (e.g. /help, the home feed) stretched edge-to-edge on widescreen
monitors. Add a default `max-w-3xl` cap on the center column and wire
the existing `noMaxWidth` and `wrapperClassName` LayoutOptions through,
so pages that need wider canvases keep working — CampaignsPage,
CampaignDetailPage, CreateCampaignPage, EventDashboardPage, and WorldPage
already opt out via `noMaxWidth: true` or the `fullBleed` preset.
The previous overhaul left the campaigns content nested inside the
Twitter-style three-column MainLayout (LeftSidebar + 600-px center
column + WidgetSidebar + mobile FAB + mobile bottom nav). It looked
like a Nostr client that happened to render campaign cards instead of
a fundraising site.
This commit takes the chrome down to studs:
- New FundraiserLayout: a sticky GoFundMe-style TopNav with logo,
Discover / Start a campaign / About links, the existing LoginArea
on the right (so the avatar dropdown / Log in & Sign up buttons all
keep working unchanged), and a primary "Start a campaign" pill.
Mobile collapses to a hamburger drawer with the same items plus
quick shortcuts to Wallet / Bitcoin / Notifications / Profile /
Settings for logged-in users.
- One full-width content area below the nav and a slim site footer.
No LeftSidebar, no WidgetSidebar, no FAB, no MobileTopBar/BottomNav.
- The old layout still provides LayoutStoreContext / DrawerContext /
CenterColumnContext / NavHiddenContext so every page that calls
useLayoutOptions(...) keeps mounting cleanly. FAB / sidebar /
scroll-direction options are simply ignored.
Routing changes:
- / now renders CampaignsPage directly (instead of dispatching
through a configurable HomePage). /campaigns redirects to /.
- The orphaned HomePage.tsx is removed.
Campaign pages were calibrated for the old 600-px center column.
Re-flowed them to take advantage of the full canvas:
- Hero copy is recentred under max-w-7xl with GoFundMe-style language
("Where successful fundraisers start.").
- Campaign grid grows to four columns on xl screens.
- CampaignDetailPage drops its local sticky sub-header (redundant
under the global TopNav) and the donation rail re-anchors to the
new nav height.
- CreateCampaignPage drops its sticky sub-header and reads as a
proper landing form.
The legacy MainLayout / LeftSidebar / WidgetSidebar / MobileTopBar /
MobileBottomNav / MobileDrawer / FloatingComposeButton components
remain on disk but are no longer mounted; they tree-shake out of the
production bundle.
Pivot the homepage from a Twitter-style social feed to a GoFundMe-style
fundraising hub. Introduces a new addressable kind 30223 "Campaign" that
carries the marketing-style metadata (title, summary, cover image, story,
category, goal, deadline, location) plus a list of recipient pubkeys with
optional split weights. Documented in NIP.md alongside the kind 8333
onchain-zap spec it builds on.
Donations are sent as a single multi-output Bitcoin transaction (one
output per recipient, derived Taproot addresses) using the existing
buildUnsignedMultiOutputPsbt + useBitcoinSigner infrastructure that
backs community on-chain zaps. After broadcast, the client publishes
one kind 8333 receipt per recipient with the campaign's `a` coordinate
so the donation aggregates into the campaign's totals.
UI surfaces:
- /campaigns is now the default homePage. Hero, two featured slots
(placeholders in src/lib/featuredCampaigns.ts), then a grid of
user-submitted campaigns.
- /campaigns/new is a full create form with cover upload, slug
collision check, recipient builder with per-row weights, and
preset/custom donation-amount UX.
- naddr1 identifiers for kind 30223 route to CampaignDetailPage via
NIP19Page (full story rendered through the existing ArticleContent
markdown component, plus a sticky donate rail with progress).
- DonateDialog presets are tuned for on-chain amounts (10K-1M sats)
with a dust-aware minimum guard derived from the split math.
- Fundraisers sidebar item with a HandHeart icon.
Kept the existing social-feed pages addressable from the sidebar; the
overhaul is scoped to the home/landing experience rather than removing
the underlying Nostr features.
- Re-add the Messages item to the left sidebar with its previous
MessageSquareMore lucide icon. Drop requiresAuth so logged-out users
also see the entry — the page is a static recommendation.
- Restore 'messages' to the default sidebarOrder in App.tsx.
- Add WhiteNoiseIcon (the logomark from whitenoise.chat, recolored to
currentColor so it adapts to theme) and use it on the /messages
install-CTA card in place of the generic Lock glyph.
The @samthomson/nostr-messaging library opens fresh NRelay1 sockets per
participant per relay outside the shared NPool, fanning out to every
conversation partner's NIP-65 + NIP-17 inbox relays plus all
discoveryRelays in hybrid mode. In practice this drives connection counts
to several hundred relays per session.
Rather than band-aid the fan-out, drop the feature entirely and point
users to White Noise for end-to-end encrypted Nostr chat.
- Replace /messages with a 'Install White Noise' CTA card (route kept)
- Delete MessagingSettingsPage, DMProviderWrapper, messaging-intro.png
- Remove DMProvider wrapper and PROTOCOL_MODE config from App.tsx
- Drop messaging config from AppConfig, AppConfigSchema,
EncryptedSettingsSchema, EncryptedSettings, and the NostrSync /
useInitialSync sync paths
- Remove messages sidebar entry, default sidebarOrder slot, and
SettingsPage messaging card
- Uninstall @samthomson/nostr-messaging and drop its tailwind content
glob and vitest deps.inline entry
- Update copy in PrivacyPolicy, AdvancedSettings delete-account warning,
ProfileSettings nsec warning, RequestToVanishDialog deletion checklist,
MainLayout comment, and NIP.md
- Leave kind 4 rendering (EncryptedMessageContent) intact so DM events
authored elsewhere still display in feeds and quote embeds
Regression-of: 5b8d2d5c
The previous SW eviction commit wiped caches and called clients.claim()
on activate, but that only changes which SW handles future fetches — it
does not re-render a tab that already finished loading the stale bundle.
In practice, returning users had to manually close and reopen the tab
before seeing the new build.
Fix: after clients.claim(), iterate self.clients.matchAll({ type: 'window' })
and call client.navigate(client.url) on each one. Since this SW has no
fetch handler, the navigation falls through to the network and the tab
re-renders against the fresh index.html + hashed bundle.
Caveats:
- Users mid-interaction (typing a post, scrolling) lose their unsaved
state. Acceptable trade — the alternative is they stay on a broken
cached bundle indefinitely.
- Fires exactly once per user (only on the install -> activate transition
for a byte-different /sw.js). No reload loop.
Also corrected the misleading comment on the main.tsx registration: that
registration is forward-looking insurance for future cache busts, not the
mechanism that evicts the old SW. The browser's own SW update check is
what re-fetches /sw.js out-of-band; our in-page JS never runs on a tab
the old precache SW is controlling.
A previous version of Agora deployed at agora.spot shipped a precaching
service worker that is still controlling returning browsers and serving
them stale HTML/JS — they never see new deploys.
The fix has three parts:
1. public/sw.js — on activate, delete every Cache Storage entry the old
SW left behind. This SW has no fetch handler, so once it takes over
nothing re-populates the cache.
2. src/main.tsx — register /sw.js unconditionally on every web page load.
Previously only usePushNotifications registered it, which meant users
who never visited NotificationSettings stayed pinned to the old SW
forever. Native (Capacitor) skips this — there is no stale SW on the
filesystem origin.
3. .gitlab-ci.yml — the deploy-web rsync was excluding sw.js from the
first pass and never re-adding it to the second pass, so deploys
silently never updated sw.js. Now it ships in the second pass
alongside index.html (after hashed assets land).