fix(17): WR-05 IN-03 IN-01 resize-aware useIsPhone hook; OidcRedirect side-effect in effect

This commit is contained in:
Lucas Berger
2026-06-18 13:40:00 -04:00
parent c2ceebf130
commit a4a7438641
6 changed files with 73 additions and 25 deletions
+2 -5
View File
@@ -55,6 +55,7 @@ import { SyncStateToast } from './SyncStateToast.js';
import { ColorLegend } from './ColorLegend.js';
import { SkeletonCalendar } from './SkeletonCalendar.js';
import { InstallPrompt } from './InstallPrompt.js';
import { useIsPhone } from '../hooks/useIsPhone.js';
// ── Helpers ────────────────────────────────────────────────────────────────
@@ -71,10 +72,6 @@ function resolveDefaultView(persistedView: string): string {
return persistedView;
}
function isPhone(): boolean {
return typeof window !== 'undefined' && window.matchMedia('(max-width: 767px)').matches;
}
// ── Component ──────────────────────────────────────────────────────────────
export function CalendarShell() {
@@ -249,7 +246,7 @@ export function CalendarShell() {
const isInitialLoading = eventsQuery.isLoading && !eventsQuery.data;
const isEventsError = eventsQuery.isError;
const phone = isPhone();
const phone = useIsPhone();
// ── Auth splash (D-10) ────────────────────────────────────────────────────
// Gate the calendar render on auth state so no calendar shell, skeleton, or
+2 -1
View File
@@ -31,6 +31,7 @@ import {
type SaveCredentialPayload,
type SaveMyCredentialPayload,
} from '../api/client.js';
import { useIsPhone } from '../hooks/useIsPhone.js';
export type CredentialSheetMode = 'admin-rotate' | 'admin-add' | 'self-service';
@@ -76,6 +77,7 @@ export function CredentialSheet({
triggerRef,
}: CredentialSheetProps) {
const queryClient = useQueryClient();
const phone = useIsPhone();
const [password, setPassword] = useState('');
const [email, setEmail] = useState('');
const [validationError, setValidationError] = useState<string | null>(null);
@@ -153,7 +155,6 @@ export function CredentialSheet({
const heading = headingFor(mode);
const isPending = credentialMutation.isPending;
const saveDisabled = isPending || password.trim().length === 0 || email.trim().length === 0;
const phone = window.matchMedia('(max-width: 767px)').matches;
return (
<>
+5 -6
View File
@@ -26,6 +26,7 @@ import { X, Bell, AlertCircle, Loader2, LogOut } from 'lucide-react';
import { useQuery, useMutation } from '@tanstack/react-query';
import { useNavigate } from 'react-router';
import { usePushSubscription } from '../hooks/usePushSubscription.js';
import { useIsPhone } from '../hooks/useIsPhone.js';
import { InstructionSheet } from './InstructionSheet.js';
import { fetchMe, fetchAuthMode, fetchChangePassword, fetchLinkOidc, fetchLocalLogout } from '../api/client.js';
@@ -55,6 +56,8 @@ interface SettingsSheetProps {
export function SettingsSheet({ isOpen, onClose }: SettingsSheetProps) {
const { subscribe, permission, isSubscribed, setEnabled } = usePushSubscription();
const navigate = useNavigate();
// WR-05: resize-aware so an iPad rotation across 767px reflows the sheet.
const phone = useIsPhone();
const [isTogglingOn, setIsTogglingOn] = useState(false);
const [instructionsOpen, setInstructionsOpen] = useState(false);
@@ -132,8 +135,6 @@ export function SettingsSheet({ isOpen, onClose }: SettingsSheetProps) {
if (!isOpen) return null;
const phone = window.matchMedia('(max-width: 767px)').matches;
// D-07: fire-and-best-effort logout — navigate to /login regardless of API success/failure
const handleSignOut = async () => {
try {
@@ -589,6 +590,7 @@ interface ChangePasswordSheetProps {
}
function ChangePasswordSheet({ isOpen, onClose }: ChangePasswordSheetProps) {
const phone = useIsPhone();
const [currentPassword, setCurrentPassword] = useState('');
const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
@@ -647,8 +649,6 @@ function ChangePasswordSheet({ isOpen, onClose }: ChangePasswordSheetProps) {
if (!isOpen) return null;
const phone = window.matchMedia('(max-width: 767px)').matches;
return (
<>
{/* Backdrop */}
@@ -921,6 +921,7 @@ interface LinkOidcSheetProps {
}
function LinkOidcSheet({ isOpen, onClose }: LinkOidcSheetProps) {
const phone = useIsPhone();
const [error, setError] = useState<string | null>(null);
const headingRef = useRef<HTMLHeadingElement>(null);
@@ -959,8 +960,6 @@ function LinkOidcSheet({ isOpen, onClose }: LinkOidcSheetProps) {
if (!isOpen) return null;
const phone = window.matchMedia('(max-width: 767px)').matches;
return (
<>
{/* Backdrop */}