mirror of
https://github.com/movie-web/native-app.git
synced 2025-09-13 14:53:24 +00:00
add register and login screens
This commit is contained in:
@@ -41,6 +41,7 @@
|
|||||||
"expo-av": "~13.10.5",
|
"expo-av": "~13.10.5",
|
||||||
"expo-brightness": "~11.8.0",
|
"expo-brightness": "~11.8.0",
|
||||||
"expo-build-properties": "~0.11.1",
|
"expo-build-properties": "~0.11.1",
|
||||||
|
"expo-clipboard": "^5.0.1",
|
||||||
"expo-constants": "~15.4.5",
|
"expo-constants": "~15.4.5",
|
||||||
"expo-file-system": "~16.0.8",
|
"expo-file-system": "~16.0.8",
|
||||||
"expo-haptics": "~12.8.1",
|
"expo-haptics": "~12.8.1",
|
||||||
|
@@ -55,7 +55,7 @@ const TestDownloadButton = (props: {
|
|||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name="download"
|
name="download"
|
||||||
size={24}
|
size={24}
|
||||||
color={theme.buttonSecondaryText.val}
|
color={theme.silver300.val}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onPress={async () => {
|
onPress={async () => {
|
||||||
|
@@ -1,53 +1,12 @@
|
|||||||
import { Link } from "expo-router";
|
import { Link } from "expo-router";
|
||||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
import { H3, H5, Paragraph, View } from "tamagui";
|
||||||
import { H2, H5, Paragraph, useTheme, View } from "tamagui";
|
|
||||||
|
|
||||||
import ScreenLayout from "~/components/layout/ScreenLayout";
|
import ScreenLayout from "~/components/layout/ScreenLayout";
|
||||||
import { MWButton } from "~/components/ui/Button";
|
import { MWButton } from "~/components/ui/Button";
|
||||||
import { MWCard } from "~/components/ui/Card";
|
import { MWCard } from "~/components/ui/Card";
|
||||||
import { MWInput } from "~/components/ui/Input";
|
import { MWInput } from "~/components/ui/Input";
|
||||||
import { useAuth } from "~/hooks/useAuth";
|
|
||||||
import { useAuthStore } from "~/stores/settings";
|
import { useAuthStore } from "~/stores/settings";
|
||||||
|
|
||||||
function TestButtons() {
|
|
||||||
const theme = useTheme();
|
|
||||||
const { login } = useAuth();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
<MWButton
|
|
||||||
type="secondary"
|
|
||||||
backgroundColor="$sheetItemBackground"
|
|
||||||
marginBottom="$4"
|
|
||||||
icon={
|
|
||||||
<MaterialCommunityIcons
|
|
||||||
name="login"
|
|
||||||
size={24}
|
|
||||||
color={theme.buttonSecondaryText.val}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
onPress={async () => {
|
|
||||||
const passhphrase = "";
|
|
||||||
if (!passhphrase) {
|
|
||||||
alert("Please configure your passphrase");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const account = await login({
|
|
||||||
mnemonic: passhphrase,
|
|
||||||
userData: {
|
|
||||||
device: "phone",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
console.log(account);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
test login
|
|
||||||
</MWButton>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function MovieWebScreen() {
|
export default function MovieWebScreen() {
|
||||||
const { backendUrl, setBackendUrl } = useAuthStore();
|
const { backendUrl, setBackendUrl } = useAuthStore();
|
||||||
|
|
||||||
@@ -59,16 +18,15 @@ export default function MovieWebScreen() {
|
|||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TestButtons />
|
<MWCard bordered padded>
|
||||||
<MWCard bordered>
|
<MWCard.Header>
|
||||||
<MWCard.Header padded>
|
<H3 fontWeight="$bold" paddingBottom="$1">
|
||||||
<H2 fontWeight="$bold" paddingBottom="$1">
|
|
||||||
Sync to the cloud
|
Sync to the cloud
|
||||||
</H2>
|
</H3>
|
||||||
<H5 color="$ash50" fontWeight="$semibold" paddingVertical="$3">
|
<H5 color="$shade200" fontWeight="$semibold" paddingVertical="$3">
|
||||||
Share your watch progress between devices and keep them synced.
|
Share your watch progress between devices and keep them synced.
|
||||||
</H5>
|
</H5>
|
||||||
<Paragraph color="$ash50">
|
<Paragraph color="$shade200">
|
||||||
First choose the backend you want to use. If you do not know what
|
First choose the backend you want to use. If you do not know what
|
||||||
this does, use the default and click on 'Get started'.
|
this does, use the default and click on 'Get started'.
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
@@ -77,24 +35,24 @@ export default function MovieWebScreen() {
|
|||||||
<View padding="$4">
|
<View padding="$4">
|
||||||
<MWInput
|
<MWInput
|
||||||
placeholder={backendUrl}
|
placeholder={backendUrl}
|
||||||
type="search"
|
type="authentication"
|
||||||
value={backendUrl}
|
value={backendUrl}
|
||||||
onChangeText={setBackendUrl}
|
onChangeText={setBackendUrl}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<MWCard.Footer padded justifyContent="center">
|
<MWCard.Footer padded justifyContent="center">
|
||||||
<MWButton type="purple">
|
<Link
|
||||||
<Link
|
href={{
|
||||||
href={{
|
pathname: "/sync/trust/[url]",
|
||||||
pathname: "/sync/trust/[url]",
|
params: { url: backendUrl },
|
||||||
params: { url: backendUrl },
|
}}
|
||||||
}}
|
asChild
|
||||||
style={{ color: "white", fontWeight: "bold" }}
|
>
|
||||||
>
|
<MWButton type="purple" width="100%">
|
||||||
Get started
|
Get started
|
||||||
</Link>
|
</MWButton>
|
||||||
</MWButton>
|
</Link>
|
||||||
</MWCard.Footer>
|
</MWCard.Footer>
|
||||||
</MWCard>
|
</MWCard>
|
||||||
</ScreenLayout>
|
</ScreenLayout>
|
||||||
|
@@ -206,7 +206,7 @@ export default function SettingsScreen() {
|
|||||||
android: "android",
|
android: "android",
|
||||||
})}
|
})}
|
||||||
size={24}
|
size={24}
|
||||||
color={theme.buttonSecondaryText.val}
|
color={theme.silver300.val}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
iconAfter={
|
iconAfter={
|
||||||
@@ -229,7 +229,7 @@ export default function SettingsScreen() {
|
|||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name="broom"
|
name="broom"
|
||||||
size={24}
|
size={24}
|
||||||
color={theme.buttonSecondaryText.val}
|
color={theme.silver300.val}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onPress={() => clearCacheDirectory()}
|
onPress={() => clearCacheDirectory()}
|
||||||
@@ -304,7 +304,7 @@ export function UpdateSheet({
|
|||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name={Platform.select({ ios: "apple", android: "android" })}
|
name={Platform.select({ ios: "apple", android: "android" })}
|
||||||
size={24}
|
size={24}
|
||||||
color={theme.buttonSecondaryText.val}
|
color={theme.silver300.val}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onPress={() => WebBrowser.openBrowserAsync(downloadUrl)}
|
onPress={() => WebBrowser.openBrowserAsync(downloadUrl)}
|
||||||
|
79
apps/expo/src/app/sync/login.tsx
Normal file
79
apps/expo/src/app/sync/login.tsx
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import { Stack } from "expo-router";
|
||||||
|
import { H4, Label, Paragraph, Text, YStack } 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 Page() {
|
||||||
|
return (
|
||||||
|
<ScreenLayout
|
||||||
|
showHeader={false}
|
||||||
|
contentContainerStyle={{
|
||||||
|
flexGrow: 1,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack.Screen
|
||||||
|
options={{
|
||||||
|
title: "",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MWCard bordered padded>
|
||||||
|
<MWCard.Header>
|
||||||
|
<H4 fontWeight="$bold" textAlign="center">
|
||||||
|
Login to your account
|
||||||
|
</H4>
|
||||||
|
|
||||||
|
<Paragraph
|
||||||
|
color="$ash50"
|
||||||
|
textAlign="center"
|
||||||
|
fontWeight="$semibold"
|
||||||
|
paddingVertical="$4"
|
||||||
|
>
|
||||||
|
Please enter your passphrase to login to your account
|
||||||
|
</Paragraph>
|
||||||
|
</MWCard.Header>
|
||||||
|
|
||||||
|
<YStack paddingBottom="$5">
|
||||||
|
<YStack gap="$1">
|
||||||
|
<Label fontWeight="$bold">12-Word passphrase</Label>
|
||||||
|
<MWInput
|
||||||
|
type="authentication"
|
||||||
|
placeholder="Passphrase"
|
||||||
|
secureTextEntry
|
||||||
|
autoCorrect={false}
|
||||||
|
/>
|
||||||
|
</YStack>
|
||||||
|
<YStack gap="$1">
|
||||||
|
<Label fontWeight="$bold">Device name</Label>
|
||||||
|
<MWInput
|
||||||
|
type="authentication"
|
||||||
|
placeholder="Personal phone"
|
||||||
|
autoCorrect={false}
|
||||||
|
/>
|
||||||
|
</YStack>
|
||||||
|
</YStack>
|
||||||
|
|
||||||
|
<MWCard.Footer
|
||||||
|
padded
|
||||||
|
justifyContent="center"
|
||||||
|
flexDirection="column"
|
||||||
|
gap="$4"
|
||||||
|
>
|
||||||
|
<MWButton type="purple">Login</MWButton>
|
||||||
|
|
||||||
|
<Paragraph color="$ash50" textAlign="center" fontWeight="$semibold">
|
||||||
|
Don't have an account yet?{"\n"}
|
||||||
|
<Text color="$purple100" fontWeight="$bold">
|
||||||
|
Create an account.
|
||||||
|
</Text>
|
||||||
|
</Paragraph>
|
||||||
|
</MWCard.Footer>
|
||||||
|
</MWCard>
|
||||||
|
</ScreenLayout>
|
||||||
|
);
|
||||||
|
}
|
190
apps/expo/src/app/sync/register/account.tsx
Normal file
190
apps/expo/src/app/sync/register/account.tsx
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { Link, Stack } from "expo-router";
|
||||||
|
import { FontAwesome6, Ionicons } from "@expo/vector-icons";
|
||||||
|
import { Circle, H4, Label, Paragraph, View, XStack, YStack } from "tamagui";
|
||||||
|
import { LinearGradient } from "tamagui/linear-gradient";
|
||||||
|
|
||||||
|
import ScreenLayout from "~/components/layout/ScreenLayout";
|
||||||
|
import { MWButton } from "~/components/ui/Button";
|
||||||
|
import { MWCard } from "~/components/ui/Card";
|
||||||
|
import { MWInput } from "~/components/ui/Input";
|
||||||
|
|
||||||
|
const colors = ["#0A54FF", "#CF2E68", "#F9DD7F", "#7652DD", "#2ECFA8"] as const;
|
||||||
|
|
||||||
|
function ColorPicker(props: {
|
||||||
|
value: (typeof colors)[number];
|
||||||
|
onInput: (v: (typeof colors)[number]) => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<XStack gap="$2">
|
||||||
|
{colors.map((color) => {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
onPress={() => props.onInput(color)}
|
||||||
|
flexGrow={1}
|
||||||
|
height="$4"
|
||||||
|
borderRadius="$4"
|
||||||
|
justifyContent="center"
|
||||||
|
alignItems="center"
|
||||||
|
backgroundColor={color}
|
||||||
|
key={color}
|
||||||
|
>
|
||||||
|
{props.value === color ? (
|
||||||
|
<Ionicons name="checkmark-circle" size={24} color="white" />
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</XStack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const icons = [
|
||||||
|
"user-group",
|
||||||
|
"couch",
|
||||||
|
"mobile-screen",
|
||||||
|
"ticket",
|
||||||
|
"handcuffs",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
function UserIconPicker(props: {
|
||||||
|
value: (typeof icons)[number];
|
||||||
|
onInput: (v: (typeof icons)[number]) => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<XStack gap="$2">
|
||||||
|
{icons.map((icon) => {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
flexGrow={1}
|
||||||
|
height="$4"
|
||||||
|
borderRadius="$4"
|
||||||
|
justifyContent="center"
|
||||||
|
alignItems="center"
|
||||||
|
backgroundColor={props.value === icon ? "$purple400" : "$shade400"}
|
||||||
|
borderColor={props.value === icon ? "$purple200" : "$shade400"}
|
||||||
|
borderWidth={1}
|
||||||
|
key={icon}
|
||||||
|
onPress={() => props.onInput(icon)}
|
||||||
|
>
|
||||||
|
<FontAwesome6 name={icon} size={24} color="white" />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</XStack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AvatarProps {
|
||||||
|
colorA: string;
|
||||||
|
colorB: string;
|
||||||
|
icon: (typeof icons)[number];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Avatar(props: AvatarProps) {
|
||||||
|
return (
|
||||||
|
<Circle
|
||||||
|
backgroundColor={props.colorA}
|
||||||
|
height="$6"
|
||||||
|
width="$6"
|
||||||
|
justifyContent="center"
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<LinearGradient
|
||||||
|
colors={[props.colorA, props.colorB]}
|
||||||
|
start={{ x: 0, y: 0 }}
|
||||||
|
end={{ x: 1, y: 1 }}
|
||||||
|
borderRadius="$12"
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
justifyContent="center"
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<FontAwesome6 name={props.icon} size={24} color="white" />
|
||||||
|
</LinearGradient>
|
||||||
|
</Circle>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
const [color, setColor] = useState<(typeof colors)[number]>(colors[0]);
|
||||||
|
const [color2, setColor2] = useState<(typeof colors)[number]>(colors[0]);
|
||||||
|
const [icon, setIcon] = useState<(typeof icons)[number]>(icons[0]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScreenLayout
|
||||||
|
showHeader={false}
|
||||||
|
contentContainerStyle={{
|
||||||
|
flexGrow: 1,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack.Screen
|
||||||
|
options={{
|
||||||
|
title: "",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MWCard bordered padded>
|
||||||
|
<MWCard.Header>
|
||||||
|
<View alignItems="center" marginBottom="$3">
|
||||||
|
<Avatar colorA={color} colorB={color2} icon={icon} />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<H4 fontWeight="$bold" textAlign="center">
|
||||||
|
Account information
|
||||||
|
</H4>
|
||||||
|
|
||||||
|
<Paragraph
|
||||||
|
color="$shade200"
|
||||||
|
textAlign="center"
|
||||||
|
fontWeight="$normal"
|
||||||
|
paddingTop="$4"
|
||||||
|
>
|
||||||
|
Enter a name for your device and pick colours and a user icon of
|
||||||
|
your choosing
|
||||||
|
</Paragraph>
|
||||||
|
</MWCard.Header>
|
||||||
|
|
||||||
|
<YStack paddingBottom="$5">
|
||||||
|
<YStack gap="$1">
|
||||||
|
<Label fontWeight="$bold">Device name</Label>
|
||||||
|
<MWInput
|
||||||
|
type="authentication"
|
||||||
|
placeholder="Passphrase"
|
||||||
|
secureTextEntry
|
||||||
|
autoCorrect={false}
|
||||||
|
/>
|
||||||
|
</YStack>
|
||||||
|
<YStack gap="$1">
|
||||||
|
<Label fontWeight="$bold">Profile color one</Label>
|
||||||
|
<ColorPicker value={color} onInput={(color) => setColor(color)} />
|
||||||
|
</YStack>
|
||||||
|
<YStack gap="$1">
|
||||||
|
<Label fontWeight="$bold">Profile color two</Label>
|
||||||
|
<ColorPicker value={color2} onInput={(color) => setColor2(color)} />
|
||||||
|
</YStack>
|
||||||
|
<YStack gap="$1">
|
||||||
|
<Label fontWeight="$bold">User icon</Label>
|
||||||
|
<UserIconPicker value={icon} onInput={(icon) => setIcon(icon)} />
|
||||||
|
</YStack>
|
||||||
|
</YStack>
|
||||||
|
|
||||||
|
<MWCard.Footer justifyContent="center" flexDirection="column" gap="$4">
|
||||||
|
<Link
|
||||||
|
href={{
|
||||||
|
pathname: "/sync/register/confirm",
|
||||||
|
}}
|
||||||
|
replace
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<MWButton type="purple" width="100%">
|
||||||
|
Next
|
||||||
|
</MWButton>
|
||||||
|
</Link>
|
||||||
|
</MWCard.Footer>
|
||||||
|
</MWCard>
|
||||||
|
</ScreenLayout>
|
||||||
|
);
|
||||||
|
}
|
68
apps/expo/src/app/sync/register/confirm.tsx
Normal file
68
apps/expo/src/app/sync/register/confirm.tsx
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { Link, Stack } from "expo-router";
|
||||||
|
import { H4, Label, Paragraph, YStack } 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 Page() {
|
||||||
|
return (
|
||||||
|
<ScreenLayout
|
||||||
|
showHeader={false}
|
||||||
|
contentContainerStyle={{
|
||||||
|
flexGrow: 1,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack.Screen
|
||||||
|
options={{
|
||||||
|
title: "",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MWCard bordered padded>
|
||||||
|
<MWCard.Header>
|
||||||
|
<H4 fontWeight="$bold" textAlign="center">
|
||||||
|
Confirm your passphrase
|
||||||
|
</H4>
|
||||||
|
|
||||||
|
<Paragraph
|
||||||
|
color="$shade200"
|
||||||
|
textAlign="center"
|
||||||
|
fontWeight="$normal"
|
||||||
|
paddingTop="$4"
|
||||||
|
>
|
||||||
|
Please enter your passphrase from earlier to confirm you have saved
|
||||||
|
it and to create your account
|
||||||
|
</Paragraph>
|
||||||
|
</MWCard.Header>
|
||||||
|
|
||||||
|
<YStack paddingBottom="$5">
|
||||||
|
<YStack gap="$1">
|
||||||
|
<Label fontWeight="$bold">12-Word passphrase</Label>
|
||||||
|
<MWInput
|
||||||
|
type="authentication"
|
||||||
|
placeholder="Passphrase"
|
||||||
|
secureTextEntry
|
||||||
|
autoCorrect={false}
|
||||||
|
/>
|
||||||
|
</YStack>
|
||||||
|
</YStack>
|
||||||
|
|
||||||
|
<MWCard.Footer justifyContent="center" flexDirection="column" gap="$4">
|
||||||
|
<Link
|
||||||
|
href={{
|
||||||
|
pathname: "/(tabs)/movie-web",
|
||||||
|
}}
|
||||||
|
replace
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<MWButton type="purple">Create account</MWButton>
|
||||||
|
</Link>
|
||||||
|
</MWCard.Footer>
|
||||||
|
</MWCard>
|
||||||
|
</ScreenLayout>
|
||||||
|
);
|
||||||
|
}
|
132
apps/expo/src/app/sync/register/index.tsx
Normal file
132
apps/expo/src/app/sync/register/index.tsx
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import { TouchableOpacity } from "react-native-gesture-handler";
|
||||||
|
import * as Clipboard from "expo-clipboard";
|
||||||
|
import { Link, Stack } from "expo-router";
|
||||||
|
import { Feather } from "@expo/vector-icons";
|
||||||
|
import { H4, Paragraph, Text, useTheme, View, XStack, YStack } from "tamagui";
|
||||||
|
|
||||||
|
import { genMnemonic } from "@movie-web/api";
|
||||||
|
|
||||||
|
import ScreenLayout from "~/components/layout/ScreenLayout";
|
||||||
|
import { MWButton } from "~/components/ui/Button";
|
||||||
|
import { MWCard } from "~/components/ui/Card";
|
||||||
|
|
||||||
|
function PassphraseWord({ word }: { word: string }) {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
width="$10"
|
||||||
|
borderRadius="$4"
|
||||||
|
paddingHorizontal="$4"
|
||||||
|
paddingVertical="$3"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
backgroundColor="$shade400"
|
||||||
|
>
|
||||||
|
<Text fontWeight="$bold">{word}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
const theme = useTheme();
|
||||||
|
const words = genMnemonic().split(" ");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScreenLayout
|
||||||
|
showHeader={false}
|
||||||
|
contentContainerStyle={{
|
||||||
|
flexGrow: 1,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack.Screen
|
||||||
|
options={{
|
||||||
|
title: "",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MWCard bordered padded>
|
||||||
|
<MWCard.Header>
|
||||||
|
<H4 fontWeight="$bold" textAlign="center">
|
||||||
|
Your passphrase
|
||||||
|
</H4>
|
||||||
|
|
||||||
|
<Paragraph
|
||||||
|
color="$shade200"
|
||||||
|
textAlign="center"
|
||||||
|
fontWeight="$normal"
|
||||||
|
paddingTop="$4"
|
||||||
|
>
|
||||||
|
Your passphrase acts as your username and password. Make sure to
|
||||||
|
keep it safe as you will need to enter it to login to your account
|
||||||
|
</Paragraph>
|
||||||
|
</MWCard.Header>
|
||||||
|
|
||||||
|
<YStack
|
||||||
|
borderRadius="$4"
|
||||||
|
borderColor="$shade200"
|
||||||
|
borderWidth="$0.5"
|
||||||
|
marginBottom="$4"
|
||||||
|
>
|
||||||
|
<XStack
|
||||||
|
gap="$1"
|
||||||
|
borderBottomWidth="$0.5"
|
||||||
|
borderColor="$shade200"
|
||||||
|
paddingVertical="$2"
|
||||||
|
paddingHorizontal="$4"
|
||||||
|
>
|
||||||
|
<Text fontWeight="$bold" flexGrow={1}>
|
||||||
|
Passphrase
|
||||||
|
</Text>
|
||||||
|
<TouchableOpacity
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
gap: 8,
|
||||||
|
}}
|
||||||
|
onPress={async () => {
|
||||||
|
await Clipboard.setStringAsync(words.join(""));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Feather name="copy" size={18} color={theme.shade200.val} />
|
||||||
|
<Text color="$shade200" fontWeight="$bold">
|
||||||
|
Copy
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</XStack>
|
||||||
|
<View
|
||||||
|
flexWrap="wrap"
|
||||||
|
flexDirection="row"
|
||||||
|
gap="$4"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
padding="$3"
|
||||||
|
>
|
||||||
|
{words.map((word, index) => (
|
||||||
|
<PassphraseWord key={index} word={word} />
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</YStack>
|
||||||
|
|
||||||
|
<MWCard.Footer justifyContent="center" flexDirection="column" gap="$4">
|
||||||
|
<Link
|
||||||
|
href={{
|
||||||
|
pathname: "/sync/register/account",
|
||||||
|
}}
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<MWButton type="purple">I have saved my passphrase</MWButton>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Paragraph color="$ash50" textAlign="center" fontWeight="$semibold">
|
||||||
|
Already have an account?{"\n"}
|
||||||
|
<Text color="$purple100" fontWeight="$bold">
|
||||||
|
<Link href="/sync/login">Login here</Link>
|
||||||
|
</Text>
|
||||||
|
</Paragraph>
|
||||||
|
</MWCard.Footer>
|
||||||
|
</MWCard>
|
||||||
|
</ScreenLayout>
|
||||||
|
);
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
import { Stack, useLocalSearchParams } from "expo-router";
|
import { Link, Stack, useLocalSearchParams } from "expo-router";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { H4, Paragraph, Text, View } from "tamagui";
|
import { H4, Paragraph, Text, View } from "tamagui";
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ export default function Page() {
|
|||||||
title: "",
|
title: "",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<MWCard bordered>
|
<MWCard bordered padded>
|
||||||
<MWCard.Header padded>
|
<MWCard.Header padded>
|
||||||
<H4 fontWeight="$bold" textAlign="center">
|
<H4 fontWeight="$bold" textAlign="center">
|
||||||
Do you trust this server?
|
Do you trust this server?
|
||||||
@@ -90,15 +90,23 @@ export default function Page() {
|
|||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
gap="$4"
|
gap="$4"
|
||||||
>
|
>
|
||||||
<MWButton type="purple">I trust this server</MWButton>
|
<Link
|
||||||
<MWButton type="cancel">Go back</MWButton>
|
href={{
|
||||||
|
pathname: "/sync/register",
|
||||||
<Paragraph color="$ash50" textAlign="center" fontWeight="$semibold">
|
}}
|
||||||
Already have an account?{" "}
|
asChild
|
||||||
<Text color="$purple100" fontWeight="$bold">
|
>
|
||||||
Login here
|
<MWButton type="purple">I trust this server</MWButton>
|
||||||
</Text>
|
</Link>
|
||||||
</Paragraph>
|
<Link
|
||||||
|
href={{
|
||||||
|
pathname: "/(tabs)/",
|
||||||
|
}}
|
||||||
|
replace
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<MWButton type="cancel">Go back</MWButton>
|
||||||
|
</Link>
|
||||||
</MWCard.Footer>
|
</MWCard.Footer>
|
||||||
</MWCard>
|
</MWCard>
|
||||||
</ScreenLayout>
|
</ScreenLayout>
|
||||||
|
@@ -58,7 +58,7 @@ export const AudioTrackSelector = () => {
|
|||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name="volume-high"
|
name="volume-high"
|
||||||
size={24}
|
size={24}
|
||||||
color={theme.buttonSecondaryText.val}
|
color={theme.silver300.val}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onPress={() => setOpen(true)}
|
onPress={() => setOpen(true)}
|
||||||
|
@@ -65,7 +65,7 @@ export const CaptionsSelector = () => {
|
|||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name="subtitles"
|
name="subtitles"
|
||||||
size={24}
|
size={24}
|
||||||
color={theme.buttonSecondaryText.val}
|
color={theme.silver300.val}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onPress={() => setOpen(true)}
|
onPress={() => setOpen(true)}
|
||||||
|
@@ -38,7 +38,7 @@ export const DownloadButton = () => {
|
|||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name="download"
|
name="download"
|
||||||
size={24}
|
size={24}
|
||||||
color={theme.buttonSecondaryText.val}
|
color={theme.silver300.val}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onPress={() =>
|
onPress={() =>
|
||||||
|
@@ -47,7 +47,7 @@ const EpisodeSelector = ({
|
|||||||
<Ionicons
|
<Ionicons
|
||||||
name="arrow-back"
|
name="arrow-back"
|
||||||
size={24}
|
size={24}
|
||||||
color={theme.buttonSecondaryText.val}
|
color={theme.silver300.val}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
setSelectedSeason(null);
|
setSelectedSeason(null);
|
||||||
props.onOpenChange?.(false);
|
props.onOpenChange?.(false);
|
||||||
@@ -119,7 +119,7 @@ export const SeasonSelector = () => {
|
|||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name="audio-video"
|
name="audio-video"
|
||||||
size={24}
|
size={24}
|
||||||
color={theme.buttonSecondaryText.val}
|
color={theme.silver300.val}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onPress={() => setOpen(true)}
|
onPress={() => setOpen(true)}
|
||||||
|
@@ -23,7 +23,7 @@ export const SettingsSelector = () => {
|
|||||||
<MaterialIcons
|
<MaterialIcons
|
||||||
name="display-settings"
|
name="display-settings"
|
||||||
size={24}
|
size={24}
|
||||||
color={theme.buttonSecondaryText.val}
|
color={theme.silver300.val}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onPress={() => setOpen(true)}
|
onPress={() => setOpen(true)}
|
||||||
|
@@ -102,7 +102,7 @@ const EmbedsPart = ({
|
|||||||
<Ionicons
|
<Ionicons
|
||||||
name="arrow-back"
|
name="arrow-back"
|
||||||
size={24}
|
size={24}
|
||||||
color={theme.buttonSecondaryText.val}
|
color={theme.silver300.val}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
props.onOpenChange?.(false);
|
props.onOpenChange?.(false);
|
||||||
}}
|
}}
|
||||||
@@ -160,7 +160,7 @@ export const SourceSelector = () => {
|
|||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name="video"
|
name="video"
|
||||||
size={24}
|
size={24}
|
||||||
color={theme.buttonSecondaryText.val}
|
color={theme.silver300.val}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onPress={() => setOpen(true)}
|
onPress={() => setOpen(true)}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Card, styled, withStaticProperties } from "tamagui";
|
import { Card, styled, withStaticProperties } from "tamagui";
|
||||||
|
|
||||||
export const MWCardFrame = styled(Card, {
|
export const MWCardFrame = styled(Card, {
|
||||||
backgroundColor: "$shade400",
|
backgroundColor: "$shade600",
|
||||||
borderColor: "$shade400",
|
borderColor: "$shade400",
|
||||||
|
|
||||||
variants: {
|
variants: {
|
||||||
|
@@ -6,21 +6,37 @@ export const MWInput = styled(Input, {
|
|||||||
variants: {
|
variants: {
|
||||||
type: {
|
type: {
|
||||||
default: {
|
default: {
|
||||||
backgroundColor: "$inputBackground",
|
backgroundColor: "$ash600",
|
||||||
color: "$inputText",
|
color: "$ash100",
|
||||||
placeholderTextColor: "$placeHolderText",
|
placeholderTextColor: "$ash200",
|
||||||
borderColor: "$inputBorder",
|
borderColor: "$ash500",
|
||||||
outlineStyle: "none",
|
outlineStyle: "none",
|
||||||
|
focusStyle: {
|
||||||
|
borderColor: "$ash300",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
backgroundColor: "$searchBackground",
|
backgroundColor: "$shade500",
|
||||||
|
color: "$shade100",
|
||||||
borderColor: "$colorTransparent",
|
borderColor: "$colorTransparent",
|
||||||
placeholderTextColor: "$searchPlaceholder",
|
placeholderTextColor: "$shade100",
|
||||||
outlineStyle: "none",
|
outlineStyle: "none",
|
||||||
focusStyle: {
|
focusStyle: {
|
||||||
borderColor: "$colorTransparent",
|
borderColor: "$colorTransparent",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
authentication: {
|
||||||
|
backgroundColor: "$shade500",
|
||||||
|
color: "$shade100",
|
||||||
|
placeholderTextColor: "$shade400",
|
||||||
|
outlineStyle: "none",
|
||||||
|
focusStyle: {
|
||||||
|
borderColor: "$shade300",
|
||||||
|
},
|
||||||
|
pressStyle: {
|
||||||
|
backgroundColor: "$shade500",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@@ -71,7 +71,7 @@ export function useAuth() {
|
|||||||
backendUrl,
|
backendUrl,
|
||||||
publicKeyBase64Url,
|
publicKeyBase64Url,
|
||||||
);
|
);
|
||||||
const signature = await signChallenge(keys, challenge);
|
const signature = signChallenge(keys, challenge);
|
||||||
const loginResult = await loginAccount(backendUrl, {
|
const loginResult = await loginAccount(backendUrl, {
|
||||||
challenge: {
|
challenge: {
|
||||||
code: challenge,
|
code: challenge,
|
||||||
@@ -110,7 +110,7 @@ export function useAuth() {
|
|||||||
registerData.recaptchaToken,
|
registerData.recaptchaToken,
|
||||||
);
|
);
|
||||||
const keys = await keysFromMnemonic(registerData.mnemonic);
|
const keys = await keysFromMnemonic(registerData.mnemonic);
|
||||||
const signature = await signChallenge(keys, challenge);
|
const signature = signChallenge(keys, challenge);
|
||||||
const registerResult = await registerAccount(backendUrl, {
|
const registerResult = await registerAccount(backendUrl, {
|
||||||
challenge: {
|
challenge: {
|
||||||
code: challenge,
|
code: challenge,
|
||||||
|
@@ -57,17 +57,6 @@ const createThemeConfig = (tokens: Tokens) => ({
|
|||||||
|
|
||||||
loadingIndicator: tokens.purple.c200,
|
loadingIndicator: tokens.purple.c200,
|
||||||
|
|
||||||
buttonSecondaryBackground: tokens.ash.c700,
|
|
||||||
buttonSecondaryText: tokens.semantic.silver.c300,
|
|
||||||
buttonSecondaryBackgroundHover: tokens.ash.c700,
|
|
||||||
buttonPrimaryBackground: tokens.white,
|
|
||||||
buttonPrimaryText: tokens.black,
|
|
||||||
buttonPrimaryBackgroundHover: tokens.semantic.silver.c100,
|
|
||||||
buttonPurpleBackground: tokens.purple.c500,
|
|
||||||
buttonPurpleBackgroundHover: tokens.purple.c400,
|
|
||||||
buttonCancelBackground: tokens.ash.c500,
|
|
||||||
buttonCancelBackgroundHover: tokens.ash.c300,
|
|
||||||
|
|
||||||
switchActiveTrackColor: tokens.purple.c300,
|
switchActiveTrackColor: tokens.purple.c300,
|
||||||
switchInactiveTrackColor: tokens.ash.c500,
|
switchInactiveTrackColor: tokens.ash.c500,
|
||||||
switchThumbColor: tokens.white,
|
switchThumbColor: tokens.white,
|
||||||
|
11
pnpm-lock.yaml
generated
11
pnpm-lock.yaml
generated
@@ -95,6 +95,9 @@ importers:
|
|||||||
expo-build-properties:
|
expo-build-properties:
|
||||||
specifier: ~0.11.1
|
specifier: ~0.11.1
|
||||||
version: 0.11.1(expo@50.0.14)
|
version: 0.11.1(expo@50.0.14)
|
||||||
|
expo-clipboard:
|
||||||
|
specifier: ^5.0.1
|
||||||
|
version: 5.0.1(expo@50.0.14)
|
||||||
expo-constants:
|
expo-constants:
|
||||||
specifier: ~15.4.5
|
specifier: ~15.4.5
|
||||||
version: 15.4.5(expo@50.0.14)
|
version: 15.4.5(expo@50.0.14)
|
||||||
@@ -7860,6 +7863,14 @@ packages:
|
|||||||
semver: 7.5.4
|
semver: 7.5.4
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/expo-clipboard@5.0.1(expo@50.0.14):
|
||||||
|
resolution: {integrity: sha512-JH853QJPr5W3h87If3aDTnMK+ESSIrwzU2TdfZrqZttVDY2pMIf/w37mVHHNYodXM4ATHXadtOkjKbAa0DWwUg==}
|
||||||
|
peerDependencies:
|
||||||
|
expo: '*'
|
||||||
|
dependencies:
|
||||||
|
expo: 50.0.14(@babel/core@7.23.9)(@react-native/babel-preset@0.73.21)
|
||||||
|
dev: false
|
||||||
|
|
||||||
/expo-constants@15.4.5(expo@50.0.14):
|
/expo-constants@15.4.5(expo@50.0.14):
|
||||||
resolution: {integrity: sha512-1pVVjwk733hbbIjtQcvUFCme540v4gFemdNlaxM2UXKbfRCOh2hzgKN5joHMOysoXQe736TTUrRj7UaZI5Yyhg==}
|
resolution: {integrity: sha512-1pVVjwk733hbbIjtQcvUFCme540v4gFemdNlaxM2UXKbfRCOh2hzgKN5joHMOysoXQe736TTUrRj7UaZI5Yyhg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
Reference in New Issue
Block a user