refactor: include relays in kind3
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Kieran 2024-01-30 22:38:23 +00:00
parent ad8d0af976
commit 07510d92ca
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
6 changed files with 63 additions and 42 deletions

View File

@ -2,7 +2,7 @@ import { HexKey } from "@snort/system";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import AsyncButton from "@/Components/Button/AsyncButton"; import AsyncButton from "@/Components/Button/AsyncButton";
import useEventPublisher from "@/Hooks/useEventPublisher"; import useFollowsControls from "@/Hooks/useFollowControls";
import useLogin from "@/Hooks/useLogin"; import useLogin from "@/Hooks/useLogin";
import { parseId } from "@/Utils"; import { parseId } from "@/Utils";
@ -14,32 +14,18 @@ export interface FollowButtonProps {
} }
export default function FollowButton(props: FollowButtonProps) { export default function FollowButton(props: FollowButtonProps) {
const pubkey = parseId(props.pubkey); const pubkey = parseId(props.pubkey);
const { publisher, system } = useEventPublisher(); const readonly = useLogin(s => s.readonly);
const { follows, readonly } = useLogin(s => ({ follows: s.follows, readonly: s.readonly })); const control = useFollowsControls();
const isFollowing = follows.item.includes(pubkey); const isFollowing = control.isFollowing(pubkey);
const baseClassname = props.className ? `${props.className} ` : ""; const baseClassname = props.className ? `${props.className} ` : "";
async function follow(pubkey: HexKey) {
if (publisher) {
const ev = await publisher.contactList([pubkey, ...follows.item].map(a => ["p", a]));
system.BroadcastEvent(ev);
}
}
async function unfollow(pubkey: HexKey) {
if (publisher) {
const ev = await publisher.contactList(follows.item.filter(a => a !== pubkey).map(a => ["p", a]));
system.BroadcastEvent(ev);
}
}
return ( return (
<AsyncButton <AsyncButton
className={isFollowing ? `${baseClassname} secondary` : `${baseClassname} primary`} className={isFollowing ? `${baseClassname} secondary` : `${baseClassname} primary`}
disabled={readonly} disabled={readonly}
onClick={async e => { onClick={async e => {
e.stopPropagation(); e.stopPropagation();
await (isFollowing ? unfollow(pubkey) : follow(pubkey)); await (isFollowing ? control.removeFollow([pubkey]) : control.addFollow([pubkey]));
}}> }}>
{isFollowing ? <FormattedMessage {...messages.Unfollow} /> : <FormattedMessage {...messages.Follow} />} {isFollowing ? <FormattedMessage {...messages.Unfollow} /> : <FormattedMessage {...messages.Follow} />}
</AsyncButton> </AsyncButton>

View File

@ -1,12 +1,10 @@
import { dedupe } from "@snort/shared";
import { HexKey } from "@snort/system"; import { HexKey } from "@snort/system";
import { ReactNode, useMemo } from "react"; import { ReactNode, useMemo } from "react";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import ProfilePreview from "@/Components/User/ProfilePreview"; import ProfilePreview from "@/Components/User/ProfilePreview";
import useEventPublisher from "@/Hooks/useEventPublisher"; import useFollowsControls from "@/Hooks/useFollowControls";
import useLogin from "@/Hooks/useLogin"; import useLogin from "@/Hooks/useLogin";
import { setFollows } from "@/Utils/Login";
import AsyncButton from "../Button/AsyncButton"; import AsyncButton from "../Button/AsyncButton";
import messages from "../messages"; import messages from "../messages";
@ -30,19 +28,13 @@ export default function FollowListBase({
actions, actions,
profileActions, profileActions,
}: FollowListBaseProps) { }: FollowListBaseProps) {
const { publisher, system } = useEventPublisher(); const control = useFollowsControls();
const { id, follows } = useLogin(s => ({ id: s.id, follows: s.follows }));
const login = useLogin(); const login = useLogin();
const profilePreviewOptions = useMemo(() => ({ about: showAbout, profileCards: true }), [showAbout]); const profilePreviewOptions = useMemo(() => ({ about: showAbout, profileCards: true }), [showAbout]);
async function followAll() { async function followAll() {
if (publisher) { await control.addFollow(pubkeys);
const newFollows = dedupe([...pubkeys, ...follows.item]);
const ev = await publisher.contactList(newFollows.map(a => ["p", a]));
setFollows(id, newFollows, ev.created_at);
await system.BroadcastEvent(ev);
}
} }
return ( return (

View File

@ -0,0 +1,42 @@
import { dedupe } from "@snort/shared";
import { useMemo } from "react";
import useEventPublisher from "./useEventPublisher";
import useLogin from "./useLogin";
/**
* Simple hook for adding / removing follows
*/
export default function useFollowsControls() {
const { publisher, system } = useEventPublisher();
const { follows, relays } = useLogin(s => ({ follows: s.follows.item, readonly: s.readonly, relays: s.relays.item }));
return useMemo(() => {
const publishList = async (newList: Array<string>) => {
if (publisher) {
const ev = await publisher.contactList(
newList.map(a => ["p", a]),
relays,
);
system.BroadcastEvent(ev);
}
};
return {
isFollowing: (pk: string) => {
return follows.includes(pk);
},
addFollow: async (pk: Array<string>) => {
const newList = dedupe([...follows, ...pk]);
await publishList(newList);
},
removeFollow: async (pk: Array<string>) => {
const newList = follows.filter(a => !pk.includes(a));
await publishList(newList);
},
setFollows: async (pk: Array<string>) => {
await publishList(dedupe(pk));
},
};
}, [follows, relays, publisher, system]);
}

View File

@ -6,9 +6,9 @@ import { FormattedMessage, FormattedNumber } from "react-intl";
import AsyncButton from "@/Components/Button/AsyncButton"; import AsyncButton from "@/Components/Button/AsyncButton";
import ProfileImage from "@/Components/User/ProfileImage"; import ProfileImage from "@/Components/User/ProfileImage";
import useEventPublisher from "@/Hooks/useEventPublisher"; import useEventPublisher from "@/Hooks/useEventPublisher";
import useFollowsControls from "@/Hooks/useFollowControls";
import useLogin from "@/Hooks/useLogin"; import useLogin from "@/Hooks/useLogin";
import { Day } from "@/Utils/Const"; import { Day } from "@/Utils/Const";
import { setFollows } from "@/Utils/Login";
import { FollowsRelayHealth } from "./follows-relay-health"; import { FollowsRelayHealth } from "./follows-relay-health";
@ -18,13 +18,14 @@ const enum PruneStage {
} }
export function PruneFollowList() { export function PruneFollowList() {
const { id, follows } = useLogin(s => ({ id: s.id, follows: s.follows })); const { follows } = useLogin(s => ({ id: s.id, follows: s.follows }));
const { publisher, system } = useEventPublisher(); const { system } = useEventPublisher();
const uniqueFollows = dedupe(follows.item); const uniqueFollows = dedupe(follows.item);
const [status, setStatus] = useState<PruneStage>(); const [status, setStatus] = useState<PruneStage>();
const [progress, setProgress] = useState(0); const [progress, setProgress] = useState(0);
const [lastPost, setLastPosts] = useState<Record<string, number>>(); const [lastPost, setLastPosts] = useState<Record<string, number>>();
const [unfollow, setUnfollow] = useState<Array<string>>([]); const [unfollow, setUnfollow] = useState<Array<string>>([]);
const followControls = useFollowsControls();
async function fetchLastPosts() { async function fetchLastPosts() {
setStatus(PruneStage.FetchLastPostTimestamp); setStatus(PruneStage.FetchLastPostTimestamp);
@ -74,12 +75,7 @@ export function PruneFollowList() {
}, [uniqueFollows, unfollow]); }, [uniqueFollows, unfollow]);
async function publishFollowList() { async function publishFollowList() {
const newFollows = newFollowList.map(a => ["p", a]) as Array<[string, string]>; await followControls.setFollows(newFollowList);
if (publisher) {
const ev = await publisher.contactList(newFollows);
await system.BroadcastEvent(ev);
setFollows(id, newFollowList, ev.created_at * 1000);
}
} }
function getStatus() { function getStatus() {

View File

@ -122,8 +122,10 @@ export async function generateNewLogin(
const publisher = EventPublisher.privateKey(privateKey); const publisher = EventPublisher.privateKey(privateKey);
// Create new contact list following self and site account // Create new contact list following self and site account
const contactList = [publicKey, ...CONFIG.signUp.defaultFollows.map(a => bech32ToHex(a))].map(a => ["p", a]); const contactList = [publicKey, ...CONFIG.signUp.defaultFollows.map(a => bech32ToHex(a))].map(a => ["p", a]) as Array<
const ev = await publisher.contactList(contactList); [string, string]
>;
const ev = await publisher.contactList(contactList, newRelays);
system.BroadcastEvent(ev); system.BroadcastEvent(ev);
// Create relay metadata event // Create relay metadata event

View File

@ -257,9 +257,12 @@ export class EventPublisher {
return await this.#sign(eb); return await this.#sign(eb);
} }
async contactList(tags: Array<[string, string]>) { async contactList(tags: Array<[string, string]>, relays?: Record<string, RelaySettings>) {
const eb = this.#eb(EventKind.ContactList); const eb = this.#eb(EventKind.ContactList);
tags.forEach(a => eb.tag(a)); tags.forEach(a => eb.tag(a));
if (relays) {
eb.content(JSON.stringify(relays));
}
return await this.#sign(eb); return await this.#sign(eb);
} }