frontend client for gemstone. decentralised workplace app
2
fork

Configure Feed

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

feat: initial shard settings

serenity af46d18d 3984e2a3

+203 -14
+23
src/components/Settings/RegisterShardModalContent.tsx
··· 1 + import { Text } from "@/components/primitives/Text"; 2 + import { useFacet } from "@/lib/facet"; 3 + import { useCurrentPalette } from "@/providers/ThemeProvider"; 4 + import { TextInput, View } from "react-native"; 5 + 6 + export const RegisterShardModalContent = () => { 7 + const { semantic } = useCurrentPalette(); 8 + const { atoms } = useFacet(); 9 + 10 + return ( 11 + <View 12 + style={{ 13 + backgroundColor: semantic.surface, 14 + borderRadius: atoms.radii.lg, 15 + display: "flex", 16 + gap: 4, 17 + }} 18 + > 19 + <TextInput placeholder="Hihihihiihihih" /> 20 + <Text>Hello</Text> 21 + </View> 22 + ); 23 + };
+1 -1
src/components/Settings/ShardInfo.tsx
··· 28 28 return ( 29 29 <View style={{ flexDirection: "row", gap: 6, alignItems: "center" }}> 30 30 {isLoading ? ( 31 - <Loading /> 31 + <Loading size="small" /> 32 32 ) : ( 33 33 <> 34 34 <Text>{shardDomain}</Text>
+178 -13
src/components/Settings/ShardSettings.tsx
··· 1 + import { Loading } from "@/components/primitives/Loading"; 1 2 import { Text } from "@/components/primitives/Text"; 3 + import { RegisterShardModalContent } from "@/components/Settings/RegisterShardModalContent"; 4 + import { ShardInfo } from "@/components/Settings/ShardInfo"; 2 5 import { useFacet } from "@/lib/facet"; 6 + import { fade } from "@/lib/facet/src/lib/colors"; 7 + import type { AtUri } from "@/lib/types/atproto"; 8 + import { stringToAtUri } from "@/lib/utils/atproto"; 9 + import { useOAuthSessionGuaranteed } from "@/providers/OAuthProvider"; 3 10 import { useCurrentPalette } from "@/providers/ThemeProvider"; 4 - import { View } from "react-native"; 11 + import { getUserShards } from "@/queries/get-shards-from-pds"; 12 + import type { OAuthSession } from "@atproto/oauth-client"; 13 + import { useQuery } from "@tanstack/react-query"; 14 + import { Gem, HardDrive, Plus } from "lucide-react-native"; 15 + import { useState } from "react"; 16 + import { Modal, Pressable, View } from "react-native"; 5 17 6 18 export const ShardSettings = () => { 7 19 const { semantic } = useCurrentPalette(); 8 20 const { atoms, typography } = useFacet(); 21 + const session = useOAuthSessionGuaranteed(); 22 + const [showRegisterModal, setShowRegisterModal] = useState(false); 9 23 10 - return ( 24 + const { data: shards, isLoading } = useQuery({ 25 + queryKey: ["shard", session.did], 26 + queryFn: async () => { 27 + return await shardQueryFn(session); 28 + }, 29 + }); 30 + 31 + return isLoading ? ( 32 + <Loading /> 33 + ) : ( 11 34 <View 12 35 style={{ 13 36 borderWidth: 1, 14 37 borderColor: semantic.borderVariant, 15 38 borderRadius: atoms.radii.lg, 16 - padding: 8, 39 + padding: 12, 40 + paddingVertical: 16, 41 + gap: 16, 42 + width: "50%", 17 43 }} 18 44 > 19 - <Text 20 - style={[ 21 - typography.weights.byName.medium, 22 - typography.sizes.lg, 23 - { 24 - paddingLeft: 8, 25 - }, 26 - ]} 45 + <View 46 + style={{ 47 + flexDirection: "row", 48 + alignItems: "center", 49 + marginLeft: 6, 50 + gap: 6, 51 + }} 27 52 > 28 - Shards 29 - </Text> 53 + <HardDrive height={20} width={20} color={semantic.text} /> 54 + <Text 55 + style={[ 56 + typography.weights.byName.medium, 57 + typography.sizes.xl, 58 + ]} 59 + > 60 + Shards 61 + </Text> 62 + </View> 63 + {shards && shards.length > 0 && ( 64 + <View style={{ marginLeft: 10, gap: 8 }}> 65 + <View 66 + style={{ 67 + flexDirection: "row", 68 + alignItems: "center", 69 + gap: 4, 70 + }} 71 + > 72 + <Gem height={16} width={16} color={semantic.text} /> 73 + <Text style={[typography.weights.byName.normal]}> 74 + Your Shards 75 + </Text> 76 + </View> 77 + <View 78 + style={{ 79 + gap: 4, 80 + marginLeft: 8, 81 + }} 82 + > 83 + {shards.map((shard, idx) => ( 84 + <ShardInfo key={idx} shard={shard} /> 85 + ))} 86 + </View> 87 + </View> 88 + )} 89 + <View> 90 + <Pressable 91 + style={{ 92 + flexDirection: "row", 93 + alignItems: "center", 94 + marginLeft: 10, 95 + gap: 4, 96 + backgroundColor: semantic.primary, 97 + alignSelf: "flex-start", 98 + padding: 8, 99 + paddingRight: 12, 100 + borderRadius: atoms.radii.md, 101 + }} 102 + onPress={() => { 103 + setShowRegisterModal(true); 104 + }} 105 + > 106 + <Plus height={16} width={16} color={semantic.textInverse} /> 107 + <Text 108 + style={[ 109 + typography.weights.byName.normal, 110 + { color: semantic.textInverse }, 111 + ]} 112 + > 113 + Register a Shard 114 + </Text> 115 + </Pressable> 116 + <Modal 117 + visible={showRegisterModal} 118 + onRequestClose={() => { 119 + setShowRegisterModal(!showRegisterModal); 120 + }} 121 + animationType="fade" 122 + transparent={true} 123 + > 124 + <Pressable 125 + style={{ 126 + flex: 1, 127 + cursor: "auto", 128 + alignItems: "center", 129 + justifyContent: "center", 130 + backgroundColor: fade(semantic.backgroundDarker, 60), 131 + }} 132 + onPress={() => { 133 + setShowRegisterModal(false); 134 + }} 135 + > 136 + <Pressable 137 + style={{ 138 + flex: 0, 139 + cursor: "auto", 140 + alignItems: "center", 141 + }} 142 + onPress={(e) => { 143 + e.stopPropagation(); 144 + }} 145 + > 146 + <RegisterShardModalContent /> 147 + </Pressable> 148 + </Pressable> 149 + </Modal> 150 + </View> 30 151 </View> 31 152 ); 32 153 }; 154 + 155 + const shardQueryFn = async (session: OAuthSession) => { 156 + const shards = await getUserShards({ 157 + pdsEndpoint: session.serverMetadata.issuer, 158 + did: session.did, 159 + }); 160 + 161 + if (!shards.ok) { 162 + console.error("getMembershipRecordsFromPds error.", shards.error); 163 + throw new Error( 164 + `Something went wrong while getting the user's membership records.}`, 165 + ); 166 + } 167 + 168 + const results = shards.data 169 + .map((record) => { 170 + const convertResult = stringToAtUri(record.uri); 171 + if (!convertResult.ok) { 172 + console.error( 173 + "Could not convert", 174 + record, 175 + "into at:// URI object.", 176 + convertResult.error, 177 + ); 178 + return; 179 + } 180 + if (!convertResult.data.collection || !convertResult.data.rKey) { 181 + console.error( 182 + record, 183 + "did not convert to a full at:// URI with collection and rkey.", 184 + ); 185 + return; 186 + } 187 + const uri: Required<AtUri> = { 188 + authority: convertResult.data.authority, 189 + collection: convertResult.data.collection, 190 + rKey: convertResult.data.rKey, 191 + }; 192 + return { uri, value: record.value }; 193 + }) 194 + .filter((atUri) => atUri !== undefined); 195 + 196 + return results; 197 + };
+1
src/components/Settings/index.tsx
··· 10 10 flexDirection: "column", 11 11 padding: 32, 12 12 gap: 16, 13 + alignItems: "center", 13 14 }} 14 15 > 15 16 <ShardSettings />