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" + "> 密钥即为 *mj-api-secret* (如果没有设置 secret 请填 `null` \n" +
"> 白名单即为 *white-list*(如果没有回调 IP 白名单默认接收所有 IP 的回调,不需要加 | 以及后面的内容) \n" + "> 白名单即为 *white-list*(如果没有回调 IP 白名单默认接收所有 IP 的回调,不需要加 | 以及后面的内容) \n" +
"> 接入点填写你的 Midjourney Proxy 的部署地址,如 *http://localhost:8080*, *https://example.com* \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; top: -34px;
right: 0; right: 0;
user-select: none; user-select: none;
cursor: pointer;
transition: 0.25s ease;
&:hover {
p, svg {
color: hsl(var(--text-dark));
}
}
p { p {
color: hsl(var(--text-secondary-dark)); color: hsl(var(--text-secondary-dark));
@ -91,16 +99,12 @@
line-height: 1; line-height: 1;
margin: 0 0 0 6px; margin: 0 0 0 6px;
padding: 0; padding: 0;
transition: 0.25s ease;
} }
svg { svg {
cursor: pointer;
color: hsl(var(--text-secondary-dark)); color: hsl(var(--text-secondary-dark));
transition: .2s; transition: 0.25s ease;
&:hover {
color: hsl(var(--text-dark));
}
} }
} }
} }

View File

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

View File

@ -66,7 +66,7 @@ function MessageSegment(props: MessageProps) {
return; return;
props.onFocusLeave && props.onFocusLeave(event); props.onFocusLeave && props.onFocusLeave(event);
} catch (e) { } 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 { useConversationActions, useMessages } from "@/store/chat.ts";
import { MessageSquarePlus } from "lucide-react"; import { MessageSquarePlus } from "lucide-react";
import Github from "@/components/ui/icons/Github.tsx"; import Github from "@/components/ui/icons/Github.tsx";
import { openWindow } from "@/utils/device.ts";
function ProjectLink() { function ProjectLink() {
const messages = useMessages(); const messages = useMessages();
@ -21,7 +22,7 @@ function ProjectLink() {
variant="outline" variant="outline"
size="icon" size="icon"
onClick={() => 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" /> <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.is-subscribed")}</TableHead>
<TableHead>{t("admin.level")}</TableHead> <TableHead>{t("admin.level")}</TableHead>
<TableHead>{t("admin.total-month")}</TableHead> <TableHead>{t("admin.total-month")}</TableHead>
{useDeeptrain && <TableHead>{t("admin.enterprise")}</TableHead>}
<TableHead>{t("admin.is-banned")}</TableHead> <TableHead>{t("admin.is-banned")}</TableHead>
<TableHead>{t("admin.is-admin")}</TableHead> <TableHead>{t("admin.is-admin")}</TableHead>
<TableHead>{t("admin.action")}</TableHead> <TableHead>{t("admin.action")}</TableHead>
@ -422,9 +421,6 @@ function UserTable() {
{t(`admin.identity.${userTypeArray[user.level]}`)} {t(`admin.identity.${userTypeArray[user.level]}`)}
</TableCell> </TableCell>
<TableCell>{user.total_month}</TableCell> <TableCell>{user.total_month}</TableCell>
{useDeeptrain && (
<TableCell>{t(user.enterprise.toString())}</TableCell>
)}
<TableCell>{t(user.is_banned.toString())}</TableCell> <TableCell>{t(user.is_banned.toString())}</TableCell>
<TableCell>{t(user.is_admin.toString())}</TableCell> <TableCell>{t(user.is_admin.toString())}</TableCell>
<TableCell> <TableCell>

View File

@ -20,6 +20,7 @@ import {
ListStart, ListStart,
Plug, Plug,
Shield, Shield,
User,
} from "lucide-react"; } from "lucide-react";
import { openDialog as openSub } from "@/store/subscription.ts"; import { openDialog as openSub } from "@/store/subscription.ts";
import { openDialog as openPackageDialog } from "@/store/package.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 openSharingDialog } from "@/store/sharing.ts";
import { openDialog as openApiDialog } from "@/store/api.ts"; import { openDialog as openApiDialog } from "@/store/api.ts";
import router from "@/router.tsx"; import router from "@/router.tsx";
import { useDeeptrain } from "@/conf/env.ts"; import { deeptrainEndpoint, useDeeptrain } from "@/conf/env.ts";
import React from "react"; import React from "react";
import { subscriptionDataSelector } from "@/store/globals.ts"; import { subscriptionDataSelector } from "@/store/globals.ts";
import { openWindow } from "@/utils/device.ts";
import { DeeptrainOnly } from "@/conf/deeptrain.tsx";
type MenuBarProps = { type MenuBarProps = {
children: React.ReactNode; children: React.ReactNode;
@ -70,12 +73,12 @@ function MenuBar({ children, className }: MenuBarProps) {
{t("sub.title")} {t("sub.title")}
</DropdownMenuItem> </DropdownMenuItem>
)} )}
{useDeeptrain && ( <DeeptrainOnly>
<DropdownMenuItem onClick={() => dispatch(openPackageDialog())}> <DropdownMenuItem onClick={() => dispatch(openPackageDialog())}>
<Boxes className={`h-4 w-4 mr-1`} /> <Boxes className={`h-4 w-4 mr-1`} />
{t("pkg.title")} {t("pkg.title")}
</DropdownMenuItem> </DropdownMenuItem>
)} </DeeptrainOnly>
<DropdownMenuItem onClick={() => dispatch(openInvitationDialog())}> <DropdownMenuItem onClick={() => dispatch(openInvitationDialog())}>
<Gift className={`h-4 w-4 mr-1`} /> <Gift className={`h-4 w-4 mr-1`} />
{t("invitation.invitation")} {t("invitation.invitation")}
@ -88,6 +91,14 @@ function MenuBar({ children, className }: MenuBarProps) {
<Plug className={`h-4 w-4 mr-1`} /> <Plug className={`h-4 w-4 mr-1`} />
{t("api.title")} {t("api.title")}
</DropdownMenuItem> </DropdownMenuItem>
<DeeptrainOnly>
<DropdownMenuItem
onClick={() => openWindow(`${deeptrainEndpoint}/home`)}
>
<User className={`h-4 w-4 mr-1`} />
{t("my-account")}
</DropdownMenuItem>
</DeeptrainOnly>
{admin && ( {admin && (
<DropdownMenuItem onClick={() => router.navigate("/admin")}> <DropdownMenuItem onClick={() => router.navigate("/admin")}>
<Shield className={`h-4 w-4 mr-1`} /> <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 { extractMessage, filterMessage } from "@/utils/processor.ts";
import { copyClipboard } from "@/utils/dom.ts"; import { copyClipboard } from "@/utils/dom.ts";
import { useEffectAsync, useAnimation } from "@/utils/hook.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 { Button } from "@/components/ui/button.tsx";
import { selectMenu, setMenu } from "@/store/menu.ts"; import { selectMenu, setMenu } from "@/store/menu.ts";
import { import {
@ -314,7 +314,7 @@ function SidebarConversationList({
onClick={async (e) => { onClick={async (e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
window.open(shared, "_blank"); openWindow(shared, "_blank");
}} }}
> >
{t("share.view")} {t("share.view")}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -33,7 +33,7 @@
"broadcast": "Broadcast", "broadcast": "Broadcast",
"fatal": "App crashed", "fatal": "App crashed",
"download-fatal-log": "Download error log", "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": { "tag": {
"free": "Free", "free": "Free",
"official": "Official", "official": "Official",
@ -753,5 +753,6 @@
"model": "Model", "model": "Model",
"min-quota": "Minimum Balance", "min-quota": "Minimum Balance",
"your-quota": "Your balance", "your-quota": "Your balance",
"title": "Title" "title": "Title",
"my-account": "My Account"
} }

View File

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

View File

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

View File

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

View File

@ -40,3 +40,18 @@ export function useMobile(): boolean {
return mobile; 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; if (!el || !target) return false;
return el.contains(target) && (!notIncludeSelf || el !== target); if (!notIncludeSelf) {
} return el.contains(target);
}
export function isContainEventTarget( return el === target || el.contains(target);
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);
} }

View File

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