update mobile adapter and store

This commit is contained in:
Zhang Minghan 2023-11-14 10:01:45 +08:00
parent 3e03da83c6
commit fcec3d8a4a
18 changed files with 118 additions and 88 deletions

View File

@ -61,7 +61,7 @@ export const modelColorMapper: Record<string, string> = {
"code-llama-13b": "#01a9f0",
"code-llama-7b": "#01a9f0",
"hunyuan": "#0052d9"
hunyuan: "#0052d9",
};
export function getModelColor(model: string): string {

View File

@ -23,7 +23,7 @@
height: max-content;
padding: 1rem 2rem;
@media (max-width: 668px) {
@media (max-width: 940px) {
flex-direction: column;
padding: 1rem;
@ -103,6 +103,7 @@
flex-direction: row;
flex-wrap: wrap;
padding: 1rem 1.5rem;
width: 100%;
.chart-box {
width: calc(50% - 1rem);
@ -116,5 +117,13 @@
border: 1px solid hsl(var(--border));
user-select: none;
box-shadow: 0 0 1rem 0 hsla(var(--foreground), 0.1);
@media (max-width: 680px) {
width: 100%;
}
}
@media (max-width: 680px) {
padding: 1rem;
}
}

View File

@ -14,6 +14,10 @@
}
}
&.mobile {
padding: 1rem;
}
.pagination {
display: flex;
justify-content: center;

View File

@ -36,12 +36,35 @@
border-radius: var(--radius);
font-size: 16px;
&:before {
content: '';
display: inline-block;
width: 4px;
height: 2rem;
background: hsl(var(--text));
border-radius: var(--radius);
margin-right: 0;
opacity: 0;
transition: 0.25s;
transition-property: opacity, margin-right;
}
&:hover {
background: var(--conversation-card-hover);
&:before {
opacity: .25;
margin-right: 0.75rem;
}
}
&.active {
background: var(--conversation-card-active);
&:before {
opacity: 1;
margin-right: 0.75rem;
}
}
& > * {

View File

@ -32,6 +32,7 @@
--border: 37 26% 83%;
--border-hover: 37 26% 78%;
--border-active: 37 26% 73%;
--input: 37 26% 90%;
--input-unread: 37 26% 70%;
--ring: 222.2 84% 4.9%;
@ -80,6 +81,7 @@
--border: 240 3.7% 15.9%;
--border-hover: 240 3.7% 20.9%;
--border-active: 240 3.7% 25.9%;
--input: 240 3.7% 15.9%;
--input-unread: 240 3.7% 50%;
--ring: 240 4.9% 83.9%;

View File

@ -122,11 +122,11 @@
max-width: 100%;
padding: 8px 16px;
border-radius: var(--radius);
border: 1px solid hsl(var(--border));
border: 1px solid hsl(var(--border-hover));
transition: 0.25s linear;
&:hover {
border-color: hsl(var(--border-hover));
border-color: hsl(var(--border-active));
}
}

View File

@ -1,10 +1,11 @@
import { useSelector } from "react-redux";
import { selectMenu } from "@/store/menu.ts";
import { useDispatch, useSelector } from "react-redux";
import { closeMenu, selectMenu } from "@/store/menu.ts";
import React, { useMemo } from "react";
import { LayoutDashboard, Settings, Users } from "lucide-react";
import router from "@/router.tsx";
import { useLocation } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { mobile } from "@/utils/device.ts";
type MenuItemProps = {
title: string;
@ -14,6 +15,7 @@ type MenuItemProps = {
function MenuItem({ title, icon, path }: MenuItemProps) {
const location = useLocation();
const dispatch = useDispatch();
const active = useMemo(
() =>
location.pathname === `/admin${path}` ||
@ -21,11 +23,13 @@ function MenuItem({ title, icon, path }: MenuItemProps) {
[location.pathname, path],
);
const redirect = async () => {
if (mobile) dispatch(closeMenu());
await router.navigate(`/admin${path}`);
};
return (
<div
className={`menu-item ${active ? "active" : ""}`}
onClick={() => router.navigate(`/admin${path}`)}
>
<div className={`menu-item ${active ? "active" : ""}`} onClick={redirect}>
<div className={`menu-item-icon`}>{icon}</div>
<div className={`menu-item-title`}>{title}</div>
</div>

View File

@ -1,6 +1,6 @@
import { useTranslation } from "react-i18next";
import { useDispatch, useSelector } from "react-redux";
import { logout, selectUsername } from "@/store/auth.ts";
import { logout, selectAdmin, selectUsername } from "@/store/auth.ts";
import { openDialog as openQuotaDialog, quotaSelector } from "@/store/quota.ts";
import {
DropdownMenu,
@ -19,12 +19,14 @@ import {
Gift,
ListStart,
Plug,
Shield,
} from "lucide-react";
import { openDialog as openSub } from "@/store/subscription.ts";
import { openDialog as openPackageDialog } from "@/store/package.ts";
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";
type MenuBarProps = {
children: React.ReactNode;
@ -36,6 +38,7 @@ function MenuBar({ children, className }: MenuBarProps) {
const dispatch = useDispatch();
const username = useSelector(selectUsername);
const quota = useSelector(quotaSelector);
const admin = useSelector(selectAdmin);
return (
<DropdownMenu>
@ -71,6 +74,12 @@ function MenuBar({ children, className }: MenuBarProps) {
<Plug className={`h-4 w-4 mr-1`} />
{t("api.title")}
</DropdownMenuItem>
{admin && (
<DropdownMenuItem onClick={() => router.navigate("/admin")}>
<Shield className={`h-4 w-4 mr-1`} />
{t("admin.users")}
</DropdownMenuItem>
)}
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Button

View File

@ -9,7 +9,6 @@ import {
FolderKanban,
Link,
Newspaper,
Shield,
Users2,
} from "lucide-react";
import router from "@/router.tsx";
@ -21,10 +20,8 @@ import {
DialogTitle,
} from "@/components/ui/dialog.tsx";
import { getLanguage } from "@/i18n.ts";
import { selectAdmin } from "@/store/auth.ts";
function ChatSpace() {
const admin = useSelector(selectAdmin);
const [open, setOpen] = useState(false);
const { t } = useTranslation();
const subscription = useSelector(isSubscribedSelector);
@ -85,12 +82,6 @@ function ChatSpace() {
</DialogContent>
</Dialog>
<div className={`space-footer`}>
{admin && (
<p>
<Shield className={`h-3 w-3 mr-1`} />
<a onClick={() => router.navigate("/admin")}>{t("admin.users")}</a>
</p>
)}
<p>
<Link className={`h-3 w-3 mr-1`} />
<a

View File

@ -14,8 +14,7 @@ export function parseProgressbar(data: string) {
<p className={`text-primary select-none text-center`}>
Generating: {progress < 0 ? 0 : progress.toFixed()}%
</p>
{
progress > 0 && (
{progress > 0 && (
<div
className={`progressbar relative h-4 w-full overflow-hidden rounded-full bg-muted min-w-[20vw]`}
role={`progressbar`}
@ -30,8 +29,7 @@ export function parseProgressbar(data: string) {
data-max={100}
/>
</div>
)
}
)}
</div>
);
}

View File

@ -8,7 +8,7 @@ import {
} from "@/utils/env.ts";
import { getMemory } from "@/utils/memory.ts";
export const version = "3.6.22";
export const version = "3.6.23";
export const dev: boolean = getDev();
export const deploy: boolean = true;
export let rest_api: string = getRestApi(deploy);

View File

@ -14,14 +14,15 @@ import {
certSelector,
closeDialog,
dialogSelector,
refreshPackageTask,
refreshPackage,
setDialog,
teenagerSelector,
} from "@/store/package.ts";
import { useEffect } from "react";
import { Gift } from "lucide-react";
import { Separator } from "@/components/ui/separator.tsx";
import { Badge } from "@/components/ui/badge.tsx";
import { useEffectAsync } from "@/utils/hook.ts";
import { selectAuthenticated } from "@/store/auth.ts";
function Package() {
const { t } = useTranslation();
@ -29,10 +30,15 @@ function Package() {
const open = useSelector(dialogSelector);
const cert = useSelector(certSelector);
const teenager = useSelector(teenagerSelector);
const auth = useSelector(selectAuthenticated);
useEffect(() => {
refreshPackageTask(dispatch);
}, []);
useEffectAsync(async () => {
if (!auth) return;
const task = setInterval(() => refreshPackage(dispatch), 20000);
await refreshPackage(dispatch);
return () => clearInterval(task);
}, [auth]);
return (
<Dialog open={open} onOpenChange={(open) => dispatch(setDialog(open))}>

View File

@ -2,11 +2,11 @@ import { useDispatch, useSelector } from "react-redux";
import {
closeDialog,
dialogSelector,
refreshQuotaTask,
refreshQuota,
setDialog,
} from "@/store/quota.ts";
import { useTranslation } from "react-i18next";
import { useEffect, useState } from "react";
import { useState } from "react";
import {
Dialog,
DialogContent,
@ -40,6 +40,8 @@ import {
import { AlertDialogTitle } from "@radix-ui/react-alert-dialog";
import { buyQuota } from "@/conversation/addition.ts";
import { useToast } from "@/components/ui/use-toast.ts";
import { useEffectAsync } from "@/utils/hook.ts";
import { selectAuthenticated } from "@/store/auth.ts";
type AmountComponentProps = {
amount: number;
@ -78,10 +80,16 @@ function Quota() {
const [current, setCurrent] = useState(1);
const [amount, setAmount] = useState(10);
const open = useSelector(dialogSelector);
const auth = useSelector(selectAuthenticated);
const dispatch = useDispatch();
useEffect(() => {
refreshQuotaTask(dispatch);
}, []);
useEffectAsync(async () => {
if (!auth) return;
const task = setInterval(() => refreshQuota(dispatch), 5000);
await refreshQuota(dispatch);
return () => clearInterval(task);
}, [auth]);
return (
<Dialog

View File

@ -4,7 +4,6 @@ import {
expiredSelector,
isSubscribedSelector,
refreshSubscription,
refreshSubscriptionTask,
setDialog,
usageSelector,
} from "@/store/subscription.ts";
@ -20,7 +19,7 @@ import {
import { useDispatch, useSelector } from "react-redux";
import { useTranslation } from "react-i18next";
import { useToast } from "@/components/ui/use-toast.ts";
import React, { useEffect } from "react";
import React from "react";
import "@/assets/pages/subscription.less";
import {
BookText,
@ -52,6 +51,8 @@ import {
} from "@/components/ui/select.tsx";
import { Badge } from "@/components/ui/badge.tsx";
import { buySubscription } from "@/conversation/addition.ts";
import { useEffectAsync } from "@/utils/hook.ts";
import { selectAuthenticated } from "@/store/auth.ts";
function calc_prize(month: number): number {
const base = 32 * month;
@ -169,10 +170,16 @@ function Subscription() {
const enterprise = useSelector(enterpriseSelector);
const expired = useSelector(expiredSelector);
const usage = useSelector(usageSelector);
const auth = useSelector(selectAuthenticated);
const dispatch = useDispatch();
useEffect(() => {
refreshSubscriptionTask(dispatch);
}, []);
useEffectAsync(async () => {
if (!auth) return;
const task = setInterval(() => refreshSubscription(dispatch), 10000);
await refreshSubscription(dispatch);
return () => clearInterval(task);
}, [auth]);
return (
<Dialog

View File

@ -7,12 +7,13 @@ import {
import { useTranslation } from "react-i18next";
import InvitationTable from "@/components/admin/InvitationTable.tsx";
import UserTable from "@/components/admin/UserTable.tsx";
import {mobile} from "@/utils/device.ts";
function Users() {
const { t } = useTranslation();
return (
<div className={`user-interface`}>
<div className={`user-interface ${mobile ? 'mobile' : ''}`}>
<Card>
<CardHeader className={`select-none`}>
<CardTitle>{t("admin.users")}</CardTitle>

View File

@ -42,17 +42,7 @@ export const dialogSelector = (state: any): boolean => state.package.dialog;
export const certSelector = (state: any): boolean => state.package.cert;
export const teenagerSelector = (state: any): boolean => state.package.teenager;
const refreshPackage = async (dispatch: AppDispatch) => {
const current = new Date().getTime(); //@ts-ignore
if (window.hasOwnProperty("package") && current - window.package < 2500)
return; //@ts-ignore
window.package = current;
export const refreshPackage = async (dispatch: AppDispatch) => {
const response = await getPackage();
if (response.status) dispatch(refreshState(response));
};
export const refreshPackageTask = (dispatch: AppDispatch) => {
setInterval(() => refreshPackage(dispatch), 20000);
refreshPackage(dispatch).then();
};

View File

@ -50,16 +50,7 @@ export const quotaValueSelector = (state: RootState): number =>
export const quotaSelector = (state: RootState): string =>
state.quota.quota.toFixed(2);
const refreshQuota = async (dispatch: AppDispatch) => {
const current = new Date().getTime(); //@ts-ignore
if (window.hasOwnProperty("quota") && current - window.quota < 5000) return; //@ts-ignore
window.quota = current;
export const refreshQuota = async (dispatch: AppDispatch) => {
const response = await axios.get("/quota");
if (response.data.status) dispatch(setQuota(response.data.quota));
};
export const refreshQuotaTask = (dispatch: AppDispatch) => {
setInterval(() => refreshQuota(dispatch), 8000);
refreshQuota(dispatch).then();
};

View File

@ -55,19 +55,6 @@ export const enterpriseSelector = (state: any): boolean =>
state.subscription.enterprise;
export const refreshSubscription = async (dispatch: AppDispatch) => {
const current = new Date().getTime(); //@ts-ignore
if (
window.hasOwnProperty("subscription") && //@ts-ignore
current - window.subscription < 15000
)
return; //@ts-ignore
window.subscription = current;
const response = await getSubscription();
if (response.status) dispatch(updateSubscription(response));
};
export const refreshSubscriptionTask = (dispatch: AppDispatch) => {
setInterval(() => refreshSubscription(dispatch), 20000);
refreshSubscription(dispatch).then();
};