225 Commits

Author SHA1 Message Date
Goblin f131198feb Eranos: Grin-only fundraising (rebrand + Grin payments + gold)
Deploy to GitHub Pages / deploy (push) Has been cancelled
Test / test (push) Has been cancelled
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.
2026-07-02 08:12:51 -04:00
Chad Curtis 6e03a4abb9 Showcase all Venezuela relief campaigns instead of one
Replace the single baked-in terremoto-venezuela campaign with a live,
agnostic showcase: every Venezuela-located campaign tagged
`humanitarian-aid` or `emergency-relief` and created since the
earthquake is pulled in and rendered as a card carousel across the home
hero and the dedicated relief page.

- New useVenezuelaReliefCampaigns hook queries kind 33863 filtered by
  `#i: [iso3166:VE]` + `#t: [humanitarian-aid, emergency-relief]` +
  `since` (quake timestamp), and aggregates on-chain raised totals the
  same way useProfileCampaignStats does (one /address lookup per
  campaign, summed; no per-receipt /tx fan-out). The flagship
  terremoto-venezuela campaign predates the geo-tag convention and
  carries no `iso3166:VE` tag, so it's pinned by coordinate and merged
  ahead of the filtered results, deduped by aTag.
- Extend useCampaigns with `categories` (`#t`) and `since` options.
- New VenezuelaReliefShowcase: an auto-panning marquee of CampaignCards
  (seamless -50% loop, hover/focus pause, click-drag + momentum, edge
  fades, prefers-reduced-motion fallback to a native scroll rail),
  ported from the surveil deck shelf.
- Hero/page CTAs and the popup donate button now point at the relief
  page (no more single-campaign naddr deep-link); the page CTA scrolls
  to the showcase. Drop the embedded CampaignDetailPage, the
  .relief-donate-flash CSS, and the now-unused scroll-to-campaign logic.
- VenezuelaReliefGoal now reads the aggregate hook and shows the
  combined raised total + matching-campaign count only (no goal bar —
  an aggregate goal across independent campaigns is meaningless).
- Tighten the hero height so the carousel sits near the fold.
- Locale updates across all 16 languages: drop `startCampaign`/`goalOf`,
  add `showcaseTitle` + `campaignCount`.
2026-06-27 12:40:18 -05:00
Alex Gleason e9eebaeeca Fix npm audit vulnerabilities
- Patch react-router open redirect (GHSA-2j2x-hqr9-3h42) within 6.x
- Bump vitest to 4.x to fix UI server file read/exec (GHSA-5xrq-8626-4rwp)

npm audit now reports 0 vulnerabilities; full test suite passes.
2026-06-05 10:43:42 -05:00
Chad Curtis 7a18d500ee release: v2.8.9 2026-06-02 09:15:30 -05:00
lemon 702d374a06 Sync lockfile version 2026-06-02 04:10:04 -07:00
Chad Curtis 49049f98e7 Fix squashed app icon: render logo SVG with preserved aspect ratio
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.
2026-06-02 04:53:57 -05:00
Chad Curtis a5cc9c5163 Break walletHeroNote onto two lines 2026-06-02 04:24:57 -05:00
Chad Curtis 5d4d0825c6 Remove "no payout setup" from wallet hero note copy 2026-06-02 04:10:33 -05:00
Alex Gleason dfb0a52603 Upgrade Nostrify 2026-06-01 22:20:43 +02:00
Alex Gleason f5cdbb6f3a Upgrade Nostrify (this fixes everything) 2026-05-31 02:52:50 +02:00
Alex Gleason dc43f723fb Trim the eager countries chunk from 244 KB to 47 KB
src/lib/countries.ts imported the full iso-3166 package solely to build
a Set of valid ISO 3166-2 subdivision codes for validation. That dataset
(~5000 objects with names, parents, and tree structure) landed in the
eagerly-preloaded countries chunk because NoteContent, ComposeBox, and
campaign.ts all import from countries.ts on the critical path.

Ship only the subdivision code strings instead, generated at build time
into src/lib/subdivisionCodes.ts via scripts/gen-subdivision-codes.mjs.
iso-3166 moves to devDependencies since only the generator script needs
it now. The strict-validation contract (rejecting US-ZZ etc.) is
preserved.
2026-05-30 02:20:01 +02:00
Alex Gleason bae49e6123 Add QR scanner to Send dialog
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.
2026-05-28 06:02:58 -05:00
Alex Gleason ef49ad8862 npm audit fix 2026-05-24 16:40:44 -05:00
mkfain 97da8ae822 Add Han display font for Chinese hero text
Bebas Neue (the .font-display family) ships only Latin glyphs, so
Chinese hero headlines fall back to system fonts and lose their
industrial display character. Add @fontsource/noto-sans-tc weight 900
and a :lang() rule that swaps it in for any .font-display element
while the page language is a Chinese variant (zh, zh-Hant, zh-TW,
zh-HK).

The fontsource CSS uses unicode-range descriptors, so non-Chinese
users do not download the Han glyph slices (effectively zero cost
for Latin-only locales).

The rule reverses Tailwind's italic, uppercase, tracking, and the
hero's 0.022em -webkit-text-stroke fatten trick — none of those are
meaningful for CJK text and the stroke trick muddies strokes at
weight 900.
2026-05-24 15:57:52 -05:00
Alex Gleason 64546f5a7f Remove unused npm dependencies
Drop 13 packages that have zero imports across src/, configs, and
native projects:

- @radix-ui/react-{menubar,navigation-menu,aspect-ratio,context-menu}
  (no shadcn primitive consumes them)
- react-leaflet, leaflet, @types/leaflet (no map usage)
- smol-toml, fflate, html-to-image, input-otp
- react-resizable-panels, react-day-picker

Also drops the orphaned .leaflet-* CSS overrides in src/index.css.
2026-05-23 20:28:52 -05:00
Alex Gleason 7c50fa9a90 remove user-customizable theme system
Agora's colors and fonts are now hardcoded in the bundle. There is no
runtime CSS-variable injection, no remote-loaded theme, no font-family
override from event data, no background image, no recolored favicon.
Switching themes only toggles the .dark class on <html>.

Removed:

- src/themes.ts: ThemeConfig, ThemesConfig, CoreThemeColors, ThemeFont,
  ThemeBackground, themePresets (22 presets), buildThemeCssFromCore,
  deriveTokensFromCore, the 'custom' Theme variant. Now exports only
  resolveTheme(theme) -> 'light' | 'dark'.
- src/lib/fontLoader.ts: deleted. Mounted arbitrary @font-face rules with
  event-sourced URLs and font-family overrides — the primary CSS-injection
  vector. sanitizeCssString moved to src/lib/cssSanitize.ts (still used by
  the Letter feature).
- AppProvider hooks useApplyFonts / useApplyBackground / useApplyFavicon.
- useTheme.applyCustomTheme — the entrypoint that wrote external palettes
  to global theme state.
- ColorMomentEyeButton + its callers in NoteCard and PostDetailPage. Color
  Moments (kind 3367) still render as palette art, but the 'Set as theme'
  button is gone.
- LetterAttachment in LetterDetailSheet — the gift-box UI that applied an
  attached color moment as the recipient's theme.
- paletteToTheme() from colorMomentUtils — only getColors() remains.
- customTheme / themes fields from AppConfig, AppConfigSchema,
  EncryptedSettings, EncryptedSettingsSchema. Existing customTheme values
  in localStorage and encrypted NIP-78 settings are now ignored.
- 14 unused @fontsource packages. Only the 10 letter fonts and the 2 base
  UI fonts (Inter Variable, Bebas Neue) remain. noto-sans-nushu is kept
  for the encrypted-letter obfuscation indicator.

Added:

- Static :root {} and .dark {} blocks in src/index.css with the 19 shadcn
  tokens hardcoded. These were previously injected at runtime by
  AppProvider; without them the app would lose all colors when the runtime
  injector was removed.
- src/lib/cssSanitize.ts holding the sanitizeCssString helper for the
  Letter feature's font-family interpolation.

Simplified:

- public/theme.js: no longer reads customTheme or themes from localStorage,
  just resolves system/light/dark with hardcoded built-ins. Must stay in
  sync with src/index.css colors.
- src/lib/fonts.ts: trimmed to only the 10 letter fonts. findBundledFont
  and resolveCssFamily removed (they were only used by fontLoader).
2026-05-23 14:31:04 -05:00
Alex Gleason 55d873548d remove iframe.diy sandbox, nsite preview, and webxdc runtime
The iframe.diy-based sandbox infrastructure powered two features:
the nsite preview dialog (Run button on NsiteCard and AppHandlerContent)
and the webxdc embed (cartridge launcher + sandboxed iframe runtime
with kind 4932/20932 sync events).

Both are removed wholesale:

- iframe sandbox plumbing: SandboxFrame, src/lib/sandbox/*,
  iframeSubdomain, previewInjectedScript.
- nsite preview: NsitePreviewDialog, NsitePermissionManager,
  NsitePermissionPrompt, useNsiteSignerRpc, nsitePermissions,
  nsiteNostrProvider, NsitePlayerContext.
- webxdc runtime: WebxdcEmbed, Webxdc, GameControls, useWebxdc,
  webxdcMeta, public/cartridge.png, NOSTR_WEBXDC.md,
  @webxdc/types dependency, kindLabels/signerWithNudge entries
  for kinds 4932/20932.
- AppConfig: drop sandboxDomain, showWebxdc, feedIncludeWebxdc.
- extraKinds: drop kind 1063 webxdc entry; sidebar drops 'webxdc'.
- ComposeBox: .xdc uploads now flow through the generic file path
  (no UUID injection, no manifest extraction, .xdc removed from
  the file picker accept list).
- NoteContent / FileMetadataContent: webxdc branches removed; .xdc
  attachments fall through to the generic file card.
- LayoutContext / CenterColumnContext: only consumed by the removed
  fullscreen preview panels — deleted along with its provider in
  AppRouter.

NsiteCard keeps its rich link-preview card but loses the Run button
and preview dialog. AppHandlerContent keeps a kind 35128 `a`-tag
reference but replaces 'Run' with an external 'Open Site' link to
`<pubkeyB36><dTag>.nsite.lol`. The standard HTML iframe `sandbox`
attribute used by SpotifyEmbed/TweetEmbed/RedditEmbed is unrelated
to iframe.diy and stays.
2026-05-23 13:38:28 -05:00
Alex Gleason 553edf761e Merge branch 'main' of gitlab.com:soapbox-pub/agora 2026-05-22 01:21:57 -05:00
Alex Gleason 05332e31c9 Swap bitcoinjs-lib for @scure/btc-signer
Replace the bitcoinjs-lib + ecpair + @bitcoinerlab/secp256k1 + Buffer-polyfill
stack with @scure/btc-signer (plus @noble/curves for BIP-352 point math)
across every consumer:

  - src/lib/bitcoin.ts: P2TR payment + PSBT build/sign/finalize via btc.p2tr
    and btc.Transaction. signPsbtLocal hands the raw 32-byte private key to
    signIdx, which detects tapInternalKey and applies the BIP-341 TapTweak
    internally — the ECPair + manual taggedHash('TapTweak', ...) song-and-dance
    is gone. The empty-string-on-invalid-pubkey contract is preserved via an
    explicit on-curve check using schnorr.utils.lift_x.
  - src/lib/hdwallet/derivation.ts: deriveLeafTaprootSigner is removed in
    favour of deriveLeafPrivateKey, which is now sufficient because
    signIdx tweaks internally. The lazy ensureEcc / ECPairFactory plumbing
    is gone.
  - src/lib/hdwallet/transaction.ts: PSBT pipeline ported to btc.Transaction;
    signHdPsbt now wipes the materialised leaf privkey after signIdx().
  - src/lib/hdwallet/sp/{crypto,scanner}.ts: replace ecc.pointFromScalar /
    pointAdd / pointMultiply with secp256k1.Point methods from @noble/curves.
    pointMultiplyCompressed is exported for the scanner. Noble multiply is
    strict (throws on scalar 0 or >= n) so the wrappers preserve the previous
    "Failed to compute …" semantics.
  - src/lib/campaign.ts: parseCampaignWallet uses the shared
    validateBitcoinAddress helper instead of bitcoin.address.toOutputScript.
  - src/lib/bitcoin-signers.ts: NSecSignerBtc no longer touches Buffer.
  - src/lib/polyfills.ts + src/main.tsx: drop the global Buffer polyfill and
    the bitcoin.initEccLib(ecc) bootstrap — neither is needed anymore.

package.json: removes bitcoinjs-lib, ecpair, @bitcoinerlab/secp256k1, and
the buffer polyfill package; adds @scure/btc-signer ^2.2.0. @noble/curves
and @scure/{base,bip32,bip39} were already in tree.

bitcoin.test.ts gains a PSBT round-trip regression block. The unsigned-PSBT
hex fixtures in those tests were captured from the bitcoinjs-lib pipeline
before the migration, so the new build path is asserted to produce
byte-for-byte identical PSBT envelopes (input layout, output ordering,
fee-vs-change decision, PSBT v0 serialisation). Signing uses random aux so
witness bytes differ run-to-run; the tests verify the resulting raw tx hex
has the right Schnorr-key-path witness shape (0x01 stack + 0x40-byte sig)
for every input, plus that signPsbtLocal still throws when no input belongs
to the signer.

All 25 bitcoin.test.ts tests pass; full \`npm run test\` (72 tests + tsc +
eslint + vite build) is green.
2026-05-22 00:58:46 -05:00
Chad Curtis 53a7c01a9e Polish home hero: typeface, highlight box, line spacing, map artifacts
- Switch the hook headline to Bebas Neue (font-display family) at heavier
  size with a synthetic webkit-text-stroke fatten, italic, uppercase, and
  tight leading so 'Connecting activists to / unstoppable funding.' reads
  as a single editorial statement.
- Force the orange highlight onto its own line via <br>; tune left/right
  padding and inner text offset so the U sits flush with 'Connecting'
  above while the box extends past the word as a flourish.
- Fix two horizontal slashes across the world map caused by
  antimeridian-crossing rings (Russia, Antarctica): detect any longitude
  step > 180° and close+restart the SVG subpath instead of drawing the
  connecting line.
- Drop the 2008-era left-edge darkening gradient and the bottom
  vignette behind the map.
- Dim the central radial brand-orange glow (~half alpha).
- Tighten the arc-flow dash period and pixel size.
2026-05-22 00:42:39 -05:00
Alex Gleason 5d872e9a95 Add silent payment (BIP-352) address to /hdwallet Receive dialog
Derive a static sp1q… identifier from the user's nsec via BIP-352's
spend/scan key paths (m/352'/0'/0'/0'/0 and m/352'/0'/0'/1'/0), then
bech32m-encode scan_pubkey || spend_pubkey with HRP "sp" and version 0.

The Receive dialog now has two tabs: the existing BIP86 fresh-address
flow and a Silent payment tab that shows the static address with QR
and copy. The SP tab is labelled receive-only — the wallet doesn't yet
scan for incoming silent payments, so funds sent there won't appear in
the balance until scan + spend support is wired in.
2026-05-21 16:23:54 -05:00
Alex Gleason 522c265041 Add HD Bitcoin wallet at /hdwallet
A production-grade BIP86 Taproot HD wallet, separate from the single-address
wallet at /wallet. The seed is derived deterministically from the user's nsec
via HKDF-SHA-256 with an app-specific info string, so there is no new secret
for the user to back up \u2014 if they have their nsec they have the wallet.

Architecture:

  - src/lib/hdwallet/derivation.ts  \u2014 HKDF seed, BIP86 (m/86'/0'/0'),
    receive (0) and change (1) chains, per-leaf P2TR addresses, TapTweaked
    signing keys.

  - src/lib/hdwallet/scan.ts  \u2014 Standard gap-limit (20) scan across both
    chains via Esplora. Aggregated UTXO set, balance, and tx history
    (merged by txid so send-with-change shows as one row).

  - src/lib/hdwallet/transaction.ts  \u2014 Largest-first coin selection
    (confirmed first), multi-input P2TR PSBT build with per-input
    tapInternalKey from re-derived child keys, fresh change addresses on
    the internal chain (no address reuse).

  - useHdWalletAccess  \u2014 Gates on login type === 'nsec'. Extension and
    bunker logins keep the key isolated, so the page shows an explanatory
    card with a link back to /wallet.

  - useHdWallet  \u2014 Scan + tx history queries (60 s refresh), persisted
    receive-cursor in secure storage (Keychain on native, localStorage on
    web), auto-advance when chain catches up so old addresses are never
    re-shown.

  - HDWalletPage  \u2014 Mirrors /wallet's clean UX: big USD balance, send
    button, QR + truncated address, 'New address' rotator, collapsible tx
    history.

  - HDSendBitcoinDialog  \u2014 Mirrors SendBitcoinDialog (USD presets, fee
    speed picker, two-tap arm for large amounts, raw-address privacy
    disclaimer, success screen) but uses the HD UTXO set across many
    addresses and signs with per-input HD-derived keys.
2026-05-21 12:48:40 -05:00
lemon 634e161085 Remove ephemeral geo chat 2026-05-18 09:49:31 -07:00
Alex Gleason c5b929187a Remove Nostr direct messaging feature
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
2026-05-17 14:37:49 -05:00
lemon 0e117fa417 Add Bitcoin wallet primitives 2026-05-17 00:59:36 -07:00
lemon c4cfc0bd2c Use stable Breez Spark SDK 2026-05-16 23:38:37 -07:00
Alex Gleason e6ea96d69f Sync package-lock.json to version 2.8.0
Left over from the ditto/main merge — version was bumped back to
2.8.0 (Agora's version) in package.json post-merge but the lockfile
wasn't regenerated until npm install ran.
2026-05-13 19:33:26 -05:00
Alex Gleason 740fc1c63c Merge ditto/main into agora
Pulls in 387 commits from ditto/main while preserving Agora-specific
features. Where the two codebases diverged on the same concept, kept
the Agora side per project direction.

Kept Agora-specific:
- SparkWallet stack (over Ditto's nostr-derived Bitcoin wallet)
- Communities (NIP-72 + chat + members), Messages, Organizers,
  Actions, Verified, Appearance settings
- DMProviderWrapper, country/organizer moderation in NoteMoreMenu
- 'Agora' branding, pub.agora.app bundle ID, version 2.8.0
- Built-in theme system (src/themes.ts) only

Rejected from Ditto:
- All Blobbi virtual pet code (80+ files, route, provider, sidebar,
  kind labels, feed setting, NIP.md entries, CSS animations)
- Custom theme events (kinds 36767/16767) — ThemesPage, ThemeContent,
  active profile themes, theme snapshot recovery
- On-chain zaps (kind 8333) and the entire Bitcoin wallet implementation
  (useBitcoinWallet, bitcoin-signers, BitcoinContentHeader,
  bitcoinjs-lib / @bitcoinerlab/secp256k1 / ecpair / tiny-secp256k1)
- ZapSuccessScreen (depended on dropped bitcoin lib)

Pulled in from Ditto:
- .agents/skills/* (12 new specialized skills, slim AGENTS.md)
- @nostrify bumps to 0.52 / 0.6 / 0.37
- New routes/pages: Music, Podcasts, Videos, Vines, Wikipedia, Books,
  Bluesky, Archive, AIChat, Trends, Webxdc, Highlights, Decks, Emojis,
  Development, Treasures, Colors, Packs
- Birdstar feed integration (kinds 2473, 12473, 30621)
- Wikipedia/Wikidata/Scryfall lookup in ExternalContentPage
- release-notes CI job + extract-release-notes.mjs script
- nsite:// URI handling in feed/sidebar
- iOS fastlane setup
- src/lib/avatarShape.ts + Avatar shape prop (kept for new Music/People
  components that depend on it)

Preserved Agora's ABSOLUTE 'NEVER COMMIT' rule at the top of AGENTS.md
and dropped Ditto's contradicting 'Commit at the end of every task'
section.

Validation: npm run test passes (tsc, eslint, 40/40 vitest, vite build).
2026-05-13 18:35:03 -05:00
Alex Gleason 474ec6cc99 Make the Esplora API base URL configurable
Hardcoded MEMPOOL_API constant in src/lib/bitcoin.ts becomes a baseUrl
parameter on every fetch helper, sourced from a new `esploraBaseUrl`
field on AppConfig (default `https://mempool.space/api`). The wallet,
zap dialogs, on-chain zap verification, and NIP-73 Bitcoin tx/address
pages now read the URL from useAppContext and pass it through, so
self-hosted Esplora deployments (or Blockstream's) work without code
changes. The mempool.space-specific `/v1/prices` extension is still
appended by fetchBtcPrice.
2026-05-13 11:23:36 -05:00
Alex Gleason b8773c47d7 Automate App Store releases via self-hosted Mac runner
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.
2026-05-11 12:59:04 -07:00
Alex Gleason d84f2b790f Link people-list avatars to profiles and prefer naddr for kind 3
Stacked avatars in PeopleAvatarStack are now clickable, navigating to
the user's npub profile so readers can jump straight to a member from a
follow list, follow set, or follow pack — not only from the surrounding
post. Each avatar is wrapped in a Link with a stopPropagation handler so
the click doesn't bubble up to the card-level navigation, and the focus
ring is now visible on keyboard focus.

Kind 3 follow-list events are legacy replaceable kinds (NIP-01) but
fell outside the 10000–19999 range that NoteCard, EmbeddedPeopleListCard,
PostDetailPage, and NoteMoreMenu all special-cased — so clicking a
follow list in a feed went to a per-event nevent that pinned to a stale
revision instead of the stable naddr. The four call sites are now
unified behind a new lib/encodeEvent.ts helper that treats kinds 0, 3,
and 41 as replaceable, alongside 10000–19999 and 30000–39999. The same
helper exposes encodeEventNevent for callers that intentionally want to
reference a historical version (e.g. the profile-recovery dialog).
2026-05-09 17:06:10 -07:00
Alex Gleason 9810f813a8 release: v2.13.1 2026-05-05 23:06:07 -05:00
Alex Gleason 7be92b8eec Simplify on-chain zap dialog
Redesign the on-chain flow around a sleek 'Send $X' experience: click
the big number to edit, presets sit underneath, comment collapses behind
a chevron, recipient address and OR divider are gone, the send button
just says 'Send $5.20' with the fee included, and fee speeds are
deduplicated so duplicate sat/vB tiers don't repeat.

The fee speed now auto-adjusts when the amount changes to keep the fee
below 40% of the send amount — once the user manually picks a speed,
auto-adjustment is disabled for the session.

Dialog framing switches from 'Send a Zap' to 'Send Bitcoin', the
redundant description line is dropped, and the (?) popover routes to
one of two new tab-specific FAQ entries (send-bitcoin-onchain or
send-bitcoin-lightning).
2026-05-05 19:35:52 -05:00
Alex Gleason 0497aa33c9 Show live progress during the nostrconnect login handshake
When a user tapped "Open Signer App", the dialog previously stayed
frozen on the same screen — same button, same copy-URI fallback, no
feedback — until the login either succeeded (and the dialog dismissed)
or timed out after two minutes. With slow or flaky signers (Amber's
current listening-REQ bug being the immediate trigger, but any NIP-46
signer that takes more than a second or two to respond hits the same
hole) this looked indistinguishable from a hang. Users retapped the
button, closed the dialog, gave up.

Now the dialog swaps the QR / Open Signer App area for a centered
spinner with a live status line as the handshake advances:

- "Waiting for signer connection…" while the signer app has the user
  and we're listening on kind 24133 for the connect-ack.
- "Getting public key…" once the connect-ack arrives and we're
  issuing the NIP-46 get_public_key RPC.

On mobile the swap happens synchronously when the user taps "Open
Signer App" so they see the progress state the moment they return
from the signer — this is the most important window, since that's
exactly when the original UI left them staring at a button they
were worried they needed to re-tap. On desktop the QR stays visible
through the awaiting-connect phase (they may still be scanning with
a different device) and only swaps in once the signer has
acknowledged.

The progress view includes a Cancel link (primary color, matches the
"Create account" affordance) that aborts the in-flight subscription
and regenerates fresh connect params — equivalent to the existing
Retry path, but reachable while the handshake is live instead of
only after a failure.

The handshake phases are surfaced via the new `onStatus` callback
on `NLogin.fromNostrConnect` in @nostrify/react 0.6.0. Bumps
@nostrify/react to ^0.6.0 and @nostrify/nostrify / @nostrify/types
to their matching versions (^0.52.0 / ^0.37.0) to avoid duplicate
nested package copies that would otherwise split type identity.

Incidental cleanup while editing the dialog: the Copy URI button and
the "Tap to open your signer app" / "Scan with your signer app"
status lines are removed. The primary Open Signer App button is
self-explanatory on mobile, and the QR on desktop doesn't need a
caption.
2026-05-04 21:42:45 -05:00
Alex Gleason 1597e7540b Disable user relays by default
User relays are no longer used until the user explicitly opts in via
Settings > Network. Adds a useUserRelays toggle alongside the existing
useAppRelays toggle in RelayListManager, defaulting to false. Fresh
installs and new accounts will only query app-default relays until the
user enables their personal NIP-65 list.

The user's relay list (kind 10002) is still synced from Nostr and
managed in the UI when logged in — the toggle only controls whether it
is included in the effective relay set used by NostrProvider's pool
and useNativeNotifications. The setting is persisted to AppConfig and
synced cross-device via NIP-78 encrypted settings.

getEffectiveRelays now takes both flags and short-circuits accordingly,
producing an empty list when both are off (instead of the previous
behavior of always returning user relays).
2026-05-03 09:00:43 -05:00
Alex Gleason 74a2522af1 Show right widget sidebar at iPad-landscape width
The right sidebar previously required xl (1280px) to appear, so horizontal
iPad (1024px) viewports saw only the left sidebar + main column. Use the
existing lg breakpoint (1024px) to control right-sidebar visibility, and
let the sidebars scale fluidly with the viewport by setting them to w-1/4
max-w-[300px] instead of fixed pixel widths. The center column (flex-1)
absorbs whatever space remains, so the layout fills the viewport smoothly
from 1024px up through the 1200px wrapper cap instead of leaving dead
space at intermediate widths. Below the lg breakpoint, the left sidebar
keeps its fixed 300px width.
2026-05-01 22:10:11 -05:00
Alex Gleason c5d5165f84 Merge remote-tracking branch 'origin/main' into wallet
# Conflicts:
#	package-lock.json
#	src/components/CommentContext.tsx
#	src/components/ExternalContentHeader.tsx
#	src/components/NoteCard.tsx
#	src/pages/VinesFeedPage.tsx
2026-04-29 21:44:07 -05:00
Alex Gleason cf2f466772 Restrict unknown-kind previews to the NIP-31 alt tag
The alt-tag fallback shipped in 9813a226 let several display paths fall
through to other tags (title, name, summary, description, d) or to the
raw content when alt was absent. For an unknown kind like attestr.xyz
(31871), that surfaced the opaque d-tag identifier
'e5272de9:289bce03a0b7:1777206698' as the preview and leaked raw content
into the hover-card reply indicator — both worse than the 'This event
kind is not supported' tombstone the feature was meant to produce.

Tighten the fallback everywhere an unknown kind might render:

- getEventFallbackText: only the NIP-31 alt tag; no title/name/d.
- CommentContext.getEventDisplayName: known kinds keep title/name/d,
  unknown kinds consider only alt. getKindLabel returns 'an unsupported
  event' instead of 'a post', so 'Commenting on ...' never implies the
  root is a text note.
- EmbeddedNote tagMeta: alt only, no title/name/description fallback.
- ExternalContentHeader.AddressableEventPreview: drop d-tag fallback.
- EmbeddedNaddr: gate rich title/description/content rendering behind
  a known-kind check; unknown kinds render UnknownKindContent instead
  of extractMetadata, which was leaking plaintext content as the
  description when the body wasn't JSON.

Regression-of: 9813a226
2026-04-28 14:44:48 -05:00
Alex Gleason 9813a226ec Render unknown event kinds with a NIP-31 alt-tag fallback
Previously, any kind not explicitly handled by NoteCard or
PostDetailContent fell through to the kind-1 text-note renderer, which
ran the URL/hashtag/nostr: tokenizer over arbitrary content — broken
for events whose content is JSON or empty.

Add an UnknownKindContent component that displays the NIP-31 'alt' tag
(falling back to title/name/summary/d) in a rounded card, or a dashed
'This event kind is not supported' tombstone when the event carries no
fallback text. Route to it from both dispatchers when the kind isn't
1, 11, or 1111.

Extend the same handling to embedded quote previews (EmbeddedNote,
EmbeddedNaddr, AddressableEventPreview) so reply-context hover cards,
compose previews, more-menu previews, notification references, and
inline nostr: mentions all display unknown kinds consistently instead
of feeding JSON or arbitrary content to the kind-1 tokenizer.
2026-04-28 14:05:51 -05:00
sam 42430e510d update messaging dep so that syncing thing stays visible 2026-04-28 19:24:31 +07:00
sam d96361c578 updated messaging dep 2026-04-28 18:39:32 +07:00
Alex Gleason c957041cf3 Render Birdstar bird detections and custom constellations
Adds feed support for kind 2473 (bird-by-ear detections) and kind 30621
(user-drawn star figures) from Birdstar. Detections render as species
cards using the existing Wikidata + Wikipedia summary hooks; constellations
render as gnomonically-projected SVG star-maps backed by the Hipparcos
catalog from d3-celestial. The 1.1 MB catalog is code-split via lazy() so
it only loads when a constellation event is actually viewed.
2026-04-28 03:12:24 -05:00
sam 50637a4dc1 updated to latest messaging package to resolve peer dep issue 2026-04-27 18:44:30 +07:00
Alex Gleason e9def50a85 Render webxdc embeds as a tilted, color-tinted cartridge
Replace the plain launch card with a Game Boy-style cartridge (using
public/cartridge.png) whose label region centers the app icon. The whole
cartridge scales as one image and reacts to the pointer with the
existing useCardTilt 3D effect.

The cartridge is tinted by the icon's dominant color: a new
useDominantColor hook samples the icon in an off-screen canvas, picks
the heaviest hue bucket, and exposes it as HSL. A mask-image layer
masked to the cartridge silhouette blends the color over the grayscale
PNG with mix-blend-mode: color, preserving the shading. Grayscale or
CORS-blocked icons fall back to the original gray cartridge.

The app name moves out from under the cartridge and into the description
card in FileMetadataContent — rendered larger and bolder above the
note's content — while WebxdcEmbed still renders its own name card when
a parent isn't providing one (e.g. the kind 1 imeta path).
2026-04-27 01:15:54 -05:00
sam 2852590e09 updated messaging dep 2026-04-27 13:09:54 +07:00
sam b5c941f9fb use latest messaging dep 2026-04-25 20:34:46 +07:00
sam 9cdbb7c9e8 expose legacy nip4 option in settings 2026-04-25 19:05:51 +07:00
Chad Curtis 8fe0751a67 Add overstimulation visual feedback: shockwave, UI crumble, and debris
Replace the invisible click shield with a dramatic visual sequence when
Blobbi reaches max overstimulation: a radial shockwave expands from
Blobbi, a dark backdrop covers the UI (Blobbi stands alone in the void),
and canvas debris particles rain down like rubble. On recovery the
backdrop fades and debris converges back.

Also fix the debug bypass in useShakeReaction (true || threshold) that
shipped nausea on every shake regardless of hunger stat, and remove the
toast notification replaced by the visual overlay.
2026-04-23 21:55:28 -05:00
Chad Curtis 0156a82629 Fix overflow menu rendering inside compose modal
The Poll/Spoiler popover opened side="bottom" from the toolbar, which
is near the bottom edge of the dialog.  Because the dialog container
uses overflow-hidden (needed for flex layout containment) and the
PortalContainerProvider portals content inside the dialog DOM, the
popover was clipped.

Switch to side="top" so the menu opens upward into the visible area
of the modal.
2026-04-23 15:25:44 -05:00
sam d1ca846d30 updated messaging dep 2026-04-23 20:02:41 +05:45