Commit Graph

2799 Commits

Author SHA1 Message Date
Chad Curtis dd87bc96ec Fix top nav arc overlapping letter compose picker drawer
Set hasSubHeader on LetterComposePage so the MobileTopBar uses a flat
rect instead of the down-arc variant, preventing the 20px arc overhang
from painting over the LetterEditor picker panel.
2026-04-05 06:15:34 -05:00
Chad Curtis a12d5db560 Add follow URI system with QR sharing and immersive follow page
Introduce a /follow/:npub deep link that auto-follows a user when
visited by a logged-in user, or presents an immersive business card
with a 'Follow on Ditto' CTA for logged-out visitors. The page applies
the target user's profile theme, renders their feed with infinite
scroll, and uses the same banner/avatar/arc styling as the main profile.

Add a FollowQRDialog that generates a themed QR code for the follow
URL. The QR colors are derived from the active theme: primary color
for modules (with contrast-safe darkening/lightening), and background
color for the QR background. Foreground text color is used when it is
colorful and offers significantly better contrast.

Surface the QR dialog from: own profile page (top-level button),
profile more menu, desktop sidebar account popover, and mobile drawer.
2026-04-05 06:01:48 -05:00
Alex Gleason 29696fa3d3 Apply nearest-neighbor scaling to small custom emoji images
Custom emoji images with natural dimensions <= 16x16 now render with
image-rendering: pixelated to preserve crisp pixels instead of blurring.

Also consolidates 6 direct <img> sites to use the shared CustomEmojiImg
component so all custom emoji rendering benefits from this behavior.
2026-04-04 22:58:42 -05:00
Chad Curtis ffc31e8e8f Merge branch 'fix/blobbi-reuse-existing-eggs' into 'main'
Fix repeated egg creation and reuse existing eggs during ceremony

See merge request soapbox-pub/ditto!158
2026-04-05 02:09:45 +00:00
filemon 720a7e91fe Base ceremony decision on actual companion stages, not onboardingDone flag
The onboardingDone flag can be true on inconsistent accounts where the
user never actually hatched an egg. Now the ceremony check always waits
for companions to load and inspects their real stages:

- Any baby/adult exists: skip ceremony, auto-fix flag if needed
- Only eggs exist: ceremony with existing egg (regardless of flag)
- No companions resolved: ceremony creates a new egg

A ceremonyCheckDone flag prevents the effect from re-firing as
companion data updates during normal use.
2026-04-04 21:06:22 -03:00
filemon 05096e2cd9 Fix duplicate egg creation on every page load during onboarding
The ceremony was triggered whenever onboardingDone was false, without
waiting for companion data to load. This caused a new egg to be
published on every page visit/refresh for users mid-onboarding.

Now the decision tree waits for companions to load before deciding:
- No profile / no pets: ceremony creates a new egg (brand new user)
- Has baby/adult: skip ceremony, auto-fix onboardingDone flag
- Has only eggs: reuse an existing egg via existingCompanion prop
- Stale pet references: treat as new user

The chosen egg is locked in a ref so mid-ceremony refreshes don't
switch eggs or create duplicates.
2026-04-04 20:37:11 -03:00
filemon 05667460eb Fix first-time egg ceremony not covering RightSidebar
Portal the first-time hatching ceremony to document.body with z-[100],
matching the subsequent hatch ceremony implementation. The overlay was
previously rendered inline inside the center column's stacking context
(relative z-0), which prevented its fixed z-50 from painting over the
sibling RightSidebar.
2026-04-04 20:15:52 -03:00
Chad Curtis b10dae7655 Persist companion position across page navigations instead of replaying entry animation 2026-04-04 17:18:24 -05:00
Chad Curtis c799b9efd6 Fix crash when rendering egg: guard against undefined allTags from CompanionData cast 2026-04-04 17:14:55 -05:00
Chad Curtis fe4834e157 Remove deprecated dead code: selector modal state, useRerollMission plumbing, unused companion prop 2026-04-04 17:11:43 -05:00
Chad Curtis 5d972249a4 Fix all ESLint errors: remove unused imports, variables, and props across 4 files 2026-04-04 17:03:32 -05:00
Chad Curtis f607a01577 Fix ambiguous Tailwind duration-[2000ms] class warning 2026-04-04 16:50:56 -05:00
Chad Curtis 1e232e6a9e Blobbi hatching ceremony: immersive egg-to-blobbi experience with redesigned care UI
Replaces the old onboarding tour with a full hatching ceremony featuring golden aura,
sparkles, typewriter dialog, and fade-to-white reveal. Redesigns the BlobbiPage with
curved arc stats, floating action bubbles, overlay drawer tabs, and responsive layout.
Adds companion pill button, simplified photo modal, and egg animation styles.
Removes the old tour system (FirstHatchTour, tour hooks, tour types).
2026-04-04 16:49:51 -05:00
Alex Gleason 72b63dac21 Set default AppConfig.client to Ditto's kind 31990 handler naddr 2026-04-04 13:30:09 -05:00
Chad Curtis be82cb9626 Propagate relay and author hints to all event fetch call sites
Wire relay URL hints (from e/E tag position [2]) and author pubkey hints
(from e/E tag position [4] or p/P tag fallback) through every component
that fetches a referenced event:

- NoteCard: use getParentEventHints, pass hints through ReplyContext
- ReplyContext: accept and forward relay/author hints to EmbeddedNote
- CommentContext: extract hints from E/A tags in parseCommentRoot,
  pass to useEvent, useAddrEvent, and EmbeddedNote
- NotificationsPage: extract hints from e tag in ReferencedNoteCard
- usePollVoteLabel: extract hints from e tag for parent poll fetch
- ComposeBox: pass quotedEvent.pubkey as authorHint to EmbeddedNote
2026-04-04 06:03:33 -05:00
Chad Curtis c2c6f711b5 Fix parent author hint extraction and useEvent query cache keying
getParentEventHints only looked at position [4] of the e tag for the parent
author pubkey, but many clients (e.g. Wisp) omit it. When the relay hint
doesn't have the event, Tier 3 (NIP-65 outbox resolution) never fired
because authorHint was undefined. Now falls back to the first p tag, which
per NIP-10 convention holds the parent author's pubkey.

Also include relays and authorHint in the useEvent queryKey so calls with
different hints aren't served stale null results from a hint-less query.
2026-04-04 05:50:21 -05:00
Chad Curtis 3fba81a7d2 Fix ancestor thread fetching to use relay hints and author outbox relays
AncestorThread was calling useEvent(eventId) without relay hints or author
hints, so ancestor events only resolved via Tier 1 (user's configured relays).
Tiers 2 (relay hints from e tags) and 3 (author's NIP-65 outbox relays) were
never activated, causing parent events on personal relays to silently fail.

Added getParentEventHints() to extract relay URL and author pubkey from NIP-10
e tags, and wired both through AncestorThread's recursive chain.
2026-04-04 05:22:28 -05:00
Chad Curtis 6f2b51197f Add option filter bars to poll voters modal with scrollable overflow and accent divider 2026-04-04 03:23:39 -05:00
Chad Curtis 00c801e9dc Add poll voter interactions, kind 1018 vote rendering, and DRY activity card refactor
Poll voters:
- Clickable voter avatar stack + vote count on polls (before and after voting)
- Voters modal showing each voter with avatar, name, option, and nevent link
- Extract VoterAvatarsButton to DRY the avatar stack pattern

Kind 1018 vote rendering:
- Register in PostDetailPage as compact activity card with parent poll ancestor
- Register in NoteCard with threaded + normal variants (user avatar, not icon)
- Register in CommentContext with Vote icon, 'a vote' label, and rich hover showing voter + option
- Extract usePollVoteLabel hook to DRY vote label resolution across 3 call sites

ActivityCard refactor:
- Extract shared ActivityCard and ActorRow from NoteCard
- Refactor reaction (kind 7), repost (kind 6/16), zap (kind 9735), and poll vote (kind 1018)
- Reuse ActivityCard in PostDetailPage for vote detail view
- Net ~250 line reduction in NoteCard
2026-04-04 03:09:20 -05:00
Chad Curtis 47e7d05cb9 Add poll voter avatars, voters modal, and kind 1018 vote detail view
- Show clickable voter avatar stack + vote count on polls (both before and after voting)
- Clicking opens a voters modal listing each voter with avatar, name, voted option, and link to their vote nevent
- Extract VoterAvatarsButton to DRY the avatar stack pattern
- Register kind 1018 in PostDetailPage so vote nevents render as compact activity cards (avatar + 'voted' + label)
- Parent poll appears as threaded ancestor above the vote card
- Use PostActionBar for vote detail action buttons
2026-04-04 02:42:19 -05:00
Chad Curtis 4ef6d1b149 Revert "Use relaxed eoseTimeout (1000ms) for Blobbi queries to ensure freshest data"
This reverts commit ed083bfdad.
2026-04-04 01:56:40 -05:00
Alex Gleason badd19d27c Reorder default sidebar: Blobbi, Badges, Emojis, Letters, Themes 2026-04-04 00:25:16 -05:00
Alex Gleason 7fa6e574f8 Fix lightbox z-index by portaling inside Lightbox itself, not just ImageGallery
The previous fix (db502b46) only portaled the Lightbox when rendered
from ImageGallery. But Lightbox is also rendered directly by
NoteContent, MediaCollage, and MagicDeckContent — all still trapped
inside the center column's z-0 stacking context (added in 8e3f778f).

Move createPortal(…, document.body) into Lightbox so every consumer
escapes the stacking context automatically.
2026-04-03 23:27:53 -05:00
Alex Gleason bc1c4cb7cf Merge branch 'main' of gitlab.com:soapbox-pub/ditto 2026-04-03 22:50:34 -05:00
Chad Curtis 119f684fb3 Fix sharp corners on compose box by adding rounded-2xl 2026-04-03 22:41:16 -05:00
Chad Curtis 45134ef9cc Allow file uploads in poll composer
Remove the separate pollQuestion state and poll builder branch. Poll
mode now reuses the normal textarea/preview ternary (with edit/preview
toggle, file uploads, paste handling, imeta tags) and renders poll
options and settings below it.
2026-04-03 22:29:32 -05:00
Chad Curtis db502b462c Fix lightbox appearing behind right sidebar by portaling to document.body 2026-04-03 21:52:05 -05:00
Chad Curtis ed083bfdad Use relaxed eoseTimeout (1000ms) for Blobbi queries to ensure freshest data
The default pool eoseTimeout (300ms) races and resolves shortly after the
fastest relay. Blobbi pet state and profile data are accuracy-sensitive —
stale data from a single fast relay can cause data loss when mutations
overwrite newer versions on other relays.

- Add eoseTimeout option to fetchFreshEvent and new fetchFreshEvents variant
- Update useBlobbisCollection, useBlobbonautProfile, and useBlobbiSleepToggle
  to use fetchFreshEvents/fetchFreshEvent with eoseTimeout: 1000
- Widen NostrBatcher.req() type to pass through eoseTimeout to NPool
- Gate unconditional console.log in parseBlobbiEvent behind import.meta.env.DEV
- Remove unconditional console.logs from useBlobbisCollection
2026-04-03 21:39:00 -05:00
Alex Gleason 47811f9190 Use NIP-5A canonical subdomains for nsite preview iframe origins
Instead of generating a random session ID for the iframe subdomain,
derive it from the nsite event using the NIP-5A canonical format:
- Root sites (kind 15128): npub subdomain
- Named sites (kind 35128): base36(pubkey) + d-tag subdomain

Extract hexToBase36 and getNsiteSubdomain into a shared utility
used by both NsiteCard and NsitePreviewDialog.
2026-04-03 18:37:28 -05:00
Alex Gleason ba99cdc51c Fix MIME type for nsite assets by always using extension-based detection
Blossom servers commonly return incorrect Content-Type headers (e.g. text/plain
for .js files), causing browsers to reject module scripts under strict MIME
checking. Since we always know the file path from the manifest, use guessMimeType
based on the file extension instead of trusting the Blossom response header.
2026-04-03 18:13:40 -05:00
Alex Gleason 7092f7306f Serve nsite previews directly from Blossom instead of proxying through nsite.lol gateway
NsitePreviewDialog now builds a path→sha256 manifest from the event's 'path'
tags and resolves files directly from Blossom servers (from the event's 'server'
tags, falling back to the user's configured app Blossom servers). Each fetch
request from the iframe is intercepted, the sha256 is looked up in the manifest,
and the blob is fetched from the first Blossom server that responds successfully.
Unknown paths fall back to /index.html to support SPA client-side routing.

- NsitePreviewDialog: remove nsiteUrl proxy, accept NostrEvent instead
- NsiteCard: pass event directly to dialog
- AppHandlerContent: use useAddrEvent to fetch the kind 35128 event by
  pubkey+d-tag from the 'a' tag, then pass the event to the dialog; disable
  Run button until the nsite event is loaded; remove unused hexToBase36
2026-04-03 18:10:22 -05:00
Alex Gleason 357dd56de0 Merge branch 'main' of gitlab.com:soapbox-pub/ditto 2026-04-03 17:54:42 -05:00
Alex Gleason fadec0574a Add Run button to NsiteCard for in-app nsite preview 2026-04-03 17:49:06 -05:00
Alex Gleason 469806886a Fix card navigation firing on button/link clicks in NoteCard 2026-04-03 17:45:36 -05:00
Alex Gleason f7ab980ecd Fix nsite preview panel height using measured column rect
Replace absolute/sticky positioning with fixed + inline styles derived
from a ResizeObserver on the center column element. The panel now sits
at exactly the column's left/top/width and fills to the bottom of the
viewport, unaffected by the column's pb-overscroll padding.
2026-04-03 17:41:09 -05:00
Alex Gleason c6b5ab2284 Replace address bar and external link with app icon and name in preview nav bar 2026-04-03 17:36:17 -05:00
Alex Gleason 2231673ee6 Fix nsite preview panel to fill exactly the center column
Add CenterColumnContext to LayoutContext and expose the center column DOM
element from MainLayout via a useState ref callback. NsitePreviewDialog now
portals into that element using absolute inset-0 instead of fixed positioning
with hardcoded sidebar insets, so it always covers exactly the center column
regardless of viewport width.
2026-04-03 17:30:00 -05:00
Alex Gleason f8907475f9 Link client tag name to /:naddr on post detail page 2026-04-03 17:28:29 -05:00
Alex Gleason 4252841125 Replace dialog with fixed center-column overlay panel for nsite preview
Remove the Radix Dialog and browser chrome (back/forward/refresh/fullscreen).
The preview now renders as a portal-based fixed panel that overlays exactly
the center column using responsive left/right insets matching the sidebar
widths (sidebar:left-[300px], xl:right-[300px]). A slim nav bar at the top
shows the nsite:// URL, an external-link button, and a close button.
2026-04-03 17:25:55 -05:00
Alex Gleason ee8220c1f0 Use nsite://<name><path> in preview address bar
Separate the proxy target (nsite.lol gateway URL) from the display URL.
Pass nsiteName through to the dialog so the address bar shows a clean
nsite:// scheme with no gateway hostname.
2026-04-03 17:11:45 -05:00
Alex Gleason 11e29646a7 Show nsite:// URL in preview address bar instead of gateway URL 2026-04-03 17:09:03 -05:00
Alex Gleason a9bab7f8e8 Remove default dialog close button from nsite preview 2026-04-03 17:03:09 -05:00
Alex Gleason 0b69ab51f4 Fix Content-Type header matching in nsite preview proxy
The iframe-fetch-client does an exact equality check for "text/html",
but real servers return "text/html; charset=UTF-8". Also, the browser
fetch() API lowercases all header names while main.js checks Title-Case
keys. Fix both: re-key headers to Title-Case and strip charset params
from Content-Type values before sending them to the iframe.
2026-04-03 17:01:59 -05:00
Alex Gleason 2a32e79b13 feat: change AppConfig.client to naddr1 format, decode relay hint per NIP-89
AppConfig.client now expects a NIP-19 naddr1 string pointing to the app's
kind 31990 handler event instead of a raw 'a' tag value. useNostrPublish
decodes the naddr at publish time to extract the 31990:<pubkey>:<d-tag>
addr and any embedded relay hint, producing a fully NIP-89-compliant
client tag: ["client", <name>, <addr>, <relay-hint>].
2026-04-03 16:57:57 -05:00
Alex Gleason 39fc7549ac Add Run button and nsite preview dialog to app handler cards
When a kind 31990 app event includes an 'a' tag pointing to a kind 35128
nsite, display a 'Run' button that opens an in-app preview dialog. The
dialog embeds the nsite in a sandboxed iframe via the Shakespeare
iframe-fetch-client protocol (local-shakespeare.dev), proxying fetch
requests from the iframe to the live nsite URL so the SPA renders
without needing CORS headers on the origin server.
2026-04-03 16:53:25 -05:00
Alex Gleason 414f42e339 Add Blobbi (kind 31124) to the Ditto homepage feed 2026-04-03 16:50:17 -05:00
Alex Gleason 8e3f778f5b Improve Zapstore and app handler card display
- Rename Zapstore kind labels to include 'Zapstore' prefix across all
  label registries (NoteCard, PostDetailPage, CommentContext,
  ExternalContentHeader, NotificationsPage, extraKinds)
- Wrap Zapstore (32267, 30063, 3063) compact and detail content in
  rounded bordered cards with hover effects; remove redundant mt-2/mt-3
  margins from component roots
- Replace useLinkPreview thumbnail with metadata banner/picture in kind
  31990 app handler cards (compact and full views)
- Add pt-4 to Zapstore detail card wrappers in PostDetailPage
- Fix sticky tab bar (SubHeaderBar z-10) being painted over by card
  content: remove z-10 from AppHandlerContent inner div and add z-0 to
  the main content column in MainLayout
2026-04-03 16:20:40 -05:00
Alex Gleason 7d83273410 Simplify sidebar media query to a single useQuery with inline fallback logic 2026-04-03 00:53:45 -05:00
Alex Gleason fabcb4170d Fill profile media sidebar with kind 1 fallback when kind 20 results are sparse
When fewer than 9 media-native events (kind 20, 21, 22, etc.) are found for a
profile, perform a secondary query for kind 1 events with search:media:true and
append them to fill the remaining slots. Kind 20 events are always displayed first.
2026-04-03 00:36:44 -05:00
Alex Gleason 3e429fe0b0 Add rendering for Zapstore release (kind 30063) and asset (kind 3063) events
- New ZapstoreReleaseContent component: shows app icon/name fetched from the
  linked kind 32267, version badge, channel badge, release notes, and a
  downloads section that fetches and renders each linked kind 3063 asset
- New ZapstoreAssetContent component: shows MIME-type icon, platform/arch
  badges, file size, SHA-256 hash, commit hash, supported NIPs, and APK
  certificate hashes
- Register both kinds in NoteCard, PostDetailPage, extraKinds, CommentContext,
  ExternalContentHeader, and NotificationsPage label/icon maps
- Route kind 3063 to the Zapstore relay in NostrProvider and useEvent
- Kind 3063 is excluded from feeds (display-only on direct navigation)
2026-04-02 23:09:01 -05:00