Update sidebar (#1188)

* refactor

* fix build

* refactor

* refactor

* display deposit in sidebar header menu

* minor fix

* fix

* fix document locate
This commit is contained in:
an-lee
2024-11-19 09:34:40 +08:00
committed by GitHub
parent 73eb02911f
commit 59bcb437f8
15 changed files with 422 additions and 362 deletions

View File

@@ -20,6 +20,6 @@
},
"dependencies": {
"cheerio": "^1.0.0",
"swiper": "^11.1.14"
"swiper": "^11.1.15"
}
}

View File

View File

View File

@@ -40,7 +40,7 @@
"@electron-forge/plugin-vite": "<7.5.0",
"@electron-forge/publisher-github": "<7.5.0",
"@electron/fuses": "^1.8.0",
"@playwright/test": "^1.48.2",
"@playwright/test": "^1.49.0",
"@tailwindcss/typography": "^0.5.15",
"@types/ahoy.js": "^0.4.2",
"@types/autosize": "^4.0.3",
@@ -62,8 +62,8 @@
"@types/unzipper": "^0.10.10",
"@types/validator": "^13.12.2",
"@types/wavesurfer.js": "^6.0.12",
"@typescript-eslint/eslint-plugin": "^8.14.0",
"@typescript-eslint/parser": "^8.14.0",
"@typescript-eslint/eslint-plugin": "^8.15.0",
"@typescript-eslint/parser": "^8.15.0",
"@vitejs/plugin-react": "^4.3.3",
"autoprefixer": "^10.4.20",
"electron": "^33.2.0",

View File

@@ -199,7 +199,12 @@ export const DocumentPlayer = () => {
data-tooltip-id="global-tooltip"
variant="outline"
size="icon"
onClick={() => locateSegment(playingSegment.id)}
onClick={() => {
const el = locateSegment(playingSegment.id);
if (el) {
el.scrollIntoView({ behavior: "smooth" });
}
}}
>
<LocateFixedIcon className="w-4 h-4" />
</Button>

View File

@@ -1,2 +1,3 @@
export * from "./layout";
export * from "./title-bar";
export * from "./sidebar";

View File

@@ -32,6 +32,7 @@ import {
PanelLeftCloseIcon,
ChevronsUpDownIcon,
LogOutIcon,
CreditCardIcon,
} from "lucide-react";
import { useLocation, Link, useNavigate } from "react-router-dom";
import { t } from "i18next";
@@ -46,7 +47,7 @@ export const Sidebar = () => {
const activeTab = location.pathname;
const { EnjoyApp, cable, displayPreferences, setDisplayPreferences } =
useContext(AppSettingsProviderContext);
const [isOpen, setIsOpen] = useState(true);
const [isCollapsed, setIsCollapsed] = useState(false);
useEffect(() => {
if (!cable) return;
@@ -61,14 +62,14 @@ export const Sidebar = () => {
// Save the sidebar state to cache
useEffect(() => {
EnjoyApp.cacheObjects.set("sidebarOpen", isOpen);
}, [isOpen]);
EnjoyApp.cacheObjects.set("sidebarOpen", isCollapsed);
}, [isCollapsed]);
// Restore the sidebar state from cache
useEffect(() => {
EnjoyApp.cacheObjects.get("sidebarOpen").then((value) => {
if (value !== undefined) {
setIsOpen(!!value);
setIsCollapsed(!!value);
}
});
}, []);
@@ -76,17 +77,17 @@ export const Sidebar = () => {
return (
<div
className={`h-[calc(100vh-2rem)] pt-8 transition-all relative draggable-region ${
isOpen ? "w-48" : "w-12"
isCollapsed ? "w-12" : "w-48"
}`}
data-testid="sidebar"
>
<div
className={`fixed top-0 left-0 h-full bg-muted ${
isOpen ? "w-48" : "w-12"
isCollapsed ? "w-12" : "w-48"
}`}
>
<ScrollArea className="w-full h-full pb-12 pt-8">
<SidebarHeader isOpen={isOpen} />
<SidebarHeader isCollapsed={isCollapsed} />
<div className="grid gap-2 mb-4">
<SidebarItem
href="/"
@@ -94,7 +95,7 @@ export const Sidebar = () => {
tooltip={t("sidebar.home")}
active={activeTab === "/"}
Icon={HomeIcon}
isOpen={isOpen}
isCollapsed={isCollapsed}
/>
<SidebarItem
@@ -103,7 +104,7 @@ export const Sidebar = () => {
tooltip={t("sidebar.chats")}
active={activeTab.startsWith("/chats")}
Icon={MessagesSquareIcon}
isOpen={isOpen}
isCollapsed={isCollapsed}
/>
<SidebarItem
@@ -112,7 +113,7 @@ export const Sidebar = () => {
tooltip={t("sidebar.courses")}
active={activeTab.startsWith("/courses")}
Icon={GraduationCapIcon}
isOpen={isOpen}
isCollapsed={isCollapsed}
/>
<Separator />
@@ -123,7 +124,7 @@ export const Sidebar = () => {
tooltip={t("sidebar.audios")}
active={activeTab.startsWith("/audios")}
Icon={HeadphonesIcon}
isOpen={isOpen}
isCollapsed={isCollapsed}
/>
<SidebarItem
@@ -132,7 +133,7 @@ export const Sidebar = () => {
tooltip={t("sidebar.videos")}
active={activeTab.startsWith("/videos")}
Icon={VideoIcon}
isOpen={isOpen}
isCollapsed={isCollapsed}
/>
<SidebarItem
@@ -141,7 +142,7 @@ export const Sidebar = () => {
tooltip={t("sidebar.documents")}
active={activeTab.startsWith("/documents")}
Icon={NewspaperIcon}
isOpen={isOpen}
isCollapsed={isCollapsed}
/>
<Separator />
@@ -153,7 +154,7 @@ export const Sidebar = () => {
active={activeTab.startsWith("/conversations")}
Icon={BotIcon}
testid="sidebar-conversations"
isOpen={isOpen}
isCollapsed={isCollapsed}
/>
<SidebarItem
@@ -163,7 +164,7 @@ export const Sidebar = () => {
active={activeTab.startsWith("/pronunciation_assessments")}
Icon={SpeechIcon}
testid="sidebar-pronunciation-assessments"
isOpen={isOpen}
isCollapsed={isCollapsed}
/>
<SidebarItem
@@ -172,7 +173,7 @@ export const Sidebar = () => {
tooltip={t("sidebar.notes")}
active={activeTab === "/notes"}
Icon={NotebookPenIcon}
isOpen={isOpen}
isCollapsed={isCollapsed}
/>
<SidebarItem
@@ -181,7 +182,7 @@ export const Sidebar = () => {
tooltip={t("sidebar.vocabulary")}
active={activeTab.startsWith("/vocabulary")}
Icon={BookMarkedIcon}
isOpen={isOpen}
isCollapsed={isCollapsed}
/>
<Separator />
@@ -192,7 +193,7 @@ export const Sidebar = () => {
variant={displayPreferences ? "default" : "ghost"}
id="preferences-button"
className={`w-full ${
isOpen ? "justify-start" : "justify-center"
isCollapsed ? "justify-center" : "justify-start"
}`}
data-tooltip-id="global-tooltip"
data-tooltip-content={t("sidebar.preferences")}
@@ -200,7 +201,7 @@ export const Sidebar = () => {
onClick={() => setDisplayPreferences(true)}
>
<SettingsIcon className="size-4" />
{isOpen && (
{!isCollapsed && (
<span className="ml-2"> {t("sidebar.preferences")} </span>
)}
</Button>
@@ -228,16 +229,18 @@ export const Sidebar = () => {
size="sm"
variant="ghost"
className={`w-full non-draggable-region ${
isOpen ? "justify-start" : "justify-center"
isCollapsed ? "justify-center" : "justify-start"
}`}
onClick={() => setIsOpen(!isOpen)}
onClick={() => setIsCollapsed(!isCollapsed)}
>
{isOpen ? (
<PanelLeftCloseIcon className="size-4" />
) : (
{isCollapsed ? (
<PanelLeftOpenIcon className="size-4" />
) : (
<PanelLeftCloseIcon className="size-4" />
)}
{!isCollapsed && (
<span className="ml-2"> {t("sidebar.collapse")} </span>
)}
{isOpen && <span className="ml-2"> {t("sidebar.collapse")} </span>}
</Button>
</div>
</div>
@@ -252,9 +255,9 @@ const SidebarItem = (props: {
active: boolean;
Icon: LucideIcon;
testid?: string;
isOpen: boolean;
isCollapsed: boolean;
}) => {
const { href, label, tooltip, active, Icon, testid, isOpen } = props;
const { href, label, tooltip, active, Icon, testid, isCollapsed } = props;
return (
<Link
@@ -268,38 +271,47 @@ const SidebarItem = (props: {
<Button
size="sm"
variant={active ? "default" : "ghost"}
className={`w-full ${isOpen ? "justify-start" : "justify-center"}`}
className={`w-full ${isCollapsed ? "justify-center" : "justify-start"}`}
>
<Icon className="size-4" />
{isOpen && <span className="ml-2">{label}</span>}
{!isCollapsed && <span className="ml-2">{label}</span>}
</Button>
</Link>
);
};
const SidebarHeader = (props: { isOpen: boolean }) => {
const { isOpen } = props;
const { user, logout } = useContext(AppSettingsProviderContext);
const SidebarHeader = (props: { isCollapsed: boolean }) => {
const { isCollapsed } = props;
const { user, logout, refreshAccount, setDisplayDepositDialog } = useContext(
AppSettingsProviderContext
);
const [open, setOpen] = useState(false);
const navigate = useNavigate();
useEffect(() => {
if (open) {
refreshAccount?.();
}
}, [open]);
if (!user) {
return null;
}
return (
<div className="py-3 px-1 sticky top-0 bg-muted z-10 non-draggable-region">
<DropdownMenu>
<DropdownMenu open={open} onOpenChange={setOpen}>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
className={`w-full h-12 hover:bg-background ${
isOpen ? "justify-start" : "justify-center px-1"
isCollapsed ? "justify-center px-1" : "justify-start"
}`}
>
<Avatar className="size-8">
<AvatarImage src={user.avatarUrl} />
</Avatar>
{isOpen && (
{!isCollapsed && (
<>
<div className="ml-2 flex flex-col leading-none">
<span className="text-left text-sm font-medium line-clamp-1">
@@ -334,6 +346,14 @@ const SidebarHeader = (props: { isOpen: boolean }) => {
<UsersRoundIcon className="size-4 ml-auto" />
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
onSelect={() => setDisplayDepositDialog(true)}
className="cursor-pointer"
>
<span className="flex-1 truncate">${user.balance}</span>
<CreditCardIcon className="size-4 ml-auto" />
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onSelect={logout} className="cursor-pointer">
<span>{t("logout")}</span>
<LogOutIcon className="size-4 ml-auto" />

View File

@@ -4,7 +4,6 @@ export * from "./markdown-wrapper";
export * from "./no-records-found";
export * from "./page-placeholder";
export * from "./universal-player";
export * from "./sidebar";
export * from "./wavesurfer-player";
export * from "./tts-form";
export * from "./gpt-form";

View File

@@ -6,248 +6,43 @@ import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
DialogClose,
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
toast,
Separator,
} from "@renderer/components/ui";
import { LoaderSpin } from "@renderer/components";
import { LoaderIcon } from "lucide-react";
import { formatDateTime } from "@/renderer/lib/utils";
import Chart from "chart.js/auto";
export const BalanceSettings = () => {
const { webApi, EnjoyApp } = useContext(AppSettingsProviderContext);
const [balance, setBalance] = useState<number>(0);
const [depositAmount, setDepositAmount] = useState<number>(10);
const [loading, setLoading] = useState<boolean>(false);
const [paymentCreated, setPaymentCreated] = useState<boolean>(false);
const [payments, setPayments] = useState<any[]>([]);
const { refreshAccount, user, setDisplayDepositDialog } = useContext(
AppSettingsProviderContext
);
const [displayUsage, setDisplayUsage] = useState<boolean>(false);
const refreshPayments = () => {
webApi
.payments({
paymentType: "deposit",
})
.then(({ payments }) => {
setPayments(payments);
})
.catch((error) => {
toast.error(error.message);
});
};
const refreshBalance = () => {
webApi.me().then((user) => {
setBalance(user.balance);
});
};
const createDepositPayment = (processor = "stripe") => {
if (loading) return;
setLoading(true);
webApi
.createPayment({
amount: depositAmount,
paymentType: "deposit",
processor,
})
.then((payment) => {
if (payment?.payUrl) {
setPaymentCreated(true);
EnjoyApp.shell.openExternal(payment.payUrl);
}
})
.catch((error) => {
toast.error(error.message);
})
.finally(() => {
setLoading(false);
});
};
useEffect(() => {
refreshBalance();
refreshAccount?.();
}, []);
if (!balance) return null;
if (!user?.balance) return null;
return (
<div className="flex items-start justify-between py-4">
<div className="">
<div className="mb-2">{t("balance")}</div>
<div className="text-sm text-muted-foreground mb-2">${balance}</div>
<div className="text-sm text-muted-foreground mb-2">
${user.balance}
</div>
</div>
<div className="flex gap-2 items-center">
<Dialog
onOpenChange={(value) => {
if (value) {
refreshPayments();
} else {
setPaymentCreated(false);
refreshBalance();
}
}}
<Button
variant="secondary"
size="sm"
className=""
onClick={() => setDisplayDepositDialog(true)}
>
<DialogTrigger asChild>
<Button
onClick={() => setPaymentCreated(false)}
variant="secondary"
size="sm"
className=""
>
{t("deposit")}
</Button>
</DialogTrigger>
<DialogContent className="max-h-full overflow-y-auto">
<DialogHeader>
<DialogTitle>{t("deposit")}</DialogTitle>
<DialogDescription>{t("depositDescription")}</DialogDescription>
</DialogHeader>
{paymentCreated ? (
<>
<LoaderSpin />
<div className="text-center">
{t("pleaseCompletePaymentInPopupWindow")}
</div>
</>
) : (
<>
<div className="grid grid-cols-4 gap-4">
{[2, 10, 50, 100].map((amount) => (
<div
className={`text-xl w-full h-20 border rounded-md flex items-center justify-center cursor-pointer shadow hover:bg-gray-100 hover:dark:text-primary-foreground transition-colors duration-200 ease-in-out ${
amount == depositAmount
? "bg-gray-100 dark:text-primary-foreground"
: ""
}`}
key={`deposit-amount-${amount}`}
onClick={() => setDepositAmount(amount)}
>
${amount}
</div>
))}
</div>
<div className="text-sm">{t("depositDisclaimer")}</div>
</>
)}
<Separator />
<div className="flex items-center justify-between space-x-4">
<div className="flex items-center w-64 justify-around">
<img
src="assets/usdt.png"
className="w-auto h-8 rounded-full"
/>
<img src="assets/usdc.png" className="w-auto h-8" />
<img src="assets/eth.png" className="w-auto h-8" />
<img src="assets/trx.png" className="w-auto h-8" />
<img src="assets/doge.png" className="w-auto h-8" />
<img src="assets/bnb.png" className="w-auto h-8" />
</div>
<Button
variant="default"
disabled={paymentCreated || loading}
className="w-32 bg-blue-500 hover:bg-blue-600 transition-colors duration-200 ease-in-out"
onClick={() => createDepositPayment("mixin")}
>
{loading && (
<LoaderIcon className="w-4 h-4 mr-2 animate-spin" />
)}
<span>Crypto {t("pay")}</span>
</Button>
</div>
<div className="flex items-center justify-between space-x-4">
<div className="flex items-center w-64 justify-around">
<img src="assets/mastercard.png" className="w-auto h-8" />
<img src="assets/visa.png" className="w-auto h-8" />
<img src="assets/unionpay.png" className="w-auto h-8" />
<img src="assets/alipay.png" className="w-auto h-8" />
<img src="assets/wechatpay.png" className="w-auto h-8" />
</div>
<Button
className="w-32"
variant="default"
disabled={paymentCreated || loading}
onClick={() => createDepositPayment()}
>
{loading && (
<LoaderIcon className="w-4 h-4 mr-2 animate-spin" />
)}
<span>Stripe {t("pay")}</span>
</Button>
</div>
<Separator />
<DialogFooter>
<DialogClose asChild>
<Button variant="secondary">
{paymentCreated ? t("finish") : t("cancel")}
</Button>
</DialogClose>
</DialogFooter>
{payments.length > 0 && (
<div className="">
<Table>
<TableCaption>{t("recentDeposits")}</TableCaption>
<TableHeader>
<TableRow>
<TableHead>ID</TableHead>
<TableHead className="capitalize">
{t("amount")}
</TableHead>
<TableHead className="capitalize">
{t("status")}
</TableHead>
<TableHead className="capitalize">
{t("processor")}
</TableHead>
<TableHead className="capitalize">{t("date")}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{payments.map((payment) => (
<TableRow key={payment.id}>
<TableCell>
<span className="text-xs bg-muted font-mono p-0.5 rounded select-text">
{payment.id.split("-").shift()}
</span>
</TableCell>
<TableCell>${payment.amount}</TableCell>
<TableCell className="">{payment.status}</TableCell>
<TableCell className="capitalize">
{payment.processor}
</TableCell>
<TableCell className="text-xs">
{formatDateTime(payment.createdAt)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
)}
</DialogContent>
</Dialog>
{t("deposit")}
</Button>
<Dialog open={displayUsage} onOpenChange={setDisplayUsage}>
<DialogTrigger asChild>

View File

@@ -0,0 +1,177 @@
import { t } from "i18next";
import { AppSettingsProviderContext } from "@renderer/context";
import { useContext, useEffect, useState } from "react";
import {
Button,
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
toast,
Separator,
} from "@renderer/components/ui";
import { LoaderSpin } from "@renderer/components";
import { LoaderIcon } from "lucide-react";
import { formatDateTime } from "@/renderer/lib/utils";
export const Deposit = () => {
const { webApi, EnjoyApp } = useContext(AppSettingsProviderContext);
const [paymentCreated, setPaymentCreated] = useState<boolean>(false);
const [payments, setPayments] = useState<any[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [depositAmount, setDepositAmount] = useState<number>(10);
const refreshPayments = () => {
webApi
.payments({
paymentType: "deposit",
})
.then(({ payments }) => {
setPayments(payments);
})
.catch((error) => {
toast.error(error.message);
});
};
const createDepositPayment = (processor = "stripe") => {
if (loading) return;
setLoading(true);
webApi
.createPayment({
amount: depositAmount,
paymentType: "deposit",
processor,
})
.then((payment) => {
if (payment?.payUrl) {
setPaymentCreated(true);
EnjoyApp.shell.openExternal(payment.payUrl);
}
})
.catch((error) => {
toast.error(error.message);
})
.finally(() => {
setLoading(false);
});
};
useEffect(() => {
refreshPayments();
}, []);
return (
<>
{paymentCreated ? (
<>
<LoaderSpin />
<div className="text-center">
{t("pleaseCompletePaymentInPopupWindow")}
</div>
</>
) : (
<>
<div className="grid grid-cols-4 gap-4">
{[2, 10, 50, 100].map((amount) => (
<div
className={`text-xl w-full h-20 border rounded-md flex items-center justify-center cursor-pointer shadow hover:bg-gray-100 hover:dark:text-primary-foreground transition-colors duration-200 ease-in-out ${
amount == depositAmount
? "bg-gray-100 dark:text-primary-foreground"
: ""
}`}
key={`deposit-amount-${amount}`}
onClick={() => setDepositAmount(amount)}
>
${amount}
</div>
))}
</div>
<div className="text-sm">{t("depositDisclaimer")}</div>
</>
)}
<Separator />
<div className="flex items-center justify-between space-x-4">
<div className="flex items-center w-64 justify-around">
<img src="assets/usdt.png" className="w-auto h-8 rounded-full" />
<img src="assets/usdc.png" className="w-auto h-8" />
<img src="assets/eth.png" className="w-auto h-8" />
<img src="assets/trx.png" className="w-auto h-8" />
<img src="assets/doge.png" className="w-auto h-8" />
<img src="assets/bnb.png" className="w-auto h-8" />
</div>
<Button
variant="default"
disabled={paymentCreated || loading}
className="w-32 bg-blue-500 hover:bg-blue-600 transition-colors duration-200 ease-in-out"
onClick={() => createDepositPayment("mixin")}
>
{loading && <LoaderIcon className="w-4 h-4 mr-2 animate-spin" />}
<span>Crypto {t("pay")}</span>
</Button>
</div>
<div className="flex items-center justify-between space-x-4">
<div className="flex items-center w-64 justify-around">
<img src="assets/mastercard.png" className="w-auto h-8" />
<img src="assets/visa.png" className="w-auto h-8" />
<img src="assets/unionpay.png" className="w-auto h-8" />
<img src="assets/alipay.png" className="w-auto h-8" />
<img src="assets/wechatpay.png" className="w-auto h-8" />
</div>
<Button
className="w-32"
variant="default"
disabled={paymentCreated || loading}
onClick={() => createDepositPayment()}
>
{loading && <LoaderIcon className="w-4 h-4 mr-2 animate-spin" />}
<span>Stripe {t("pay")}</span>
</Button>
</div>
<Separator />
{payments.length > 0 && (
<Table>
<TableCaption>{t("recentDeposits")}</TableCaption>
<TableHeader>
<TableRow>
<TableHead>ID</TableHead>
<TableHead className="capitalize">{t("amount")}</TableHead>
<TableHead className="capitalize">{t("status")}</TableHead>
<TableHead className="capitalize">{t("processor")}</TableHead>
<TableHead className="capitalize">{t("date")}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{payments.map((payment) => (
<TableRow key={payment.id}>
<TableCell>
<span className="text-xs bg-muted font-mono p-0.5 rounded select-text">
{payment.id.split("-").shift()}
</span>
</TableCell>
<TableCell>${payment.amount}</TableCell>
<TableCell className="">{payment.status}</TableCell>
<TableCell className="capitalize">
{payment.processor}
</TableCell>
<TableCell className="text-xs">
{formatDateTime(payment.createdAt)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)}
</>
);
};

View File

@@ -37,3 +37,5 @@ export * from "./vocabulary-settings";
export * from "./dict-settings";
export * from "./echogarden-stt-settings";
export * from "./deposit";

View File

@@ -15,10 +15,18 @@ import {
AlertDialogFooter,
AlertDialogCancel,
AlertDialogAction,
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogFooter,
DialogClose,
Button,
} from "@renderer/components/ui";
import { t } from "i18next";
import { redirect } from "react-router-dom";
import { Preferences } from "@renderer/components";
import { Deposit } from "@renderer/components";
type AppSettingsProviderState = {
webApi: Client;
@@ -31,6 +39,7 @@ type AppSettingsProviderState = {
libraryPath?: string;
login?: (user: UserType) => void;
logout?: () => void;
refreshAccount?: () => Promise<void>;
setLibraryPath?: (path: string) => Promise<void>;
EnjoyApp: EnjoyAppType;
language?: "en" | "zh-CN";
@@ -51,6 +60,8 @@ type AppSettingsProviderState = {
ipaMappings?: { [key: string]: string };
displayPreferences?: boolean;
setDisplayPreferences?: (display: boolean) => void;
displayDepositDialog?: boolean;
setDisplayDepositDialog?: (display: boolean) => void;
};
const EnjoyApp = window.__ENJOY_APP__;
@@ -88,6 +99,8 @@ export const AppSettingsProvider = ({
IPA_MAPPINGS
);
const [loggingOut, setLoggingOut] = useState<boolean>(false);
const [displayDepositDialog, setDisplayDepositDialog] =
useState<boolean>(false);
const [displayPreferences, setDisplayPreferences] = useState<boolean>(false);
const db = useContext(DbProviderContext);
@@ -245,6 +258,15 @@ export const AppSettingsProvider = ({
setVocabularyConfig(config);
};
const refreshAccount = async () => {
webApi.me().then((u) => {
setUser({
...user,
...u,
});
});
};
useEffect(() => {
if (db.state === "connected") {
fetchLanguages();
@@ -340,6 +362,7 @@ export const AppSettingsProvider = ({
user,
login,
logout: () => setLoggingOut(true),
refreshAccount,
libraryPath,
setLibraryPath: setLibraryPathHandler,
proxy,
@@ -354,6 +377,8 @@ export const AppSettingsProvider = ({
ipaMappings,
displayPreferences,
setDisplayPreferences,
displayDepositDialog,
setDisplayDepositDialog,
}}
>
{children}
@@ -380,6 +405,26 @@ export const AppSettingsProvider = ({
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<Dialog
open={displayDepositDialog}
onOpenChange={setDisplayDepositDialog}
>
<DialogContent className="max-h-full overflow-y-auto">
<DialogHeader>
<DialogTitle>{t("deposit")}</DialogTitle>
<DialogDescription>{t("depositDescription")}</DialogDescription>
</DialogHeader>
{displayDepositDialog && <Deposit />}
<DialogFooter>
<DialogClose asChild>
<Button variant="secondary">{t("close")}</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
</AppSettingsProviderContext.Provider>
);
};

View File

@@ -9,9 +9,7 @@ import {
} from "@renderer/context";
export default () => {
const { initialized, EnjoyApp, user } = useContext(
AppSettingsProviderContext
);
const { initialized, user } = useContext(AppSettingsProviderContext);
const [started, setStarted] = useState(false);
const db = useContext(DbProviderContext);

View File

@@ -15,6 +15,35 @@ export default defineConfig((env) => {
const forgeEnv = env as ConfigEnv<"build">;
const { forgeConfigSelf } = forgeEnv;
const define = getBuildDefine(forgeEnv);
const staticCopyTargets = [
{
src: `lib/youtubedr/${
process.env.PACKAGE_OS_ARCH || os.arch()
}/${os.platform()}/*`,
dest: "lib/youtubedr",
},
{
src: "lib/dictionaries/*",
dest: "lib/dictionaries",
},
{
src: "src/main/db/migrations/*",
dest: "migrations",
},
{
src: "samples/*",
dest: "samples",
},
];
if (os.platform() === "darwin") {
staticCopyTargets.push({
src: `lib/whisper.cpp/${
process.env.PACKAGE_OS_ARCH || os.arch()
}/${os.platform()}/*`,
dest: "lib/whisper",
});
}
const config: UserConfig = {
build: {
lib: {
@@ -44,32 +73,7 @@ export default defineConfig((env) => {
plugins: [
pluginHotRestart("restart"),
viteStaticCopy({
targets: [
{
src: `lib/whisper.cpp/${
process.env.PACKAGE_OS_ARCH || os.arch()
}/${os.platform()}/*`,
dest: "lib/whisper",
},
{
src: `lib/youtubedr/${
process.env.PACKAGE_OS_ARCH || os.arch()
}/${os.platform()}/*`,
dest: "lib/youtubedr",
},
{
src: "lib/dictionaries/*",
dest: "lib/dictionaries",
},
{
src: "src/main/db/migrations/*",
dest: "migrations",
},
{
src: "samples/*",
dest: "samples",
},
],
targets: staticCopyTargets,
}),
],
define,

154
yarn.lock
View File

@@ -18,7 +18,7 @@ __metadata:
markdown-it-sup: "npm:^2.0.0"
mermaid: "npm:^11.4.0"
sass: "npm:^1.81.0"
swiper: "npm:^11.1.14"
swiper: "npm:^11.1.15"
vitepress: "npm:^1.5.0"
vitepress-plugin-mermaid: "npm:^2.0.17"
vue: "npm:^3.5.13"
@@ -4499,14 +4499,14 @@ __metadata:
languageName: node
linkType: hard
"@playwright/test@npm:^1.48.2":
version: 1.48.2
resolution: "@playwright/test@npm:1.48.2"
"@playwright/test@npm:^1.49.0":
version: 1.49.0
resolution: "@playwright/test@npm:1.49.0"
dependencies:
playwright: "npm:1.48.2"
playwright: "npm:1.49.0"
bin:
playwright: cli.js
checksum: 10c0/68bab3bee8d716111e9a166785e6c3c406b6a184fc46d03b5468fcbb92b6242e5628f6a75f9d286e2491ec0e9e59af67542a1f114b6659d790b5a1f41e4d305b
checksum: 10c0/2890d52ee45bd83b5501f17a77c77f12ba934d257fda4b288405c6d91f94b83c4fcbdff3c0be89c2aaeea3d13576b72ec9a70be667ff844b342044afd72a246e
languageName: node
linkType: hard
@@ -7719,15 +7719,15 @@ __metadata:
languageName: node
linkType: hard
"@typescript-eslint/eslint-plugin@npm:^8.14.0":
version: 8.14.0
resolution: "@typescript-eslint/eslint-plugin@npm:8.14.0"
"@typescript-eslint/eslint-plugin@npm:^8.15.0":
version: 8.15.0
resolution: "@typescript-eslint/eslint-plugin@npm:8.15.0"
dependencies:
"@eslint-community/regexpp": "npm:^4.10.0"
"@typescript-eslint/scope-manager": "npm:8.14.0"
"@typescript-eslint/type-utils": "npm:8.14.0"
"@typescript-eslint/utils": "npm:8.14.0"
"@typescript-eslint/visitor-keys": "npm:8.14.0"
"@typescript-eslint/scope-manager": "npm:8.15.0"
"@typescript-eslint/type-utils": "npm:8.15.0"
"@typescript-eslint/utils": "npm:8.15.0"
"@typescript-eslint/visitor-keys": "npm:8.15.0"
graphemer: "npm:^1.4.0"
ignore: "npm:^5.3.1"
natural-compare: "npm:^1.4.0"
@@ -7738,66 +7738,68 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
checksum: 10c0/46c82eb45be82ffec0ab04728a5180691b1d17002c669864861a3044b6d2105a75ca23cc80d18721b40b5e7dff1eff4ed68a43d726e25d55f3e466a9fbeeb873
checksum: 10c0/90ef10cc7d37a81abec4f4a3ffdfc3a0da8e99d949e03c75437e96e8ab2e896e34b85ab64718690180a7712581031b8611c5d8e7666d6ed4d60b9ace834d58e3
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:^8.14.0":
version: 8.14.0
resolution: "@typescript-eslint/parser@npm:8.14.0"
"@typescript-eslint/parser@npm:^8.15.0":
version: 8.15.0
resolution: "@typescript-eslint/parser@npm:8.15.0"
dependencies:
"@typescript-eslint/scope-manager": "npm:8.14.0"
"@typescript-eslint/types": "npm:8.14.0"
"@typescript-eslint/typescript-estree": "npm:8.14.0"
"@typescript-eslint/visitor-keys": "npm:8.14.0"
"@typescript-eslint/scope-manager": "npm:8.15.0"
"@typescript-eslint/types": "npm:8.15.0"
"@typescript-eslint/typescript-estree": "npm:8.15.0"
"@typescript-eslint/visitor-keys": "npm:8.15.0"
debug: "npm:^4.3.4"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
peerDependenciesMeta:
typescript:
optional: true
checksum: 10c0/522b7afd25cd302c0510cc71985ba55ff92ecc5dbe3fc74a76fefea0169252fdd4b8cad6291fef05f63dfc173951af450dca20859c7f23e387b2e7410e8b97b1
checksum: 10c0/19c25aea0dc51faa758701a5319a89950fd30494d9d645db8ced84fb60714c5e7d4b51fc4ee8ccb07ddefec88c51ee307ee7e49addd6330ee8f3e7ee9ba329fc
languageName: node
linkType: hard
"@typescript-eslint/scope-manager@npm:8.14.0":
version: 8.14.0
resolution: "@typescript-eslint/scope-manager@npm:8.14.0"
"@typescript-eslint/scope-manager@npm:8.15.0":
version: 8.15.0
resolution: "@typescript-eslint/scope-manager@npm:8.15.0"
dependencies:
"@typescript-eslint/types": "npm:8.14.0"
"@typescript-eslint/visitor-keys": "npm:8.14.0"
checksum: 10c0/1e1295c6f9febadf63559aad328b23d960510ce6b4c9f74e10d881c3858fa7f1db767cd1af5272d2fe7c9c5c7daebee71854e6f841e413e5d70af282f6616e26
"@typescript-eslint/types": "npm:8.15.0"
"@typescript-eslint/visitor-keys": "npm:8.15.0"
checksum: 10c0/c27dfdcea4100cc2d6fa967f857067cbc93155b55e648f9f10887a1b9372bb76cf864f7c804f3fa48d7868d9461cdef10bcea3dab7637d5337e8aa8042dc08b9
languageName: node
linkType: hard
"@typescript-eslint/type-utils@npm:8.14.0":
version: 8.14.0
resolution: "@typescript-eslint/type-utils@npm:8.14.0"
"@typescript-eslint/type-utils@npm:8.15.0":
version: 8.15.0
resolution: "@typescript-eslint/type-utils@npm:8.15.0"
dependencies:
"@typescript-eslint/typescript-estree": "npm:8.14.0"
"@typescript-eslint/utils": "npm:8.14.0"
"@typescript-eslint/typescript-estree": "npm:8.15.0"
"@typescript-eslint/utils": "npm:8.15.0"
debug: "npm:^4.3.4"
ts-api-utils: "npm:^1.3.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
peerDependenciesMeta:
typescript:
optional: true
checksum: 10c0/42616a664b38ca418e13504247e5e1bad6ae85c045b48e5735ffab977d4bd58cc86fb9d2292bbb314fa408d78d4b0454c3a27dbf9f881f9921917a942825c806
checksum: 10c0/20f09c79c83b38a962cf7eff10d47a2c01bcc0bab7bf6d762594221cd89023ef8c7aec26751c47b524f53f5c8d38bba55a282529b3df82d5f5ab4350496316f9
languageName: node
linkType: hard
"@typescript-eslint/types@npm:8.14.0":
version: 8.14.0
resolution: "@typescript-eslint/types@npm:8.14.0"
checksum: 10c0/7707f900e24e60e6780c5705f69627b7c0ef912cb3b095dfc8f4a0c84e866c66b1c4c10278cf99724560dc66985ec640750c4192786a09b853f9bb4c3ca5a7ce
"@typescript-eslint/types@npm:8.15.0":
version: 8.15.0
resolution: "@typescript-eslint/types@npm:8.15.0"
checksum: 10c0/84abc6fd954aff13822a76ac49efdcb90a55c0025c20eee5d8cebcfb68faff33b79bbc711ea524e0209cecd90c5ee3a5f92babc7083c081d3a383a0710264a41
languageName: node
linkType: hard
"@typescript-eslint/typescript-estree@npm:8.14.0":
version: 8.14.0
resolution: "@typescript-eslint/typescript-estree@npm:8.14.0"
"@typescript-eslint/typescript-estree@npm:8.15.0":
version: 8.15.0
resolution: "@typescript-eslint/typescript-estree@npm:8.15.0"
dependencies:
"@typescript-eslint/types": "npm:8.14.0"
"@typescript-eslint/visitor-keys": "npm:8.14.0"
"@typescript-eslint/types": "npm:8.15.0"
"@typescript-eslint/visitor-keys": "npm:8.15.0"
debug: "npm:^4.3.4"
fast-glob: "npm:^3.3.2"
is-glob: "npm:^4.0.3"
@@ -7807,31 +7809,34 @@ __metadata:
peerDependenciesMeta:
typescript:
optional: true
checksum: 10c0/5e890d22bd067095f871cf144907a8c302db5b5f014c58906ad58d7f23569951cba805042eac6844744e5abb0d3648c9cc221a91b0703da0a8d6345dc1f83e74
checksum: 10c0/3af5c129532db3575349571bbf64d32aeccc4f4df924ac447f5d8f6af8b387148df51965eb2c9b99991951d3dadef4f2509d7ce69bf34a2885d013c040762412
languageName: node
linkType: hard
"@typescript-eslint/utils@npm:8.14.0":
version: 8.14.0
resolution: "@typescript-eslint/utils@npm:8.14.0"
"@typescript-eslint/utils@npm:8.15.0":
version: 8.15.0
resolution: "@typescript-eslint/utils@npm:8.15.0"
dependencies:
"@eslint-community/eslint-utils": "npm:^4.4.0"
"@typescript-eslint/scope-manager": "npm:8.14.0"
"@typescript-eslint/types": "npm:8.14.0"
"@typescript-eslint/typescript-estree": "npm:8.14.0"
"@typescript-eslint/scope-manager": "npm:8.15.0"
"@typescript-eslint/types": "npm:8.15.0"
"@typescript-eslint/typescript-estree": "npm:8.15.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
checksum: 10c0/1fcc2651d870832a799a5d1c85fc9421853508a006d6a6073c8316b012489dda77e123d13aea8f53eb9030a2da2c0eb273a6946a9941caa2519b99b33e89b720
peerDependenciesMeta:
typescript:
optional: true
checksum: 10c0/65743f51845a1f6fd2d21f66ca56182ba33e966716bdca73d30b7a67c294e47889c322de7d7b90ab0818296cd33c628e5eeeb03cec7ef2f76c47de7a453eeda2
languageName: node
linkType: hard
"@typescript-eslint/visitor-keys@npm:8.14.0":
version: 8.14.0
resolution: "@typescript-eslint/visitor-keys@npm:8.14.0"
"@typescript-eslint/visitor-keys@npm:8.15.0":
version: 8.15.0
resolution: "@typescript-eslint/visitor-keys@npm:8.15.0"
dependencies:
"@typescript-eslint/types": "npm:8.14.0"
eslint-visitor-keys: "npm:^3.4.3"
checksum: 10c0/d0faf70ed9ecff5e36694bbb161a90bea6db59e0e79a7d4f264d67d565c12b13733d664b736b2730935f013c87ce3155cea954a533d28e99987681bc5f6259c3
"@typescript-eslint/types": "npm:8.15.0"
eslint-visitor-keys: "npm:^4.2.0"
checksum: 10c0/02a954c3752c4328482a884eb1da06ca8fb72ae78ef28f1d854b18f3779406ed47263af22321cf3f65a637ec7584e5f483e34a263b5c8cec60ec85aebc263574
languageName: node
linkType: hard
@@ -11987,7 +11992,7 @@ __metadata:
"@langchain/core": "npm:^0.3.18"
"@langchain/ollama": "npm:^0.1.2"
"@mozilla/readability": "npm:^0.5.0"
"@playwright/test": "npm:^1.48.2"
"@playwright/test": "npm:^1.49.0"
"@radix-ui/react-accordion": "npm:^1.2.1"
"@radix-ui/react-alert-dialog": "npm:^1.1.2"
"@radix-ui/react-aspect-ratio": "npm:^1.1.0"
@@ -12035,8 +12040,8 @@ __metadata:
"@types/unzipper": "npm:^0.10.10"
"@types/validator": "npm:^13.12.2"
"@types/wavesurfer.js": "npm:^6.0.12"
"@typescript-eslint/eslint-plugin": "npm:^8.14.0"
"@typescript-eslint/parser": "npm:^8.14.0"
"@typescript-eslint/eslint-plugin": "npm:^8.15.0"
"@typescript-eslint/parser": "npm:^8.15.0"
"@uidotdev/usehooks": "npm:^2.4.1"
"@vidstack/react": "npm:^1.12.12"
"@vitejs/plugin-react": "npm:^4.3.3"
@@ -19236,7 +19241,16 @@ __metadata:
languageName: node
linkType: hard
"playwright-core@npm:1.48.2, playwright-core@npm:^1.48.2":
"playwright-core@npm:1.49.0":
version: 1.49.0
resolution: "playwright-core@npm:1.49.0"
bin:
playwright-core: cli.js
checksum: 10c0/22c1a72fabdcc87bd1cd4d40a032d2c5b94cf94ba7484dc182048c3fa1c8ec26180b559d8cac4ca9870e8fd6bdf5ef9d9f54e7a31fd60d67d098fcffc5e4253b
languageName: node
linkType: hard
"playwright-core@npm:^1.48.2":
version: 1.48.2
resolution: "playwright-core@npm:1.48.2"
bin:
@@ -19245,18 +19259,18 @@ __metadata:
languageName: node
linkType: hard
"playwright@npm:1.48.2":
version: 1.48.2
resolution: "playwright@npm:1.48.2"
"playwright@npm:1.49.0":
version: 1.49.0
resolution: "playwright@npm:1.49.0"
dependencies:
fsevents: "npm:2.3.2"
playwright-core: "npm:1.48.2"
playwright-core: "npm:1.49.0"
dependenciesMeta:
fsevents:
optional: true
bin:
playwright: cli.js
checksum: 10c0/ecde4ee4767556868b24d7700f3502692a3cb14c8ef127052b51b48833ffcce80942954fb188a9b72505122b48b1b625d1bb486721e1c4f2e980215328ba1ad5
checksum: 10c0/e94d662747cd147d0573570fec90dadc013c1097595714036fc8934a075c5a82ab04a49111b03b1f762ea86429bdb7c94460901896901e20970b30ce817cc93f
languageName: node
linkType: hard
@@ -22103,10 +22117,10 @@ __metadata:
languageName: node
linkType: hard
"swiper@npm:^11.1.14":
version: 11.1.14
resolution: "swiper@npm:11.1.14"
checksum: 10c0/a1075cbd2254aedd22b411ea03cbd7a91ba1cc79d7c2dc41f215e987811e449a2915a26b39fdfc1d7c08250fa2cfbe51d28476f834e75033c73ea357a4ac9f8d
"swiper@npm:^11.1.15":
version: 11.1.15
resolution: "swiper@npm:11.1.15"
checksum: 10c0/3faabc5b33c0663513e8e41c4ef7920d4c9a1ca6b988848f2e51e1db0d01471a1bae22fce3177b9d3ab2c128fc6df23074415ae7957bd0f8542fd7387fa5263d
languageName: node
linkType: hard