feat: optimize code syntax

This commit is contained in:
Zhang Minghan 2024-03-10 10:06:50 +08:00
parent 9c6b11cbc6
commit 4fe8cb4574
23 changed files with 122 additions and 105 deletions

View File

@ -205,7 +205,7 @@ export const ChannelInfos: Record<string, ChannelInfo> = {
"> 密钥即为 *mj-api-secret* (如果没有设置 secret 请填 `null` \n" +
"> 白名单即为 *white-list*(如果没有回调 IP 白名单默认接收所有 IP 的回调,不需要加 | 以及后面的内容) \n" +
"> 接入点填写你的 Midjourney Proxy 的部署地址,如 *http://localhost:8080*, *https://example.com* \n" +
"> 注意:**请在系统设置中设置后端的公网 IP / 域名,否则无法接收回调**",
"> 注意:**请在系统设置中设置后端的公网 IP / 域名,否则无法接收回调报错 please provide available notify url** \n",
},
};

View File

@ -84,6 +84,14 @@
top: -34px;
right: 0;
user-select: none;
cursor: pointer;
transition: 0.25s ease;
&:hover {
p, svg {
color: hsl(var(--text-dark));
}
}
p {
color: hsl(var(--text-secondary-dark));
@ -91,16 +99,12 @@
line-height: 1;
margin: 0 0 0 6px;
padding: 0;
transition: 0.25s ease;
}
svg {
cursor: pointer;
color: hsl(var(--text-secondary-dark));
transition: .2s;
&:hover {
color: hsl(var(--text-dark));
}
transition: 0.25s ease;
}
}
}

View File

@ -15,6 +15,7 @@ import { openDialog as openSubscriptionDialog } from "@/store/subscription.ts";
import { AppDispatch } from "@/store";
import {
CalendarPlus,
Check,
Cloud,
CloudCog,
Cloudy,
@ -31,7 +32,6 @@ import {
Youtube,
} from "lucide-react";
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";
@ -49,6 +49,8 @@ import { DialogClose } from "@radix-ui/react-dialog";
import { appLogo } from "@/conf/env.ts";
import { subscriptionDataSelector } from "@/store/globals.ts";
import { useMessageActions } from "@/store/chat.ts";
import { useTemporaryState } from "@/utils/hook.ts";
import Icon from "@/components/utils/Icon.tsx";
type MarkdownProps = {
children: string;
@ -115,9 +117,10 @@ function MarkdownContent({
}: MarkdownProps) {
const dispatch = useDispatch();
const { t } = useTranslation();
const { toast } = useToast();
const { send: sendAction } = useMessageActions();
const { state, triggerState } = useTemporaryState();
const subscription = useSelector(subscriptionDataSelector);
useEffect(() => {
@ -274,22 +277,30 @@ function MarkdownContent({
},
code({ inline, className, children, ...props }) {
const match = /language-(\w+)/.exec(className || "");
const language = match ? match[1].toLowerCase() : "";
const language = match ? match[1].toLowerCase() : "unknown";
if (language === "file") return parseFile(children.toString());
if (language === "progress")
return parseProgressbar(children.toString());
return !inline && match ? (
if (inline)
return (
<code className={cn("code-inline", className)} {...props}>
{children}
</code>
);
return (
<div className={`markdown-syntax`}>
<div className={`markdown-syntax-header`}>
<Copy
className={`h-3 w-3`}
<div
className={`markdown-syntax-header`}
onClick={async () => {
await copyClipboard(children.toString());
toast({
title: t("share.copied"),
});
triggerState();
}}
>
<Icon
icon={state ? <Check /> : <Copy />}
className={`h-3 w-3`}
/>
<p>{language}</p>
</div>
@ -304,10 +315,6 @@ function MarkdownContent({
className={cn("code-block", codeStyle)}
/>
</div>
) : (
<code className={cn("code-inline", className)} {...props}>
{children}
</code>
);
},
}}

View File

@ -66,7 +66,7 @@ function MessageSegment(props: MessageProps) {
return;
props.onFocusLeave && props.onFocusLeave(event);
} catch (e) {
console.debug(e);
console.debug(`[message] cannot leave focus: ${e}`);
}
}}
>

View File

@ -2,6 +2,7 @@ import { Button } from "./ui/button.tsx";
import { useConversationActions, useMessages } from "@/store/chat.ts";
import { MessageSquarePlus } from "lucide-react";
import Github from "@/components/ui/icons/Github.tsx";
import { openWindow } from "@/utils/device.ts";
function ProjectLink() {
const messages = useMessages();
@ -21,7 +22,7 @@ function ProjectLink() {
variant="outline"
size="icon"
onClick={() =>
window.open("https://github.com/Deeptrain-Community/chatnio")
openWindow("https://github.com/Deeptrain-Community/chatnio")
}
>
<Github className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100" />

View File

@ -399,7 +399,6 @@ function UserTable() {
<TableHead>{t("admin.is-subscribed")}</TableHead>
<TableHead>{t("admin.level")}</TableHead>
<TableHead>{t("admin.total-month")}</TableHead>
{useDeeptrain && <TableHead>{t("admin.enterprise")}</TableHead>}
<TableHead>{t("admin.is-banned")}</TableHead>
<TableHead>{t("admin.is-admin")}</TableHead>
<TableHead>{t("admin.action")}</TableHead>
@ -422,9 +421,6 @@ function UserTable() {
{t(`admin.identity.${userTypeArray[user.level]}`)}
</TableCell>
<TableCell>{user.total_month}</TableCell>
{useDeeptrain && (
<TableCell>{t(user.enterprise.toString())}</TableCell>
)}
<TableCell>{t(user.is_banned.toString())}</TableCell>
<TableCell>{t(user.is_admin.toString())}</TableCell>
<TableCell>

View File

@ -20,6 +20,7 @@ import {
ListStart,
Plug,
Shield,
User,
} from "lucide-react";
import { openDialog as openSub } from "@/store/subscription.ts";
import { openDialog as openPackageDialog } from "@/store/package.ts";
@ -27,9 +28,11 @@ import { openDialog as openInvitationDialog } from "@/store/invitation.ts";
import { openDialog as openSharingDialog } from "@/store/sharing.ts";
import { openDialog as openApiDialog } from "@/store/api.ts";
import router from "@/router.tsx";
import { useDeeptrain } from "@/conf/env.ts";
import { deeptrainEndpoint, useDeeptrain } from "@/conf/env.ts";
import React from "react";
import { subscriptionDataSelector } from "@/store/globals.ts";
import { openWindow } from "@/utils/device.ts";
import { DeeptrainOnly } from "@/conf/deeptrain.tsx";
type MenuBarProps = {
children: React.ReactNode;
@ -70,12 +73,12 @@ function MenuBar({ children, className }: MenuBarProps) {
{t("sub.title")}
</DropdownMenuItem>
)}
{useDeeptrain && (
<DeeptrainOnly>
<DropdownMenuItem onClick={() => dispatch(openPackageDialog())}>
<Boxes className={`h-4 w-4 mr-1`} />
{t("pkg.title")}
</DropdownMenuItem>
)}
</DeeptrainOnly>
<DropdownMenuItem onClick={() => dispatch(openInvitationDialog())}>
<Gift className={`h-4 w-4 mr-1`} />
{t("invitation.invitation")}
@ -88,6 +91,14 @@ function MenuBar({ children, className }: MenuBarProps) {
<Plug className={`h-4 w-4 mr-1`} />
{t("api.title")}
</DropdownMenuItem>
<DeeptrainOnly>
<DropdownMenuItem
onClick={() => openWindow(`${deeptrainEndpoint}/home`)}
>
<User className={`h-4 w-4 mr-1`} />
{t("my-account")}
</DropdownMenuItem>
</DeeptrainOnly>
{admin && (
<DropdownMenuItem onClick={() => router.navigate("/admin")}>
<Shield className={`h-4 w-4 mr-1`} />

View File

@ -14,7 +14,7 @@ import { useToast } from "@/components/ui/use-toast.ts";
import { extractMessage, filterMessage } from "@/utils/processor.ts";
import { copyClipboard } from "@/utils/dom.ts";
import { useEffectAsync, useAnimation } from "@/utils/hook.ts";
import { mobile } from "@/utils/device.ts";
import { mobile, openWindow } from "@/utils/device.ts";
import { Button } from "@/components/ui/button.tsx";
import { selectMenu, setMenu } from "@/store/menu.ts";
import {
@ -314,7 +314,7 @@ function SidebarConversationList({
onClick={async (e) => {
e.preventDefault();
e.stopPropagation();
window.open(shared, "_blank");
openWindow(shared, "_blank");
}}
>
{t("share.view")}

View File

@ -25,7 +25,7 @@ function ScrollAction(
const position = target.scrollTop + target.clientHeight;
const height = target.scrollHeight;
const diff = Math.abs(position - height);
setVisibility(diff > 20);
setVisibility(diff > 50);
};
useEffect(() => {

View File

@ -30,6 +30,8 @@ import { openDialog, quotaSelector } from "@/store/quota.ts";
import { getPlanPrice } from "@/conf/subscription.tsx";
import { Plans } from "@/api/types.tsx";
import { subscriptionDataSelector } from "@/store/globals.ts";
import { openWindow } from "@/utils/device.ts";
import { DeeptrainOnly } from "@/conf/deeptrain.tsx";
function countPrice(data: Plans, base: number, month: number): number {
const price = getPlanPrice(data, base) * month;
@ -100,7 +102,7 @@ async function callBuyAction(
useDeeptrain
? setTimeout(() => {
window.open(`${deeptrainEndpoint}/home/wallet`);
openWindow(`${deeptrainEndpoint}/home/wallet`);
}, 2000)
: dispatch(openDialog());
}
@ -186,7 +188,7 @@ export function Upgrade({ level, current }: UpgradeProps) {
price: countPrice(subscriptionData, level, month).toFixed(2),
})}
{useDeeptrain && (
<DeeptrainOnly>
<span className={`tax`}>
&nbsp; (
{t("sub.price-tax", {
@ -196,7 +198,7 @@ export function Upgrade({ level, current }: UpgradeProps) {
})}
)
</span>
)}
</DeeptrainOnly>
</p>
</div>
<DialogFooter className={`translate-y-1.5`}>

View File

@ -1,8 +0,0 @@
import { deeptrainAppName, deeptrainEndpoint } from "@/conf/env.ts";
import { dev } from "@/conf/bootstrap.ts";
export function goDeepLogin() {
location.href = `${deeptrainEndpoint}/login?app=${
dev ? "dev" : deeptrainAppName
}`;
}

View File

@ -0,0 +1,17 @@
import {
deeptrainAppName,
deeptrainEndpoint,
useDeeptrain,
} from "@/conf/env.ts";
import { dev } from "@/conf/bootstrap.ts";
import React from "react";
export function goDeepLogin() {
location.href = `${deeptrainEndpoint}/login?app=${
dev ? "dev" : deeptrainAppName
}`;
}
export function DeeptrainOnly({ children }: { children: React.ReactNode }) {
return useDeeptrain ? <>{children}</> : null;
}

View File

@ -24,6 +24,7 @@ import { Badge } from "@/components/ui/badge.tsx";
import { useEffectAsync } from "@/utils/hook.ts";
import { selectAuthenticated } from "@/store/auth.ts";
import { deeptrainEndpoint, useDeeptrain } from "@/conf/env.ts";
import { openWindow } from "@/utils/device.ts";
function PackageDialog() {
const { t } = useTranslation();
@ -79,7 +80,7 @@ function PackageDialog() {
</Button>
<Button
variant={`default`}
onClick={() => window.open(`${deeptrainEndpoint}/home/package`)}
onClick={() => openWindow(`${deeptrainEndpoint}/home/package`)}
>
{t("pkg.go")}
</Button>

View File

@ -48,6 +48,7 @@ import {
import { useRedeem } from "@/api/redeem.ts";
import { cn } from "@/components/ui/lib/utils.ts";
import { subscriptionDataSelector } from "@/store/globals.ts";
import { openWindow } from "@/utils/device.ts";
type AmountComponentProps = {
amount: number;
@ -240,7 +241,7 @@ function QuotaDialog() {
<AlertDialogAction
onClick={async () => {
if (!useDeeptrain) {
window.open(
openWindow(
`${buyLink}?quota=${amount}`,
"_blank",
);
@ -274,7 +275,7 @@ function QuotaDialog() {
});
useDeeptrain &&
setTimeout(() => {
window.open(
openWindow(
`${deeptrainEndpoint}/home/wallet`,
);
}, 2000);

View File

@ -37,6 +37,7 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu.tsx";
import { getSharedLink, SharingPreviewForm } from "@/api/sharing.ts";
import { openWindow } from "@/utils/device.ts";
type ShareTableProps = {
data: SharingPreviewForm[];
@ -79,7 +80,7 @@ function ShareTable({ data }: ShareTableProps) {
<DropdownMenuContent align={`center`}>
<DropdownMenuItem
onClick={() => {
window.open(getSharedLink(row.hash), "_blank");
openWindow(getSharedLink(row.hash), "_blank");
}}
>
<Eye className={`h-4 w-4 mr-1`} />

View File

@ -36,7 +36,7 @@
"broadcast": "公告",
"fatal": "应用崩溃",
"download-fatal-log": "下载错误日志",
"fatal-tips": "请您先检查您的网络,浏览器兼容性,尝试清除浏览器缓存并刷新页面。如果问题仍然存在,请将日志提供给开发者以便我们排查问题。",
"fatal-tips": "请您先检查您的网络,浏览器兼容性,尝试清除浏览器缓存并刷新页面。如果问题仍然存在,请下载日志并提供完整复现步骤以给开发者以便我们排查问题。",
"request-error": "请求失败,原因:{{reason}}",
"delete": "删除",
"remove": "移除",
@ -52,6 +52,7 @@
"min-quota": "最低余额",
"your-quota": "您的余额",
"title": "标题",
"my-account": "我的账户",
"auth": {
"username": "用户名",
"username-placeholder": "请输入用户名",

View File

@ -33,7 +33,7 @@
"broadcast": "Broadcast",
"fatal": "App crashed",
"download-fatal-log": "Download error log",
"fatal-tips": "Please try to check your network, browser compatibility, try to clear the browser cache and refresh the page. If the problem still exists, please provide the log to the developer so that we can troubleshoot the problem.",
"fatal-tips": "Please check your web, browser compatibility first, try clearing your browser cache and refreshing the page. If the problem persists, please download the log and provide the complete reproduction steps to the developer so we can troubleshoot the issue.",
"tag": {
"free": "Free",
"official": "Official",
@ -753,5 +753,6 @@
"model": "Model",
"min-quota": "Minimum Balance",
"your-quota": "Your balance",
"title": "Title"
"title": "Title",
"my-account": "My Account"
}

View File

@ -33,7 +33,7 @@
"broadcast": "公告",
"fatal": "アプリのクラッシュ",
"download-fatal-log": "エラーログのダウンロード",
"fatal-tips": "まずウェブとブラウザの互換性を確認し、ブラウザのキャッシュをクリアしてページを更新してみてください。問題が解決しない場合は、問題のトラブルシューティングを行うために、開発者にログを提供してください。",
"fatal-tips": "まずウェブとブラウザの互換性を確認し、ブラウザのキャッシュをクリアしてページを更新してみてください。問題が解決しない場合は、ログをダウンロードして完全な再現手順を開発者に提供し、問題のトラブルシューティングを行います。",
"tag": {
"free": "無料",
"official": "広汽Hondaが",
@ -753,5 +753,6 @@
"model": "モデル",
"min-quota": "最低残高",
"your-quota": "残高",
"title": "タイトル"
"title": "タイトル",
"my-account": "マイアカウント"
}

View File

@ -33,7 +33,7 @@
"broadcast": "Объявление",
"fatal": "Приложение вылетело",
"download-fatal-log": "Скачать журнал ошибок",
"fatal-tips": "Пожалуйста, попробуйте проверить свою сеть, совместимость браузера, попробуйте очистить кэш браузера и обновить страницу. Если проблема все еще существует, пожалуйста, предоставьте журнал разработчику, чтобы мы могли устранить проблему.",
"fatal-tips": "Сначала проверьте совместимость веб-браузера, попробуйте очистить кэш браузера и обновить страницу. Если проблема не устранена, загрузите журнал и предоставьте разработчику полные инструкции по воспроизведению, чтобы мы могли устранить проблему.",
"tag": {
"free": "Бесплатно",
"official": "Официальный",
@ -753,5 +753,6 @@
"model": "модель",
"min-quota": "Минимальный баланс",
"your-quota": "Ваш баланс",
"title": "заглавие"
"title": "заглавие",
"my-account": "Моя учетная запись"
}

View File

@ -1,6 +1,6 @@
import router from "@/router.tsx";
import { useDeeptrain } from "@/conf/env.ts";
import { goDeepLogin } from "@/conf/deeptrain.ts";
import { goDeepLogin } from "@/conf/deeptrain.tsx";
export let event: BeforeInstallPromptEvent | undefined;

View File

@ -40,3 +40,18 @@ export function useMobile(): boolean {
return mobile;
}
export function openWindow(url: string, target?: string): void {
/**
* Open a new window with the given URL.
* If the device does not support opening a new window, the URL will be opened in the current window.
* @param url The URL to open.
* @param target The target of the URL.
*/
if (mobile) {
window.location.href = url;
} else {
window.open(url, target);
}
}

View File

@ -321,22 +321,8 @@ export function isContainDom(
*/
if (!el || !target) return false;
return el.contains(target) && (!notIncludeSelf || el !== target);
}
export function isContainEventTarget(
el: HTMLElement | undefined | null,
e: Event,
) {
/**
* Test if element contains event target
* @param el Element
* @param e Event
* @example
* const el = document.getElementById("el");
* const handler = (e: Event) => console.log(isContainEventTarget(el, e));
* el.addEventListener("click", handler);
*/
return isContainDom(el, e.target as HTMLElement);
if (!notIncludeSelf) {
return el.contains(target);
}
return el === target || el.contains(target);
}

View File

@ -44,36 +44,15 @@ export function useAnimation(
};
}
export function useShared<T>(): {
hook: (v: T) => void;
useHook: () => Promise<T>;
export function useTemporaryState(interval?: number): {
state: boolean;
triggerState: () => void;
} {
/**
* Share value between components, useful for sharing data between components / redux dispatches
*
* @example
*
* const dispatch = useDispatch();
* const { hook, useHook } = useShared<string>();
*
* dispatch(updateMigration({ hook }));
* const response = await useHook();
*/
let value: T | undefined = undefined;
const [stamp, setStamp] = React.useState<number>(0);
const triggerState = () => setStamp(new Date().getTime());
return {
hook: (v: T) => {
value = v;
},
useHook: () => {
return new Promise<T>((resolve) => {
if (value) return resolve(value);
const interval = setInterval(() => {
if (value) {
clearInterval(interval);
resolve(value);
}
}, 50);
});
},
state: Date.now() - stamp < (interval ?? 3000),
triggerState,
};
}