diff --git a/server/ui/src/App.tsx b/server/ui/src/App.tsx
index 5005208..0fefa7e 100644
--- a/server/ui/src/App.tsx
+++ b/server/ui/src/App.tsx
@@ -6,12 +6,10 @@ import {
History,
Images,
KeyRound,
- LogOut,
PieChart,
Settings,
- UserCog,
} from 'lucide-react'
-import { run, useStore } from './store'
+import { useStore } from './store'
import { Anmelden } from './pages/Anmelden'
import { Start } from './pages/Start'
import { Galerie } from './pages/Galerie'
@@ -23,7 +21,7 @@ import { Verlauf } from './pages/Verlauf'
import { Api } from './pages/Api'
import { Meldungen } from './components/Meldungen'
import { gehZu, usePfad } from './router'
-import { Avatar } from './components/Avatar'
+import { Kontomenue } from './components/Kontomenue'
/** Die Breite des Inhalts — an EINER Stelle.
*
@@ -41,10 +39,14 @@ const seiten = [
{ pfad: '/speicher', name: 'Speicher', kind: PieChart, Inhalt: Speicher },
{ pfad: '/tokens', name: 'Token', kind: KeyRound, Inhalt: Tokens },
{ pfad: '/api', name: 'API', kind: Code2, Inhalt: Api },
- { pfad: '/konto', name: 'Konto', kind: UserCog, Inhalt: Konto },
{ pfad: '/einstellungen', name: 'Einstellungen', kind: Settings, Inhalt: Einstellungen },
] as const
+/** Seiten OHNE Reiter. Sie sind ueber die Adresse und ueber das Kontomenue
+ * erreichbar -- ein Lesezeichen auf /konto soll nicht ins Leere fuehren, nur
+ * weil der Reiter verschwunden ist. */
+const versteckt = [{ pfad: '/konto', Inhalt: Konto }] as const
+
export function App() {
// Einzeln ausgewählt und nicht als ganzer Laden: `useStore()` ohne
// Auswahlfunktion abonniert ALLES, und dann zeichnet jede Meldung das
@@ -52,7 +54,6 @@ export function App() {
const user = useStore((s) => s.user)
const checking = useStore((s) => s.checking)
const checkSession = useStore((s) => s.checkSession)
- const logout = useStore((s) => s.logout)
const pfad = usePfad()
useEffect(() => {
@@ -80,8 +81,9 @@ export function App() {
)
}
+ const ohneReiter = versteckt.find((s) => s.pfad === pfad)
const aktiv = seiten.find((s) => s.pfad === pfad) ?? seiten[0]
- const Inhalt = aktiv.Inhalt
+ const Inhalt = ohneReiter ? ohneReiter.Inhalt : aktiv.Inhalt
return (
@@ -124,21 +126,8 @@ export function App() {
})}
-
-
-
- {user.username}
-
-
-
+
+
diff --git a/server/ui/src/auffrischen.ts b/server/ui/src/auffrischen.ts
index db04189..c9dbd9a 100644
--- a/server/ui/src/auffrischen.ts
+++ b/server/ui/src/auffrischen.ts
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useRef, useState } from 'react'
import { api, type Stand } from './api'
+import { useMerker } from './merker'
// Automatisch auffrischen — aber nicht, indem man alle fünf Sekunden alles neu
// lädt.
@@ -13,7 +14,6 @@ import { api, type Stand } from './api'
// Knopf. Eine Liste, die sich beim Klicken selbst neu sortiert, ist der Grund,
// warum Leute Auto-Refresh abschalten.
-const SPEICHER_SCHLUESSEL = 'd4rk_media.auffrischen'
const TAKT_MS = 5000
const gleich = (a: Stand | null, b: Stand | null) =>
@@ -33,16 +33,12 @@ export function useAuffrischen({
* Hinweis. Etwa: eine Auswahl steht, oder ein Bild ist offen. */
blockiert: boolean
}) {
- const [an, setAn] = useState(() => localStorage.getItem(SPEICHER_SCHLUESSEL) !== 'aus')
+ const [an, setAn] = useMerker('auffrischen', true)
const [wartet, setWartet] = useState(false)
const bekannt = useRef(null)
const blockiertRef = useRef(blockiert)
blockiertRef.current = blockiert
- useEffect(() => {
- localStorage.setItem(SPEICHER_SCHLUESSEL, an ? 'an' : 'aus')
- }, [an])
-
/** Nach eigenem Zutun (Löschen, Hochladen) gleichzuziehen — sonst meldet
* der Fühler die eigene Änderung als „neu". */
const merken = useCallback(async () => {
diff --git a/server/ui/src/components/Kontomenue.tsx b/server/ui/src/components/Kontomenue.tsx
new file mode 100644
index 0000000..9867b14
--- /dev/null
+++ b/server/ui/src/components/Kontomenue.tsx
@@ -0,0 +1,102 @@
+import { useEffect, useRef, useState } from 'react'
+import { ChevronDown, LogOut, UserCog } from 'lucide-react'
+import { run, useStore } from '../store'
+import { gehZu } from '../router'
+import { Avatar } from './Avatar'
+
+/** Das Konto hinter dem eigenen Namen.
+ *
+ * Vorher war es ein eigener Reiter neben Galerie und Verlauf — und stand damit
+ * gleichrangig neben Dingen, die man täglich benutzt, obwohl man sein Passwort
+ * zweimal im Jahr ändert. Hinter dem Namen ist der Platz, an dem jeder danach
+ * sucht.
+ *
+ * Der Pfad /konto bleibt bestehen: ein Lesezeichen darauf soll nicht ins Leere
+ * führen, nur weil der Reiter verschwunden ist. */
+export function Kontomenue() {
+ const user = useStore((s) => s.user)
+ const logout = useStore((s) => s.logout)
+ const [offen, setOffen] = useState(false)
+ const huelle = useRef(null)
+
+ // Klick daneben und Escape schließen. Ein Menü, das offen bleibt, bis man
+ // genau den Knopf wieder trifft, ist ein Menü, das im Weg steht.
+ useEffect(() => {
+ if (!offen) return
+ const daneben = (e: MouseEvent) => {
+ if (!huelle.current?.contains(e.target as Node)) setOffen(false)
+ }
+ const taste = (e: KeyboardEvent) => {
+ if (e.key === 'Escape') setOffen(false)
+ }
+ document.addEventListener('mousedown', daneben)
+ window.addEventListener('keydown', taste)
+ return () => {
+ document.removeEventListener('mousedown', daneben)
+ window.removeEventListener('keydown', taste)
+ }
+ }, [offen])
+
+ if (!user) return null
+
+ const eintrag =
+ 'flex w-full items-center gap-2 px-3 py-2 text-left text-sm text-schrift-leise ' +
+ 'transition-colors hover:bg-flaeche-hoch hover:text-schrift'
+
+ return (
+