feat: Added repost button

This commit is contained in:
florian 2024-03-05 16:36:48 +00:00
parent d74d9d86df
commit e9f626fa6c
6 changed files with 125 additions and 23 deletions

View File

@ -51,26 +51,46 @@ const Home = () => {
</div>
{state.userNPub && (
<>
{' '}
<h2>Your...</h2>
<div
className="topic"
style={{}}
onClick={() =>
nav({
...currentSettings,
topic: undefined,
npubs: [],
tags: [],
list: undefined,
follows: true,
showReplies: false,
showReposts: true,
showAdult,
})
}
>
<div className="topic-title">Follows</div>
<div className="topics">
<div
className="topic"
style={{}}
onClick={() =>
nav({
...currentSettings,
topic: undefined,
npubs: [],
tags: [],
list: undefined,
follows: true,
showReplies: false,
showReposts: true,
showAdult,
})
}
>
<div className="topic-title">Your follows</div>
</div>
<div
className="topic"
style={{}}
onClick={() =>
nav({
...currentSettings,
topic: undefined,
npubs: [state.userNPub || ''],
tags: [],
list: undefined,
follows: false,
showReplies: false,
showReposts: true,
showAdult,
})
}
>
<div className="topic-title">Your own profile</div>
</div>
</div>
</>
)}

View File

@ -0,0 +1,16 @@
const IconRepost = () => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 19" fill="none">
<g>
<path
d="M12.2197 1.65717C12.5126 1.36428 12.9874 1.36428 13.2803 1.65717L16.2803 4.65717C16.5732 4.95006 16.5732 5.42494 16.2803 5.71783L13.2803 8.71783C12.9874 9.01072 12.5126 9.01072 12.2197 8.71783C11.9268 8.42494 11.9268 7.95006 12.2197 7.65717L13.9393 5.9375H5.85C5.20757 5.9375 4.77085 5.93808 4.43328 5.96566C4.10447 5.99253 3.93632 6.04122 3.81902 6.10099C3.53677 6.2448 3.3073 6.47427 3.16349 6.75652C3.10372 6.87381 3.05503 7.04197 3.02816 7.37078C3.00058 7.70835 3 8.14507 3 8.7875V8.9375C3 9.35171 2.66421 9.6875 2.25 9.6875C1.83579 9.6875 1.5 9.35171 1.5 8.9375V8.75653C1.49999 8.15281 1.49998 7.65452 1.53315 7.24863C1.56759 6.82706 1.64151 6.43953 1.82698 6.07553C2.1146 5.51104 2.57354 5.0521 3.13803 4.76448C3.50203 4.57901 3.88956 4.50509 4.31113 4.47065C4.71703 4.43748 5.2153 4.43749 5.81903 4.4375L13.9393 4.4375L12.2197 2.71783C11.9268 2.42494 11.9268 1.95006 12.2197 1.65717Z"
fill="currentColor"
/>
<path
d="M15.75 9.6875C15.3358 9.6875 15 10.0233 15 10.4375V10.5875C15 11.2299 14.9994 11.6667 14.9718 12.0042C14.945 12.333 14.8963 12.5012 14.8365 12.6185C14.6927 12.9007 14.4632 13.1302 14.181 13.274C14.0637 13.3338 13.8955 13.3825 13.5667 13.4093C13.2292 13.4369 12.7924 13.4375 12.15 13.4375H4.06066L5.78033 11.7178C6.07322 11.4249 6.07322 10.9501 5.78033 10.6572C5.48744 10.3643 5.01256 10.3643 4.71967 10.6572L1.71967 13.6572C1.42678 13.9501 1.42678 14.4249 1.71967 14.7178L4.71967 17.7178C5.01256 18.0107 5.48744 18.0107 5.78033 17.7178C6.07322 17.4249 6.07322 16.9501 5.78033 16.6572L4.06066 14.9375H12.181C12.7847 14.9375 13.283 14.9375 13.6889 14.9044C14.1104 14.8699 14.498 14.796 14.862 14.6105C15.4265 14.3229 15.8854 13.864 16.173 13.2995C16.3585 12.9355 16.4324 12.5479 16.4669 12.1264C16.5 11.7205 16.5 11.2222 16.5 10.6185V10.4375C16.5 10.0233 16.1642 9.6875 15.75 9.6875Z"
fill="currentColor"
/>
</g>
</svg>
);
export default IconRepost;

View File

@ -133,6 +133,14 @@
color: white;
}
.bottom-controls button.reposted {
cursor: default;
}
.bottom-controls button.reposted svg {
color: lawngreen;
}
.bottomPanel {
position: absolute;
bottom: 0px;

View File

@ -35,6 +35,7 @@ import useWindowSize from '../utils/useWindowSize';
import { useNavigate } from 'react-router-dom';
import { useAtom } from 'jotai';
import { followsAtom } from '../ngine/state';
import IconRepost from './Icons/IconRepost';
// type AlbyNostr = typeof window.nostr & { enabled: boolean };
@ -85,12 +86,12 @@ const SlideShow = () => {
const { nav, currentSettings: settings } = useNav();
const [state] = useGlobalState();
const [imageIdx, setImageIdx] = useState<number | undefined>();
const { zapClick, heartClick, zapState, heartState } = useZapsAndReations(state.activeImage, state.userNPub);
const { zapClick, heartClick, zapState, heartState, repostClick, repostState } = useZapsAndReations(state.activeImage, state.userNPub);
const navigate = useNavigate();
state.profile;
const listAuthors = useAuthorsFromList(settings.list);
const [contacts] = useAtom(followsAtom);
const filter = useMemo(() => {
const authorsToQuery = settings.follows
? contacts?.tags.filter(t => t[0] === 'p').map(t => t[1]) || []
@ -114,7 +115,7 @@ const SlideShow = () => {
const { events } = useEvents(filter, {
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL,
// when seeing global, close stream because of too many updates.
closeOnEose: settings.npubs.length == 0 && settings.tags.length == 0
closeOnEose: settings.npubs.length == 0 && settings.tags.length == 0,
});
useEffect(() => {
@ -324,6 +325,9 @@ const SlideShow = () => {
<div className="bottom-controls">
{state.userNPub && state.activeImage && (
<>
<button className={`repost ${repostState?'reposted':''}`} onClick={() => !repostState && repostClick()}>
<IconRepost></IconRepost>
</button>
<button
className={`heart ${heartState}`}
onClick={() => state.activeImage && heartClick(state.activeImage)}

19
src/utils/useReposts.ts Normal file
View File

@ -0,0 +1,19 @@
import { useMemo } from 'react';
import useEvents from '../ngine/hooks/useEvents';
import { nip19 } from 'nostr-tools';
const KIND_REPOST = 6;
const useReposts = (npub?: string) => {
const pubkey = npub && (nip19.decode(npub).data as string);
const { events } = useEvents(
{ kinds: [KIND_REPOST], authors: pubkey ? [pubkey] : [], limit: 100 },
{ disable: !npub }
);
const reposts = useMemo(() => events.flatMap(e => e.getMatchingTags('e').map(t => t[1]).flat()), [events]);
return reposts;
};
export default useReposts;

View File

@ -2,13 +2,15 @@ import { useNDK } from '../ngine/context';
import { NostrImage } from '../components/nostrImageDownload';
import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk';
import { nip19 } from 'nostr-tools';
import { useEffect, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import useReposts from './useReposts';
export type HeartState = 'none' | 'liked' | 'liking';
export type ZapState = 'none' | 'zapped' | 'zapping' | 'error';
const useZapsAndReations = (currentImageData?: NostrImage, userNPub?: string) => {
const ndk = useNDK();
const reposts = useReposts(userNPub);
const [zapState, setZapState] = useState<ZapState>('none');
const [heartState, setHeartState] = useState<HeartState>('none');
@ -23,6 +25,11 @@ const useZapsAndReations = (currentImageData?: NostrImage, userNPub?: string) =>
return { selfLiked: events && events.size > 0 };
};
const repostState = useMemo(
() => reposts.some(r => r == currentImageData?.post.event.id),
[currentImageData?.post.event.id, reposts]
);
useEffect(() => {
setZapState('none');
setHeartState('none');
@ -98,11 +105,39 @@ const useZapsAndReations = (currentImageData?: NostrImage, userNPub?: string) =>
currentImage.post.wasZapped = true;
};
const repostClick = async () => {
if (!userNPub) return;
const orgEvent = currentImageData?.post.event;
if (!orgEvent) return;
const relayUrl = orgEvent.relay?.url;
if (!relayUrl) {
console.error('no relay url found for original event.');
return;
}
const repostEvent = new NDKEvent(ndk, {
kind: 6, // Repost
pubkey: nip19.decode(userNPub).data as string,
created_at: Math.floor(new Date().getTime() / 1000),
content: JSON.stringify(orgEvent.rawEvent),
tags: [
['e', orgEvent.id, relayUrl],
['p', orgEvent.author.pubkey],
],
});
console.log(repostEvent);
await repostEvent.publish();
};
return {
zapState,
heartState,
zapClick,
heartClick,
repostClick,
repostState,
};
};