Fix: Improve UI (#103)

* use sonner

* fix ui

* fix post audio player
This commit is contained in:
an-lee
2024-01-13 22:59:57 +08:00
committed by GitHub
parent d6a4b24a1e
commit fe0542e8c6
26 changed files with 210 additions and 204 deletions

View File

@@ -117,6 +117,7 @@
"lucide-react": "^0.308.0",
"mark.js": "^8.11.1",
"microsoft-cognitiveservices-speech-sdk": "^1.34.0",
"next-themes": "^0.2.1",
"openai": "^4.24.1",
"pitchfinder": "^2.3.2",
"postcss": "^8.4.33",
@@ -133,6 +134,7 @@
"rimraf": "^5.0.5",
"sequelize": "^6.35.2",
"sequelize-typescript": "^2.1.6",
"sonner": "^1.3.1",
"sqlite3": "^5.1.7",
"tailwind-scrollbar-hide": "^1.1.7",
"umzug": "^3.5.0",

View File

@@ -197,6 +197,7 @@
"youAreReadyToGo": "You are ready to go",
"welcomeBack": "Welcome back! {{name}}",
"download": "Download",
"downloading": "Downloading {{file}}",
"chooseAIModelDependingOnYourHardware": "Choose AI Model depending on your hardware",
"areYouSureToDownload": "Are you sure to download {{name}}?",
"yourModelsWillBeDownloadedTo": "Your models will be downloaded to {{path}}",

View File

@@ -197,6 +197,7 @@
"youAreReadyToGo": "您已准备就绪",
"welcomeBack": "欢迎回来, {{name}}",
"download": "下载",
"downloading": "正在下载 {{file}}",
"chooseAIModelDependingOnYourHardware": "根据您的硬件选择合适的 AI 模型",
"areYouSureToDownload": "您确定要下载 {{name}} 吗?",
"yourModelsWillBeDownloadedTo": "您的模型将下载到目录 {{path}}",

View File

@@ -6,19 +6,29 @@ import {
} from "@renderer/context";
import router from "./router";
import { RouterProvider } from "react-router-dom";
import { Toaster, useToast } from "@renderer/components/ui";
import { t } from "i18next";
import { Toaster, toast } from "@renderer/components/ui";
import { Tooltip } from "react-tooltip";
import { useHotkeys } from "react-hotkeys-hook";
function App() {
const { toast } = useToast();
window.__ENJOY_APP__.onNotification((_event, notification) => {
toast({
title: t(notification.type),
description: notification.message,
variant: notification.type === "error" ? "destructive" : "default",
});
switch (notification.type) {
case "success":
toast.success(notification.message);
break;
case "error":
toast.error(notification.message);
break;
case "info":
toast.info(notification.message);
break;
case "warning":
toast.warning(notification.message);
break;
default:
toast.message(notification.message);
break;
}
});
const ControlOrCommand = navigator.platform.includes("Mac")
@@ -43,7 +53,7 @@ function App() {
<AISettingsProvider>
<DbProvider>
<RouterProvider router={router} />
<Toaster />
<Toaster richColors />
<Tooltip id="global-tooltip" />
</DbProvider>
</AISettingsProvider>

View File

@@ -21,13 +21,12 @@ import {
AlertDialogCancel,
Button,
ScrollArea,
useToast,
toast,
} from "@renderer/components/ui";
import { t } from "i18next";
export const AudioDetail = (props: { id?: string; md5?: string }) => {
const { id, md5 } = props;
const { toast } = useToast();
const { addDblistener, removeDbListener } = useContext(DbProviderContext);
const { EnjoyApp, webApi } = useContext(AppSettingsProviderContext);
@@ -62,8 +61,7 @@ export const AudioDetail = (props: { id?: string; md5?: string }) => {
try {
await EnjoyApp.audios.upload(audio.id);
} catch (err) {
toast({
title: t("shareFailed"),
toast.error(t("shareFailed"), {
description: err.message,
});
return;
@@ -75,14 +73,12 @@ export const AudioDetail = (props: { id?: string; md5?: string }) => {
targetId: audio.id,
})
.then(() => {
toast({
title: t("shared"),
toast.success(t("sharedSuccessfully"), {
description: t("sharedAudio"),
});
})
.catch((err) => {
toast({
title: t("shareFailed"),
toast.error(t("shareFailed"), {
description: err.message,
});
});

View File

@@ -25,7 +25,7 @@ import {
DialogContent,
DialogHeader,
DialogTitle,
useToast,
toast,
} from "@renderer/components/ui";
import {
DbProviderContext,
@@ -48,7 +48,6 @@ export const AudiosComponent = () => {
const { EnjoyApp } = useContext(AppSettingsProviderContext);
const [offset, setOffest] = useState(0);
const [loading, setLoading] = useState(false);
const { toast } = useToast();
const navigate = useNavigate();
@@ -87,10 +86,7 @@ export const AudiosComponent = () => {
dispatchAudios({ type: "append", records: _audios });
})
.catch((err) => {
toast({
description: err.message,
variant: "destructive",
});
toast.error(err.message);
})
.finally(() => {
setLoading(false);

View File

@@ -1,6 +1,6 @@
import { useContext, useEffect, useState } from "react";
import { AppSettingsProviderContext } from "@renderer/context";
import { ScrollArea } from "@renderer/components/ui";
import { ScrollArea, toast } from "@renderer/components/ui";
import { LoaderSpin } from "@renderer/components";
import { MessageCircleIcon } from "lucide-react";
@@ -20,11 +20,10 @@ export const ConversationsShortcut = (props: {
content: prompt,
})
.then((replies) => {
console.log(replies);
onReply(replies);
})
.catch((error) => {
console.error(error);
toast.error(error.message);
})
.finally(() => {
setLoading(false);

View File

@@ -1,10 +1,9 @@
import { Button, useToast } from "@renderer/components/ui";
import { Button, toast } from "@renderer/components/ui";
import { useContext, useEffect } from "react";
import { AppSettingsProviderContext } from "@renderer/context";
import { t } from "i18next";
export const LoginForm = () => {
const { toast } = useToast();
const { EnjoyApp, login, webApi } = useContext(AppSettingsProviderContext);
const handleMixinLogin = () => {
@@ -21,11 +20,7 @@ export const LoginForm = () => {
const { state, url, error } = event;
if (error) {
toast({
title: t("error"),
description: error,
variant: "destructive",
});
toast.error(error);
EnjoyApp.view.hide();
return;
}
@@ -35,11 +30,7 @@ export const LoginForm = () => {
const code = new URL(url).searchParams.get("code");
if (!url.startsWith(webApi.baseUrl)) {
toast({
title: t("error"),
description: t("invalidRedirectUrl"),
variant: "destructive",
});
toast.error(t("invalidRedirectUrl"));
EnjoyApp.view.hide();
}
@@ -53,11 +44,7 @@ export const LoginForm = () => {
EnjoyApp.view.hide();
});
} else {
toast({
title: t("error"),
description: t("failedToLogin"),
variant: "destructive",
});
toast.error(t("failedToLogin"));
EnjoyApp.view.hide();
}
}

View File

@@ -17,7 +17,7 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
useToast,
toast,
} from "@renderer/components/ui";
import { SpeechPlayer } from "@renderer/components";
import { useContext, useState } from "react";
@@ -45,7 +45,6 @@ export const UserMessageComponent = (props: {
const { user, webApi } = useContext(AppSettingsProviderContext);
const [_, copyToClipboard] = useCopyToClipboard();
const [copied, setCopied] = useState<boolean>(false);
const { toast } = useToast();
const handleShare = async () => {
if (message.role === "user") {
@@ -58,15 +57,10 @@ export const UserMessageComponent = (props: {
},
})
.then(() => {
toast({
description: t("sharedPrompt"),
});
toast(t("sharedSuccessfully"), { description: t("sharedPrompt") });
})
.catch((err) => {
toast({
title: t("shareFailed"),
description: err.message,
});
toast.error(t("shareFailed"), { description: err.message });
});
}
};

View File

@@ -1,4 +1,4 @@
import { useContext, useEffect, useState } from "react";
import { useContext, useState } from "react";
import { AppSettingsProviderContext } from "@renderer/context";
import { ConversationsShortcut } from "@renderer/components";
import {
@@ -18,7 +18,7 @@ import {
DialogHeader,
DialogTitle,
ScrollArea,
useToast,
toast,
} from "@renderer/components/ui";
import { t } from "i18next";
import Markdown from "react-markdown";
@@ -37,7 +37,6 @@ export const PostActions = (props: { post: PostType }) => {
const [_, copyToClipboard] = useCopyToClipboard();
const [copied, setCopied] = useState<boolean>(false);
const { EnjoyApp } = useContext(AppSettingsProviderContext);
const { toast } = useToast();
const [asking, setAsking] = useState<boolean>(false);
const [aiReplies, setAiReplies] = useState<MessageType[]>([]);
@@ -50,9 +49,7 @@ export const PostActions = (props: { post: PostType }) => {
try {
const video = await EnjoyApp.videos.findOne({ md5: medium.md5 });
if (video) {
toast({
description: t("videoAlreadyAddedToLibrary"),
});
toast.info(t("videoAlreadyAddedToLibrary"));
return;
}
} catch (error) {
@@ -65,21 +62,17 @@ export const PostActions = (props: { post: PostType }) => {
md5: medium.md5,
})
.then(() => {
toast({
description: t("videoSuccessfullyAddedToLibrary"),
});
toast.success(t("videoSuccessfullyAddedToLibrary"));
});
} else if (medium.mediumType === "Audio") {
try {
const audio = await EnjoyApp.audios.findOne({ md5: medium.md5 });
if (audio) {
toast({
description: t("audioAlreadyAddedToLibrary"),
});
toast.info(t("audioAlreadyAddedToLibrary"));
return;
}
} catch (error) {
console.error(error);
toast.error(error.message);
}
EnjoyApp.audios
@@ -88,9 +81,7 @@ export const PostActions = (props: { post: PostType }) => {
md5: medium.md5,
})
.then(() => {
toast({
description: t("audioSuccessfullyAddedToLibrary"),
});
toast.success(t("audioSuccessfullyAddedToLibrary"));
});
}
};

View File

@@ -6,12 +6,83 @@ import { Button, Skeleton } from "@renderer/components/ui";
import { PlayIcon, PauseIcon } from "lucide-react";
import { useIntersectionObserver } from "@uidotdev/usehooks";
import { secondsToTimestamp } from "@renderer/lib/utils";
import { MediaPlayer, MediaProvider } from "@vidstack/react";
import {
DefaultAudioLayout,
defaultLayoutIcons,
} from "@vidstack/react/player/layouts/default";
export const STORAGE_WORKER_ENDPOINT = "https://enjoy-storage.baizhiheizi.com";
export const PostAudio = (props: {
audio: Partial<MediumType>;
height?: number;
}) => {
const { audio, height = 80 } = props;
const [currentTime, setCurrentTime] = useState<number>(0);
const { webApi } = useContext(AppSettingsProviderContext);
const [transcription, setTranscription] = useState<TranscriptionType>();
const currentTranscription = (transcription?.result || []).find(
(s) =>
currentTime >= s.offsets.from / 1000.0 &&
currentTime <= s.offsets.to / 1000.0
);
useEffect(() => {
webApi
.transcriptions({
targetMd5: audio.md5,
})
.then((response) => {
setTranscription(response?.transcriptions?.[0]);
});
}, [audio.md5]);
return (
<div className="w-full">
{audio.sourceUrl.startsWith(STORAGE_WORKER_ENDPOINT) ? (
<WavesurferPlayer
currentTime={currentTime}
setCurrentTime={setCurrentTime}
audio={audio}
height={height}
/>
) : (
<MediaPlayer
onTimeUpdate={({ currentTime: _currentTime }) => {
setCurrentTime(_currentTime);
}}
src={audio.sourceUrl}
>
<MediaProvider />
<DefaultAudioLayout icons={defaultLayoutIcons} />
</MediaPlayer>
)}
{currentTranscription && (
<div className="mt-2 bg-muted px-4 py-2 rounded">
<div className="text-muted-foreground text-center font-serif">
{currentTranscription.text}
</div>
</div>
)}
{audio.coverUrl && (
<div className="mt-2">
<img src={audio.coverUrl} className="w-full rounded" />
</div>
)}
</div>
);
};
const WavesurferPlayer = (props: {
audio: Partial<MediumType>;
height?: number;
currentTime: number;
setCurrentTime: (currentTime: number) => void;
}) => {
const { audio, height = 80, currentTime, setCurrentTime } = props;
const [initialized, setInitialized] = useState(false);
const [isPlaying, setIsPlaying] = useState(false);
const [wavesurfer, setWavesurfer] = useState(null);
@@ -20,15 +91,6 @@ export const PostAudio = (props: {
threshold: 1,
});
const [duration, setDuration] = useState<number>(0);
const { webApi } = useContext(AppSettingsProviderContext);
const [currentTime, setCurrentTime] = useState<number>(0);
const [transcription, setTranscription] = useState<TranscriptionType>();
const currentTranscription = (transcription?.result || []).find(
(s) =>
currentTime >= s.offsets.from / 1000.0 &&
currentTime <= s.offsets.to / 1000.0
);
const onPlayClick = useCallback(() => {
wavesurfer.isPlaying() ? wavesurfer.pause() : wavesurfer.play();
@@ -93,18 +155,8 @@ export const PostAudio = (props: {
};
}, [wavesurfer]);
useEffect(() => {
webApi
.transcriptions({
targetMd5: audio.md5,
})
.then((response) => {
setTranscription(response?.transcriptions?.[0]);
});
}, [audio.md5]);
return (
<div className="w-full">
<>
<div className="flex justify-end">
<span className="text-xs text-muted-foreground">
{secondsToTimestamp(duration)}
@@ -141,20 +193,6 @@ export const PostAudio = (props: {
ref={containerRef}
></div>
</div>
{currentTranscription && (
<div className="mt-2 bg-muted px-4 py-2 rounded">
<div className="text-muted-foreground text-center font-serif">
{currentTranscription.text}
</div>
</div>
)}
{audio.coverUrl && (
<div className="">
<img src={audio.coverUrl} className="w-full rounded" />
</div>
)}
</div>
</>
);
};

View File

@@ -37,7 +37,7 @@ export const PostMedium = (props: { medium: MediumType }) => {
<div className="text-xs text-muted-foreground">
{t("sharedAudio")}
</div>
<PostAudio audio={medium as Partial<AudioType>} />
<PostAudio audio={medium} />
</>
)}
</div>

View File

@@ -1,7 +1,7 @@
import { useContext, useEffect, useState } from "react";
import { AppSettingsProviderContext } from "@renderer/context";
import { PostCard, LoaderSpin } from "@renderer/components";
import { useToast, Button } from "@renderer/components//ui";
import { toast, Button } from "@renderer/components//ui";
import { t } from "i18next";
export const Posts = () => {
@@ -9,23 +9,16 @@ export const Posts = () => {
const [loading, setLoading] = useState<boolean>(true);
const [posts, setPosts] = useState<PostType[]>([]);
const [nextPage, setNextPage] = useState(1);
const { toast } = useToast();
const handleDelete = (id: string) => {
webApi
.deletePost(id)
.then(() => {
toast({
description: t("removeSharingSuccessfully"),
});
toast.success(t("removeSharingSuccessfully"));
setPosts(posts.filter((post) => post.id !== id));
})
.catch((error) => {
toast({
title: t("removeSharingFailed"),
description: error.message,
variant: "destructive",
});
toast.error(t("removeSharingFailed"), { description: error.message });
});
};
@@ -42,10 +35,7 @@ export const Posts = () => {
setNextPage(res.next);
})
.catch((err) => {
toast({
description: err.message,
variant: "destructive",
});
toast.error(err.message);
})
.finally(() => {
setLoading(false);

View File

@@ -1,5 +1,5 @@
import { t } from "i18next";
import { Button, useToast } from "@renderer/components/ui";
import { Button, toast } from "@renderer/components/ui";
import { AppSettingsProviderContext } from "@renderer/context";
import { useState, useContext } from "react";
import { LoaderIcon } from "lucide-react";
@@ -7,14 +7,11 @@ import { LoaderIcon } from "lucide-react";
export const About = () => {
const { version } = useContext(AppSettingsProviderContext);
const [checking, setChecking] = useState<boolean>(false);
const { toast } = useToast();
const checkUpdate = () => {
setChecking(true);
setTimeout(() => {
setChecking(false);
toast({
description: t("alreadyLatestVersion"),
});
toast.info(t("alreadyLatestVersion"));
}, 1000);
};

View File

@@ -22,7 +22,7 @@ import {
Input,
Label,
Separator,
useToast,
toast,
} from "@renderer/components/ui";
import { WhisperModelOptions } from "@renderer/components";
import {
@@ -196,7 +196,6 @@ const OpenaiSettings = () => {
const { openai, setOpenai } = useContext(AISettingsProviderContext);
const [editing, setEditing] = useState(false);
const ref = useRef<HTMLInputElement>();
const { toast } = useToast();
const handleSave = () => {
if (!ref.current) return;
@@ -206,10 +205,7 @@ const OpenaiSettings = () => {
});
setEditing(false);
toast({
title: t("success"),
description: t("openaiKeySaved"),
});
toast.success(t("openaiKeySaved"));
};
useEffect(() => {
@@ -264,7 +260,6 @@ const GoogleGenerativeAiSettings = () => {
);
const [editing, setEditing] = useState(false);
const ref = useRef<HTMLInputElement>();
const { toast } = useToast();
const handleSave = () => {
if (!ref.current) return;
@@ -274,10 +269,7 @@ const GoogleGenerativeAiSettings = () => {
});
setEditing(false);
toast({
title: t("success"),
description: t("googleGenerativeAiKeySaved"),
});
toast.success(t("googleGenerativeAiKeySaved"));
};
useEffect(() => {

View File

@@ -4,7 +4,7 @@ import { useState, useEffect, useRef } from "react";
import RecordPlugin from "wavesurfer.js/dist/plugins/record";
import WaveSurfer from "wavesurfer.js";
import { cn } from "@renderer/lib/utils";
import { RadialProgress, useToast } from "@renderer/components/ui";
import { RadialProgress, toast } from "@renderer/components/ui";
import { useHotkeys } from "react-hotkeys-hook";
export const RecordButton = (props: {
@@ -16,7 +16,6 @@ export const RecordButton = (props: {
const { className, disabled, onRecordBegin, onRecordEnd } = props;
const [isRecording, setIsRecording] = useState<boolean>(false);
const [duration, setDuration] = useState<number>(0);
const { toast } = useToast();
useHotkeys(["command+alt+r", "control+alt+r"], () => {
if (disabled) return;
@@ -67,10 +66,7 @@ export const RecordButton = (props: {
if (duration > 1000) {
onRecordEnd(blob, duration);
} else {
toast({
description: t("recordTooShort"),
variant: "warning",
});
toast.warning(t("recordTooShort"));
}
}}
/>

View File

@@ -16,7 +16,7 @@ import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
useToast,
toast,
} from "@renderer/components/ui";
import {
MoreHorizontalIcon,
@@ -36,7 +36,6 @@ export const RecordingCard = (props: {
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const { EnjoyApp, webApi } = useContext(AppSettingsProviderContext);
const [isPlaying, setIsPlaying] = useState(false);
const { toast } = useToast();
const handleDelete = () => {
EnjoyApp.recordings.destroy(recording.id);
@@ -46,11 +45,7 @@ export const RecordingCard = (props: {
try {
await EnjoyApp.recordings.upload(recording.id);
} catch (error) {
toast({
title: t("shareFailed"),
description: error.message,
variant: "destructive",
});
toast.error(t("shareFailed"), { description: error.message });
return;
}
}
@@ -61,14 +56,13 @@ export const RecordingCard = (props: {
targetType: "Recording",
})
.then(() => {
toast({
toast.success(t("sharedSuccessfully"), {
description: t("sharedRecording"),
});
})
.catch((error) => {
toast({
toast.error(t("shareFailed"), {
description: error.message,
variant: "destructive",
});
});
};

View File

@@ -13,9 +13,9 @@ export * from "./input";
export * from "./avatar";
export * from "./alert-dialog";
export * from "./card";
export * from "./toast";
export * from "./use-toast";
export * from "./toaster";
// export * from "./toast";
// export * from "./use-toast";
// export * from "./toaster";
export * from "./toggle";
export * from "./radio-group";
export * from "./scroll-area";
@@ -33,3 +33,4 @@ export * from "./select";
export * from "./sheet";
export * from "./hover-card";
export * from "./floating-toolbar";
export * from "./sonner";

View File

@@ -0,0 +1,31 @@
"use client";
import { useTheme } from "next-themes";
import { Toaster as Sonner, toast } from "sonner";
type ToasterProps = React.ComponentProps<typeof Sonner>;
const Toaster = ({ ...props }: ToasterProps) => {
const { theme = "system" } = useTheme();
return (
<Sonner
theme={theme as ToasterProps["theme"]}
className="toaster group"
toastOptions={{
classNames: {
toast:
"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
description: "group-[.toast]:text-muted-foreground",
actionButton:
"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
cancelButton:
"group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
},
}}
{...props}
/>
);
};
export { Toaster, toast };

View File

@@ -21,13 +21,12 @@ import {
AlertDialogCancel,
Button,
ScrollArea,
useToast,
toast,
} from "@renderer/components/ui";
import { t } from "i18next";
export const VideoDetail = (props: { id?: string; md5?: string }) => {
const { id, md5 } = props;
const { toast } = useToast();
const { addDblistener, removeDbListener } = useContext(DbProviderContext);
const { EnjoyApp, webApi } = useContext(AppSettingsProviderContext);
@@ -61,8 +60,7 @@ export const VideoDetail = (props: { id?: string; md5?: string }) => {
const handleShare = async () => {
if (!video.source.startsWith("http")) {
toast({
title: t("shareFailed"),
toast.error(t("shareFailed"), {
description: t("cannotShareLocalVideo"),
});
return;
@@ -72,10 +70,7 @@ export const VideoDetail = (props: { id?: string; md5?: string }) => {
try {
await EnjoyApp.videos.upload(video.id);
} catch (err) {
toast({
title: t("shareFailed"),
description: err.message,
});
toast.error(t("shareFailed"), { description: err.message });
return;
}
}
@@ -86,15 +81,10 @@ export const VideoDetail = (props: { id?: string; md5?: string }) => {
targetId: video.id,
})
.then(() => {
toast({
description: t("sharedVideo"),
});
toast.success(t("sharedSuccessfully"), { description: t("sharedVideo") });
})
.catch((err) => {
toast({
title: t("shareFailed"),
description: err.message,
});
toast.error(t("shareFailed"), { description: err.message });
});
setSharing(false);
};

View File

@@ -25,7 +25,7 @@ import {
DialogContent,
DialogHeader,
DialogTitle,
useToast,
toast,
} from "@renderer/components/ui";
import {
DbProviderContext,
@@ -48,7 +48,6 @@ export const VideosComponent = () => {
const { EnjoyApp } = useContext(AppSettingsProviderContext);
const [offset, setOffest] = useState(0);
const [loading, setLoading] = useState(false);
const { toast } = useToast();
const navigate = useNavigate();
@@ -87,10 +86,7 @@ export const VideosComponent = () => {
dispatchVideos({ type: "append", records: _videos });
})
.catch((err) => {
toast({
description: err.message,
variant: "destructive",
});
toast.error(err.message);
})
.finally(() => {
setLoading(false);

View File

@@ -14,7 +14,7 @@ import {
CardContent,
CardFooter,
ScrollArea,
useToast,
toast,
Progress,
} from "@renderer/components/ui";
import { t } from "i18next";
@@ -67,10 +67,8 @@ export const WhisperModelOptionsPanel = () => {
export const WhisperModelOptions = () => {
const [selectingModel, setSelectingModel] = useState<ModelType | null>(null);
const [availableModels, setAvailableModels] = useState<ModelType[]>([]);
const { whisperModelsPath, whisperModel, setWhisperModel, EnjoyApp } = useContext(
AppSettingsProviderContext
);
const { toast } = useToast();
const { whisperModelsPath, whisperModel, setWhisperModel, EnjoyApp } =
useContext(AppSettingsProviderContext);
useEffect(() => {
updateAvailableModels();
@@ -126,10 +124,7 @@ export const WhisperModelOptions = () => {
if (option.downloaded) {
setWhisperModel(option.name);
} else if (option.downloadState) {
toast({
title: "Downloading",
description: `${option.name} is downloading...`,
});
toast.warning(t("downloading", { file: option.name }));
} else {
setSelectingModel(option);
}

View File

@@ -6,7 +6,7 @@ import {
Sheet,
SheetContent,
SheetTrigger,
useToast,
toast,
} from "@renderer/components/ui";
import { MessageComponent, ConversationForm } from "@renderer/components";
import { SendIcon, BotIcon, LoaderIcon, SettingsIcon } from "lucide-react";
@@ -29,8 +29,6 @@ export default () => {
const [content, setConent] = useState<string>("");
const [submitting, setSubmitting] = useState<boolean>(false);
const { toast } = useToast();
const [messages, dispatchMessages] = useReducer(messagesReducer, []);
const [offset, setOffest] = useState(0);
const [loading, setLoading] = useState<boolean>(false);
@@ -79,10 +77,7 @@ export default () => {
const handleSubmit = async (text?: string, file?: string) => {
if (submitting) {
toast({
title: t("warning"),
description: t("anotherRequestIsPending"),
});
toast.warning(t("anotherRequestIsPending"));
}
text = text ? text : content;

View File

@@ -1,4 +1,4 @@
import { Input, Button, ScrollArea, useToast } from "@renderer/components/ui";
import { Input, Button, ScrollArea, toast } from "@renderer/components/ui";
import {
LoaderSpin,
StoryViewer,
@@ -27,7 +27,6 @@ export default () => {
const [loading, setLoading] = useState(true);
const [readable, setReadable] = useState(true);
const { EnjoyApp, webApi } = useContext(AppSettingsProviderContext);
const { toast } = useToast();
const [meanings, setMeanings] = useState<MeaningType[]>([]);
const [marked, setMarked] = useState<boolean>(false);
const [doc, setDoc] = useState<any>(null);
@@ -73,10 +72,7 @@ export default () => {
if (state == "did-fail-load") {
setLoading(false);
if (error) {
toast({
title: error,
variant: "destructive",
});
toast.error(error);
setError(error);
}

View File

@@ -1,5 +1,5 @@
import { t } from "i18next";
import { ScrollArea, useToast } from "@renderer/components/ui";
import { ScrollArea, toast } from "@renderer/components/ui";
import {
LoaderSpin,
PagePlaceholder,
@@ -24,7 +24,6 @@ export default () => {
const [scanning, setScanning] = useState<boolean>(false);
const [marked, setMarked] = useState<boolean>(true);
const [doc, setDoc] = useState<any>(null);
const { toast } = useToast();
const fetchStory = async () => {
webApi
@@ -123,15 +122,11 @@ export default () => {
webApi
.createPost({ targetId: story.id, targetType: "Story" })
.then(() => {
toast({
description: t("sharedStory"),
});
toast.success(t("sharedStory"));
})
.catch((error) => {
toast({
title: t("shareFailed"),
toast.error(t("shareFailed"), {
description: error.message,
variant: "destructive",
});
});
};

View File

@@ -5818,6 +5818,7 @@ __metadata:
lucide-react: "npm:^0.308.0"
mark.js: "npm:^8.11.1"
microsoft-cognitiveservices-speech-sdk: "npm:^1.34.0"
next-themes: "npm:^0.2.1"
octokit: "npm:^3.1.2"
openai: "npm:^4.24.1"
pitchfinder: "npm:^2.3.2"
@@ -5835,6 +5836,7 @@ __metadata:
rimraf: "npm:^5.0.5"
sequelize: "npm:^6.35.2"
sequelize-typescript: "npm:^2.1.6"
sonner: "npm:^1.3.1"
sqlite3: "npm:^5.1.7"
tailwind-merge: "npm:^2.2.0"
tailwind-scrollbar-hide: "npm:^1.1.7"
@@ -9561,6 +9563,17 @@ __metadata:
languageName: node
linkType: hard
"next-themes@npm:^0.2.1":
version: 0.2.1
resolution: "next-themes@npm:0.2.1"
peerDependencies:
next: "*"
react: "*"
react-dom: "*"
checksum: 979dec0a2de049ce7d1b5da835e7f7dc3b7ec83ba9e464348f497a52a6a6e5b5c395c97f071f66a63f50f22cce89fb6d19061ec7e75643b0eab215b21794bde7
languageName: node
linkType: hard
"nice-try@npm:^1.0.4":
version: 1.0.5
resolution: "nice-try@npm:1.0.5"
@@ -11552,6 +11565,16 @@ __metadata:
languageName: node
linkType: hard
"sonner@npm:^1.3.1":
version: 1.3.1
resolution: "sonner@npm:1.3.1"
peerDependencies:
react: ^18.0.0
react-dom: ^18.0.0
checksum: 324d2b2edb8bf15a38239f67e533bbf9f38425adf432c9542e7a9610b7451ec5d4082d2e8ddb564096f63f44ba7ec4d2e137f8b0af1786a0becac346fae2b28a
languageName: node
linkType: hard
"source-map-js@npm:^1.0.2":
version: 1.0.2
resolution: "source-map-js@npm:1.0.2"