Commit Graph

3174 Commits

Author SHA1 Message Date
filemon e4a4fa2c4f Merge branch 'feat/blobbi-room-based-layout' into feat/blobbi-major-room-progression-split 2026-04-10 11:42:04 -03:00
filemon 29d61d9f1a Merge branch 'fix/blobbi-item-use-cooldown' into feat/blobbi-major-room-progression-split 2026-04-10 11:39:34 -03:00
Chad Curtis 72268dfde6 Merge branch 'feat/feed-blobbi-status-visuals' into 'main'
Reflect companion condition in feed Blobbi cards

See merge request soapbox-pub/ditto!169
2026-04-10 13:05:44 +00:00
Alex Gleason 7b63f6112c Clean up profile header: remove lightning address, NIP-05 check icon, and trailing slash from website URLs 2026-04-09 22:30:38 -05:00
Alex Gleason ce61d8d1a6 Restore right sidebar for profile pages, keep fields mobile-only 2026-04-09 22:00:50 -05:00
filemon c4a10b1303 Merge branch 'main' into feat/feed-blobbi-status-visuals 2026-04-09 15:27:28 -03:00
Chad Curtis 76c6846e91 Render BOLT11 lightning invoices in note content
Detect lnbc/lntb/lnbcrt/lntbs invoices (with optional lightning: prefix)
in note text and render them as interactive cards with a theme-aware QR
code, decoded amount, copy button, and Open in Wallet action.

- Add lightning-invoice token type to NoteContent tokenizer
- Create LightningInvoiceCard with tap-to-expand square QR, cqw-scaled
  amount text, and responsive layout
- Extract shared theme-aware QR color logic into src/lib/qrColors.ts
  (deduplicate from FollowQRDialog)
2026-04-09 08:02:26 -05:00
Alex Gleason ac1e82b52d release: v2.6.2 v2.6.2 2026-04-08 23:38:31 -05:00
Alex Gleason 437b8de652 Remove right sidebar content and show profile fields inline 2026-04-08 23:34:03 -05:00
Alex Gleason adadb6ed53 Fix native file downloads: save directly to Documents on iOS/Android 2026-04-08 22:54:46 -05:00
Alex Gleason f7c90a4a23 Remove trending hashtags section from logged-out homepage 2026-04-08 22:28:22 -05:00
Alex Gleason 82632bb76c Store nostr:login in secure storage on native platforms
Use capacitor-secure-storage-plugin to persist login credentials
(nsec keys) in iOS Keychain / Android KeyStore instead of plaintext
localStorage. Web behavior is unchanged. Existing native users are
auto-migrated on first launch: if secure storage is empty but
localStorage has data, it is moved over and the plaintext copy is
removed.

Also ignore ios/ directory in ESLint (Capacitor-generated files).
2026-04-08 22:20:48 -05:00
Alex Gleason 3a70d34e6d npm audit fix 2026-04-08 22:12:03 -05:00
Alex Gleason 221d3f4aff Merge branch 'mobile-search' 2026-04-08 22:11:38 -05:00
Alex Gleason 6a1a462ab0 Upgrade @nostrify/react to ^0.5.0 (async storage support)
Upgrade to the new version that includes the NLoginStorage interface
and storage/fallback props on NostrLoginProvider for pluggable async
storage backends (e.g. Capacitor Secure Storage).

- Add resolve.dedupe for react/react-dom to prevent dual-React issues
- Update NoteContent tests to use async findBy* queries since the
  provider now always awaits storage initialization
2026-04-08 22:08:56 -05:00
Alex Gleason 5ee8bc1cc0 Improve mobile search UX: lock scroll, hide bottom nav, dismiss accessory bar, and fix close behavior 2026-04-08 22:04:26 -05:00
Alex Gleason 76d53859cf Simplify webxdc to always open in fullscreen panel 2026-04-08 20:47:46 -05:00
Alex Gleason e482afbd3f Fix sandbox origin isolation and Android build issues 2026-04-08 20:47:42 -05:00
Alex Gleason 11ff27efe2 Enable iOS swipe-back navigation and fix bottom nav layout 2026-04-08 20:47:37 -05:00
Alex Gleason 8f6f678132 Add safe area padding and fix fullscreen sandbox on iOS 2026-04-08 20:47:32 -05:00
Alex Gleason f25139103c Add native SandboxPlugin for iOS and Android 2026-04-08 20:47:28 -05:00
Alex Gleason 0028b506e7 Fix webxdc bridge: serve script via resolveFile instead of injectedScripts
SandboxFrame's virtual script serving intercepted /webxdc.js and served
the empty placeholder content before resolveFile was ever called. The
dynamically generated bridge script (which embeds selfAddr etc.) was
never reaching the iframe.

Move bridge serving and HTML injection into resolveFileWithBridge so
the content is served from bridgeScriptRef after onReady populates it.
2026-04-08 16:55:01 -05:00
Alex Gleason 926c27d51c Fix webxdc race condition: await onReady before sending init
The sandbox frame was sending init immediately and calling onReady
concurrently, so fetch requests arrived before the archive was
downloaded and unzipped. Now onReady is awaited before init is sent,
matching the original Webxdc behavior.
2026-04-08 16:50:44 -05:00
Alex Gleason c4454ee2a1 Refactor iframe.diy usage into unified SandboxFrame component
Extract duplicated sandbox protocol logic from NsitePreviewDialog and
Webxdc into a single SandboxFrame component. Shared utilities (MIME
types, base64, HTML injection, JSON-RPC types) move to src/lib/sandbox/.

Add configurable sandboxDomain to AppConfig so the iframe.diy domain
can be overridden via ditto.json, preparing for native Capacitor
implementations.

Strip unused console/navigation/error RPC from previewInjectedScript,
leaving only the /index.html path normalization.
2026-04-08 16:41:23 -05:00
Chad Curtis e56737f776 Fix blobbi discovery: query by author instead of relying on profile.has[]
The Blobbi collection was previously discovered via the profile's has[] tag
list, meaning any blobbi whose d-tag was missing from that secondary index
would be invisible to the user despite existing on the relay.

Now useBlobbisCollection() without args queries all kind 31124 events by
author + ecosystem namespace tag — the user authored these events, so that
is the source of truth. The profile.has[] list is still used for selection
ordering preference, but no longer gates discovery.

The dList parameter remains available for targeted fetches (e.g. the
companion layer only needs one specific blobbi).
2026-04-08 11:02:03 -05:00
Chad Curtis feb6c1a9f6 Add drop shadow and solid gradient to overflow tab arrows 2026-04-08 10:27:17 -05:00
Chad Curtis 6f8d225597 Increase overflow tab arrow stroke to 4 and boost contrast 2026-04-08 10:22:04 -05:00
Chad Curtis 9ecd99a6a1 Add 'Write a letter' option to profile more menu
Adds a Mail-icon menu item in the profile more menu for other users'
profiles. Navigates to /letters/compose?to={npub} so the recipient is
pre-filled, matching the same flow used by the notification reply button.
2026-04-08 04:01:11 -05:00
Chad Curtis 287097627d Hide delivery method when push disabled; fix persistent description
Only show the delivery method radio group when push notifications are
enabled. Update the persistent option description to explain it is for
devices that don't support push notifications (e.g. GrapheneOS).
2026-04-08 00:20:20 -05:00
Chad Curtis 3ee491a63b Add push vs persistent notification delivery option for Android
Default to push mode (no foreground service). Persistent mode with
the always-on background polling service is opt-in via the new
Delivery Method section in notification settings.

- Add notificationStyle ('push' | 'persistent') to EncryptedSettings
- Show radio group in NotificationSettings on native platforms
- Pass notificationStyle through Capacitor plugin to SharedPreferences
- DittoNotificationPlugin starts/stops foreground service on style change
- MainActivity only starts service on launch when style is persistent
- Re-enable unread polling on native when push mode is active
2026-04-07 10:54:30 -05:00
Chad Curtis 7944f73da3 fix: use fetchFreshEvent and preserve non-p-tags in Follow All handlers
FollowPackDetailContent, TeamSoapboxCard, and InitialSyncGate all had
handleFollowAll implementations that queried kind 3 directly (bypassing
fetchFreshEvent) and rebuilt the tag array with only p-tags, silently
dropping all non-p-tags (relay hints, petnames, etc.). They also did
not pass prev for published_at preservation.

Align all three with the safe pattern already used in FollowPage and
useFollowActions.
2026-04-07 09:03:07 -05:00
Chad Curtis 17c1936817 Support follow pack/set naddr identifiers on /follow URL
The /follow route now accepts naddr1 identifiers for follow packs
(kind 39089) and follow sets (kind 30000) in addition to npub/nprofile.

Renders an immersive fullscreen layout with pack info hero, avatar
stack, big Follow All CTA with status indicator, and Feed/Members
tabs using the standard SubHeaderBar arc.

Follow All uses the safe fetch-fresh -> modify -> publish pattern
with prev for published_at preservation.

Shared components (PackFeedTab, MemberCard, MemberCardSkeleton) and
parsePackEvent are reused from FollowPackDetailContent and packUtils.

Also fixes SubHeaderBar tab indicator positioning when innerClassName
centers the tab container (adds containerOffset + ResizeObserver for
layout-dependent recalculation).
2026-04-07 08:55:27 -05:00
Chad Curtis c570f4689d Merge branch 'curated-ditto-feed' into 'main'
Curate Ditto feed by curator follow list with photos, divines, videos, and music

See merge request soapbox-pub/ditto!164
2026-04-07 12:52:23 +00:00
Chad Curtis 064ab1e101 Address MR review: extract feed hook, fix cache key, add error handling, make curator configurable
- Remove unused 'authors' parameter from useInfiniteHotFeed
- Extract inline query from Feed.tsx into useCuratedDittoFeed hook
- Use content-based fingerprint for query key instead of list length
- Add error state handling so curator fetch failure shows empty state
  instead of infinite skeletons for first-time visitors
- Move hardcoded curator pubkey to AppConfig (curatorPubkey) so it
  can be overridden via ditto.json without a code change
- Remove LANDING_KINDS/LANDING_WEBXDC_FILTER from Feed.tsx (now in hook)
2026-04-07 07:48:23 -05:00
Alex Gleason 9c0d49b904 Add OPFS as blocked API in lockdown-mode skill 2026-04-06 18:42:45 -05:00
Alex Gleason 69634e7c05 Update lockdown-mode skill with cross-platform availability info
Lockdown Mode is not iOS-only — it's available on iOS 16+, iPadOS 16+,
watchOS 10+, and macOS Ventura+. Add platform availability section with
Apple Support reference link, rename report file to ios-report.txt to
clarify it's iOS-specific, and broaden the skill description.
2026-04-06 16:13:57 -05:00
Alex Gleason db48ce7c40 Add raw diagnostic report as skill reference file 2026-04-06 16:05:52 -05:00
Alex Gleason 36c6e537a7 Add lockdown-mode agent skill with iOS Lockdown Mode API reference 2026-04-06 15:59:29 -05:00
Alex Gleason cbc3df0bef Allow any dev server host via ALLOWED_HOSTS env var 2026-04-06 14:40:31 -05:00
Alex Gleason 2ecd557430 Fix IndexedDB crash on iOS Lockdown Mode
openDatabase() now catches errors from idb's openDB() (which throws
synchronously when indexedDB is undefined) and returns null. All
consumers — profileCache, nip05Cache, dmMessageStore — check for null
and silently degrade to in-memory only.

The DM message store also stops re-throwing errors, which previously
could produce unhandled rejections in DMProvider.
2026-04-06 13:41:32 -05:00
filemon 61c84ed137 Fix conditional hook call in BlobbiStateCard
Move the early return for null companion below all hooks so useMemo
calls are unconditional. The null/egg guard is now inside the recipe
useMemo, and isSleeping/isEgg use optional chaining.
2026-04-06 13:46:50 -03:00
filemon a24b755e08 Use projected decay stats for feed Blobbi visuals
Replace raw companion.stats with calculateProjectedDecay() output so
feed cards reflect the Blobbi's real current condition after time-based
stat decay, matching what the room view shows via useProjectedBlobbiState.

The pure calculateProjectedDecay() function is called once per render
inside useMemo (no setInterval per card), keeping feed rendering
lightweight while staying consistent with the room's decay math.
2026-04-06 13:28:42 -03:00
filemon 46a970b900 Reflect companion condition in feed Blobbi cards
BlobbiStateCard now resolves the same status recipe used by the room
view (resolveStatusRecipe) from the on-chain stats, so feed Blobbis
show hunger, dirt, sleepiness, sadness, and sickness visuals.

A new attenuateRecipeForFeed() helper scales down body-effect particle
counts and removes flies to keep the smaller feed-card size readable.
Sleeping Blobbis get the buildSleepingRecipe() overlay, matching the
room behaviour.
2026-04-06 12:42:57 -03:00
Alex Gleason 594e7ea8fa ci: add build-web job to produce downloadable artifact
The old 'pages' job was removed when deploying switched to nsite,
which broke the artifact download URL on the docs site. This adds
a new build-web job that builds the web app on main and saves the
dist/ directory as a downloadable artifact.
2026-04-06 01:09:10 -05:00
Alex Gleason 0a5e72efd0 release: v2.6.1 v2.6.1 2026-04-06 00:58:23 -05:00
Alex Gleason 0f1021e0d3 Switch nsite preview from local-shakespeare.dev to iframe.diy
Replace the local-shakespeare.dev preview domain with iframe.diy, which
provides a service-worker based sandbox. This brings the nsite preview
implementation in line with Shakespeare's approach.

Key changes:
- iframe.diy handshake: listen for 'ready', respond with 'init'
- Derive private HMAC-SHA256 subdomains via deriveIframeSubdomain('nsite', ...)
- Inject preview script into HTML responses for console forwarding,
  SPA navigation tracking, and /index.html path normalization
- Remove sandbox attribute (iframe.diy manages its own sandboxing)
- Serve injected script from virtual /__injected__/preview.js path
2026-04-06 00:51:39 -05:00
filemon 286572777b Fix stat crown instability on room switch; hide Closet from navigation
Stat crown fix — root cause and solution:
The heroWidth measurement used a useRef + useEffect([], ...) pattern
that created a ResizeObserver once on mount. When switching rooms,
BlobbiRoomHero unmounts and remounts inside the new room component,
creating a new DOM element. But the ResizeObserver was still watching
the old detached element, so heroWidth went stale (often falling to
the default 375px or reading 0 from the detached node).

Fix: replaced useRef + useEffect with a callback ref (heroCallbackRef)
that disconnects the old ResizeObserver and creates a new one every
time the ref is assigned to a new element. This ensures heroWidth is
always measured from the currently-mounted hero container.

Additionally, the arc spread and radius values were too extreme on
desktop (120/190/230 degrees, 300px radius) which made the stats
fly far apart. Now using balanced moderate values:
- Mobile: 80/110/140 degrees
- Desktop: 90/130/160 degrees
- Radius: smooth 110px → 200px interpolation based on container width

This produces a stable, moderate crown that looks the same on initial
render and after room switches, with desktop having slightly more
breathing room than mobile without being exaggerated.

Closet hidden:
Removed 'closet' from DEFAULT_ROOM_ORDER with a comment explaining
how to re-enable it. The BlobbiClosetRoom component, its type in
BlobbiRoomId, its ROOM_META entry, and its ROOM_COMPONENTS mapping
all remain intact — only the navigation sequence excludes it.
2026-04-06 02:34:57 -03:00
Alex Gleason be65c659b2 Derive private iframe.diy subdomains with HMAC-SHA256
The i-tag UUID used for webxdc coordination is attacker-controlled.
Using it directly as the iframe.diy subdomain lets a malicious event
author pick a subdomain that collides with another app's origin,
gaining access to its localStorage/IndexedDB.

Introduce a persistent random seed in localStorage (ditto:seed) and
derive the subdomain as base36(HMAC-SHA256(seed, prefix|identifier)).
The prefix (e.g. "webxdc") domain-separates different use-cases.
The subdomain is stable per device+app but unpredictable to event
authors.
2026-04-06 00:33:55 -05:00
filemon 7fd4b7ab69 Polish carousel stability, desktop stat spacing, poop UX, and visual harmony
Carousel stability:
- Focused item container is now overflow-hidden with explicit dimensions
  (w-20 h-[4.5rem] / sm:w-24 sm:h-[5.5rem]) so no content can push
  the layout wider/taller
- Label uses a fixed max-width (w-16 sm:w-20) with text-center truncate
  so changing items never shifts the arrows
- Preview slots also have overflow-hidden
- Empty state matches the populated carousel height

Desktop stat spacing (mobile unchanged):
- Arc spread widened to 120/190/230 degrees (was 100/160/200)
- Radius scaled up to 300px max (was 260px)
- This gives stat indicators much more breathing room on desktop
  without affecting the mobile layout at all

Poop placement refinement:
- Pre-computed safe-zone positions in lower-left and lower-right
  corners, avoiding the central Blobbi hero area
- Positions stored on each PoopInstance so they're stable
- Shovel mode: active state shows ring indicator around the button
  and label changes to 'Done'; poop gets drop-shadow-lg when
  hoverable; hover:scale-150 for clearer interaction feedback

Care room fix:
- Treat button no longer calls invalid 'interact' DirectAction;
  uses a small food item from shop instead
- Both left/right slots always render the same fixed width
  (RoomActionButton or w-14/w-20 spacer) so switching between
  hygiene/medicine items never causes layout shift

Visual harmony:
- Closet room bottom bar now uses the same flex items-center
  justify-center horizontal layout instead of a different
  flex-col structure, matching the rhythm of all other rooms
2026-04-06 02:28:53 -03:00
filemon c9525a0233 Add carousel stability, sleep overlay, care room conditional actions, poop system
Multiple polish and interaction improvements:

1. Carousel stability:
   Focused item area now uses fixed dimensions (w-20 h-16 / sm:w-24 sm:h-20)
   so the carousel never reflows when switching between items. Arrows stay
   perfectly stable. Preview slots also have fixed w-10 h-12 dimensions.

2. Desktop stat spacing:
   Arc spread widened further on desktop: 100/160/200 degrees (was 90/140/180)
   with radius up to 260px (was 220px). Stats now have much more breathing
   room on desktop while mobile stays unchanged.

3. Bedroom sleep button centered:
   Sleep/wake button is now in the center of the bottom bar instead of
   right-aligned, making the bedroom feel more focused.

4. Sleep dark overlay:
   When Blobbi is sleeping, a radial-gradient dark overlay covers the
   entire room shell (all rooms, not just bedroom). Uses z-20 with
   pointer-events-none so controls remain usable. Scoped to the room
   shell only — app header, bottom nav, and other app UI stay unaffected.

5. Renamed Bathroom → Care Room:
   Room label changed from 'Bathroom' to 'Care Room' with icon changed
   from bathtub to bandage emoji, since the room also contains medicine.

6. Care room conditional side actions:
   ItemCarousel now supports onFocusChange callback and meta field.
   When a hygiene item is focused: Towel (left) + Shower (right).
   When a medicine item is focused: Lollipop/Treat (left) + empty (right).
   Side buttons swap reactively as the user cycles through items.

7. Temporary local poop system:
   - poop-system.ts: ephemeral generation based on hunger >= 95 (overfeed,
     kitchen-only) and hours since last interaction (time-based, random room)
   - Generated once on mount, no persistence
   - Poop appears as floating emoji in affected rooms
   - Kitchen shows a Shovel button when any poop exists anywhere
   - Shovel mode: clicking poop removes it and awards 5 XP via toast
   - All rooms accept RoomPoopState prop for future expansion
   - BlobbiRoomContext extended with lastFeedTimestamp
2026-04-06 02:05:19 -03:00