feat: support quota is not enough dialog

This commit is contained in:
Zhang Minghan 2024-02-19 18:32:16 +08:00
parent 66d3f7f7e6
commit afa19d75f8
7 changed files with 137 additions and 8 deletions

View File

@ -176,6 +176,28 @@
background: none !important;
color: hsl(var(--text));
.prompt-row {
border: 1px solid var(--assistant-border);
border-radius: var(--radius);
margin: 0.25rem 0;
.grow {
min-width: 0.75rem;
}
.value {
display: flex;
flex-direction: row;
align-items: center;
font-family: var(--font-family);
margin: 0 !important;
}
svg {
transform: translateY(1px);
}
}
ol, ul, menu {
list-style: inherit;
}

View File

@ -9,16 +9,22 @@ import rehypeRaw from "rehype-raw";
import { parseFile } from "./plugins/file.tsx";
import "@/assets/markdown/all.less";
import { useEffect, useMemo } from "react";
import { useDispatch } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { openDialog as openQuotaDialog } from "@/store/quota.ts";
import { openDialog as openSubscriptionDialog } from "@/store/subscription.ts";
import { AppDispatch } from "@/store";
import {
CalendarPlus,
Cloud,
CloudCog,
Cloudy,
Codepen,
Codesandbox,
Copy,
Github,
Maximize,
Package,
Plus,
RefreshCcwDot,
Twitter,
Wand2,
@ -41,6 +47,8 @@ import {
} from "@/components/ui/dialog.tsx";
import { DialogClose } from "@radix-ui/react-dialog";
import { posterEvent } from "@/events/poster.ts";
import { appLogo } from "@/conf/env.ts";
import { subscriptionDataSelector } from "@/store/globals.ts";
type MarkdownProps = {
children: string;
@ -109,6 +117,8 @@ function MarkdownContent({
const { t } = useTranslation();
const { toast } = useToast();
const subscription = useSelector(subscriptionDataSelector);
useEffect(() => {
document.querySelectorAll(".file-instance").forEach((el) => {
const parent = el.parentElement as HTMLElement;
@ -130,6 +140,81 @@ function MarkdownContent({
children={children}
skipHtml={!acceptHtml}
components={{
p({ children }) {
// if the format is `user quota is not enough error (model: gpt-3.5-turbo-1106, minimum quota: 0.01, your quota: -77.77)`, return special component
const match = children
.toString()
.match(
/user quota is not enough error \(model: (.*), minimum quota: (.*), your quota: (.*)\)/,
);
if (match) {
const [, model, minimum, quota] = match;
const plan = subscription
.flatMap((p) => p.items.map((i) => i.models.includes(model)))
.includes(true);
return (
<div
className={`flex flex-col items-center w-[40vw] max-w-[320px] py-2`}
>
<img
src={appLogo}
alt={""}
className={`w-16 h-16 m-6 inline-block`}
/>
<div
className={`prompt-row flex flex-row w-full items-center justify-center px-4 py-2`}
>
<Package className={`h-4 w-4 mr-1`} />
{t("model")}
<div className={`grow`} />
<p className={`value`}>{model}</p>
</div>
<div
className={`prompt-row flex flex-row w-full items-center justify-center px-4 py-2`}
>
<Cloudy className={`h-4 w-4 mr-1`} />
{t("your-quota")}
<div className={`grow`} />
<p className={`value`}>
{quota}
<Cloud className={`h-4 w-4 ml-1`} />
</p>
</div>
<div
className={`prompt-row flex flex-row w-full items-center justify-center px-4 py-2`}
>
<CloudCog className={`h-4 w-4 mr-1`} />
{t("min-quota")}
<div className={`grow`} />
<p className={`value`}>
{minimum}
<Cloud className={`h-4 w-4 ml-1`} />
</p>
</div>
<Button
className={`mt-4 w-full`}
onClick={() => dispatch(openQuotaDialog())}
>
<Plus className={`h-4 w-4 mr-1`} />
{t("buy.dialog-title")}
</Button>
{plan && (
<Button
variant={`outline`}
className={`mt-2 w-full`}
onClick={() => dispatch(openSubscriptionDialog())}
>
<CalendarPlus className={`h-4 w-4 mr-1`} />
{t("sub.dialog-title")}
</Button>
)}
</div>
);
}
return <p>{children}</p>;
},
a({ href, children }) {
const url: string = href?.toString() || "";

View File

@ -287,7 +287,13 @@ function QuotaDialog() {
</AlertDialogContent>
</AlertDialog>
</div>
{!useDeeptrain && (
{useDeeptrain ? (
<div
className={`flex flex-row w-full justify-center items-center mt-2 select-none`}
>
{t("buy.deeptrain-tip")}
</div>
) : (
<div className={`flex flex-row w-full`}>
<Input
className={`redeem-input mr-2 text-center`}

View File

@ -48,6 +48,9 @@
"i-know": "我已知晓",
"empty": "空空如也",
"exit": "离开",
"model": "模型",
"min-quota": "最低余额",
"your-quota": "您的余额",
"auth": {
"username": "用户名",
"username-placeholder": "请输入用户名",
@ -184,6 +187,7 @@
"success-prompt": "您已成功购买 {{amount}} 点数。",
"redeem": "兑换",
"redeem-placeholder": "请输入兑换码",
"deeptrain-tip": "提示:在 Deeptrain 充值至钱包后,请返回此处,点击购买相应点数",
"exchange-success": "兑换成功",
"exchange-success-prompt": "您已成功兑换 {{amount}} 点数。",
"failed": "购买失败",

View File

@ -139,7 +139,8 @@
"exchange-success-prompt": "You have successfully redeemed {{amount}} credits.",
"exchange-failed": "Failed",
"exchange-failed-prompt": "Redemption failed for {{reason}}",
"buy-link": "Go to the deal"
"buy-link": "Go to the deal",
"deeptrain-tip": "Tip: Once Deeptrain has reloaded to your wallet, come back here and click to buy the appropriate credits"
},
"pkg": {
"title": "Packages",
@ -710,5 +711,8 @@
"i-know": "Yes, I understand.",
"submit": "Send",
"empty": "empty",
"exit": "Leave"
"exit": "Leave",
"model": "Model",
"min-quota": "Minimum Balance",
"your-quota": "Your balance"
}

View File

@ -139,7 +139,8 @@
"exchange-success-prompt": "{{amount}}クレジットを正常に引き換えました。",
"exchange-failed": "引き換えに失敗しました",
"exchange-failed-prompt": "{{reason}}のため、引き換えに失敗しました",
"buy-link": "購入しに行く"
"buy-link": "購入しに行く",
"deeptrain-tip": "ヒント: Deeptrainがウォレットにリロードされたら、ここに戻ってクリックして適切なクレジットを購入してください"
},
"pkg": {
"title": "パック",
@ -710,5 +711,8 @@
"i-know": "私は知っています",
"submit": "提出",
"empty": "空",
"exit": "離れる"
"exit": "離れる",
"model": "モデル",
"min-quota": "最低残高",
"your-quota": "残高"
}

View File

@ -139,7 +139,8 @@
"exchange-success-prompt": "Вы успешно использовали {{amount}} кредита (-ов).",
"exchange-failed": "Сбой обмена",
"exchange-failed-prompt": "Не удалось погасить по {{reason}}",
"buy-link": "Перейти к покупке"
"buy-link": "Перейти к покупке",
"deeptrain-tip": "Совет: как только Deeptrain перезагрузится на ваш кошелек, вернитесь сюда и нажмите, чтобы купить соответствующие кредиты"
},
"pkg": {
"title": "Пакеты",
@ -710,5 +711,8 @@
"i-know": "Мне известно о",
"submit": "передавать",
"empty": "Пусто",
"exit": "Закрыть"
"exit": "Закрыть",
"model": "модель",
"min-quota": "Минимальный баланс",
"your-quota": "Ваш баланс"
}