Files
AfiS-Aktensystem/web/components/chat/ChatLazy.tsx
T
Harlekin e0b03ea336
Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
Gedankenstriche durch Bindestriche ersetzt
375 Vorkommen in 210 Dateien. Betrifft Kommentare, Oberflaechentexte und die
README; an keiner Stelle Bedeutung, nur Zeichensetzung.
2026-09-04 08:54:52 +02:00

74 lines
2.4 KiB
TypeScript

'use client'
import dynamic from 'next/dynamic'
import { useEffect, useRef, useState, type ComponentProps } from 'react'
import { chatText } from '@/lib/i18n/chat'
import { SkeletonLine } from '@/components/Skeleton'
import type { Chat } from './Chat'
const ChatInner = dynamic(() => import('./Chat').then((modul) => modul.Chat), {
loading: () => <Platzhalter />,
// Der Chat wird ohnehin erst nach dem Scrollen gebraucht und lebt von einer
// offenen Verbindung. Ihn auf dem Server mitzurendern brächte nichts und
// zöge sein Bündel in die erste Ladung der Fallakte.
ssr: false,
})
/**
* Lädt den Chat erst, wenn er in Sichtweite kommt.
*
* Zwei Gründe, beide handfest: die Fallakte ist das, weswegen die Seite
* geöffnet wird - ihr Bündelbudget soll nicht für einen Chat draufgehen, den
* viele nie aufklappen. Und der Chat hält eine offene Verbindung zum
* Ereignisstrom; wer nur die Akte liest, soll sie nicht aufbauen.
*
* Der Platzhalter hat die Höhe des Chats, damit beim Nachladen nichts springt.
* Siehe docs/09-performance-und-ux.md.
*/
export function ChatLazy(props: ComponentProps<typeof Chat>) {
const [sichtbar, setSichtbar] = useState(false)
const anker = useRef<HTMLDivElement>(null)
useEffect(() => {
const element = anker.current
if (!element) return
// Ohne IntersectionObserver (sehr alte Browser) sofort laden: lieber
// grösser als kaputt.
if (typeof IntersectionObserver === 'undefined') {
setSichtbar(true)
return
}
const beobachter = new IntersectionObserver(
(eintraege) => {
if (eintraege.some((eintrag) => eintrag.isIntersecting)) {
setSichtbar(true)
beobachter.disconnect()
}
},
// Etwas vorher anfangen, damit er beim Ankommen schon steht.
{ rootMargin: '300px' },
)
beobachter.observe(element)
return () => beobachter.disconnect()
}, [])
return <div ref={anker}>{sichtbar ? <ChatInner {...props} /> : <Platzhalter />}</div>
}
function Platzhalter() {
return (
<div className="border border-line">
<div className="border-b border-line px-4 py-2">
<p className="font-condensed text-xs text-text-dim">{chatText.heading}</p>
</div>
<div className="space-y-3 px-4 py-4">
<SkeletonLine width="12rem" />
<SkeletonLine width="70%" />
<SkeletonLine width="55%" />
</div>
</div>
)
}