diff --git a/src/element/async-button.tsx b/src/element/async-button.tsx index 4e8153a..95d05a3 100644 --- a/src/element/async-button.tsx +++ b/src/element/async-button.tsx @@ -2,7 +2,8 @@ import "./async-button.css"; import { useState } from "react"; import Spinner from "element/spinner"; -interface AsyncButtonProps extends React.ButtonHTMLAttributes { +interface AsyncButtonProps + extends React.ButtonHTMLAttributes { disabled?: boolean; onClick(e: React.MouseEvent): Promise | void; children?: React.ReactNode; @@ -28,8 +29,15 @@ export default function AsyncButton(props: AsyncButtonProps) { } return ( - ; +import { EventKind, EventPublisher } from "@snort/system"; +import { useLogin } from "hooks/login"; +import useFollows from "hooks/follows"; +import AsyncButton from "element/async-button"; +import { System } from "index"; + +export function LoggedInFollowButton({ + loggedIn, + pubkey, +}: { + loggedIn: string; + pubkey: string; +}) { + const { contacts, relays } = useFollows(loggedIn, true); + const isFollowing = contacts.find((t) => t.at(1) === pubkey); + + async function unfollow() { + const pub = await EventPublisher.nip7(); + if (pub) { + const ev = await pub.generic((eb) => { + eb.kind(EventKind.ContactList).content(JSON.stringify(relays)); + for (const c of contacts) { + if (c.at(1) !== pubkey) { + eb.tag(c); + } + } + return eb; + }); + console.debug(ev); + System.BroadcastEvent(ev); + } + } + + async function follow() { + const pub = await EventPublisher.nip7(); + if (pub) { + const ev = await pub.generic((eb) => { + eb.kind(EventKind.ContactList).content(JSON.stringify(relays)); + for (const tag of contacts) { + eb.tag(tag); + } + eb.tag(["p", pubkey]); + return eb; + }); + console.debug(ev); + System.BroadcastEvent(ev); + } + } + + return ( + + {isFollowing ? "Unfollow" : "Follow"} + + ); +} + +export function FollowButton({ pubkey }: { pubkey: string }) { + const login = useLogin(); + return login?.pubkey ? ( + + ) : null; } diff --git a/src/hooks/follows.ts b/src/hooks/follows.ts new file mode 100644 index 0000000..657354d --- /dev/null +++ b/src/hooks/follows.ts @@ -0,0 +1,28 @@ +import { useMemo } from "react"; +import { EventKind, ReplaceableNoteStore, RequestBuilder } from "@snort/system"; +import { useRequestBuilder } from "@snort/system-react"; +import { System } from "index"; + +export default function useFollows(pubkey: string, leaveOpen = false) { + const sub = useMemo(() => { + const b = new RequestBuilder(`follows:${pubkey.slice(0, 12)}`); + b.withOptions({ + leaveOpen, + }) + .withFilter() + .authors([pubkey]) + .kinds([EventKind.ContactList]); + return b; + }, [pubkey, leaveOpen]); + + const { data } = useRequestBuilder( + System, + ReplaceableNoteStore, + sub + ); + + const contacts = (data?.tags ?? []).filter((t) => t.at(0) === "p"); + const relays = JSON.parse(data?.content ?? "{}"); + + return { contacts, relays }; +} diff --git a/src/pages/profile-page.tsx b/src/pages/profile-page.tsx index 2b8375a..1a44a0f 100644 --- a/src/pages/profile-page.tsx +++ b/src/pages/profile-page.tsx @@ -13,6 +13,7 @@ import { Profile } from "element/profile"; import { Icon } from "element/icon"; import { SendZapsDialog } from "element/send-zap"; import { VideoTile } from "element/video-tile"; +import { FollowButton } from "element/follow-button"; import { useProfile } from "hooks/profile"; import useTopZappers from "hooks/top-zappers"; import { Text } from "element/text"; @@ -81,8 +82,6 @@ export function ProfilePage() { } } - // todo: follow - return (
@@ -125,6 +124,7 @@ export function ProfilePage() { targetName={profile?.name || link.id} /> )} +
{profile?.name &&

{profile.name}

}