mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Add continue with oidc button on login and register pages
This commit is contained in:
parent
5f8d0d1b99
commit
239ad9a4dc
@ -22,11 +22,21 @@ namespace Kyoo.Authentication.Models;
|
||||
|
||||
public class ServerInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of oidc providers configured for this instance of kyoo.
|
||||
/// </summary>
|
||||
public Dictionary<string, OidcInfo> Oidc { get; set; }
|
||||
}
|
||||
|
||||
public class OidcInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of this oidc service. Human readable.
|
||||
/// </summary>
|
||||
public string DisplayName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A url returing a square logo for this provider.
|
||||
/// </summary>
|
||||
public string? LogoUrl { get; set; }
|
||||
}
|
||||
|
@ -32,3 +32,4 @@ export * from "./watch-info";
|
||||
export * from "./watch-status";
|
||||
export * from "./watchlist";
|
||||
export * from "./user";
|
||||
export * from "./server-info";
|
||||
|
54
front/packages/models/src/resources/server-info.ts
Normal file
54
front/packages/models/src/resources/server-info.ts
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { z } from "zod";
|
||||
import { imageFn } from "..";
|
||||
|
||||
export const OidcInfoP = z.object({
|
||||
/*
|
||||
* The name of this oidc service. Human readable.
|
||||
*/
|
||||
displayName: z.string(),
|
||||
/*
|
||||
* A url returing a square logo for this provider.
|
||||
*/
|
||||
logoUrl: z.string().nullable(),
|
||||
});
|
||||
|
||||
export const ServerInfoP = z.object({
|
||||
/*
|
||||
* The list of oidc providers configured for this instance of kyoo.
|
||||
*/
|
||||
oidc: z
|
||||
.record(z.string(), OidcInfoP)
|
||||
.transform((x) =>
|
||||
Object.fromEntries(
|
||||
Object.entries(x).map(([provider, info]) => [
|
||||
provider,
|
||||
{ ...info, link: imageFn(`/auth/login/${provider}`) },
|
||||
]),
|
||||
),
|
||||
),
|
||||
});
|
||||
|
||||
/**
|
||||
* A season of a Show.
|
||||
*/
|
||||
export type ServerInfo = z.infer<typeof ServerInfoP>;
|
@ -23,14 +23,17 @@ import { Theme, useYoshiki } from "yoshiki/native";
|
||||
import { PressableFeedback } from "./links";
|
||||
import { P } from "./text";
|
||||
import { ts } from "./utils";
|
||||
import { View } from "react-native";
|
||||
import { Falsy, View } from "react-native";
|
||||
|
||||
export const Button = forwardRef<
|
||||
View,
|
||||
{ text: string; licon?: ReactElement; icon?: ReactElement } & ComponentProps<
|
||||
typeof PressableFeedback
|
||||
>
|
||||
>(function Button({ text, icon, licon, ...props }, ref) {
|
||||
{
|
||||
children?: ReactElement | Falsy;
|
||||
text?: string;
|
||||
licon?: ReactElement | Falsy;
|
||||
icon?: ReactElement | Falsy;
|
||||
} & ComponentProps<typeof PressableFeedback>
|
||||
>(function Button({ children, text, icon, licon, ...props }, ref) {
|
||||
const { css } = useYoshiki("button");
|
||||
|
||||
return (
|
||||
@ -55,17 +58,20 @@ export const Button = forwardRef<
|
||||
props as any,
|
||||
)}
|
||||
>
|
||||
<View
|
||||
{...css({
|
||||
paddingX: ts(3),
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
})}
|
||||
>
|
||||
{licon}
|
||||
<P {...css({ textAlign: "center" }, "text")}>{text}</P>
|
||||
{icon}
|
||||
</View>
|
||||
{(licon || text || icon) != null && (
|
||||
<View
|
||||
{...css({
|
||||
paddingX: ts(3),
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
})}
|
||||
>
|
||||
{licon}
|
||||
{text && <P {...css({ textAlign: "center" }, "text")}>{text}</P>}
|
||||
{icon}
|
||||
</View>
|
||||
)}
|
||||
{children}
|
||||
</PressableFeedback>
|
||||
);
|
||||
});
|
||||
|
@ -19,7 +19,7 @@
|
||||
*/
|
||||
|
||||
import { HR as EHR } from "@expo/html-elements";
|
||||
import { px, Stylable, useYoshiki } from "yoshiki/native";
|
||||
import { percent, px, Stylable, useYoshiki } from "yoshiki/native";
|
||||
import { ts } from "./utils";
|
||||
|
||||
export const HR = ({
|
||||
@ -39,13 +39,13 @@ export const HR = ({
|
||||
},
|
||||
orientation === "vertical" && {
|
||||
width: px(1),
|
||||
height: "auto",
|
||||
height: percent(100),
|
||||
marginY: ts(1),
|
||||
marginX: ts(2),
|
||||
},
|
||||
orientation === "horizontal" && {
|
||||
height: px(1),
|
||||
width: "auto",
|
||||
width: percent(100),
|
||||
marginX: ts(1),
|
||||
marginY: ts(2),
|
||||
},
|
||||
|
@ -29,7 +29,7 @@ import { percent, px, useYoshiki } from "yoshiki/native";
|
||||
import { DefaultLayout } from "../layout";
|
||||
import { FormPage } from "./form";
|
||||
import { PasswordInput } from "./password-input";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { OidcLogin } from "./oidc";
|
||||
|
||||
export const cleanApiUrl = (apiUrl: string) => {
|
||||
if (Platform.OS === "web") return undefined;
|
||||
@ -45,7 +45,6 @@ export const LoginPage: QueryPage = () => {
|
||||
const [error, setError] = useState<string | undefined>(undefined);
|
||||
|
||||
const router = useRouter();
|
||||
const queryClient = useQueryClient();
|
||||
const { t } = useTranslation();
|
||||
const { css } = useYoshiki();
|
||||
|
||||
@ -56,6 +55,7 @@ export const LoginPage: QueryPage = () => {
|
||||
})}
|
||||
>
|
||||
<H1>{t("login.login")}</H1>
|
||||
<OidcLogin />
|
||||
{Platform.OS !== "web" && (
|
||||
<>
|
||||
<P {...css({ paddingLeft: ts(1) })}>{t("login.server")}</P>
|
||||
@ -102,4 +102,6 @@ export const LoginPage: QueryPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
LoginPage.getFetchUrls = () => [OidcLogin.query()];
|
||||
|
||||
LoginPage.getLayout = DefaultLayout;
|
||||
|
83
front/packages/ui/src/login/oidc.tsx
Normal file
83
front/packages/ui/src/login/oidc.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Kyoo - A portable and vast media library solution.
|
||||
* Copyright (c) Kyoo.
|
||||
*
|
||||
* See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
*
|
||||
* Kyoo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Kyoo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { QueryIdentifier, ServerInfo, ServerInfoP, useFetch } from "@kyoo/models";
|
||||
import { Button, HR, P, Skeleton, tooltip, ts } from "@kyoo/primitives";
|
||||
import { View, ImageBackground } from "react-native";
|
||||
import { percent, rem, useYoshiki } from "yoshiki/native";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ErrorView } from "../errors";
|
||||
|
||||
export const OidcLogin = () => {
|
||||
const { css } = useYoshiki();
|
||||
const { t } = useTranslation();
|
||||
const { data, error } = useFetch(OidcLogin.query());
|
||||
|
||||
const btn = css({ width: { xs: percent(100), sm: percent(75) }, marginY: ts(1) });
|
||||
|
||||
return (
|
||||
<View {...css({ alignItems: "center", marginY: ts(1) })}>
|
||||
{error ? (
|
||||
<ErrorView error={error} />
|
||||
) : data ? (
|
||||
Object.values(data.oidc).map((x) => (
|
||||
<Button
|
||||
href={x.link}
|
||||
key={x.displayName}
|
||||
licon={
|
||||
x.logoUrl != null && (
|
||||
<ImageBackground
|
||||
source={{ uri: x.logoUrl }}
|
||||
{...css({ width: ts(3), height: ts(3), marginRight: ts(2) })}
|
||||
/>
|
||||
)
|
||||
}
|
||||
text={t("login.via", { provider: x.displayName })}
|
||||
{...tooltip(t("login.via", { provider: x.displayName }))}
|
||||
{...btn}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
[...Array(3)].map((_, i) => (
|
||||
<Button key={i} {...btn}>
|
||||
<Skeleton {...css({ width: percent(66), marginY: rem(0.5) })} />
|
||||
</Button>
|
||||
))
|
||||
)}
|
||||
<View
|
||||
{...css({
|
||||
marginY: ts(1),
|
||||
flexDirection: "row",
|
||||
width: percent(100),
|
||||
alignItems: "center",
|
||||
})}
|
||||
>
|
||||
<HR {...css({ flexGrow: 1 })} />
|
||||
<P>{t("misc.or")}</P>
|
||||
<HR {...css({ flexGrow: 1 })} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
OidcLogin.query = (): QueryIdentifier<ServerInfo> => ({
|
||||
path: ["info"],
|
||||
parser: ServerInfoP,
|
||||
});
|
@ -30,6 +30,7 @@ import { DefaultLayout } from "../layout";
|
||||
import { FormPage } from "./form";
|
||||
import { PasswordInput } from "./password-input";
|
||||
import { cleanApiUrl } from "./login";
|
||||
import { OidcLogin } from "./oidc";
|
||||
|
||||
export const RegisterPage: QueryPage = () => {
|
||||
const [apiUrl, setApiUrl] = useState("");
|
||||
@ -46,6 +47,7 @@ export const RegisterPage: QueryPage = () => {
|
||||
return (
|
||||
<FormPage>
|
||||
<H1>{t("login.register")}</H1>
|
||||
<OidcLogin />
|
||||
{Platform.OS !== "web" && (
|
||||
<>
|
||||
<P {...css({ paddingLeft: ts(1) })}>{t("login.server")}</P>
|
||||
@ -109,4 +111,6 @@ export const RegisterPage: QueryPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
RegisterPage.getFetchUrls = () => [OidcLogin.query()];
|
||||
|
||||
RegisterPage.getLayout = DefaultLayout;
|
||||
|
@ -68,7 +68,8 @@
|
||||
"more": "More",
|
||||
"expand": "Expand",
|
||||
"collapse": "Collapse",
|
||||
"edit": "Edit"
|
||||
"edit": "Edit",
|
||||
"or": "OR"
|
||||
},
|
||||
"navbar": {
|
||||
"home": "Home",
|
||||
@ -165,6 +166,7 @@
|
||||
"login": {
|
||||
"login": "Login",
|
||||
"register": "Register",
|
||||
"via": "Continue via {{provider}}",
|
||||
"add-account": "Add account",
|
||||
"logout": "Logout",
|
||||
"server": "Server Address",
|
||||
|
Loading…
x
Reference in New Issue
Block a user