5db4db1d95
- RoomTimeline.tsx: add eslint-disable comment for intentional eventsLength dep on timelineSegments useMemo (needed to detect in-place timeline mutations) - Remove ~47 stale eslint-disable-next-line comments across 28 files for rules that are now off in the flat config (no-param-reassign, jsx-a11y/media-has-caption, react/no-array-index-key, etc); run prettier to reformat - vite.config.js: move manualChunks from rollupOptions.output to rolldownOptions.output so Rolldown (Vite 8) actually applies it; main bundle drops from 3.5 MB to 814 kB gzip-248 kB, matrix-sdk gets its own 1.16 MB cacheable chunk Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
126 lines
4.2 KiB
TypeScript
126 lines
4.2 KiB
TypeScript
import React, { useId } from 'react';
|
|
|
|
const MESSAGES = [
|
|
{ showAvatar: true, lines: [{ w: '55%' }, { w: '35%' }] },
|
|
{ showAvatar: false, lines: [{ w: '72%' }] },
|
|
{ showAvatar: false, lines: [{ w: '48%' }, { w: '60%' }] },
|
|
{ showAvatar: true, lines: [{ w: '80%' }] },
|
|
{ showAvatar: false, lines: [{ w: '40%' }] },
|
|
{ showAvatar: true, lines: [{ w: '65%' }, { w: '50%' }, { w: '30%' }] },
|
|
{ showAvatar: false, lines: [{ w: '58%' }] },
|
|
{ showAvatar: true, lines: [{ w: '45%' }] },
|
|
{ showAvatar: false, lines: [{ w: '70%' }, { w: '25%' }] },
|
|
];
|
|
|
|
export function RoomSkeleton() {
|
|
const id = useId().replace(/:/g, '');
|
|
const shimmerKeyframes = `
|
|
@keyframes shimmer-${id} {
|
|
0% { background-position: -400px 0; }
|
|
100% { background-position: 400px 0; }
|
|
}
|
|
`;
|
|
|
|
const shimmer = {
|
|
background:
|
|
'linear-gradient(90deg, var(--skeleton-base) 25%, var(--skeleton-shine) 50%, var(--skeleton-base) 75%)',
|
|
backgroundSize: '800px 100%',
|
|
animation: `shimmer-${id} 1.6s ease-in-out infinite`,
|
|
borderRadius: '4px',
|
|
} as React.CSSProperties;
|
|
|
|
return (
|
|
<>
|
|
<style>{shimmerKeyframes}</style>
|
|
<div
|
|
style={
|
|
{
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
flexGrow: 1,
|
|
height: '100%',
|
|
overflow: 'hidden',
|
|
// CSS vars resolve against both light and dark themes automatically
|
|
'--skeleton-base': 'color-mix(in srgb, currentColor 8%, transparent)',
|
|
'--skeleton-shine': 'color-mix(in srgb, currentColor 15%, transparent)',
|
|
} as React.CSSProperties
|
|
}
|
|
>
|
|
{/* Header — matches PageHeader size="600" (56px) */}
|
|
<div
|
|
style={{
|
|
height: '56px',
|
|
borderBottom: '1px solid color-mix(in srgb, currentColor 10%, transparent)',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '12px',
|
|
padding: '0 16px',
|
|
flexShrink: 0,
|
|
}}
|
|
>
|
|
{/* Avatar */}
|
|
<div
|
|
style={{
|
|
...shimmer,
|
|
width: '32px',
|
|
height: '32px',
|
|
borderRadius: '50%',
|
|
flexShrink: 0,
|
|
}}
|
|
/>
|
|
{/* Room name */}
|
|
<div style={{ ...shimmer, width: '140px', height: '16px' }} />
|
|
{/* Spacer */}
|
|
<div style={{ flex: 1 }} />
|
|
{/* Icon buttons */}
|
|
<div style={{ ...shimmer, width: '24px', height: '24px', borderRadius: '4px' }} />
|
|
<div style={{ ...shimmer, width: '24px', height: '24px', borderRadius: '4px' }} />
|
|
</div>
|
|
|
|
{/* Timeline */}
|
|
<div style={{ flex: 1, overflowY: 'hidden', padding: '16px 0' }}>
|
|
{MESSAGES.map((msg, i) => (
|
|
<div
|
|
key={i}
|
|
style={{
|
|
display: 'flex',
|
|
gap: '12px',
|
|
padding: '4px 16px',
|
|
alignItems: 'flex-start',
|
|
marginBottom: msg.showAvatar ? '8px' : '2px',
|
|
}}
|
|
>
|
|
{/* Avatar — only shown on first message in a group */}
|
|
<div style={{ width: '36px', flexShrink: 0 }}>
|
|
{msg.showAvatar && (
|
|
<div style={{ ...shimmer, width: '36px', height: '36px', borderRadius: '50%' }} />
|
|
)}
|
|
</div>
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px', flex: 1 }}>
|
|
{/* Username on first in group */}
|
|
{msg.showAvatar && (
|
|
<div style={{ ...shimmer, width: '90px', height: '12px', marginBottom: '2px' }} />
|
|
)}
|
|
{msg.lines.map((line, j) => (
|
|
<div key={j} style={{ ...shimmer, width: line.w, height: '14px' }} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Input bar */}
|
|
<div
|
|
style={{
|
|
borderTop: '1px solid color-mix(in srgb, currentColor 10%, transparent)',
|
|
padding: '12px 16px',
|
|
flexShrink: 0,
|
|
}}
|
|
>
|
|
<div style={{ ...shimmer, width: '100%', height: '44px', borderRadius: '8px' }} />
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|