Commit Graph

1800 Commits

Author SHA1 Message Date
Alex Gleason 59311e6b2c Restore mobile bottom nav with scroll-to-hide behavior
Bring back the mobile bottom navigation bar with Home, Notifications, and
Search buttons. The nav slides out of view when scrolling down and reappears
on upward scroll, keeping the viewport uncluttered while reading. Search
retains the popover account search sheet for fast profile lookup without
navigating away. Updated CSS height calculations for full-screen pages
(AI chat, vines, livestream) and FAB positioning to account for the nav.
2026-03-13 22:47:17 -05:00
Alex Gleason 3727975178 Move 'Set a status' into profile menu on mobile drawer 2026-03-13 22:30:17 -05:00
Alex Gleason e95a63fcf6 Fix emoji picker touch scrolling on mobile in avatar shape dialog 2026-03-13 22:16:59 -05:00
Alex Gleason 2373848d2e Use drop-shadow border for emoji-shaped avatars on profile pages
Replace border/ring with a crisp drop-shadow outline (via emojiAvatarBorderStyle)
so the border follows the emoji mask shape without clipping. Shrink emoji avatars
by the border width so they match the visual footprint of bordered circular ones.
2026-03-13 21:58:45 -05:00
Alex Gleason f23faa7ef3 Fix mute list not being honored: parse public tags and support NIP-04
The mute list (kind 10000) was only reading encrypted content, ignoring
public tags in the event's tags array. Per NIP-51, mute items can be
stored as either public tags or private encrypted content. Most Nostr
clients store mutes as public tags, so these were being silently ignored.

Changes:
- Parse both public tags and encrypted content from kind 10000 events
- Add NIP-04 backward compatibility (detect by '?iv=' in ciphertext)
- Deduplicate items that appear in both public and private sections
- Apply same fixes to useInitialSync for login-time bootstrapping

Fixes: https://gitlab.com/soapbox-pub/ditto/-/work_items/96
2026-03-13 21:42:56 -05:00
Alex Gleason d6aec14936 Fix edit profile avatar/banner hover: keep pencil badge visible and replace camera icons with pencil 2026-03-13 21:23:10 -05:00
Alex Gleason 86cb2a28a8 Merge branch 'shapes' into 'main'
Emoji avatar shapes: use any emoji as an avatar mask via kind 0 metadata

See merge request soapbox-pub/ditto!68
2026-03-14 02:17:11 +00:00
Alex Gleason 10bb16abfb Clean up avatar shape code: remove magic strings, type casts, and inconsistencies
- avatar.tsx: consolidate to single isEmojiShape boolean, fix AvatarFallback using same logic as Avatar
- avatarShape.ts: accept NostrMetadata directly in getAvatarShape (no cast needed)
- Remove 'as Record<string, unknown>' casts from all ~50 call sites
- Replace 'circle' magic string with empty string in form defaults and parseShape
- Use isValidAvatarShape instead of string comparison in save logic
- ProfileCard: extract IIFE overlay style into useMemo, use isEmojiShape throughout
2026-03-13 21:14:03 -05:00
Alex Gleason b661e9be9b Remove predefined shape dead code: delete AvatarShapePicker, UserAvatar, and geometric shape utilities 2026-03-13 20:57:09 -05:00
Alex Gleason 6569478fc4 Remove implementation notes from avatar shape NIP section 2026-03-13 20:48:03 -05:00
Alex Gleason 91b96984cb Make NIP.md implementation notes platform-generic (web, iOS, Android) 2026-03-13 20:47:38 -05:00
Alex Gleason 196f3dd64e Update NIP.md: shape field is emoji-only, remove predefined shape references 2026-03-13 20:47:07 -05:00
Alex Gleason 3d5417cf97 Add 'Remove avatar shape' button to shape picker dialog 2026-03-13 20:40:15 -05:00
Alex Gleason a55baebd22 Use Dialog instead of Popover for avatar shape emoji picker 2026-03-13 20:36:51 -05:00
Alex Gleason 259dbcc20f Replace avatar click with dropdown menu: Change avatar, Set avatar shape, Remove avatar
Move avatar shape selection from standalone sections in EditProfileForm and
ProfileSettings into a dropdown menu on the ProfileCard avatar itself.
Clicking the avatar now shows a context menu instead of immediately opening
the file browser.
2026-03-13 20:35:15 -05:00
Alex Gleason 55e8075542 Support emoji as avatar shape: use any emoji as a mask over the avatar
Extend the kind-0 'shape' field to accept any emoji string in addition to
the predefined geometric shapes (circle, triangle, hexagon, star, etc.).
When an emoji is chosen, its native OS glyph is rendered onto a canvas,
the alpha channel extracted as a PNG mask, and applied via CSS mask-image.

Implementation:
- Widen AvatarShape type to accept emoji strings alongside predefined names
- isEmoji() detects any short non-ASCII string (avoids fragile Unicode regex)
- getEmojiMaskUrl() renders emoji at 512px, crops to tight bounding box
  (with alpha threshold to ignore shadows/glows), squares the crop, scales
  to 256px output, converts to white+alpha mask PNG. Cached per emoji.
- Avatar component applies mask-image for emoji, clip-path for geometric
- ProfileCard uses ring-4 instead of border-4 for emoji shapes to avoid
  mask/content misalignment (border is inside the element; ring is outside)
- AvatarShapePicker adds emoji-mart picker in a popover
- Form schemas use z.string() to accommodate both predefined and emoji values
- NIP.md documents emoji shape support
2026-03-13 20:27:14 -05:00
Alex Gleason 9e03579d0f Fix heart shape: use polygon approximation instead of path() which requires pixel coordinates
clip-path: path() uses pixel coordinates, making a 0-1 normalized path
invisible. Replaced with a parametric heart curve sampled into a 50-point
polygon using percentage coordinates, consistent with all other shapes.
2026-03-13 17:29:03 -05:00
Alex Gleason 61ea5ff1fe Add heart shape and document kind 0 shape extension in NIP.md
- Add 'heart' as the 8th avatar shape using SVG path() clip-path with cubic beziers
- Document the kind 0 'shape' metadata extension in NIP.md with all 8 defined values,
  client behavior rules, and forward-compatibility guidance
2026-03-13 17:25:50 -05:00
Alex Gleason 0781b1a87c Merge origin/main and re-apply avatar shape changes to conflicted files 2026-03-13 17:23:30 -05:00
Alex Gleason 6c67b34c60 npm audit fix 2026-03-13 17:08:49 -05:00
Alex Gleason f7c1ad05d0 Merge branch 'feature/development-feed' into 'main'
Development feed with NostrHub-style cards and Shakespeare integration

See merge request soapbox-pub/ditto!38
2026-03-13 21:46:01 +00:00
Dirk Rost b9c590598d Merge main into feature/development-feed and fix lint issues
Resolve merge conflicts from badges, books, help, and emojis rename.
Fix biome lint: add type="button" to all buttons, use template literals,
format new components with project conventions.
2026-03-13 16:34:36 -05:00
Alex Gleason 3b79b384e5 Add avatar shapes: users can set a shape (circle, triangle, hexagon, star, etc.) for their avatar via kind 0 metadata
- New 'shape' property on kind 0 profile metadata with 7 predefined shapes:
  circle, triangle, inverted-triangle, hexagon, star, inverted-star, hexagram
- Avatar component updated with clip-path support for non-circle shapes
- Visual shape picker added to both EditProfileForm and ProfileSettings
- Shape applied across all 49 avatar render sites in the app
- New UserAvatar wrapper component for future simplified avatar rendering
- Unknown shape values gracefully fall back to circle (forward compatible)
2026-03-13 16:14:40 -05:00
Alex Gleason d3d377fc2f Merge branch 'vite8' into 'main'
Upgrade to Vite 8 with Rolldown and switch to @vitejs/plugin-react v6

See merge request soapbox-pub/ditto!67
2026-03-13 21:11:26 +00:00
Alex Gleason 77d9375fca Upgrade to Vite 8 with Rolldown and switch to @vitejs/plugin-react v6
Replace Vite 6 (esbuild + Rollup) with Vite 8 (Rolldown), reducing
build times from ~12s to ~3.5s. Switch from the archived
@vitejs/plugin-react-swc to @vitejs/plugin-react v6 which uses Oxc
for React Refresh transforms.
2026-03-13 16:07:51 -05:00
Dirk Rost 980d4678c4 Merge branch 'fix/color-overflow' into 'main'
New Theme Modal Mobile Fixes

See merge request soapbox-pub/ditto!64
2026-03-13 20:33:24 +00:00
Dirk Rost 6ba1fb6bd2 Merge branch 'fix/yt-embed-res' into 'main'
Use sddefault.jpg for YouTube thumbnails for reliability

See merge request soapbox-pub/ditto!63
2026-03-13 20:33:23 +00:00
Lemon 7e19a7f773 New Theme Modal Mobile Fixes 2026-03-13 20:33:23 +00:00
Lemon 18a0b7dce0 Use sddefault.jpg for YouTube thumbnails for reliability 2026-03-13 20:33:23 +00:00
Alex Gleason a67337b798 Merge branch 'collage' into 'main'
Replace square media grids with justified collage layout using imeta dim tags

See merge request soapbox-pub/ditto!66
2026-03-13 20:26:37 +00:00
Dirk Rost 5ca5b975ea Merge branch 'fix/search-input-remount' into 'main'
Fix search page remounting on every keystroke

See merge request soapbox-pub/ditto!65
2026-03-13 20:03:57 +00:00
Alex Gleason d82bdc4613 Rename MediaGrid to MediaCollage
The component is a justified collage layout, not a grid. Renames the file,
component, skeleton, and props interface, and updates all imports.
2026-03-13 14:58:17 -05:00
Alex Gleason 487d2d778d Fix LightboxImage onLoad stability with useCallback instead of lint suppression
Pass markLoaded directly to LightboxSlot/LightboxImage as (url: string) => void,
and derive a stable handleLoaded callback via useCallback inside LightboxImage.
This removes the eslint-disable comment and properly declares all useEffect deps.
2026-03-13 14:54:23 -05:00
Alex Gleason a50782952e Fix infinite re-render loop in LightboxImage useEffect
The onLoad callback passed to LightboxImage was recreated on every render
(inline arrow function). When combined with a cached image triggering the
useEffect, this caused: effect fires → onLoad() → setState → re-render →
new onLoad ref → effect fires again. Remove onLoad from the dependency
array since the check only needs to run when src changes.
2026-03-13 14:38:11 -05:00
Alex Gleason 31770b9858 Drop orphan single-item trailing row in sidebar media collage 2026-03-13 14:32:46 -05:00
Alex Gleason 2286aa7994 Remove redundant spinner below media collage, keep only skeleton loading
The MediaGrid now renders its own skeleton rows when hasNextPage is true,
so the external Loader2 spinner is no longer needed. Keep the intersection
observer sentinel div for infinite scroll triggering.
2026-03-13 14:27:30 -05:00
Alex Gleason 641c85ed56 Hide orphan trailing row when more pages are loading, show skeleton instead
When hasNextPage is true and the last row is incomplete (not fully packed),
hide it to prevent oversized straggler thumbnails. Show 2 skeleton rows as
a loading placeholder. Once the next page loads, those items get properly
packed with new neighbors.
2026-03-13 14:21:43 -05:00
Alex Gleason 932483049e Fix collage row height: set aspectRatio on the row, not individual items
Items in a row now all stretch to the full row height. The row's aspect
ratio is the sum of its items' aspect ratios (since at equal height, total
width = sum of ARs * height). Individual items use flexGrow proportional
to their AR and h-full to fill the row.
2026-03-13 14:14:48 -05:00
Alex Gleason dd9405da24 Add matching padding around media collage grid 2026-03-13 14:12:51 -05:00
Alex Gleason 3e14f3ae9d Increase collage gap to 6px and add rounded corners to thumbnails 2026-03-13 14:11:54 -05:00
Alex Gleason da78949705 Make media collage responsive: larger thumbnails on mobile
On mobile (<768px), limit rows to 2 items max with a taller target row
height (45% vs 30%), so each image gets more screen real estate. Skeleton
loading state also adapts to show 2 items per row on mobile.
2026-03-13 14:09:26 -05:00
Alex Gleason 9b4019252d Fix collage layout: use flexGrow + aspectRatio instead of paddingBottom hack
The paddingBottom percentage approach doesn't work correctly in flex layouts
because percentages are relative to the containing block's width, not the
element's own computed width. Switch to CSS aspect-ratio property with
flexGrow proportional to each item's aspect ratio, which correctly sizes
items within flex rows.
2026-03-13 13:49:48 -05:00
Alex Gleason 07448b0cc7 Replace square media grids with justified collage layout using imeta dim tags
Use a Google Photos-style justified row layout that respects image aspect
ratios from imeta dim tags. Images without dim data default to 1:1 ratio.
Applies to both the profile Media tab (MediaGrid) and the sidebar Media
widget (ProfileRightSidebar).
2026-03-13 12:50:00 -05:00
Alex Gleason f5bfabe328 Replace lucide Menu icon with custom BarsStaggeredIcon in mobile top bar 2026-03-13 00:32:51 -05:00
Alex Gleason 2016e0444e fix: make nos.lol read-only since it requires POW for writes
nos.lol requires 28-bit proof-of-work for EVENT submissions, which the
app doesn't implement. Every publish attempt was failing against this
relay. Changed to read-only so we still benefit from reading its data
without broken write attempts.
2026-03-12 00:30:32 -05:00
Alex Gleason 635d52ca28 Fix theme selector z-index and cap preset count in sidebar dropdowns
The theme dropdown was appearing behind the account popover because
DropdownMenuContent (z-200) portals below PopoverContent (z-260).
Bumped theme dropdown z-index to 270 so it renders above. Also capped
featured preset themes to 5 in both sidebar dropdowns to keep the menu
manageable, with a 'More...' link for the full list.
2026-03-11 22:48:10 -05:00
Lemon 2700cf0188 Fix dropped characters during search typing
The URL→state sync effect was overwriting the input with stale
debounced values. When the debounce fired (e.g. 'hello'), it updated
?q in the URL, which triggered the reverse sync to overwrite the
input — even if the user had already typed further (e.g. 'hello w').

Use a ref guard so the URL→state sync only runs for external URL
changes (browser navigation, sidebar search), not for our own writes.
2026-03-11 20:29:30 -07:00
Lemon 6bd03f32ff Fix search page remounting on every keystroke
Remove SearchPageKeyed wrapper that used ?q as a React key, causing
the entire SearchPage to remount (and lose input focus) whenever the
URL query parameter changed. Debounce the search query before syncing
it to the URL and passing it to data-fetching hooks, preventing
excessive re-renders and relay queries while typing.
2026-03-11 20:26:04 -07:00
Alex Gleason b3b2e4f077 Move Plausible config into AppConfig (supports .env and ditto.json) 2026-03-11 14:35:47 -05:00
Alex Gleason 932398c0aa Add favicon.ico generated from logo.png 2026-03-11 13:03:14 -05:00