fix(mobile): apply useModalStyle to remaining dialog files (Bug #9)
Completes the mobile fullscreen modal pass — adds useModalStyle to DeviceVerificationSetup, DeviceVerificationReset, AddExistingModal, RoomEncryption prompt, RoomUpgradeDialog, Modal500, ReadReceiptAvatars, and RoomTopicViewer. All floating Dialog/Modal components now go fullscreen on mobile (≤750px). UIAFlowOverlay was already fullscreen via <Overlay>; JoinRulesSwitcher/RoomNotificationSwitcher are dropdowns. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+3
-3
@@ -82,10 +82,10 @@ This document tracks identified bugs, edge cases, and architectural discrepancie
|
|||||||
### 9. Modal Float-Style Responsiveness
|
### 9. Modal Float-Style Responsiveness
|
||||||
|
|
||||||
- **File:** Multiple modal files
|
- **File:** Multiple modal files
|
||||||
- **Status:** **PARTIALLY FIXED ⚠️ UNTESTED** — applied to 7 modals; ~13 remaining modal files still need the hook applied
|
- **Status:** **FIXED ⚠️ UNTESTED** — needs verification by opening each modal on a real mobile device
|
||||||
- **Issue:** Modals appear as floating boxes on mobile, creating navigation and readability challenges.
|
- **Issue:** Modals appear as floating boxes on mobile, creating navigation and readability challenges.
|
||||||
- **Fix Applied:** Created `useModalStyle(desktopMaxWidth)` hook (`src/app/hooks/useModalStyle.ts`) that returns fullscreen styles on mobile (no border-radius, no max-width, `height: 100%`) and desktop box styles otherwise. Applied to: `LeaveRoomPrompt`, `LeaveSpacePrompt`, `ReportRoomModal`, `ReportUserModal`, `DeviceVerification`, `InviteUserPrompt`, `LogoutDialog`.
|
- **Fix Applied:** Created `useModalStyle(desktopMaxWidth)` hook (`src/app/hooks/useModalStyle.ts`) that returns fullscreen styles on mobile (no border-radius, no max-width, `height: 100%`) and desktop box styles otherwise. Applied to all 22+ modal files: `LeaveRoomPrompt`, `LeaveSpacePrompt`, `ReportRoomModal`, `ReportUserModal`, `DeviceVerification`, `InviteUserPrompt`, `LogoutDialog`, `DeviceVerificationSetup`, `DeviceVerificationReset`, `JoinAddressPrompt`, `JumpToTime`, `EditHistoryModal`, `ForwardMessageDialog`, `RemindMeDialog`, `CreateRoomModal`, `CreateSpaceModal`, `ScheduleMessageModal`, `PollCreator`, `AddExistingModal`, `RoomEncryption`, `RoomUpgrade`, `Modal500`, `ReadReceiptAvatars`, `RoomTopicViewer`.
|
||||||
- **Remaining:** Apply `useModalStyle` to: `DeviceVerificationSetup`, `UIAFlowOverlay`, `JoinAddressPrompt`, `JoinRulesSwitcher`, `RoomNotificationSwitcher`, and others that render floating dialogs.
|
- **Note:** `UIAFlowOverlay` already fullscreen via `<Overlay>` — no change needed. `JoinRulesSwitcher`/`RoomNotificationSwitcher` are dropdowns, not modals.
|
||||||
|
|
||||||
### 10. Composer Keyboard Obscurity
|
### 10. Composer Keyboard Obscurity
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { FormEventHandler, forwardRef, useCallback, useState } from 'react';
|
import React, { FormEventHandler, forwardRef, useCallback, useState } from 'react';
|
||||||
|
import { useModalStyle } from '../hooks/useModalStyle';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
Header,
|
Header,
|
||||||
@@ -287,9 +288,10 @@ type DeviceVerificationSetupProps = {
|
|||||||
export const DeviceVerificationSetup = forwardRef<HTMLDivElement, DeviceVerificationSetupProps>(
|
export const DeviceVerificationSetup = forwardRef<HTMLDivElement, DeviceVerificationSetupProps>(
|
||||||
({ onCancel }, ref) => {
|
({ onCancel }, ref) => {
|
||||||
const [recoveryKey, setRecoveryKey] = useState<string>();
|
const [recoveryKey, setRecoveryKey] = useState<string>();
|
||||||
|
const modalStyle = useModalStyle(480);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog ref={ref}>
|
<Dialog ref={ref} style={modalStyle}>
|
||||||
<Header
|
<Header
|
||||||
style={{
|
style={{
|
||||||
padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
|
padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
|
||||||
@@ -324,9 +326,10 @@ type DeviceVerificationResetProps = {
|
|||||||
export const DeviceVerificationReset = forwardRef<HTMLDivElement, DeviceVerificationResetProps>(
|
export const DeviceVerificationReset = forwardRef<HTMLDivElement, DeviceVerificationResetProps>(
|
||||||
({ onCancel }, ref) => {
|
({ onCancel }, ref) => {
|
||||||
const [reset, setReset] = useState(false);
|
const [reset, setReset] = useState(false);
|
||||||
|
const modalStyle = useModalStyle(480);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog ref={ref}>
|
<Dialog ref={ref} style={modalStyle}>
|
||||||
<Header
|
<Header
|
||||||
style={{
|
style={{
|
||||||
padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
|
padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ import React, { ReactNode } from 'react';
|
|||||||
import FocusTrap from 'focus-trap-react';
|
import FocusTrap from 'focus-trap-react';
|
||||||
import { Modal, Overlay, OverlayBackdrop, OverlayCenter } from 'folds';
|
import { Modal, Overlay, OverlayBackdrop, OverlayCenter } from 'folds';
|
||||||
import { stopPropagation } from '../utils/keyboard';
|
import { stopPropagation } from '../utils/keyboard';
|
||||||
|
import { useModalStyle } from '../hooks/useModalStyle';
|
||||||
|
|
||||||
type Modal500Props = {
|
type Modal500Props = {
|
||||||
requestClose: () => void;
|
requestClose: () => void;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
};
|
};
|
||||||
export function Modal500({ requestClose, children }: Modal500Props) {
|
export function Modal500({ requestClose, children }: Modal500Props) {
|
||||||
|
const modalStyle = useModalStyle(560);
|
||||||
return (
|
return (
|
||||||
<Overlay open backdrop={<OverlayBackdrop />}>
|
<Overlay open backdrop={<OverlayBackdrop />}>
|
||||||
<OverlayCenter>
|
<OverlayCenter>
|
||||||
@@ -19,7 +21,7 @@ export function Modal500({ requestClose, children }: Modal500Props) {
|
|||||||
escapeDeactivates: stopPropagation,
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal size="500" variant="Background">
|
<Modal size="500" variant="Background" style={modalStyle}>
|
||||||
{children}
|
{children}
|
||||||
</Modal>
|
</Modal>
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { UserAvatar } from '../user-avatar';
|
|||||||
import { StackedAvatar } from '../stacked-avatar';
|
import { StackedAvatar } from '../stacked-avatar';
|
||||||
import { EventReaders } from '../event-readers';
|
import { EventReaders } from '../event-readers';
|
||||||
import { stopPropagation } from '../../utils/keyboard';
|
import { stopPropagation } from '../../utils/keyboard';
|
||||||
|
import { useModalStyle } from '../../hooks/useModalStyle';
|
||||||
|
|
||||||
const MAX_DISPLAY = 5;
|
const MAX_DISPLAY = 5;
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ export function ReadReceiptAvatars({
|
|||||||
const useAuthentication = useMediaAuthentication();
|
const useAuthentication = useMediaAuthentication();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [lotusTerminal] = useSetting(settingsAtom, 'lotusTerminal');
|
const [lotusTerminal] = useSetting(settingsAtom, 'lotusTerminal');
|
||||||
|
const modalStyle = useModalStyle(360);
|
||||||
|
|
||||||
if (userIds.length === 0) return null;
|
if (userIds.length === 0) return null;
|
||||||
|
|
||||||
@@ -51,7 +53,7 @@ export function ReadReceiptAvatars({
|
|||||||
escapeDeactivates: stopPropagation,
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal variant="Surface" size="300">
|
<Modal variant="Surface" size="300" style={modalStyle}>
|
||||||
<EventReaders room={room} eventId={eventId} requestClose={() => setOpen(false)} />
|
<EventReaders room={room} eventId={eventId} requestClose={() => setOpen(false)} />
|
||||||
</Modal>
|
</Modal>
|
||||||
</FocusTrap>
|
</FocusTrap>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import parse from 'html-react-parser';
|
import parse from 'html-react-parser';
|
||||||
import { as, Box, Header, Icon, IconButton, Icons, Modal, Scroll, Text } from 'folds';
|
import { as, Box, Header, Icon, IconButton, Icons, Modal, Scroll, Text } from 'folds';
|
||||||
|
import { useModalStyle } from '../../hooks/useModalStyle';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Linkify from 'linkify-react';
|
import Linkify from 'linkify-react';
|
||||||
import * as css from './style.css';
|
import * as css from './style.css';
|
||||||
@@ -17,6 +18,7 @@ export const RoomTopicViewer = as<
|
|||||||
}
|
}
|
||||||
>(({ name, topic, requestClose, className, ...props }, ref) => {
|
>(({ name, topic, requestClose, className, ...props }, ref) => {
|
||||||
const topicStr = typeof topic === 'string' ? topic : topic.topic;
|
const topicStr = typeof topic === 'string' ? topic : topic.topic;
|
||||||
|
const modalStyle = useModalStyle(480);
|
||||||
const isFormatted =
|
const isFormatted =
|
||||||
typeof topic !== 'string' &&
|
typeof topic !== 'string' &&
|
||||||
topic.format === 'org.matrix.custom.html' &&
|
topic.format === 'org.matrix.custom.html' &&
|
||||||
@@ -28,6 +30,7 @@ export const RoomTopicViewer = as<
|
|||||||
flexHeight
|
flexHeight
|
||||||
className={classNames(css.ModalFlex, className)}
|
className={classNames(css.ModalFlex, className)}
|
||||||
aria-labelledby="room-topic-title"
|
aria-labelledby="room-topic-title"
|
||||||
|
style={modalStyle}
|
||||||
{...props}
|
{...props}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ import { StateEvent } from '../../../types/matrix/room';
|
|||||||
import { getViaServers } from '../../plugins/via-servers';
|
import { getViaServers } from '../../plugins/via-servers';
|
||||||
import { rateLimitedActions } from '../../utils/matrix';
|
import { rateLimitedActions } from '../../utils/matrix';
|
||||||
import { useAlive } from '../../hooks/useAlive';
|
import { useAlive } from '../../hooks/useAlive';
|
||||||
|
import { useModalStyle } from '../../hooks/useModalStyle';
|
||||||
|
|
||||||
const SEARCH_OPTS: UseAsyncSearchOptions = {
|
const SEARCH_OPTS: UseAsyncSearchOptions = {
|
||||||
limit: 500,
|
limit: 500,
|
||||||
@@ -72,6 +73,7 @@ type AddExistingModalProps = {
|
|||||||
};
|
};
|
||||||
export function AddExistingModal({ parentId, space, requestClose }: AddExistingModalProps) {
|
export function AddExistingModal({ parentId, space, requestClose }: AddExistingModalProps) {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
|
const modalStyle = useModalStyle(480);
|
||||||
const useAuthentication = useMediaAuthentication();
|
const useAuthentication = useMediaAuthentication();
|
||||||
const alive = useAlive();
|
const alive = useAlive();
|
||||||
|
|
||||||
@@ -188,7 +190,7 @@ export function AddExistingModal({ parentId, space, requestClose }: AddExistingM
|
|||||||
escapeDeactivates: stopPropagation,
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal size="300">
|
<Modal size="300" style={modalStyle}>
|
||||||
<Box grow="Yes" direction="Column">
|
<Box grow="Yes" direction="Column">
|
||||||
<Header
|
<Header
|
||||||
size="500"
|
size="500"
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import { useRoom } from '../../../hooks/useRoom';
|
|||||||
import { useStateEvent } from '../../../hooks/useStateEvent';
|
import { useStateEvent } from '../../../hooks/useStateEvent';
|
||||||
import { stopPropagation } from '../../../utils/keyboard';
|
import { stopPropagation } from '../../../utils/keyboard';
|
||||||
import { RoomPermissionsAPI } from '../../../hooks/useRoomPermissions';
|
import { RoomPermissionsAPI } from '../../../hooks/useRoomPermissions';
|
||||||
|
import { useModalStyle } from '../../../hooks/useModalStyle';
|
||||||
|
|
||||||
const ROOM_ENC_ALGO = 'm.megolm.v1.aes-sha2';
|
const ROOM_ENC_ALGO = 'm.megolm.v1.aes-sha2';
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@ type RoomEncryptionProps = {
|
|||||||
export function RoomEncryption({ permissions }: RoomEncryptionProps) {
|
export function RoomEncryption({ permissions }: RoomEncryptionProps) {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const room = useRoom();
|
const room = useRoom();
|
||||||
|
const modalStyle = useModalStyle(480);
|
||||||
|
|
||||||
const canEnable = permissions.stateEvent(StateEvent.RoomEncryption, mx.getSafeUserId());
|
const canEnable = permissions.stateEvent(StateEvent.RoomEncryption, mx.getSafeUserId());
|
||||||
const content = useStateEvent(room, StateEvent.RoomEncryption)?.getContent<{
|
const content = useStateEvent(room, StateEvent.RoomEncryption)?.getContent<{
|
||||||
@@ -111,7 +113,7 @@ export function RoomEncryption({ permissions }: RoomEncryptionProps) {
|
|||||||
escapeDeactivates: stopPropagation,
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Dialog variant="Surface">
|
<Dialog variant="Surface" style={modalStyle}>
|
||||||
<Header
|
<Header
|
||||||
style={{
|
style={{
|
||||||
padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
|
padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ import { useAlive } from '../../../hooks/useAlive';
|
|||||||
import { creatorsSupported } from '../../../utils/matrix';
|
import { creatorsSupported } from '../../../utils/matrix';
|
||||||
import { useRoomCreators } from '../../../hooks/useRoomCreators';
|
import { useRoomCreators } from '../../../hooks/useRoomCreators';
|
||||||
import { BreakWord } from '../../../styles/Text.css';
|
import { BreakWord } from '../../../styles/Text.css';
|
||||||
|
import { useModalStyle } from '../../../hooks/useModalStyle';
|
||||||
|
|
||||||
function RoomUpgradeDialog({ requestClose }: { requestClose: () => void }) {
|
function RoomUpgradeDialog({ requestClose }: { requestClose: () => void }) {
|
||||||
const mx = useMatrixClient();
|
const mx = useMatrixClient();
|
||||||
const room = useRoom();
|
const room = useRoom();
|
||||||
const alive = useAlive();
|
const alive = useAlive();
|
||||||
const creators = useRoomCreators(room);
|
const creators = useRoomCreators(room);
|
||||||
|
const modalStyle = useModalStyle(480);
|
||||||
|
|
||||||
const capabilities = useCapabilities();
|
const capabilities = useCapabilities();
|
||||||
const roomVersions = capabilities['m.room_versions'];
|
const roomVersions = capabilities['m.room_versions'];
|
||||||
@@ -93,7 +95,7 @@ function RoomUpgradeDialog({ requestClose }: { requestClose: () => void }) {
|
|||||||
escapeDeactivates: stopPropagation,
|
escapeDeactivates: stopPropagation,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Dialog variant="Surface">
|
<Dialog variant="Surface" style={modalStyle}>
|
||||||
<Header
|
<Header
|
||||||
style={{
|
style={{
|
||||||
padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
|
padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
|
||||||
|
|||||||
Reference in New Issue
Block a user