Display NIP-89 client tags on events

This commit is contained in:
hzrd149 2024-08-30 09:17:32 -05:00
parent 8bb7fc1cee
commit 085e12a699
9 changed files with 91 additions and 17 deletions

View File

@ -0,0 +1,5 @@
---
"nostrudel": minor
---
Display NIP-89 client tags on events

View File

@ -13,6 +13,7 @@ COPY . .
ENV VITE_COMMIT_HASH=""
ENV VITE_APP_VERSION="custom"
ENV ENABLE_CLIENT_TAG="false"
RUN yarn build
FROM nginx:stable-alpine-slim AS main

View File

@ -0,0 +1,13 @@
import { Text } from "@chakra-ui/react";
import { NostrEvent } from "nostr-tools";
export default function NotePublishedUsing({ event }: { event: NostrEvent }) {
const clientTag = event.tags.find((t) => t[0] === "client");
if (!clientTag) return;
return (
<Text as="span" fontStyle="italic" fontSize="sm">
using {clientTag[1]}
</Text>
);
}

View File

@ -47,6 +47,7 @@ import ZapBubbles from "./components/zap-bubbles";
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
import relayHintService from "../../../services/event-relay-hint";
import localSettings from "../../../services/local-settings";
import NotePublishedUsing from "../note-published-using";
export type TimelineNoteProps = Omit<CardProps, "children"> & {
event: NostrEvent;
@ -108,6 +109,7 @@ export function TimelineNote({
<Timestamp timestamp={event.created_at} />
</Link>
<POWIcon event={event} boxSize={5} />
<NotePublishedUsing event={event} />
<Flex grow={1} />
{showSignatureVerification && <EventVerificationIcon event={event} />}
{!hideDrawerButton && (

View File

@ -1,3 +1,4 @@
import { kinds } from "nostr-tools";
import { safeRelayUrl, safeRelayUrls } from "./helpers/relay";
export const DEFAULT_SEARCH_RELAYS = safeRelayUrls([
@ -45,3 +46,11 @@ export const NOSTR_CONNECT_PERMISSIONS = [
"sign_event:6",
"sign_event:7",
];
export const NEVER_ATTACH_CLIENT_TAG = [kinds.EncryptedDirectMessage];
export const ENABLE_CLIENT_TAG = import.meta.env.VITE_ENABLE_CLIENT_TAG !== "false";
export const NIP_89_CLIENT_TAG = [
"client",
"noStrudel",
"31990:266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5:1686066542546",
];

View File

@ -13,6 +13,8 @@ import eventReactionsService from "../../services/event-reactions";
import { localRelay } from "../../services/local-relay";
import deleteEventService from "../../services/delete-events";
import userMailboxesService from "../../services/user-mailboxes";
import localSettings from "../../services/local-settings";
import { NEVER_ATTACH_CLIENT_TAG, NIP_89_CLIENT_TAG } from "../../const";
type PublishContextType = {
log: PublishAction[];
@ -78,7 +80,16 @@ export default function PublishProvider({ children }: PropsWithChildren) {
let signed: NostrEvent;
if (!Object.hasOwn(event, "sig")) {
let draft: EventTemplate = event as EventTemplate;
// add pubkey relay hints
draft = userMailboxesService.addPubkeyRelayHints(draft);
// add client tag
if (localSettings.addClientTag.value && !NEVER_ATTACH_CLIENT_TAG.includes(event.kind)) {
draft.tags = [...draft.tags.filter((t) => t[0] !== "client"), NIP_89_CLIENT_TAG];
}
// request signature
signed = await requestSignature(draft);
} else signed = event as NostrEvent;

View File

@ -1,8 +1,7 @@
import { generateSecretKey } from "nostr-tools";
import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
import { PersistentSubject } from "../classes/subject";
import { DEFAULT_SIGNAL_RELAYS } from "../const";
import { ENABLE_CLIENT_TAG, DEFAULT_SIGNAL_RELAYS } from "../const";
import {
BooleanLocalStorageEntry,
NullableNumberLocalStorageEntry,
@ -45,6 +44,9 @@ const webRtcRecentConnections = new LocalStorageEntry(
(value) => value.join(","),
);
// posting
const addClientTag = new BooleanLocalStorageEntry("add-client-tag", ENABLE_CLIENT_TAG);
const localSettings = {
idbMaxEvents,
wasmPersistForDays,
@ -53,6 +55,7 @@ const localSettings = {
webRtcLocalIdentity,
webRtcSignalingRelays,
webRtcRecentConnections,
addClientTag,
};
if (import.meta.env.DEV) {

View File

@ -19,6 +19,7 @@ import {
AlertTitle,
AlertDescription,
Heading,
Switch,
} from "@chakra-ui/react";
import { matchSorter } from "match-sorter";
@ -28,6 +29,8 @@ import useUsersMediaServers from "../../../hooks/use-user-media-servers";
import useCurrentAccount from "../../../hooks/use-current-account";
import useSettingsForm from "../use-settings-form";
import VerticalPageLayout from "../../../components/vertical-page-layout";
import localSettings from "../../../services/local-settings";
import useSubject from "../../../hooks/use-subject";
export default function PostSettings() {
const account = useCurrentAccount();
@ -64,6 +67,8 @@ export default function PostSettings() {
);
};
const addClientTag = useSubject(localSettings.addClientTag);
return (
<VerticalPageLayout as="form" onSubmit={submit} flex={1}>
<Heading size="md">Post Settings</Heading>
@ -158,6 +163,26 @@ export default function PostSettings() {
<span>How much Proof of work to mine when writing notes. setting this to 0 will disable it</span>
</FormHelperText>
</FormControl>
<FormControl>
<Flex alignItems="center">
<FormLabel htmlFor="autoShowMedia" mb="0">
Add client tag
</FormLabel>
<Switch
id="autoShowMedia"
isChecked={addClientTag}
onChange={() => localSettings.addClientTag.next(!localSettings.addClientTag.value)}
/>
</Flex>
<FormHelperText>
Enabled: Attach the{" "}
<Link isExternal href="https://github.com/nostr-protocol/nips/blob/master/89.md#client-tag">
NIP-89
</Link>{" "}
client tag to events
</FormHelperText>
</FormControl>
</Flex>
<Button
ml="auto"

View File

@ -30,6 +30,7 @@ import ZapBubbles from "../../../components/note/timeline-note/components/zap-bu
import DetailsTabs from "./details-tabs";
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
import relayHintService from "../../../services/event-relay-hint";
import NotePublishedUsing from "../../../components/note/note-published-using";
export type ThreadItemProps = {
post: ThreadItem;
@ -45,6 +46,7 @@ function ThreadPost({ post, initShowReplies, focusId, level = -1 }: ThreadItemPr
const muteFilter = useClientSideMuteFilter();
const isFocused = level === -1;
const replies = post.replies.filter((r) => !muteFilter(r.event));
const numberOfReplies = countReplies(replies);
const isMuted = muteFilter(post.event);
@ -67,7 +69,6 @@ function ThreadPost({ post, initShowReplies, focusId, level = -1 }: ThreadItemPr
<UserAvatarLink pubkey={post.event.pubkey} size="sm" />
<UserLink pubkey={post.event.pubkey} fontWeight="bold" isTruncated />
<UserDnsIdentity pubkey={post.event.pubkey} onlyIcon />
<POWIcon event={post.event} boxSize={5} />
<Link
as={RouterLink}
whiteSpace="nowrap"
@ -76,19 +77,23 @@ function ThreadPost({ post, initShowReplies, focusId, level = -1 }: ThreadItemPr
>
<Timestamp timestamp={post.event.created_at} />
</Link>
{replies.length > 0 ? (
<Button variant="ghost" onClick={expanded.onToggle} rightIcon={expanded.isOpen ? <Minus /> : <Expand01 />}>
({numberOfReplies})
</Button>
) : (
<IconButton
variant="ghost"
onClick={expanded.onToggle}
icon={expanded.isOpen ? <Minus /> : <Expand01 />}
aria-label={expanded.isOpen ? "Collapse" : "Expand"}
title={expanded.isOpen ? "Collapse" : "Expand"}
/>
)}
<POWIcon event={post.event} boxSize={5} />
<NotePublishedUsing event={post.event} />
<Spacer />
{!isFocused &&
(replies.length > 0 ? (
<Button variant="ghost" onClick={expanded.onToggle} rightIcon={expanded.isOpen ? <Minus /> : <Expand01 />}>
({numberOfReplies})
</Button>
) : (
<IconButton
variant="ghost"
onClick={expanded.onToggle}
icon={expanded.isOpen ? <Minus /> : <Expand01 />}
aria-label={expanded.isOpen ? "Collapse" : "Expand"}
title={expanded.isOpen ? "Collapse" : "Expand"}
/>
))}
</Flex>
);
@ -153,7 +158,7 @@ function ThreadPost({ post, initShowReplies, focusId, level = -1 }: ThreadItemPr
)}
</Flex>
{replyForm.isOpen && <ReplyForm item={post} onCancel={replyForm.onClose} onSubmitted={replyForm.onClose} />}
{level === -1 ? (
{isFocused ? (
<DetailsTabs post={post} />
) : (
expanded.isOpen &&