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 - }; 1 + import { Stack } from "expo-router"; 2 + import "react-native-reanimated"; 11 3 12 4 export default function RootLayout() { 13 - const colorScheme = useColorScheme(); 14 - 15 5 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> 6 + <Stack> 7 + <Stack.Screen name="home" options={{ headerShown: false }} /> 8 + <Stack.Screen name="music" /> 9 + </Stack> 23 10 ); 24 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 8 "name": "atproto-music-poc", 9 9 "version": "1.0.0", 10 10 "dependencies": { 11 + "@atproto/api": "^0.16.11", 12 + "@atproto/identity": "^0.4.9", 13 + "@atproto/oauth-client-browser": "^0.3.32", 11 14 "@expo/metro-runtime": "~6.1.2", 12 15 "@expo/vector-icons": "^15.0.2", 13 16 "@react-navigation/bottom-tabs": "^7.4.0", 14 17 "@react-navigation/elements": "^2.6.3", 15 18 "@react-navigation/native": "^7.1.8", 16 19 "expo": "~54.0.10", 20 + "expo-audio": "^1.0.13", 21 + "expo-auth-session": "^7.0.8", 17 22 "expo-constants": "~18.0.9", 23 + "expo-document-picker": "^14.0.7", 18 24 "expo-font": "~14.0.8", 19 25 "expo-haptics": "~15.0.7", 20 26 "expo-image": "~3.0.8", ··· 56 62 } 57 63 } 58 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 + }, 59 308 "node_modules/@babel/code-frame": { 60 309 "version": "7.27.1", 61 310 "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", ··· 2637 2886 "@tybys/wasm-util": "^0.10.0" 2638 2887 } 2639 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 + }, 2640 2916 "node_modules/@nodelib/fs.scandir": { 2641 2917 "version": "2.1.5", 2642 2918 "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", ··· 4677 4953 "url": "https://github.com/sponsors/ljharb" 4678 4954 } 4679 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 + }, 4680 4962 "node_modules/babel-jest": { 4681 4963 "version": "29.7.0", 4682 4964 "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", ··· 6657 6939 "react-native": "*" 6658 6940 } 6659 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 + }, 6660 6992 "node_modules/expo-constants": { 6661 6993 "version": "18.0.9", 6662 6994 "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.9.tgz", ··· 6671 7003 "react-native": "*" 6672 7004 } 6673 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 + }, 6674 7015 "node_modules/expo-file-system": { 6675 7016 "version": "19.0.15", 6676 7017 "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.15.tgz", ··· 7727 8068 "version": "1.4.0", 7728 8069 "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", 7729 8070 "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", 7730 - "dev": true, 7731 8071 "license": "MIT" 7732 8072 }, 7733 8073 "node_modules/has-bigints": { ··· 8526 8866 "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 8527 8867 "license": "ISC" 8528 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 + }, 8529 8875 "node_modules/istanbul-lib-coverage": { 8530 8876 "version": "3.2.2", 8531 8877 "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", ··· 8762 9108 "resolved": "https://registry.npmjs.org/jimp-compact/-/jimp-compact-0.16.1.tgz", 8763 9109 "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==", 8764 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 + } 8765 9120 }, 8766 9121 "node_modules/js-tokens": { 8767 9122 "version": "4.0.0", ··· 9754 10109 "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 9755 10110 "license": "MIT" 9756 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 + }, 9757 10118 "node_modules/mz": { 9758 10119 "version": "2.7.0", 9759 10120 "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", ··· 12436 12797 "url": "https://github.com/sponsors/jonschlinkert" 12437 12798 } 12438 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 + }, 12439 12809 "node_modules/tmpl": { 12440 12810 "version": "1.0.5", 12441 12811 "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", ··· 12667 13037 }, 12668 13038 "engines": { 12669 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" 12670 13049 } 12671 13050 }, 12672 13051 "node_modules/unbox-primitive": {
+11 -5
package.json
··· 11 11 "lint": "expo lint" 12 12 }, 13 13 "dependencies": { 14 - "@expo/vector-icons": "^15.0.2", 14 + "@atproto/api": "^0.16.11", 15 + "@atproto/identity": "^0.4.9", 16 + "@atproto/oauth-client-browser": "^0.3.32", 15 17 "@expo/metro-runtime": "~6.1.2", 18 + "@expo/vector-icons": "^15.0.2", 16 19 "@react-navigation/bottom-tabs": "^7.4.0", 17 20 "@react-navigation/elements": "^2.6.3", 18 21 "@react-navigation/native": "^7.1.8", 19 22 "expo": "~54.0.10", 23 + "expo-audio": "^1.0.13", 24 + "expo-auth-session": "^7.0.8", 20 25 "expo-constants": "~18.0.9", 26 + "expo-document-picker": "^14.0.7", 21 27 "expo-font": "~14.0.8", 22 28 "expo-haptics": "~15.0.7", 23 29 "expo-image": "~3.0.8", ··· 32 38 "react-dom": "19.1.0", 33 39 "react-native": "0.81.4", 34 40 "react-native-gesture-handler": "~2.28.0", 35 - "react-native-worklets": "0.5.1", 36 41 "react-native-reanimated": "~4.1.1", 37 42 "react-native-safe-area-context": "~5.6.0", 38 43 "react-native-screens": "~4.16.0", 39 - "react-native-web": "~0.21.0" 44 + "react-native-web": "~0.21.0", 45 + "react-native-worklets": "0.5.1" 40 46 }, 41 47 "devDependencies": { 42 48 "@types/react": "~19.1.0", 43 - "typescript": "~5.9.2", 44 49 "eslint": "^9.25.0", 45 - "eslint-config-expo": "~10.0.0" 50 + "eslint-config-expo": "~10.0.0", 51 + "typescript": "~5.9.2" 46 52 }, 47 53 "private": true 48 54 }