···
+
import { Loading } from "@/components/primitives/Loading";
+
import { Text } from "@/components/primitives/Text";
+
import { useFacet } from "@/lib/facet";
+
import { lighten } from "@/lib/facet/src/lib/colors";
+
import type { AtUri } from "@/lib/types/atproto";
+
import { didSchema } from "@/lib/types/atproto";
+
import type { SystemsGmstnDevelopmentChannelInvite } from "@/lib/types/lexicon/systems.gmstn.development.channel.invite";
+
import { didDocResolver, stringToAtUri } from "@/lib/utils/atproto";
+
import { inviteNewUser } from "@/lib/utils/gmstn";
+
useOAuthAgentGuaranteed,
+
useOAuthSessionGuaranteed,
+
} from "@/providers/OAuthProvider";
+
import { useCurrentPalette } from "@/providers/ThemeProvider";
+
import { getInviteRecordsFromPds } from "@/queries/get-invites-from-pds";
+
import { type OAuthSession } from "@atproto/oauth-client";
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
+
import { format } from "date-fns";
+
import { Plus } from "lucide-react-native";
+
import { useState, type Dispatch, type SetStateAction } from "react";
+
import { FlatList, Pressable, TextInput, View } from "react-native";
+
export const InviteUserModalContent = ({
+
setShowInviteModal: Dispatch<SetStateAction<boolean>>;
+
const { semantic } = useCurrentPalette();
+
const { atoms, typography } = useFacet();
+
const [inputText, setInputText] = useState("");
+
const session = useOAuthSessionGuaranteed();
+
const agent = useOAuthAgentGuaranteed();
+
const queryClient = useQueryClient();
+
const { isLoading, data: invites } = useQuery({
+
queryKey: ["invites", session.did],
+
return await invitesQueryFn({ session, channelAtUri });
+
const { mutate: inviteUserMutation, isPending: mutationPending } =
+
mutationFn: async () => {
+
} = didSchema.safeParse(inputText);
+
if (!success) throw new Error(error.message);
+
const inviteRes = await inviteNewUser({
+
$type: "com.atproto.repo.strongRef",
+
if (!inviteRes.ok) throw new Error(inviteRes.error);
+
onSuccess: async () => {
+
await queryClient.invalidateQueries({
+
queryKey: ["invites", session.did],
+
const disableSubmitButton = (() => {
+
const { success } = didSchema.safeParse(inputText);
+
if (!success) return true;
+
return !inputText.trim();
+
backgroundColor: semantic.surface,
+
borderRadius: atoms.radii.lg,
+
<View style={{ gap: 4 }}>
+
borderColor: semantic.borderVariant,
+
borderRadius: atoms.radii.md,
+
fontFamily: typography.families.primary,
+
typography.weights.byName.extralight,
+
onChangeText={(text) => {
+
placeholder="did:plc:... or did:web:..."
+
placeholderTextColor={semantic.textPlaceholder}
+
console.log("mutating");
+
disabled={disableSubmitButton}
+
<Loading size="small" />
+
backgroundColor: disableSubmitButton
+
? semantic.textPlaceholder
+
? lighten(semantic.primary, 7)
+
alignSelf: "flex-start",
+
borderRadius: atoms.radii.md,
+
borderColor: semantic.borderVariant,
+
<View style={{ gap: 4 }}>
+
<Text>Invited users:</Text>
+
<Loading size="small" />
+
data={invites.toReversed()}
+
renderItem={({ item }) => (
+
<InvitedUser invite={item} />
+
keyExtractor={(_, index) => index.toString()}
+
contentContainerStyle={{
+
showsVerticalScrollIndicator={false}
+
const invitesQueryFn = async ({
+
const invites = await getInviteRecordsFromPds({
+
pdsEndpoint: session.serverMetadata.issuer,
+
console.error("invitesQueryFn error.", invites.error);
+
`Something went wrong while getting the user's channel records.}`,
+
const results = invites.data
+
const convertResult = stringToAtUri(record.uri);
+
if (!convertResult.ok) {
+
"into at:// URI object.",
+
if (!convertResult.data.collection || !convertResult.data.rKey) {
+
"did not convert to a full at:// URI with collection and rkey.",
+
const uri: Required<AtUri> = {
+
authority: convertResult.data.authority,
+
collection: convertResult.data.collection,
+
rKey: convertResult.data.rKey,
+
return { uri, value: record.value };
+
.filter((atUri) => atUri !== undefined)
+
.filter((atUri) => atUri.value.channel.uri === channelAtUri);
+
value: SystemsGmstnDevelopmentChannelInvite;
+
const { isLoading, data: handle } = useQuery({
+
queryKey: ["handle", invite.value.recipient],
+
const didDoc = await didDocResolver.resolve(invite.value.recipient);
+
if (!didDoc.alsoKnownAs)
+
throw new Error("DID did not resolve to handle");
+
if (didDoc.alsoKnownAs.length === 0)
+
"No alsoKnownAs in DID document. It might be malformed.",
+
return didDoc.alsoKnownAs[0].slice(5);
+
<Loading size="small" />
+
justifyContent: "space-between",
+
invite.value.createdAt,