Erfolgreiche Verbindung merken und automatisch wieder aufbauen; Hinweistexte gekürzt
Die zuletzt erfolgreich verbundene SwyxTray-Adresse landet im localStorage; beim Laden der Seite wird damit sofort wieder verbunden, manuelles Trennen vergisst sie. Untertitel, Backend-Hinweis und Webhook-Erklärtexte entfernt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -65,10 +65,7 @@ export default function WebhookPanel({ events, status, error, onClear }: Props)
|
|||||||
{error && <p className="note note--error">{error}</p>}
|
{error && <p className="note note--error">{error}</p>}
|
||||||
|
|
||||||
{events.length === 0 ? (
|
{events.length === 0 ? (
|
||||||
<p className="note">
|
<p className="note">Noch keine Nachricht eingegangen.</p>
|
||||||
Noch keine Nachricht eingegangen. Ein fremdes System schickt Adressdaten an{' '}
|
|
||||||
<code>POST /api/webhook</code>; sie erscheinen hier ohne Zutun.
|
|
||||||
</p>
|
|
||||||
) : (
|
) : (
|
||||||
<ul className="webhook">
|
<ul className="webhook">
|
||||||
{events.map((event) => (
|
{events.map((event) => (
|
||||||
@@ -83,12 +80,6 @@ export default function WebhookPanel({ events, status, error, onClear }: Props)
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<p className="note">
|
|
||||||
Der Webhook nimmt beliebiges JSON an – Objekt wie Liste – und zeigt es hier unverändert.
|
|
||||||
Der Verlauf liegt im Speicher des Backends und ist nach dessen Neustart leer; „Leeren"
|
|
||||||
verwirft ihn auch dort.
|
|
||||||
</p>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,36 @@ export const DEFAULT_WS_PATH = '/ws'
|
|||||||
export const DEFAULT_WS_URL =
|
export const DEFAULT_WS_URL =
|
||||||
import.meta.env.VITE_WS_URL ?? `ws://${DEFAULT_WS_HOST}:${DEFAULT_WS_PORT}${DEFAULT_WS_PATH}`
|
import.meta.env.VITE_WS_URL ?? `ws://${DEFAULT_WS_HOST}:${DEFAULT_WS_PORT}${DEFAULT_WS_PATH}`
|
||||||
|
|
||||||
|
const SAVED_WS_URL_KEY = 'swyxweb.wsUrl'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zuletzt erfolgreich verbundene Adresse. localStorage kann fehlen oder gesperrt
|
||||||
|
* sein (Privatmodus, Browser-Einstellung) – dann wird schlicht nichts gemerkt.
|
||||||
|
*/
|
||||||
|
export function loadSavedWsUrl(): string | null {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem(SAVED_WS_URL_KEY)
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveWsUrl(url: string): void {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(SAVED_WS_URL_KEY, url)
|
||||||
|
} catch {
|
||||||
|
// Ohne Speicher kein Wiederverbinden nach Neustart – mehr geht nicht verloren.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearSavedWsUrl(): void {
|
||||||
|
try {
|
||||||
|
localStorage.removeItem(SAVED_WS_URL_KEY)
|
||||||
|
} catch {
|
||||||
|
// Siehe saveWsUrl.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface ClientConfig {
|
export interface ClientConfig {
|
||||||
websocketUrl: string
|
websocketUrl: string
|
||||||
host: string
|
host: string
|
||||||
|
|||||||
@@ -131,11 +131,6 @@ code {
|
|||||||
gap: 16px;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page__footer {
|
|
||||||
color: var(--text-muted);
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
background: var(--surface);
|
background: var(--surface);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useRef, useState, type FormEvent, type ReactNode } from 'react'
|
import { useEffect, useRef, useState, type FormEvent, type ReactNode } from 'react'
|
||||||
import { DEFAULT_WS_URL, fetchClientConfig } from '../config'
|
import { DEFAULT_WS_URL, fetchClientConfig, clearSavedWsUrl, loadSavedWsUrl, saveWsUrl } from '../config'
|
||||||
import { useSwyxTray } from '../hooks/useSwyxTray'
|
import { useSwyxTray } from '../hooks/useSwyxTray'
|
||||||
import { useCallNotifications } from '../hooks/useCallNotifications'
|
import { useCallNotifications } from '../hooks/useCallNotifications'
|
||||||
import { useWebhook } from '../hooks/useWebhook'
|
import { useWebhook } from '../hooks/useWebhook'
|
||||||
@@ -68,13 +68,25 @@ export default function HomePage() {
|
|||||||
|
|
||||||
// Der Benutzer soll seine eingetippte Adresse nicht durch die Backend-Antwort verlieren.
|
// Der Benutzer soll seine eingetippte Adresse nicht durch die Backend-Antwort verlieren.
|
||||||
const urlTouched = useRef(false)
|
const urlTouched = useRef(false)
|
||||||
|
// Adresse des laufenden Verbindungsversuchs; gemerkt wird sie erst, wenn die
|
||||||
|
// Verbindung tatsächlich zustande kommt.
|
||||||
|
const pendingUrl = useRef<string | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// Mit der zuletzt erfolgreich verbundenen Adresse direkt wieder verbinden –
|
||||||
|
// die Backend-Konfiguration muss dann nicht gefragt werden.
|
||||||
|
const saved = loadSavedWsUrl()
|
||||||
|
if (saved) {
|
||||||
|
urlTouched.current = true
|
||||||
|
setUrl(saved)
|
||||||
|
pendingUrl.current = saved
|
||||||
|
tray.connect(saved)
|
||||||
|
return
|
||||||
|
}
|
||||||
const controller = new AbortController()
|
const controller = new AbortController()
|
||||||
fetchClientConfig(controller.signal)
|
fetchClientConfig(controller.signal)
|
||||||
.then((backendUrl) => {
|
.then((backendUrl) => {
|
||||||
if (controller.signal.aborted) return
|
if (controller.signal.aborted) return
|
||||||
setConfigNote(`Adresse vom Backend übernommen: ${backendUrl}`)
|
|
||||||
if (!urlTouched.current) setUrl(backendUrl)
|
if (!urlTouched.current) setUrl(backendUrl)
|
||||||
})
|
})
|
||||||
.catch((e: unknown) => {
|
.catch((e: unknown) => {
|
||||||
@@ -83,12 +95,28 @@ export default function HomePage() {
|
|||||||
setConfigNote(`Backend-Konfiguration nicht abrufbar (${message}) – Standardadresse wird verwendet.`)
|
setConfigNote(`Backend-Konfiguration nicht abrufbar (${message}) – Standardadresse wird verwendet.`)
|
||||||
})
|
})
|
||||||
return () => controller.abort()
|
return () => controller.abort()
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- nur beim Laden der Seite
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
// Erst die zustande gekommene Verbindung zählt: dann die Adresse merken, damit
|
||||||
|
// sie nach Verbindungsverlust oder Neustart automatisch wieder aufgebaut wird.
|
||||||
|
useEffect(() => {
|
||||||
|
if (status === 'open' && pendingUrl.current) saveWsUrl(pendingUrl.current)
|
||||||
|
}, [status])
|
||||||
|
|
||||||
function handleConnect(event: FormEvent) {
|
function handleConnect(event: FormEvent) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const target = url.trim()
|
const target = url.trim()
|
||||||
if (target) tray.connect(target)
|
if (!target) return
|
||||||
|
pendingUrl.current = target
|
||||||
|
tray.connect(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manuelles Trennen heißt auch: nach einem Neustart nicht wieder verbinden.
|
||||||
|
function handleDisconnect() {
|
||||||
|
clearSavedWsUrl()
|
||||||
|
pendingUrl.current = null
|
||||||
|
tray.disconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSendRaw(event: FormEvent) {
|
function handleSendRaw(event: FormEvent) {
|
||||||
@@ -104,7 +132,7 @@ export default function HomePage() {
|
|||||||
<StadtboteSignet className="brand__signet" />
|
<StadtboteSignet className="brand__signet" />
|
||||||
<div>
|
<div>
|
||||||
<h1 className="brand__name">Stadtbote</h1>
|
<h1 className="brand__name">Stadtbote</h1>
|
||||||
<p className="page__subtitle">SwyxWeb · Telefonie über die SwyxTray-App</p>
|
<p className="page__subtitle">SwyxWeb</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<StatusBadge status={status} />
|
<StatusBadge status={status} />
|
||||||
@@ -228,7 +256,7 @@ export default function HomePage() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="button"
|
className="button"
|
||||||
onClick={tray.disconnect}
|
onClick={handleDisconnect}
|
||||||
disabled={!isBusyConnection}
|
disabled={!isBusyConnection}
|
||||||
>
|
>
|
||||||
Trennen
|
Trennen
|
||||||
@@ -292,13 +320,6 @@ export default function HomePage() {
|
|||||||
</section>
|
</section>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer className="page__footer">
|
|
||||||
SwyxTray · Kommandos <code>call</code> / <code>answer</code> / <code>hangup</code> /{' '}
|
|
||||||
<code>tabs</code> / <code>opentab</code> / <code>closetab</code> / <code>contacts</code> ·
|
|
||||||
Zustand über{' '}
|
|
||||||
<code>snapshot</code> · Standardziel <code>{DEFAULT_WS_URL}</code>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user