Skip to content

Commit

Permalink
profile improvements / hotfixes (#1485)
Browse files Browse the repository at this point in the history
* chore: rescale xp

* chore: remove console.log

* feat: add detailed tooltip to barchart

* chore: adjust tooltip spacing
  • Loading branch information
dominik-stumpf authored Sep 13, 2024
1 parent 8d7551a commit d3c1af7
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 16 deletions.
1 change: 1 addition & 0 deletions src/app/(marketing)/profile/[username]/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const MAX_LEVEL = 100
export const XP_SUM = 1e6
export const RANKS = [
{ color: "#78c93d", title: "novice", polygonCount: 20 },
{ color: "#88d525", title: "learner", polygonCount: 20 },
Expand Down
62 changes: 50 additions & 12 deletions src/app/(marketing)/profile/_components/ActivityChart.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Separator } from "@/components/ui/Separator"
import { Skeleton } from "@/components/ui/Skeleton"
import { Schemas } from "@guildxyz/types"
import { localPoint } from "@visx/event"
Expand All @@ -10,7 +11,13 @@ import { useMemo } from "react"
import { useExperienceProgression } from "../_hooks/useExperienceProgression"
import { useExperiences } from "../_hooks/useExperiences"

type TooltipData = Schemas["Experience"]
type GroupedByEventType = Partial<
Record<
Schemas["Experience"]["eventType"],
{ entries: Schemas["Experience"][]; sum: number }
>
>
type TooltipData = { sum: Schemas["Experience"]; entries: GroupedByEventType }
const verticalMargin = 0

const getX = (xp: Schemas["Experience"]) => xp.id.toString()
Expand Down Expand Up @@ -53,7 +60,8 @@ const ActivityChartChildren = ({
).valueOf()
groupedData.set(commonDay, [...(groupedData.get(commonDay) ?? []), rawXp])
}
const data = [...groupedData.entries()]
const groupedByDate = [...groupedData.entries()]
const data = groupedByDate
.reduce<Schemas["Experience"][]>((acc, [_, xpGroup]) => {
return [
...acc,
Expand Down Expand Up @@ -89,11 +97,24 @@ const ActivityChartChildren = ({
[yMax]
)

const extendedExperiences: GroupedByEventType[] = []
for (let i = 0; i < groupedByDate.length; i += 1) {
const [_, byDate] = groupedByDate[i]
const groupedByEventType: GroupedByEventType = {}
for (const entry of byDate) {
const prev = groupedByEventType[entry.eventType]
groupedByEventType[entry.eventType] = {
sum: (prev?.sum || 0) + entry.amount,
entries: [...(prev?.entries || []), entry],
}
}
extendedExperiences[i] = groupedByEventType
}
return width < 10 ? null : (
<div className="relative">
<svg width={width} height={height} ref={containerRef}>
<Group top={verticalMargin / 2}>
{data.map((currentXp) => {
{data.map((currentXp, i) => {
const x = getX(currentXp)
const barWidth = xScale.bandwidth()
const barHeight = yMax - (yScale(getY(currentXp)) ?? 0)
Expand All @@ -118,7 +139,10 @@ const ActivityChartChildren = ({
const eventSvgCoords = localPoint(event)
const left = (barX || 0) + barWidth / 2
showTooltip({
tooltipData: currentXp,
tooltipData: {
sum: currentXp,
entries: extendedExperiences[i],
},
tooltipTop: eventSvgCoords?.y,
tooltipLeft: left,
})
Expand All @@ -144,15 +168,29 @@ const ActivityChartChildren = ({
left={tooltipLeft}
unstyled
applyPositionStyle
className="rounded border bg-card px-2 py-1 text-sm"
className="rounded border bg-card py-2 text-sm"
>
<strong>+{tooltipData.amount} XP</strong>
<div className="text-muted-foreground">
{new Date(tooltipData.createdAt).toLocaleDateString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
})}
<div className="flex items-baseline gap-3 px-2">
<strong>+{tooltipData.sum.amount} XP</strong>
<div className="text-muted-foreground text-xs">
{new Date(tooltipData.sum.createdAt).toLocaleDateString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
})}
</div>
</div>
<Separator className="my-2" />
<div className="flex flex-col gap-1 px-2 text-xs">
{Object.entries(tooltipData.entries).map(([key, value]) => (
<div key={key} className="capitalize">
+{value.sum}{" "}
<span className="text-muted-foreground">
({value.entries.length})
</span>{" "}
{key.replace("_", " ").toLowerCase()}{" "}
</div>
))}
</div>
</TooltipInPortal>
)}
Expand Down
17 changes: 13 additions & 4 deletions src/app/(marketing)/profile/_hooks/useExperienceProgression.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { MAX_LEVEL, RANKS } from "../[username]/constants"
import { MAX_LEVEL, RANKS, XP_SUM } from "../[username]/constants"
import { useExperiences } from "../_hooks/useExperiences"

const generateExponentialArray = (steps: number, exponent: number): number[] => {
return Array.from({ length: steps }, (_, i) => Math.pow(i, exponent))
const generateExponentialArray = (
steps: number,
sum: number,
exponent: number
): number[] => {
const baseSum = (Math.pow(exponent, steps) - 1) / (exponent - 1)
const scaleFactor = sum / baseSum
return Array.from({ length: steps }, (_, i) => Math.pow(exponent, i) * scaleFactor)
}

export const calculateXpProgression = ({
experienceCount,
}: { experienceCount: number }) => {
const levels = generateExponentialArray(MAX_LEVEL, 2).map((num) => Math.floor(num))
if (MAX_LEVEL < 1) throw new Error(`max level must be positive`)
const levels = generateExponentialArray(MAX_LEVEL, XP_SUM, 1.008)
.map((num) => Math.floor(num))
.map((value, _, arr) => value - arr[0])
const levelIndex = levels.findIndex((xp) => experienceCount < xp)
const level = levelIndex === -1 ? MAX_LEVEL : levelIndex
const currentLevelXp = level > 0 ? levels[level - 1] : 0
Expand Down

0 comments on commit d3c1af7

Please sign in to comment.