fix(#300): Web-Layout stabil bei Orientierungswechsel #301

Merged
admin-mrrm merged 2 commits from fix/300-categorize-orientation-change into main 2026-05-15 08:31:31 +02:00
Owner

Closes #300

Problem

Im Web brach die laufende Mail-Kategorisierung ab, sobald das Gerät gedreht wurde. Ursache: Layout wählte über useIsWideScreen() zwischen WideLayout und NarrowLayout — beim Überschreiten der 768px-Breakpoint-Linie wechselte der Parent-Komponententyp, was React zwang, den gesamten Outlet-Subtree (inkl. MailFoldersRoute) zu unmount-/remounten. Der Categorize-State (useCategorizeFolder) lebt in dem Component und ging dabei verloren.

Fix

Layout rendert jetzt einen stabilen Baum:

  • Beide Nav-Varianten (Narrow-Header oben + Wide-Sidebar links) sind immer gemountet.
  • Sichtbarkeit toggelt rein per CSS via Tamagui-Media-Props ($gtSm).
  • {children} wird genau einmal gerendert und überlebt Viewport-Wechsel.

Der useIsWideScreen-Hook im Web ist damit ungenutzt und wurde entfernt (Mobile-Variante unter apps/mobile bleibt).

Tests

  • Neu: apps/web/src/routes/layout.spec.tsx — assert: mountCount der Children bleibt 1 über mehrere Breakpoint-Crossings (NARROW → WIDE → NARROW).
  • Vorher (RED): mountCount === 2. Nachher (GREEN): mountCount === 1.
  • pnpm test (44/44 grün), pnpm typecheck ✓, pnpm build ✓.

Wiki

Kein Update — interner Layout-Fix ohne Feature-Auswirkung. Designentscheidung ist im Kommentar in layout.tsx dokumentiert.

Closes #300 ## Problem Im Web brach die laufende Mail-Kategorisierung ab, sobald das Gerät gedreht wurde. Ursache: `Layout` wählte über `useIsWideScreen()` zwischen `WideLayout` und `NarrowLayout` — beim Überschreiten der 768px-Breakpoint-Linie wechselte der **Parent-Komponententyp**, was React zwang, den gesamten Outlet-Subtree (inkl. `MailFoldersRoute`) zu unmount-/remounten. Der Categorize-State (`useCategorizeFolder`) lebt in dem Component und ging dabei verloren. ## Fix `Layout` rendert jetzt **einen stabilen Baum**: - Beide Nav-Varianten (Narrow-Header oben + Wide-Sidebar links) sind immer gemountet. - Sichtbarkeit toggelt rein per CSS via Tamagui-Media-Props (`$gtSm`). - `{children}` wird genau einmal gerendert und überlebt Viewport-Wechsel. Der `useIsWideScreen`-Hook im Web ist damit ungenutzt und wurde entfernt (Mobile-Variante unter `apps/mobile` bleibt). ## Tests - Neu: `apps/web/src/routes/layout.spec.tsx` — assert: `mountCount` der Children bleibt `1` über mehrere Breakpoint-Crossings (NARROW → WIDE → NARROW). - Vorher (RED): `mountCount === 2`. Nachher (GREEN): `mountCount === 1`. - `pnpm test` (44/44 grün), `pnpm typecheck` ✓, `pnpm build` ✓. ## Wiki Kein Update — interner Layout-Fix ohne Feature-Auswirkung. Designentscheidung ist im Kommentar in `layout.tsx` dokumentiert.
fix(#300): Web-Layout stabil bei Orientierungswechsel
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
03f3f9f881
Layout schaltete bei window.resize zwischen WideLayout und
NarrowLayout um — das re-mountete die gesamte Outlet-Subtree und
brach laufende Mail-Kategorisierungen ab (State lebt in
useCategorizeFolder).

Layout rendert jetzt einen stabilen Baum; beide Nav-Varianten sind
immer gemountet, die Sichtbarkeit toggelt via Tamagui-Media-Props
($gtSm). useIsWideScreen-Hook im Web entfällt (Mobile-Variante
bleibt unverändert).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
chore(web): ESLint v9 Flat-Config + pre-existing Lint-Fehler beheben
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
1d6c419a78
Im Zuge von #300 ist aufgefallen, dass `pnpm lint` im Web-Workspace
ohne eslint.config.js abbricht (ESLint v9 verlangt Flat-Config).
Web nutzt jetzt analog zu api/shared-types `@mrrmlab/config/eslint/react`.

Mit funktionierendem Linter wurden 5 vorbestehende Verstöße sichtbar
und gefixt:
- mail/hooks.ts: inline `import('@mrrmlab/api-client').X` → top-level `import type` (consistent-type-imports)
- mail-review.tsx, mail-tags-manager.tsx: rohe `"` in JSX-Text → `&ldquo;/&rdquo;` (react/no-unescaped-entities)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Author
Owner

Zusatz im PR:

1. ESLint-Setup (Web)
Web-Workspace hatte keine eslint.config.js (ESLint v9 Flat-Config). pnpm lint brach mit Konfig-Fehler ab. Jetzt analog zu api/shared-types: nutzt @mrrmlab/config/eslint/react.

Damit der Linter wieder läuft, fielen 5 vorbestehende Verstöße auf, die mitgefixt sind:

  • mail/hooks.ts:155 — inline import() Type-Annotation → top-level import type (consistent-type-imports)
  • mail-review.tsx:269, mail-tags-manager.tsx:66 — rohe " in JSX-Text → &ldquo;/&rdquo; (react/no-unescaped-entities)

2. Audit ähnlicher Remount-Patterns (Web + Mobile)
Ganzer Monorepo durchsucht nach dem Anti-Pattern „bedingt unterschiedliche Parent-Komponenten um children rendern".

  • apps/web/src/routes/layout.tsx — der Original-Fall, mit diesem PR gefixt.
  • apps/mobile/app/(drawer)/_layout.tsx — sieht auf den ersten Blick verdächtig aus (drawerType: isWide ? permanent : front), ist aber nicht vom selben Bug betroffen: Quellcode von react-native-drawer-layout zeigt, dass drawerType nur Styles und ein Geschwister-Overlay umschaltet — die {children} (Scenes) hängen immer am selben Tree-Slot. Kein Parent-Typ-Wechsel ⇒ kein Remount.
  • Sonst nichts gefunden — kein anderes isX ? <A>{children}</A> : <B>{children}</B>-Muster im Repo.

Alles grün: pnpm lint, pnpm typecheck, pnpm test (44/44), pnpm build.

Zusatz im PR: **1. ESLint-Setup (Web)** Web-Workspace hatte keine `eslint.config.js` (ESLint v9 Flat-Config). `pnpm lint` brach mit Konfig-Fehler ab. Jetzt analog zu api/shared-types: nutzt `@mrrmlab/config/eslint/react`. Damit der Linter wieder läuft, fielen 5 vorbestehende Verstöße auf, die mitgefixt sind: - `mail/hooks.ts:155` — inline `import()` Type-Annotation → top-level `import type` (`consistent-type-imports`) - `mail-review.tsx:269`, `mail-tags-manager.tsx:66` — rohe `"` in JSX-Text → `&ldquo;/&rdquo;` (`react/no-unescaped-entities`) **2. Audit ähnlicher Remount-Patterns (Web + Mobile)** Ganzer Monorepo durchsucht nach dem Anti-Pattern „bedingt unterschiedliche Parent-Komponenten um `children` rendern". - **`apps/web/src/routes/layout.tsx`** — der Original-Fall, mit diesem PR gefixt. - **`apps/mobile/app/(drawer)/_layout.tsx`** — sieht auf den ersten Blick verdächtig aus (`drawerType: isWide ? permanent : front`), ist aber **nicht** vom selben Bug betroffen: Quellcode von `react-native-drawer-layout` zeigt, dass `drawerType` nur Styles und ein Geschwister-Overlay umschaltet — die `{children}` (Scenes) hängen immer am selben Tree-Slot. Kein Parent-Typ-Wechsel ⇒ kein Remount. - **Sonst nichts gefunden** — kein anderes `isX ? <A>{children}</A> : <B>{children}</B>`-Muster im Repo. Alles grün: `pnpm lint`, `pnpm typecheck`, `pnpm test` (44/44), `pnpm build`.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
admin-mrrm/mrrmlabapp!301
No description provided.