Commit Graph

4447 Commits

Author SHA1 Message Date
Alex Gleason ee08760a93 Remove unused sidebar CSS variables, Tailwind mappings, and shadcn sidebar components 2026-02-24 23:13:00 -06:00
Alex Gleason 73a6efb66f Merge branch 'main' of gitlab.com:soapbox-pub/ditto-mew 2026-02-24 23:06:43 -06:00
Alex Gleason 147bcdea73 Add sort:hot filter to homepage Global feed for better content quality 2026-02-24 23:06:35 -06:00
Chad Curtis de8211d27a Remove zap drawer; use Dialog modal on all screen sizes 2026-02-24 22:56:47 -06:00
Chad Curtis 6f583fd647 Probe image dimensions on load when dim tag is missing; hide media behind Read more truncation 2026-02-24 22:54:18 -06:00
Chad Curtis 582f92b208 Increase FAB plus icon stroke width 2026-02-24 22:41:59 -06:00
Alex Gleason 965f47c527 Replace Forest theme preset with Toxic 2026-02-24 22:16:36 -06:00
Alex Gleason 67f0dc32e7 Merge branch 'main' of gitlab.com:soapbox-pub/ditto-mew
# Conflicts:
#	src/components/AppProvider.tsx
#	src/hooks/useTheme.ts
2026-02-24 22:09:09 -06:00
Alex Gleason 72cf6bc6e8 Add extended theme presets (settings-only) and featured flag
Add 5 new presets: Midnight, Forest, Lavender, Ocean, and Sunset. These
appear only on the /settings/appearance page. The sidebar dropdown and
mobile drawer cycle continue to show only featured presets (Black and
Pink). Active preset detection searches all presets so the label and
emoji display correctly even for non-featured presets.
2026-02-24 22:03:32 -06:00
Chad Curtis c55c075434 Fix notifications re-triggering unread after being marked as read
Use an optimistic local cursor ref that updates immediately on markAsRead,
so newNotificationIds collapses to empty before the query cache catches up.
This prevents the NotificationsPage effect from re-calling markAsRead in a loop.
2026-02-24 22:02:41 -06:00
Chad Curtis 2e6beb3c23 Fix settings toggles being overwritten by stale sync
- Advance lastSyncedTimestamp when skipping due to recentlyWritten() so the
  same snapshot cannot be re-applied once the write window expires
- Use pendingSettings ref in useEncryptedSettings to accumulate rapid mutations
  instead of reading stale cache data
- Fix ContentSettings handleToggle to not merge against stale nostrSettings
2026-02-24 22:01:33 -06:00
Alex Gleason 5b23d0a656 Simplify theme dropdown 'More...' link: remove cog icon 2026-02-24 21:58:19 -06:00
Alex Gleason d6803cba04 Add 'More' link to theme dropdown and remove title
Navigate to /settings/appearance from the theme picker dropdown.
Drop the 'Choose theme' label to keep it compact.
2026-02-24 21:58:05 -06:00
Alex Gleason 228b5042d7 Use emoji for black theme preset 2026-02-24 21:56:11 -06:00
Alex Gleason 6328c6f6ae Remove builtin/custom labels from theme UI and add preset emojis
Drop the 'Builtin' and 'Custom' section headers from the ThemeSelector
grid and the LeftSidebar dropdown so the distinction is invisible to
users. Add emoji metadata to theme presets (ThemePreset interface) and
display them in the sidebar dropdown and mobile drawer cycle button
instead of the generic Palette icon.
2026-02-24 21:54:44 -06:00
Alex Gleason 7a85ec73ed Refactor theme system: separate builtin themes from custom presets
Restructure the theme architecture so 'light', 'dark', and 'system' are
builtin themes with build-time colors (customizable by self-hosters),
while 'black', 'pink', and future themes become presets that apply to a
single 'custom' theme slot.

Key changes:
- Theme type is now 'light' | 'dark' | 'system' | 'custom'
- builtinThemes (light/dark) and themePresets (black/pink/etc.) are
  separate maps in themes.ts
- AppConfig gains optional customTheme (ThemeTokens) field
- useTheme exposes applyCustomTheme(tokens) for applying presets
- Custom theme tokens sync across devices via encrypted settings
- Backward compat: legacy 'black'/'pink' values in localStorage and
  encrypted settings auto-migrate to custom + preset tokens
- All UI surfaces updated: ThemeSelector, LeftSidebar dropdown,
  MobileDrawer cycle, and onboarding ThemeStep
2026-02-24 21:46:28 -06:00
Chad Curtis 3c91952e5c Fix theme flicker by using useLayoutEffect for CSS var injection
useEffect fires after paint, causing a one-frame flash of old colors when switching themes. useLayoutEffect fires synchronously before paint, eliminating the flicker. Also keep document.documentElement.className in sync with the active theme.
2026-02-24 21:36:30 -06:00
Alex Gleason 02697385eb Merge branch 'main' of gitlab.com:soapbox-pub/ditto-mew 2026-02-24 21:10:14 -06:00
Chad Curtis dad8dc5040 Remove duplicate border-l from live stream chat sidebar 2026-02-24 20:57:11 -06:00
Chad Curtis 66a0388c0a Move column borders to MainLayout wrapper to fix top border gap
Borders were on each page's <main> element, causing them to start below the top of the column. Moving sidebar:border-l and xl:border-r to the wrapper div in MainLayout ensures borders span the full column height from the very top.
2026-02-24 20:57:11 -06:00
Chad Curtis 984bada0a4 Add middle-click to open posts in new tab, fix word breaking in compose box
- Add useOpenPost hook to centralize left/middle-click navigation logic
- Apply middle-click (aux click) support to NoteCard, hot posts sidebar, and streams feed
- Fix ComposeBox textarea using break-all instead of break-words
2026-02-24 20:57:11 -06:00
Alex Gleason b42ca9434a Add 'system' theme option that follows OS prefers-color-scheme 2026-02-24 20:56:17 -06:00
Alex Gleason fb2e2e6678 Add pointer cursor and hover underline to 'Replying to' label 2026-02-24 20:47:41 -06:00
Alex Gleason d4f2f05090 Scope reply hover card trigger to only the 'Replying to' text
The HoverCard trigger now wraps only the 'Replying to' label, not the
@username mentions. This prevents the post preview hover card from
conflicting with the profile hover cards on usernames.
2026-02-24 20:46:42 -06:00
Alex Gleason 64768ffafe Disable nested hover cards inside reply context preview
ProfileHoverCards on avatar, name, and @mentions inside EmbeddedNote
are now suppressed when rendered within the reply context hover card,
preventing nested hover card nesting.
2026-02-24 20:43:14 -06:00
Alex Gleason eb51f24d48 Add hover card preview of parent post on 'Replying to' line
Hovering over the 'Replying to @user' context on reply posts now shows
an embedded preview of the parent event, making it easy to see what is
being replied to without navigating away.

- Extract getParentEventId to shared utility (src/lib/nostrEvents.ts)
- Pass parentEventId from NoteCard to ReplyContext
- Wrap the reply context line in a HoverCard with EmbeddedNote preview
2026-02-24 20:40:28 -06:00
Chad Curtis be5249b67d Fix first-click toggle reset from stale closure in useLocalStorage
useLocalStorage's setValue captured 'state' at closure-definition time.
When multiple updateConfig calls fired before a re-render (e.g. NostrSync
and useInitialSync both calling updateConfig on startup), each call's
functional updater received the same stale state snapshot instead of the
accumulated result of previous calls — so later writes silently discarded
earlier ones.

Fixed by using React's built-in functional setState form (setState(prev =>
...)) which always receives the true current state from React's queue,
making all sequential updateConfig calls chain correctly regardless of
render timing.
2026-02-24 20:27:18 -06:00
Chad Curtis e8fe66f91c Fix settings toggles resetting due to sync race conditions
Three bugs caused settings toggles to get stuck or reset:

1. useEncryptedSettings: onSuccess was updating the parsedSettings cache
   under the old event ID, causing NostrSync to re-apply the stale remote
   event when the new one arrived with a different ID. Fixed by using the
   signed event's ID for both cache entries.

2. ContentTypeRow/SubKindRow: handleToggle was building the Nostr payload
   from the local React feedSettings state, which lags behind on rapid
   successive toggles. Fixed by merging against the Nostr-cached settings
   (nostrSettings?.feedSettings) as the base instead.

3. NostrSync: lastSyncedTimestamp ref reset to 0 on every page reload, so
   the 10-second recentlyWritten() guard was the only protection against
   stale relay events overwriting local changes. Fixed by seeding the ref
   with the current remote lastSync on first settings load, skipping
   application of any event that isn't strictly newer than what was
   already in the cache.
2026-02-24 20:24:43 -06:00
Alex Gleason 6d4714f2c9 Merge branch 'settings-reorganize' into 'main'
Reorganize Settings into menu-based navigation with sub-pages

See merge request soapbox-pub/ditto-mew!13
2026-02-25 02:02:31 +00:00
Mary Kate Fain df269b2520 Add 'Edit theme' option to own profile's three-dot menu
Shows a Palette-icon menu item in the ProfileMoreMenu when viewing
your own profile. Clicking it navigates to /settings/appearance.
2026-02-24 19:47:06 -06:00
Alex Gleason 5d008e3745 Fix Bookmarks sticky header losing padding on scroll 2026-02-24 19:44:29 -06:00
Mary Kate Fain 2ac9960b31 Reorganize Settings into menu-based navigation with sub-pages
Replace the single tabbed Settings page with a landing menu that navigates
to dedicated sub-pages, following the Agora pattern:

- Settings landing: card-based menu with icons and descriptions
- Profile: wraps EditProfileForm (auth-gated)
- Appearance: Theme sub-tab with visual theme selector (light/dark/black/pink
  + custom placeholder) and Content sub-tab with existing content settings
- Wallet: dedicated page for NWC wallet management (auth-gated)
- Notifications: push notification enable/disable toggle (auth-gated)
- Advanced: network relays, stats source, and system settings (wallet
  section removed since it has its own page now)
2026-02-24 19:42:40 -06:00
Alex Gleason e66222a7fc Remove descriptions from sidebar Add dropdown, keep only names 2026-02-24 19:31:59 -06:00
Alex Gleason 4c10c954c5 Use bottom-20 for mobile FAB position 2026-02-24 19:27:45 -06:00
Alex Gleason 7401022b05 Add spacing-22 to Tailwind config and use bottom-22 for mobile FAB 2026-02-24 19:27:34 -06:00
Alex Gleason 3b5f53eaa8 Fix FAB mobile position: use bottom-24 (valid Tailwind v3 spacing) 2026-02-24 19:26:10 -06:00
Alex Gleason 9d6eea7869 Fix FAB position on mobile to sit above bottom navigation bar 2026-02-24 19:25:25 -06:00
Alex Gleason 4fa5e391c7 Add floating compose button (+) to center column on all feed pages
- FloatingComposeButton accepts kind prop: kind=1 opens compose dialog, others show 'Coming soon'
- FAB positioned sticky bottom-right within center column (visible on all screen sizes)
- Center column gets bottom padding (pb-24) to account for FAB obstruction
- LayoutContext extended with fabKind option passed through layout system
- Home feed uses kind=1, KindFeedPage passes its kind, StreamsFeedPage uses 30311
2026-02-24 19:11:42 -06:00
Alex Gleason b5e813917c Replace Compose button with Add button to enable sidebar items from settings/content 2026-02-24 19:00:54 -06:00
Chad Curtis 42a9535761 fix: remove lazy loading to prevent endless spinner on direct event load
The endless spinner issue was caused by lazy loading pages without a Suspense
fallback. When users navigated to nevent URLs or other NIP-19 identifiers, the
Suspense boundary had no fallback, causing a blank screen or stuck preloader.

Changes:
- Removed all lazy() imports from AppRouter
- Removed Suspense boundary (no longer needed without lazy loading)
- All pages now load eagerly for instant rendering
- Kept the HexIdentifierPage loading skeleton fix from previous commit

This ensures users see immediate visual feedback when loading any page,
especially when navigating directly to event URLs via nevent identifiers.
2026-02-24 23:48:09 +00:00
Chad Curtis 9bd8f24829 fix: show loading skeleton instead of null for hex identifier resolution
When users navigate to a raw 64-char hex URL (event ID or pubkey), the app
now shows a proper loading skeleton while resolving the identifier type,
instead of returning null which caused an endless spinner due to Suspense.

- Export PostDetailShell and PostDetailSkeleton from PostDetailPage
- Use proper loading UI in HexIdentifierPage component
- Matches loading pattern used by PostDetailPage and AddrPostDetailPage
2026-02-24 23:42:59 +00:00
Alex Gleason fb2e5cee91 Add Divine relay when looking up addressable short videos 2026-02-24 17:14:44 -06:00
Alex Gleason 386a2fec65 Use kind 16 (generic repost) for reposting non-kind-1 events
Per NIP-18, kind 6 is specifically for reposting kind 1 text notes,
while kind 16 should be used for all other event types. Added
isRepostKind() and getRepostKind() helpers to centralize the logic,
and updated all repost-related code paths: publishing, deletion,
status checks, interactions, feed processing, notifications, and
stream/search filtering.
2026-02-24 15:22:19 -06:00
Chad Curtis 1af6a45a77 Fix preloader flash and green logo on light/pink themes
Defer body inline style removal until CSS variables are injected by
useApplyTheme, preventing a one-frame flash of unstyled background.

Replace preloader logo <img> with CSS mask technique matching the React
DittoLogo component, so the logo uses the theme's primary color instead
of filter:invert() which turned purple into green.
2026-02-24 00:39:27 -06:00
Alex Gleason b61f315a59 Generate ephemeral Nostr keypair for logged-out webxdc sessions
When no user is logged in, generate a random keypair once per hook instance
and use it to sign webxdc events (kind 4932 state updates and kind 20932
realtime). selfAddr/selfName are now derived from the ephemeral key instead
of returning empty strings.
2026-02-24 00:01:49 -06:00
Alex Gleason 805a35fea5 Add dim and blurhash to imeta tags for uploaded images
After a file uploads, decode it on a small canvas (64px wide) to
compute the natural dimensions (dim) and a 4x3 blurhash. Both are
injected into the NIP-94 tag array before storing in uploadedFileGroups,
so the published imeta tag includes them automatically.
2026-02-23 23:13:17 -06:00
Alex Gleason eabaaa3fd5 Fix VideoPlayer letterboxing when dim is known
When the container has an aspect-ratio from the dim tag, switch the
video to absolute inset-0 h-full to fill it cleanly. Fall back to
max-h-[70vh] object-cover for videos without dim data.
2026-02-23 23:08:32 -06:00
Alex Gleason fa7c141382 Add blurhash placeholders to profile Media widget
- Extract blurhash from imeta tags for all event kinds (non-kind-1
  via the first imeta tag; kind-1 by matching the URL against imeta)
- New MediaTile component renders Blurhash canvas before the image/video
  loads, falling back to a Skeleton when no blurhash is available
- Tile uses absolute positioning so the placeholder fills the square cell
2026-02-23 23:07:18 -06:00
Alex Gleason 15ae7c4735 Fix GridImage dim/blurhash placeholder sizing
- Move aspect-ratio and height constraints onto the button container so
  both placeholder and image can fill it with absolute inset-0
- Pass integer decode resolution to Blurhash and size the canvas via
  CSS width/height 100% instead of relying on string props
- Remove stale per-element skeletonStyle/heightStyle juggling
2026-02-23 23:02:41 -06:00
Alex Gleason 8febb405e5 Use imeta dim/blurhash for media placeholders before load
- Parse NIP-94 dim and blurhash fields in parseImetaMap (NoteCard + PostDetailPage)
- ImageGallery/GridImage: use dim for correct aspect-ratio skeleton sizing; render Blurhash canvas placeholder when available
- VideoPlayer: set aspect-ratio container from dim; show Blurhash canvas before first frame/poster loads
- Install react-blurhash (+ blurhash) for canvas rendering
2026-02-23 22:58:56 -06:00