im profil kann ein nutzer nun seine daten exportieren. man kann seinen account löschen. nach 2 jahren werden inaktive accounts gelöscht, 1 monat vorher wird eine email geschickt. Hilfetexte und Legal Pages in der Guest PWA korrigiert und vom layout her optimiert (dark mode).
This commit is contained in:
@@ -1,24 +1,31 @@
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
markdown: string;
|
||||
markdown?: string;
|
||||
html?: string;
|
||||
};
|
||||
|
||||
export function LegalMarkdown({ markdown }: Props) {
|
||||
const html = React.useMemo(() => {
|
||||
let safe = markdown
|
||||
export function LegalMarkdown({ markdown = '', html }: Props) {
|
||||
const derived = React.useMemo(() => {
|
||||
if (html && html.trim().length > 0) {
|
||||
return html;
|
||||
}
|
||||
|
||||
const escaped = markdown
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
safe = safe.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
||||
safe = safe.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, '<em>$1</em>');
|
||||
safe = safe.replace(/\[(.+?)\]\((https?:[^\s)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
|
||||
safe = safe
|
||||
|
||||
return escaped
|
||||
.split(/\n{2,}/)
|
||||
.map((block) => `<p>${block.replace(/\n/g, '<br/>')}</p>`)
|
||||
.join('\n');
|
||||
return safe;
|
||||
}, [markdown]);
|
||||
}, [markdown, html]);
|
||||
|
||||
return <div className="prose prose-sm dark:prose-invert" dangerouslySetInnerHTML={{ __html: html }} />;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="prose prose-sm max-w-none dark:prose-invert [&_:where(p,ul,ol,li)]:text-foreground [&_:where(h1,h2,h3,h4,h5,h6)]:text-foreground"
|
||||
dangerouslySetInnerHTML={{ __html: derived }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -36,10 +36,10 @@ type ViewState =
|
||||
};
|
||||
|
||||
type LegalDocumentState =
|
||||
| { phase: 'idle'; title: string; body: string }
|
||||
| { phase: 'loading'; title: string; body: string }
|
||||
| { phase: 'ready'; title: string; body: string }
|
||||
| { phase: 'error'; title: string; body: string };
|
||||
| { phase: 'idle'; title: string; markdown: string; html: string }
|
||||
| { phase: 'loading'; title: string; markdown: string; html: string }
|
||||
| { phase: 'ready'; title: string; markdown: string; html: string }
|
||||
| { phase: 'error'; title: string; markdown: string; html: string };
|
||||
|
||||
type NameStatus = 'idle' | 'saved';
|
||||
|
||||
@@ -223,10 +223,10 @@ function LegalView({
|
||||
<CardHeader>
|
||||
<CardTitle>{document.title || t(translationKey ?? 'settings.legal.fallbackTitle')}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="prose prose-sm max-w-none dark:prose-invert">
|
||||
<LegalMarkdown markdown={document.body} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
<CardContent className="prose prose-sm max-w-none dark:prose-invert [&_:where(p,ul,ol,li)]:text-foreground [&_:where(h1,h2,h3,h4,h5,h6)]:text-foreground">
|
||||
<LegalMarkdown markdown={document.markdown} html={document.html} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -418,17 +418,18 @@ function useLegalDocument(slug: string | null, locale: LocaleCode): LegalDocumen
|
||||
const [state, setState] = React.useState<LegalDocumentState>({
|
||||
phase: 'idle',
|
||||
title: '',
|
||||
body: '',
|
||||
markdown: '',
|
||||
html: '',
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!slug) {
|
||||
setState({ phase: 'idle', title: '', body: '' });
|
||||
setState({ phase: 'idle', title: '', markdown: '', html: '' });
|
||||
return;
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
setState({ phase: 'loading', title: '', body: '' });
|
||||
setState({ phase: 'loading', title: '', markdown: '', html: '' });
|
||||
|
||||
const langParam = encodeURIComponent(locale);
|
||||
fetch(`/api/v1/legal/${encodeURIComponent(slug)}?lang=${langParam}`, {
|
||||
@@ -443,7 +444,8 @@ function useLegalDocument(slug: string | null, locale: LocaleCode): LegalDocumen
|
||||
setState({
|
||||
phase: 'ready',
|
||||
title: payload.title ?? '',
|
||||
body: payload.body_markdown ?? '',
|
||||
markdown: payload.body_markdown ?? '',
|
||||
html: payload.body_html ?? '',
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -451,7 +453,7 @@ function useLegalDocument(slug: string | null, locale: LocaleCode): LegalDocumen
|
||||
return;
|
||||
}
|
||||
console.error('Failed to load legal page', error);
|
||||
setState({ phase: 'error', title: '', body: '' });
|
||||
setState({ phase: 'error', title: '', markdown: '', html: '' });
|
||||
});
|
||||
|
||||
return () => controller.abort();
|
||||
|
||||
Reference in New Issue
Block a user