fix vine reactions and kind 16 repost for addressable events

- VineHeartButton: add optimistic cache updates and query invalidation on
  reaction so the heart fills and count increments immediately without
  a page refresh
- RepostMenu: include NIP-18 'a' tag (kind:pubkey:d-tag) when reposting
  addressable events (30000–39999) so kind 16 events are properly linked
  back to the original vine
This commit is contained in:
Chad Curtis
2026-03-03 00:36:55 -06:00
parent 2ecd89b368
commit 530f5b4d0d
2 changed files with 38 additions and 2 deletions
+5
View File
@@ -58,6 +58,11 @@ export function RepostMenu({ event, children }: RepostMenuProps) {
// Kind 16 generic reposts require a 'k' tag with the original event's kind
if (repostKind === 16) {
tags.push(['k', String(event.kind)]);
// Addressable events (3000039999) should include an 'a' tag per NIP-18
if (event.kind >= 30000 && event.kind < 40000) {
const dTag = event.tags.find(([name]) => name === 'd')?.[1] ?? '';
tags.push(['a', `${event.kind}:${event.pubkey}:${dTag}`]);
}
}
publishEvent(
+33 -2
View File
@@ -5,6 +5,7 @@ import {
useCallback,
useMemo,
} from 'react';
import { useQueryClient } from '@tanstack/react-query';
import { Link } from 'react-router-dom';
import { useSeoMeta } from '@unhead/react';
import {
@@ -24,7 +25,7 @@ import { useQuery } from '@tanstack/react-query';
import { useStreamKind } from '@/hooks/useStreamKind';
import { useAuthor } from '@/hooks/useAuthor';
import { useCurrentUser } from '@/hooks/useCurrentUser';
import { useEventStats } from '@/hooks/useTrending';
import { useEventStats, type EventStats } from '@/hooks/useTrending';
import { useAppContext } from '@/hooks/useAppContext';
import { useBlossomFallback } from '@/hooks/useBlossomFallback';
import { useLayoutOptions } from '@/contexts/LayoutContext';
@@ -128,12 +129,42 @@ export function VineHeartButton({ event, label, noBackground }: { event: NostrEv
const { user } = useCurrentUser();
const userReaction = useUserReaction(event.id);
const { mutate: publishEvent } = useNostrPublish();
const queryClient = useQueryClient();
const hasReacted = !!userReaction;
const handleClick = (e: React.MouseEvent) => {
e.stopPropagation();
if (!user || hasReacted) return;
publishEvent({ kind: 7, content: '+', tags: [['e', event.id], ['p', event.pubkey], ['k', String(event.kind)]] });
// Optimistically update stats cache
const prevStats = queryClient.getQueryData<EventStats>(['event-stats', event.id]);
if (prevStats) {
queryClient.setQueryData<EventStats>(['event-stats', event.id], {
...prevStats,
reactions: prevStats.reactions + 1,
});
}
// Optimistically mark user as having reacted
queryClient.setQueryData(['user-reaction', event.id], { content: '👍' });
publishEvent(
{ kind: 7, content: '+', tags: [['e', event.id], ['p', event.pubkey], ['k', String(event.kind)]] },
{
onSuccess: () => {
setTimeout(() => {
queryClient.invalidateQueries({ queryKey: ['event-stats', event.id] });
queryClient.invalidateQueries({ queryKey: ['event-interactions', event.id] });
}, 3000);
},
onError: () => {
// Revert optimistic updates
if (prevStats) {
queryClient.setQueryData<EventStats>(['event-stats', event.id], prevStats);
}
queryClient.removeQueries({ queryKey: ['user-reaction', event.id] });
},
},
);
};
return (