Feat may set api url (#850)

* add api url settings

* may edit api setting before login
This commit is contained in:
an-lee
2024-07-20 17:18:33 +08:00
committed by GitHub
parent fef5aac7b2
commit 223ae743ed
11 changed files with 242 additions and 58 deletions

View File

@@ -9,6 +9,10 @@ import {
AvatarFallback,
Button,
toast,
Tabs,
TabsList,
TabsTrigger,
TabsContent,
} from "@renderer/components/ui";
import { useContext, useEffect, useState } from "react";
import { AppSettingsProviderContext } from "@renderer/context";
@@ -19,6 +23,8 @@ import {
GithubLoginButton,
BanduLoginButton,
MixinLoginButton,
ProxySettings,
ApiUrlSettings,
} from "@renderer/components";
import { EmailLoginForm } from "./email-login-form";
import { Client } from "@/api";
@@ -68,71 +74,101 @@ export const LoginForm = () => {
if (rememberedUser) {
return (
<div className="px-4 py-2 border rounded-lg w-full max-w-sm">
<div className="flex items-start justify-between py-4">
<div className="">
<div className="flex items-center space-x-2">
<Avatar>
<AvatarImage
crossOrigin="anonymous"
src={rememberedUser.avatarUrl}
/>
<AvatarFallback className="text-xl">
{rememberedUser.name[0].toUpperCase()}
</AvatarFallback>
</Avatar>
<Tabs className="w-full max-w-md" defaultValue="login">
<TabsList className="w-full grid grid-cols-2">
<TabsTrigger value="login">{t("login")}</TabsTrigger>
<TabsTrigger value="advanced">{t("advanced")}</TabsTrigger>
</TabsList>
<TabsContent value="login">
<div className="px-4 py-2 border rounded-lg w-full max-w-md">
<div className="flex items-start justify-between py-4">
<div className="">
<div className="text-sm font-semibold">
{rememberedUser.name}
</div>
<div className="text-xs text-muted-foreground">
{rememberedUser.id}
<div className="flex items-center space-x-2">
<Avatar>
<AvatarImage
crossOrigin="anonymous"
src={rememberedUser.avatarUrl}
/>
<AvatarFallback className="text-xl">
{rememberedUser.name[0].toUpperCase()}
</AvatarFallback>
</Avatar>
<div className="">
<div className="text-sm font-semibold">
{rememberedUser.name}
</div>
<div className="text-xs text-muted-foreground">
{rememberedUser.id}
</div>
</div>
</div>
</div>
<div className="grid grid-cols-2 gap-2">
<Button
variant="secondary"
size="sm"
onClick={() => setRememberedUser(null)}
>
{t("reLogin")}
</Button>
<Button
variant="default"
size="sm"
onClick={loginWithRememberedUser}
>
{t("login")}
</Button>
</div>
</div>
</div>
<div className="grid grid-cols-2 gap-2">
<Button
variant="secondary"
size="sm"
onClick={() => setRememberedUser(null)}
>
{t("reLogin")}
</Button>
<Button
variant="default"
size="sm"
onClick={loginWithRememberedUser}
>
{t("login")}
</Button>
</div>
</div>
</div>
</TabsContent>
<TabsContent value="advanced">
<Card className="w-full max-w-md">
<CardContent className="mt-6">
<ApiUrlSettings />
<Separator />
<ProxySettings />
</CardContent>
</Card>
</TabsContent>
</Tabs>
);
}
return (
<Card className="w-full max-w-sm">
<CardHeader>
<CardTitle>{t("login")}</CardTitle>
</CardHeader>
<Tabs className="w-full max-w-md" defaultValue="login">
<TabsList className="w-full grid grid-cols-2">
<TabsTrigger value="login">{t("login")}</TabsTrigger>
<TabsTrigger value="advanced">{t("advanced")}</TabsTrigger>
</TabsList>
<TabsContent value="login">
<Card className="w-full max-w-md">
<CardContent className="mt-6">
<EmailLoginForm />
<CardContent>
<EmailLoginForm />
<div className="">
<Separator className="my-4" />
<div className="flex items-center justify-center text-xs text-muted-foreground mb-4">
{t("youCanAlsoLoginWith")}
</div>
<div className="flex items-center space-x-2 justify-center">
<GithubLoginButton />
<MixinLoginButton />
<BanduLoginButton />
</div>
</div>
</CardContent>
</Card>
<div className="">
<Separator className="my-4" />
<div className="flex items-center justify-center text-xs text-muted-foreground mb-4">
{t("youCanAlsoLoginWith")}
</div>
<div className="flex items-center space-x-2 justify-center">
<GithubLoginButton />
<MixinLoginButton />
<BanduLoginButton />
</div>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="advanced">
<Card className="w-full max-w-md">
<CardContent className="mt-6">
<ApiUrlSettings />
<Separator />
<ProxySettings />
</CardContent>
</Card>
</TabsContent>
</Tabs>
);
};

View File

@@ -0,0 +1,112 @@
import * as z from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { t } from "i18next";
import {
Button,
Form,
FormField,
FormItem,
FormControl,
Input,
toast,
} from "@renderer/components/ui";
import { AppSettingsProviderContext } from "@renderer/context";
import { useContext, useState, useEffect } from "react";
import { InfoIcon } from "lucide-react";
export const ApiUrlSettings = () => {
const { apiUrl, setApiUrl } = useContext(AppSettingsProviderContext);
const [editing, setEditing] = useState(false);
const apiConfigSchema = z.object({
url: z.string().url(),
});
const form = useForm({
mode: "onBlur",
resolver: zodResolver(apiConfigSchema),
values: {
url: apiUrl,
},
});
const onSubmit = async (data: z.infer<typeof apiConfigSchema>) => {
setApiUrl(data.url).then(() => {
toast.success(t("apiUrlUpdated"));
setEditing(false);
});
};
useEffect(() => {}, [apiUrl]);
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<div className="flex items-start justify-between py-4">
<div className="">
<div className="mb-2">{t("apiSettings")}</div>
<div className="text-sm text-muted-foreground mb-2 ml-1">
<FormField
control={form.control}
name="url"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
disabled={!editing}
placeholder="https://enjoy.bot"
value={field.value || ""}
onChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
</div>
</div>
<div className="">
<div className="flex items-center space-x-2 justify-end mb-2">
{editing ? (
<>
<Button
variant="secondary"
onClick={(e) => {
setEditing(!editing);
e.preventDefault();
}}
size="sm"
>
{t("cancel")}
</Button>
<Button
variant="default"
onClick={() => onSubmit(form.getValues())}
size="sm"
>
{t("save")}
</Button>
</>
) : (
<Button
variant="secondary"
onClick={(e) => {
setEditing(!editing);
e.preventDefault();
}}
size="sm"
>
{t("edit")}
</Button>
)}
</div>
<div className="text-xs text-muted-foreground">
<InfoIcon className="mr-1 w-3 h-3 inline" />
<span>{t("reloadIsNeededAfterChanged")}</span>
</div>
</div>
</div>
</form>
</Form>
);
};

View File

@@ -1,5 +1,6 @@
export * from "./preferences";
export * from "./about";
export * from "./api-url-settings";
export * from "./appearance";
export * from "./hotkeys";

View File

@@ -2,6 +2,7 @@ import { t } from "i18next";
import { Button, ScrollArea, Separator } from "@renderer/components/ui";
import {
About,
ApiUrlSettings,
Appearance,
DefaultEngineSettings,
Hotkeys,
@@ -54,6 +55,8 @@ export const Preferences = () => {
<div className="font-semibold mb-4 capitilized">
{t("advancedSettings")}
</div>
<ApiUrlSettings />
<Separator />
<ProxySettings />
<Separator />
<ResetSettings />

View File

@@ -10,6 +10,7 @@ import { SENTRY_DSN } from "@/constants";
type AppSettingsProviderState = {
webApi: Client;
apiUrl?: string;
setApiUrl?: (url: string) => Promise<void>;
user: UserType | null;
initialized: boolean;
version?: string;
@@ -164,6 +165,12 @@ export const AppSettingsProvider = ({
});
};
const setApiUrlHandler = async (url: string) => {
EnjoyApp.settings.setApiUrl(url).then(() => {
EnjoyApp.app.reload();
});
};
const createCable = async (token: string) => {
const wsUrl = await EnjoyApp.app.wsUrl();
const consumer = createConsumer(wsUrl + "/cable?token=" + token);
@@ -220,6 +227,7 @@ export const AppSettingsProvider = ({
version,
webApi,
apiUrl,
setApiUrl: setApiUrlHandler,
user,
login,
logout,