mirror of
https://github.com/coaidev/coai.git
synced 2025-05-22 14:30:14 +09:00
feat: support site announcement (#49)
This commit is contained in:
parent
8e678637fb
commit
1e07a245c5
@ -53,6 +53,7 @@
|
||||
"react-router-dom": "^6.17.0",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"rehype-katex": "^6.0.3",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"remark-breaks": "^4.0.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-math": "^5.1.1",
|
||||
|
143
app/pnpm-lock.yaml
generated
143
app/pnpm-lock.yaml
generated
@ -128,6 +128,9 @@ dependencies:
|
||||
rehype-katex:
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.3
|
||||
rehype-raw:
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.0
|
||||
remark-breaks:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0
|
||||
@ -2150,6 +2153,12 @@ packages:
|
||||
'@types/unist': 2.0.9
|
||||
dev: false
|
||||
|
||||
/@types/hast@3.0.3:
|
||||
resolution: {integrity: sha512-2fYGlaDy/qyLlhidX42wAH0KBi2TCjKMH8CHmBXgRlJ3Y+OXTiqsPQ6IWarZKwF1JoUcAJdPogv1d4b0COTpmQ==}
|
||||
dependencies:
|
||||
'@types/unist': 3.0.2
|
||||
dev: false
|
||||
|
||||
/@types/hoist-non-react-statics@3.3.4:
|
||||
resolution: {integrity: sha512-ZchYkbieA+7tnxwX/SCBySx9WwvWR8TaP5tb2jRAzwvLb/rWchGw3v0w3pqUbUvj0GCwW2Xz/AVPSk6kUGctXQ==}
|
||||
dependencies:
|
||||
@ -2393,7 +2402,6 @@ packages:
|
||||
|
||||
/@ungap/structured-clone@1.2.0:
|
||||
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
|
||||
dev: true
|
||||
|
||||
/@vitejs/plugin-react-swc@3.4.0(vite@4.5.0):
|
||||
resolution: {integrity: sha512-m7UaA4Uvz82N/0EOVpZL4XsFIakRqrFKeSNxa1FBLSXGvWrWRBwmZb4qxk+ZIVAZcW3c3dn5YosomDgx62XWcQ==}
|
||||
@ -3497,6 +3505,19 @@ packages:
|
||||
web-namespaces: 2.0.1
|
||||
dev: false
|
||||
|
||||
/hast-util-from-parse5@8.0.1:
|
||||
resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==}
|
||||
dependencies:
|
||||
'@types/hast': 3.0.3
|
||||
'@types/unist': 3.0.2
|
||||
devlop: 1.1.0
|
||||
hastscript: 8.0.0
|
||||
property-information: 6.3.0
|
||||
vfile: 6.0.1
|
||||
vfile-location: 5.0.2
|
||||
web-namespaces: 2.0.1
|
||||
dev: false
|
||||
|
||||
/hast-util-is-element@2.1.3:
|
||||
resolution: {integrity: sha512-O1bKah6mhgEq2WtVMk+Ta5K7pPMqsBBlmzysLdcwKVrqzZQ0CHqUPiIVspNhAG1rvxpvJjtGee17XfauZYKqVA==}
|
||||
dependencies:
|
||||
@ -3514,6 +3535,42 @@ packages:
|
||||
'@types/hast': 2.3.7
|
||||
dev: false
|
||||
|
||||
/hast-util-parse-selector@4.0.0:
|
||||
resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==}
|
||||
dependencies:
|
||||
'@types/hast': 3.0.3
|
||||
dev: false
|
||||
|
||||
/hast-util-raw@9.0.1:
|
||||
resolution: {integrity: sha512-5m1gmba658Q+lO5uqL5YNGQWeh1MYWZbZmWrM5lncdcuiXuo5E2HT/CIOp0rLF8ksfSwiCVJ3twlgVRyTGThGA==}
|
||||
dependencies:
|
||||
'@types/hast': 3.0.3
|
||||
'@types/unist': 3.0.2
|
||||
'@ungap/structured-clone': 1.2.0
|
||||
hast-util-from-parse5: 8.0.1
|
||||
hast-util-to-parse5: 8.0.0
|
||||
html-void-elements: 3.0.0
|
||||
mdast-util-to-hast: 13.1.0
|
||||
parse5: 7.1.2
|
||||
unist-util-position: 5.0.0
|
||||
unist-util-visit: 5.0.0
|
||||
vfile: 6.0.1
|
||||
web-namespaces: 2.0.1
|
||||
zwitch: 2.0.4
|
||||
dev: false
|
||||
|
||||
/hast-util-to-parse5@8.0.0:
|
||||
resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==}
|
||||
dependencies:
|
||||
'@types/hast': 3.0.3
|
||||
comma-separated-tokens: 2.0.3
|
||||
devlop: 1.1.0
|
||||
property-information: 6.3.0
|
||||
space-separated-tokens: 2.0.2
|
||||
web-namespaces: 2.0.1
|
||||
zwitch: 2.0.4
|
||||
dev: false
|
||||
|
||||
/hast-util-to-text@3.1.2:
|
||||
resolution: {integrity: sha512-tcllLfp23dJJ+ju5wCCZHVpzsQQ43+moJbqVX3jNWPB7z/KFC4FyZD6R7y94cHL6MQ33YtMZL8Z0aIXXI4XFTw==}
|
||||
dependencies:
|
||||
@ -3547,6 +3604,16 @@ packages:
|
||||
space-separated-tokens: 2.0.2
|
||||
dev: false
|
||||
|
||||
/hastscript@8.0.0:
|
||||
resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==}
|
||||
dependencies:
|
||||
'@types/hast': 3.0.3
|
||||
comma-separated-tokens: 2.0.3
|
||||
hast-util-parse-selector: 4.0.0
|
||||
property-information: 6.3.0
|
||||
space-separated-tokens: 2.0.2
|
||||
dev: false
|
||||
|
||||
/he@1.2.0:
|
||||
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
|
||||
hasBin: true
|
||||
@ -3582,6 +3649,10 @@ packages:
|
||||
void-elements: 3.1.0
|
||||
dev: false
|
||||
|
||||
/html-void-elements@3.0.0:
|
||||
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
|
||||
dev: false
|
||||
|
||||
/i18next@23.6.0:
|
||||
resolution: {integrity: sha512-z0Cxr0MGkt+kli306WS4nNNM++9cgt2b2VCMprY92j+AIab/oclgPxdwtTZVLP1zn5t5uo8M6uLsZmYrcjr3HA==}
|
||||
dependencies:
|
||||
@ -4062,6 +4133,20 @@ packages:
|
||||
unist-util-visit: 4.1.2
|
||||
dev: false
|
||||
|
||||
/mdast-util-to-hast@13.1.0:
|
||||
resolution: {integrity: sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==}
|
||||
dependencies:
|
||||
'@types/hast': 3.0.3
|
||||
'@types/mdast': 4.0.3
|
||||
'@ungap/structured-clone': 1.2.0
|
||||
devlop: 1.1.0
|
||||
micromark-util-sanitize-uri: 2.0.0
|
||||
trim-lines: 3.0.1
|
||||
unist-util-position: 5.0.0
|
||||
unist-util-visit: 5.0.0
|
||||
vfile: 6.0.1
|
||||
dev: false
|
||||
|
||||
/mdast-util-to-markdown@1.5.0:
|
||||
resolution: {integrity: sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==}
|
||||
dependencies:
|
||||
@ -4247,6 +4332,13 @@ packages:
|
||||
micromark-util-types: 1.1.0
|
||||
dev: false
|
||||
|
||||
/micromark-util-character@2.0.1:
|
||||
resolution: {integrity: sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==}
|
||||
dependencies:
|
||||
micromark-util-symbol: 2.0.0
|
||||
micromark-util-types: 2.0.0
|
||||
dev: false
|
||||
|
||||
/micromark-util-chunked@1.1.0:
|
||||
resolution: {integrity: sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==}
|
||||
dependencies:
|
||||
@ -4287,6 +4379,10 @@ packages:
|
||||
resolution: {integrity: sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==}
|
||||
dev: false
|
||||
|
||||
/micromark-util-encode@2.0.0:
|
||||
resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==}
|
||||
dev: false
|
||||
|
||||
/micromark-util-html-tag-name@1.2.0:
|
||||
resolution: {integrity: sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==}
|
||||
dev: false
|
||||
@ -4311,6 +4407,14 @@ packages:
|
||||
micromark-util-symbol: 1.1.0
|
||||
dev: false
|
||||
|
||||
/micromark-util-sanitize-uri@2.0.0:
|
||||
resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==}
|
||||
dependencies:
|
||||
micromark-util-character: 2.0.1
|
||||
micromark-util-encode: 2.0.0
|
||||
micromark-util-symbol: 2.0.0
|
||||
dev: false
|
||||
|
||||
/micromark-util-subtokenize@1.1.0:
|
||||
resolution: {integrity: sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==}
|
||||
dependencies:
|
||||
@ -4324,10 +4428,18 @@ packages:
|
||||
resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==}
|
||||
dev: false
|
||||
|
||||
/micromark-util-symbol@2.0.0:
|
||||
resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==}
|
||||
dev: false
|
||||
|
||||
/micromark-util-types@1.1.0:
|
||||
resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==}
|
||||
dev: false
|
||||
|
||||
/micromark-util-types@2.0.0:
|
||||
resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==}
|
||||
dev: false
|
||||
|
||||
/micromark@3.2.0:
|
||||
resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==}
|
||||
dependencies:
|
||||
@ -5098,6 +5210,14 @@ packages:
|
||||
unist-util-visit: 4.1.2
|
||||
dev: false
|
||||
|
||||
/rehype-raw@7.0.0:
|
||||
resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==}
|
||||
dependencies:
|
||||
'@types/hast': 3.0.3
|
||||
hast-util-raw: 9.0.1
|
||||
vfile: 6.0.1
|
||||
dev: false
|
||||
|
||||
/relateurl@0.2.7:
|
||||
resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==}
|
||||
engines: {node: '>= 0.10'}
|
||||
@ -5598,6 +5718,12 @@ packages:
|
||||
'@types/unist': 2.0.9
|
||||
dev: false
|
||||
|
||||
/unist-util-position@5.0.0:
|
||||
resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
|
||||
dependencies:
|
||||
'@types/unist': 3.0.2
|
||||
dev: false
|
||||
|
||||
/unist-util-remove-position@4.0.2:
|
||||
resolution: {integrity: sha512-TkBb0HABNmxzAcfLf4qsIbFbaPDvMO6wa3b3j4VcEzFVaw1LBKwnW4/sRJ/atSLSzoIg41JWEdnE7N6DIhGDGQ==}
|
||||
dependencies:
|
||||
@ -5639,6 +5765,14 @@ packages:
|
||||
unist-util-visit-parents: 5.1.3
|
||||
dev: false
|
||||
|
||||
/unist-util-visit@5.0.0:
|
||||
resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
|
||||
dependencies:
|
||||
'@types/unist': 3.0.2
|
||||
unist-util-is: 6.0.0
|
||||
unist-util-visit-parents: 6.0.1
|
||||
dev: false
|
||||
|
||||
/universalify@2.0.0:
|
||||
resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
@ -5740,6 +5874,13 @@ packages:
|
||||
vfile: 5.3.7
|
||||
dev: false
|
||||
|
||||
/vfile-location@5.0.2:
|
||||
resolution: {integrity: sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==}
|
||||
dependencies:
|
||||
'@types/unist': 3.0.2
|
||||
vfile: 6.0.1
|
||||
dev: false
|
||||
|
||||
/vfile-message@3.1.4:
|
||||
resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==}
|
||||
dependencies:
|
||||
|
@ -1,5 +1,6 @@
|
||||
import axios from "axios";
|
||||
import {
|
||||
setAnnouncement,
|
||||
setAppLogo,
|
||||
setAppName,
|
||||
setBlobEndpoint,
|
||||
@ -11,6 +12,7 @@ export type SiteInfo = {
|
||||
logo: string;
|
||||
docs: string;
|
||||
file: string;
|
||||
announcement: string;
|
||||
};
|
||||
|
||||
export async function getSiteInfo(): Promise<SiteInfo> {
|
||||
@ -19,7 +21,7 @@ export async function getSiteInfo(): Promise<SiteInfo> {
|
||||
return response.data as SiteInfo;
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
return { title: "", logo: "", docs: "", file: "" };
|
||||
return { title: "", logo: "", docs: "", file: "", announcement: "" };
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,5 +31,6 @@ export function syncSiteInfo() {
|
||||
setAppLogo(info.logo);
|
||||
setDocsUrl(info.docs);
|
||||
setBlobEndpoint(info.file);
|
||||
setAnnouncement(info.announcement);
|
||||
});
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
import remarkBreaks from "remark-breaks";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import rehypeRaw from "rehype-raw";
|
||||
import { parseFile } from "./plugins/file.tsx";
|
||||
import "@/assets/markdown/all.less";
|
||||
import { useEffect, useMemo } from "react";
|
||||
@ -24,10 +25,12 @@ import { copyClipboard } from "@/utils/dom.ts";
|
||||
import { useToast } from "./ui/use-toast.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { parseProgressbar } from "@/components/plugins/progress.tsx";
|
||||
import { cn } from "@/components/ui/lib/utils.ts";
|
||||
|
||||
type MarkdownProps = {
|
||||
children: string;
|
||||
className?: string;
|
||||
acceptHtml?: boolean;
|
||||
};
|
||||
|
||||
function doAction(dispatch: AppDispatch, url: string): boolean {
|
||||
@ -69,7 +72,7 @@ function getSocialIcon(url: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function MarkdownContent({ children, className }: MarkdownProps) {
|
||||
function MarkdownContent({ children, className, acceptHtml }: MarkdownProps) {
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
@ -82,12 +85,18 @@ function MarkdownContent({ children, className }: MarkdownProps) {
|
||||
});
|
||||
}, [children]);
|
||||
|
||||
const rehypePlugins = useMemo(() => {
|
||||
const plugins = [rehypeKatex];
|
||||
return acceptHtml ? [...plugins, rehypeRaw] : plugins;
|
||||
}, [acceptHtml]);
|
||||
|
||||
return (
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkMath, remarkGfm, remarkBreaks]}
|
||||
rehypePlugins={[rehypeKatex]}
|
||||
className={`markdown-body ${className}`}
|
||||
remarkPlugins={[remarkMath, remarkGfm, remarkBreaks]} // @ts-ignore
|
||||
rehypePlugins={rehypePlugins}
|
||||
className={cn("markdown-body", className)}
|
||||
children={children}
|
||||
skipHtml={!acceptHtml}
|
||||
components={{
|
||||
a({ href, children }) {
|
||||
const url: string = href?.toString() || "";
|
||||
@ -151,10 +160,14 @@ function MarkdownContent({ children, className }: MarkdownProps) {
|
||||
|
||||
function Markdown(props: MarkdownProps) {
|
||||
// memoize the component
|
||||
const { children, className } = props;
|
||||
const { children, className, acceptHtml } = props;
|
||||
return useMemo(
|
||||
() => <MarkdownContent className={className}>{children}</MarkdownContent>,
|
||||
[props.children, props.className],
|
||||
() => (
|
||||
<MarkdownContent className={className} acceptHtml={acceptHtml}>
|
||||
{children}
|
||||
</MarkdownContent>
|
||||
),
|
||||
[props.children, props.className, props.acceptHtml],
|
||||
);
|
||||
}
|
||||
|
||||
|
54
app/src/components/app/Announcement.tsx
Normal file
54
app/src/components/app/Announcement.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { announcementEvent } from "@/events/announcement.ts";
|
||||
import { Bell, Check } from "lucide-react";
|
||||
import Markdown from "@/components/Markdown.tsx";
|
||||
|
||||
function Announcement() {
|
||||
const { t } = useTranslation();
|
||||
const [announcement, setAnnouncement] = useState<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
announcementEvent.bind((data: string) => setAnnouncement(data));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AlertDialog
|
||||
open={announcement !== ""}
|
||||
onOpenChange={() => setAnnouncement("")}
|
||||
>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle
|
||||
className={"flex flex-row items-center select-none"}
|
||||
>
|
||||
<Bell className="inline-block w-4 h-4 mr-2" />
|
||||
<p className={`translate-y-[-1px]`}>{t("announcement")}</p>
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
<Markdown acceptHtml={true}>{announcement}</Markdown>
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>{t("close")}</AlertDialogCancel>
|
||||
<AlertDialogAction>
|
||||
<Check className="w-4 h-4 mr-1" />
|
||||
{t("i-know")}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
}
|
||||
|
||||
export default Announcement;
|
@ -19,6 +19,7 @@ import { Model } from "@/api/types.ts";
|
||||
import { ChargeProps, nonBilling } from "@/admin/charge.ts";
|
||||
import { dispatchSubscriptionData } from "@/store/globals.ts";
|
||||
import { marketEvent } from "@/events/market.ts";
|
||||
import Announcement from "@/components/app/Announcement.tsx";
|
||||
|
||||
function AppProvider() {
|
||||
const dispatch = useDispatch();
|
||||
@ -57,6 +58,7 @@ function AppProvider() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Announcement />
|
||||
<Broadcast />
|
||||
<NavBar />
|
||||
<ThemeProvider />
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { updateDocumentTitle, updateFavicon } from "@/utils/dom.ts";
|
||||
import { getMemory, setMemory } from "@/utils/memory.ts";
|
||||
import { announcementEvent } from "@/events/announcement.ts";
|
||||
|
||||
export let appName =
|
||||
localStorage.getItem("app_name") ||
|
||||
@ -71,7 +73,7 @@ export function setAppName(name: string): void {
|
||||
* set the app name in localStorage
|
||||
*/
|
||||
name = name.trim() || "Chat Nio";
|
||||
localStorage.setItem("app_name", name);
|
||||
setMemory("app_name", name);
|
||||
appName = name;
|
||||
|
||||
updateDocumentTitle(name);
|
||||
@ -82,7 +84,7 @@ export function setAppLogo(logo: string): void {
|
||||
* set the app logo in localStorage
|
||||
*/
|
||||
logo = logo.trim() || "/favicon.ico";
|
||||
localStorage.setItem("app_logo", logo);
|
||||
setMemory("app_logo", logo);
|
||||
appLogo = logo;
|
||||
|
||||
updateFavicon(logo);
|
||||
@ -93,7 +95,7 @@ export function setDocsUrl(url: string): void {
|
||||
* set the docs url in localStorage
|
||||
*/
|
||||
url = url.trim() || "https://docs.chatnio.net";
|
||||
localStorage.setItem("docs_url", url);
|
||||
setMemory("docs_url", url);
|
||||
docsEndpoint = url;
|
||||
}
|
||||
|
||||
@ -102,6 +104,16 @@ export function setBlobEndpoint(endpoint: string): void {
|
||||
* set the blob endpoint in localStorage
|
||||
*/
|
||||
endpoint = endpoint.trim() || "https://blob.chatnio.net";
|
||||
localStorage.setItem("blob_endpoint", endpoint);
|
||||
setMemory("blob_endpoint", endpoint);
|
||||
blobEndpoint = endpoint;
|
||||
}
|
||||
|
||||
export function setAnnouncement(announcement: string): void {
|
||||
/**
|
||||
* set the announcement in localStorage
|
||||
*/
|
||||
if (getMemory("announcement") === announcement) return;
|
||||
setMemory("announcement", announcement);
|
||||
|
||||
announcementEvent.emit(announcement);
|
||||
}
|
||||
|
5
app/src/events/announcement.ts
Normal file
5
app/src/events/announcement.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { EventCommitter } from "@/events/struct.ts";
|
||||
|
||||
export const announcementEvent = new EventCommitter<string>({
|
||||
name: "announcement",
|
||||
});
|
@ -43,6 +43,8 @@
|
||||
"upward": "上移",
|
||||
"downward": "下移",
|
||||
"save": "保存",
|
||||
"announcement": "站点公告",
|
||||
"i-know": "我已知晓",
|
||||
"auth": {
|
||||
"username": "用户名",
|
||||
"username-placeholder": "请输入用户名",
|
||||
|
@ -550,5 +550,7 @@
|
||||
"remove": "remove",
|
||||
"upward": "Top",
|
||||
"downward": "Move down",
|
||||
"save": "Save"
|
||||
"save": "Save",
|
||||
"announcement": "Site Announcement",
|
||||
"i-know": "Yes, I understand."
|
||||
}
|
@ -550,5 +550,7 @@
|
||||
"remove": "追放",
|
||||
"upward": "上へ移動",
|
||||
"downward": "下へ移動",
|
||||
"save": "保存"
|
||||
"save": "保存",
|
||||
"announcement": "サイトのお知らせ",
|
||||
"i-know": "私は知っています"
|
||||
}
|
@ -550,5 +550,7 @@
|
||||
"remove": "Убрать",
|
||||
"upward": "Выше",
|
||||
"downward": "Ниже",
|
||||
"save": "Сохранить"
|
||||
"save": "Сохранить",
|
||||
"announcement": "Объявление о площадке",
|
||||
"i-know": "Мне известно о"
|
||||
}
|
@ -404,6 +404,7 @@ function Site({ data, dispatch, onChange }: CompProps<SiteState>) {
|
||||
<Label>{t("admin.system.announcement")}</Label>
|
||||
<Textarea
|
||||
value={data.announcement}
|
||||
rows={12}
|
||||
onChange={(e) =>
|
||||
dispatch({
|
||||
type: "update:site.announcement",
|
||||
|
Loading…
Reference in New Issue
Block a user