-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
185 additions
and
24 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"nostrudel": minor | ||
--- | ||
|
||
Show profile badges on users profile |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { useMemo } from "react"; | ||
|
||
import singleEventService from "../services/single-event"; | ||
import { useReadRelayUrls } from "./use-client-relays"; | ||
import useSubjects from "./use-subjects"; | ||
|
||
export default function useSingleEvents(ids?: string[], additionalRelays: string[] = []) { | ||
const readRelays = useReadRelayUrls(additionalRelays); | ||
const subjects = useMemo(() => { | ||
return ids?.map((id) => singleEventService.requestEvent(id, readRelays)) ?? []; | ||
}, [ids, readRelays.join("|")]); | ||
|
||
return useSubjects(subjects); | ||
} |
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,30 @@ | ||
import { Kind } from "nostr-tools"; | ||
|
||
import useReplaceableEvent from "./use-replaceable-event"; | ||
import { PROFILE_BADGES_IDENTIFIER, parseProfileBadges } from "../helpers/nostr/badges"; | ||
import useReplaceableEvents from "./use-replaceable-events"; | ||
import useSingleEvents from "./use-single-events"; | ||
import { getEventCoordinate } from "../helpers/nostr/events"; | ||
import { NostrEvent } from "../types/nostr-event"; | ||
|
||
export default function useUserProfileBadges(pubkey: string, additionalRelays: string[] = []) { | ||
const profileBadgesEvent = useReplaceableEvent({ | ||
pubkey, | ||
kind: Kind.ProfileBadge, | ||
identifier: PROFILE_BADGES_IDENTIFIER, | ||
}); | ||
const parsed = profileBadgesEvent ? parseProfileBadges(profileBadgesEvent) : []; | ||
|
||
const badges = useReplaceableEvents(parsed.map((b) => b.badgeCord)); | ||
const awardEvents = useSingleEvents(parsed.map((b) => b.awardEventId)); | ||
|
||
const final: { badge: NostrEvent; award: NostrEvent }[] = []; | ||
for (const p of parsed) { | ||
const badge = badges.find((e) => getEventCoordinate(e) === p.badgeCord); | ||
const award = awardEvents.find((e) => e.id === p.awardEventId); | ||
|
||
if (badge && award) final.push({ badge, award }); | ||
} | ||
|
||
return final; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { | ||
Button, | ||
Flex, | ||
FlexProps, | ||
Heading, | ||
Image, | ||
Link, | ||
Modal, | ||
ModalBody, | ||
ModalCloseButton, | ||
ModalContent, | ||
ModalFooter, | ||
ModalHeader, | ||
ModalOverlay, | ||
Text, | ||
Tooltip, | ||
useDisclosure, | ||
} from "@chakra-ui/react"; | ||
import { Link as RouterLink } from "react-router-dom"; | ||
|
||
import useUserProfileBadges from "../../../hooks/use-user-profile-badges"; | ||
import { getBadgeDescription, getBadgeImage, getBadgeName } from "../../../helpers/nostr/badges"; | ||
import { getEventCoordinate } from "../../../helpers/nostr/events"; | ||
import { NostrEvent } from "../../../types/nostr-event"; | ||
import { getSharableEventAddress } from "../../../helpers/nip19"; | ||
import { UserAvatarLink } from "../../../components/user-avatar-link"; | ||
import { UserLink } from "../../../components/user-link"; | ||
import Timestamp from "../../../components/timestamp"; | ||
|
||
function Badge({ pubkey, badge, award }: { pubkey: string; badge: NostrEvent; award: NostrEvent }) { | ||
const naddr = getSharableEventAddress(badge); | ||
const modal = useDisclosure(); | ||
const description = getBadgeDescription(badge); | ||
|
||
return ( | ||
<> | ||
<Link | ||
as={RouterLink} | ||
to={`/badges/${naddr}`} | ||
onClick={(e) => { | ||
e.preventDefault(); | ||
modal.onOpen(); | ||
}} | ||
> | ||
<Tooltip label={getBadgeName(badge)}> | ||
<Image w="14" h="14" src={getBadgeImage(badge)?.src ?? ""} /> | ||
</Tooltip> | ||
</Link> | ||
<Modal isOpen={modal.isOpen} onClose={modal.onClose}> | ||
<ModalOverlay /> | ||
<ModalContent> | ||
<Image src={getBadgeImage(badge)?.src ?? ""} /> | ||
<ModalCloseButton /> | ||
<ModalHeader px="4" pt="2" pb="0"> | ||
{getBadgeName(badge)} | ||
</ModalHeader> | ||
<ModalBody px="4" py="2"> | ||
<Text> | ||
Created by <UserAvatarLink pubkey={badge.pubkey} size="xs" />{" "} | ||
<UserLink fontWeight="bold" pubkey={badge.pubkey} /> on <Timestamp timestamp={badge.created_at} /> | ||
</Text> | ||
<Text> | ||
Date Awarded: <Timestamp timestamp={award.created_at} /> | ||
</Text> | ||
<Heading size="sm" mt="4"> | ||
Description: | ||
</Heading> | ||
{description && <Text>{description}</Text>} | ||
</ModalBody> | ||
<ModalFooter p="4"> | ||
<Button as={RouterLink} to={`/badges/${naddr}`}> | ||
Details | ||
</Button> | ||
</ModalFooter> | ||
</ModalContent> | ||
</Modal> | ||
</> | ||
); | ||
} | ||
|
||
export default function UserProfileBadges({ pubkey, ...props }: Omit<FlexProps, "children"> & { pubkey: string }) { | ||
const profileBadges = useUserProfileBadges(pubkey); | ||
|
||
if (profileBadges.length === 0) return null; | ||
|
||
return ( | ||
<Flex gap="2" wrap="wrap" {...props}> | ||
{profileBadges.map(({ badge, award }) => ( | ||
<Badge key={getEventCoordinate(badge)} pubkey={pubkey} badge={badge} award={award} /> | ||
))} | ||
</Flex> | ||
); | ||
} |