fix(mobile): touch targets, keyboard viewport, PageNav overflow, modal fullscreen
- Bug #10: use `100dvh` on <html> so layout shrinks when mobile virtual keyboard appears (prevents composer from being pushed off-screen) - Bug #7: add `minWidth/minHeight: 44px` to all 8 composer toolbar IconButtons on mobile via mobileOrTablet() check (WCAG 2.1 AA touch target requirement) - Bug #8: add `@media (max-width: 750px) { width: 100% }` to PageNav recipe variants so the nav panel fills full width on mobile instead of overflowing with its fixed desktop width - Bug #9: introduce `useModalStyle(maxWidth)` hook — returns fullscreen styles on mobile (no border-radius, no max-width cap, height 100%) and desktop box styles otherwise; applied to LeaveRoomPrompt, LeaveSpacePrompt, ReportRoomModal, ReportUserModal - Bug #11: mark as FALSE POSITIVE in LOTUS_BUGS.md — `useState(() => atom(...))` is the correct Jotai pattern for stable local atom references - Scheduled Messages persistence: mark as FIXED — already uses atomWithStorage + createJSONStorage with error-safe JSON parsing - UrlPreviewCard TDS colors: mark as BRAND EXCEPTION — SVG logo fills and site badge backgrounds are official third-party brand colors; cannot convert without inventing new CSS variables (violates TDS rule 3) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ import {
|
||||
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
|
||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
import { stopPropagation } from '../../utils/keyboard';
|
||||
import { useModalStyle } from '../../hooks/useModalStyle';
|
||||
|
||||
type ReportCategory = 'spam' | 'harassment' | 'inappropriate' | 'other';
|
||||
|
||||
@@ -36,6 +37,7 @@ type ReportRoomModalProps = {
|
||||
|
||||
export function ReportRoomModal({ roomId, onClose }: ReportRoomModalProps) {
|
||||
const mx = useMatrixClient();
|
||||
const modalStyle = useModalStyle(420);
|
||||
const [category, setCategory] = useState<ReportCategory>('spam');
|
||||
|
||||
const [reportState, submitReport] = useAsyncCallback(
|
||||
@@ -103,9 +105,7 @@ export function ReportRoomModal({ roomId, onClose }: ReportRoomModalProps) {
|
||||
background: color.Surface.Container,
|
||||
borderRadius: config.radii.R400,
|
||||
boxShadow: color.Other.Shadow,
|
||||
width: '100%',
|
||||
maxWidth: 420,
|
||||
overflow: 'hidden',
|
||||
...modalStyle,
|
||||
}}
|
||||
>
|
||||
<Header
|
||||
|
||||
@@ -20,6 +20,7 @@ import { Method } from 'matrix-js-sdk';
|
||||
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
|
||||
import { useMatrixClient } from '../../hooks/useMatrixClient';
|
||||
import { stopPropagation } from '../../utils/keyboard';
|
||||
import { useModalStyle } from '../../hooks/useModalStyle';
|
||||
|
||||
type ReportCategory = 'spam' | 'harassment' | 'inappropriate' | 'other';
|
||||
|
||||
@@ -37,6 +38,7 @@ type ReportUserModalProps = {
|
||||
|
||||
export function ReportUserModal({ userId, onClose }: ReportUserModalProps) {
|
||||
const mx = useMatrixClient();
|
||||
const modalStyle = useModalStyle(420);
|
||||
const [category, setCategory] = useState<ReportCategory>('spam');
|
||||
|
||||
const [reportState, submitReport] = useAsyncCallback(
|
||||
@@ -109,9 +111,7 @@ export function ReportUserModal({ userId, onClose }: ReportUserModalProps) {
|
||||
background: color.Surface.Container,
|
||||
borderRadius: config.radii.R400,
|
||||
boxShadow: color.Other.Shadow,
|
||||
width: '100%',
|
||||
maxWidth: 420,
|
||||
overflow: 'hidden',
|
||||
...modalStyle,
|
||||
}}
|
||||
>
|
||||
<Header
|
||||
|
||||
@@ -210,6 +210,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
|
||||
const [toolbar, setToolbar] = useSetting(settingsAtom, 'editorToolbar');
|
||||
const [composerToolbarButtons] = useSetting(settingsAtom, 'composerToolbarButtons');
|
||||
const touchTarget = mobileOrTablet() ? { minWidth: '44px', minHeight: '44px' } : undefined;
|
||||
const showFormat = composerToolbarButtons?.showFormat ?? true;
|
||||
const showEmoji = composerToolbarButtons?.showEmoji ?? true;
|
||||
const showSticker = composerToolbarButtons?.showSticker ?? true;
|
||||
@@ -936,6 +937,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
variant="SurfaceVariant"
|
||||
size="300"
|
||||
radii="300"
|
||||
style={touchTarget}
|
||||
>
|
||||
<Icon src={Icons.PlusCircle} />
|
||||
</IconButton>
|
||||
@@ -947,6 +949,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
variant="SurfaceVariant"
|
||||
size="300"
|
||||
radii="300"
|
||||
style={touchTarget}
|
||||
aria-label={toolbar ? 'Hide formatting toolbar' : 'Show formatting toolbar'}
|
||||
aria-pressed={toolbar}
|
||||
onClick={() => setToolbar(!toolbar)}
|
||||
@@ -998,6 +1001,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
variant="SurfaceVariant"
|
||||
size="300"
|
||||
radii="300"
|
||||
style={touchTarget}
|
||||
>
|
||||
<Icon
|
||||
src={Icons.Sticker}
|
||||
@@ -1016,6 +1020,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
variant="SurfaceVariant"
|
||||
size="300"
|
||||
radii="300"
|
||||
style={touchTarget}
|
||||
>
|
||||
<Icon
|
||||
src={Icons.Smile}
|
||||
@@ -1063,6 +1068,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
size="300"
|
||||
radii="300"
|
||||
disabled={gifUploading}
|
||||
style={touchTarget}
|
||||
>
|
||||
{gifUploading ? (
|
||||
<Spinner variant="Secondary" size="100" />
|
||||
@@ -1119,6 +1125,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
size="300"
|
||||
radii="300"
|
||||
title="Share location"
|
||||
style={touchTarget}
|
||||
>
|
||||
{locating ? (
|
||||
<Spinner variant="Secondary" size="100" />
|
||||
@@ -1135,6 +1142,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
size="300"
|
||||
radii="300"
|
||||
title="Create poll"
|
||||
style={touchTarget}
|
||||
>
|
||||
<Icon src={Icons.OrderList} size="100" />
|
||||
</IconButton>
|
||||
@@ -1170,6 +1178,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
variant="SurfaceVariant"
|
||||
size="300"
|
||||
radii="300"
|
||||
style={touchTarget}
|
||||
aria-label="Schedule message"
|
||||
title="Schedule message"
|
||||
>
|
||||
@@ -1181,6 +1190,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||
variant="SurfaceVariant"
|
||||
size="300"
|
||||
radii="300"
|
||||
style={touchTarget}
|
||||
aria-label="Send message"
|
||||
>
|
||||
<Icon src={Icons.Send} />
|
||||
|
||||
Reference in New Issue
Block a user