Skip to content

Commit

Permalink
feat: update subscription data context
Browse files Browse the repository at this point in the history
  • Loading branch information
zmh-program committed Jan 17, 2024
1 parent f8af707 commit aacc6b9
Show file tree
Hide file tree
Showing 20 changed files with 235 additions and 103 deletions.
42 changes: 31 additions & 11 deletions app/src/api/connection.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { tokenField, websocketEndpoint } from "@/conf";
import { getMemory } from "@/utils/memory.ts";
import { getErrorMessage } from "@/utils/base.ts";

export const endpoint = `${websocketEndpoint}/chat`;
export const maxRetry = 5;

export type StreamMessage = {
conversation?: number;
Expand Down Expand Up @@ -72,20 +74,38 @@ export class Connection {
return true;
}

public sendWithRetry(t: any, data: ChatProps): void {
public sendWithRetry(t: any, data: ChatProps, times?: number): void {
try {
if (!this.send(data)) {
setTimeout(() => {
this.sendWithRetry(t, data);
}, 500);
if (!times || times < maxRetry) {
if (!this.send(data)) {
setTimeout(() => {
this.sendWithRetry(t, data, (times ?? 0) + 1);
}, 500);
}

return;
}
} catch {
if (t !== undefined)
this.triggerCallback({
message: t("request-failed"),
end: true,
});
} catch (e) {
console.warn(
`[connection] failed to send message: ${getErrorMessage(e)}`,
);
}

const trace = {
message: data.message,
endpoint: endpoint,
};

t &&
this.triggerCallback({
message: `
${t("request-failed")}
\`\`\`json
${JSON.stringify(trace, null, 2)}
\`\`\`
`,
end: true,
});
}

public close(): void {
Expand Down
3 changes: 2 additions & 1 deletion app/src/api/v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export async function getApiModels(): Promise<string[]> {
export async function getApiPlans(): Promise<Plan[]> {
try {
const res = await axios.get("/v1/plans");
return res.data as Plan[];
const plans = res.data as Plan[];
return plans.filter((plan: Plan) => plan.level !== 0);
} catch (e) {
console.warn(e);
return [];
Expand Down
13 changes: 5 additions & 8 deletions app/src/components/app/AppProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@ import { ThemeProvider } from "@/components/ThemeProvider.tsx";
import DialogManager from "@/dialogs";
import Broadcast from "@/components/Broadcast.tsx";
import { useEffectAsync } from "@/utils/hook.ts";
import { allModels, subscriptionData, supportModels } from "@/conf";
import { allModels, supportModels } from "@/conf";
import { channelModels } from "@/admin/channel.ts";
import {
getApiCharge,
getApiMarket,
getApiModels,
getApiPlans,
} from "@/api/v1.ts";
import { loadPreferenceModels } from "@/utils/storage.ts";
import { loadPreferenceModels } from "@/conf/storage.ts";
import { resetJsArray } from "@/utils/base.ts";
import { useDispatch } from "react-redux";
import { initChatModels } from "@/store/chat.ts";
import { Model, Plan } from "@/api/types.ts";
import { Model } from "@/api/types.ts";
import { ChargeProps, nonBilling } from "@/admin/charge.ts";
import { dispatchSubscriptionData } from "@/store/globals.ts";

function AppProvider() {
const dispatch = useDispatch();
Expand Down Expand Up @@ -46,11 +47,7 @@ function AppProvider() {
if (!channelModels.includes(model)) channelModels.push(model);
});

const plans = await getApiPlans();
resetJsArray(
subscriptionData,
plans.filter((plan: Plan) => plan.level !== 0),
);
dispatchSubscriptionData(dispatch, await getApiPlans());
}, [allModels]);

return (
Expand Down
4 changes: 3 additions & 1 deletion app/src/components/app/MenuBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { openDialog as openApiDialog } from "@/store/api.ts";
import router from "@/router.tsx";
import { useDeeptrain } from "@/conf/env.ts";
import React from "react";
import { subscriptionData } from "@/conf";
import { subscriptionDataSelector } from "@/store/globals.ts";

type MenuBarProps = {
children: React.ReactNode;
Expand All @@ -43,6 +43,8 @@ function MenuBar({ children, className }: MenuBarProps) {
const quota = useSelector(quotaSelector);
const admin = useSelector(selectAdmin);

const subscriptionData = useSelector(subscriptionDataSelector);

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>{children}</DropdownMenuTrigger>
Expand Down
16 changes: 11 additions & 5 deletions app/src/components/home/ModelFinder.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import SelectGroup, { SelectItemProps } from "@/components/SelectGroup.tsx";
import { subscriptionData, supportModels } from "@/conf";
import { supportModels } from "@/conf";
import {
openMarket,
selectModel,
Expand All @@ -10,7 +10,7 @@ import { useTranslation } from "react-i18next";
import { useDispatch, useSelector } from "react-redux";
import { selectAuthenticated } from "@/store/auth.ts";
import { useToast } from "@/components/ui/use-toast.ts";
import { Model } from "@/api/types.ts";
import { Model, Plans } from "@/api/types.ts";
import { modelEvent } from "@/events/model.ts";
import { levelSelector } from "@/store/subscription.ts";
import { teenagerSelector } from "@/store/package.ts";
Expand All @@ -19,6 +19,7 @@ import { useEffect, useMemo, useState } from "react";
import { Sparkles } from "lucide-react";
import { goAuth } from "@/utils/app.ts";
import { includingModelFromPlan } from "@/conf/subscription.tsx";
import { subscriptionDataSelector } from "@/store/globals.ts";

function GetModel(name: string): Model {
return supportModels.find((model) => model.id === name) as Model;
Expand All @@ -28,8 +29,8 @@ type ModelSelectorProps = {
side?: "left" | "right" | "top" | "bottom";
};

function filterModel(model: Model, level: number) {
if (includingModelFromPlan(level, model.id)) {
function filterModel(data: Plans, model: Model, level: number) {
if (includingModelFromPlan(data, level, model.id)) {
return {
name: model.id,
value: model.name,
Expand All @@ -55,6 +56,8 @@ function ModelFinder(props: ModelSelectorProps) {
const student = useSelector(teenagerSelector);
const list = useSelector(selectModelList);

const subscriptionData = useSelector(subscriptionDataSelector);

const [sync, setSync] = useState<boolean>(false);

modelEvent.bind((target: string) => {
Expand All @@ -77,7 +80,10 @@ function ModelFinder(props: ModelSelectorProps) {
} as Model);

return [
...raw.map((model: Model): SelectItemProps => filterModel(model, level)),
...raw.map(
(model: Model): SelectItemProps =>
filterModel(subscriptionData, model, level),
),
{
icon: <Sparkles size={16} />,
name: "market",
Expand Down
9 changes: 6 additions & 3 deletions app/src/components/home/ModelMarket.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
X,
} from "lucide-react";
import React, { useMemo, useState } from "react";
import { subscriptionData, supportModels } from "@/conf";
import { supportModels } from "@/conf";
import { isUrl, splitList } from "@/utils/base.ts";
import { Model } from "@/api/types.ts";
import { useDispatch, useSelector } from "react-redux";
Expand All @@ -37,7 +37,7 @@ import {
Draggable,
DropResult,
} from "react-beautiful-dnd";
import { savePreferenceModels } from "@/utils/storage.ts";
import { savePreferenceModels } from "@/conf/storage.ts";
import { cn } from "@/components/ui/lib/utils.ts";
import { Badge } from "@/components/ui/badge.tsx";
import {
Expand All @@ -49,6 +49,7 @@ import {
import { useMobile } from "@/utils/device.ts";
import Tips from "@/components/Tips.tsx";
import { includingModelFromPlan } from "@/conf/subscription.tsx";
import { subscriptionDataSelector } from "@/store/globals.ts";

type SearchBarProps = {
value: string;
Expand Down Expand Up @@ -115,14 +116,16 @@ function ModelItem({
const student = useSelector(teenagerSelector);
const auth = useSelector(selectAuthenticated);

const subscriptionData = useSelector(subscriptionDataSelector);

const state = useMemo(() => {
if (current === model.id) return 0;
if (list.includes(model.id)) return 1;
return 2;
}, [model, current, list]);

const pro = useMemo(() => {
return includingModelFromPlan(level, model.id);
return includingModelFromPlan(subscriptionData, level, model.id);
}, [subscriptionData, model, level, student]);

const avatar = useMemo(() => {
Expand Down
26 changes: 20 additions & 6 deletions app/src/components/home/subscription/BuyDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ import { deeptrainEndpoint, useDeeptrain } from "@/conf/env.ts";
import { AppDispatch } from "@/store";
import { openDialog } from "@/store/quota.ts";
import { getPlanPrice } from "@/conf/subscription.tsx";
import { Plans } from "@/api/types.ts";
import { subscriptionDataSelector } from "@/store/globals.ts";

function countPrice(base: number, month: number): number {
const price = getPlanPrice(base) * month;
function countPrice(data: Plans, base: number, month: number): number {
const price = getPlanPrice(data, base) * month;
if (month >= 36) {
return price * 0.7;
} else if (month >= 12) {
Expand All @@ -43,11 +45,12 @@ function countPrice(base: number, month: number): number {
}

function countUpgradePrice(
data: Plans,
level: number,
target: number,
days: number,
): number {
const bias = getPlanPrice(target) - getPlanPrice(level);
const bias = getPlanPrice(data, target) - getPlanPrice(data, level);
const v = (bias / 30) * days;
return v > 0 ? v + 1 : 0; // time count offset
}
Expand Down Expand Up @@ -127,6 +130,8 @@ export function Upgrade({ level, current }: UpgradeProps) {
const dispatch = useDispatch();
const { toast } = useToast();

const subscriptionData = useSelector(subscriptionDataSelector);

const isCurrent = useMemo(() => current === level, [current, level]);
const isUpgrade = useMemo(() => current < level, [current, level]);

Expand Down Expand Up @@ -170,13 +175,17 @@ export function Upgrade({ level, current }: UpgradeProps) {
</SelectContent>
</Select>
<p className={`price`}>
{t("sub.price", { price: countPrice(level, month).toFixed(2) })}
{t("sub.price", {
price: countPrice(subscriptionData, level, month).toFixed(2),
})}

{useDeeptrain && (
<span className={`tax`}>
&nbsp; (
{t("sub.price-tax", {
price: (countPrice(level, month) * 0.25).toFixed(1),
price: (
countPrice(subscriptionData, level, month) * 0.25
).toFixed(1),
})}
)
</span>
Expand Down Expand Up @@ -223,7 +232,12 @@ export function Upgrade({ level, current }: UpgradeProps) {
{isUpgrade && (
<p className={`price`}>
{t("sub.upgrade-price", {
price: countUpgradePrice(current, level, expired).toFixed(2),
price: countUpgradePrice(
subscriptionData,
current,
level,
expired,
).toFixed(2),
})}
</p>
)}
Expand Down
6 changes: 2 additions & 4 deletions app/src/conf/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Model, Plans } from "@/api/types.ts";
import { Model } from "@/api/types.ts";
import {
getDev,
getRestApi,
getTokenField,
getWebsocketApi,
} from "@/conf/env.ts";
import { syncSiteInfo } from "@/admin/api/info.ts";
import { getOfflineModels, loadPreferenceModels } from "@/utils/storage.ts";
import { getOfflineModels, loadPreferenceModels } from "@/conf/storage.ts";
import { setAxiosConfig } from "@/conf/api.ts";

export const version = "3.8.6"; // version of the current build
Expand All @@ -20,8 +20,6 @@ export let websocketEndpoint: string = getWebsocketApi(deploy); // api endpoint
export let supportModels: Model[] = loadPreferenceModels(getOfflineModels()); // support models in model market of the current site
export let allModels: string[] = supportModels.map((model) => model.id); // all support model id list of the current site

export let subscriptionData: Plans = []; // subscription data of the current site

setAxiosConfig({
endpoint: apiEndpoint,
token: tokenField,
Expand Down
21 changes: 20 additions & 1 deletion app/src/utils/storage.ts → app/src/conf/storage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getMemory, setMemory } from "@/utils/memory.ts";
import { Model } from "@/api/types.ts";
import { Model, Plan } from "@/api/types.ts";

export function savePreferenceModels(models: Model[]): void {
setMemory("model_preference", models.map((item) => item.id).join(","));
Expand Down Expand Up @@ -68,3 +68,22 @@ export function getOfflineModels(): Model[] {
const memory = getMemory("model_offline");
return memory && memory.length ? parseOfflineModels(memory) : [];
}

export function setOfflinePlans(plans: Plan[]): void {
setMemory("plan_offline", JSON.stringify(plans));
}

export function parseOfflinePlans(plans: string): Plan[] {
try {
const parsed = JSON.parse(plans);
if (!Array.isArray(parsed)) return [];
return parsed.filter((item) => typeof item === "object");
} catch {
return [];
}
}

export function getOfflinePlans(): Plan[] {
const memory = getMemory("plan_offline");
return memory && memory.length ? parseOfflinePlans(memory) : [];
}
29 changes: 14 additions & 15 deletions app/src/conf/subscription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import {
AudioLines,
} from "lucide-react";
import React, { useMemo } from "react";
import { subscriptionData } from "@/conf/index.ts";
import { Plan } from "@/api/types.ts";
import { Plan, Plans } from "@/api/types.ts";
import Icon from "@/components/utils/Icon.tsx";

export const subscriptionIcons: Record<string, React.ReactElement> = {
Expand Down Expand Up @@ -39,25 +38,25 @@ export function SubscriptionIcon({ type, className }: SubscriptionIconProps) {
return <Icon icon={icon} className={className} />;
}

export function getPlan(level: number): Plan {
const raw = subscriptionData.filter((item) => item.level === level);
return raw.length > 0
? raw[0]
: subscriptionData.length
? subscriptionData[0]
: { level: 0, price: 0, items: [] };
export function getPlan(data: Plans, level: number): Plan {
const raw = data.filter((item) => item.level === level);
return raw.length > 0 ? raw[0] : { level: 0, price: 0, items: [] };
}

export function getPlanModels(level: number): string[] {
return getPlan(level).items.flatMap((item) => item.models);
export function getPlanModels(data: Plans, level: number): string[] {
return getPlan(data, level).items.flatMap((item) => item.models);
}

export function includingModelFromPlan(level: number, model: string): boolean {
return getPlanModels(level).includes(model);
export function includingModelFromPlan(
data: Plans,
level: number,
model: string,
): boolean {
return getPlanModels(data, level).includes(model);
}

export function getPlanPrice(level: number): number {
return getPlan(level).price;
export function getPlanPrice(data: Plans, level: number): number {
return getPlan(data, level).price;
}

export function getPlanName(level: number): string {
Expand Down
Loading

0 comments on commit aacc6b9

Please sign in to comment.