frontend client for gemstone. decentralised workplace app
2
fork

Configure Feed

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

feat: channels fetching and settings

serenity b8dd6754 0aa41cf4

+191 -6
+19
src/components/Settings/ChannelInfo.tsx
··· 1 + import { Text } from "@/components/primitives/Text"; 2 + import type { AtUri } from "@/lib/types/atproto"; 3 + import type { SystemsGmstnDevelopmentChannel } from "@/lib/types/lexicon/systems.gmstn.development.channel"; 4 + import { View } from "react-native"; 5 + 6 + export const ChannelInfo = ({ 7 + channel, 8 + }: { 9 + channel: { 10 + value: SystemsGmstnDevelopmentChannel; 11 + uri: Required<AtUri>; 12 + }; 13 + }) => { 14 + return ( 15 + <View> 16 + <Text>{channel.value.name}</Text> 17 + </View> 18 + ); 19 + };
+131
src/components/Settings/ChannelSettings.tsx
··· 1 + import { Loading } from "@/components/primitives/Loading"; 2 + import { Text } from "@/components/primitives/Text"; 3 + import { ChannelInfo } from "@/components/Settings/ChannelInfo"; 4 + import { useFacet } from "@/lib/facet"; 5 + import type { AtUri } from "@/lib/types/atproto"; 6 + import { stringToAtUri } from "@/lib/utils/atproto"; 7 + import { useOAuthSessionGuaranteed } from "@/providers/OAuthProvider"; 8 + import { useCurrentPalette } from "@/providers/ThemeProvider"; 9 + import { getChannelRecordsFromPds } from "@/queries/get-channels-from-pds"; 10 + import type { OAuthSession } from "@atproto/oauth-client"; 11 + import { useQuery } from "@tanstack/react-query"; 12 + import { Gem, MessagesSquare } from "lucide-react-native"; 13 + import { View } from "react-native"; 14 + 15 + export const ChannelSettings = () => { 16 + const { atoms, typography } = useFacet(); 17 + const { semantic } = useCurrentPalette(); 18 + const session = useOAuthSessionGuaranteed(); 19 + 20 + const { isLoading, data: channels } = useQuery({ 21 + queryKey: ["channels", session.did], 22 + queryFn: async () => { 23 + return await channelsQueryFn(session); 24 + }, 25 + }); 26 + 27 + return isLoading ? ( 28 + <Loading /> 29 + ) : ( 30 + <View 31 + style={{ 32 + borderWidth: 1, 33 + borderColor: semantic.borderVariant, 34 + borderRadius: atoms.radii.lg, 35 + padding: 12, 36 + paddingVertical: 16, 37 + gap: 16, 38 + width: "50%", 39 + }} 40 + > 41 + <View 42 + style={{ 43 + flexDirection: "row", 44 + alignItems: "center", 45 + marginLeft: 6, 46 + gap: 6, 47 + }} 48 + > 49 + <MessagesSquare height={20} width={20} color={semantic.text} /> 50 + <Text 51 + style={[ 52 + typography.weights.byName.medium, 53 + typography.sizes.xl, 54 + ]} 55 + > 56 + Channels 57 + </Text> 58 + </View> 59 + {channels && channels.length > 0 && ( 60 + <View style={{ marginLeft: 10, gap: 8 }}> 61 + <View 62 + style={{ 63 + flexDirection: "row", 64 + alignItems: "center", 65 + gap: 4, 66 + }} 67 + > 68 + <Gem height={16} width={16} color={semantic.text} /> 69 + <Text style={[typography.weights.byName.normal]}> 70 + Your Shards 71 + </Text> 72 + </View> 73 + <View 74 + style={{ 75 + gap: 4, 76 + marginLeft: 8, 77 + }} 78 + > 79 + {channels.map((channel, idx) => ( 80 + <ChannelInfo key={idx} channel={channel} /> 81 + ))} 82 + </View> 83 + </View> 84 + )} 85 + </View> 86 + ); 87 + }; 88 + 89 + const channelsQueryFn = async (session: OAuthSession) => { 90 + const lattices = await getChannelRecordsFromPds({ 91 + pdsEndpoint: session.serverMetadata.issuer, 92 + did: session.did, 93 + }); 94 + 95 + if (!lattices.ok) { 96 + console.error("latticeQueryFn error.", lattices.error); 97 + throw new Error( 98 + `Something went wrong while getting the user's channel records.}`, 99 + ); 100 + } 101 + 102 + const results = lattices.data 103 + .map((record) => { 104 + const convertResult = stringToAtUri(record.uri); 105 + if (!convertResult.ok) { 106 + console.error( 107 + "Could not convert", 108 + record, 109 + "into at:// URI object.", 110 + convertResult.error, 111 + ); 112 + return; 113 + } 114 + if (!convertResult.data.collection || !convertResult.data.rKey) { 115 + console.error( 116 + record, 117 + "did not convert to a full at:// URI with collection and rkey.", 118 + ); 119 + return; 120 + } 121 + const uri: Required<AtUri> = { 122 + authority: convertResult.data.authority, 123 + collection: convertResult.data.collection, 124 + rKey: convertResult.data.rKey, 125 + }; 126 + return { uri, value: record.value }; 127 + }) 128 + .filter((atUri) => atUri !== undefined); 129 + 130 + return results; 131 + };
+2
src/components/Settings/index.tsx
··· 1 + import { ChannelSettings } from "@/components/Settings/ChannelSettings"; 1 2 import { LatticeSettings } from "@/components/Settings/LatticeSettings"; 2 3 import { ShardSettings } from "@/components/Settings/ShardSettings"; 3 4 import { View } from "react-native"; ··· 13 14 alignItems: "center", 14 15 }} 15 16 > 17 + <ChannelSettings /> 16 18 <ShardSettings /> 17 19 <LatticeSettings /> 18 20 </View>
+39 -6
src/queries/get-channels-from-pds.ts
··· 14 14 }: { 15 15 pdsEndpoint: string; 16 16 did: Did; 17 - }): Promise<Result<Array<SystemsGmstnDevelopmentChannel>, unknown>> => { 17 + }): Promise< 18 + Result< 19 + Array<{ 20 + uri: string; 21 + value: SystemsGmstnDevelopmentChannel; 22 + }>, 23 + unknown 24 + > 25 + > => { 18 26 const handler = simpleFetchHandler({ service: pdsEndpoint }); 19 27 const client = new Client({ handler }); 20 28 const channelRecordsResult = await fetchRecords({ ··· 32 40 }: { 33 41 client: Client; 34 42 did: Did; 35 - }): Promise<Result<Array<SystemsGmstnDevelopmentChannel>, unknown>> => { 36 - const allRecords: Array<SystemsGmstnDevelopmentChannel> = []; 43 + }): Promise< 44 + Result< 45 + Array<{ 46 + uri: string; 47 + value: SystemsGmstnDevelopmentChannel; 48 + }>, 49 + unknown 50 + > 51 + > => { 52 + const allRecords: Array<{ 53 + uri: string; 54 + value: SystemsGmstnDevelopmentChannel; 55 + }> = []; 37 56 let cursor: string | undefined; 38 57 39 58 let continueLoop = true; ··· 54 73 }; 55 74 const { records, cursor: nextCursor } = results.data; 56 75 57 - const { success, error, data } = z 58 - .array(systemsGmstnDevelopmentChannelRecordSchema) 76 + const { 77 + success, 78 + error, 79 + data: responses, 80 + } = z 81 + .array( 82 + z.object({ 83 + cid: z.string(), 84 + uri: z.string(), 85 + value: systemsGmstnDevelopmentChannelRecordSchema, 86 + }), 87 + ) 59 88 .safeParse(records); 60 89 61 90 if (!success) return { ok: false, error: z.treeifyError(error) }; 62 91 63 - allRecords.push(...data); 92 + allRecords.push( 93 + ...responses.map((data) => { 94 + return { uri: data.uri, value: data.value }; 95 + }), 96 + ); 64 97 65 98 if (records.length < 100) continueLoop = false; 66 99 cursor = nextCursor;