feat: 🎸 add net state (#947)
This commit is contained in:
@@ -64,6 +64,10 @@ export class Client {
|
||||
);
|
||||
}
|
||||
|
||||
up() {
|
||||
return this.api.get("/up");
|
||||
}
|
||||
|
||||
auth(params: {
|
||||
provider: "mixin" | "github" | "bandu" | "email";
|
||||
code?: string;
|
||||
|
||||
@@ -653,5 +653,10 @@
|
||||
"continueLearning": "Continue learning",
|
||||
"enrollNow": "Enroll now",
|
||||
"enrollments": "Enrollments",
|
||||
"noLikesYet": "No likes yet"
|
||||
"noLikesYet": "No likes yet",
|
||||
"apiConnectTime": "API Connect Time ({{apiUrl}})",
|
||||
"ipInfo": "IP Information",
|
||||
"platformInfo": "Platform Information",
|
||||
"networkState": "Network State",
|
||||
"connectError": "Connect Error"
|
||||
}
|
||||
|
||||
@@ -653,5 +653,10 @@
|
||||
"continueLearning": "继续练习",
|
||||
"enrollNow": "加入练习",
|
||||
"enrollments": "参加的课程",
|
||||
"noLikesYet": "还没有点赞"
|
||||
"noLikesYet": "还没有点赞",
|
||||
"apiConnectTime": "API 延时 ({{apiUrl}})",
|
||||
"ipInfo": "IP 信息",
|
||||
"platformInfo": "设备信息",
|
||||
"networkState": "网络状态",
|
||||
"connectError": "连接错误"
|
||||
}
|
||||
|
||||
@@ -280,6 +280,14 @@ main.init = () => {
|
||||
});
|
||||
|
||||
// App options
|
||||
ipcMain.handle("app-platform-info", () => {
|
||||
return {
|
||||
platform: process.platform,
|
||||
arch: process.arch,
|
||||
version: process.getSystemVersion(),
|
||||
};
|
||||
});
|
||||
|
||||
ipcMain.handle("app-reset", () => {
|
||||
fs.removeSync(settings.userDataPath());
|
||||
fs.removeSync(settings.file());
|
||||
|
||||
@@ -6,6 +6,9 @@ import { Timeline } from "echogarden/dist/utilities/Timeline";
|
||||
|
||||
contextBridge.exposeInMainWorld("__ENJOY_APP__", {
|
||||
app: {
|
||||
getPlatformInfo: () => {
|
||||
return ipcRenderer.invoke("app-platform-info");
|
||||
},
|
||||
reset: () => {
|
||||
ipcRenderer.invoke("app-reset");
|
||||
},
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
MixinLoginButton,
|
||||
ProxySettings,
|
||||
ApiUrlSettings,
|
||||
NetworkState,
|
||||
} from "@renderer/components";
|
||||
import { EmailLoginForm } from "./email-login-form";
|
||||
import { Client } from "@/api";
|
||||
@@ -128,6 +129,8 @@ export const LoginForm = () => {
|
||||
<ApiUrlSettings />
|
||||
<Separator />
|
||||
<ProxySettings />
|
||||
<Separator />
|
||||
<NetworkState />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
@@ -166,6 +169,8 @@ export const LoginForm = () => {
|
||||
<ApiUrlSettings />
|
||||
<Separator />
|
||||
<ProxySettings />
|
||||
<Separator />
|
||||
<NetworkState />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
@@ -56,7 +56,7 @@ export const DefaultEngineSettings = () => {
|
||||
if (form.watch("name") === "openai") {
|
||||
const customModels = openai?.models?.split(",")?.filter(Boolean);
|
||||
|
||||
return customModels.length ? customModels : providers.openai.models;
|
||||
return customModels?.length ? customModels : providers.openai.models;
|
||||
} else {
|
||||
return providers.enjoyai.models;
|
||||
}
|
||||
|
||||
@@ -27,4 +27,5 @@ export * from "./theme-settings";
|
||||
|
||||
export * from "./proxy-settings";
|
||||
|
||||
export * from "./whisper-model-options";
|
||||
export * from "./whisper-model-options";
|
||||
export * from "./network-state";
|
||||
|
||||
150
enjoy/src/renderer/components/preferences/network-state.tsx
Normal file
150
enjoy/src/renderer/components/preferences/network-state.tsx
Normal file
@@ -0,0 +1,150 @@
|
||||
import { Client } from "@/api";
|
||||
import { t } from "i18next";
|
||||
import { useState, useContext, useEffect, useMemo } from "react";
|
||||
import { AppSettingsProviderContext } from "@renderer/context";
|
||||
import { LoaderIcon } from "lucide-react";
|
||||
|
||||
export const NetworkState = () => {
|
||||
const { apiUrl, EnjoyApp } = useContext(AppSettingsProviderContext);
|
||||
|
||||
let timeoutId: ReturnType<typeof setTimeout | null> = null;
|
||||
|
||||
const [apiConnected, setApiConnected] = useState(false);
|
||||
const [apiConnecting, setApiConnecting] = useState(false);
|
||||
const [apiConnectError, setApiConnectError] = useState(false);
|
||||
const [apiConnectTime, setApiConnectTime] = useState<number>(null);
|
||||
|
||||
const [ipInfoError, setIpInfoError] = useState(false);
|
||||
const [ipInfo, setIpInfo] = useState(null);
|
||||
|
||||
const [platformInfo, setPlatformInfo] = useState<PlatformInfo>(null);
|
||||
|
||||
const items = useMemo(() => {
|
||||
const apiStateColor =
|
||||
apiConnectTime < 200
|
||||
? "text-green-500"
|
||||
: apiConnectTime < 800
|
||||
? "text-yellow-500"
|
||||
: "text-red-500";
|
||||
|
||||
return [
|
||||
{
|
||||
title: t("apiConnectTime", { apiUrl }),
|
||||
loading: !apiConnected && apiConnecting,
|
||||
error: apiConnectError,
|
||||
value: <span className={apiStateColor}>{apiConnectTime}ms</span>,
|
||||
},
|
||||
{
|
||||
title: t("ipInfo"),
|
||||
loading: false,
|
||||
error: ipInfoError,
|
||||
value: (
|
||||
<span>
|
||||
{ipInfo
|
||||
? `${ipInfo.ip} (${ipInfo.city}, ${ipInfo.country_name})`
|
||||
: "-"}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("platformInfo"),
|
||||
loading: false,
|
||||
error: false,
|
||||
value: (
|
||||
<span>
|
||||
{platformInfo
|
||||
? `${platformInfo.platform} ${platformInfo.arch} ${platformInfo.version}`
|
||||
: "-"}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
];
|
||||
}, [
|
||||
apiConnectTime,
|
||||
apiConnecting,
|
||||
apiConnected,
|
||||
apiConnectError,
|
||||
ipInfo,
|
||||
ipInfoError,
|
||||
platformInfo,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
pollingAction();
|
||||
getPlatformInfo();
|
||||
|
||||
return () => {
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
async function pollingAction() {
|
||||
await Promise.all([getApiConnectTime(), getIpInfo()]);
|
||||
|
||||
timeoutId = setTimeout(() => pollingAction(), 10000);
|
||||
}
|
||||
|
||||
async function getApiConnectTime() {
|
||||
setApiConnecting(true);
|
||||
try {
|
||||
const client = new Client({ baseUrl: apiUrl });
|
||||
const startTime = new Date().getTime();
|
||||
await client.up();
|
||||
const endTime = new Date().getTime();
|
||||
|
||||
setApiConnectTime(endTime - startTime);
|
||||
setApiConnected(true);
|
||||
} catch (error) {
|
||||
setApiConnectError(true);
|
||||
setApiConnected(false);
|
||||
}
|
||||
setApiConnecting(false);
|
||||
}
|
||||
|
||||
async function getIpInfo() {
|
||||
try {
|
||||
await fetch("https://ipapi.co/json")
|
||||
.then((resp) => resp.json())
|
||||
.then((info) => setIpInfo(info));
|
||||
} catch (error) {
|
||||
setIpInfoError(true);
|
||||
}
|
||||
}
|
||||
|
||||
async function getPlatformInfo() {
|
||||
const info = await EnjoyApp.app.getPlatformInfo();
|
||||
setPlatformInfo(info);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="py-4">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="mb-2">{t("networkState")}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{items.map((item, index) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="text-sm text-muted-foreground flex justify-between my-2"
|
||||
>
|
||||
<div className="">{item.title}</div>
|
||||
<div className="">
|
||||
{item.loading ? (
|
||||
<LoaderIcon className="w-4 h-4 animate-spin" />
|
||||
) : item.error ? (
|
||||
<span className="text-red-500">{t("connectError")}</span>
|
||||
) : (
|
||||
item.value
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
ResetAllSettings,
|
||||
NativeLanguageSettings,
|
||||
LearningLanguageSettings,
|
||||
NetworkState,
|
||||
} from "@renderer/components";
|
||||
import { useState } from "react";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
@@ -56,6 +57,8 @@ export const Preferences = () => {
|
||||
<Separator />
|
||||
<ProxySettings />
|
||||
<Separator />
|
||||
<NetworkState />
|
||||
<Separator />
|
||||
<ResetSettings />
|
||||
<Separator />
|
||||
<ResetAllSettings />
|
||||
|
||||
@@ -12,13 +12,11 @@ import {
|
||||
Input,
|
||||
toast,
|
||||
} from "@renderer/components/ui";
|
||||
import { InfoIcon } from "lucide-react";
|
||||
import { AppSettingsProviderContext } from "@renderer/context";
|
||||
import { useContext, useState, useEffect } from "react";
|
||||
import { useContext, useState } from "react";
|
||||
|
||||
export const ProxySettings = () => {
|
||||
const { proxy, setProxy } = useContext(AppSettingsProviderContext);
|
||||
const [ipData, setIpData] = useState(null);
|
||||
const [editing, setEditing] = useState(false);
|
||||
|
||||
const proxyConfigSchema = z.object({
|
||||
@@ -51,22 +49,6 @@ export const ProxySettings = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const checkIp = async () => {
|
||||
fetch("https://ipapi.co/json")
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
setIpData(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
toast.error(error.message);
|
||||
setIpData(null);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
checkIp();
|
||||
}, [proxy]);
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||
@@ -91,18 +73,6 @@ export const ProxySettings = () => {
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{form.getValues("enabled") && ipData && (
|
||||
<div className="text-sm text-muted-foreground mb-2 ml-1">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div>
|
||||
IP: {ipData.ip} ({ipData.city}, {ipData.country_name})
|
||||
</div>
|
||||
<div>
|
||||
<InfoIcon size={16} className="cursor-pointer" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2 justify-end">
|
||||
|
||||
1
enjoy/src/types/enjoy-app.d.ts
vendored
1
enjoy/src/types/enjoy-app.d.ts
vendored
@@ -1,5 +1,6 @@
|
||||
type EnjoyAppType = {
|
||||
app: {
|
||||
getPlatformInfo: () => Promise<PlatformInfo>;
|
||||
reset: () => Promise<void>;
|
||||
resetSettings: () => Promise<void>;
|
||||
relaunch: () => Promise<void>;
|
||||
|
||||
6
enjoy/src/types/index.d.ts
vendored
6
enjoy/src/types/index.d.ts
vendored
@@ -177,3 +177,9 @@ type GptEngineSettingType = {
|
||||
baseUrl?: string;
|
||||
key?: string;
|
||||
};
|
||||
|
||||
type PlatformInfo = {
|
||||
platform: string;
|
||||
arch: string;
|
||||
version: string;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user