this repo has no description

add initial poc

+9
LICENSE.md
···
··· 1 + MIT License 2 + 3 + Copyright (c) 2025 Ruben Grimm 4 + 5 + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 + 7 + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 + 9 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-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
··· 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&apos;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
··· 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 - });
···
+6 -19
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 }
··· 1 + import { Stack } from "expo-router"; 2 + import "react-native-reanimated"; 3 4 export default function RootLayout() { 5 return ( 6 + <Stack> 7 + <Stack.Screen name="home" options={{ headerShown: false }} /> 8 + <Stack.Screen name="music" /> 9 + </Stack> 10 ); 11 }
+59
app/index.tsx
···
··· 1 + import { BrowserOAuthClient } from "@atproto/oauth-client-browser"; 2 + import { useRouter } from "expo-router"; 3 + import { useEffect, useRef } from "react"; 4 + import { Button, TextInput } from "react-native"; 5 + 6 + const redirect_uri = "http://127.0.0.1:8081"; 7 + const scope = "atproto transition:generic"; 8 + 9 + export default function HomeScreen() { 10 + const client = useRef<BrowserOAuthClient>(null); 11 + const router = useRouter(); 12 + 13 + useEffect(() => { 14 + if (client.current) return; 15 + 16 + async function initializeClient() { 17 + client.current = new BrowserOAuthClient({ 18 + handleResolver: "https://bsky.social", 19 + // Note that the origin of the "client_id" URL must be "http://localhost" when 20 + // using this configuration, regardless of the actual hostname ("127.0.0.1" or 21 + // "[::1]"), port or pathname. Only the `redirect_uris` must contain the 22 + // actual url that will be used to redirect the user back to the application. 23 + clientMetadata: { 24 + client_id: `http://localhost?redirect_uri=${encodeURIComponent( 25 + redirect_uri 26 + )}&scope=${encodeURIComponent(scope)}`, 27 + redirect_uris: [redirect_uri], 28 + scope, 29 + token_endpoint_auth_method: "none", 30 + }, 31 + }); 32 + const session = await client.current!.init(); 33 + if (session) { 34 + router.replace("/music"); 35 + } 36 + } 37 + initializeClient().catch(console.error); 38 + }, [router]); 39 + 40 + const handlValueRef = useRef<string>(""); 41 + const startAuth = async () => { 42 + const handle = handlValueRef.current; 43 + await client.current!.signIn(handle, { 44 + state: "this-is-state", 45 + redirect_uri, 46 + scope, 47 + }); 48 + }; 49 + 50 + return ( 51 + <> 52 + <TextInput 53 + onChangeText={(text) => (handlValueRef.current = text)} 54 + placeholder="Enter your Bluesky Handle" 55 + /> 56 + <Button title="Login with Bluesky" onPress={startAuth} /> 57 + </> 58 + ); 59 + }
+65
app/music.tsx
···
··· 1 + import { Agent } from "@atproto/api"; 2 + import { 3 + BrowserOAuthClient, 4 + type OAuthSession, 5 + } from "@atproto/oauth-client-browser"; 6 + import * as DocumentPicker from "expo-document-picker"; 7 + import { useRouter } from "expo-router"; 8 + import { useEffect, useRef, useState } from "react"; 9 + import { Button } from "react-native"; 10 + 11 + const redirect_uri = "http://127.0.0.1:8081"; 12 + const scope = "atproto transition:generic"; 13 + 14 + export default function MusicScreen() { 15 + const client = useRef<BrowserOAuthClient>(null); 16 + const [session, setSession] = useState<OAuthSession | null>(null); 17 + const router = useRouter(); 18 + 19 + useEffect(() => { 20 + if (client.current) return; 21 + 22 + async function initializeClient() { 23 + client.current = new BrowserOAuthClient({ 24 + handleResolver: "https://bsky.social", 25 + // Note that the origin of the "client_id" URL must be "http://localhost" when 26 + // using this configuration, regardless of the actual hostname ("127.0.0.1" or 27 + // "[::1]"), port or pathname. Only the `redirect_uris` must contain the 28 + // actual url that will be used to redirect the user back to the application. 29 + clientMetadata: { 30 + client_id: `http://localhost?redirect_uri=${encodeURIComponent( 31 + redirect_uri 32 + )}&scope=${encodeURIComponent(scope)}`, 33 + redirect_uris: [redirect_uri], 34 + scope, 35 + token_endpoint_auth_method: "none", 36 + }, 37 + }); 38 + const res = await client.current!.init(); 39 + if (!res?.session) { 40 + router.replace("/"); 41 + } else { 42 + setSession(res.session); 43 + } 44 + } 45 + initializeClient().catch(console.error); 46 + }, [router]); 47 + 48 + const uploadMusic = async () => { 49 + if (!session) return; 50 + 51 + const doc = await DocumentPicker.getDocumentAsync({ type: "audio/*" }); 52 + const agent = new Agent(session); 53 + const blob = await agent.com.atproto.repo.uploadBlob( 54 + await doc?.assets?.[0]?.file?.bytes() 55 + ); 56 + await agent.com.atproto.repo.putRecord({ 57 + repo: session.did, 58 + collection: "social.drips.music.track", 59 + rkey: "test-track", 60 + record: { blob: blob.data.blob }, 61 + }); 62 + }; 63 + 64 + return <Button onPress={uploadMusic} title="Musik hochladen" />; 65 + }
+380 -1
package-lock.json
··· 8 "name": "atproto-music-poc", 9 "version": "1.0.0", 10 "dependencies": { 11 "@expo/metro-runtime": "~6.1.2", 12 "@expo/vector-icons": "^15.0.2", 13 "@react-navigation/bottom-tabs": "^7.4.0", 14 "@react-navigation/elements": "^2.6.3", 15 "@react-navigation/native": "^7.1.8", 16 "expo": "~54.0.10", 17 "expo-constants": "~18.0.9", 18 "expo-font": "~14.0.8", 19 "expo-haptics": "~15.0.7", 20 "expo-image": "~3.0.8", ··· 56 } 57 } 58 }, 59 "node_modules/@babel/code-frame": { 60 "version": "7.27.1", 61 "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", ··· 2637 "@tybys/wasm-util": "^0.10.0" 2638 } 2639 }, 2640 "node_modules/@nodelib/fs.scandir": { 2641 "version": "2.1.5", 2642 "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", ··· 4677 "url": "https://github.com/sponsors/ljharb" 4678 } 4679 }, 4680 "node_modules/babel-jest": { 4681 "version": "29.7.0", 4682 "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", ··· 6657 "react-native": "*" 6658 } 6659 }, 6660 "node_modules/expo-constants": { 6661 "version": "18.0.9", 6662 "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.9.tgz", ··· 6671 "react-native": "*" 6672 } 6673 }, 6674 "node_modules/expo-file-system": { 6675 "version": "19.0.15", 6676 "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.15.tgz", ··· 7727 "version": "1.4.0", 7728 "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", 7729 "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", 7730 - "dev": true, 7731 "license": "MIT" 7732 }, 7733 "node_modules/has-bigints": { ··· 8526 "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 8527 "license": "ISC" 8528 }, 8529 "node_modules/istanbul-lib-coverage": { 8530 "version": "3.2.2", 8531 "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", ··· 8762 "resolved": "https://registry.npmjs.org/jimp-compact/-/jimp-compact-0.16.1.tgz", 8763 "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==", 8764 "license": "MIT" 8765 }, 8766 "node_modules/js-tokens": { 8767 "version": "4.0.0", ··· 9754 "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 9755 "license": "MIT" 9756 }, 9757 "node_modules/mz": { 9758 "version": "2.7.0", 9759 "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", ··· 12436 "url": "https://github.com/sponsors/jonschlinkert" 12437 } 12438 }, 12439 "node_modules/tmpl": { 12440 "version": "1.0.5", 12441 "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", ··· 12667 }, 12668 "engines": { 12669 "node": "*" 12670 } 12671 }, 12672 "node_modules/unbox-primitive": {
··· 8 "name": "atproto-music-poc", 9 "version": "1.0.0", 10 "dependencies": { 11 + "@atproto/api": "^0.16.11", 12 + "@atproto/identity": "^0.4.9", 13 + "@atproto/oauth-client-browser": "^0.3.32", 14 "@expo/metro-runtime": "~6.1.2", 15 "@expo/vector-icons": "^15.0.2", 16 "@react-navigation/bottom-tabs": "^7.4.0", 17 "@react-navigation/elements": "^2.6.3", 18 "@react-navigation/native": "^7.1.8", 19 "expo": "~54.0.10", 20 + "expo-audio": "^1.0.13", 21 + "expo-auth-session": "^7.0.8", 22 "expo-constants": "~18.0.9", 23 + "expo-document-picker": "^14.0.7", 24 "expo-font": "~14.0.8", 25 "expo-haptics": "~15.0.7", 26 "expo-image": "~3.0.8", ··· 62 } 63 } 64 }, 65 + "node_modules/@atproto-labs/did-resolver": { 66 + "version": "0.2.1", 67 + "resolved": "https://registry.npmjs.org/@atproto-labs/did-resolver/-/did-resolver-0.2.1.tgz", 68 + "integrity": "sha512-zSoHyqwwRYUtMNLW+RrWsImt1U5S47nJv5FfmAXTmon6wVKjxKD/PFrD1pg/4G6THqJmQHTs1Hj+54XVupYnvQ==", 69 + "license": "MIT", 70 + "dependencies": { 71 + "@atproto-labs/fetch": "0.2.3", 72 + "@atproto-labs/pipe": "0.1.1", 73 + "@atproto-labs/simple-store": "0.3.0", 74 + "@atproto-labs/simple-store-memory": "0.1.4", 75 + "@atproto/did": "0.2.0", 76 + "zod": "^3.23.8" 77 + } 78 + }, 79 + "node_modules/@atproto-labs/fetch": { 80 + "version": "0.2.3", 81 + "resolved": "https://registry.npmjs.org/@atproto-labs/fetch/-/fetch-0.2.3.tgz", 82 + "integrity": "sha512-NZtbJOCbxKUFRFKMpamT38PUQMY0hX0p7TG5AEYOPhZKZEP7dHZ1K2s1aB8MdVH0qxmqX7nQleNrrvLf09Zfdw==", 83 + "license": "MIT", 84 + "dependencies": { 85 + "@atproto-labs/pipe": "0.1.1" 86 + } 87 + }, 88 + "node_modules/@atproto-labs/handle-resolver": { 89 + "version": "0.3.1", 90 + "resolved": "https://registry.npmjs.org/@atproto-labs/handle-resolver/-/handle-resolver-0.3.1.tgz", 91 + "integrity": "sha512-mLZdMNvwomgnn9sffKO1/xr02ctgeiT0FUVw7JekbchTckub2RM7qMu8Rw1mC4bpCpW+i7DXDiOxpoajkppwYQ==", 92 + "license": "MIT", 93 + "dependencies": { 94 + "@atproto-labs/simple-store": "0.3.0", 95 + "@atproto-labs/simple-store-memory": "0.1.4", 96 + "@atproto/did": "0.2.0", 97 + "zod": "^3.23.8" 98 + } 99 + }, 100 + "node_modules/@atproto-labs/identity-resolver": { 101 + "version": "0.3.1", 102 + "resolved": "https://registry.npmjs.org/@atproto-labs/identity-resolver/-/identity-resolver-0.3.1.tgz", 103 + "integrity": "sha512-jCgotRRqPykPwh4gh0FBLOqeofv1G8OH/DZ5s88HWm7biUZeksZwDrEvL5TnqEFUpXT3O9Hcyp/XEpfCAplRoQ==", 104 + "license": "MIT", 105 + "dependencies": { 106 + "@atproto-labs/did-resolver": "0.2.1", 107 + "@atproto-labs/handle-resolver": "0.3.1" 108 + } 109 + }, 110 + "node_modules/@atproto-labs/pipe": { 111 + "version": "0.1.1", 112 + "resolved": "https://registry.npmjs.org/@atproto-labs/pipe/-/pipe-0.1.1.tgz", 113 + "integrity": "sha512-hdNw2oUs2B6BN1lp+32pF7cp8EMKuIN5Qok2Vvv/aOpG/3tNSJ9YkvfI0k6Zd188LeDDYRUpYpxcoFIcGH/FNg==", 114 + "license": "MIT" 115 + }, 116 + "node_modules/@atproto-labs/simple-store": { 117 + "version": "0.3.0", 118 + "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store/-/simple-store-0.3.0.tgz", 119 + "integrity": "sha512-nOb6ONKBRJHRlukW1sVawUkBqReLlLx6hT35VS3imaNPwiXDxLnTK7lxw3Lrl9k5yugSBDQAkZAq3MPTEFSUBQ==", 120 + "license": "MIT" 121 + }, 122 + "node_modules/@atproto-labs/simple-store-memory": { 123 + "version": "0.1.4", 124 + "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store-memory/-/simple-store-memory-0.1.4.tgz", 125 + "integrity": "sha512-3mKY4dP8I7yKPFj9VKpYyCRzGJOi5CEpOLPlRhoJyLmgs3J4RzDrjn323Oakjz2Aj2JzRU/AIvWRAZVhpYNJHw==", 126 + "license": "MIT", 127 + "dependencies": { 128 + "@atproto-labs/simple-store": "0.3.0", 129 + "lru-cache": "^10.2.0" 130 + } 131 + }, 132 + "node_modules/@atproto-labs/simple-store-memory/node_modules/lru-cache": { 133 + "version": "10.4.3", 134 + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", 135 + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", 136 + "license": "ISC" 137 + }, 138 + "node_modules/@atproto/api": { 139 + "version": "0.16.11", 140 + "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.16.11.tgz", 141 + "integrity": "sha512-1dhfQNHiclb102RW+Ea8Nft5olfqU0Ev/vlQaSX6mWNo1aP5zT+sPODJ8+BTUOYk3vcuvL7QMkqA/rLYy2PMyw==", 142 + "license": "MIT", 143 + "dependencies": { 144 + "@atproto/common-web": "^0.4.3", 145 + "@atproto/lexicon": "^0.5.1", 146 + "@atproto/syntax": "^0.4.1", 147 + "@atproto/xrpc": "^0.7.5", 148 + "await-lock": "^2.2.2", 149 + "multiformats": "^9.9.0", 150 + "tlds": "^1.234.0", 151 + "zod": "^3.23.8" 152 + } 153 + }, 154 + "node_modules/@atproto/common-web": { 155 + "version": "0.4.3", 156 + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.3.tgz", 157 + "integrity": "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg==", 158 + "license": "MIT", 159 + "dependencies": { 160 + "graphemer": "^1.4.0", 161 + "multiformats": "^9.9.0", 162 + "uint8arrays": "3.0.0", 163 + "zod": "^3.23.8" 164 + } 165 + }, 166 + "node_modules/@atproto/crypto": { 167 + "version": "0.4.4", 168 + "resolved": "https://registry.npmjs.org/@atproto/crypto/-/crypto-0.4.4.tgz", 169 + "integrity": "sha512-Yq9+crJ7WQl7sxStVpHgie5Z51R05etaK9DLWYG/7bR5T4bhdcIgF6IfklLShtZwLYdVVj+K15s0BqW9a8PSDA==", 170 + "license": "MIT", 171 + "dependencies": { 172 + "@noble/curves": "^1.7.0", 173 + "@noble/hashes": "^1.6.1", 174 + "uint8arrays": "3.0.0" 175 + }, 176 + "engines": { 177 + "node": ">=18.7.0" 178 + } 179 + }, 180 + "node_modules/@atproto/did": { 181 + "version": "0.2.0", 182 + "resolved": "https://registry.npmjs.org/@atproto/did/-/did-0.2.0.tgz", 183 + "integrity": "sha512-BskT39KYbwY1DUsWekkHh47xS+wvJpFq5F9acsicNfYniinyAMnNTzGKQEhnjQuG7K0qQItg/SnmC+y0tJXV7Q==", 184 + "license": "MIT", 185 + "dependencies": { 186 + "zod": "^3.23.8" 187 + } 188 + }, 189 + "node_modules/@atproto/identity": { 190 + "version": "0.4.9", 191 + "resolved": "https://registry.npmjs.org/@atproto/identity/-/identity-0.4.9.tgz", 192 + "integrity": "sha512-pRYCaeaEJMZ4vQlRQYYTrF3cMiRp21n/k/pUT1o7dgKby56zuLErDmFXkbKfKWPf7SgWRgamSaNmsGLqAOD7lQ==", 193 + "license": "MIT", 194 + "dependencies": { 195 + "@atproto/common-web": "^0.4.3", 196 + "@atproto/crypto": "^0.4.4" 197 + }, 198 + "engines": { 199 + "node": ">=18.7.0" 200 + } 201 + }, 202 + "node_modules/@atproto/jwk": { 203 + "version": "0.5.0", 204 + "resolved": "https://registry.npmjs.org/@atproto/jwk/-/jwk-0.5.0.tgz", 205 + "integrity": "sha512-Qi2NtEqhkG+uz3CKia4+H05WMV/z//dz3ESo5+cyBKrOnxVTJ5ZubMyltWjoYvy6v/jLhorXdDWcjn07yky7MQ==", 206 + "license": "MIT", 207 + "dependencies": { 208 + "multiformats": "^9.9.0", 209 + "zod": "^3.23.8" 210 + } 211 + }, 212 + "node_modules/@atproto/jwk-jose": { 213 + "version": "0.1.10", 214 + "resolved": "https://registry.npmjs.org/@atproto/jwk-jose/-/jwk-jose-0.1.10.tgz", 215 + "integrity": "sha512-Eiu/u4tZHz3IIhHZt0zneYEffSAO3Oqk/ToKwlu1TqKte6sjtPs/4uquSiAAGFYozqgo92JC/AQclWzzkHI5QQ==", 216 + "license": "MIT", 217 + "dependencies": { 218 + "@atproto/jwk": "0.5.0", 219 + "jose": "^5.2.0" 220 + } 221 + }, 222 + "node_modules/@atproto/jwk-webcrypto": { 223 + "version": "0.1.10", 224 + "resolved": "https://registry.npmjs.org/@atproto/jwk-webcrypto/-/jwk-webcrypto-0.1.10.tgz", 225 + "integrity": "sha512-JZsavs6JiSmw5rgcjkGDwzr1aCJGdybZOjVfYH+m9sXRU1BrUCA30uwNfZY7eFyWXyRAnCFiYiGVZgypXyKotw==", 226 + "license": "MIT", 227 + "dependencies": { 228 + "@atproto/jwk": "0.5.0", 229 + "@atproto/jwk-jose": "0.1.10", 230 + "zod": "^3.23.8" 231 + } 232 + }, 233 + "node_modules/@atproto/lexicon": { 234 + "version": "0.5.1", 235 + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.5.1.tgz", 236 + "integrity": "sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A==", 237 + "license": "MIT", 238 + "dependencies": { 239 + "@atproto/common-web": "^0.4.3", 240 + "@atproto/syntax": "^0.4.1", 241 + "iso-datestring-validator": "^2.2.2", 242 + "multiformats": "^9.9.0", 243 + "zod": "^3.23.8" 244 + } 245 + }, 246 + "node_modules/@atproto/oauth-client": { 247 + "version": "0.5.6", 248 + "resolved": "https://registry.npmjs.org/@atproto/oauth-client/-/oauth-client-0.5.6.tgz", 249 + "integrity": "sha512-O1S9lPptJxWPcNd2kODaLgWntz+A7PzskU2hP4IFa7hVLs4aEnEt9dKq5wJE97tDli8mgyh/ndPQhxUaCVQ5iQ==", 250 + "license": "MIT", 251 + "dependencies": { 252 + "@atproto-labs/did-resolver": "0.2.1", 253 + "@atproto-labs/fetch": "0.2.3", 254 + "@atproto-labs/handle-resolver": "0.3.1", 255 + "@atproto-labs/identity-resolver": "0.3.1", 256 + "@atproto-labs/simple-store": "0.3.0", 257 + "@atproto-labs/simple-store-memory": "0.1.4", 258 + "@atproto/did": "0.2.0", 259 + "@atproto/jwk": "0.5.0", 260 + "@atproto/oauth-types": "0.4.1", 261 + "@atproto/xrpc": "0.7.5", 262 + "multiformats": "^9.9.0", 263 + "zod": "^3.23.8" 264 + } 265 + }, 266 + "node_modules/@atproto/oauth-client-browser": { 267 + "version": "0.3.32", 268 + "resolved": "https://registry.npmjs.org/@atproto/oauth-client-browser/-/oauth-client-browser-0.3.32.tgz", 269 + "integrity": "sha512-h6Rsa/LgMnugaVKkMtHbQ1DSlhhIhL4HbjW1egg7z0BQdWJzczJ8nX3guHN5r/YioL6vOigxbMim+p/Z1LeG9g==", 270 + "license": "MIT", 271 + "dependencies": { 272 + "@atproto-labs/did-resolver": "0.2.1", 273 + "@atproto-labs/handle-resolver": "0.3.1", 274 + "@atproto-labs/simple-store": "0.3.0", 275 + "@atproto/did": "0.2.0", 276 + "@atproto/jwk": "0.5.0", 277 + "@atproto/jwk-webcrypto": "0.1.10", 278 + "@atproto/oauth-client": "0.5.6", 279 + "@atproto/oauth-types": "0.4.1" 280 + } 281 + }, 282 + "node_modules/@atproto/oauth-types": { 283 + "version": "0.4.1", 284 + "resolved": "https://registry.npmjs.org/@atproto/oauth-types/-/oauth-types-0.4.1.tgz", 285 + "integrity": "sha512-c5ixf2ZOzcltOu1fDBnO/tok6Wj7JDDK66+Z0q/+bAr8LXgOnxP7zQfJ+DD4gTkB+saTqsqWtVv8qvx/IEtm1g==", 286 + "license": "MIT", 287 + "dependencies": { 288 + "@atproto/jwk": "0.5.0", 289 + "zod": "^3.23.8" 290 + } 291 + }, 292 + "node_modules/@atproto/syntax": { 293 + "version": "0.4.1", 294 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.1.tgz", 295 + "integrity": "sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw==", 296 + "license": "MIT" 297 + }, 298 + "node_modules/@atproto/xrpc": { 299 + "version": "0.7.5", 300 + "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.5.tgz", 301 + "integrity": "sha512-MUYNn5d2hv8yVegRL0ccHvTHAVj5JSnW07bkbiaz96UH45lvYNRVwt44z+yYVnb0/mvBzyD3/ZQ55TRGt7fHkA==", 302 + "license": "MIT", 303 + "dependencies": { 304 + "@atproto/lexicon": "^0.5.1", 305 + "zod": "^3.23.8" 306 + } 307 + }, 308 "node_modules/@babel/code-frame": { 309 "version": "7.27.1", 310 "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", ··· 2886 "@tybys/wasm-util": "^0.10.0" 2887 } 2888 }, 2889 + "node_modules/@noble/curves": { 2890 + "version": "1.9.7", 2891 + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", 2892 + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", 2893 + "license": "MIT", 2894 + "dependencies": { 2895 + "@noble/hashes": "1.8.0" 2896 + }, 2897 + "engines": { 2898 + "node": "^14.21.3 || >=16" 2899 + }, 2900 + "funding": { 2901 + "url": "https://paulmillr.com/funding/" 2902 + } 2903 + }, 2904 + "node_modules/@noble/hashes": { 2905 + "version": "1.8.0", 2906 + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", 2907 + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", 2908 + "license": "MIT", 2909 + "engines": { 2910 + "node": "^14.21.3 || >=16" 2911 + }, 2912 + "funding": { 2913 + "url": "https://paulmillr.com/funding/" 2914 + } 2915 + }, 2916 "node_modules/@nodelib/fs.scandir": { 2917 "version": "2.1.5", 2918 "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", ··· 4953 "url": "https://github.com/sponsors/ljharb" 4954 } 4955 }, 4956 + "node_modules/await-lock": { 4957 + "version": "2.2.2", 4958 + "resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz", 4959 + "integrity": "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==", 4960 + "license": "MIT" 4961 + }, 4962 "node_modules/babel-jest": { 4963 "version": "29.7.0", 4964 "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", ··· 6939 "react-native": "*" 6940 } 6941 }, 6942 + "node_modules/expo-audio": { 6943 + "version": "1.0.13", 6944 + "resolved": "https://registry.npmjs.org/expo-audio/-/expo-audio-1.0.13.tgz", 6945 + "integrity": "sha512-Z91jMYv/BMpn5Dkyu204RyNl2EpXRx8HgScTYlwRRYewtfTH8NN+bwZQAApn33NwGhW1cIlcuG/XCAeJBr9l4Q==", 6946 + "license": "MIT", 6947 + "peerDependencies": { 6948 + "expo": "*", 6949 + "react": "*", 6950 + "react-native": "*" 6951 + } 6952 + }, 6953 + "node_modules/expo-auth-session": { 6954 + "version": "7.0.8", 6955 + "resolved": "https://registry.npmjs.org/expo-auth-session/-/expo-auth-session-7.0.8.tgz", 6956 + "integrity": "sha512-kpo2Jva+6uVjk6TmNqWAoqTnULXZaEVa9l4uf8JH32uDMt/iZQhM0fauy7Ww+y910Euhv5djCP7cPj8KWv6cmQ==", 6957 + "license": "MIT", 6958 + "dependencies": { 6959 + "expo-application": "~7.0.7", 6960 + "expo-constants": "~18.0.8", 6961 + "expo-crypto": "~15.0.7", 6962 + "expo-linking": "~8.0.8", 6963 + "expo-web-browser": "~15.0.7", 6964 + "invariant": "^2.2.4" 6965 + }, 6966 + "peerDependencies": { 6967 + "react": "*", 6968 + "react-native": "*" 6969 + } 6970 + }, 6971 + "node_modules/expo-auth-session/node_modules/expo-application": { 6972 + "version": "7.0.7", 6973 + "resolved": "https://registry.npmjs.org/expo-application/-/expo-application-7.0.7.tgz", 6974 + "integrity": "sha512-Jt1/qqnoDUbZ+bK91+dHaZ1vrPDtRBOltRa681EeedkisqguuEeUx4UHqwVyDK2oHWsK6lO3ojetoA4h8OmNcg==", 6975 + "license": "MIT", 6976 + "peerDependencies": { 6977 + "expo": "*" 6978 + } 6979 + }, 6980 + "node_modules/expo-auth-session/node_modules/expo-crypto": { 6981 + "version": "15.0.7", 6982 + "resolved": "https://registry.npmjs.org/expo-crypto/-/expo-crypto-15.0.7.tgz", 6983 + "integrity": "sha512-FUo41TwwGT2e5rA45PsjezI868Ch3M6wbCZsmqTWdF/hr+HyPcrp1L//dsh/hsrsyrQdpY/U96Lu71/wXePJeg==", 6984 + "license": "MIT", 6985 + "dependencies": { 6986 + "base64-js": "^1.3.0" 6987 + }, 6988 + "peerDependencies": { 6989 + "expo": "*" 6990 + } 6991 + }, 6992 "node_modules/expo-constants": { 6993 "version": "18.0.9", 6994 "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.9.tgz", ··· 7003 "react-native": "*" 7004 } 7005 }, 7006 + "node_modules/expo-document-picker": { 7007 + "version": "14.0.7", 7008 + "resolved": "https://registry.npmjs.org/expo-document-picker/-/expo-document-picker-14.0.7.tgz", 7009 + "integrity": "sha512-81Jh8RDD0GYBUoSTmIBq30hXXjmkDV1ZY2BNIp1+3HR5PDSh2WmdhD/Ezz5YFsv46hIXHsQc+Kh1q8vn6OLT9Q==", 7010 + "license": "MIT", 7011 + "peerDependencies": { 7012 + "expo": "*" 7013 + } 7014 + }, 7015 "node_modules/expo-file-system": { 7016 "version": "19.0.15", 7017 "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.15.tgz", ··· 8068 "version": "1.4.0", 8069 "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", 8070 "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", 8071 "license": "MIT" 8072 }, 8073 "node_modules/has-bigints": { ··· 8866 "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 8867 "license": "ISC" 8868 }, 8869 + "node_modules/iso-datestring-validator": { 8870 + "version": "2.2.2", 8871 + "resolved": "https://registry.npmjs.org/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz", 8872 + "integrity": "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==", 8873 + "license": "MIT" 8874 + }, 8875 "node_modules/istanbul-lib-coverage": { 8876 "version": "3.2.2", 8877 "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", ··· 9108 "resolved": "https://registry.npmjs.org/jimp-compact/-/jimp-compact-0.16.1.tgz", 9109 "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==", 9110 "license": "MIT" 9111 + }, 9112 + "node_modules/jose": { 9113 + "version": "5.10.0", 9114 + "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", 9115 + "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", 9116 + "license": "MIT", 9117 + "funding": { 9118 + "url": "https://github.com/sponsors/panva" 9119 + } 9120 }, 9121 "node_modules/js-tokens": { 9122 "version": "4.0.0", ··· 10109 "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 10110 "license": "MIT" 10111 }, 10112 + "node_modules/multiformats": { 10113 + "version": "9.9.0", 10114 + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", 10115 + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 10116 + "license": "(Apache-2.0 AND MIT)" 10117 + }, 10118 "node_modules/mz": { 10119 "version": "2.7.0", 10120 "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", ··· 12797 "url": "https://github.com/sponsors/jonschlinkert" 12798 } 12799 }, 12800 + "node_modules/tlds": { 12801 + "version": "1.260.0", 12802 + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.260.0.tgz", 12803 + "integrity": "sha512-78+28EWBhCEE7qlyaHA9OR3IPvbCLiDh3Ckla593TksfFc9vfTsgvH7eS+dr3o9qr31gwGbogcI16yN91PoRjQ==", 12804 + "license": "MIT", 12805 + "bin": { 12806 + "tlds": "bin.js" 12807 + } 12808 + }, 12809 "node_modules/tmpl": { 12810 "version": "1.0.5", 12811 "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", ··· 13037 }, 13038 "engines": { 13039 "node": "*" 13040 + } 13041 + }, 13042 + "node_modules/uint8arrays": { 13043 + "version": "3.0.0", 13044 + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz", 13045 + "integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==", 13046 + "license": "MIT", 13047 + "dependencies": { 13048 + "multiformats": "^9.4.2" 13049 } 13050 }, 13051 "node_modules/unbox-primitive": {
+11 -5
package.json
··· 11 "lint": "expo lint" 12 }, 13 "dependencies": { 14 - "@expo/vector-icons": "^15.0.2", 15 "@expo/metro-runtime": "~6.1.2", 16 "@react-navigation/bottom-tabs": "^7.4.0", 17 "@react-navigation/elements": "^2.6.3", 18 "@react-navigation/native": "^7.1.8", 19 "expo": "~54.0.10", 20 "expo-constants": "~18.0.9", 21 "expo-font": "~14.0.8", 22 "expo-haptics": "~15.0.7", 23 "expo-image": "~3.0.8", ··· 32 "react-dom": "19.1.0", 33 "react-native": "0.81.4", 34 "react-native-gesture-handler": "~2.28.0", 35 - "react-native-worklets": "0.5.1", 36 "react-native-reanimated": "~4.1.1", 37 "react-native-safe-area-context": "~5.6.0", 38 "react-native-screens": "~4.16.0", 39 - "react-native-web": "~0.21.0" 40 }, 41 "devDependencies": { 42 "@types/react": "~19.1.0", 43 - "typescript": "~5.9.2", 44 "eslint": "^9.25.0", 45 - "eslint-config-expo": "~10.0.0" 46 }, 47 "private": true 48 }
··· 11 "lint": "expo lint" 12 }, 13 "dependencies": { 14 + "@atproto/api": "^0.16.11", 15 + "@atproto/identity": "^0.4.9", 16 + "@atproto/oauth-client-browser": "^0.3.32", 17 "@expo/metro-runtime": "~6.1.2", 18 + "@expo/vector-icons": "^15.0.2", 19 "@react-navigation/bottom-tabs": "^7.4.0", 20 "@react-navigation/elements": "^2.6.3", 21 "@react-navigation/native": "^7.1.8", 22 "expo": "~54.0.10", 23 + "expo-audio": "^1.0.13", 24 + "expo-auth-session": "^7.0.8", 25 "expo-constants": "~18.0.9", 26 + "expo-document-picker": "^14.0.7", 27 "expo-font": "~14.0.8", 28 "expo-haptics": "~15.0.7", 29 "expo-image": "~3.0.8", ··· 38 "react-dom": "19.1.0", 39 "react-native": "0.81.4", 40 "react-native-gesture-handler": "~2.28.0", 41 "react-native-reanimated": "~4.1.1", 42 "react-native-safe-area-context": "~5.6.0", 43 "react-native-screens": "~4.16.0", 44 + "react-native-web": "~0.21.0", 45 + "react-native-worklets": "0.5.1" 46 }, 47 "devDependencies": { 48 "@types/react": "~19.1.0", 49 "eslint": "^9.25.0", 50 + "eslint-config-expo": "~10.0.0", 51 + "typescript": "~5.9.2" 52 }, 53 "private": true 54 }