fix(ui): native inputs/checkboxes, QR fallback, focus + report modal cleanup
- N23 RoomServerACL: raw text input -> folds Input; raw checkbox -> folds Checkbox
- N24 PolicyListViewer: raw room-id input -> folds Input (Critical variant on error)
- N25 ExportRoomHistory: raw <input type="date"> x2 -> folds Input
- N26 RoomShareInvite: QR <img> gets loading="lazy" + onError fallback card
("QR code unavailable") instead of a broken-image icon
- N27 GifPicker: FocusTrap returnFocusOnDeactivate:false (matches EmojiBoard)
- N76 Report modals: drop redundant Cancel button (dismiss via header x /
click-outside, like MessageReportItem)
- N5 ReadReceiptAvatars: hover/focus moved to co-located css :hover/:focus-visible
(removed JS onMouseEnter/Leave .style mutation)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { Box, Button, config, Icon, Icons, Text } from 'folds';
|
||||
import { Box, Button, color, config, Icon, Icons, Text } from 'folds';
|
||||
import { SequenceCard } from '../../../components/sequence-card';
|
||||
import { SequenceCardStyle } from '../../room-settings/styles.css';
|
||||
import { SettingTile } from '../../../components/setting-tile';
|
||||
@@ -12,6 +12,7 @@ export function RoomShareInvite() {
|
||||
const mx = useMatrixClient();
|
||||
const room = useRoom();
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [qrError, setQrError] = useState(false);
|
||||
|
||||
const domain = mx.getDomain() ?? undefined;
|
||||
const inviteUrl = getMatrixToRoom(room.roomId, domain ? [domain] : undefined);
|
||||
@@ -63,13 +64,35 @@ export function RoomShareInvite() {
|
||||
</Box>
|
||||
</Box>
|
||||
<Box justifyContent="Center">
|
||||
<img
|
||||
src={qrSrc}
|
||||
alt="QR code for room invite link"
|
||||
width={160}
|
||||
height={160}
|
||||
style={{ display: 'block', borderRadius: config.radii.R300 }}
|
||||
/>
|
||||
{qrError ? (
|
||||
<Box
|
||||
direction="Column"
|
||||
alignItems="Center"
|
||||
justifyContent="Center"
|
||||
gap="100"
|
||||
style={{
|
||||
width: 160,
|
||||
height: 160,
|
||||
borderRadius: config.radii.R300,
|
||||
background: color.SurfaceVariant.Container,
|
||||
}}
|
||||
>
|
||||
<Icon size="400" src={Icons.Warning} />
|
||||
<Text size="T200" priority="300" align="Center">
|
||||
QR code unavailable
|
||||
</Text>
|
||||
</Box>
|
||||
) : (
|
||||
<img
|
||||
src={qrSrc}
|
||||
alt="QR code for room invite link"
|
||||
width={160}
|
||||
height={160}
|
||||
loading="lazy"
|
||||
onError={() => setQrError(true)}
|
||||
style={{ display: 'block', borderRadius: config.radii.R300 }}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</CutoutCard>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { Box, Button, Icon, IconButton, Icons, Scroll, Spinner, Text, config, color } from 'folds';
|
||||
import { Box, Button, Icon, IconButton, Icons, Input, Scroll, Spinner, Text } from 'folds';
|
||||
import { EventType } from 'matrix-js-sdk';
|
||||
import { Page, PageContent, PageHeader } from '../../components/page';
|
||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
@@ -255,40 +255,24 @@ ${msgRows}
|
||||
<Box gap="400" wrap="Wrap">
|
||||
<Box direction="Column" gap="100" style={{ flex: 1, minWidth: 160 }}>
|
||||
<Text size="T300">From</Text>
|
||||
<input
|
||||
<Input
|
||||
type="date"
|
||||
variant="Secondary"
|
||||
size="400"
|
||||
radii="300"
|
||||
value={fromDate}
|
||||
onChange={(e) => setFromDate(e.target.value)}
|
||||
style={{
|
||||
background: color.Surface.Container,
|
||||
color: color.Surface.OnContainer,
|
||||
border: `1px solid ${color.Surface.ContainerLine}`,
|
||||
borderRadius: config.radii.R300,
|
||||
padding: `${config.space.S200} ${config.space.S300}`,
|
||||
fontSize: 'inherit',
|
||||
fontFamily: 'inherit',
|
||||
width: '100%',
|
||||
boxSizing: 'border-box',
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Box direction="Column" gap="100" style={{ flex: 1, minWidth: 160 }}>
|
||||
<Text size="T300">To</Text>
|
||||
<input
|
||||
<Input
|
||||
type="date"
|
||||
variant="Secondary"
|
||||
size="400"
|
||||
radii="300"
|
||||
value={toDate}
|
||||
onChange={(e) => setToDate(e.target.value)}
|
||||
style={{
|
||||
background: color.Surface.Container,
|
||||
color: color.Surface.OnContainer,
|
||||
border: `1px solid ${color.Surface.ContainerLine}`,
|
||||
borderRadius: config.radii.R300,
|
||||
padding: `${config.space.S200} ${config.space.S300}`,
|
||||
fontSize: 'inherit',
|
||||
fontFamily: 'inherit',
|
||||
width: '100%',
|
||||
boxSizing: 'border-box',
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import { Badge, Box, Button, Icon, IconButton, Icons, Scroll, Text, color, config } from 'folds';
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
Icon,
|
||||
IconButton,
|
||||
Icons,
|
||||
Input,
|
||||
Scroll,
|
||||
Text,
|
||||
color,
|
||||
config,
|
||||
} from 'folds';
|
||||
import { EventTimeline, MatrixEvent, Room } from 'matrix-js-sdk';
|
||||
import { Page, PageContent, PageHeader } from '../../components/page';
|
||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
@@ -242,7 +254,7 @@ export function PolicyListViewer({ requestClose }: PolicyListViewerProps) {
|
||||
gap="300"
|
||||
>
|
||||
<Box gap="200" alignItems="Center">
|
||||
<input
|
||||
<Input
|
||||
ref={inputRef}
|
||||
value={roomIdInput}
|
||||
onChange={(e) => setRoomIdInput(e.target.value)}
|
||||
@@ -250,17 +262,10 @@ export function PolicyListViewer({ requestClose }: PolicyListViewerProps) {
|
||||
if (e.key === 'Enter') handleLoad();
|
||||
}}
|
||||
placeholder="!roomId:server or #alias:server"
|
||||
style={{
|
||||
flexGrow: 1,
|
||||
padding: `${config.space.S200} ${config.space.S300}`,
|
||||
borderRadius: config.radii.R300,
|
||||
border: `1px solid ${error ? color.Critical.Main : color.Surface.ContainerLine}`,
|
||||
background: color.Surface.Container,
|
||||
color: color.Surface.OnContainer,
|
||||
fontSize: 'inherit',
|
||||
fontFamily: 'inherit',
|
||||
outline: 'none',
|
||||
}}
|
||||
variant={error ? 'Critical' : 'Secondary'}
|
||||
size="400"
|
||||
radii="300"
|
||||
style={{ flexGrow: 1 }}
|
||||
/>
|
||||
<Button
|
||||
onClick={handleLoad}
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
import React, { useCallback, useRef, useState } from 'react';
|
||||
import { Box, Button, Icon, IconButton, Icons, Scroll, Spinner, Text, color, config } from 'folds';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Checkbox,
|
||||
Icon,
|
||||
IconButton,
|
||||
Icons,
|
||||
Input,
|
||||
Scroll,
|
||||
Spinner,
|
||||
Text,
|
||||
color,
|
||||
config,
|
||||
} from 'folds';
|
||||
import { Page, PageContent, PageHeader } from '../../components/page';
|
||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
import { useRoom } from '../../hooks/useRoom';
|
||||
@@ -97,22 +110,14 @@ function ServerList({ label, entries, canEdit, onAdd, onRemove }: ServerListProp
|
||||
|
||||
{canEdit && (
|
||||
<Box direction="Column" gap="100">
|
||||
<input
|
||||
<Input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
placeholder="e.g. *.example.com or badserver.org"
|
||||
onKeyDown={handleKeyDown}
|
||||
style={{
|
||||
background: color.Surface.Container,
|
||||
color: color.Surface.OnContainer,
|
||||
border: `1px solid ${error ? color.Critical.Main : color.Surface.ContainerLine}`,
|
||||
borderRadius: config.radii.R300,
|
||||
padding: `${config.space.S200} ${config.space.S300}`,
|
||||
fontSize: 'inherit',
|
||||
fontFamily: 'inherit',
|
||||
width: '100%',
|
||||
boxSizing: 'border-box',
|
||||
}}
|
||||
variant={error ? 'Critical' : 'Secondary'}
|
||||
size="400"
|
||||
radii="300"
|
||||
/>
|
||||
{error && (
|
||||
<Text size="T200" style={{ color: color.Critical.Main }}>
|
||||
@@ -295,18 +300,13 @@ export function RoomServerACL({ requestClose }: RoomServerACLProps) {
|
||||
gap="200"
|
||||
>
|
||||
<Box alignItems="Center" gap="300">
|
||||
<input
|
||||
type="checkbox"
|
||||
<Checkbox
|
||||
id="allow-ip-literals"
|
||||
checked={allowIpLiterals}
|
||||
disabled={!canEdit}
|
||||
onChange={(e) => setAllowIpLiterals(e.target.checked)}
|
||||
style={{
|
||||
width: 16,
|
||||
height: 16,
|
||||
flexShrink: 0,
|
||||
cursor: canEdit ? 'pointer' : 'default',
|
||||
}}
|
||||
onClick={() => setAllowIpLiterals(!allowIpLiterals)}
|
||||
size="300"
|
||||
variant="Primary"
|
||||
/>
|
||||
<Box direction="Column" gap="0">
|
||||
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
|
||||
|
||||
@@ -186,9 +186,6 @@ export function ReportRoomModal({ roomId, onClose }: ReportRoomModalProps) {
|
||||
</Box>
|
||||
|
||||
<Box gap="200" justifyContent="End">
|
||||
<Button type="button" variant="Secondary" fill="None" radii="300" onClick={onClose}>
|
||||
<Text size="B400">Cancel</Text>
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="Critical"
|
||||
|
||||
@@ -192,9 +192,6 @@ export function ReportUserModal({ userId, onClose }: ReportUserModalProps) {
|
||||
</Box>
|
||||
|
||||
<Box gap="200" justifyContent="End">
|
||||
<Button type="button" variant="Secondary" fill="None" radii="300" onClick={onClose}>
|
||||
<Text size="B400">Cancel</Text>
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="Critical"
|
||||
|
||||
Reference in New Issue
Block a user