mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-06-24 07:46:25 +02:00
Support pinning articles
This commit is contained in:
parent
a12621d7d8
commit
6ff03b512c
5
.changeset/mean-steaks-notice.md
Normal file
5
.changeset/mean-steaks-notice.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"nostrudel": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Support pinning articles
|
@ -1,21 +1,30 @@
|
|||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { MenuItem } from "@chakra-ui/react";
|
import { MenuItem } from "@chakra-ui/react";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
import { kinds } from "nostr-tools";
|
||||||
|
import { getEventUID } from "nostr-idb";
|
||||||
|
|
||||||
import useCurrentAccount from "../../hooks/use-current-account";
|
import useCurrentAccount from "../../hooks/use-current-account";
|
||||||
import useUserPinList from "../../hooks/use-user-pin-list";
|
import useUserPinList from "../../hooks/use-user-pin-list";
|
||||||
import { DraftNostrEvent, NostrEvent, isETag } from "../../types/nostr-event";
|
import { DraftNostrEvent, NostrEvent } from "../../types/nostr-event";
|
||||||
import { PIN_LIST_KIND, listAddEvent, listRemoveEvent } from "../../helpers/nostr/lists";
|
import { PIN_LIST_KIND, isEventInList, listAddEvent, listRemoveEvent } from "../../helpers/nostr/lists";
|
||||||
import { PinIcon } from "../icons";
|
import { PinIcon } from "../icons";
|
||||||
import { usePublishEvent } from "../../providers/global/publish-provider";
|
import { usePublishEvent } from "../../providers/global/publish-provider";
|
||||||
|
|
||||||
export default function PinNoteMenuItem({ event }: { event: NostrEvent }) {
|
export default function PinEventMenuItem({ event }: { event: NostrEvent }) {
|
||||||
const publish = usePublishEvent();
|
const publish = usePublishEvent();
|
||||||
const account = useCurrentAccount();
|
const account = useCurrentAccount();
|
||||||
const { list } = useUserPinList(account?.pubkey);
|
const { list } = useUserPinList(account?.pubkey);
|
||||||
|
|
||||||
const isPinned = list?.tags.some((t) => isETag(t) && t[1] === event.id) ?? false;
|
const isPinned = isEventInList(list, event);
|
||||||
const label = isPinned ? "Unpin Note" : "Pin Note";
|
|
||||||
|
let type = "Note";
|
||||||
|
switch (event.kind) {
|
||||||
|
case kinds.LongFormArticle:
|
||||||
|
type = "Article";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const label = isPinned ? `Unpin ${type}` : `Pin ${type}`;
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const togglePin = useCallback(async () => {
|
const togglePin = useCallback(async () => {
|
||||||
@ -27,8 +36,8 @@ export default function PinNoteMenuItem({ event }: { event: NostrEvent }) {
|
|||||||
tags: list?.tags ? Array.from(list.tags) : [],
|
tags: list?.tags ? Array.from(list.tags) : [],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isPinned) draft = listRemoveEvent(draft, event.id);
|
if (isPinned) draft = listRemoveEvent(draft, event);
|
||||||
else draft = listAddEvent(draft, event.id);
|
else draft = listAddEvent(draft, event);
|
||||||
|
|
||||||
await publish(label, draft);
|
await publish(label, draft);
|
||||||
setLoading(false);
|
setLoading(false);
|
@ -51,10 +51,10 @@ export default function BookmarkEventButton({
|
|||||||
const removeFromList = lists.find((list) => inLists.includes(list) && !cords.includes(getEventCoordinate(list)));
|
const removeFromList = lists.find((list) => inLists.includes(list) && !cords.includes(getEventCoordinate(list)));
|
||||||
|
|
||||||
if (addToList) {
|
if (addToList) {
|
||||||
const draft = listAddEvent(addToList, event.id);
|
const draft = listAddEvent(addToList, event);
|
||||||
await publish("Add to list", draft);
|
await publish("Add to list", draft);
|
||||||
} else if (removeFromList) {
|
} else if (removeFromList) {
|
||||||
const draft = listRemoveEvent(removeFromList, event.id);
|
const draft = listRemoveEvent(removeFromList, event);
|
||||||
await publish("Remove from list", draft);
|
await publish("Remove from list", draft);
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
@ -7,7 +7,7 @@ import { NostrEvent } from "../../types/nostr-event";
|
|||||||
import { DotsMenuButton, MenuIconButtonProps } from "../dots-menu-button";
|
import { DotsMenuButton, MenuIconButtonProps } from "../dots-menu-button";
|
||||||
import NoteTranslationModal from "../../views/tools/transform-note/translation";
|
import NoteTranslationModal from "../../views/tools/transform-note/translation";
|
||||||
import Translate01 from "../icons/translate-01";
|
import Translate01 from "../icons/translate-01";
|
||||||
import PinNoteMenuItem from "../common-menu-items/pin-note";
|
import PinEventMenuItem from "../common-menu-items/pin-event";
|
||||||
import ShareLinkMenuItem from "../common-menu-items/share-link";
|
import ShareLinkMenuItem from "../common-menu-items/share-link";
|
||||||
import OpenInAppMenuItem from "../common-menu-items/open-in-app";
|
import OpenInAppMenuItem from "../common-menu-items/open-in-app";
|
||||||
import MuteUserMenuItem from "../common-menu-items/mute-user";
|
import MuteUserMenuItem from "../common-menu-items/mute-user";
|
||||||
@ -47,7 +47,7 @@ export default function NoteMenu({ event, ...props }: { event: NostrEvent } & Om
|
|||||||
<MenuItem onClick={broadcast} icon={<BroadcastEventIcon />}>
|
<MenuItem onClick={broadcast} icon={<BroadcastEventIcon />}>
|
||||||
Broadcast
|
Broadcast
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<PinNoteMenuItem event={event} />
|
<PinEventMenuItem event={event} />
|
||||||
<DebugEventMenuItem event={event} />
|
<DebugEventMenuItem event={event} />
|
||||||
</DotsMenuButton>
|
</DotsMenuButton>
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import { getPublicKey, nip19 } from "nostr-tools";
|
|||||||
|
|
||||||
import { Tag, isATag, isETag, isPTag } from "../types/nostr-event";
|
import { Tag, isATag, isETag, isPTag } from "../types/nostr-event";
|
||||||
import { safeRelayUrls } from "./relay";
|
import { safeRelayUrls } from "./relay";
|
||||||
|
import { parseCoordinate } from "./nostr/event";
|
||||||
|
|
||||||
export function isHex(str?: string) {
|
export function isHex(str?: string) {
|
||||||
if (str?.match(/^[0-9a-f]+$/i)) return true;
|
if (str?.match(/^[0-9a-f]+$/i)) return true;
|
||||||
@ -61,39 +62,30 @@ export function encodeDecodeResult(result: nip19.DecodeResult) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getPointerFromTag(tag: Tag): nip19.DecodeResult | null {
|
export function getPointerFromTag(tag: Tag): nip19.DecodeResult | null {
|
||||||
if (isETag(tag)) {
|
switch (tag[0]) {
|
||||||
if (!tag[1]) return null;
|
case "e": {
|
||||||
return {
|
if (!tag[1]) return null;
|
||||||
type: "nevent",
|
|
||||||
data: {
|
|
||||||
id: tag[1],
|
|
||||||
relays: tag[2] ? [tag[2]] : undefined,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} else if (isATag(tag)) {
|
|
||||||
const [_, coordinate, relay] = tag;
|
|
||||||
const parts = coordinate.split(":") as (string | undefined)[];
|
|
||||||
const kind = parts[0] && parseInt(parts[0]);
|
|
||||||
const pubkey = parts[1];
|
|
||||||
const d = parts[2];
|
|
||||||
|
|
||||||
if (!kind) return null;
|
const pointer: nip19.DecodeResult = { type: "nevent", data: { id: tag[1] } };
|
||||||
if (!pubkey) return null;
|
if (tag[2]) pointer.data.relays = [tag[2]];
|
||||||
if (!d) return null;
|
return pointer;
|
||||||
|
}
|
||||||
|
case "a": {
|
||||||
|
const parsed = parseCoordinate(tag[1]);
|
||||||
|
if (!parsed?.identifier) return null;
|
||||||
|
|
||||||
return {
|
const pointer: nip19.DecodeResult = {
|
||||||
type: "naddr",
|
type: "naddr",
|
||||||
data: {
|
data: { pubkey: parsed.pubkey, identifier: parsed.identifier, kind: parsed.kind },
|
||||||
kind,
|
};
|
||||||
pubkey,
|
if (tag[2]) pointer.data.relays = [tag[2]];
|
||||||
identifier: d,
|
return pointer;
|
||||||
relays: relay ? [relay] : undefined,
|
}
|
||||||
},
|
case "p": {
|
||||||
};
|
const [_, pubkey, relay] = tag;
|
||||||
} else if (isPTag(tag)) {
|
if (!pubkey) return null;
|
||||||
const [_, pubkey, relay] = tag;
|
return { type: "nprofile", data: { pubkey, relays: relay ? [relay] : undefined } };
|
||||||
if (!pubkey) return null;
|
}
|
||||||
return { type: "nprofile", data: { pubkey, relays: relay ? [relay] : undefined } };
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { kinds, nip19 } from "nostr-tools";
|
import { EventTemplate, NostrEvent, kinds, nip19 } from "nostr-tools";
|
||||||
|
|
||||||
import { DraftNostrEvent, NostrEvent, PTag, isATag, isDTag, isETag, isPTag, isRTag } from "../../types/nostr-event";
|
import { PTag, isATag, isDTag, isPTag, isRTag } from "../../types/nostr-event";
|
||||||
import { parseCoordinate, replaceOrAddSimpleTag } from "./event";
|
import { getEventCoordinate, replaceOrAddSimpleTag } from "./event";
|
||||||
import { getRelayVariations, safeRelayUrls } from "../relay";
|
import { getRelayVariations, safeRelayUrls } from "../relay";
|
||||||
|
import { getPointerFromTag } from "../nip19";
|
||||||
|
|
||||||
export const MUTE_LIST_KIND = kinds.Mutelist;
|
export const MUTE_LIST_KIND = kinds.Mutelist;
|
||||||
export const PIN_LIST_KIND = kinds.Pinlist;
|
export const PIN_LIST_KIND = kinds.Pinlist;
|
||||||
@ -27,13 +28,13 @@ export function getListName(event: NostrEvent) {
|
|||||||
event.tags.find(isDTag)?.[1]
|
event.tags.find(isDTag)?.[1]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export function setListName(draft: DraftNostrEvent, name: string) {
|
export function setListName(draft: EventTemplate, name: string) {
|
||||||
replaceOrAddSimpleTag(draft, "name", name);
|
replaceOrAddSimpleTag(draft, "name", name);
|
||||||
}
|
}
|
||||||
export function getListDescription(event: NostrEvent) {
|
export function getListDescription(event: NostrEvent) {
|
||||||
return event.tags.find((t) => t[0] === "description")?.[1];
|
return event.tags.find((t) => t[0] === "description")?.[1];
|
||||||
}
|
}
|
||||||
export function setListDescription(draft: DraftNostrEvent, description: string) {
|
export function setListDescription(draft: EventTemplate, description: string) {
|
||||||
replaceOrAddSimpleTag(draft, "description", description);
|
replaceOrAddSimpleTag(draft, "description", description);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +55,7 @@ export function isSpecialListKind(kind: number) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cloneList(list: NostrEvent, keepCreatedAt = false): DraftNostrEvent {
|
export function cloneList(list: NostrEvent, keepCreatedAt = false): EventTemplate {
|
||||||
return {
|
return {
|
||||||
kind: list.kind,
|
kind: list.kind,
|
||||||
content: list.content,
|
content: list.content,
|
||||||
@ -63,35 +64,33 @@ export function cloneList(list: NostrEvent, keepCreatedAt = false): DraftNostrEv
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPubkeysFromList(event: NostrEvent | DraftNostrEvent) {
|
export function getPubkeysFromList(event: NostrEvent | EventTemplate) {
|
||||||
return event.tags.filter(isPTag).map((t) => ({ pubkey: t[1], relay: t[2], petname: t[3] }));
|
return event.tags.filter(isPTag).map((t) => ({ pubkey: t[1], relay: t[2], petname: t[3] }));
|
||||||
}
|
}
|
||||||
export function getEventPointersFromList(event: NostrEvent | DraftNostrEvent): nip19.EventPointer[] {
|
export function getReferencesFromList(event: NostrEvent | EventTemplate) {
|
||||||
return event.tags.filter(isETag).map((t) => (t[2] ? { id: t[1], relays: [t[2]] } : { id: t[1] }));
|
|
||||||
}
|
|
||||||
export function getReferencesFromList(event: NostrEvent | DraftNostrEvent) {
|
|
||||||
return event.tags.filter(isRTag).map((t) => ({ url: t[1], petname: t[2] }));
|
return event.tags.filter(isRTag).map((t) => ({ url: t[1], petname: t[2] }));
|
||||||
}
|
}
|
||||||
export function getRelaysFromList(event: NostrEvent | DraftNostrEvent) {
|
export function getRelaysFromList(event: NostrEvent | EventTemplate) {
|
||||||
if (event.kind === kinds.RelayList) return safeRelayUrls(event.tags.filter(isRTag).map((t) => t[1]));
|
if (event.kind === kinds.RelayList) return safeRelayUrls(event.tags.filter(isRTag).map((t) => t[1]));
|
||||||
else return safeRelayUrls(event.tags.filter((t) => t[0] === "relay" && t[1]).map((t) => t[1]) as string[]);
|
else return safeRelayUrls(event.tags.filter((t) => t[0] === "relay" && t[1]).map((t) => t[1]) as string[]);
|
||||||
}
|
}
|
||||||
export function getCoordinatesFromList(event: NostrEvent | DraftNostrEvent) {
|
export function getCoordinatesFromList(event: NostrEvent | EventTemplate) {
|
||||||
return event.tags.filter(isATag).map((t) => ({ coordinate: t[1], relay: t[2] }));
|
return event.tags.filter(isATag).map((t) => ({ coordinate: t[1], relay: t[2] }));
|
||||||
}
|
}
|
||||||
export function getAddressPointersFromList(event: NostrEvent | DraftNostrEvent): nip19.AddressPointer[] {
|
export function getEventPointersFromList(event: NostrEvent | EventTemplate): nip19.EventPointer[] {
|
||||||
const pointers: nip19.AddressPointer[] = [];
|
return event.tags
|
||||||
|
.map(getPointerFromTag)
|
||||||
for (const tag of event.tags) {
|
.filter((r) => r?.type === "nevent")
|
||||||
if (!tag[1]) continue;
|
.map((r) => r.data);
|
||||||
const relay = tag[2];
|
}
|
||||||
const parsed = parseCoordinate(tag[1]);
|
export function getAddressPointersFromList(event: NostrEvent | EventTemplate): nip19.AddressPointer[] {
|
||||||
if (!parsed?.identifier) continue;
|
return event.tags
|
||||||
|
.map(getPointerFromTag)
|
||||||
pointers.push({ ...parsed, identifier: parsed?.identifier, relays: relay ? [relay] : undefined });
|
.filter((r) => r?.type === "naddr")
|
||||||
}
|
.map((r) => r.data);
|
||||||
|
}
|
||||||
return pointers;
|
export function getPointersFromList(event: NostrEvent | EventTemplate) {
|
||||||
|
return event.tags.map(getPointerFromTag).filter((r) => r !== null);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isRelayInList(list: NostrEvent, relay: string) {
|
export function isRelayInList(list: NostrEvent, relay: string) {
|
||||||
@ -102,8 +101,16 @@ export function isPubkeyInList(list?: NostrEvent, pubkey?: string) {
|
|||||||
if (!pubkey || !list) return false;
|
if (!pubkey || !list) return false;
|
||||||
return list.tags.some((t) => t[0] === "p" && t[1] === pubkey);
|
return list.tags.some((t) => t[0] === "p" && t[1] === pubkey);
|
||||||
}
|
}
|
||||||
|
export function isEventInList(list?: NostrEvent, event?: NostrEvent) {
|
||||||
|
if (!event || !list) return false;
|
||||||
|
|
||||||
export function createEmptyContactList(): DraftNostrEvent {
|
if (kinds.isParameterizedReplaceableKind(event.kind)) {
|
||||||
|
const cord = getEventCoordinate(event);
|
||||||
|
return list.tags.some((t) => t[0] === "a" && t[1] === cord);
|
||||||
|
} else return list.tags.some((t) => t[0] === "e" && t[1] === event.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createEmptyContactList(): EventTemplate {
|
||||||
return {
|
return {
|
||||||
created_at: dayjs().unix(),
|
created_at: dayjs().unix(),
|
||||||
content: "",
|
content: "",
|
||||||
@ -113,11 +120,11 @@ export function createEmptyContactList(): DraftNostrEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function listAddPerson(
|
export function listAddPerson(
|
||||||
list: NostrEvent | DraftNostrEvent,
|
list: NostrEvent | EventTemplate,
|
||||||
pubkey: string,
|
pubkey: string,
|
||||||
relay?: string,
|
relay?: string,
|
||||||
petname?: string,
|
petname?: string,
|
||||||
): DraftNostrEvent {
|
): EventTemplate {
|
||||||
if (list.tags.some((t) => t[0] === "p" && t[1] === pubkey)) throw new Error("Person already in list");
|
if (list.tags.some((t) => t[0] === "p" && t[1] === pubkey)) throw new Error("Person already in list");
|
||||||
const pTag: PTag = ["p", pubkey, relay ?? "", petname ?? ""];
|
const pTag: PTag = ["p", pubkey, relay ?? "", petname ?? ""];
|
||||||
while (pTag[pTag.length - 1] === "") pTag.pop();
|
while (pTag[pTag.length - 1] === "") pTag.pop();
|
||||||
@ -130,7 +137,7 @@ export function listAddPerson(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listRemovePerson(list: NostrEvent | DraftNostrEvent, pubkey: string): DraftNostrEvent {
|
export function listRemovePerson(list: NostrEvent | EventTemplate, pubkey: string): EventTemplate {
|
||||||
return {
|
return {
|
||||||
created_at: dayjs().unix(),
|
created_at: dayjs().unix(),
|
||||||
kind: list.kind,
|
kind: list.kind,
|
||||||
@ -139,26 +146,32 @@ export function listRemovePerson(list: NostrEvent | DraftNostrEvent, pubkey: str
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listAddEvent(list: NostrEvent | DraftNostrEvent, event: string, relay?: string): DraftNostrEvent {
|
export function listAddEvent(list: NostrEvent | EventTemplate, event: NostrEvent, relay?: string): EventTemplate {
|
||||||
if (list.tags.some((t) => t[0] === "e" && t[1] === event)) throw new Error("Event already in list");
|
const tag = kinds.isParameterizedReplaceableKind(event.kind) ? ["a", getEventCoordinate(event)] : ["e", event.id];
|
||||||
|
if (relay) tag.push(relay);
|
||||||
|
|
||||||
|
if (list.tags.some((t) => t[0] === tag[0] && t[1] === tag[1])) throw new Error("Event already in list");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
created_at: dayjs().unix(),
|
created_at: dayjs().unix(),
|
||||||
kind: list.kind,
|
kind: list.kind,
|
||||||
content: list.content,
|
content: list.content,
|
||||||
tags: [...list.tags, relay ? ["e", event, relay] : ["e", event]],
|
tags: [...list.tags, tag],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listRemoveEvent(list: NostrEvent | DraftNostrEvent, event: string): DraftNostrEvent {
|
export function listRemoveEvent(list: NostrEvent | EventTemplate, event: NostrEvent): EventTemplate {
|
||||||
|
const tag = kinds.isParameterizedReplaceableKind(event.kind) ? ["a", getEventCoordinate(event)] : ["e", event.id];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
created_at: dayjs().unix(),
|
created_at: dayjs().unix(),
|
||||||
kind: list.kind,
|
kind: list.kind,
|
||||||
content: list.content,
|
content: list.content,
|
||||||
tags: list.tags.filter((t) => !(t[0] === "e" && t[1] === event)),
|
tags: list.tags.filter((t) => !(t[0] === tag[0] && t[1] === tag[1])),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listAddRelay(list: NostrEvent | DraftNostrEvent, relay: string): DraftNostrEvent {
|
export function listAddRelay(list: NostrEvent | EventTemplate, relay: string): EventTemplate {
|
||||||
if (list.tags.some((t) => t[0] === "e" && t[1] === relay)) throw new Error("Relay already in list");
|
if (list.tags.some((t) => t[0] === "e" && t[1] === relay)) throw new Error("Relay already in list");
|
||||||
return {
|
return {
|
||||||
created_at: dayjs().unix(),
|
created_at: dayjs().unix(),
|
||||||
@ -168,7 +181,7 @@ export function listAddRelay(list: NostrEvent | DraftNostrEvent, relay: string):
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listRemoveRelay(list: NostrEvent | DraftNostrEvent, relay: string): DraftNostrEvent {
|
export function listRemoveRelay(list: NostrEvent | EventTemplate, relay: string): EventTemplate {
|
||||||
return {
|
return {
|
||||||
created_at: dayjs().unix(),
|
created_at: dayjs().unix(),
|
||||||
kind: list.kind,
|
kind: list.kind,
|
||||||
@ -177,11 +190,7 @@ export function listRemoveRelay(list: NostrEvent | DraftNostrEvent, relay: strin
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listAddCoordinate(
|
export function listAddCoordinate(list: NostrEvent | EventTemplate, coordinate: string, relay?: string): EventTemplate {
|
||||||
list: NostrEvent | DraftNostrEvent,
|
|
||||||
coordinate: string,
|
|
||||||
relay?: string,
|
|
||||||
): DraftNostrEvent {
|
|
||||||
if (list.tags.some((t) => t[0] === "a" && t[1] === coordinate)) throw new Error("Event already in list");
|
if (list.tags.some((t) => t[0] === "a" && t[1] === coordinate)) throw new Error("Event already in list");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -192,7 +201,7 @@ export function listAddCoordinate(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function listRemoveCoordinate(list: NostrEvent | DraftNostrEvent, coordinate: string): DraftNostrEvent {
|
export function listRemoveCoordinate(list: NostrEvent | EventTemplate, coordinate: string): EventTemplate {
|
||||||
return {
|
return {
|
||||||
created_at: dayjs().unix(),
|
created_at: dayjs().unix(),
|
||||||
kind: list.kind,
|
kind: list.kind,
|
||||||
|
@ -37,7 +37,7 @@ export default function useEventBookmarkActions(event: NostrEvent) {
|
|||||||
if (!isBookmarked) return;
|
if (!isBookmarked) return;
|
||||||
|
|
||||||
if (isReplaceable(event.kind)) draft = listRemoveCoordinate(draft, getEventCoordinate(event));
|
if (isReplaceable(event.kind)) draft = listRemoveCoordinate(draft, getEventCoordinate(event));
|
||||||
else draft = listRemoveEvent(draft, event.id);
|
else draft = listRemoveEvent(draft, event);
|
||||||
|
|
||||||
await publish("Remove Bookmark", draft);
|
await publish("Remove Bookmark", draft);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@ -54,7 +54,7 @@ export default function useEventBookmarkActions(event: NostrEvent) {
|
|||||||
|
|
||||||
if (isBookmarked) return;
|
if (isBookmarked) return;
|
||||||
if (isReplaceable(event.kind)) draft = listAddCoordinate(draft, getEventCoordinate(event));
|
if (isReplaceable(event.kind)) draft = listAddCoordinate(draft, getEventCoordinate(event));
|
||||||
else draft = listAddEvent(draft, event.id);
|
else draft = listAddEvent(draft, event);
|
||||||
|
|
||||||
await publish("Bookmark Note", draft);
|
await publish("Bookmark Note", draft);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { PIN_LIST_KIND, getEventPointersFromList } from "../helpers/nostr/lists";
|
import { PIN_LIST_KIND, getPointersFromList } from "../helpers/nostr/lists";
|
||||||
import { RequestOptions } from "../services/replaceable-events";
|
import { RequestOptions } from "../services/replaceable-events";
|
||||||
import useCurrentAccount from "./use-current-account";
|
import useCurrentAccount from "./use-current-account";
|
||||||
import useReplaceableEvent from "./use-replaceable-event";
|
import useReplaceableEvent from "./use-replaceable-event";
|
||||||
@ -9,7 +9,7 @@ export default function useUserPinList(pubkey?: string, relays: string[] = [], o
|
|||||||
|
|
||||||
const list = useReplaceableEvent(key ? { kind: PIN_LIST_KIND, pubkey: key } : undefined, relays, opts);
|
const list = useReplaceableEvent(key ? { kind: PIN_LIST_KIND, pubkey: key } : undefined, relays, opts);
|
||||||
|
|
||||||
const events = list ? getEventPointersFromList(list) : [];
|
const pointers = list ? getPointersFromList(list) : [];
|
||||||
|
|
||||||
return { list, events };
|
return { list, pointers };
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import Recording02 from "../../../components/icons/recording-02";
|
|||||||
import Translate01 from "../../../components/icons/translate-01";
|
import Translate01 from "../../../components/icons/translate-01";
|
||||||
import { BroadcastEventIcon } from "../../../components/icons";
|
import { BroadcastEventIcon } from "../../../components/icons";
|
||||||
import DebugEventMenuItem from "../../../components/debug-modal/debug-event-menu-item";
|
import DebugEventMenuItem from "../../../components/debug-modal/debug-event-menu-item";
|
||||||
|
import PinEventMenuItem from "../../../components/common-menu-items/pin-event";
|
||||||
|
|
||||||
export default function ArticleMenu({
|
export default function ArticleMenu({
|
||||||
article,
|
article,
|
||||||
@ -34,6 +35,7 @@ export default function ArticleMenu({
|
|||||||
<ShareLinkMenuItem event={article} />
|
<ShareLinkMenuItem event={article} />
|
||||||
<CopyEmbedCodeMenuItem event={article} />
|
<CopyEmbedCodeMenuItem event={article} />
|
||||||
<DeleteEventMenuItem event={article} />
|
<DeleteEventMenuItem event={article} />
|
||||||
|
<PinEventMenuItem event={article} />
|
||||||
|
|
||||||
{/* <MenuItem as={RouterLink} icon={<Recording02 />} to={`/tools/transform/${address}?tab=tts`}>
|
{/* <MenuItem as={RouterLink} icon={<Recording02 />} to={`/tools/transform/${address}?tab=tts`}>
|
||||||
Text to speech
|
Text to speech
|
||||||
@ -41,7 +43,6 @@ export default function ArticleMenu({
|
|||||||
<MenuItem as={RouterLink} icon={<Translate01 />} to={`/tools/transform/${address}?tab=translation`}>
|
<MenuItem as={RouterLink} icon={<Translate01 />} to={`/tools/transform/${address}?tab=translation`}>
|
||||||
Translate
|
Translate
|
||||||
</MenuItem> */}
|
</MenuItem> */}
|
||||||
|
|
||||||
<MenuItem onClick={broadcast} icon={<BroadcastEventIcon />}>
|
<MenuItem onClick={broadcast} icon={<BroadcastEventIcon />}>
|
||||||
Broadcast
|
Broadcast
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
@ -28,9 +28,9 @@ export default function ChannelJoinButton({
|
|||||||
|
|
||||||
let draft: DraftNostrEvent;
|
let draft: DraftNostrEvent;
|
||||||
if (isSubscribed) {
|
if (isSubscribed) {
|
||||||
draft = listRemoveEvent(favList, channel.id);
|
draft = listRemoveEvent(favList, channel);
|
||||||
} else {
|
} else {
|
||||||
draft = listAddEvent(favList, channel.id);
|
draft = listAddEvent(favList, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
await publish(isSubscribed ? "Leave Channel" : "Join Channel", draft);
|
await publish(isSubscribed ? "Leave Channel" : "Join Channel", draft);
|
||||||
|
@ -6,20 +6,20 @@ import { EmbedEventPointer } from "../../../components/embed-event";
|
|||||||
|
|
||||||
export default function UserPinnedEvents({ pubkey }: { pubkey: string }) {
|
export default function UserPinnedEvents({ pubkey }: { pubkey: string }) {
|
||||||
const contextRelays = useAdditionalRelayContext();
|
const contextRelays = useAdditionalRelayContext();
|
||||||
const { events, list } = useUserPinList(pubkey, contextRelays, { alwaysRequest: true });
|
const { pointers } = useUserPinList(pubkey, contextRelays, { alwaysRequest: true });
|
||||||
const showAll = useDisclosure();
|
const showAll = useDisclosure();
|
||||||
|
|
||||||
if (events.length === 0) return null;
|
if (pointers.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" gap="2">
|
<Flex direction="column" gap="2">
|
||||||
<Heading size="md" my="2">
|
<Heading size="md" my="2">
|
||||||
Pinned
|
Pinned
|
||||||
</Heading>
|
</Heading>
|
||||||
{(showAll.isOpen ? events : events.slice(0, 2)).map((event) => (
|
{(showAll.isOpen ? pointers : pointers.slice(0, 2)).map((pointer) => (
|
||||||
<EmbedEventPointer key={event.id} pointer={{ type: "nevent", data: event }} />
|
<EmbedEventPointer key={JSON.stringify(pointer.data)} pointer={pointer} />
|
||||||
))}
|
))}
|
||||||
{!showAll.isOpen && events.length > 2 && (
|
{!showAll.isOpen && pointers.length > 2 && (
|
||||||
<Button variant="link" pt="4" onClick={showAll.onOpen}>
|
<Button variant="link" pt="4" onClick={showAll.onOpen}>
|
||||||
Show All
|
Show All
|
||||||
</Button>
|
</Button>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user