mirror of
https://github.com/movie-web/native-app.git
synced 2025-09-13 14:23:28 +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 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() {
|
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 { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
import { ScrollView } from "tamagui";
|
import { ScrollView } from "tamagui";
|
||||||
import { LinearGradient } from "tamagui/linear-gradient";
|
import { LinearGradient } from "tamagui/linear-gradient";
|
||||||
@@ -5,26 +6,14 @@ import { LinearGradient } from "tamagui/linear-gradient";
|
|||||||
import { Header } from "./Header";
|
import { Header } from "./Header";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children?: React.ReactNode;
|
|
||||||
onScrollBeginDrag?: () => void;
|
|
||||||
onMomentumScrollEnd?: () => void;
|
|
||||||
showHeader?: boolean;
|
showHeader?: boolean;
|
||||||
scrollEnabled?: boolean;
|
|
||||||
keyboardDismissMode?: "none" | "on-drag" | "interactive";
|
|
||||||
keyboardShouldPersistTaps?: "always" | "never" | "handled";
|
|
||||||
contentContainerStyle?: Record<string, unknown>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ScreenLayout({
|
export default function ScreenLayout({
|
||||||
children,
|
children,
|
||||||
onScrollBeginDrag,
|
|
||||||
onMomentumScrollEnd,
|
|
||||||
showHeader = true,
|
showHeader = true,
|
||||||
scrollEnabled,
|
...props
|
||||||
keyboardDismissMode,
|
}: ScrollViewProps & Props) {
|
||||||
keyboardShouldPersistTaps,
|
|
||||||
contentContainerStyle,
|
|
||||||
}: Props) {
|
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -47,15 +36,10 @@ export default function ScreenLayout({
|
|||||||
>
|
>
|
||||||
{showHeader && <Header />}
|
{showHeader && <Header />}
|
||||||
<ScrollView
|
<ScrollView
|
||||||
onScrollBeginDrag={onScrollBeginDrag}
|
|
||||||
onMomentumScrollEnd={onMomentumScrollEnd}
|
|
||||||
scrollEnabled={scrollEnabled}
|
|
||||||
keyboardDismissMode={keyboardDismissMode}
|
|
||||||
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
|
|
||||||
contentContainerStyle={contentContainerStyle}
|
|
||||||
marginTop="$4"
|
marginTop="$4"
|
||||||
flexGrow={1}
|
flexGrow={1}
|
||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
@@ -7,21 +7,33 @@ export const MWButton = styled(Button, {
|
|||||||
backgroundColor: "$buttonPrimaryBackground",
|
backgroundColor: "$buttonPrimaryBackground",
|
||||||
color: "$buttonPrimaryText",
|
color: "$buttonPrimaryText",
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
|
pressStyle: {
|
||||||
|
backgroundColor: "$buttonPrimaryBackgroundHover",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
backgroundColor: "$buttonSecondaryBackground",
|
backgroundColor: "$buttonSecondaryBackground",
|
||||||
color: "$buttonSecondaryText",
|
color: "$buttonSecondaryText",
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
|
pressStyle: {
|
||||||
|
backgroundColor: "$buttonSecondaryBackgroundHover",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
purple: {
|
purple: {
|
||||||
backgroundColor: "$buttonPurpleBackground",
|
backgroundColor: "$buttonPurpleBackground",
|
||||||
color: "white",
|
color: "white",
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
|
pressStyle: {
|
||||||
|
backgroundColor: "$buttonPurpleBackgroundHover",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
cancel: {
|
cancel: {
|
||||||
backgroundColor: "$buttonCancelBackground",
|
backgroundColor: "$buttonCancelBackground",
|
||||||
color: "white",
|
color: "white",
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
|
pressStyle: {
|
||||||
|
backgroundColor: "$buttonCancelBackgroundHover",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const,
|
} 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