+2
-1
app.json
+2
-1
app.json
-35
app/(tabs)/_layout.tsx
-35
app/(tabs)/_layout.tsx
···
1
-
import { Tabs } from 'expo-router';
2
-
import React from 'react';
3
-
4
-
import { HapticTab } from '@/components/haptic-tab';
5
-
import { IconSymbol } from '@/components/ui/icon-symbol';
6
-
import { Colors } from '@/constants/theme';
7
-
import { useColorScheme } from '@/hooks/use-color-scheme';
8
-
9
-
export default function TabLayout() {
10
-
const colorScheme = useColorScheme();
11
-
12
-
return (
13
-
<Tabs
14
-
screenOptions={{
15
-
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
16
-
headerShown: false,
17
-
tabBarButton: HapticTab,
18
-
}}>
19
-
<Tabs.Screen
20
-
name="index"
21
-
options={{
22
-
title: 'Home',
23
-
tabBarIcon: ({ color }) => <IconSymbol size={28} name="house.fill" color={color} />,
24
-
}}
25
-
/>
26
-
<Tabs.Screen
27
-
name="explore"
28
-
options={{
29
-
title: 'Explore',
30
-
tabBarIcon: ({ color }) => <IconSymbol size={28} name="paperplane.fill" color={color} />,
31
-
}}
32
-
/>
33
-
</Tabs>
34
-
);
35
-
}
-112
app/(tabs)/explore.tsx
-112
app/(tabs)/explore.tsx
···
1
-
import { Image } from 'expo-image';
2
-
import { Platform, StyleSheet } from 'react-native';
3
-
4
-
import { Collapsible } from '@/components/ui/collapsible';
5
-
import { ExternalLink } from '@/components/external-link';
6
-
import ParallaxScrollView from '@/components/parallax-scroll-view';
7
-
import { ThemedText } from '@/components/themed-text';
8
-
import { ThemedView } from '@/components/themed-view';
9
-
import { IconSymbol } from '@/components/ui/icon-symbol';
10
-
import { Fonts } from '@/constants/theme';
11
-
12
-
export default function TabTwoScreen() {
13
-
return (
14
-
<ParallaxScrollView
15
-
headerBackgroundColor={{ light: '#D0D0D0', dark: '#353636' }}
16
-
headerImage={
17
-
<IconSymbol
18
-
size={310}
19
-
color="#808080"
20
-
name="chevron.left.forwardslash.chevron.right"
21
-
style={styles.headerImage}
22
-
/>
23
-
}>
24
-
<ThemedView style={styles.titleContainer}>
25
-
<ThemedText
26
-
type="title"
27
-
style={{
28
-
fontFamily: Fonts.rounded,
29
-
}}>
30
-
Explore
31
-
</ThemedText>
32
-
</ThemedView>
33
-
<ThemedText>This app includes example code to help you get started.</ThemedText>
34
-
<Collapsible title="File-based routing">
35
-
<ThemedText>
36
-
This app has two screens:{' '}
37
-
<ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> and{' '}
38
-
<ThemedText type="defaultSemiBold">app/(tabs)/explore.tsx</ThemedText>
39
-
</ThemedText>
40
-
<ThemedText>
41
-
The layout file in <ThemedText type="defaultSemiBold">app/(tabs)/_layout.tsx</ThemedText>{' '}
42
-
sets up the tab navigator.
43
-
</ThemedText>
44
-
<ExternalLink href="https://docs.expo.dev/router/introduction">
45
-
<ThemedText type="link">Learn more</ThemedText>
46
-
</ExternalLink>
47
-
</Collapsible>
48
-
<Collapsible title="Android, iOS, and web support">
49
-
<ThemedText>
50
-
You can open this project on Android, iOS, and the web. To open the web version, press{' '}
51
-
<ThemedText type="defaultSemiBold">w</ThemedText> in the terminal running this project.
52
-
</ThemedText>
53
-
</Collapsible>
54
-
<Collapsible title="Images">
55
-
<ThemedText>
56
-
For static images, you can use the <ThemedText type="defaultSemiBold">@2x</ThemedText> and{' '}
57
-
<ThemedText type="defaultSemiBold">@3x</ThemedText> suffixes to provide files for
58
-
different screen densities
59
-
</ThemedText>
60
-
<Image
61
-
source={require('@/assets/images/react-logo.png')}
62
-
style={{ width: 100, height: 100, alignSelf: 'center' }}
63
-
/>
64
-
<ExternalLink href="https://reactnative.dev/docs/images">
65
-
<ThemedText type="link">Learn more</ThemedText>
66
-
</ExternalLink>
67
-
</Collapsible>
68
-
<Collapsible title="Light and dark mode components">
69
-
<ThemedText>
70
-
This template has light and dark mode support. The{' '}
71
-
<ThemedText type="defaultSemiBold">useColorScheme()</ThemedText> hook lets you inspect
72
-
what the user's current color scheme is, and so you can adjust UI colors accordingly.
73
-
</ThemedText>
74
-
<ExternalLink href="https://docs.expo.dev/develop/user-interface/color-themes/">
75
-
<ThemedText type="link">Learn more</ThemedText>
76
-
</ExternalLink>
77
-
</Collapsible>
78
-
<Collapsible title="Animations">
79
-
<ThemedText>
80
-
This template includes an example of an animated component. The{' '}
81
-
<ThemedText type="defaultSemiBold">components/HelloWave.tsx</ThemedText> component uses
82
-
the powerful{' '}
83
-
<ThemedText type="defaultSemiBold" style={{ fontFamily: Fonts.mono }}>
84
-
react-native-reanimated
85
-
</ThemedText>{' '}
86
-
library to create a waving hand animation.
87
-
</ThemedText>
88
-
{Platform.select({
89
-
ios: (
90
-
<ThemedText>
91
-
The <ThemedText type="defaultSemiBold">components/ParallaxScrollView.tsx</ThemedText>{' '}
92
-
component provides a parallax effect for the header image.
93
-
</ThemedText>
94
-
),
95
-
})}
96
-
</Collapsible>
97
-
</ParallaxScrollView>
98
-
);
99
-
}
100
-
101
-
const styles = StyleSheet.create({
102
-
headerImage: {
103
-
color: '#808080',
104
-
bottom: -90,
105
-
left: -35,
106
-
position: 'absolute',
107
-
},
108
-
titleContainer: {
109
-
flexDirection: 'row',
110
-
gap: 8,
111
-
},
112
-
});
-98
app/(tabs)/index.tsx
-98
app/(tabs)/index.tsx
···
1
-
import { Image } from 'expo-image';
2
-
import { Platform, StyleSheet } from 'react-native';
3
-
4
-
import { HelloWave } from '@/components/hello-wave';
5
-
import ParallaxScrollView from '@/components/parallax-scroll-view';
6
-
import { ThemedText } from '@/components/themed-text';
7
-
import { ThemedView } from '@/components/themed-view';
8
-
import { Link } from 'expo-router';
9
-
10
-
export default function HomeScreen() {
11
-
return (
12
-
<ParallaxScrollView
13
-
headerBackgroundColor={{ light: '#A1CEDC', dark: '#1D3D47' }}
14
-
headerImage={
15
-
<Image
16
-
source={require('@/assets/images/partial-react-logo.png')}
17
-
style={styles.reactLogo}
18
-
/>
19
-
}>
20
-
<ThemedView style={styles.titleContainer}>
21
-
<ThemedText type="title">Welcome!</ThemedText>
22
-
<HelloWave />
23
-
</ThemedView>
24
-
<ThemedView style={styles.stepContainer}>
25
-
<ThemedText type="subtitle">Step 1: Try it</ThemedText>
26
-
<ThemedText>
27
-
Edit <ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> to see changes.
28
-
Press{' '}
29
-
<ThemedText type="defaultSemiBold">
30
-
{Platform.select({
31
-
ios: 'cmd + d',
32
-
android: 'cmd + m',
33
-
web: 'F12',
34
-
})}
35
-
</ThemedText>{' '}
36
-
to open developer tools.
37
-
</ThemedText>
38
-
</ThemedView>
39
-
<ThemedView style={styles.stepContainer}>
40
-
<Link href="/modal">
41
-
<Link.Trigger>
42
-
<ThemedText type="subtitle">Step 2: Explore</ThemedText>
43
-
</Link.Trigger>
44
-
<Link.Preview />
45
-
<Link.Menu>
46
-
<Link.MenuAction title="Action" icon="cube" onPress={() => alert('Action pressed')} />
47
-
<Link.MenuAction
48
-
title="Share"
49
-
icon="square.and.arrow.up"
50
-
onPress={() => alert('Share pressed')}
51
-
/>
52
-
<Link.Menu title="More" icon="ellipsis">
53
-
<Link.MenuAction
54
-
title="Delete"
55
-
icon="trash"
56
-
destructive
57
-
onPress={() => alert('Delete pressed')}
58
-
/>
59
-
</Link.Menu>
60
-
</Link.Menu>
61
-
</Link>
62
-
63
-
<ThemedText>
64
-
{`Tap the Explore tab to learn more about what's included in this starter app.`}
65
-
</ThemedText>
66
-
</ThemedView>
67
-
<ThemedView style={styles.stepContainer}>
68
-
<ThemedText type="subtitle">Step 3: Get a fresh start</ThemedText>
69
-
<ThemedText>
70
-
{`When you're ready, run `}
71
-
<ThemedText type="defaultSemiBold">npm run reset-project</ThemedText> to get a fresh{' '}
72
-
<ThemedText type="defaultSemiBold">app</ThemedText> directory. This will move the current{' '}
73
-
<ThemedText type="defaultSemiBold">app</ThemedText> to{' '}
74
-
<ThemedText type="defaultSemiBold">app-example</ThemedText>.
75
-
</ThemedText>
76
-
</ThemedView>
77
-
</ParallaxScrollView>
78
-
);
79
-
}
80
-
81
-
const styles = StyleSheet.create({
82
-
titleContainer: {
83
-
flexDirection: 'row',
84
-
alignItems: 'center',
85
-
gap: 8,
86
-
},
87
-
stepContainer: {
88
-
gap: 8,
89
-
marginBottom: 8,
90
-
},
91
-
reactLogo: {
92
-
height: 178,
93
-
width: 290,
94
-
bottom: 0,
95
-
left: 0,
96
-
position: 'absolute',
97
-
},
98
-
});
-24
app/_layout.tsx
-24
app/_layout.tsx
···
1
-
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
2
-
import { Stack } from 'expo-router';
3
-
import { StatusBar } from 'expo-status-bar';
4
-
import 'react-native-reanimated';
5
-
6
-
import { useColorScheme } from '@/hooks/use-color-scheme';
7
-
8
-
export const unstable_settings = {
9
-
anchor: '(tabs)',
10
-
};
11
-
12
-
export default function RootLayout() {
13
-
const colorScheme = useColorScheme();
14
-
15
-
return (
16
-
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
17
-
<Stack>
18
-
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
19
-
<Stack.Screen name="modal" options={{ presentation: 'modal', title: 'Modal' }} />
20
-
</Stack>
21
-
<StatusBar style="auto" />
22
-
</ThemeProvider>
23
-
);
24
-
}
-29
app/modal.tsx
-29
app/modal.tsx
···
1
-
import { Link } from 'expo-router';
2
-
import { StyleSheet } from 'react-native';
3
-
4
-
import { ThemedText } from '@/components/themed-text';
5
-
import { ThemedView } from '@/components/themed-view';
6
-
7
-
export default function ModalScreen() {
8
-
return (
9
-
<ThemedView style={styles.container}>
10
-
<ThemedText type="title">This is a modal</ThemedText>
11
-
<Link href="/" dismissTo style={styles.link}>
12
-
<ThemedText type="link">Go to home screen</ThemedText>
13
-
</Link>
14
-
</ThemedView>
15
-
);
16
-
}
17
-
18
-
const styles = StyleSheet.create({
19
-
container: {
20
-
flex: 1,
21
-
alignItems: 'center',
22
-
justifyContent: 'center',
23
-
padding: 20,
24
-
},
25
-
link: {
26
-
marginTop: 15,
27
-
paddingVertical: 15,
28
-
},
29
-
});
+122
-5
bun.lock
+122
-5
bun.lock
···
5
5
"": {
6
6
"name": "dusksky",
7
7
"dependencies": {
8
+
"@atcute/bluesky": "^3.2.14",
9
+
"@atcute/bluesky-richtext-segmenter": "^2.0.4",
10
+
"@atcute/client": "^4.1.1",
8
11
"@expo/vector-icons": "^15.0.3",
12
+
"@formatjs/intl-segmenter": "^12.0.5",
13
+
"@pchmn/expo-material3-theme": "^1.3.2",
9
14
"@react-navigation/bottom-tabs": "^7.4.0",
10
15
"@react-navigation/elements": "^2.6.3",
11
16
"@react-navigation/native": "^7.1.8",
17
+
"@shopify/flash-list": "2.0.2",
18
+
"@tanstack/react-query": "^5.90.12",
12
19
"expo": "~54.0.30",
13
20
"expo-constants": "~18.0.12",
14
21
"expo-font": "~14.0.10",
···
20
27
"expo-status-bar": "~3.0.9",
21
28
"expo-symbols": "~1.0.8",
22
29
"expo-system-ui": "~6.0.9",
30
+
"expo-video": "~3.0.15",
23
31
"expo-web-browser": "~15.0.10",
24
32
"react": "19.1.0",
25
33
"react-dom": "19.1.0",
···
35
43
"@types/react": "~19.1.0",
36
44
"eslint": "^9.25.0",
37
45
"eslint-config-expo": "~10.0.0",
46
+
"expo-atlas": "^0.4.0",
38
47
"typescript": "~5.9.2",
39
48
},
40
49
},
41
50
},
42
51
"packages": {
43
52
"@0no-co/graphql.web": ["@0no-co/graphql.web@1.2.0", "", { "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0" }, "optionalPeers": ["graphql"] }, "sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw=="],
53
+
54
+
"@atcute/atproto": ["@atcute/atproto@3.1.9", "", { "dependencies": { "@atcute/lexicons": "^1.2.2" } }, "sha512-DyWwHCTdR4hY2BPNbLXgVmm7lI+fceOwWbE4LXbGvbvVtSn+ejSVFaAv01Ra3kWDha0whsOmbJL8JP0QPpf1+w=="],
55
+
56
+
"@atcute/bluesky": ["@atcute/bluesky@3.2.14", "", { "dependencies": { "@atcute/atproto": "^3.1.9", "@atcute/lexicons": "^1.2.5" } }, "sha512-XlVuF55AYIyplmKvlGLlj+cUvk9ggxNRPczkTPIY991xJ4qDxDHpBJ39ekAV4dWcuBoRo2o9JynzpafPu2ljDA=="],
57
+
58
+
"@atcute/bluesky-richtext-segmenter": ["@atcute/bluesky-richtext-segmenter@2.0.4", "", { "dependencies": { "@atcute/bluesky": "^3.2.5", "@atcute/lexicons": "^1.2.2" } }, "sha512-6m5QEAv4lU3qTy5MeJXJRRG33acipYJnMW1T7W/KrMyThGhQ7jSTTh8Z48quElgivgX7MDj6o/ow1oLUsjsCKw=="],
59
+
60
+
"@atcute/client": ["@atcute/client@4.1.1", "", { "dependencies": { "@atcute/identity": "^1.1.3", "@atcute/lexicons": "^1.2.5" } }, "sha512-FROCbTTCeL5u4tO/n72jDEKyKqjdlXMB56Ehve3W/gnnLGCYWvN42sS7tvL1Mgu6sbO3yZwsXKDrmM2No4XpjA=="],
61
+
62
+
"@atcute/identity": ["@atcute/identity@1.1.3", "", { "dependencies": { "@atcute/lexicons": "^1.2.4", "@badrap/valita": "^0.4.6" } }, "sha512-oIqPoI8TwWeQxvcLmFEZLdN2XdWcaLVtlm8pNk0E72As9HNzzD9pwKPrLr3rmTLRIoULPPFmq9iFNsTeCIU9ng=="],
63
+
64
+
"@atcute/lexicons": ["@atcute/lexicons@1.2.5", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "esm-env": "^1.2.2" } }, "sha512-9yO9WdgxW8jZ7SbzUycH710z+JmsQ9W9n5S6i6eghYju32kkluFmgBeS47r8e8p2+Dv4DemS7o/3SUGsX9FR5Q=="],
44
65
45
66
"@babel/code-frame": ["@babel/code-frame@7.10.4", "", { "dependencies": { "@babel/highlight": "^7.10.4" } }, "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg=="],
46
67
···
230
251
231
252
"@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
232
253
254
+
"@badrap/valita": ["@badrap/valita@0.4.6", "", {}, "sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg=="],
255
+
233
256
"@egjs/hammerjs": ["@egjs/hammerjs@2.0.17", "", { "dependencies": { "@types/hammerjs": "^2.0.36" } }, "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A=="],
234
257
235
258
"@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="],
···
296
319
297
320
"@expo/sdk-runtime-versions": ["@expo/sdk-runtime-versions@1.0.0", "", {}, "sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ=="],
298
321
322
+
"@expo/server": ["@expo/server@0.5.3", "", { "dependencies": { "abort-controller": "^3.0.0", "debug": "^4.3.4", "source-map-support": "~0.5.21", "undici": "^6.18.2" } }, "sha512-WXsWzeBs5v/h0PUfHyNLLz07rwwO5myQ1A5DGYewyyGLmsyl61yVCe8AgAlp1wkiMsqhj2hZqI2u3K10QnCMrQ=="],
323
+
299
324
"@expo/spawn-async": ["@expo/spawn-async@1.7.2", "", { "dependencies": { "cross-spawn": "^7.0.3" } }, "sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew=="],
300
325
301
326
"@expo/sudo-prompt": ["@expo/sudo-prompt@9.3.2", "", {}, "sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw=="],
···
306
331
307
332
"@expo/xcpretty": ["@expo/xcpretty@4.3.2", "", { "dependencies": { "@babel/code-frame": "7.10.4", "chalk": "^4.1.0", "find-up": "^5.0.0", "js-yaml": "^4.1.0" }, "bin": { "excpretty": "build/cli.js" } }, "sha512-ReZxZ8pdnoI3tP/dNnJdnmAk7uLT4FjsKDGW7YeDdvdOMz2XCQSmSCM9IWlrXuWtMF9zeSB6WJtEhCQ41gQOfw=="],
308
333
334
+
"@formatjs/ecma402-abstract": ["@formatjs/ecma402-abstract@3.0.5", "", { "dependencies": { "@formatjs/fast-memoize": "3.0.1", "@formatjs/intl-localematcher": "0.7.3", "decimal.js": "^10.4.3", "tslib": "^2.8.0" } }, "sha512-TF0uoOhPhbzzAuKgOA3s8M20wZm5f6IWDq6dBVkl8gKvS7vq84AkzR9ts0oLN0pbDy6TDx0pESUgyJgMJQItDg=="],
335
+
336
+
"@formatjs/fast-memoize": ["@formatjs/fast-memoize@3.0.1", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-kzk635kEmsxrrEWQXY7uKRocFCVXR4es5OQqcqCGg2NPtQztG/OBkE9THHu6UOTxpfyIkZhh6DjPBZGRp7y3og=="],
337
+
338
+
"@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.7.3", "", { "dependencies": { "@formatjs/fast-memoize": "3.0.1", "tslib": "^2.8.0" } }, "sha512-NaeABectKdTCOnlH9VFGmMS3K0JuR7Soc2t5R2MCkBrM3H/hlKVYh0XSrcjjPkbjIdrF7L/Bzx9JtGuVaSfYlA=="],
339
+
340
+
"@formatjs/intl-segmenter": ["@formatjs/intl-segmenter@12.0.5", "", { "dependencies": { "@formatjs/ecma402-abstract": "3.0.5", "@formatjs/intl-localematcher": "0.7.3", "tslib": "^2.8.0" } }, "sha512-Xb6Bs9qMGwG+Jh5ADKfujlm2+nh4kLybD/YVwyHBDeyiY78wffs2KfXHM7xBTM3higjV8LodNWwAtOXgZud0Sg=="],
341
+
309
342
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
310
343
311
344
"@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="],
···
350
383
351
384
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
352
385
386
+
"@material/material-color-utilities": ["@material/material-color-utilities@0.2.7", "", {}, "sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ=="],
387
+
353
388
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
354
389
355
390
"@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="],
391
+
392
+
"@pchmn/expo-material3-theme": ["@pchmn/expo-material3-theme@1.3.2", "", { "dependencies": { "@material/material-color-utilities": "^0.2.7", "color": "^4.2.3" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-LT0A0qwdOg2QUKDUxLp1Wk6tj9Qk4x0HAUjJd3IBko01CbmWrLYiq0W01JTBnWczYyj2X4+1j/X3aFYrPDsRag=="],
356
393
357
394
"@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
358
395
···
432
469
433
470
"@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="],
434
471
472
+
"@shopify/flash-list": ["@shopify/flash-list@2.0.2", "", { "dependencies": { "tslib": "2.8.1" }, "peerDependencies": { "@babel/runtime": "*", "react": "*", "react-native": "*" } }, "sha512-zhlrhA9eiuEzja4wxVvotgXHtqd3qsYbXkQ3rsBfOgbFA9BVeErpDE/yEwtlIviRGEqpuFj/oU5owD6ByaNX+w=="],
473
+
435
474
"@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="],
436
475
437
476
"@sinonjs/commons": ["@sinonjs/commons@3.0.1", "", { "dependencies": { "type-detect": "4.0.8" } }, "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ=="],
438
477
439
478
"@sinonjs/fake-timers": ["@sinonjs/fake-timers@10.3.0", "", { "dependencies": { "@sinonjs/commons": "^3.0.0" } }, "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA=="],
440
479
480
+
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
481
+
482
+
"@tanstack/query-core": ["@tanstack/query-core@5.90.12", "", {}, "sha512-T1/8t5DhV/SisWjDnaiU2drl6ySvsHj1bHBCWNXd+/T+Hh1cf6JodyEYMd5sgwm+b/mETT4EV3H+zCVczCU5hg=="],
483
+
484
+
"@tanstack/react-query": ["@tanstack/react-query@5.90.12", "", { "dependencies": { "@tanstack/query-core": "5.90.12" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg=="],
485
+
441
486
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
442
487
443
488
"@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
···
572
617
573
618
"array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="],
574
619
620
+
"array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="],
621
+
575
622
"array-includes": ["array-includes@3.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-object-atoms": "^1.1.1", "get-intrinsic": "^1.3.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" } }, "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ=="],
576
623
577
624
"array.prototype.findlast": ["array.prototype.findlast@1.2.5", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ=="],
···
626
673
627
674
"baseline-browser-mapping": ["baseline-browser-mapping@2.9.11", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ=="],
628
675
676
+
"basic-auth": ["basic-auth@2.0.1", "", { "dependencies": { "safe-buffer": "5.1.2" } }, "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg=="],
677
+
629
678
"better-opn": ["better-opn@3.0.2", "", { "dependencies": { "open": "^8.0.4" } }, "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ=="],
630
679
631
680
"big-integer": ["big-integer@1.6.52", "", {}, "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg=="],
681
+
682
+
"body-parser": ["body-parser@1.20.4", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.14.0", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA=="],
632
683
633
684
"bplist-creator": ["bplist-creator@0.1.0", "", { "dependencies": { "stream-buffers": "2.2.x" } }, "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg=="],
634
685
···
698
749
699
750
"connect": ["connect@3.7.0", "", { "dependencies": { "debug": "2.6.9", "finalhandler": "1.1.2", "parseurl": "~1.3.3", "utils-merge": "1.0.1" } }, "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ=="],
700
751
752
+
"content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="],
753
+
754
+
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
755
+
701
756
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
702
757
758
+
"cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
759
+
760
+
"cookie-signature": ["cookie-signature@1.0.7", "", {}, "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="],
761
+
703
762
"core-js-compat": ["core-js-compat@3.47.0", "", { "dependencies": { "browserslist": "^4.28.0" } }, "sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ=="],
704
763
705
764
"cross-fetch": ["cross-fetch@3.2.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q=="],
···
719
778
"data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="],
720
779
721
780
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
781
+
782
+
"decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="],
722
783
723
784
"decode-uri-component": ["decode-uri-component@0.2.2", "", {}, "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="],
724
785
···
808
869
809
870
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
810
871
872
+
"esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
873
+
811
874
"espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="],
812
875
813
876
"esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
···
830
893
831
894
"expo-asset": ["expo-asset@12.0.12", "", { "dependencies": { "@expo/image-utils": "^0.8.8", "expo-constants": "~18.0.12" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-CsXFCQbx2fElSMn0lyTdRIyKlSXOal6ilLJd+yeZ6xaC7I9AICQgscY5nj0QcwgA+KYYCCEQEBndMsmj7drOWQ=="],
832
895
896
+
"expo-atlas": ["expo-atlas@0.4.3", "", { "dependencies": { "@expo/server": "^0.5.0", "arg": "^5.0.2", "chalk": "^4.1.2", "compression": "^1.7.4", "connect": "^3.7.0", "express": "^4.19.2", "freeport-async": "^2.0.0", "getenv": "^2.0.0", "morgan": "^1.10.0", "open": "^8.4.2", "serve-static": "^1.15.0", "stream-json": "^1.8.0" }, "peerDependencies": { "expo": "*" }, "bin": { "expo-atlas": "build/src/cli/bin.js" } }, "sha512-nN2bouxFvMsqZqLl0ka+eI9Ofida0PcuoE4v+7fHlgyp95X2cCL8Acf0nRKXmmIBEYazdw0d7BAOZfC0b41oxA=="],
897
+
833
898
"expo-constants": ["expo-constants@18.0.12", "", { "dependencies": { "@expo/config": "~12.0.12", "@expo/env": "~2.0.8" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-WzcKYMVNRRu4NcSzfIVRD5aUQFnSpTZgXFrlWmm19xJoDa4S3/PQNi6PNTBRc49xz9h8FT7HMxRKaC8lr0gflA=="],
834
899
835
900
"expo-file-system": ["expo-file-system@19.0.21", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-s3DlrDdiscBHtab/6W1osrjGL+C2bvoInPJD7sOwmxfJ5Woynv2oc+Fz1/xVXaE/V7HE/+xrHC/H45tu6lZzzg=="],
···
860
925
861
926
"expo-system-ui": ["expo-system-ui@6.0.9", "", { "dependencies": { "@react-native/normalize-colors": "0.81.5", "debug": "^4.3.2" }, "peerDependencies": { "expo": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native-web"] }, "sha512-eQTYGzw1V4RYiYHL9xDLYID3Wsec2aZS+ypEssmF64D38aDrqbDgz1a2MSlHLQp2jHXSs3FvojhZ9FVela1Zcg=="],
862
927
928
+
"expo-video": ["expo-video@3.0.15", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-KmxHVCtOBb1fnxXL6DpgLbEe7Qlv/vHNGTLfz0u/eY8fBC9s5cncD2BhPunEffrGvNMftBzYMYDaO86x+IYpnA=="],
929
+
863
930
"expo-web-browser": ["expo-web-browser@15.0.10", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-fvDhW4bhmXAeWFNFiInmsGCK83PAqAcQaFyp/3pE/jbdKmFKoRCWr46uZGIfN4msLK/OODhaQ/+US7GSJNDHJg=="],
864
931
865
932
"exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="],
866
933
934
+
"express": ["express@4.22.1", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.3", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g=="],
935
+
867
936
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
868
937
869
938
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
···
897
966
"fontfaceobserver": ["fontfaceobserver@2.3.0", "", {}, "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg=="],
898
967
899
968
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
969
+
970
+
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
900
971
901
972
"freeport-async": ["freeport-async@2.0.0", "", {}, "sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ=="],
902
973
···
974
1045
975
1046
"hyphenate-style-name": ["hyphenate-style-name@1.1.0", "", {}, "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw=="],
976
1047
1048
+
"iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
1049
+
977
1050
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
978
1051
979
1052
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
···
995
1068
"internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
996
1069
997
1070
"invariant": ["invariant@2.2.4", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA=="],
1071
+
1072
+
"ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="],
998
1073
999
1074
"is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
1000
1075
···
1162
1237
1163
1238
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
1164
1239
1240
+
"media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="],
1241
+
1165
1242
"memoize-one": ["memoize-one@5.2.1", "", {}, "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="],
1166
1243
1244
+
"merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="],
1245
+
1167
1246
"merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="],
1168
1247
1248
+
"methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="],
1249
+
1169
1250
"metro": ["metro@0.83.3", "", { "dependencies": { "@babel/code-frame": "^7.24.7", "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/parser": "^7.25.3", "@babel/template": "^7.25.0", "@babel/traverse": "^7.25.3", "@babel/types": "^7.25.2", "accepts": "^1.3.7", "chalk": "^4.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^4.4.0", "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "hermes-parser": "0.32.0", "image-size": "^1.0.2", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", "metro-babel-transformer": "0.83.3", "metro-cache": "0.83.3", "metro-cache-key": "0.83.3", "metro-config": "0.83.3", "metro-core": "0.83.3", "metro-file-map": "0.83.3", "metro-resolver": "0.83.3", "metro-runtime": "0.83.3", "metro-source-map": "0.83.3", "metro-symbolicate": "0.83.3", "metro-transform-plugins": "0.83.3", "metro-transform-worker": "0.83.3", "mime-types": "^2.1.27", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", "source-map": "^0.5.6", "throat": "^5.0.0", "ws": "^7.5.10", "yargs": "^17.6.2" }, "bin": { "metro": "src/cli.js" } }, "sha512-+rP+/GieOzkt97hSJ0MrPOuAH/jpaS21ZDvL9DJ35QYRDlQcwzcvUlGUf79AnQxq/2NPiS/AULhhM4TKutIt8Q=="],
1170
1251
1171
1252
"metro-babel-transformer": ["metro-babel-transformer@0.83.3", "", { "dependencies": { "@babel/core": "^7.25.2", "flow-enums-runtime": "^0.0.6", "hermes-parser": "0.32.0", "nullthrows": "^1.1.1" } }, "sha512-1vxlvj2yY24ES1O5RsSIvg4a4WeL7PFXgKOHvXTXiW0deLvQr28ExXj6LjwCCDZ4YZLhq6HddLpZnX4dEdSq5g=="],
···
1214
1295
1215
1296
"mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="],
1216
1297
1298
+
"morgan": ["morgan@1.10.1", "", { "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", "depd": "~2.0.0", "on-finished": "~2.3.0", "on-headers": "~1.1.0" } }, "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A=="],
1299
+
1217
1300
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
1218
1301
1219
1302
"mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
···
1224
1307
1225
1308
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
1226
1309
1227
-
"negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
1310
+
"negotiator": ["negotiator@0.6.4", "", {}, "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w=="],
1228
1311
1229
1312
"nested-error-stacks": ["nested-error-stacks@2.0.1", "", {}, "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A=="],
1230
1313
···
1268
1351
1269
1352
"onetime": ["onetime@2.0.1", "", { "dependencies": { "mimic-fn": "^1.0.0" } }, "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ=="],
1270
1353
1271
-
"open": ["open@7.4.2", "", { "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" } }, "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q=="],
1354
+
"open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="],
1272
1355
1273
1356
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
1274
1357
···
1298
1381
1299
1382
"path-scurry": ["path-scurry@2.0.1", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA=="],
1300
1383
1384
+
"path-to-regexp": ["path-to-regexp@0.1.12", "", {}, "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="],
1385
+
1301
1386
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
1302
1387
1303
1388
"picomatch": ["picomatch@3.0.1", "", {}, "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag=="],
···
1330
1415
1331
1416
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
1332
1417
1418
+
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
1419
+
1333
1420
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
1334
1421
1335
1422
"qrcode-terminal": ["qrcode-terminal@0.11.0", "", { "bin": { "qrcode-terminal": "./bin/qrcode-terminal.js" } }, "sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ=="],
1336
1423
1424
+
"qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="],
1425
+
1337
1426
"query-string": ["query-string@7.1.3", "", { "dependencies": { "decode-uri-component": "^0.2.2", "filter-obj": "^1.1.0", "split-on-first": "^1.0.0", "strict-uri-encode": "^2.0.0" } }, "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg=="],
1338
1427
1339
1428
"queue": ["queue@6.0.2", "", { "dependencies": { "inherits": "~2.0.3" } }, "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA=="],
1340
1429
1341
1430
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
1431
+
1432
+
"raw-body": ["raw-body@2.5.3", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "unpipe": "~1.0.0" } }, "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA=="],
1342
1433
1343
1434
"rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
1344
1435
···
1424
1515
1425
1516
"safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
1426
1517
1518
+
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
1519
+
1427
1520
"sax": ["sax@1.4.3", "", {}, "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ=="],
1428
1521
1429
1522
"scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
···
1502
1595
1503
1596
"stream-buffers": ["stream-buffers@2.2.0", "", {}, "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg=="],
1504
1597
1598
+
"stream-chain": ["stream-chain@2.2.5", "", {}, "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA=="],
1599
+
1600
+
"stream-json": ["stream-json@1.9.1", "", { "dependencies": { "stream-chain": "^2.2.5" } }, "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw=="],
1601
+
1505
1602
"strict-uri-encode": ["strict-uri-encode@2.0.0", "", {}, "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ=="],
1506
1603
1507
1604
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
···
1573
1670
"type-detect": ["type-detect@4.0.8", "", {}, "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="],
1574
1671
1575
1672
"type-fest": ["type-fest@0.7.1", "", {}, "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg=="],
1673
+
1674
+
"type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="],
1576
1675
1577
1676
"typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="],
1578
1677
···
1760
1859
1761
1860
"@react-native/community-cli-plugin/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
1762
1861
1862
+
"@react-native/dev-middleware/open": ["open@7.4.2", "", { "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" } }, "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q=="],
1863
+
1763
1864
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
1764
1865
1765
1866
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
1766
1867
1767
1868
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
1768
1869
1870
+
"accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
1871
+
1769
1872
"ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
1770
1873
1771
1874
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
1772
1875
1773
1876
"babel-plugin-polyfill-corejs2/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
1774
1877
1775
-
"better-opn/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="],
1878
+
"basic-auth/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
1879
+
1880
+
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
1776
1881
1777
1882
"cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
1778
1883
1779
1884
"compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
1780
1885
1781
-
"compression/negotiator": ["negotiator@0.6.4", "", {}, "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w=="],
1782
-
1783
1886
"connect/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
1784
1887
1785
1888
"eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
···
1795
1898
"eslint-plugin-react/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
1796
1899
1797
1900
"expo-modules-autolinking/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
1901
+
1902
+
"express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
1903
+
1904
+
"express/finalhandler": ["finalhandler@1.3.2", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "statuses": "~2.0.2", "unpipe": "~1.0.0" } }, "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg=="],
1798
1905
1799
1906
"fbjs/promise": ["promise@7.3.1", "", { "dependencies": { "asap": "~2.0.3" } }, "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg=="],
1800
1907
···
1840
1947
1841
1948
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
1842
1949
1950
+
"morgan/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
1951
+
1952
+
"morgan/on-finished": ["on-finished@2.3.0", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww=="],
1953
+
1843
1954
"npm-package-arg/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
1844
1955
1845
1956
"ora/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
···
1922
2033
1923
2034
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
1924
2035
2036
+
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
2037
+
1925
2038
"compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
1926
2039
1927
2040
"connect/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
2041
+
2042
+
"express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
1928
2043
1929
2044
"finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
1930
2045
···
1939
2054
"metro-babel-transformer/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="],
1940
2055
1941
2056
"metro/hermes-parser/hermes-estree": ["hermes-estree@0.32.0", "", {}, "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ=="],
2057
+
2058
+
"morgan/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
1942
2059
1943
2060
"ora/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
1944
2061
-25
components/external-link.tsx
-25
components/external-link.tsx
···
1
-
import { Href, Link } from 'expo-router';
2
-
import { openBrowserAsync, WebBrowserPresentationStyle } from 'expo-web-browser';
3
-
import { type ComponentProps } from 'react';
4
-
5
-
type Props = Omit<ComponentProps<typeof Link>, 'href'> & { href: Href & string };
6
-
7
-
export function ExternalLink({ href, ...rest }: Props) {
8
-
return (
9
-
<Link
10
-
target="_blank"
11
-
{...rest}
12
-
href={href}
13
-
onPress={async (event) => {
14
-
if (process.env.EXPO_OS !== 'web') {
15
-
// Prevent the default behavior of linking to the default browser on native.
16
-
event.preventDefault();
17
-
// Open the link in an in-app browser.
18
-
await openBrowserAsync(href, {
19
-
presentationStyle: WebBrowserPresentationStyle.AUTOMATIC,
20
-
});
21
-
}
22
-
}}
23
-
/>
24
-
);
25
-
}
-18
components/haptic-tab.tsx
-18
components/haptic-tab.tsx
···
1
-
import { BottomTabBarButtonProps } from '@react-navigation/bottom-tabs';
2
-
import { PlatformPressable } from '@react-navigation/elements';
3
-
import * as Haptics from 'expo-haptics';
4
-
5
-
export function HapticTab(props: BottomTabBarButtonProps) {
6
-
return (
7
-
<PlatformPressable
8
-
{...props}
9
-
onPressIn={(ev) => {
10
-
if (process.env.EXPO_OS === 'ios') {
11
-
// Add a soft haptic feedback when pressing down on the tabs.
12
-
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
13
-
}
14
-
props.onPressIn?.(ev);
15
-
}}
16
-
/>
17
-
);
18
-
}
-19
components/hello-wave.tsx
-19
components/hello-wave.tsx
···
1
-
import Animated from 'react-native-reanimated';
2
-
3
-
export function HelloWave() {
4
-
return (
5
-
<Animated.Text
6
-
style={{
7
-
fontSize: 28,
8
-
lineHeight: 32,
9
-
marginTop: -6,
10
-
animationName: {
11
-
'50%': { transform: [{ rotate: '25deg' }] },
12
-
},
13
-
animationIterationCount: 4,
14
-
animationDuration: '300ms',
15
-
}}>
16
-
👋
17
-
</Animated.Text>
18
-
);
19
-
}
-79
components/parallax-scroll-view.tsx
-79
components/parallax-scroll-view.tsx
···
1
-
import type { PropsWithChildren, ReactElement } from 'react';
2
-
import { StyleSheet } from 'react-native';
3
-
import Animated, {
4
-
interpolate,
5
-
useAnimatedRef,
6
-
useAnimatedStyle,
7
-
useScrollOffset,
8
-
} from 'react-native-reanimated';
9
-
10
-
import { ThemedView } from '@/components/themed-view';
11
-
import { useColorScheme } from '@/hooks/use-color-scheme';
12
-
import { useThemeColor } from '@/hooks/use-theme-color';
13
-
14
-
const HEADER_HEIGHT = 250;
15
-
16
-
type Props = PropsWithChildren<{
17
-
headerImage: ReactElement;
18
-
headerBackgroundColor: { dark: string; light: string };
19
-
}>;
20
-
21
-
export default function ParallaxScrollView({
22
-
children,
23
-
headerImage,
24
-
headerBackgroundColor,
25
-
}: Props) {
26
-
const backgroundColor = useThemeColor({}, 'background');
27
-
const colorScheme = useColorScheme() ?? 'light';
28
-
const scrollRef = useAnimatedRef<Animated.ScrollView>();
29
-
const scrollOffset = useScrollOffset(scrollRef);
30
-
const headerAnimatedStyle = useAnimatedStyle(() => {
31
-
return {
32
-
transform: [
33
-
{
34
-
translateY: interpolate(
35
-
scrollOffset.value,
36
-
[-HEADER_HEIGHT, 0, HEADER_HEIGHT],
37
-
[-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75]
38
-
),
39
-
},
40
-
{
41
-
scale: interpolate(scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], [2, 1, 1]),
42
-
},
43
-
],
44
-
};
45
-
});
46
-
47
-
return (
48
-
<Animated.ScrollView
49
-
ref={scrollRef}
50
-
style={{ backgroundColor, flex: 1 }}
51
-
scrollEventThrottle={16}>
52
-
<Animated.View
53
-
style={[
54
-
styles.header,
55
-
{ backgroundColor: headerBackgroundColor[colorScheme] },
56
-
headerAnimatedStyle,
57
-
]}>
58
-
{headerImage}
59
-
</Animated.View>
60
-
<ThemedView style={styles.content}>{children}</ThemedView>
61
-
</Animated.ScrollView>
62
-
);
63
-
}
64
-
65
-
const styles = StyleSheet.create({
66
-
container: {
67
-
flex: 1,
68
-
},
69
-
header: {
70
-
height: HEADER_HEIGHT,
71
-
overflow: 'hidden',
72
-
},
73
-
content: {
74
-
flex: 1,
75
-
padding: 32,
76
-
gap: 16,
77
-
overflow: 'hidden',
78
-
},
79
-
});
-60
components/themed-text.tsx
-60
components/themed-text.tsx
···
1
-
import { StyleSheet, Text, type TextProps } from 'react-native';
2
-
3
-
import { useThemeColor } from '@/hooks/use-theme-color';
4
-
5
-
export type ThemedTextProps = TextProps & {
6
-
lightColor?: string;
7
-
darkColor?: string;
8
-
type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
9
-
};
10
-
11
-
export function ThemedText({
12
-
style,
13
-
lightColor,
14
-
darkColor,
15
-
type = 'default',
16
-
...rest
17
-
}: ThemedTextProps) {
18
-
const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text');
19
-
20
-
return (
21
-
<Text
22
-
style={[
23
-
{ color },
24
-
type === 'default' ? styles.default : undefined,
25
-
type === 'title' ? styles.title : undefined,
26
-
type === 'defaultSemiBold' ? styles.defaultSemiBold : undefined,
27
-
type === 'subtitle' ? styles.subtitle : undefined,
28
-
type === 'link' ? styles.link : undefined,
29
-
style,
30
-
]}
31
-
{...rest}
32
-
/>
33
-
);
34
-
}
35
-
36
-
const styles = StyleSheet.create({
37
-
default: {
38
-
fontSize: 16,
39
-
lineHeight: 24,
40
-
},
41
-
defaultSemiBold: {
42
-
fontSize: 16,
43
-
lineHeight: 24,
44
-
fontWeight: '600',
45
-
},
46
-
title: {
47
-
fontSize: 32,
48
-
fontWeight: 'bold',
49
-
lineHeight: 32,
50
-
},
51
-
subtitle: {
52
-
fontSize: 20,
53
-
fontWeight: 'bold',
54
-
},
55
-
link: {
56
-
lineHeight: 30,
57
-
fontSize: 16,
58
-
color: '#0a7ea4',
59
-
},
60
-
});
-14
components/themed-view.tsx
-14
components/themed-view.tsx
···
1
-
import { View, type ViewProps } from 'react-native';
2
-
3
-
import { useThemeColor } from '@/hooks/use-theme-color';
4
-
5
-
export type ThemedViewProps = ViewProps & {
6
-
lightColor?: string;
7
-
darkColor?: string;
8
-
};
9
-
10
-
export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) {
11
-
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
12
-
13
-
return <View style={[{ backgroundColor }, style]} {...otherProps} />;
14
-
}
-45
components/ui/collapsible.tsx
-45
components/ui/collapsible.tsx
···
1
-
import { PropsWithChildren, useState } from 'react';
2
-
import { StyleSheet, TouchableOpacity } from 'react-native';
3
-
4
-
import { ThemedText } from '@/components/themed-text';
5
-
import { ThemedView } from '@/components/themed-view';
6
-
import { IconSymbol } from '@/components/ui/icon-symbol';
7
-
import { Colors } from '@/constants/theme';
8
-
import { useColorScheme } from '@/hooks/use-color-scheme';
9
-
10
-
export function Collapsible({ children, title }: PropsWithChildren & { title: string }) {
11
-
const [isOpen, setIsOpen] = useState(false);
12
-
const theme = useColorScheme() ?? 'light';
13
-
14
-
return (
15
-
<ThemedView>
16
-
<TouchableOpacity
17
-
style={styles.heading}
18
-
onPress={() => setIsOpen((value) => !value)}
19
-
activeOpacity={0.8}>
20
-
<IconSymbol
21
-
name="chevron.right"
22
-
size={18}
23
-
weight="medium"
24
-
color={theme === 'light' ? Colors.light.icon : Colors.dark.icon}
25
-
style={{ transform: [{ rotate: isOpen ? '90deg' : '0deg' }] }}
26
-
/>
27
-
28
-
<ThemedText type="defaultSemiBold">{title}</ThemedText>
29
-
</TouchableOpacity>
30
-
{isOpen && <ThemedView style={styles.content}>{children}</ThemedView>}
31
-
</ThemedView>
32
-
);
33
-
}
34
-
35
-
const styles = StyleSheet.create({
36
-
heading: {
37
-
flexDirection: 'row',
38
-
alignItems: 'center',
39
-
gap: 6,
40
-
},
41
-
content: {
42
-
marginTop: 6,
43
-
marginLeft: 24,
44
-
},
45
-
});
-32
components/ui/icon-symbol.ios.tsx
-32
components/ui/icon-symbol.ios.tsx
···
1
-
import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols';
2
-
import { StyleProp, ViewStyle } from 'react-native';
3
-
4
-
export function IconSymbol({
5
-
name,
6
-
size = 24,
7
-
color,
8
-
style,
9
-
weight = 'regular',
10
-
}: {
11
-
name: SymbolViewProps['name'];
12
-
size?: number;
13
-
color: string;
14
-
style?: StyleProp<ViewStyle>;
15
-
weight?: SymbolWeight;
16
-
}) {
17
-
return (
18
-
<SymbolView
19
-
weight={weight}
20
-
tintColor={color}
21
-
resizeMode="scaleAspectFit"
22
-
name={name}
23
-
style={[
24
-
{
25
-
width: size,
26
-
height: size,
27
-
},
28
-
style,
29
-
]}
30
-
/>
31
-
);
32
-
}
-41
components/ui/icon-symbol.tsx
-41
components/ui/icon-symbol.tsx
···
1
-
// Fallback for using MaterialIcons on Android and web.
2
-
3
-
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
4
-
import { SymbolWeight, SymbolViewProps } from 'expo-symbols';
5
-
import { ComponentProps } from 'react';
6
-
import { OpaqueColorValue, type StyleProp, type TextStyle } from 'react-native';
7
-
8
-
type IconMapping = Record<SymbolViewProps['name'], ComponentProps<typeof MaterialIcons>['name']>;
9
-
type IconSymbolName = keyof typeof MAPPING;
10
-
11
-
/**
12
-
* Add your SF Symbols to Material Icons mappings here.
13
-
* - see Material Icons in the [Icons Directory](https://icons.expo.fyi).
14
-
* - see SF Symbols in the [SF Symbols](https://developer.apple.com/sf-symbols/) app.
15
-
*/
16
-
const MAPPING = {
17
-
'house.fill': 'home',
18
-
'paperplane.fill': 'send',
19
-
'chevron.left.forwardslash.chevron.right': 'code',
20
-
'chevron.right': 'chevron-right',
21
-
} as IconMapping;
22
-
23
-
/**
24
-
* An icon component that uses native SF Symbols on iOS, and Material Icons on Android and web.
25
-
* This ensures a consistent look across platforms, and optimal resource usage.
26
-
* Icon `name`s are based on SF Symbols and require manual mapping to Material Icons.
27
-
*/
28
-
export function IconSymbol({
29
-
name,
30
-
size = 24,
31
-
color,
32
-
style,
33
-
}: {
34
-
name: IconSymbolName;
35
-
size?: number;
36
-
color: string | OpaqueColorValue;
37
-
style?: StyleProp<TextStyle>;
38
-
weight?: SymbolWeight;
39
-
}) {
40
-
return <MaterialIcons color={color} size={size} name={MAPPING[name]} style={style} />;
41
-
}
-53
constants/theme.ts
-53
constants/theme.ts
···
1
-
/**
2
-
* Below are the colors that are used in the app. The colors are defined in the light and dark mode.
3
-
* There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc.
4
-
*/
5
-
6
-
import { Platform } from 'react-native';
7
-
8
-
const tintColorLight = '#0a7ea4';
9
-
const tintColorDark = '#fff';
10
-
11
-
export const Colors = {
12
-
light: {
13
-
text: '#11181C',
14
-
background: '#fff',
15
-
tint: tintColorLight,
16
-
icon: '#687076',
17
-
tabIconDefault: '#687076',
18
-
tabIconSelected: tintColorLight,
19
-
},
20
-
dark: {
21
-
text: '#ECEDEE',
22
-
background: '#151718',
23
-
tint: tintColorDark,
24
-
icon: '#9BA1A6',
25
-
tabIconDefault: '#9BA1A6',
26
-
tabIconSelected: tintColorDark,
27
-
},
28
-
};
29
-
30
-
export const Fonts = Platform.select({
31
-
ios: {
32
-
/** iOS `UIFontDescriptorSystemDesignDefault` */
33
-
sans: 'system-ui',
34
-
/** iOS `UIFontDescriptorSystemDesignSerif` */
35
-
serif: 'ui-serif',
36
-
/** iOS `UIFontDescriptorSystemDesignRounded` */
37
-
rounded: 'ui-rounded',
38
-
/** iOS `UIFontDescriptorSystemDesignMonospaced` */
39
-
mono: 'ui-monospace',
40
-
},
41
-
default: {
42
-
sans: 'normal',
43
-
serif: 'serif',
44
-
rounded: 'normal',
45
-
mono: 'monospace',
46
-
},
47
-
web: {
48
-
sans: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",
49
-
serif: "Georgia, 'Times New Roman', serif",
50
-
rounded: "'SF Pro Rounded', 'Hiragino Maru Gothic ProN', Meiryo, 'MS PGothic', sans-serif",
51
-
mono: "SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace",
52
-
},
53
-
});
-1
hooks/use-color-scheme.ts
-1
hooks/use-color-scheme.ts
···
1
-
export { useColorScheme } from 'react-native';
-21
hooks/use-color-scheme.web.ts
-21
hooks/use-color-scheme.web.ts
···
1
-
import { useEffect, useState } from 'react';
2
-
import { useColorScheme as useRNColorScheme } from 'react-native';
3
-
4
-
/**
5
-
* To support static rendering, this value needs to be re-calculated on the client side for web
6
-
*/
7
-
export function useColorScheme() {
8
-
const [hasHydrated, setHasHydrated] = useState(false);
9
-
10
-
useEffect(() => {
11
-
setHasHydrated(true);
12
-
}, []);
13
-
14
-
const colorScheme = useRNColorScheme();
15
-
16
-
if (hasHydrated) {
17
-
return colorScheme;
18
-
}
19
-
20
-
return 'light';
21
-
}
-21
hooks/use-theme-color.ts
-21
hooks/use-theme-color.ts
···
1
-
/**
2
-
* Learn more about light and dark modes:
3
-
* https://docs.expo.dev/guides/color-schemes/
4
-
*/
5
-
6
-
import { Colors } from '@/constants/theme';
7
-
import { useColorScheme } from '@/hooks/use-color-scheme';
8
-
9
-
export function useThemeColor(
10
-
props: { light?: string; dark?: string },
11
-
colorName: keyof typeof Colors.light & keyof typeof Colors.dark
12
-
) {
13
-
const theme = useColorScheme() ?? 'light';
14
-
const colorFromProps = props[theme];
15
-
16
-
if (colorFromProps) {
17
-
return colorFromProps;
18
-
} else {
19
-
return Colors[theme][colorName];
20
-
}
21
-
}
+14
-5
package.json
+14
-5
package.json
···
1
1
{
2
2
"name": "dusksky",
3
-
"main": "expo-router/entry",
3
+
"main": "./index",
4
4
"version": "1.0.0",
5
5
"scripts": {
6
6
"start": "expo start",
···
11
11
"lint": "expo lint"
12
12
},
13
13
"dependencies": {
14
+
"@atcute/bluesky": "^3.2.14",
15
+
"@atcute/bluesky-richtext-segmenter": "^2.0.4",
16
+
"@atcute/client": "^4.1.1",
14
17
"@expo/vector-icons": "^15.0.3",
18
+
"@formatjs/intl-segmenter": "^12.0.5",
19
+
"@pchmn/expo-material3-theme": "^1.3.2",
15
20
"@react-navigation/bottom-tabs": "^7.4.0",
16
21
"@react-navigation/elements": "^2.6.3",
17
22
"@react-navigation/native": "^7.1.8",
23
+
"@shopify/flash-list": "2.0.2",
24
+
"@tanstack/react-query": "^5.90.12",
18
25
"expo": "~54.0.30",
19
26
"expo-constants": "~18.0.12",
20
27
"expo-font": "~14.0.10",
···
26
33
"expo-status-bar": "~3.0.9",
27
34
"expo-symbols": "~1.0.8",
28
35
"expo-system-ui": "~6.0.9",
36
+
"expo-video": "~3.0.15",
29
37
"expo-web-browser": "~15.0.10",
30
38
"react": "19.1.0",
31
39
"react-dom": "19.1.0",
32
40
"react-native": "0.81.5",
33
41
"react-native-gesture-handler": "~2.28.0",
34
-
"react-native-worklets": "0.5.1",
35
42
"react-native-reanimated": "~4.1.1",
36
43
"react-native-safe-area-context": "~5.6.0",
37
44
"react-native-screens": "~4.16.0",
38
-
"react-native-web": "~0.21.0"
45
+
"react-native-web": "~0.21.0",
46
+
"react-native-worklets": "0.5.1"
39
47
},
40
48
"devDependencies": {
41
49
"@types/react": "~19.1.0",
42
-
"typescript": "~5.9.2",
43
50
"eslint": "^9.25.0",
44
-
"eslint-config-expo": "~10.0.0"
51
+
"eslint-config-expo": "~10.0.0",
52
+
"expo-atlas": "^0.4.0",
53
+
"typescript": "~5.9.2"
45
54
},
46
55
"private": true
47
56
}
-112
scripts/reset-project.js
-112
scripts/reset-project.js
···
1
-
#!/usr/bin/env node
2
-
3
-
/**
4
-
* This script is used to reset the project to a blank state.
5
-
* It deletes or moves the /app, /components, /hooks, /scripts, and /constants directories to /app-example based on user input and creates a new /app directory with an index.tsx and _layout.tsx file.
6
-
* You can remove the `reset-project` script from package.json and safely delete this file after running it.
7
-
*/
8
-
9
-
const fs = require("fs");
10
-
const path = require("path");
11
-
const readline = require("readline");
12
-
13
-
const root = process.cwd();
14
-
const oldDirs = ["app", "components", "hooks", "constants", "scripts"];
15
-
const exampleDir = "app-example";
16
-
const newAppDir = "app";
17
-
const exampleDirPath = path.join(root, exampleDir);
18
-
19
-
const indexContent = `import { Text, View } from "react-native";
20
-
21
-
export default function Index() {
22
-
return (
23
-
<View
24
-
style={{
25
-
flex: 1,
26
-
justifyContent: "center",
27
-
alignItems: "center",
28
-
}}
29
-
>
30
-
<Text>Edit app/index.tsx to edit this screen.</Text>
31
-
</View>
32
-
);
33
-
}
34
-
`;
35
-
36
-
const layoutContent = `import { Stack } from "expo-router";
37
-
38
-
export default function RootLayout() {
39
-
return <Stack />;
40
-
}
41
-
`;
42
-
43
-
const rl = readline.createInterface({
44
-
input: process.stdin,
45
-
output: process.stdout,
46
-
});
47
-
48
-
const moveDirectories = async (userInput) => {
49
-
try {
50
-
if (userInput === "y") {
51
-
// Create the app-example directory
52
-
await fs.promises.mkdir(exampleDirPath, { recursive: true });
53
-
console.log(`📁 /${exampleDir} directory created.`);
54
-
}
55
-
56
-
// Move old directories to new app-example directory or delete them
57
-
for (const dir of oldDirs) {
58
-
const oldDirPath = path.join(root, dir);
59
-
if (fs.existsSync(oldDirPath)) {
60
-
if (userInput === "y") {
61
-
const newDirPath = path.join(root, exampleDir, dir);
62
-
await fs.promises.rename(oldDirPath, newDirPath);
63
-
console.log(`➡️ /${dir} moved to /${exampleDir}/${dir}.`);
64
-
} else {
65
-
await fs.promises.rm(oldDirPath, { recursive: true, force: true });
66
-
console.log(`❌ /${dir} deleted.`);
67
-
}
68
-
} else {
69
-
console.log(`➡️ /${dir} does not exist, skipping.`);
70
-
}
71
-
}
72
-
73
-
// Create new /app directory
74
-
const newAppDirPath = path.join(root, newAppDir);
75
-
await fs.promises.mkdir(newAppDirPath, { recursive: true });
76
-
console.log("\n📁 New /app directory created.");
77
-
78
-
// Create index.tsx
79
-
const indexPath = path.join(newAppDirPath, "index.tsx");
80
-
await fs.promises.writeFile(indexPath, indexContent);
81
-
console.log("📄 app/index.tsx created.");
82
-
83
-
// Create _layout.tsx
84
-
const layoutPath = path.join(newAppDirPath, "_layout.tsx");
85
-
await fs.promises.writeFile(layoutPath, layoutContent);
86
-
console.log("📄 app/_layout.tsx created.");
87
-
88
-
console.log("\n✅ Project reset complete. Next steps:");
89
-
console.log(
90
-
`1. Run \`npx expo start\` to start a development server.\n2. Edit app/index.tsx to edit the main screen.${
91
-
userInput === "y"
92
-
? `\n3. Delete the /${exampleDir} directory when you're done referencing it.`
93
-
: ""
94
-
}`
95
-
);
96
-
} catch (error) {
97
-
console.error(`❌ Error during script execution: ${error.message}`);
98
-
}
99
-
};
100
-
101
-
rl.question(
102
-
"Do you want to move existing files to /app-example instead of deleting them? (Y/n): ",
103
-
(answer) => {
104
-
const userInput = answer.trim().toLowerCase() || "y";
105
-
if (userInput === "y" || userInput === "n") {
106
-
moveDirectories(userInput).finally(() => rl.close());
107
-
} else {
108
-
console.log("❌ Invalid input. Please enter 'Y' or 'N'.");
109
-
rl.close();
110
-
}
111
-
}
112
-
);
+117
src/app/(tabs)/_layout.tsx
+117
src/app/(tabs)/_layout.tsx
···
1
+
import { useInfiniteQuery } from "@tanstack/react-query";
2
+
import { RefreshControl, Text, View, useColorScheme } from "react-native";
3
+
import { SafeAreaView } from "react-native-safe-area-context";
4
+
import { Client, ok, simpleFetchHandler } from "@atcute/client";
5
+
import { FlashList } from "@shopify/flash-list";
6
+
import { AppBskyFeedPost, AppBskyRichtextFacet } from "@atcute/bluesky";
7
+
import { StatusBar } from "expo-status-bar";
8
+
import EmbedView from "@/src/components/EmbedView";
9
+
import { Image } from "expo-image";
10
+
import { segmentize } from "@atcute/bluesky-richtext-segmenter";
11
+
import { Link } from "expo-router";
12
+
import { useMaterial3Theme } from "@pchmn/expo-material3-theme";
13
+
14
+
export default function TabLayout() {
15
+
const colorScheme = useColorScheme();
16
+
const { theme } = useMaterial3Theme({ sourceColor: "#f4983c" });
17
+
18
+
const feedQuery = useInfiniteQuery({
19
+
queryKey: ["feed"],
20
+
initialPageParam: "",
21
+
queryFn: async ({ signal, pageParam }) => {
22
+
const client = new Client({ handler: simpleFetchHandler({ service: "https://api.bsky.app" }) });
23
+
const res = await ok(
24
+
client.get("app.bsky.feed.getAuthorFeed", {
25
+
signal,
26
+
params: { actor: "did:plc:irx36xprktslecsbopbwnh5w", cursor: pageParam, filter: "posts_no_replies" },
27
+
})
28
+
);
29
+
30
+
return res;
31
+
},
32
+
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => lastPage.cursor,
33
+
getPreviousPageParam: (firstPage, allPages, firstPageParam, allPageParams) => firstPage.cursor,
34
+
});
35
+
36
+
return (
37
+
<>
38
+
<SafeAreaView style={{ backgroundColor: theme[colorScheme!].background }}>
39
+
<Text style={{ color: colorScheme === "light" ? "black" : "white" }}>{feedQuery.status}</Text>
40
+
{feedQuery.isError && <Text>{JSON.stringify(feedQuery.error)}</Text>}
41
+
{feedQuery.isSuccess && (
42
+
<View style={{ height: "100%" }}>
43
+
<FlashList
44
+
decelerationRate={"normal"}
45
+
snapToEnd={false}
46
+
snapToStart={false}
47
+
pagingEnabled={false}
48
+
data={feedQuery.data.pages.flatMap((val) => val.feed)}
49
+
onEndReached={() => feedQuery.fetchNextPage()}
50
+
contentContainerStyle={{ gap: 8 }}
51
+
refreshControl={<RefreshControl refreshing={feedQuery.isRefetching} />}
52
+
ListFooterComponent={<View />}
53
+
ListFooterComponentStyle={{ height: 64 }}
54
+
onRefresh={() => feedQuery.refetch()}
55
+
renderItem={({ item }) => {
56
+
return (
57
+
<View style={{ flex: 1, flexDirection: "row", gap: 8, padding: 8, minWidth: "100%", flexShrink: 0 }}>
58
+
<Image source={item.post.author.avatar} style={{ width: 44, height: 44, borderRadius: 19 }} />
59
+
<View style={{ flex: 1, gap: 4 }}>
60
+
<View style={{ flex: 1, flexDirection: "row", gap: 4 }}>
61
+
<Text
62
+
ellipsizeMode="tail"
63
+
numberOfLines={1}
64
+
style={{
65
+
fontSize: 16,
66
+
fontWeight: 700,
67
+
overflow: "hidden",
68
+
flexShrink: 1,
69
+
color: colorScheme === "light" ? "black" : "white",
70
+
}}
71
+
>
72
+
{item.post.author.displayName}
73
+
</Text>
74
+
<Text
75
+
ellipsizeMode="tail"
76
+
numberOfLines={1}
77
+
style={{
78
+
fontSize: 16,
79
+
opacity: 0.75,
80
+
overflow: "hidden",
81
+
flexShrink: 1,
82
+
color: colorScheme === "light" ? "black" : "white",
83
+
}}
84
+
>
85
+
{"@" + item.post.author.handle}
86
+
</Text>
87
+
</View>
88
+
{(item.post.record as AppBskyFeedPost.Main).text.trim().length > 0 && (
89
+
<Text selectable style={{ fontSize: 16, color: colorScheme === "light" ? "black" : "white" }}>
90
+
{segmentize(
91
+
(item.post.record as AppBskyFeedPost.Main).text,
92
+
(item.post.record as AppBskyFeedPost.Main).facets
93
+
).map((val, i) => {
94
+
if (val.features?.at(0)?.$type === "app.bsky.richtext.facet#link")
95
+
return (
96
+
<Link key={i} href={(val.features!.at(0)! as any).uri} style={{ color: "#1974D2" }}>
97
+
{val.text}
98
+
</Link>
99
+
);
100
+
101
+
return val.text;
102
+
})}
103
+
</Text>
104
+
)}
105
+
{item.post.embed && <EmbedView embed={item.post.embed} />}
106
+
</View>
107
+
</View>
108
+
);
109
+
}}
110
+
/>
111
+
</View>
112
+
)}
113
+
</SafeAreaView>
114
+
<StatusBar style="inverted" />
115
+
</>
116
+
);
117
+
}
+21
src/app/_layout.tsx
+21
src/app/_layout.tsx
···
1
+
import { Stack } from "expo-router";
2
+
import { StatusBar } from "expo-status-bar";
3
+
import "react-native-reanimated";
4
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
5
+
6
+
export const unstable_settings = {
7
+
anchor: "(tabs)",
8
+
};
9
+
10
+
const queryClient = new QueryClient();
11
+
12
+
export default function RootLayout() {
13
+
return (
14
+
<QueryClientProvider client={queryClient}>
15
+
<Stack>
16
+
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
17
+
</Stack>
18
+
<StatusBar style="auto" />
19
+
</QueryClientProvider>
20
+
);
21
+
}
+91
src/components/EmbedView.tsx
+91
src/components/EmbedView.tsx
···
1
+
import {
2
+
AppBskyEmbedExternal,
3
+
AppBskyEmbedImages,
4
+
AppBskyEmbedRecord,
5
+
AppBskyEmbedRecordWithMedia,
6
+
AppBskyEmbedVideo,
7
+
AppBskyFeedPost,
8
+
AppBskyRichtextFacet,
9
+
} from "@atcute/bluesky";
10
+
//import { segmentize } from "@atcute/bluesky-richtext-segmenter";
11
+
import { $type, is } from "@atcute/lexicons";
12
+
import { FlashList } from "@shopify/flash-list";
13
+
import { Image } from "expo-image";
14
+
import { View, Text, FlatList } from "react-native";
15
+
16
+
export default function EmbedView(props: {
17
+
embed?: $type.enforce<
18
+
| AppBskyEmbedExternal.View
19
+
| AppBskyEmbedImages.View
20
+
| AppBskyEmbedRecord.View
21
+
| AppBskyEmbedRecordWithMedia.View
22
+
| AppBskyEmbedVideo.View
23
+
>;
24
+
recursed?: true;
25
+
}) {
26
+
const switchOnEmbed = () => {
27
+
switch (props.embed?.$type) {
28
+
case "app.bsky.embed.images#view":
29
+
if (props.embed.images.length > 1)
30
+
return (
31
+
<FlatList
32
+
style={{ maxHeight: 256, gap: 8 }}
33
+
contentContainerStyle={{ gap: 8 }}
34
+
horizontal
35
+
data={props.embed.images}
36
+
renderItem={({ item }) => {
37
+
return (
38
+
<Image
39
+
style={{
40
+
aspectRatio: item.aspectRatio ? item.aspectRatio?.width / item.aspectRatio?.height : 1,
41
+
flex: 1,
42
+
height: "100%",
43
+
maxHeight: "100%",
44
+
width: "25%",
45
+
borderRadius: 16,
46
+
}}
47
+
source={item.thumb}
48
+
alt={item.alt}
49
+
contentFit="fill"
50
+
allowDownscaling={false}
51
+
autoplay
52
+
/>
53
+
);
54
+
}}
55
+
/>
56
+
);
57
+
else
58
+
return (
59
+
<View
60
+
style={{
61
+
height: "100%",
62
+
flex: 1,
63
+
alignItems: "center",
64
+
}}
65
+
>
66
+
<Image
67
+
style={{
68
+
aspectRatio: props.embed.images[0].aspectRatio
69
+
? props.embed.images[0].aspectRatio?.width / props.embed.images[0].aspectRatio?.height
70
+
: 1,
71
+
maxWidth: "100%",
72
+
height: "auto",
73
+
maxHeight: "100%",
74
+
width: "100%",
75
+
borderRadius: 16,
76
+
}}
77
+
source={props.embed.images[0].fullsize}
78
+
contentFit="contain"
79
+
allowDownscaling={false}
80
+
autoplay
81
+
/>
82
+
</View>
83
+
);
84
+
85
+
default:
86
+
return <Text>{props.embed?.$type}</Text>;
87
+
}
88
+
};
89
+
90
+
return <>{props.embed && switchOnEmbed()}</>;
91
+
}
+32
src/components/VideoPlayer.tsx
+32
src/components/VideoPlayer.tsx
···
1
+
import { useEvent } from "expo";
2
+
import { useVideoPlayer, VideoView } from "expo-video";
3
+
import { Button, View, StyleSheet } from "react-native";
4
+
5
+
export default function VideoPlayer(props: { videoSource: string }) {
6
+
const player = useVideoPlayer(props.videoSource, (player) => {});
7
+
8
+
const { isPlaying } = useEvent(player, "playingChange", { isPlaying: player.playing });
9
+
10
+
return (
11
+
<View style={styles.contentContainer}>
12
+
<VideoView style={styles.video} player={player} allowsFullscreen allowsPictureInPicture />
13
+
</View>
14
+
);
15
+
}
16
+
17
+
const styles = StyleSheet.create({
18
+
contentContainer: {
19
+
flex: 1,
20
+
padding: 10,
21
+
alignItems: "center",
22
+
justifyContent: "center",
23
+
paddingHorizontal: 50,
24
+
},
25
+
video: {
26
+
width: 350,
27
+
height: 275,
28
+
},
29
+
controlsContainer: {
30
+
padding: 10,
31
+
},
32
+
});
+5
-1
tsconfig.json
+5
-1
tsconfig.json