fix(lotus): decoration ring sized around the avatar, hidden on error / reduced motion, CDN-pinned, survives remount
- .lotusDecoration 50cqmin -> 62cqmin (cinny's inset ratio); onError hides a broken image (#4). - display:none under prefers-reduced-motion, matching the host (#19). - safeImageUrl only accepts ALLOWED_DECORATION_ORIGINS (the decorations CDN) plus blob: (#28). - Roster is no longer wiped on last teardown; the handler sends io.lotus.request_state on (re)registration so the host can re-push decorations and the pin (#17 — host half in cinny). Fixes #4 Fixes #19 Fixes #28 Fixes #17 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
co-authored by
Claude Opus 5
parent
e5d5f13923
commit
bb639bb92d
@@ -120,8 +120,11 @@ Please see LICENSE in the repository root for full details.
|
||||
opacity: 50%;
|
||||
}
|
||||
|
||||
/* [lotus #6] Profile decoration overlaid on the tile avatar. Shares the
|
||||
avatar's centred box and size so frame-style decorations sit around it. */
|
||||
/* [lotus #4] Profile decoration overlaid on the tile avatar. Sized larger than
|
||||
the avatar's box (62cqmin vs. the avatar's 50cqmin — matching cinny's own
|
||||
~50px avatar + 8px outward inset ratio, `AvatarDecoration.tsx`'s
|
||||
DEFAULT_INSET) so frame-style decoration artwork bleeds outside the avatar
|
||||
circle and surrounds it, instead of overlapping/clipping it 1:1. */
|
||||
.lotusDecoration {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
@@ -131,10 +134,19 @@ avatar's centred box and size so frame-style decorations sit around it. */
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* [lotus #19] Decorations are animated APNGs with no static asset to freeze
|
||||
to; hide them under prefers-reduced-motion, matching cinny's own
|
||||
AvatarDecoration guard and the host's behaviour of rendering just the avatar. */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.lotusDecoration {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@container mediaView (width > 0) {
|
||||
.lotusDecoration {
|
||||
inline-size: 50cqmin;
|
||||
block-size: 50cqmin;
|
||||
inline-size: 62cqmin;
|
||||
block-size: 62cqmin;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,15 @@ export const MediaView: FC<Props> = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const decoration = useLotusDecoration(userId);
|
||||
// [lotus #4] Track the URL of a decoration that failed to load (e.g. a 404
|
||||
// from the CDN, reachable given the unvalidated slug the host builds it
|
||||
// from) so we can hide the broken-image box instead of leaving it over the
|
||||
// avatar. Comparing against the current `decoration` (rather than a bare
|
||||
// boolean) means a new/changed decoration URL automatically gets a fresh
|
||||
// chance to load.
|
||||
const [erroredDecoration, setErroredDecoration] = useState<
|
||||
string | undefined
|
||||
>(undefined);
|
||||
const [handRaiseTimerVisible] = useSetting(showHandRaisedTimer);
|
||||
const [showConnectionStats] = useSetting(showConnectionStatsSetting);
|
||||
|
||||
@@ -188,15 +197,19 @@ export const MediaView: FC<Props> = ({
|
||||
style={{ display: video && videoEnabled ? "none" : "initial" }}
|
||||
/>
|
||||
{decoration &&
|
||||
decoration !== erroredDecoration &&
|
||||
!(video && videoEnabled) && (
|
||||
// [lotus #6] Profile decoration overlay, shown only when the avatar
|
||||
// is visible (i.e. not when live video is showing). Pushed by the
|
||||
// host via io.lotus.decorations; undefined unless opted in.
|
||||
// [lotus #4] Hidden entirely under prefers-reduced-motion (CSS) and
|
||||
// on a load error (onError), matching cinny's own guards.
|
||||
<img
|
||||
className={styles.lotusDecoration}
|
||||
src={decoration}
|
||||
alt=""
|
||||
aria-hidden
|
||||
onError={() => setErroredDecoration(decoration)}
|
||||
/>
|
||||
)}
|
||||
{video?.publication !== undefined && (
|
||||
|
||||
Reference in New Issue
Block a user