mirror of
https://github.com/movie-web/native-app.git
synced 2025-09-13 14:43:25 +00:00
start with movie-web page
This commit is contained in:
@@ -1,5 +1,60 @@
|
||||
import { useState } from "react";
|
||||
import { Link } from "expo-router";
|
||||
import { H2, H5, Paragraph, View } from "tamagui";
|
||||
|
||||
import ScreenLayout from "~/components/layout/ScreenLayout";
|
||||
import { MWButton } from "~/components/ui/Button";
|
||||
import { MWCard } from "~/components/ui/Card";
|
||||
import { MWInput } from "~/components/ui/Input";
|
||||
|
||||
export default function MovieWebScreen() {
|
||||
return <ScreenLayout></ScreenLayout>;
|
||||
const [url, setUrl] = useState("https://mw-backend.lonelil.ru");
|
||||
|
||||
return (
|
||||
<ScreenLayout
|
||||
contentContainerStyle={{
|
||||
flexGrow: 1,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<MWCard bordered>
|
||||
<MWCard.Header padded>
|
||||
<H2 fontWeight="$bold" paddingBottom="$1">
|
||||
Sync to the cloud
|
||||
</H2>
|
||||
<H5 color="$ash50" fontWeight="$semibold" paddingVertical="$3">
|
||||
Share your watch progress between devices and keep them synced.
|
||||
</H5>
|
||||
<Paragraph color="$ash50">
|
||||
First choose the backend you want to use. If you do not know what
|
||||
this does, use the default and click on 'Get started'.
|
||||
</Paragraph>
|
||||
</MWCard.Header>
|
||||
|
||||
<View padding="$4">
|
||||
<MWInput
|
||||
placeholder="https://mw-backend.lonelil.ru"
|
||||
type="search"
|
||||
value={url}
|
||||
onChangeText={setUrl}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<MWCard.Footer padded justifyContent="center">
|
||||
<MWButton type="purple">
|
||||
<Link
|
||||
href={{
|
||||
pathname: "/sync/trust/[url]",
|
||||
params: { url },
|
||||
}}
|
||||
style={{ color: "white", fontWeight: "bold" }}
|
||||
>
|
||||
Get started
|
||||
</Link>
|
||||
</MWButton>
|
||||
</MWCard.Footer>
|
||||
</MWCard>
|
||||
</ScreenLayout>
|
||||
);
|
||||
}
|
||||
|
14
apps/expo/src/app/sync/_layout.tsx
Normal file
14
apps/expo/src/app/sync/_layout.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Stack } from "expo-router";
|
||||
|
||||
import { BrandPill } from "~/components/BrandPill";
|
||||
|
||||
export default function Layout() {
|
||||
return (
|
||||
<Stack
|
||||
screenOptions={{
|
||||
headerTransparent: true,
|
||||
headerRight: BrandPill,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
117
apps/expo/src/app/sync/trust/[url].tsx
Normal file
117
apps/expo/src/app/sync/trust/[url].tsx
Normal file
@@ -0,0 +1,117 @@
|
||||
import { Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { H4, Paragraph, Text, View } from "tamagui";
|
||||
|
||||
import ScreenLayout from "~/components/layout/ScreenLayout";
|
||||
import { MWButton } from "~/components/ui/Button";
|
||||
import { MWCard } from "~/components/ui/Card";
|
||||
|
||||
// TODO: extract to function with cleanup and types
|
||||
const getBackendMeta = (
|
||||
url: string,
|
||||
): Promise<{
|
||||
description: string;
|
||||
hasCaptcha: boolean;
|
||||
name: string;
|
||||
url: string;
|
||||
}> => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return fetch(`${url}/meta`).then((res) => res.json());
|
||||
};
|
||||
|
||||
export default function Page() {
|
||||
const { url } = useLocalSearchParams();
|
||||
|
||||
const meta = useQuery({
|
||||
queryKey: ["backendMeta", url],
|
||||
queryFn: () => getBackendMeta(url as string),
|
||||
});
|
||||
|
||||
return (
|
||||
<ScreenLayout
|
||||
showHeader={false}
|
||||
contentContainerStyle={{
|
||||
flexGrow: 1,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: "",
|
||||
}}
|
||||
/>
|
||||
<MWCard bordered>
|
||||
<MWCard.Header padded>
|
||||
<H4 fontWeight="$bold" textAlign="center">
|
||||
Do you trust this server?
|
||||
</H4>
|
||||
|
||||
<Paragraph
|
||||
color="$ash50"
|
||||
textAlign="center"
|
||||
fontWeight="$semibold"
|
||||
paddingVertical="$4"
|
||||
>
|
||||
{meta.isLoading && "Loading..."}
|
||||
{meta.isError && "Error loading metadata"}
|
||||
{meta.isSuccess && (
|
||||
<>
|
||||
You are connecting to{" "}
|
||||
<Text
|
||||
fontWeight="$bold"
|
||||
color="white"
|
||||
textDecorationLine="underline"
|
||||
>
|
||||
{url}
|
||||
</Text>
|
||||
. Please confirm you trust it before making an account.
|
||||
</>
|
||||
)}
|
||||
</Paragraph>
|
||||
</MWCard.Header>
|
||||
|
||||
{meta.isSuccess && (
|
||||
<View
|
||||
borderColor="$shade200"
|
||||
borderWidth="$0.5"
|
||||
borderRadius="$8"
|
||||
paddingHorizontal="$5"
|
||||
paddingVertical="$4"
|
||||
width="90%"
|
||||
alignSelf="center"
|
||||
>
|
||||
<Text
|
||||
fontWeight="$bold"
|
||||
paddingBottom="$1"
|
||||
textAlign="center"
|
||||
fontSize="$4"
|
||||
>
|
||||
{meta.data.name}
|
||||
</Text>
|
||||
|
||||
<Paragraph color="$ash50" textAlign="center">
|
||||
{meta.data.description}
|
||||
</Paragraph>
|
||||
</View>
|
||||
)}
|
||||
<MWCard.Footer
|
||||
padded
|
||||
justifyContent="center"
|
||||
flexDirection="column"
|
||||
gap="$4"
|
||||
>
|
||||
<MWButton type="purple">I trust this server</MWButton>
|
||||
<MWButton type="secondary">Go back</MWButton>
|
||||
|
||||
<Paragraph color="$ash50" textAlign="center" fontWeight="$semibold">
|
||||
Already have an account?{" "}
|
||||
<Text color="$purple100" fontWeight="$bold">
|
||||
Login here
|
||||
</Text>
|
||||
</Paragraph>
|
||||
</MWCard.Footer>
|
||||
</MWCard>
|
||||
</ScreenLayout>
|
||||
);
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
import type { ScrollViewProps } from "tamagui";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { ScrollView } from "tamagui";
|
||||
import { LinearGradient } from "tamagui/linear-gradient";
|
||||
@@ -5,26 +6,14 @@ import { LinearGradient } from "tamagui/linear-gradient";
|
||||
import { Header } from "./Header";
|
||||
|
||||
interface Props {
|
||||
children?: React.ReactNode;
|
||||
onScrollBeginDrag?: () => void;
|
||||
onMomentumScrollEnd?: () => void;
|
||||
showHeader?: boolean;
|
||||
scrollEnabled?: boolean;
|
||||
keyboardDismissMode?: "none" | "on-drag" | "interactive";
|
||||
keyboardShouldPersistTaps?: "always" | "never" | "handled";
|
||||
contentContainerStyle?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export default function ScreenLayout({
|
||||
children,
|
||||
onScrollBeginDrag,
|
||||
onMomentumScrollEnd,
|
||||
showHeader = true,
|
||||
scrollEnabled,
|
||||
keyboardDismissMode,
|
||||
keyboardShouldPersistTaps,
|
||||
contentContainerStyle,
|
||||
}: Props) {
|
||||
...props
|
||||
}: ScrollViewProps & Props) {
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
return (
|
||||
@@ -47,15 +36,10 @@ export default function ScreenLayout({
|
||||
>
|
||||
{showHeader && <Header />}
|
||||
<ScrollView
|
||||
onScrollBeginDrag={onScrollBeginDrag}
|
||||
onMomentumScrollEnd={onMomentumScrollEnd}
|
||||
scrollEnabled={scrollEnabled}
|
||||
keyboardDismissMode={keyboardDismissMode}
|
||||
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
|
||||
contentContainerStyle={contentContainerStyle}
|
||||
marginTop="$4"
|
||||
flexGrow={1}
|
||||
showsVerticalScrollIndicator={false}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</ScrollView>
|
||||
|
@@ -7,21 +7,33 @@ export const MWButton = styled(Button, {
|
||||
backgroundColor: "$buttonPrimaryBackground",
|
||||
color: "$buttonPrimaryText",
|
||||
fontWeight: "bold",
|
||||
pressStyle: {
|
||||
backgroundColor: "$buttonPrimaryBackgroundHover",
|
||||
},
|
||||
},
|
||||
secondary: {
|
||||
backgroundColor: "$buttonSecondaryBackground",
|
||||
color: "$buttonSecondaryText",
|
||||
fontWeight: "bold",
|
||||
pressStyle: {
|
||||
backgroundColor: "$buttonSecondaryBackgroundHover",
|
||||
},
|
||||
},
|
||||
purple: {
|
||||
backgroundColor: "$buttonPurpleBackground",
|
||||
color: "white",
|
||||
fontWeight: "bold",
|
||||
pressStyle: {
|
||||
backgroundColor: "$buttonPurpleBackgroundHover",
|
||||
},
|
||||
},
|
||||
cancel: {
|
||||
backgroundColor: "$buttonCancelBackground",
|
||||
color: "white",
|
||||
fontWeight: "bold",
|
||||
pressStyle: {
|
||||
backgroundColor: "$buttonCancelBackgroundHover",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const,
|
||||
|
21
apps/expo/src/components/ui/Card.tsx
Normal file
21
apps/expo/src/components/ui/Card.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Card, styled, withStaticProperties } from "tamagui";
|
||||
|
||||
export const MWCardFrame = styled(Card, {
|
||||
backgroundColor: "$shade400",
|
||||
borderColor: "$shade400",
|
||||
|
||||
variants: {
|
||||
bordered: {
|
||||
true: {
|
||||
borderWidth: 1,
|
||||
borderColor: "$shade500",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const MWCard = withStaticProperties(MWCardFrame, {
|
||||
Header: Card.Header,
|
||||
Footer: Card.Footer,
|
||||
Background: Card.Background,
|
||||
});
|
Reference in New Issue
Block a user