Skip to content

Commit

Permalink
Add contribution collection to /profile, improve card (#1454)
Browse files Browse the repository at this point in the history
* feat: add collection and checkmark

* chore: add extended collection

* fix: add line clamp to edit contribution select

* feat: add point image to contribution

* fix: show cards if no point is received

* feat: add points text and leaderboard ranking

* chore: copy static folder to public

* chore: render contribution card if no collection is present

* feat: add nft to collection

* feat: add pins

* refactor: cleanup collection

* fix: remove checkmark, allow longer label

* feat: add tooltip to collection

* fix: prevent wrap on points text

* chore: remove NFT from tooltip

* chore: remove src/static dir duplication

* chore: polish borders
  • Loading branch information
dominik-stumpf authored Sep 9, 2024
1 parent ec9987f commit 83a34be
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 23 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"@emotion/styled": "^11.11.0",
"@fuels/connectors": "^0.5.0",
"@fuels/react": "^0.20.0",
"@guildxyz/types": "^1.10.2",
"@guildxyz/types": "^1.10.8",
"@hcaptcha/react-hcaptcha": "^1.4.4",
"@hookform/resolvers": "^3.3.4",
"@lexical/code": "^0.12.0",
Expand Down
23 changes: 23 additions & 0 deletions src/app/(marketing)/profile/[username]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,25 @@ const fetchPublicProfileData = async ({
},
}
)
const collectionRequests = contributions.map(
({ id }) =>
new URL(`v2/profiles/${username}/contributions/${id}/collection`, api)
)
let collections: Schemas["ContributionCollection"][] | undefined
try {
collections = await Promise.all(
collectionRequests.map((req) =>
ssrFetcher<Schemas["ContributionCollection"]>(req, {
next: {
tags: ["collections"],
revalidate: 3600,
},
})
)
)
} catch (e) {
console.error(e)
}
const roleRequests = contributions.map(
({ roleId, guildId }) => new URL(`v2/guilds/${guildId}/roles/${roleId}`, api)
)
Expand All @@ -134,6 +153,9 @@ const fetchPublicProfileData = async ({
})
)
)
const collectionsZipped = collections
? collectionRequests.map(({ pathname }, i) => [pathname, collections[i]])
: []
const guildsZipped = guildRequests.map(({ pathname }, i) => [pathname, guilds[i]])
const rolesZipped = roleRequests.map(({ pathname }, i) => [pathname, roles[i]])
return {
Expand All @@ -145,6 +167,7 @@ const fetchPublicProfileData = async ({
[farcasterProfilesRequest.pathname, farcasterProfiles],
[neynarRequest?.href, fcFollowers],
[referredUsersRequest.pathname, referredUsers],
...collectionsZipped,
...guildsZipped,
...rolesZipped,
].filter(([key, value]) => key && value)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { CheckMark } from "@/components/CheckMark"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/Avatar"
import { Card } from "@/components/ui/Card"
import { cn } from "@/lib/utils"
Expand All @@ -22,7 +21,7 @@ export const CardWithGuildLabel = ({
style={{ background: guild.theme.color }}
title={guild.name}
>
<div className="sm:-translate-x-1/2 sm:-rotate-90 flex h-full items-center gap-1 sm:absolute sm:left-1/2 sm:justify-center">
<div className="sm:-translate-x-1/2 sm:-rotate-90 flex h-full items-center gap-1 sm:absolute sm:left-1/2 sm:max-w-28 sm:justify-center">
<Avatar size="xs">
<AvatarImage
src={guild.imageUrl}
Expand All @@ -34,13 +33,12 @@ export const CardWithGuildLabel = ({
</Avatar>
<div
className={cn(
"-mt-0.5 truncate font-bold font-display text-foreground max-sm:text-sm sm:max-w-12",
"-mt-0.5 truncate font-bold font-display text-foreground text-sm",
color && (color.isDark() ? "text-white" : "text-black")
)}
>
{guild.name}
</div>
<CheckMark className="sm:hidden" />
</div>
</div>
<div className="mt-9 size-full rounded-2xl bg-card sm:mt-0 sm:ml-8">
Expand Down
24 changes: 20 additions & 4 deletions src/app/(marketing)/profile/_components/ContributionCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client"

import { Skeleton } from "@/components/ui/Skeleton"
import { Guild, Role, Schemas } from "@guildxyz/types"
import useSWRImmutable from "swr/immutable"
import { useProfile } from "../_hooks/useProfile"
import { ContributionCardView } from "./ContributionCardView"

export const ContributionCard = ({
Expand All @@ -11,6 +11,22 @@ export const ContributionCard = ({
const role = useSWRImmutable<Role>(
`/v2/guilds/${contribution.guildId}/roles/${contribution.roleId}`
)
if (!role.data || !guild.data) return
return <ContributionCardView guild={guild.data} role={role.data} />
const profile = useProfile()
const collection = useSWRImmutable<Schemas["ContributionCollection"]>(
profile.data
? `/v2/profiles/${profile.data.username}/contributions/${contribution.id}/collection`
: null
)

if (!role.data || !guild.data) {
return <Skeleton className="h-32 w-full rounded-2xl" />
}

return (
<ContributionCardView
guild={guild.data}
role={role.data}
collection={collection.data}
/>
)
}
31 changes: 22 additions & 9 deletions src/app/(marketing)/profile/_components/ContributionCardView.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/Avatar"
import { AvatarGroup } from "@/components/ui/AvatarGroup"
import { Separator } from "@/components/ui/Separator"
import { Guild, Role } from "@guildxyz/types"
import { Guild, Role, Schemas } from "@guildxyz/types"
import { Users } from "@phosphor-icons/react/dist/ssr"
import { CardWithGuildLabel } from "./CardWithGuildLabel"
import { ContributionCollection } from "./ContributionCollection"

export const ContributionCardView = ({
guild,
role,
}: { guild: Guild; role: Role }) => {
collection,
}: {
guild: Guild
role: Role
collection?: Schemas["ContributionCollection"]
}) => {
const roleAcquirementPercentage = Number(
((role.memberCount / guild.memberCount || 0) * 100).toFixed(1)
)
const collections = [collection?.NFTs, collection?.pins, collection?.points]
.filter(Boolean)
.flat()
return (
<CardWithGuildLabel guild={guild}>
<div className="grid grid-cols-[auto_1fr] items-center gap-4 p-5 md:grid-cols-[auto_auto_1fr] md:p-6">
Expand All @@ -34,13 +42,18 @@ export const ContributionCardView = ({
</p>
</div>
</div>
<div className="col-span-2 flex w-full flex-col gap-2 justify-self-end md:col-span-1 md:w-auto md:flex-row md:items-center">
<Separator className="mb-2 md:hidden" />
<div className="font-extrabold text-muted-foreground text-xs uppercase">
COLLECTION:
{collection && !!collections.length && (
<div className="col-span-2 flex w-full flex-col gap-2 justify-self-end md:col-span-1 md:w-auto md:flex-row md:items-center">
<Separator className="mb-2 md:hidden" />
<div className="font-extrabold text-muted-foreground text-xs uppercase">
COLLECTION:
</div>

<div className="ml-3 flex">
<ContributionCollection collection={collection} guildId={guild.id} />
</div>
</div>
<AvatarGroup imageUrls={["", "", ""]} count={87} size="lg" />
</div>
)}
</div>
</CardWithGuildLabel>
)
Expand Down
90 changes: 90 additions & 0 deletions src/app/(marketing)/profile/_components/ContributionCollection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/Avatar"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/Tooltip"
import { GuildReward, Schemas } from "@guildxyz/types"
import { Ranking } from "@phosphor-icons/react"
import { GuildAction } from "components/[guild]/Requirements/components/GuildCheckout/MintGuildPinContext"
import { env } from "env"
import Star from "static/icons/star.svg"
import useSWRImmutable from "swr/immutable"

export const ContributionCollection = ({
collection,
guildId,
}: { collection: Schemas["ContributionCollection"]; guildId: number }) => {
const collectionPoint = collection.points.at(0)
const collectionNft = collection.NFTs.at(0)
const collectionPin = collection.pins.at(0)
const { data: pinHash } = useSWRImmutable<string>(
collectionPin
? `/v2/guilds/${guildId}/pin?guildAction=${GuildAction[collectionPin.action]}`
: null
)
const pin = pinHash ? new URL(pinHash, env.NEXT_PUBLIC_IPFS_GATEWAY) : undefined
const { data: point } = useSWRImmutable<GuildReward>(
collectionPoint
? `/v2/guilds/${collectionPoint.guildId}/guild-platforms/${collectionPoint.guildPlatformId}`
: null
)

return (
<>
{pin && (
<Tooltip>
<TooltipTrigger>
<Avatar size="lg" className="-ml-3 border-2 border-card">
<AvatarImage src={pin.href} alt="avatar" width={32} height={32} />
<AvatarFallback />
</Avatar>
</TooltipTrigger>
<TooltipContent>Minted Guild Pin</TooltipContent>
</Tooltip>
)}
{collectionNft?.data.imageUrl && (
<Tooltip>
<TooltipTrigger>
<Avatar size="lg" className="-ml-3 border-2 border-card">
<AvatarImage
src={collectionNft.data.imageUrl}
alt="avatar"
width={32}
height={32}
/>
<AvatarFallback />
</Avatar>
</TooltipTrigger>
<TooltipContent>
Minted <strong>{collectionNft.data.name}</strong>
</TooltipContent>
</Tooltip>
)}
{point && collectionPoint && (
<>
<Avatar size="lg" className="-ml-3 border-2 border-card">
{point.platformGuildData.imageUrl ? (
<>
<AvatarImage
src={point.platformGuildData.imageUrl}
alt="avatar"
width={32}
height={32}
/>
<AvatarFallback />
</>
) : (
<Star className="size-6" />
)}
</Avatar>
<div className="-ml-3 self-center whitespace-nowrap rounded-r-lg border bg-card-secondary py-0.5 pr-2 pl-5">
<div className="text-sm">
{collectionPoint.totalPoints}&nbsp;
<span className="font-extrabold">{point.platformGuildData.name}</span>
</div>
<div className="flex items-center gap-1 text-muted-foreground text-xs">
<Ranking weight="bold" />#{collectionPoint.rank}
</div>
</div>
</>
)}
</>
)
}
2 changes: 1 addition & 1 deletion src/v2/components/ui/AvatarGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const AvatarGroup = ({
</Avatar>
))}
{diffCount > 0 && (
<Avatar className={cn(avatarVariants(avatarProps), "-ml-3")}>
<Avatar className={cn(avatarVariants(avatarProps), "-ml-3 border")}>
<AvatarFallback>+{diffCount}</AvatarFallback>
</Avatar>
)}
Expand Down

0 comments on commit 83a34be

Please sign in to comment.