Cleanup server address selector

This commit is contained in:
Zoe Roux 2024-03-06 21:27:53 +01:00
parent d52cc045e0
commit 3821950e49
9 changed files with 59 additions and 47 deletions

View File

@ -19,5 +19,6 @@
*/
import { LoginPage } from "@kyoo/ui";
import { withRoute } from "../../utils";
export default LoginPage;
export default withRoute(LoginPage);

View File

@ -19,5 +19,6 @@
*/
import { RegisterPage } from "@kyoo/ui";
import { withRoute } from "../../utils";
export default RegisterPage;
export default withRoute(RegisterPage);

View File

@ -49,16 +49,17 @@ export const queryFn = async <Parser extends z.ZodTypeAny>(
type?: Parser,
token?: string | null,
): Promise<z.infer<Parser>> => {
const url = context.apiUrl ?? getCurrentApiUrl();
const url = context.apiUrl ? null : getCurrentApiUrl();
if (token === undefined && context.authenticated !== false) token = await getToken();
const path = [url]
.concat(
"path" in context
? (context.path.filter((x) => x) as string[])
? (context.path as string[])
: "pageParam" in context && context.pageParam
? [context.pageParam as string]
: (context.queryKey.filter((x) => x) as string[]),
: (context.queryKey as string[]),
)
.filter((x) => x)
.join("/")
.replace("/?", "?");
let resp;
@ -182,19 +183,19 @@ export type QueryPage<Props = {}, Items = unknown> = ComponentType<
export const toQueryKey = (query: {
path: (string | undefined)[];
params?: { [query: string]: boolean | number | string | string[] | undefined };
options?: { apiUrl?: string };
}) => {
if (query.params) {
return [
...query.path,
"?" +
return [
query.options?.apiUrl,
...query.path,
query.params
? "?" +
Object.entries(query.params)
.filter(([_, v]) => v !== undefined)
.map(([k, v]) => `${k}=${Array.isArray(v) ? v.join(",") : v}`)
.join("&"),
];
} else {
return query.path;
}
.join("&")
: null,
];
};
export const useFetch = <Data,>(query: QueryIdentifier<Data>) => {

View File

@ -18,28 +18,37 @@
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
import { ComponentProps, ReactElement, forwardRef } from "react";
import { ComponentType, ForwardedRef, ReactElement, forwardRef } from "react";
import { Theme, useYoshiki } from "yoshiki/native";
import { PressableFeedback } from "./links";
import { P } from "./text";
import { ts } from "./utils";
import { Falsy, View } from "react-native";
import { Falsy, PressableProps, View } from "react-native";
export const Button = forwardRef<
View,
export const Button = forwardRef(function Button<AsProps = PressableProps>(
{
children,
text,
icon,
licon,
as,
...props
}: {
children?: ReactElement | Falsy;
text?: string;
licon?: ReactElement | Falsy;
icon?: ReactElement | Falsy;
} & ComponentProps<typeof PressableFeedback>
>(function Button({ children, text, icon, licon, ...props }, ref) {
as?: ComponentType<AsProps>;
} & AsProps,
ref: ForwardedRef<unknown>,
) {
const { css } = useYoshiki("button");
const Container = as ?? PressableFeedback;
return (
<PressableFeedback
ref={ref}
{...css(
<Container
ref={ref as any}
{...(css(
{
flexGrow: 0,
flexDirection: "row",
@ -48,7 +57,7 @@ export const Button = forwardRef<
overflow: "hidden",
p: ts(0.5),
borderRadius: ts(5),
borderColor: (theme) => theme.accent,
borderColor: (theme: Theme) => theme.accent,
borderWidth: ts(0.5),
fover: {
self: { bg: (theme: Theme) => theme.accent },
@ -56,7 +65,7 @@ export const Button = forwardRef<
},
},
props as any,
)}
) as AsProps)}
>
{(licon || text || icon) != null && (
<View
@ -72,6 +81,6 @@ export const Button = forwardRef<
</View>
)}
{children}
</PressableFeedback>
</Container>
);
});

View File

@ -40,10 +40,14 @@ const SvgBlob = (props: SvgProps) => {
);
};
export const FormPage = ({ children, ...props }: { children: ReactNode } & Stylable) => {
export const FormPage = ({
children,
apiUrl,
...props
}: { children: ReactNode; apiUrl?: string } & Stylable) => {
const { css } = useYoshiki();
const src = imageFn("/items/random/thumbnail");
const src = apiUrl ? `${apiUrl}/items/random/thumbnail` : imageFn("/items/random/thumbnail");
const nativeProps = Platform.select<Partial<ImageProps>>({
web: {
defaultSource: { uri: src },

View File

@ -44,6 +44,7 @@ export const LoginPage: QueryPage<{ apiUrl?: string; error?: string }> = ({
const { css } = useYoshiki();
useEffect(() => {
console.log("login", apiUrl);
if (!apiUrl && Platform.OS !== "web")
router.replace("/server-url", undefined, {
experimental: { nativeBehavior: "stack-replace", isNestedNavigator: false },
@ -51,7 +52,7 @@ export const LoginPage: QueryPage<{ apiUrl?: string; error?: string }> = ({
}, [apiUrl, router]);
return (
<FormPage>
<FormPage apiUrl={apiUrl}>
<H1>{t("login.login")}</H1>
<OidcLogin apiUrl={apiUrl} />
<P {...css({ paddingLeft: ts(1) })}>{t("login.username")}</P>

View File

@ -104,7 +104,7 @@ export const OidcCallbackPage: QueryPage<{
hasRun.current = true;
function onError(error: string) {
router.replace(`/login?error=${error}${apiUrl ? `&apiUrl=${apiUrl}` : ""}`, undefined, {
router.replace({pathname: "/login", query: { error, apiUrl }}, undefined, {
experimental: { nativeBehavior: "stack-replace", isNestedNavigator: false },
});
}

View File

@ -50,7 +50,7 @@ export const RegisterPage: QueryPage<{ apiUrl?: string }> = ({ apiUrl }) => {
}, [apiUrl, router]);
return (
<FormPage>
<FormPage apiUrl={apiUrl}>
<H1>{t("login.register")}</H1>
<OidcLogin apiUrl={apiUrl} />
<P {...css({ paddingLeft: ts(1) })}>{t("login.username")}</P>

View File

@ -19,7 +19,7 @@
*/
import { QueryIdentifier, QueryPage, ServerInfo, ServerInfoP, useFetch } from "@kyoo/models";
import { Button, P, Input, ts, H1, HR } from "@kyoo/primitives";
import { Button, P, Input, ts, H1, HR, Link } from "@kyoo/primitives";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Platform, View } from "react-native";
@ -40,8 +40,9 @@ const query: QueryIdentifier<ServerInfo> = {
};
export const ServerUrlPage: QueryPage = () => {
const [apiUrl, setApiUrl] = useState("");
const { data, error } = useFetch(query);
const [_apiUrl, setApiUrl] = useState("");
const apiUrl = cleanApiUrl(_apiUrl);
const { data, error } = useFetch({ ...query, options: { apiUrl: apiUrl } });
const router = useRouter();
const { t } = useTranslation();
const { css } = useYoshiki();
@ -57,11 +58,9 @@ export const ServerUrlPage: QueryPage = () => {
<H1>{t("login.server")}</H1>
<View {...css({ justifyContent: "center" })}>
<Input variant="big" onChangeText={setApiUrl} />
{error && (
<P {...css({ color: (theme: Theme) => theme.colors.red, alignSelf: "center" })}>
{error.errors[0]}
</P>
)}
<P {...css({ color: (theme: Theme) => theme.colors.red, alignSelf: "center" })}>
{error?.errors[0] ?? " "}
</P>
</View>
<View {...css({ marginTop: ts(5) })}>
<Button
@ -73,20 +72,16 @@ export const ServerUrlPage: QueryPage = () => {
<View {...css({ flexDirection: "row", gap: ts(2) })}>
<Button
text={t("login.login")}
onPress={async () => {
router.replace(`/login?apiUrl=${apiUrl}`, undefined, {
experimental: { nativeBehavior: "stack-replace", isNestedNavigator: false },
});
onPress={() => {
router.push(`/login?apiUrl=${apiUrl}`);
}}
disabled={error != null}
{...css({ flexGrow: 1, flexShrink: 1 })}
/>
<Button
text={t("login.register")}
onPress={async () => {
router.replace(`/register?apiUrl=${apiUrl}`, undefined, {
experimental: { nativeBehavior: "stack-replace", isNestedNavigator: false },
});
onPress={() => {
router.push(`/register?apiUrl=${apiUrl}`);
}}
disabled={error != null}
{...css({ flexGrow: 1, flexShrink: 1 })}