frontend client for gemstone. decentralised workplace app

feat: register a shard

serenity 23d463bf 83da8218

Changed files
+136 -9
src
+84 -6
src/components/Settings/RegisterShardModalContent.tsx
···
import { Text } from "@/components/primitives/Text";
import { useFacet } from "@/lib/facet";
+
import { registerNewShard } from "@/lib/utils/gmstn";
+
import { useOAuthAgentGuaranteed } from "@/providers/OAuthProvider";
import { useCurrentPalette } from "@/providers/ThemeProvider";
-
import { TextInput, View } from "react-native";
+
import type { Dispatch, SetStateAction} from "react";
+
import { useState } from "react";
+
import { Pressable, TextInput, View } from "react-native";
-
export const RegisterShardModalContent = () => {
+
export const RegisterShardModalContent = ({
+
setShowRegisterModal,
+
}: {
+
setShowRegisterModal: Dispatch<SetStateAction<boolean>>;
+
}) => {
const { semantic } = useCurrentPalette();
-
const { atoms } = useFacet();
+
const { atoms, typography } = useFacet();
+
const [inputText, setInputText] = useState("");
+
const [registerError, setRegisterError] = useState<string | undefined>(
+
undefined,
+
);
+
const agent = useOAuthAgentGuaranteed();
+
const handleSubmit = async () => {
+
const registerResult = await registerNewShard({
+
shardDomain: inputText,
+
agent,
+
});
+
if (!registerResult.ok) {
+
console.error(
+
"Something went wrong when registering the shard.",
+
registerResult.error,
+
);
+
setRegisterError(registerResult.error);
+
return;
+
}
+
setShowRegisterModal(false);
+
};
return (
<View
···
backgroundColor: semantic.surface,
borderRadius: atoms.radii.lg,
display: "flex",
-
gap: 4,
+
gap: 12,
+
padding: 16,
}}
>
-
<TextInput placeholder="Hihihihiihihih" />
-
<Text>Hello</Text>
+
<View style={{ gap: 4 }}>
+
<Text>Shard domain:</Text>
+
<TextInput
+
style={[
+
{
+
flex: 1,
+
borderWidth: 1,
+
borderColor: semantic.borderVariant,
+
borderRadius: 8,
+
paddingHorizontal: 10,
+
paddingVertical: 10,
+
color: semantic.text,
+
outline: "0",
+
fontFamily: typography.families.primary,
+
width: 256,
+
},
+
typography.weights.byName.extralight,
+
typography.sizes.sm,
+
]}
+
value={inputText}
+
onChangeText={setInputText}
+
placeholder="shard.gmstn.systems"
+
placeholderTextColor={semantic.textPlaceholder}
+
/>
+
</View>
+
<Pressable
+
style={{
+
backgroundColor: inputText.trim()
+
? semantic.primary
+
: registerError
+
? semantic.error
+
: semantic.border,
+
borderRadius: atoms.radii.lg,
+
alignItems: "center",
+
paddingVertical: 10,
+
}}
+
onPress={() => {
+
handleSubmit().catch((e: unknown) => {
+
console.error(e);
+
});
+
}}
+
>
+
<Text
+
style={[
+
typography.weights.byName.normal,
+
{ color: semantic.textInverse },
+
]}
+
>
+
Register
+
</Text>
+
</Pressable>
</View>
);
};
+8 -3
src/components/Settings/ShardSettings.tsx
···
cursor: "auto",
alignItems: "center",
justifyContent: "center",
-
backgroundColor: fade(semantic.backgroundDarker, 60),
+
backgroundColor: fade(
+
semantic.backgroundDarker,
+
60,
+
),
}}
onPress={() => {
setShowRegisterModal(false);
···
e.stopPropagation();
}}
>
-
<RegisterShardModalContent />
+
<RegisterShardModalContent
+
setShowRegisterModal={setShowRegisterModal}
+
/>
</Pressable>
</Pressable>
</Modal>
···
});
if (!shards.ok) {
-
console.error("getMembershipRecordsFromPds error.", shards.error);
+
console.error("shardQueryFn error.", shards.error);
throw new Error(
`Something went wrong while getting the user's membership records.}`,
);
+44
src/lib/utils/gmstn.ts
···
import type { Did } from "@/lib/types/atproto";
+
import type { SystemsGmstnDevelopmentShard } from "@/lib/types/lexicon/systems.gmstn.development.shard";
import { getEndpointFromDid } from "@/lib/utils/atproto";
+
import { isDomain } from "@/lib/utils/domains";
+
import type { Result } from "@/lib/utils/result";
+
import type { Agent } from "@atproto/api";
export const getLatticeEndpointFromDid = async (did: Did) => {
return await getEndpointFromDid(did, "GemstoneLattice");
···
endpoint.searchParams.append("token", sessionToken);
return new WebSocket(endpoint);
};
+
+
export const registerNewShard = async ({
+
shardDomain,
+
agent,
+
}: {
+
shardDomain: string;
+
agent: Agent;
+
}): Promise<Result<undefined, string>> => {
+
if (!isDomain(shardDomain))
+
return { ok: false, error: "Input was not a valid domain." };
+
+
const now = new Date().toISOString();
+
+
const record: Omit<SystemsGmstnDevelopmentShard, "$type"> = {
+
// @ts-expect-error we want to explicitly use the ISO string variant
+
createdAt: now,
+
// TODO: actually figure out how to support the description
+
description: "A Gemstone Systems Shard.",
+
};
+
console.log(record);
+
+
const { success } = await agent.call(
+
"com.atproto.repo.createRecord",
+
{},
+
{
+
repo: agent.did,
+
collection: "systems.gmstn.development.shard",
+
rkey: shardDomain,
+
record,
+
},
+
);
+
+
if (!success)
+
return {
+
ok: false,
+
error: "Attempted to create shard record failed. Check the domain inputs.",
+
};
+
+
return { ok: true };
+
};