-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #32 from ostyjs/notifications
Notifications Widget
- Loading branch information
Showing
13 changed files
with
454 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 4 additions & 0 deletions
4
templates/nostribe/src/features/notifications-widget/components/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export * from './mentions-and-replies'; | ||
export * from './reactions'; | ||
export * from './reposts'; | ||
export * from './zaps'; |
19 changes: 19 additions & 0 deletions
19
...ates/nostribe/src/features/notifications-widget/components/mentions-and-replies/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { NDKEvent } from '@nostr-dev-kit/ndk'; | ||
|
||
import { NoteByNoteId } from '@/features/note-widget'; | ||
|
||
export const MentionsAndReplies = ({ | ||
mentionsAndReplies, | ||
}: { | ||
mentionsAndReplies: NDKEvent[] | undefined; | ||
}) => { | ||
return mentionsAndReplies?.map((event) => ( | ||
<> | ||
<div key={event.id} className="px-2 py-1"> | ||
<div className="pt-2 rounded-md border flex flex-col gap-2"> | ||
<NoteByNoteId noteId={event.id} /> | ||
</div> | ||
</div> | ||
</> | ||
)); | ||
}; |
83 changes: 83 additions & 0 deletions
83
templates/nostribe/src/features/notifications-widget/components/reactions/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import { NDKEvent } from '@nostr-dev-kit/ndk'; | ||
import { ThumbsUpIcon } from 'lucide-react'; | ||
import { useRealtimeProfile } from 'nostr-hooks'; | ||
import { memo, useMemo } from 'react'; | ||
import { Link } from 'react-router-dom'; | ||
|
||
import { Avatar, AvatarImage } from '@/shared/components/ui/avatar'; | ||
|
||
import { Spinner } from '@/shared/components/spinner'; | ||
|
||
import { ellipsis } from '@/shared/utils'; | ||
|
||
import { NoteByNoteId } from '@/features/note-widget'; | ||
|
||
type CategorizedReactions = Map<string, NDKEvent[]>; | ||
|
||
export const Reactions = memo(({ reactions }: { reactions: NDKEvent[] | undefined }) => { | ||
const categorizedReactions: CategorizedReactions = useMemo(() => { | ||
const categorizedReactions = new Map<string, NDKEvent[]>(); | ||
|
||
reactions?.forEach((reaction) => { | ||
const eTags = reaction.getMatchingTags('e'); | ||
if (eTags.length > 0) { | ||
const eTag = eTags[eTags.length - 1]; | ||
if (eTag.length > 0) { | ||
const eventId = eTag[1]; | ||
const reactions = categorizedReactions.get(eventId) || []; | ||
reactions.push(reaction); | ||
categorizedReactions.set(eventId, reactions); | ||
} | ||
} | ||
}); | ||
|
||
return categorizedReactions; | ||
}, [reactions]); | ||
|
||
if (reactions === undefined) { | ||
return <Spinner />; | ||
} | ||
|
||
if (reactions.length === 0) { | ||
return <div>No reactions yet</div>; | ||
} | ||
|
||
return ( | ||
<> | ||
{Array.from(categorizedReactions.keys()).map((eventId) => ( | ||
<div key={eventId} className="px-2 py-1"> | ||
<div className="rounded-md border flex flex-col gap-2"> | ||
<div className="flex flex-wrap items-center gap-2 px-2 py-1 border-b"> | ||
<ThumbsUpIcon size={18} /> | ||
|
||
{categorizedReactions | ||
.get(eventId) | ||
?.map((reaction) => <Reaction key={reaction.id} reaction={reaction} />)} | ||
</div> | ||
|
||
<NoteByNoteId noteId={eventId} /> | ||
</div> | ||
</div> | ||
))} | ||
</> | ||
); | ||
}); | ||
|
||
const Reaction = memo(({ reaction }: { reaction: NDKEvent }) => { | ||
const { profile } = useRealtimeProfile(reaction.pubkey); | ||
|
||
return ( | ||
<Link to={`/profile/${reaction.author.npub}`}> | ||
<div className="flex gap-1 items-center"> | ||
<Avatar className="bg-secondary w-5 h-5"> | ||
<AvatarImage src={profile?.image} className="bg-secondary" /> | ||
</Avatar> | ||
|
||
<p className="text-xs font-light"> | ||
<span>{ellipsis(profile?.name?.toString() || reaction.author.npub, 20)}</span> | ||
<span> {reaction.content === '+' ? '👍' : reaction.content}</span> | ||
</p> | ||
</div> | ||
</Link> | ||
); | ||
}); |
82 changes: 82 additions & 0 deletions
82
templates/nostribe/src/features/notifications-widget/components/reposts/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { NDKEvent } from '@nostr-dev-kit/ndk'; | ||
import { Repeat2Icon } from 'lucide-react'; | ||
import { useRealtimeProfile } from 'nostr-hooks'; | ||
import { memo, useMemo } from 'react'; | ||
import { Link } from 'react-router-dom'; | ||
|
||
import { Avatar, AvatarImage } from '@/shared/components/ui/avatar'; | ||
|
||
import { Spinner } from '@/shared/components/spinner'; | ||
|
||
import { ellipsis } from '@/shared/utils'; | ||
|
||
import { NoteByNoteId } from '@/features/note-widget'; | ||
|
||
type CategorizedReposts = Map<string, NDKEvent[]>; | ||
|
||
export const Reposts = memo(({ reposts }: { reposts: NDKEvent[] | undefined }) => { | ||
const categorizedReposts: CategorizedReposts = useMemo(() => { | ||
const categorizedReposts = new Map<string, NDKEvent[]>(); | ||
|
||
reposts?.forEach((repost) => { | ||
const eTags = repost.getMatchingTags('e'); | ||
if (eTags.length > 0) { | ||
const eTag = eTags[eTags.length - 1]; | ||
if (eTag.length > 0) { | ||
const eventId = eTag[1]; | ||
const reposts = categorizedReposts.get(eventId) || []; | ||
reposts.push(repost); | ||
categorizedReposts.set(eventId, reposts); | ||
} | ||
} | ||
}); | ||
|
||
return categorizedReposts; | ||
}, [reposts]); | ||
|
||
if (reposts === undefined) { | ||
return <Spinner />; | ||
} | ||
|
||
if (reposts.length === 0) { | ||
return <div>No reposts yet</div>; | ||
} | ||
|
||
return ( | ||
<> | ||
{Array.from(categorizedReposts.keys()).map((eventId) => ( | ||
<div key={eventId} className="px-2 py-1"> | ||
<div className="rounded-md border flex flex-col gap-2"> | ||
<div className="flex flex-wrap items-center gap-2 px-2 py-1 border-b"> | ||
<Repeat2Icon size={18} /> | ||
|
||
{categorizedReposts | ||
.get(eventId) | ||
?.map((repost) => <Repost key={repost.id} repost={repost} />)} | ||
</div> | ||
|
||
<NoteByNoteId noteId={eventId} /> | ||
</div> | ||
</div> | ||
))} | ||
</> | ||
); | ||
}); | ||
|
||
const Repost = memo(({ repost }: { repost: NDKEvent }) => { | ||
const { profile } = useRealtimeProfile(repost.pubkey); | ||
|
||
return ( | ||
<Link to={`/profile/${repost.author.npub}`}> | ||
<div className="flex gap-1 items-center"> | ||
<Avatar className="bg-secondary w-5 h-5"> | ||
<AvatarImage src={profile?.image} className="bg-secondary" /> | ||
</Avatar> | ||
|
||
<p className="text-xs font-light"> | ||
<span>{ellipsis(profile?.name?.toString() || repost.author.npub, 20)}</span> | ||
</p> | ||
</div> | ||
</Link> | ||
); | ||
}); |
89 changes: 89 additions & 0 deletions
89
templates/nostribe/src/features/notifications-widget/components/zaps/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { NDKEvent, NDKUser, zapInvoiceFromEvent } from '@nostr-dev-kit/ndk'; | ||
import { ZapIcon } from 'lucide-react'; | ||
import { useRealtimeProfile } from 'nostr-hooks'; | ||
import { memo, useMemo } from 'react'; | ||
import { Link } from 'react-router-dom'; | ||
|
||
import { Avatar, AvatarImage } from '@/shared/components/ui/avatar'; | ||
|
||
import { Spinner } from '@/shared/components/spinner'; | ||
|
||
import { ellipsis } from '@/shared/utils'; | ||
|
||
import { NoteByNoteId } from '@/features/note-widget'; | ||
|
||
type CategorizedZaps = Map<string, NDKEvent[]>; | ||
|
||
export const Zaps = memo(({ zaps }: { zaps: NDKEvent[] | undefined }) => { | ||
const categorizedZaps: CategorizedZaps = useMemo(() => { | ||
const categorizedZaps = new Map<string, NDKEvent[]>(); | ||
|
||
zaps?.forEach((zap) => { | ||
const eTags = zap.getMatchingTags('e'); | ||
if (eTags.length > 0) { | ||
const eTag = eTags[eTags.length - 1]; | ||
if (eTag.length > 0) { | ||
const eventId = eTag[1]; | ||
const zaps = categorizedZaps.get(eventId) || []; | ||
zaps.push(zap); | ||
categorizedZaps.set(eventId, zaps); | ||
} | ||
} | ||
}); | ||
|
||
return categorizedZaps; | ||
}, [zaps]); | ||
|
||
if (zaps === undefined) { | ||
return <Spinner />; | ||
} | ||
|
||
if (zaps.length === 0) { | ||
return <div>No zaps yet</div>; | ||
} | ||
|
||
return ( | ||
<> | ||
{Array.from(categorizedZaps.keys()).map((eventId) => ( | ||
<div key={eventId} className="px-2 py-1"> | ||
<div className="rounded-md border flex flex-col gap-2"> | ||
<div className="flex flex-wrap items-center gap-2 px-2 py-1 border-b"> | ||
<ZapIcon size={18} /> | ||
|
||
{categorizedZaps.get(eventId)?.map((zap) => <Zap key={zap.id} zap={zap} />)} | ||
</div> | ||
|
||
<NoteByNoteId noteId={eventId} /> | ||
</div> | ||
</div> | ||
))} | ||
</> | ||
); | ||
}); | ||
|
||
const Zap = memo( | ||
({ zap }: { zap: NDKEvent }) => { | ||
const invoice = zapInvoiceFromEvent(zap); | ||
const { profile } = useRealtimeProfile(invoice?.zappee); | ||
const npub = useMemo( | ||
() => (invoice && invoice.zapper ? new NDKUser({ pubkey: invoice.zapper }).npub : ''), | ||
[invoice?.zapper], | ||
); | ||
|
||
return ( | ||
<Link to={`/profile/${npub}`}> | ||
<div className="flex gap-1 items-center"> | ||
<Avatar className="bg-secondary w-5 h-5"> | ||
<AvatarImage src={profile?.image} className="bg-secondary" /> | ||
</Avatar> | ||
|
||
<p className="text-xs font-light"> | ||
<span className="font-bold"> {(invoice?.amount || 0) / 1000} sats</span> | ||
<span> from {ellipsis(profile?.name ? profile.name.toString() : npub, 20)}</span> | ||
</p> | ||
</div> | ||
</Link> | ||
); | ||
}, | ||
(prevProps, nextProps) => prevProps.zap.id === nextProps.zap.id, | ||
); |
Oops, something went wrong.