add enjoy app

This commit is contained in:
an-lee
2024-01-09 15:19:32 +08:00
parent b88c52d5d8
commit aebd9ee213
434 changed files with 34955 additions and 62 deletions

View File

@@ -0,0 +1,70 @@
import { createContext, useEffect, useState, useContext } from "react";
import { AppSettingsProviderContext } from "@renderer/context";
type AISettingsProviderState = {
openai?: LlmProviderType;
setOpenai?: (config: LlmProviderType) => void;
googleGenerativeAi?: LlmProviderType;
setGoogleGenerativeAi?: (config: LlmProviderType) => void;
};
const initialState: AISettingsProviderState = {};
export const AISettingsProviderContext =
createContext<AISettingsProviderState>(initialState);
export const AISettingsProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const [openai, setOpenai] = useState<LlmProviderType>(null);
const [googleGenerativeAi, setGoogleGenerativeAi] =
useState<LlmProviderType>(null);
const { EnjoyApp } = useContext(AppSettingsProviderContext);
useEffect(() => {
fetchSettings();
}, []);
const fetchSettings = async () => {
const _openai = await EnjoyApp.settings.getLlm("openai");
if (_openai) setOpenai(_openai);
const _googleGenerativeAi = await EnjoyApp.settings.getLlm(
"googleGenerativeAi"
);
if (_googleGenerativeAi) setGoogleGenerativeAi(_googleGenerativeAi);
};
const handleSetLlm = async (
name: SupportedLlmProviderType,
config: LlmProviderType
) => {
await EnjoyApp.settings.setLlm(name, config);
const _config = await EnjoyApp.settings.getLlm(name);
switch (name) {
case "openai":
setOpenai(_config);
break;
case "googleGenerativeAi":
setGoogleGenerativeAi(_config);
break;
}
};
return (
<AISettingsProviderContext.Provider
value={{
openai,
setOpenai: (config: LlmProviderType) => handleSetLlm("openai", config),
googleGenerativeAi,
setGoogleGenerativeAi: (config: LlmProviderType) =>
handleSetLlm("googleGenerativeAi", config),
}}
>
{children}
</AISettingsProviderContext.Provider>
);
};

View File

@@ -0,0 +1,142 @@
import { createContext, useEffect, useState } from "react";
type AppSettingsProviderState = {
user: UserType | null;
initialized: boolean;
version?: string;
libraryPath?: string;
whisperModelsPath?: string;
whisperModel?: string;
login?: (user: UserType) => void;
logout?: () => void;
setLibraryPath?: (path: string) => Promise<void>;
setWhisperModel?: (name: string) => void;
ffmpegConfg?: FfmpegConfigType;
setFfmegConfig?: (config: FfmpegConfigType) => void;
EnjoyApp?: EnjoyAppType;
};
const initialState: AppSettingsProviderState = {
user: null,
initialized: false,
};
export const AppSettingsProviderContext =
createContext<AppSettingsProviderState>(initialState);
export const AppSettingsProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const [initialized, setInitialized] = useState<boolean>(false);
const [version, setVersion] = useState<string>("");
const [user, setUser] = useState<UserType | null>(null);
const [libraryPath, setLibraryPath] = useState("");
const [whisperModelsPath, setWhisperModelsPath] = useState<string>("");
const [whisperModel, setWhisperModel] = useState<string>(null);
const [ffmpegConfg, setFfmegConfig] = useState<FfmpegConfigType>(null);
const EnjoyApp = window.__ENJOY_APP__;
useEffect(() => {
fetchVersion();
fetchUser();
fetchLibraryPath();
fetchModel();
fetchFfmpegConfig();
}, []);
useEffect(() => {
updatePaths();
}, [libraryPath]);
useEffect(() => {
validate();
}, [user, libraryPath, whisperModel, ffmpegConfg]);
const fetchFfmpegConfig = async () => {
const config = await EnjoyApp.settings.getFfmpegConfig();
setFfmegConfig(config);
};
const fetchVersion = async () => {
const version = EnjoyApp.app.version;
setVersion(version);
};
const fetchUser = async () => {
const currentUser = await EnjoyApp.settings.getUser();
if (!currentUser) return;
EnjoyApp.webApi.me().then((user) => {
if (user?.id) {
login(currentUser);
} else {
logout();
}
});
};
const login = (user: UserType) => {
setUser(user);
EnjoyApp.settings.setUser(user);
};
const logout = () => {
setUser(null);
EnjoyApp.settings.setUser(null);
};
const fetchLibraryPath = async () => {
const dir = await EnjoyApp.settings.getLibrary();
setLibraryPath(dir);
};
const setLibraryPathHandler = async (dir: string) => {
await EnjoyApp.settings.setLibrary(dir);
setLibraryPath(dir);
};
const updatePaths = async () => {
const _path = await EnjoyApp.settings.getWhisperModelsPath();
setWhisperModelsPath(_path);
};
const fetchModel = async () => {
const whisperModel = await EnjoyApp.settings.getWhisperModel();
setWhisperModel(whisperModel);
};
const setModelHandler = async (name: string) => {
await EnjoyApp.settings.setWhisperModel(name);
setWhisperModel(name);
};
const validate = async () => {
setInitialized(
!!(user && libraryPath && whisperModel && ffmpegConfg?.ready)
);
};
return (
<AppSettingsProviderContext.Provider
value={{
EnjoyApp,
version,
user,
login,
logout,
libraryPath,
setLibraryPath: setLibraryPathHandler,
whisperModelsPath,
whisperModel,
setWhisperModel: setModelHandler,
ffmpegConfg,
setFfmegConfig,
initialized,
}}
>
{children}
</AppSettingsProviderContext.Provider>
);
};

View File

@@ -0,0 +1,79 @@
import { createContext, useState, useEffect, useContext } from "react";
import { AppSettingsProviderContext } from "./app-settings-provider";
import log from "electron-log/renderer";
type DbStateEnum = "connected" | "connecting" | "error" | "disconnected";
type DbState = {
state: DbStateEnum;
path?: string;
error?: string;
connect?: () => void;
addDblistener?: (callback: (event: CustomEvent) => void) => void;
removeDbListener?: (callback: (event: CustomEvent) => void) => void;
};
type DbProviderState = DbState & {
connect?: () => void;
};
const initialState: DbProviderState = {
state: "disconnected",
};
export const DbProviderContext = createContext<DbProviderState>(initialState);
export const DbProvider = ({ children }: { children: React.ReactNode }) => {
const [state, setState] = useState<DbStateEnum>("disconnected");
const [path, setPath] = useState();
const [error, setError] = useState();
const { EnjoyApp } = useContext(AppSettingsProviderContext);
const connect = async () => {
if (["connected", "connecting"].includes(state)) return;
setState("connecting");
const _db = await EnjoyApp.db.init();
setState(_db.state);
setPath(_db.path);
setError(_db.error);
};
const addDblistener = (callback: (event: CustomEvent) => void) => {
document.addEventListener("db-on-transaction", callback);
};
const removeDbListener = (callback: (event: CustomEvent) => void) => {
document.removeEventListener("db-on-transaction", callback);
};
useEffect(() => {
if (state !== "connected") return;
EnjoyApp.db.onTransaction((_event, state) => {
log.debug("db-on-transaction", state);
const event = new CustomEvent("db-on-transaction", { detail: state });
document.dispatchEvent(event);
});
return () => {
EnjoyApp.db.removeListeners();
};
}, [state]);
return (
<DbProviderContext.Provider
value={{
state,
path,
error,
connect,
addDblistener,
removeDbListener,
}}
>
{children}
</DbProviderContext.Provider>
);
};

View File

@@ -0,0 +1,4 @@
export * from "./ai-settings-provider";
export * from "./app-settings-provider";
export * from "./db-provider";
export * from "./theme-provider";

View File

@@ -0,0 +1,73 @@
import { createContext, useContext, useEffect, useState } from "react";
type Theme = "dark" | "light" | "system";
type ThemeProviderProps = {
children: React.ReactNode;
defaultTheme?: Theme;
storageKey?: string;
};
type ThemeProviderState = {
theme: Theme;
setTheme: (theme: Theme) => void;
};
const initialState: ThemeProviderState = {
theme: "system",
setTheme: () => null,
};
const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
export function ThemeProvider({
children,
defaultTheme = "system",
storageKey = "vite-ui-theme",
...props
}: ThemeProviderProps) {
const [theme, setTheme] = useState<Theme>(
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme
);
useEffect(() => {
const root = window.document.documentElement;
root.classList.remove("light", "dark");
if (theme === "system") {
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
.matches
? "dark"
: "light";
root.classList.add(systemTheme);
return;
}
root.classList.add(theme);
}, [theme]);
const value = {
theme,
setTheme: (theme: Theme) => {
localStorage.setItem(storageKey, theme);
setTheme(theme);
},
};
return (
<ThemeProviderContext.Provider {...props} value={value}>
{children}
</ThemeProviderContext.Provider>
);
}
export const useTheme = () => {
const context = useContext(ThemeProviderContext);
if (context === undefined)
throw new Error("useTheme must be used within a ThemeProvider");
return context;
};