fix: resilience, speaker animation, CSS variable fixes
- Wrap RoomTimeline in ErrorBoundary — a single bad event no longer crashes the entire timeline; shows a graceful "Timeline unavailable" message instead - Wrap RoomInput in ErrorBoundary — composer crashes show a fallback placeholder rather than a blank white section - Animate SpeakerAvatarOutline with a 1.2s pulse keyframe so it's visually distinct from a static ring; respects prefers-reduced-motion - Fix var(--border-surface-variant) undefined variable in UserRoomProfile device session rows; replaced with color.SurfaceVariant.ContainerLine UNTESTED — verify at chat.lotusguild.org post-deploy. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { Box, Button, config, Icon, IconButton, Icons, Spinner, Text, toRem } from 'folds';
|
import { Box, Button, color, config, Icon, IconButton, Icons, Spinner, Text, toRem } from 'folds';
|
||||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { VerificationRequest } from 'matrix-js-sdk/lib/crypto-api';
|
import { VerificationRequest } from 'matrix-js-sdk/lib/crypto-api';
|
||||||
@@ -197,7 +197,7 @@ function UserDeviceSessions({ userId }: UserDeviceSessionsProps) {
|
|||||||
style={{
|
style={{
|
||||||
paddingTop: config.space.S100,
|
paddingTop: config.space.S100,
|
||||||
paddingBottom: config.space.S100,
|
paddingBottom: config.space.S100,
|
||||||
borderTop: `${toRem(1)} solid var(--border-surface-variant)`,
|
borderTop: `${toRem(1)} solid ${color.SurfaceVariant.ContainerLine}`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<UserDeviceRow userId={userId} device={device} />
|
<UserDeviceRow userId={userId} device={device} />
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { style } from '@vanilla-extract/css';
|
import { keyframes, style } from '@vanilla-extract/css';
|
||||||
import { color, config, toRem } from 'folds';
|
import { color, config, toRem } from 'folds';
|
||||||
|
|
||||||
export const LiveChipText = style({
|
export const LiveChipText = style({
|
||||||
@@ -16,6 +16,19 @@ export const ControlDivider = style({
|
|||||||
height: toRem(16),
|
height: toRem(16),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const SpeakerAvatarOutline = style({
|
const speakerPulse = keyframes({
|
||||||
boxShadow: `0 0 0 ${config.borderWidth.B600} ${color.Success.Main}`,
|
'0%': { boxShadow: `0 0 0 ${config.borderWidth.B600} ${color.Success.Main}` },
|
||||||
|
'50%': { boxShadow: `0 0 0 ${toRem(4)} ${color.Success.ContainerActive}` },
|
||||||
|
'100%': { boxShadow: `0 0 0 ${config.borderWidth.B600} ${color.Success.Main}` },
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SpeakerAvatarOutline = style({
|
||||||
|
'@media': {
|
||||||
|
'(prefers-reduced-motion: no-preference)': {
|
||||||
|
animation: `${speakerPulse} 1200ms ease-in-out infinite`,
|
||||||
|
},
|
||||||
|
'(prefers-reduced-motion: reduce)': {
|
||||||
|
boxShadow: `0 0 0 ${config.borderWidth.B600} ${color.Success.Main}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useCallback, useMemo, useRef } from 'react';
|
import React, { useCallback, useMemo, useRef } from 'react';
|
||||||
|
import { ErrorBoundary } from 'react-error-boundary';
|
||||||
import { Box, Text, config } from 'folds';
|
import { Box, Text, config } from 'folds';
|
||||||
import { EventType } from 'matrix-js-sdk';
|
import { EventType } from 'matrix-js-sdk';
|
||||||
import { ReactEditor } from 'slate-react';
|
import { ReactEditor } from 'slate-react';
|
||||||
@@ -109,13 +110,31 @@ export function RoomView({ eventId }: { eventId?: string }) {
|
|||||||
return (
|
return (
|
||||||
<Page ref={roomViewRef} style={chatBgStyle}>
|
<Page ref={roomViewRef} style={chatBgStyle}>
|
||||||
<Box grow="Yes" direction="Column">
|
<Box grow="Yes" direction="Column">
|
||||||
<RoomTimeline
|
<ErrorBoundary
|
||||||
key={roomId}
|
fallback={
|
||||||
room={room}
|
<Box
|
||||||
eventId={eventId}
|
grow="Yes"
|
||||||
roomInputRef={roomInputRef}
|
direction="Column"
|
||||||
editor={editor}
|
alignItems="Center"
|
||||||
/>
|
justifyContent="Center"
|
||||||
|
gap="400"
|
||||||
|
style={{ padding: config.space.S400, opacity: 0.7 }}
|
||||||
|
>
|
||||||
|
<Text size="H4">Timeline unavailable</Text>
|
||||||
|
<Text size="T300" align="Center">
|
||||||
|
An error occurred while rendering messages. Try refreshing the page.
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<RoomTimeline
|
||||||
|
key={roomId}
|
||||||
|
room={room}
|
||||||
|
eventId={eventId}
|
||||||
|
roomInputRef={roomInputRef}
|
||||||
|
editor={editor}
|
||||||
|
/>
|
||||||
|
</ErrorBoundary>
|
||||||
<RoomViewTyping room={room} />
|
<RoomViewTyping room={room} />
|
||||||
</Box>
|
</Box>
|
||||||
<Box shrink="No" direction="Column">
|
<Box shrink="No" direction="Column">
|
||||||
@@ -129,13 +148,27 @@ export function RoomView({ eventId }: { eventId?: string }) {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{canMessage && (
|
{canMessage && (
|
||||||
<RoomInput
|
<ErrorBoundary
|
||||||
room={room}
|
fallback={
|
||||||
editor={editor}
|
<RoomInputPlaceholder
|
||||||
roomId={roomId}
|
style={{ padding: config.space.S200 }}
|
||||||
fileDropContainerRef={roomViewRef}
|
alignItems="Center"
|
||||||
ref={roomInputRef}
|
justifyContent="Center"
|
||||||
/>
|
>
|
||||||
|
<Text align="Center">
|
||||||
|
Message composer encountered an error. Try refreshing.
|
||||||
|
</Text>
|
||||||
|
</RoomInputPlaceholder>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<RoomInput
|
||||||
|
room={room}
|
||||||
|
editor={editor}
|
||||||
|
roomId={roomId}
|
||||||
|
fileDropContainerRef={roomViewRef}
|
||||||
|
ref={roomInputRef}
|
||||||
|
/>
|
||||||
|
</ErrorBoundary>
|
||||||
)}
|
)}
|
||||||
{!canMessage && (
|
{!canMessage && (
|
||||||
<RoomInputPlaceholder
|
<RoomInputPlaceholder
|
||||||
|
|||||||
Reference in New Issue
Block a user