frontend client for gemstone. decentralised workplace app
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: atcute, restructure project

serenity bc28e38c 16cdcb45

+331 -53
-21
app/[slug].tsx
··· 1 - import ChatComponentProfiled from "@/app/components/ChatComponentProfiled"; 2 - import { capitalise } from "@/app/lib/utils/strings"; 3 - import { useLocalSearchParams } from "expo-router"; 4 - import { Text, View } from "react-native"; 5 - 6 - export default function Index() { 7 - const { slug } = useLocalSearchParams(); 8 - const name = typeof slug === "string" ? slug : slug[0]; 9 - return ( 10 - <View 11 - style={{ 12 - flex: 1, 13 - justifyContent: "center", 14 - alignItems: "center", 15 - }} 16 - > 17 - <Text>Hi, {capitalise(name)}!</Text> 18 - <ChatComponentProfiled /> 19 - </View> 20 - ); 21 - }
-5
app/_layout.tsx
··· 1 - import { Stack } from "expo-router"; 2 - 3 - export default function RootLayout() { 4 - return <Stack />; 5 - }
+1 -1
app/components/ChatComponent.tsx src/components/ChatComponent.tsx
··· 1 - import { useWebSocket } from "@/app/hooks/useWebSocket"; 1 + import { useWebSocket } from "@/hooks/useWebSocket"; 2 2 import { useState } from "react"; 3 3 import { 4 4 View,
+22 -3
app/components/ChatComponentProfiled.tsx src/components/ChatComponentProfiled.tsx
··· 1 - import { useWebSocket } from "@/app/hooks/useWebSocket"; 1 + import { Loading } from "@/components/Loading"; 2 + import { useWebSocket } from "@/hooks/useWebSocket"; 3 + import type { DidPlc } from "@/lib/types/atproto"; 4 + import { getBskyProfile } from "@/queries/get-profile"; 5 + import { useQuery } from "@tanstack/react-query"; 2 6 import { useState } from "react"; 3 7 import { 4 8 View, ··· 9 13 ScrollView, 10 14 } from "react-native"; 11 15 12 - export default function ChatComponentProfiled() { 16 + export default function ChatComponentProfiled({ did }: { did: DidPlc }) { 13 17 const [inputText, setInputText] = useState(""); 14 18 const { messages, isConnected, sendMessage } = useWebSocket( 15 19 "ws://localhost:8080", ··· 22 26 } 23 27 }; 24 28 25 - return ( 29 + const { data, isPending, isError, error } = useQuery({ 30 + queryKey: ["profile"], 31 + queryFn: async () => { 32 + return await getBskyProfile(did); 33 + }, 34 + }); 35 + 36 + return isPending ? ( 37 + <Loading /> 38 + ) : isError ? ( 39 + <View> 40 + <Text>Something went wrong :(</Text> 41 + <Text>{error.message}</Text> 42 + </View> 43 + ) : ( 26 44 <View style={styles.container}> 45 + {data && <Text>Hi, {data.displayName ?? data.handle}!</Text>} 27 46 <View style={styles.header}> 28 47 <Text style={styles.status}> 29 48 {isConnected ? "🟢 Connected" : "🔴 Disconnected"}
+2 -2
app/hooks/useWebSocket.ts src/hooks/useWebSocket.ts
··· 1 - import type { ShardMessage } from "@/app/lib/types/messages"; 1 + import type { ShardMessage } from "@/lib/types/messages"; 2 2 import { 3 3 validateHistoryMessage, 4 4 validateNewMessage, 5 5 validateWsMessageString, 6 6 validateWsMessageType, 7 - } from "@/app/lib/validators"; 7 + } from "@/lib/validators"; 8 8 import { useEffect, useRef, useState } from "react"; 9 9 10 10 export function useWebSocket(url: string) {
+1 -1
app/index.tsx src/app/index.tsx
··· 1 - import ChatComponent from "@/app/components/ChatComponent"; 1 + import ChatComponent from "@/components/ChatComponent"; 2 2 import { View } from "react-native"; 3 3 4 4 export default function Index() {
app/lib/types/messages.ts src/lib/types/messages.ts
app/lib/utils/strings.ts src/lib/utils/strings.ts
+1 -5
app/lib/validators.ts src/lib/validators.ts
··· 1 - import { 2 - historyMessageSchema, 3 - shardMessageSchema, 4 - websocketMessageSchema, 5 - } from "@/app/lib/types/messages"; 1 + import { historyMessageSchema, shardMessageSchema, websocketMessageSchema } from "@/lib/types/messages"; 6 2 import { z } from "zod"; 7 3 8 4 export const validateWsMessageString = (data: unknown) => {
+5
package.json
··· 16 16 "dev:expo": "expo start" 17 17 }, 18 18 "dependencies": { 19 + "@atcute/atproto": "^3.1.7", 20 + "@atcute/bluesky": "^3.2.6", 21 + "@atcute/client": "^4.0.4", 19 22 "@expo/vector-icons": "^15.0.2", 20 23 "@react-navigation/bottom-tabs": "^7.4.0", 21 24 "@react-navigation/elements": "^2.6.3", 22 25 "@react-navigation/native": "^7.1.8", 26 + "@tanstack/react-query": "^5.90.3", 23 27 "expo": "~54.0.12", 24 28 "expo-constants": "~18.0.9", 25 29 "expo-font": "~14.0.8", ··· 39 43 "react-native-reanimated": "~4.1.1", 40 44 "react-native-safe-area-context": "~5.6.0", 41 45 "react-native-screens": "~4.16.0", 46 + "react-native-svg": "^15.14.0", 42 47 "react-native-web": "~0.21.0", 43 48 "react-native-worklets": "0.5.1", 44 49 "zod": "^4.1.12"
+179
pnpm-lock.yaml
··· 8 8 9 9 .: 10 10 dependencies: 11 + '@atcute/atproto': 12 + specifier: ^3.1.7 13 + version: 3.1.7 14 + '@atcute/bluesky': 15 + specifier: ^3.2.6 16 + version: 3.2.6 17 + '@atcute/client': 18 + specifier: ^4.0.4 19 + version: 4.0.4 11 20 '@expo/vector-icons': 12 21 specifier: ^15.0.2 13 22 version: 15.0.2(expo-font@14.0.8(expo@54.0.12)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) ··· 20 29 '@react-navigation/native': 21 30 specifier: ^7.1.8 22 31 version: 7.1.18(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) 32 + '@tanstack/react-query': 33 + specifier: ^5.90.3 34 + version: 5.90.3(react@19.1.0) 23 35 expo: 24 36 specifier: ~54.0.12 25 37 version: 54.0.12(@babel/core@7.28.4)(@expo/metro-runtime@6.1.2)(expo-router@6.0.10)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) ··· 77 89 react-native-screens: 78 90 specifier: ~4.16.0 79 91 version: 4.16.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) 92 + react-native-svg: 93 + specifier: ^15.14.0 94 + version: 15.14.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) 80 95 react-native-web: 81 96 specifier: ~0.21.0 82 97 version: 0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) ··· 119 134 graphql: 120 135 optional: true 121 136 137 + '@atcute/atproto@3.1.7': 138 + resolution: {integrity: sha512-3Ym8qaVZg2vf8qw0KO1aue39z/5oik5J+UDoSes1vr8ddw40UVLA5sV4bXSKmLnhzQHiLLgoVZXe4zaKfozPoQ==} 139 + 140 + '@atcute/bluesky@3.2.6': 141 + resolution: {integrity: sha512-jUSSTW5Th1vy0bWBazVHuhGQ3Xz4cX648WvLNpYDv7WPzlFzIWm6cnQCbUToQ+uK3K4WyVuuqYtZqqI0f4wWUQ==} 142 + 143 + '@atcute/client@4.0.4': 144 + resolution: {integrity: sha512-0vkYe6HcGAef8FS4dlGMqCCPG4I4Lve1R8Amk8UEviUVofiqlv1WGoeez9CJFL8G/7vhcgVV9rPTHLJEjZ4RdQ==} 145 + 146 + '@atcute/identity@1.1.1': 147 + resolution: {integrity: sha512-zax42n693VEhnC+5tndvO2KLDTMkHOz8UExwmklvJv7R9VujfEwiSWhcv6Jgwb3ellaG8wjiQ1lMOIjLLvwh0Q==} 148 + 149 + '@atcute/lexicons@1.2.2': 150 + resolution: {integrity: sha512-bgEhJq5Z70/0TbK5sx+tAkrR8FsCODNiL2gUEvS5PuJfPxmFmRYNWaMGehxSPaXWpU2+Oa9ckceHiYbrItDTkA==} 151 + 122 152 '@babel/code-frame@7.10.4': 123 153 resolution: {integrity: sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==} 124 154 ··· 621 651 '@babel/types@7.28.4': 622 652 resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} 623 653 engines: {node: '>=6.9.0'} 654 + 655 + '@badrap/valita@0.4.6': 656 + resolution: {integrity: sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg==} 657 + engines: {node: '>= 18'} 624 658 625 659 '@egjs/hammerjs@2.0.17': 626 660 resolution: {integrity: sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==} ··· 1247 1281 1248 1282 '@sinonjs/fake-timers@10.3.0': 1249 1283 resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} 1284 + 1285 + '@standard-schema/spec@1.0.0': 1286 + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} 1287 + 1288 + '@tanstack/query-core@5.90.3': 1289 + resolution: {integrity: sha512-HtPOnCwmx4dd35PfXU8jjkhwYrsHfuqgC8RCJIwWglmhIUIlzPP0ZcEkDAc+UtAWCiLm7T8rxeEfHZlz3hYMCA==} 1290 + 1291 + '@tanstack/react-query@5.90.3': 1292 + resolution: {integrity: sha512-i/LRL6DtuhG6bjGzavIMIVuKKPWx2AnEBIsBfuMm3YoHne0a20nWmsatOCBcVSaT0/8/5YFjNkebHAPLVUSi0Q==} 1293 + peerDependencies: 1294 + react: ^18 || ^19 1250 1295 1251 1296 '@tybys/wasm-util@0.10.1': 1252 1297 resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} ··· 1679 1724 resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} 1680 1725 engines: {node: '>=0.6'} 1681 1726 1727 + boolbase@1.0.0: 1728 + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} 1729 + 1682 1730 bplist-creator@0.1.0: 1683 1731 resolution: {integrity: sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==} 1684 1732 ··· 1877 1925 css-in-js-utils@3.1.0: 1878 1926 resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==} 1879 1927 1928 + css-select@5.2.2: 1929 + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} 1930 + 1931 + css-tree@1.1.3: 1932 + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} 1933 + engines: {node: '>=8.0.0'} 1934 + 1935 + css-what@6.2.2: 1936 + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} 1937 + engines: {node: '>= 6'} 1938 + 1880 1939 csstype@3.1.3: 1881 1940 resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 1882 1941 ··· 1966 2025 resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} 1967 2026 engines: {node: '>=0.10.0'} 1968 2027 2028 + dom-serializer@2.0.0: 2029 + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} 2030 + 2031 + domelementtype@2.3.0: 2032 + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} 2033 + 2034 + domhandler@5.0.3: 2035 + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} 2036 + engines: {node: '>= 4'} 2037 + 2038 + domutils@3.2.2: 2039 + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} 2040 + 1969 2041 dotenv-expand@11.0.7: 1970 2042 resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==} 1971 2043 engines: {node: '>=12'} ··· 2000 2072 encodeurl@2.0.0: 2001 2073 resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} 2002 2074 engines: {node: '>= 0.8'} 2075 + 2076 + entities@4.5.0: 2077 + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} 2078 + engines: {node: '>=0.12'} 2003 2079 2004 2080 env-editor@0.4.2: 2005 2081 resolution: {integrity: sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA==} ··· 2153 2229 peerDependenciesMeta: 2154 2230 jiti: 2155 2231 optional: true 2232 + 2233 + esm-env@1.2.2: 2234 + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} 2156 2235 2157 2236 espree@10.4.0: 2158 2237 resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} ··· 3010 3089 resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} 3011 3090 engines: {node: '>= 0.4'} 3012 3091 3092 + mdn-data@2.0.14: 3093 + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} 3094 + 3013 3095 memoize-one@5.2.1: 3014 3096 resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} 3015 3097 ··· 3246 3328 npm-package-arg@11.0.3: 3247 3329 resolution: {integrity: sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==} 3248 3330 engines: {node: ^16.14.0 || >=18.0.0} 3331 + 3332 + nth-check@2.1.1: 3333 + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} 3249 3334 3250 3335 nullthrows@1.1.1: 3251 3336 resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} ··· 3542 3627 react: '*' 3543 3628 react-native: '*' 3544 3629 3630 + react-native-svg@15.14.0: 3631 + resolution: {integrity: sha512-B3gYc7WztcOT4N54AtUutbe0Nuqqh/nkresY0fAXzUHYLsWuIu/yGiCCD3DKfAs6GLv5LFtWTu7N333Q+e3bkg==} 3632 + peerDependencies: 3633 + react: '*' 3634 + react-native: '*' 3635 + 3545 3636 react-native-web@0.21.1: 3546 3637 resolution: {integrity: sha512-BeNsgwwe4AXUFPAoFU+DKjJ+CVQa3h54zYX77p7GVZrXiiNo3vl03WYDYVEy5R2J2HOPInXtQZB5gmj3vuzrKg==} 3547 3638 peerDependencies: ··· 4350 4441 4351 4442 '@0no-co/graphql.web@1.2.0': {} 4352 4443 4444 + '@atcute/atproto@3.1.7': 4445 + dependencies: 4446 + '@atcute/lexicons': 1.2.2 4447 + 4448 + '@atcute/bluesky@3.2.6': 4449 + dependencies: 4450 + '@atcute/atproto': 3.1.7 4451 + '@atcute/lexicons': 1.2.2 4452 + 4453 + '@atcute/client@4.0.4': 4454 + dependencies: 4455 + '@atcute/identity': 1.1.1 4456 + '@atcute/lexicons': 1.2.2 4457 + 4458 + '@atcute/identity@1.1.1': 4459 + dependencies: 4460 + '@atcute/lexicons': 1.2.2 4461 + '@badrap/valita': 0.4.6 4462 + 4463 + '@atcute/lexicons@1.2.2': 4464 + dependencies: 4465 + '@standard-schema/spec': 1.0.0 4466 + esm-env: 1.2.2 4467 + 4353 4468 '@babel/code-frame@7.10.4': 4354 4469 dependencies: 4355 4470 '@babel/highlight': 7.25.9 ··· 4959 5074 dependencies: 4960 5075 '@babel/helper-string-parser': 7.27.1 4961 5076 '@babel/helper-validator-identifier': 7.27.1 5077 + 5078 + '@badrap/valita@0.4.6': {} 4962 5079 4963 5080 '@egjs/hammerjs@2.0.17': 4964 5081 dependencies: ··· 5858 5975 dependencies: 5859 5976 '@sinonjs/commons': 3.0.1 5860 5977 5978 + '@standard-schema/spec@1.0.0': {} 5979 + 5980 + '@tanstack/query-core@5.90.3': {} 5981 + 5982 + '@tanstack/react-query@5.90.3(react@19.1.0)': 5983 + dependencies: 5984 + '@tanstack/query-core': 5.90.3 5985 + react: 19.1.0 5986 + 5861 5987 '@tybys/wasm-util@0.10.1': 5862 5988 dependencies: 5863 5989 tslib: 2.8.1 ··· 6373 6499 6374 6500 big-integer@1.6.52: {} 6375 6501 6502 + boolbase@1.0.0: {} 6503 + 6376 6504 bplist-creator@0.1.0: 6377 6505 dependencies: 6378 6506 stream-buffers: 2.2.0 ··· 6595 6723 dependencies: 6596 6724 hyphenate-style-name: 1.1.0 6597 6725 6726 + css-select@5.2.2: 6727 + dependencies: 6728 + boolbase: 1.0.0 6729 + css-what: 6.2.2 6730 + domhandler: 5.0.3 6731 + domutils: 3.2.2 6732 + nth-check: 2.1.1 6733 + 6734 + css-tree@1.1.3: 6735 + dependencies: 6736 + mdn-data: 2.0.14 6737 + source-map: 0.6.1 6738 + 6739 + css-what@6.2.2: {} 6740 + 6598 6741 csstype@3.1.3: {} 6599 6742 6600 6743 data-view-buffer@1.0.2: ··· 6665 6808 dependencies: 6666 6809 esutils: 2.0.3 6667 6810 6811 + dom-serializer@2.0.0: 6812 + dependencies: 6813 + domelementtype: 2.3.0 6814 + domhandler: 5.0.3 6815 + entities: 4.5.0 6816 + 6817 + domelementtype@2.3.0: {} 6818 + 6819 + domhandler@5.0.3: 6820 + dependencies: 6821 + domelementtype: 2.3.0 6822 + 6823 + domutils@3.2.2: 6824 + dependencies: 6825 + dom-serializer: 2.0.0 6826 + domelementtype: 2.3.0 6827 + domhandler: 5.0.3 6828 + 6668 6829 dotenv-expand@11.0.7: 6669 6830 dependencies: 6670 6831 dotenv: 16.4.7 ··· 6690 6851 encodeurl@1.0.2: {} 6691 6852 6692 6853 encodeurl@2.0.0: {} 6854 + 6855 + entities@4.5.0: {} 6693 6856 6694 6857 env-editor@0.4.2: {} 6695 6858 ··· 6977 7140 jiti: 2.6.1 6978 7141 transitivePeerDependencies: 6979 7142 - supports-color 7143 + 7144 + esm-env@1.2.2: {} 6980 7145 6981 7146 espree@10.4.0: 6982 7147 dependencies: ··· 7878 8043 7879 8044 math-intrinsics@1.1.0: {} 7880 8045 8046 + mdn-data@2.0.14: {} 8047 + 7881 8048 memoize-one@5.2.1: {} 7882 8049 7883 8050 memoize-one@6.0.0: {} ··· 8312 8479 semver: 7.7.3 8313 8480 validate-npm-package-name: 5.0.1 8314 8481 8482 + nth-check@2.1.1: 8483 + dependencies: 8484 + boolbase: 1.0.0 8485 + 8315 8486 nullthrows@1.1.1: {} 8316 8487 8317 8488 ob1@0.83.1: ··· 8610 8781 react-freeze: 1.0.4(react@19.1.0) 8611 8782 react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0) 8612 8783 react-native-is-edge-to-edge: 1.2.1(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) 8784 + warn-once: 0.1.1 8785 + 8786 + react-native-svg@15.14.0(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): 8787 + dependencies: 8788 + css-select: 5.2.2 8789 + css-tree: 1.1.3 8790 + react: 19.1.0 8791 + react-native: 0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0) 8613 8792 warn-once: 0.1.1 8614 8793 8615 8794 react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+36
src/app/[slug].tsx
··· 1 + import ChatComponentProfiled from "@/components/ChatComponentProfiled"; 2 + import { didPlcSchema } from "@/lib/types/atproto"; 3 + import { useLocalSearchParams } from "expo-router"; 4 + import { Text, View } from "react-native"; 5 + 6 + export default function Index() { 7 + const { slug } = useLocalSearchParams(); 8 + const { success, error, data: did } = didPlcSchema.safeParse(slug); 9 + if (!success) { 10 + console.error("slug was not a did plc"); 11 + console.error(error); 12 + return ( 13 + <View 14 + style={{ 15 + flex: 1, 16 + justifyContent: "center", 17 + alignItems: "center", 18 + }} 19 + > 20 + <Text>Provide a valid did:plc pls (no did web yet)</Text> 21 + </View> 22 + ); 23 + } 24 + 25 + return ( 26 + <View 27 + style={{ 28 + flex: 1, 29 + justifyContent: "center", 30 + alignItems: "center", 31 + }} 32 + > 33 + <ChatComponentProfiled did={did} /> 34 + </View> 35 + ); 36 + }
+12
src/app/_layout.tsx
··· 1 + import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 2 + import { Stack } from "expo-router"; 3 + 4 + const queryClient = new QueryClient(); 5 + 6 + export default function RootLayout() { 7 + return ( 8 + <QueryClientProvider client={queryClient}> 9 + <Stack /> 10 + </QueryClientProvider> 11 + ); 12 + }
+11
src/components/Loading.tsx
··· 1 + import { ActivityIndicator, View } from "react-native"; 2 + 3 + export const Loading = () => { 4 + return ( 5 + <View> 6 + <ActivityIndicator size="large" /> 7 + </View> 8 + ); 9 + }; 10 + 11 + export default Loading;
+5
src/lib/types/atproto.ts
··· 1 + import { z } from "zod"; 2 + 3 + export const didPlcSchema = z.templateLiteral(["did:plc:", z.string()]); 4 + 5 + export type DidPlc = z.infer<typeof didPlcSchema>;
+5
src/lib/utils/atproto/client.ts
··· 1 + import { Client, simpleFetchHandler } from "@atcute/client"; 2 + import type {} from "@atcute/bluesky"; 3 + 4 + const handler = simpleFetchHandler({ service: "https://public.api.bsky.app" }); 5 + export const client = new Client({ handler });
+36
src/queries/get-profile.ts
··· 1 + import type { DidPlc } from "@/lib/types/atproto"; 2 + import { client } from "@/lib/utils/atproto/client"; 3 + 4 + export const getBskyProfile = async (did: DidPlc) => { 5 + const { ok, data } = await client.get("app.bsky.actor.getProfile", { 6 + params: { 7 + actor: did, 8 + }, 9 + }); 10 + 11 + if (!ok) { 12 + switch (data.error) { 13 + case "InvalidRequest": { 14 + console.error("There is no account at", did); 15 + return; 16 + } 17 + case "AccountTakedown": { 18 + console.error("Account of", did, "was taken down"); 19 + return; 20 + } 21 + case "AccountDeactivated": { 22 + console.error("Account of", did, "was deactivated"); 23 + return; 24 + } 25 + default: { 26 + console.error( 27 + "Something went wrong fetching the profile of", 28 + did, 29 + ); 30 + return; 31 + } 32 + } 33 + } 34 + 35 + return data; 36 + };
+15 -15
tsconfig.json
··· 1 1 { 2 - "extends": "expo/tsconfig.base", 3 - "compilerOptions": { 4 - "strict": true, 5 - "paths": { 6 - "@/*": [ 7 - "./*" 8 - ] 9 - } 10 - }, 11 - "include": [ 12 - "**/*.ts", 13 - "**/*.tsx", 14 - ".expo/types/**/*.ts", 15 - "expo-env.d.ts" 16 - ] 2 + "extends": "expo/tsconfig.base", 3 + "compilerOptions": { 4 + "strict": true, 5 + "paths": { 6 + "@/*": [ 7 + "./src/*" 8 + ] 9 + } 10 + }, 11 + "include": [ 12 + "**/*.ts", 13 + "**/*.tsx", 14 + ".expo/types/**/*.ts", 15 + "expo-env.d.ts" 16 + ] 17 17 }