feat: avatar decorations follow-up — Nextcloud CDN, sync script, docs
- Point DECORATION_CDN at Lotus Nextcloud WebDAV share instead of external avatardecoration.com; all 99 APNG files are self-hosted and served via direct DAV URL (no CORS issue for <img> elements) - Add onError handler to AvatarDecoration.tsx to silently hide the overlay if a file is missing or the CDN is unreachable - Rewrite scripts/syncDecorations.mjs: now sends HTTP HEAD requests to the live Nextcloud CDN (batches of 16 in parallel) and removes catalog entries for files that return non-2xx; empty categories are pruned automatically. Workflow: delete files from Nextcloud → run `npm run sync:decorations` → commit the updated avatarDecorations.ts. No local files needed. - Add public/decorations/ to .gitignore; delete the 85 MB local APNG cache that was downloaded during development (files live on Nextcloud now) - Add sync:decorations script to package.json - Update LOTUS_FEATURES.md, LOTUS_TODO.md (P5-13 + P5-14 ✓), README.md with avatar decoration documentation and catalog sync workflow Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+89
-14
@@ -11,20 +11,21 @@ Last updated: June 2026.
|
||||
2. [LotusGuild Terminal Design System (TDS) v1.2](#lotusguild-terminal-design-system-tds-v12)
|
||||
3. [Animated Chat Backgrounds (P5-4)](#animated-chat-backgrounds-p5-4)
|
||||
4. [Seasonal Theme Overlays (P5-12)](#seasonal-theme-overlays-p5-12)
|
||||
5. [Glassmorphism Sidebar (P5-3)](#glassmorphism-sidebar-p5-3)
|
||||
6. [Night Light / Blue Light Filter (P5-5)](#night-light--blue-light-filter-p5-5)
|
||||
7. [Voice / Video Call Improvements](#voice--video-call-improvements)
|
||||
8. [Per-Message Read Receipts](#per-message-read-receipts)
|
||||
9. [Delivery Status Indicators](#delivery-status-indicators)
|
||||
10. [Messaging Enhancements](#messaging-enhancements)
|
||||
11. [Presence](#presence)
|
||||
12. [UX & Composer](#ux--composer)
|
||||
13. [Room Customization](#room-customization)
|
||||
14. [Moderation](#moderation)
|
||||
15. [Notifications](#notifications)
|
||||
16. [Server Integration](#server-integration)
|
||||
17. [Infrastructure](#infrastructure)
|
||||
18. [Key Custom Files](#key-custom-files)
|
||||
5. [Avatar Decorations (P5-13/P5-14)](#avatar-decorations-p5-13p5-14)
|
||||
6. [Glassmorphism Sidebar (P5-3)](#glassmorphism-sidebar-p5-3)
|
||||
7. [Night Light / Blue Light Filter (P5-5)](#night-light--blue-light-filter-p5-5)
|
||||
8. [Voice / Video Call Improvements](#voice--video-call-improvements)
|
||||
9. [Per-Message Read Receipts](#per-message-read-receipts)
|
||||
10. [Delivery Status Indicators](#delivery-status-indicators)
|
||||
11. [Messaging Enhancements](#messaging-enhancements)
|
||||
12. [Presence](#presence)
|
||||
13. [UX & Composer](#ux--composer)
|
||||
14. [Room Customization](#room-customization)
|
||||
15. [Moderation](#moderation)
|
||||
16. [Notifications](#notifications)
|
||||
17. [Server Integration](#server-integration)
|
||||
18. [Infrastructure](#infrastructure)
|
||||
19. [Key Custom Files](#key-custom-files)
|
||||
|
||||
---
|
||||
|
||||
@@ -200,6 +201,75 @@ Decorative CSS-only overlays that activate automatically on holidays and events.
|
||||
|
||||
---
|
||||
|
||||
## Avatar Decorations (P5-13/P5-14)
|
||||
|
||||
Animated APNG overlay frames that float around user avatars, inspired by Discord's Avatar Decorations feature. Each decoration extends 8px beyond the avatar border on all sides, with a transparent center hole that reveals the avatar beneath. Other Lotus Chat users see your selected decoration in real time — stored in the Matrix profile via MSC4133.
|
||||
|
||||
### Decoration Library
|
||||
|
||||
99 hand-curated, original-IP decorations (no licensed character artwork) organized into 9 categories:
|
||||
|
||||
| Category | Count | Highlights |
|
||||
|---|---|---|
|
||||
| Gaming | 13 | Slither 'n Snack, Joystick, Space Invaders, Gaming Headsets |
|
||||
| Cyber | 9 | Cybernetic, Glitch, Digital Sunrise, Futuristic UI (3 colors) |
|
||||
| Space | 8 | Black Hole, Constellations, Solar Orbit, Aurora |
|
||||
| Fantasy | 22 | Kitsune, Phoenix, Glowing Runes, D&D dice, Crystal Balls |
|
||||
| Elements | 7 | Fire, Water, Air, Earth, Lightning, Ki Energy |
|
||||
| Japanese | 6 | Kabuto, Oni Mask, Sakura Warrior, Straw Hat |
|
||||
| Nature | 12 | **Lotus Flower**, Koi Pond, Sakura, Fall Leaves, Fireflies |
|
||||
| Spooky | 13 | Candlelight, Witch Hat, Ghosts, Jack-o'-Lantern |
|
||||
| Cozy | 11 | Cozy Cat, Fox Hat (3 colors), Cat Ears, Frog Hat |
|
||||
|
||||
All decoration files are 256×256 APNGs. They animate natively in all modern browsers via `<img>` elements.
|
||||
|
||||
### Architecture
|
||||
|
||||
**Profile storage — MSC4133:**
|
||||
Decoration preference is stored in the public Matrix profile field `io.lotus.avatar_decoration` (a slug string, e.g. `lotus_flower`). Any Lotus Chat user viewing your profile sees your current decoration.
|
||||
|
||||
**CDN:**
|
||||
Files are self-hosted on the Lotus Nextcloud instance. Direct access: `https://drive.lotusguild.org/public.php/dav/files/{token}/cinny-decorations/{slug}.png`. `<img>` elements load cross-origin freely — no CORS headers needed.
|
||||
|
||||
**Module-level cache with in-flight deduplication:**
|
||||
`useAvatarDecoration(userId)` fetches the profile field once per user per session. A `Map<userId, slug|null>` cache prevents redundant requests; a second `pending` waiters map ensures multiple components requesting the same userId simultaneously share one HTTP request rather than firing duplicates.
|
||||
|
||||
**Wrapping pattern:**
|
||||
`AvatarDecoration` renders a `position: relative; display: inline-flex` wrapper div. The decoration `<img>` is `position: absolute` with `top/left/right/bottom: -8px`, extending equally on all sides while the `z-index: 10` keeps it above the avatar. `onError` hides the image if the CDN file is absent. This wrapper sits outside `PresenceRingAvatar` so the presence ring and decoration layer are fully independent.
|
||||
|
||||
### Placement — Where Decorations Render
|
||||
|
||||
| Location | File |
|
||||
|---|---|
|
||||
| Message timeline | `src/app/features/room/message/Message.tsx` |
|
||||
| Members drawer | `src/app/features/room/MembersDrawer.tsx` |
|
||||
| `@mention` autocomplete | `src/app/components/editor/autocomplete/UserMentionAutocomplete.tsx` |
|
||||
| Inbox / notifications | `src/app/pages/client/inbox/Notifications.tsx` |
|
||||
|
||||
### Settings — Decoration Picker
|
||||
|
||||
**Settings → Account → Avatar Decoration** shows a scrollable grid of all decorations, grouped by category. Each cell is a 52×52px button with a live preview of the APNG. The currently selected decoration gets a 2px cyan border. "No Decoration" clears the field. Changes are saved only when the "Save" button is clicked (visible only when a change is pending). After save, `invalidateDecorationCache(userId)` forces other components to re-fetch.
|
||||
|
||||
### Catalog Sync Script
|
||||
|
||||
After deleting decoration files from the Nextcloud share, run:
|
||||
|
||||
```bash
|
||||
npm run sync:decorations
|
||||
```
|
||||
|
||||
The script (`scripts/syncDecorations.mjs`) sends HTTP HEAD requests to the CDN URL for every slug in `avatarDecorations.ts` and automatically removes entries for files that returned 404. Empty categories are pruned automatically. Review with `git diff`.
|
||||
|
||||
### Files
|
||||
|
||||
- `src/app/features/lotus/avatarDecorations.ts` — full catalog (`DECORATION_CATEGORIES`, `ALL_DECORATIONS`, `decorationUrl()`, `DECORATION_CDN`)
|
||||
- `src/app/hooks/useAvatarDecoration.ts` — profile fetch, module-level cache, `invalidateDecorationCache()`
|
||||
- `src/app/components/avatar-decoration/AvatarDecoration.tsx` — wrapper component with APNG overlay
|
||||
- `src/app/features/settings/account/ProfileDecoration.tsx` — settings UI (picker grid, save button)
|
||||
- `scripts/syncDecorations.mjs` — CDN sync script to prune deleted decorations from the catalog
|
||||
|
||||
---
|
||||
|
||||
## Glassmorphism Sidebar (P5-3)
|
||||
|
||||
An optional frosted-glass sidebar style toggled in **Settings → Appearance**.
|
||||
@@ -969,3 +1039,8 @@ The `encUrlPreview` setting defaults to `true` rather than `false`. A security a
|
||||
| `src/app/hooks/useExtendedProfile.ts` | MSC4133 extended profile fields (`m.pronouns`, `m.tz`) read/write |
|
||||
| `src/app/hooks/useLocalTime.ts` | Derives current local time from `m.tz` profile field, updates every 60s |
|
||||
| `src/app/components/url-preview/UrlPreviewCard.tsx` | 13 domain-specific URL preview layouts plus generic fallback with favicon |
|
||||
| `src/app/features/lotus/avatarDecorations.ts` | Avatar decoration catalog, CDN URL, `decorationUrl()` helper |
|
||||
| `src/app/hooks/useAvatarDecoration.ts` | Profile field fetch with module-level cache and in-flight deduplication |
|
||||
| `src/app/components/avatar-decoration/AvatarDecoration.tsx` | APNG overlay wrapper rendered around avatars in timeline, members drawer, autocomplete |
|
||||
| `src/app/features/settings/account/ProfileDecoration.tsx` | Settings decoration picker — scrollable grid, category headers, save button |
|
||||
| `scripts/syncDecorations.mjs` | CDN HEAD-check sync script: removes catalog entries for deleted Nextcloud files |
|
||||
|
||||
Reference in New Issue
Block a user