Skip to content

Commit

Permalink
Merge pull request #24 from ostyjs/performance-to-the-moon
Browse files Browse the repository at this point in the history
Performance to the moon
  • Loading branch information
sepehr-safari authored Jan 5, 2025
2 parents 42aa595 + 24de5a0 commit 79e2444
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 175 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-osty",
"version": "0.6.1",
"version": "0.6.2",
"type": "module",
"license": "MIT",
"author": "Sepehr Safari",
Expand Down
4 changes: 3 additions & 1 deletion templates/react-shadcn/src/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ export const App = () => {
const { loginFromLocalStorage } = useLogin();

useEffect(() => {
initNdk({ explicitRelayUrls: ['wss://nos.lol', 'wss://relay.primal.net'] });
initNdk({
explicitRelayUrls: ['wss://nos.lol', 'wss://relay.primal.net', 'wss://relay.nostr.band'],
});
}, [initNdk]);

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NDKEvent, NDKUser } from '@nostr-dev-kit/ndk';
import { useRealtimeProfile } from 'nostr-hooks';
import { useMemo } from 'react';
import { memo, useMemo } from 'react';
import { Link } from 'react-router-dom';

import { ellipsis } from '@/shared/utils';
Expand All @@ -9,85 +9,99 @@ import { NoteByNoteId } from '@/features/note-widget';

import { useNoteContent } from './hooks';

export const NoteContent = ({ event }: { event: NDKEvent }) => {
const { chunks, inView, ref } = useNoteContent(event.content);
export const NoteContent = memo(
({ event }: { event: NDKEvent }) => {
const { chunks, inView, ref } = useNoteContent(event.content);

return (
<div className="pb-2" ref={ref}>
{chunks.map((chunk, index) => {
switch (chunk.type) {
case 'text':
case 'naddr':
case 'nevent':
return (
<span key={index} className="[overflow-wrap:anywhere]">
{chunk.content}
</span>
);
case 'image':
return (
<img key={index} src={chunk.content} alt="Image" loading="lazy" className="w-full" />
);
case 'video':
return <video key={index} src={chunk.content} controls className="w-full" />;
case 'youtube':
return (
<iframe
key={index}
src={`https://www.youtube.com/embed/${chunk.content}`}
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
className="w-full"
/>
);
case 'url':
return (
<a
key={index}
href={chunk.content}
target="_blank"
rel="noreferrer"
className="text-blue-700 hover:underline [overflow-wrap:anywhere]"
>
{chunk.content}
</a>
);
case 'note':
return (
<>
<div className="p-4 bg-secondary/50">
<NoteByNoteId key={index} noteId={chunk.content} />
</div>
</>
);
case 'nprofile':
case 'npub':
if (inView) {
return <ProfileMention key={index} pubkey={chunk.content} />;
} else {
return (
<div className="pb-2" ref={ref}>
{chunks.map((chunk, index) => {
switch (chunk.type) {
case 'text':
case 'naddr':
case 'nevent':
return (
<span key={index} className="[overflow-wrap:anywhere]">
{chunk.content}
</span>
);
case 'image':
return (
<img
key={index}
src={chunk.content}
alt="Image"
loading="lazy"
className="w-full"
/>
);
case 'video':
return <video key={index} src={chunk.content} controls className="w-full" />;
case 'youtube':
return (
<iframe
key={index}
src={`https://www.youtube.com/embed/${chunk.content}`}
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
className="w-full"
/>
);
case 'url':
return (
<a
key={index}
href={chunk.content}
target="_blank"
rel="noreferrer"
className="text-blue-700 hover:underline [overflow-wrap:anywhere]"
>
{chunk.content}
</a>
);
case 'note':
if (inView) {
return (
<div className="p-4 bg-secondary/50">
<NoteByNoteId key={index} noteId={chunk.content} />
</div>
);
} else {
return null;
}
case 'nprofile':
case 'npub':
if (inView) {
return <ProfileMention key={index} pubkey={chunk.content} />;
} else {
return null;
}
default:
return null;
}
default:
return null;
}
})}
</div>
);
};
}
})}
</div>
);
},
(prev, next) => prev.event.id === next.event.id,
);

const ProfileMention = ({ pubkey }: { pubkey: string }) => {
const npub = useMemo(() => {
return new NDKUser({ pubkey }).npub;
}, [pubkey]);
const ProfileMention = memo(
({ pubkey }: { pubkey: string }) => {
const npub = useMemo(() => {
return new NDKUser({ pubkey }).npub;
}, [pubkey]);

const { profile } = useRealtimeProfile(pubkey);
const { profile } = useRealtimeProfile(pubkey);

return (
<>
<Link to={`/profile/${npub}`} className="text-purple-700 hover:underline">
@{profile?.name || ellipsis(npub || '', 10)}
</Link>
</>
);
};
return (
<>
<Link to={`/profile/${npub}`} className="text-purple-700 hover:underline">
@{profile?.name || ellipsis(npub || '', 10)}
</Link>
</>
);
},
(prev, next) => prev.pubkey === next.pubkey,
);
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { NDKEvent } from '@nostr-dev-kit/ndk';
import { memo } from 'react';

import { NoteCommentsWidget } from '@/features/note-comments-widget';

Expand All @@ -12,28 +13,31 @@ import {

import { useNoteFooter } from './hooks';

export const NoteFooter = ({ event }: { event: NDKEvent }) => {
const { inView, ref, setShowingComments, showingComments } = useNoteFooter();
export const NoteFooter = memo(
({ event }: { event: NDKEvent }) => {
const { inView, ref, setShowingComments, showingComments } = useNoteFooter();

return (
<>
<div className="flex items-center justify-between gap-2" ref={ref}>
<NoteCommentBtn
onClick={() => setShowingComments((prev) => !prev)}
event={event}
inView={inView}
/>
return (
<>
<div className="flex items-center justify-between gap-2" ref={ref}>
<NoteCommentBtn
onClick={() => setShowingComments((prev) => !prev)}
event={event}
inView={inView}
/>

<NoteZapBtn event={event} inView={inView} />
<NoteZapBtn event={event} inView={inView} />

<NoteLikeBtn event={event} inView={inView} />
<NoteLikeBtn event={event} inView={inView} />

<NoteRepostBtn event={event} inView={inView} />
<NoteRepostBtn event={event} inView={inView} />

<NoteBookmarkBtn event={event} inView={inView} />
</div>
<NoteBookmarkBtn event={event} inView={inView} />
</div>

{showingComments && <NoteCommentsWidget event={event} />}
</>
);
};
{showingComments && <NoteCommentsWidget event={event} />}
</>
);
},
(prev, next) => prev.event.id === next.event.id,
);
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const NoteZapBtn = ({ event, inView }: { event: NDKEvent; inView: boolean

return (
<>
<ZapWidget target={event}>
<ZapWidget target={inView ? event : undefined}>
<Button
variant="link"
size="icon"
Expand Down
80 changes: 43 additions & 37 deletions templates/react-shadcn/src/features/note-widget/index.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,53 @@
import { NDKEvent } from '@nostr-dev-kit/ndk';
import { useNdk } from 'nostr-hooks';
import { useEffect, useState } from 'react';
import { memo, useEffect, useState } from 'react';

import { Spinner } from '@/shared/components/spinner';

import { NoteContent, NoteFooter, NoteHeader } from './components';

export const NoteByEvent = ({ event }: { event: NDKEvent | null | undefined }) => {
if (event === undefined) {
return <Spinner />;
}

if (event === null) {
return <div className="px-2 border-b">Note not found</div>;
}

if (event) {
return (
<>
<div className="px-2">
<NoteHeader event={event} />
<NoteContent event={event} />
<NoteFooter event={event} />
</div>
</>
);
}
};

export const NoteByNoteId = ({ noteId }: { noteId: string | undefined }) => {
const [event, setEvent] = useState<NDKEvent | null | undefined>(undefined);

const { ndk } = useNdk();

useEffect(() => {
if (!ndk || !noteId) {
return;
export const NoteByEvent = memo(
({ event }: { event: NDKEvent | null | undefined }) => {
if (event === undefined) {
return <Spinner />;
}

ndk.fetchEvent(noteId).then((event) => {
setEvent(event);
});
}, [noteId, ndk, setEvent]);
if (event === null) {
return <div className="px-2 border-b">Note not found</div>;
}

return <NoteByEvent event={event} />;
};
if (event) {
return (
<>
<div className="px-2">
<NoteHeader event={event} />
<NoteContent event={event} />
<NoteFooter event={event} />
</div>
</>
);
}
},
(prev, next) => prev.event?.id === next.event?.id,
);

export const NoteByNoteId = memo(
({ noteId }: { noteId: string | undefined }) => {
const [event, setEvent] = useState<NDKEvent | null | undefined>(undefined);

const { ndk } = useNdk();

useEffect(() => {
if (!ndk || !noteId) {
return;
}

ndk.fetchEvent(noteId).then((event) => {
setEvent(event);
});
}, [noteId, ndk, setEvent]);

return <NoteByEvent event={event} />;
},
(prev, next) => prev.noteId === next.noteId,
);
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ export const NotesFeedWidget = () => {
) : processedEvents ? (
<div className="pt-2 flex flex-col gap-2">
{processedEvents.map((event, i) => (
<div className={cn({ 'border-b': i !== processedEvents.length - 1 })}>
<NoteByEvent key={event.id} event={event} />
<div key={event.id} className={cn({ 'border-b': i !== processedEvents.length - 1 })}>
<NoteByEvent event={event} />
</div>
))}
</div>
Expand All @@ -61,7 +61,7 @@ export const NotesFeedWidget = () => {

{hasMore && (
<div className="py-4 flex justify-center">
<Button variant="secondary" onClick={() => loadMore(50)} className="w-full">
<Button variant="secondary" onClick={() => loadMore(100)} className="w-full">
Load more
</Button>
</div>
Expand Down
Loading

0 comments on commit 79e2444

Please sign in to comment.