Commit Graph

4447 Commits

Author SHA1 Message Date
Derek Ross bbe53a4c69 Filter Mentions tab to only show pure mentions, not replies
The Mentions tab now excludes kind 1 reply events (those with NIP-10
reply/root e-tags), showing only pure mentions where someone tagged the
user in a new post. Kind 1111 comments continue to appear in both tabs.
2026-03-22 15:54:41 -04:00
Alex Gleason 590bc6689e Fix archive.org metadata parsing: response is wrapped in { result: [] } 2026-03-22 14:53:00 -05:00
Derek Ross 88d9e783b8 Distinguish replies from mentions in notifications
Kind 1 events that are replies (have NIP-10 reply/root e-tags) now show
a reply icon with 'replied to your note' instead of the '@' icon with
'mentioned you'. Only pure mentions (no reply threading) use the mention
label.
2026-03-22 15:50:11 -04:00
Alex Gleason 81c5d7f275 Fetch correct aspect ratio for archive.org embeds from metadata API 2026-03-22 14:47:33 -05:00
Derek Ross 854f9aca23 Fix notification UX issues: real-time updates, zap/reaction visibility, comment counts
- Fix silent notification drops: reactions, reposts, and zaps were being
  discarded when the referenced event couldn't be fetched from relays.
  Now keeps notifications with missing context instead of hiding them.
  Zaps no longer require the author-ownership check since the #p filter
  already confirms the user is the recipient. (fixes tabs appearing
  identical and missing zap notifications)

- Add real-time WebSocket subscriptions for instant notification updates
  instead of relying solely on 60-second polling. Both the full
  notification list and the unread dot indicator now react immediately
  when new events arrive.

- Wire up zap amounts from NIP-85 stats (zap_amount tag on kind 30383)
  through to the NoteCard action bar, replacing the hardcoded 0.

- Seed client-side reply counts into the event-stats cache from the
  loaded comment tree in PostDetailPage, ensuring sub-comment counts
  are visible even when NIP-85 stats are unavailable for kind 1111.

Closes #136
2026-03-22 15:42:45 -04:00
Alex Gleason 489a7bba10 Add Internet Archive embed support with click-to-load facade 2026-03-22 14:41:57 -05:00
Alex Gleason 7522ff13f7 Show actual page title on link pages instead of hostname 2026-03-22 14:37:59 -05:00
Alex Gleason d64a75f7ae Move Nostr identifiers from submit-redirect to autocomplete suggestions
Enter/submit now always performs a text search instead of redirecting
when the query looks like a NIP-05, npub, nevent, etc.

Identifiers are detected in real-time and shown as autocomplete items:
- NIP-05 (user@domain.com, domain.com): resolves to profile via .well-known
- npub/nprofile: shows profile with avatar and metadata
- note/nevent: shows event content preview with author
- naddr: shows addressable event with title/content preview
- hex: shows navigable link to the identifier

Resolved profiles are deduplicated from regular search results.
Works in both desktop sidebar and mobile bottom sheet search.
2026-03-22 14:26:18 -05:00
Alex Gleason 10972064a6 Clean up LinkPreview/LinkEmbed props for consistency
Replace the confusing externalLink/hideActions/showDiscuss props with
a clearer model:

- navigateToComments: opt-in to /i/ navigation (default: card opens
  the URL externally, which is the natural expectation)
- showActions: single prop controlling the action button across both
  LinkPreview and DiscussBar (default: true)

The action button always offers the opposite of what clicking the card
does: 'Discuss' when the card opens externally, 'Open' when the card
navigates to /i/.
2026-03-22 14:04:26 -05:00
Alex Gleason ed26f25f50 Restore Discuss button on link previews in posts, hide on /i/ page
Link previews now show the correct action button based on context:
- In posts (externalLink): card opens externally, shows 'Discuss' button
- In hover cards (!externalLink): card navigates to /i/, shows 'Open' button
- On /i/ page (hideActions): no action button since both would be redundant
2026-03-22 13:59:41 -05:00
Alex Gleason e9e8104b1f Fix link previews in posts opening /i/ instead of externally
LinkEmbed now defaults externalLink to true so link previews embedded
in post content open the URL externally as expected. Only standalone
uses of LinkPreview (like the CommentContext hover card) navigate to
the /i/ comment page.
2026-03-22 13:58:02 -05:00
Alex Gleason eb7f2d1d0d Remove redundant ExternalLink icon next to domain name in link preview 2026-03-22 13:56:39 -05:00
Alex Gleason 669bcf3175 Add 'Open' button to link preview for opening URL externally
When the card navigates to /i/ (the default), show an 'Open' button
with an external link icon that opens the original URL in a new tab.
Hidden when externalLink is true since the whole card already opens
the URL externally.
2026-03-22 13:55:39 -05:00
Alex Gleason e5ef738bf4 Fix hover card link preview navigating externally instead of to /i/ page 2026-03-22 13:54:19 -05:00
Alex Gleason 1e48489978 Make link preview cards navigate to /i/ comment page on click
Remove the Discuss button from LinkPreview since the whole card now
navigates to /i/:url. Add externalLink prop so the card opens the URL
in a new tab instead when already on the /i/ page or in a hover card.
2026-03-22 13:53:08 -05:00
Alex Gleason 45870fcf11 Add link preview hover card to URL comment context 2026-03-22 13:50:25 -05:00
Alex Gleason 370ff05bdb Show page title with favicon in URL comment context
Fetch OEmbed link preview for URL identifiers in CommentContext so
'Commenting on' displays the page title alongside the site favicon
instead of a raw hostname+pathname.
2026-03-22 13:49:16 -05:00
Alex Gleason 61cb27a465 Show page title instead of 'Comment on this link' in URL search option 2026-03-22 13:47:26 -05:00
Alex Gleason af2b68eddd Add 'Comment on this link' option to search autocomplete for URLs
When a full URL (http/https) is entered in the search bar, a new
'Comment on this link' option appears below the existing 'Search for'
option. Clicking it navigates to /i/<url> for commenting. The option
shows a link preview thumbnail (fetched via OEmbed) on the left where
avatars usually are, with the site's favicon as fallback. Works in
both the desktop sidebar dropdown and mobile bottom nav search sheet.
Keyboard navigation updated to support the new item.
2026-03-22 13:28:58 -05:00
Alex Gleason 149955e099 Fix profile timeline gaps by over-fetching and filtering kinds per tab
The profile feed queried all enabled kinds (20+) with limit:15, but the
Posts tab filters client-side to only show non-reply posts. Events from
other kinds (badges, themes, emojis, etc.) consumed the limit, and the
pagination cursor jumped over time ranges containing visible posts.

Three-pronged fix:
- Filter kinds per active tab at query time (exclude kind 1111 on Posts)
- Over-fetch 3x the page size to compensate for reply filtering
- Auto-fetch next page when visible items fall below threshold

Closes #146
2026-03-22 00:22:29 -05:00
Alex Gleason 85b47a7a62 Consolidate 4 duplicate vanish compact cards into VanishCardCompact
The same stripes + glitch icon + title + npub + reason layout was
duplicated across EmbeddedVanishCard, EmbeddedVanishPost,
VanishEventContent compact mode, and an inline version in NoteCard's
threaded branch. Extract a single VanishCardCompact component and
replace all four copies with it.

Net reduction of 76 lines.
2026-03-21 22:39:13 -05:00
Alex Gleason 58b28f7c1a Fix vanish card stripes overflowing in threaded ancestor view
The vanish layout (kind 62) in NoteCard didn't handle the threaded/
threadedLast props, so when rendered as a parent in AncestorThread,
it used the standard feed card layout where the caution stripes are
siblings of the bordered container — visually appearing to escape
above and below the card.

Add a dedicated threaded branch for vanish cards that:
- Uses the avatar column + connector line pattern like other threaded cards
- Places the stripes INSIDE the bordered container (as top/bottom bands)
  so they stay visually contained within the card boundary
2026-03-21 22:33:37 -05:00
Alex Gleason eab19f0d72 Add mute list recovery dialog to /settings/content
Allow users to browse and restore historical versions of their
kind 10000 mute list, similar to the existing Profile Recovery
dialog. Each snapshot is decrypted and displays a summary of
muted items by type (users, hashtags, words, threads).
2026-03-21 22:23:53 -05:00
Alex Gleason 2942af1823 Remove Sentry session replay integration
The replay integration hooks into every DOM mutation via rrwebWrapped
event listeners, adding ~133ms of overhead during heavy re-renders.
We only use Sentry for error reporting, not session replay.
2026-03-21 22:09:57 -05:00
Alex Gleason d980a8af5b Fix performance regression: cache parsed metadata and memoize NoteCard
The IndexedDB profile cache (d9e943d0) introduced a regression where
parseAuthorEvent() — which runs the full Zod n.metadata().parse() chain —
was called synchronously on every render via TanStack Query's initialData
option. With 50+ NoteCards in a feed and no React.memo, this caused ~970ms
of Zod validation + ~1.3s of Nostrify core overhead per tab navigation.

Fix by:
1. Storing pre-parsed metadata in ProfileCacheEntry so useAuthor can
   provide initialData without re-running Zod on every render
2. Wrapping NoteCard in React.memo to prevent re-renders when props
   haven't changed (event objects are referentially stable from the
   query cache)
2026-03-21 21:58:02 -05:00
Alex Gleason 0451895308 Merge branch 'main' of gitlab.com:soapbox-pub/ditto 2026-03-21 20:57:33 -05:00
Alex Gleason 96e52c1427 Add Follows tab to Profile Recovery dialog for restoring prior kind 3 follow lists 2026-03-21 20:55:05 -05:00
Alex Gleason 77ecd7c417 Apply avatar shape to profile snapshots in recovery dialog 2026-03-21 20:52:11 -05:00
Alex Gleason 2ac38cb0f5 Use req() instead of query() for recovery to bypass NSet replaceable deduplication 2026-03-21 20:48:42 -05:00
Dirk Rost 0c519a9cd9 Merge branch 'fix/account-switch-stale-data' into 'main'
Fix stale user data persisting across account switches

Closes #94

See merge request soapbox-pub/ditto!95
2026-03-22 01:48:13 +00:00
Mary Kate 8dd9b72630 Fix stale user data persisting across account switches 2026-03-22 01:48:13 +00:00
Alex Gleason d42a8f19fe Skip batcher for replaceable queries without an explicit limit 2026-03-21 20:43:35 -05:00
Alex Gleason 4e02d45081 Fix profile recovery queries getting batched by splitting tabs into separate components 2026-03-21 20:39:11 -05:00
Alex Gleason 8c570ed106 Add Profile Recovery dialog to restore historical profiles and themes 2026-03-21 20:12:52 -05:00
filemon 69dde41d9c fix(blobbi): rebalance daily mission rewards and persist to kind 31125
- Rebalance rewards: interact_6=30, feed_2=20, clean_1=20, sing_1=25,
  play_music_1=25, sleep_1=20, change_shape_1=40, take_photo_1=35
- Total daily reward now 75-105 coins (was 240-330)
- Add useClaimMissionReward hook to persist coins to Blobbonaut profile
- Claim flow now updates kind 31125 event, not just localStorage
- Add idempotency check to prevent double-crediting
- Update query cache and invalidate after successful claim
- Pass profile and updateProfileEvent to BlobbiMissionsModal
2026-03-21 21:55:18 -03:00
filemon e159e5bb6d feat(blobbi): implement daily missions system
- Add mission pool with 8 missions (interact, feed, clean, sing, play_music, sleep, take_photo, change_shape)
- Implement weighted random selection for 3 daily missions per user
- Add localStorage persistence with automatic daily reset
- Track progress from all Blobbi actions (inventory, direct actions, sleep, photo, profile shape)
- Add DailyMissionsPanel UI component with progress bars and claim buttons
- Integrate daily missions section into BlobbiMissionsModal
- Each mission rewards 80-110 coins depending on difficulty
2026-03-21 21:43:42 -03:00
filemon fce3c81029 fix(blobbi): ensure Blobbi eyes are always open in photo exports
Added disableBlink option to prevent Blobbi from being captured mid-blink
when downloading or posting photos.

Changes:
- useBlobbiEyes: Added disableBlink option that keeps blinkScaleY at 1 (fully open)
- BlobbiBabyVisual: Added disableBlink prop, passed to useBlobbiEyes
- BlobbiAdultVisual: Added disableBlink prop, passed to useBlobbiEyes
- BlobbiStageVisual: Added disableBlink prop, passed to child visuals
- BlobbiPolaroidCard: Now uses disableBlink={true} alongside lookMode='forward'

Photo mode behavior:
- lookMode='forward': Eyes look straight ahead (no mouse tracking)
- disableBlink={true}: Eyes stay fully open (no blinking animation)
- animated={false}: No ambient animations

Normal dashboard behavior unchanged - Blobbi still blinks and follows pointer.
2026-03-21 20:56:13 -03:00
filemon b14717eddb fix(blobbi): ensure polaroid export matches modal preview exactly
The date text was wrapping to a second line in the exported PNG but not
in the modal preview. This was caused by html-to-image rendering text
differently when using Tailwind classes.

Fixes:
- Convert caption area to inline styles for consistent html-to-image export
- Add whitespace: nowrap to date, stage badge, and caption elements
- Add explicit width constraint to caption container
- Use inline styles instead of Tailwind classes for all caption text

This ensures the downloaded PNG and Blossom-posted image match the
modal preview exactly, with the date staying on one line.
2026-03-21 20:50:23 -03:00
filemon 5891014ff6 refactor(blobbi): refine polaroid photo implementation
1. Polaroid layout - now looks like a real polaroid:
   - White frame on ALL sides (top: 16px, sides: 16px, bottom: 80px)
   - Photo area is inset within the white frame
   - Caption area positioned at bottom of frame
   - Consistent off-white background (#fafafa) for export

2. Removed noisy debug logs:
   - Removed [BlobbiStageVisual][baby] and [adult] console.logs
   - These were render-time logs that added noise without value

3. Improved export/post robustness:
   - URL extraction now explicitly finds 'url' tag instead of assuming [0][1]
   - Added error handling if upload returns no URL
   - Safer parsing that doesn't depend on tag order

4. Verified visual export consistency:
   - White frame visible on all sides
   - Blobbi centered in photo area
   - No clipping or gaps
   - Caption properly positioned
2026-03-21 20:39:50 -03:00
filemon b51535bfa0 feat(blobbi): implement Blobbi Photo (Polaroid) feature
Add the ability to take polaroid-style photos of Blobbis and share them:

- Add lookMode prop to Blobbi rendering system:
  - 'follow-pointer': Eyes track mouse cursor (default, existing behavior)
  - 'forward': Eyes look straight ahead (for photos/export)
  - Updated useBlobbiEyes, BlobbiBabyVisual, BlobbiAdultVisual, BlobbiStageVisual

- Create BlobbiPolaroidCard component:
  - Classic polaroid-style frame with white background and shadow
  - Soft gradient background for photo area
  - Caption area with Blobbi name, stage, and date
  - Fixed dimensions (320x400) for consistent export
  - Built with HTML+CSS (not canvas) for easy customization

- Create BlobbiPhotoModal component:
  - Opens from 'Take a Photo' button on BlobbiPage
  - Shows polaroid preview with Blobbi looking forward
  - Download button: exports as PNG using html-to-image
  - Post button: uploads to Blossom and creates kind 1 note
  - Clean, minimal UI focused on the photo

- Wire up to BlobbiPage:
  - Photo modal state and handler
  - Connected to floating 'Take a Photo' action button

Dependencies:
- Added html-to-image for DOM-to-PNG conversion
2026-03-21 20:29:03 -03:00
filemon 8476edd18f chore(blobbi): temporarily hide unimplemented floating action buttons
Hide 'Set as Companion' and 'Open PiP' buttons from BlobbiDashboardFloatingControls
until their features are implemented. Code is commented out with TODO markers for
easy re-enablement later.

Remaining visible buttons: Take a Photo, Info, Hatch/Evolve actions
2026-03-21 20:15:04 -03:00
filemon e528cdb36d feat(blobbi): add stage-aware validation and semantic cleanup
- Detect final stage from tags and validate tags against stage constraints
- Remove tags not valid for the detected stage (e.g., adult_type only on adults)
- Fix state after transitions (incubating/evolving -> active)
- Validate state is valid for the stage (per VALID_STATES_BY_STAGE)
- Skip required tag recovery for tags not valid for current stage
- Skip persistent tag recovery for tags not valid for current stage
- Add dev diagnostics with console.warn when repairs are applied
- Return finalStage in TagRepairResult for caller inspection
2026-03-21 20:08:56 -03:00
filemon 8696c698ed feat(blobbi): implement tag integrity guard for all Blobbi events
Add validateAndRepairBlobbiTags function that ensures tag integrity
whenever a Blobbi is republished. The guard:

1. Validates tags against the canonical schema
2. Removes deprecated tags automatically
3. Removes task-related tags during stage transitions (when requested)
4. Recovers missing required tags from:
   - Previous canonical tags (if available)
   - System defaults (for b, t, client only)
5. Preserves all persistent tags from previous state
6. NEVER invents personality/trait/adult_type values

Integration points:
- mergeBlobbiStateTagsForRepublish: validates all tag merges
- useBlobbiHatch: validates with task cleanup before publishing
- useBlobbiEvolve: validates with task cleanup before publishing
- buildMigrationTags: validates migration output

Repair strategy:
- System tags (b, t, client): recover from defaults
- Identity tags (name, seed, d): recover from previous only, never invent
- Personality tags: preserve if exist, never invent
- Visual tags: preserve (regenerable from seed if needed)
- Stats: preserve current values
- Task tags: cleanup during transitions
2026-03-21 20:02:20 -03:00
filemon f9de5282c9 fix(blobbi): preserve personality/extension tags in migration flow
Update buildMigrationTags to preserve all persistent tags when migrating
legacy Blobbis to canonical format:

- personality, trait, favorite_food, voice_type, mood
- adult_type
- theme, crossover_app

Per blobbi-tag-schema.md spec: Do NOT invent values for tags that don't
exist. Only preserve values that are already present in the legacy event.

Also adds docs/blobbi/blobbi-tag-schema.md as the product spec for all
Blobbi tag definitions. The runtime schema in blobbi-tag-schema.ts MUST
align with this spec.
2026-03-21 19:49:53 -03:00
filemon a4c2895c68 docs(blobbi): add canonical tag schema for Blobbi events
Create blobbi-tag-schema.ts as the single source of truth for all
Blobbi tag definitions. The schema documents:

- 35 canonical tags organized into 11 categories
- Tag metadata: required, stages, persistent, source, regenerable
- 8 deprecated tags with migration guidance
- Helper functions for schema queries and validation

Categories:
- system: d, b, t, client
- identity: name, seed, generation
- visual: base_color, secondary_color, eye_color, pattern, special_mark, size
- personality: personality, trait, favorite_food, voice_type, mood
- stats: hunger, happiness, health, hygiene, energy
- state: stage, state, last_interaction, last_decay_at
- task: state_started_at, task, task_completed
- progression: experience, care_streak
- social: visible_to_others, breeding_ready
- evolution: adult_type
- extension: theme, crossover_app

Also updates MANAGED_BLOBBI_STATE_TAG_NAMES to include extension
tags (theme, crossover_app) and reorganizes comments by category.
2026-03-21 19:39:34 -03:00
filemon e33ee800bc fix(blobbi): preserve identity attributes across stage transitions
- Add identity/personality tags (personality, trait, favorite_food,
  voice_type, mood, adult_type) to MANAGED_BLOBBI_STATE_TAG_NAMES
- Add 'interact_6_progress' to DEPRECATED_BLOBBI_TAG_NAMES to remove
  legacy interaction tracking
- Update hatch flow to clean only task/state-specific tags while
  preserving all identity attributes from canonical.allTags
- Update evolve flow to clean only task/state-specific tags while
  preserving all identity attributes, and set state to 'active'

This ensures Blobbi identity persists across egg → baby → adult
transitions, treating them as persistent entities rather than
reconstructed objects at each stage.
2026-03-21 19:35:53 -03:00
filemon 24aa80840c Merge branch 'main' into feat-blobbi 2026-03-21 19:24:47 -03:00
The Daniel d9aa6258cd fix: allow nsec paste in login field 2026-03-21 18:07:46 -04:00
The Daniel 941e6ee4e6 feat: block nsec paste in all input fields with warning toast
Adds a global capture-phase paste listener that detects nsec private
keys and prevents them from being pasted into any field. Shows a
destructive toast warning the user that private keys should never
be shared.
2026-03-21 18:03:17 -04:00
Alex Gleason 5a960ff31c Auto-prepend wss:// to relay URLs missing a protocol prefix 2026-03-21 13:54:04 -05:00