Add badges by rarities to profile
This commit is contained in:
parent
d6e7ecfe1f
commit
f24600755b
|
@ -4,7 +4,7 @@ export type Badge = {
|
||||||
type: BadgeTypes
|
type: BadgeTypes
|
||||||
createdTime: number
|
createdTime: number
|
||||||
data: { [key: string]: any }
|
data: { [key: string]: any }
|
||||||
name: 'Proven Correct' | 'Streaker' | 'Market Maker'
|
name: 'Proven Correct' | 'Streaker' | 'Market Creator'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BadgeTypes = 'PROVEN_CORRECT' | 'STREAKER' | 'MARKET_CREATOR'
|
export type BadgeTypes = 'PROVEN_CORRECT' | 'STREAKER' | 'MARKET_CREATOR'
|
||||||
|
@ -53,7 +53,7 @@ const calculateProvenCorrectBadgeRarity = (badge: ProvenCorrectBadge) => {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
export const streakerBadgeRarityThresholds = [1, 50, 200]
|
export const streakerBadgeRarityThresholds = [1, 50, 250]
|
||||||
const calculateStreakerBadgeRarity = (badge: StreakerBadge) => {
|
const calculateStreakerBadgeRarity = (badge: StreakerBadge) => {
|
||||||
const { totalBettingStreak } = badge.data
|
const { totalBettingStreak } = badge.data
|
||||||
const thresholdArray = streakerBadgeRarityThresholds
|
const thresholdArray = streakerBadgeRarityThresholds
|
||||||
|
@ -67,10 +67,10 @@ const calculateStreakerBadgeRarity = (badge: StreakerBadge) => {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
export const marketMakerBadgeRarityThresholds = [1, 50, 200]
|
export const marketCreatorBadgeRarityThresholds = [1, 75, 300]
|
||||||
const calculateMarketMakerBadgeRarity = (badge: MarketCreatorBadge) => {
|
const calculateMarketCreatorBadgeRarity = (badge: MarketCreatorBadge) => {
|
||||||
const { totalContractsCreated } = badge.data
|
const { totalContractsCreated } = badge.data
|
||||||
const thresholdArray = marketMakerBadgeRarityThresholds
|
const thresholdArray = marketCreatorBadgeRarityThresholds
|
||||||
let i = thresholdArray.length - 1
|
let i = thresholdArray.length - 1
|
||||||
while (i >= 0) {
|
while (i >= 0) {
|
||||||
if (totalContractsCreated == thresholdArray[i]) {
|
if (totalContractsCreated == thresholdArray[i]) {
|
||||||
|
@ -81,10 +81,9 @@ const calculateMarketMakerBadgeRarity = (badge: MarketCreatorBadge) => {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
export type rarities = 'common' | 'bronze' | 'silver' | 'gold'
|
export type rarities = 'bronze' | 'silver' | 'gold'
|
||||||
|
|
||||||
const rarityRanks: { [key: number]: rarities } = {
|
const rarityRanks: { [key: number]: rarities } = {
|
||||||
0: 'common',
|
|
||||||
1: 'bronze',
|
1: 'bronze',
|
||||||
2: 'silver',
|
2: 'silver',
|
||||||
3: 'gold',
|
3: 'gold',
|
||||||
|
@ -98,7 +97,7 @@ export const calculateBadgeRarity = (badge: Badge) => {
|
||||||
]
|
]
|
||||||
case 'MARKET_CREATOR':
|
case 'MARKET_CREATOR':
|
||||||
return rarityRanks[
|
return rarityRanks[
|
||||||
calculateMarketMakerBadgeRarity(badge as MarketCreatorBadge)
|
calculateMarketCreatorBadgeRarity(badge as MarketCreatorBadge)
|
||||||
]
|
]
|
||||||
case 'STREAKER':
|
case 'STREAKER':
|
||||||
return rarityRanks[calculateStreakerBadgeRarity(badge as StreakerBadge)]
|
return rarityRanks[calculateStreakerBadgeRarity(badge as StreakerBadge)]
|
||||||
|
@ -107,12 +106,21 @@ export const calculateBadgeRarity = (badge: Badge) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const calculateTotalUsersBadges = (user: User) => {
|
export const getBadgesByRarity = (user: User) => {
|
||||||
const { achievements } = user
|
const rarities: { [key in rarities]: number } = {
|
||||||
if (!achievements) return 0
|
bronze: 0,
|
||||||
return (
|
silver: 0,
|
||||||
(achievements.marketCreator?.totalBadges ?? 0) +
|
gold: 0,
|
||||||
(achievements.provenCorrect?.totalBadges ?? 0) +
|
|
||||||
(achievements.streaker?.totalBadges ?? 0)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
Object.values(user.achievements).map((value) => {
|
||||||
|
value.badges.map((badge) => {
|
||||||
|
rarities[calculateBadgeRarity(badge)] =
|
||||||
|
(rarities[calculateBadgeRarity(badge)] ?? 0) + 1
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return rarities
|
||||||
|
}
|
||||||
|
|
||||||
|
export const goldClassName = 'text-amber-400'
|
||||||
|
export const silverClassName = 'text-gray-500'
|
||||||
|
export const bronzeClassName = 'text-amber-900'
|
||||||
|
|
|
@ -53,17 +53,14 @@ export type User = {
|
||||||
freeMarketsCreated?: number
|
freeMarketsCreated?: number
|
||||||
isBannedFromPosting?: boolean
|
isBannedFromPosting?: boolean
|
||||||
|
|
||||||
achievements?: {
|
achievements: {
|
||||||
provenCorrect?: {
|
provenCorrect?: {
|
||||||
totalBadges: number
|
|
||||||
badges: ProvenCorrectBadge[]
|
badges: ProvenCorrectBadge[]
|
||||||
}
|
}
|
||||||
marketCreator?: {
|
marketCreator?: {
|
||||||
totalBadges: number
|
|
||||||
badges: MarketCreatorBadge[]
|
badges: MarketCreatorBadge[]
|
||||||
}
|
}
|
||||||
streaker?: {
|
streaker?: {
|
||||||
totalBadges: number
|
|
||||||
badges: StreakerBadge[]
|
badges: StreakerBadge[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -332,7 +332,6 @@ async function handleBettingStreakBadgeAward(
|
||||||
achievements: {
|
achievements: {
|
||||||
...user.achievements,
|
...user.achievements,
|
||||||
streaker: {
|
streaker: {
|
||||||
totalBadges: (user.achievements?.streaker?.totalBadges ?? 0) + 1,
|
|
||||||
badges: [...(user.achievements?.streaker?.badges ?? []), badge],
|
badges: [...(user.achievements?.streaker?.badges ?? []), badge],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { User } from '../../common/user'
|
||||||
import * as admin from 'firebase-admin'
|
import * as admin from 'firebase-admin'
|
||||||
import {
|
import {
|
||||||
MarketCreatorBadge,
|
MarketCreatorBadge,
|
||||||
marketMakerBadgeRarityThresholds,
|
marketCreatorBadgeRarityThresholds,
|
||||||
} from '../../common/badge'
|
} from '../../common/badge'
|
||||||
|
|
||||||
export const onCreateContract = functions
|
export const onCreateContract = functions
|
||||||
|
@ -50,10 +50,10 @@ async function handleMarketCreatorBadgeAward(contractCreator: User) {
|
||||||
.where('creatorId', '==', contractCreator.id)
|
.where('creatorId', '==', contractCreator.id)
|
||||||
.where('resolution', '!=', 'CANCEL')
|
.where('resolution', '!=', 'CANCEL')
|
||||||
)
|
)
|
||||||
if (contracts.length in marketMakerBadgeRarityThresholds) {
|
if (contracts.length in marketCreatorBadgeRarityThresholds) {
|
||||||
const badge = {
|
const badge = {
|
||||||
type: 'MARKET_CREATOR',
|
type: 'MARKET_CREATOR',
|
||||||
name: 'Market Maker',
|
name: 'Market Creator',
|
||||||
data: {
|
data: {
|
||||||
totalContractsCreated: contracts.length,
|
totalContractsCreated: contracts.length,
|
||||||
},
|
},
|
||||||
|
@ -67,9 +67,6 @@ async function handleMarketCreatorBadgeAward(contractCreator: User) {
|
||||||
achievements: {
|
achievements: {
|
||||||
...contractCreator.achievements,
|
...contractCreator.achievements,
|
||||||
marketCreator: {
|
marketCreator: {
|
||||||
totalBadges:
|
|
||||||
(contractCreator.achievements?.marketCreator?.totalBadges ?? 0) +
|
|
||||||
1,
|
|
||||||
badges: [
|
badges: [
|
||||||
...(contractCreator.achievements?.marketCreator?.badges ?? []),
|
...(contractCreator.achievements?.marketCreator?.badges ?? []),
|
||||||
badge,
|
badge,
|
||||||
|
|
|
@ -82,8 +82,6 @@ async function handleResolvedContract(contract: Contract) {
|
||||||
achievements: {
|
achievements: {
|
||||||
...user.achievements,
|
...user.achievements,
|
||||||
provenCorrect: {
|
provenCorrect: {
|
||||||
totalBadges:
|
|
||||||
(user.achievements?.provenCorrect?.totalBadges ?? 0) + 1,
|
|
||||||
badges: [
|
badges: [
|
||||||
...(user.achievements?.provenCorrect?.badges ?? []),
|
...(user.achievements?.provenCorrect?.badges ?? []),
|
||||||
newProvenCorrectBadge,
|
newProvenCorrectBadge,
|
||||||
|
|
102
functions/src/scripts/backfill-badges.ts
Normal file
102
functions/src/scripts/backfill-badges.ts
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
import * as admin from 'firebase-admin'
|
||||||
|
|
||||||
|
import { initAdmin } from './script-init'
|
||||||
|
import { getUser, getValues } from '../utils'
|
||||||
|
import { Contract } from 'common/contract'
|
||||||
|
import {
|
||||||
|
MarketCreatorBadge,
|
||||||
|
marketCreatorBadgeRarityThresholds,
|
||||||
|
StreakerBadge,
|
||||||
|
streakerBadgeRarityThresholds,
|
||||||
|
} from 'common/badge'
|
||||||
|
import { User } from 'common/user'
|
||||||
|
import { filterDefined } from 'common/util/array'
|
||||||
|
initAdmin()
|
||||||
|
|
||||||
|
const firestore = admin.firestore()
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
// const users = await getAllUsers()
|
||||||
|
// const users = filterDefined([await getUser('6hHpzvRG0pMq8PNJs7RZj2qlZGn2')])
|
||||||
|
const users = filterDefined([await getUser('AJwLWoo3xue32XIiAVrL5SyR1WB2')])
|
||||||
|
await Promise.all(
|
||||||
|
users.map(async (user) => {
|
||||||
|
console.log('Added achievements to user', user.id)
|
||||||
|
if (!user.id) return
|
||||||
|
if (user.achievements === undefined) {
|
||||||
|
await firestore.collection('users').doc(user.id).update({
|
||||||
|
achievements: {},
|
||||||
|
})
|
||||||
|
user.achievements = {}
|
||||||
|
}
|
||||||
|
user.achievements = await awardMarketCreatorBadges(user)
|
||||||
|
user.achievements = await awardBettingStreakBadges(user)
|
||||||
|
// going to ignore backfilling the proven correct badges for now
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (require.main === module) main().then(() => process.exit())
|
||||||
|
|
||||||
|
async function awardMarketCreatorBadges(user: User) {
|
||||||
|
// Award market maker badges
|
||||||
|
const contracts = await getValues<Contract>(
|
||||||
|
firestore
|
||||||
|
.collection(`contracts`)
|
||||||
|
.where('creatorId', '==', user.id)
|
||||||
|
.where('resolution', '!=', 'CANCEL')
|
||||||
|
)
|
||||||
|
|
||||||
|
const achievements = {
|
||||||
|
...user.achievements,
|
||||||
|
marketCreator: {
|
||||||
|
badges: [...(user.achievements.marketCreator?.badges ?? [])],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for (const threshold of marketCreatorBadgeRarityThresholds) {
|
||||||
|
if (contracts.length >= threshold) {
|
||||||
|
const badge = {
|
||||||
|
type: 'MARKET_CREATOR',
|
||||||
|
name: 'Market Creator',
|
||||||
|
data: {
|
||||||
|
totalContractsCreated: threshold,
|
||||||
|
},
|
||||||
|
createdTime: Date.now(),
|
||||||
|
} as MarketCreatorBadge
|
||||||
|
achievements.marketCreator.badges.push(badge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update user
|
||||||
|
await firestore.collection('users').doc(user.id).update({
|
||||||
|
achievements,
|
||||||
|
})
|
||||||
|
return achievements
|
||||||
|
}
|
||||||
|
|
||||||
|
async function awardBettingStreakBadges(user: User) {
|
||||||
|
const streak = user.currentBettingStreak ?? 0
|
||||||
|
const achievements = {
|
||||||
|
...user.achievements,
|
||||||
|
streaker: {
|
||||||
|
badges: [...(user.achievements?.streaker?.badges ?? [])],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for (const threshold of streakerBadgeRarityThresholds) {
|
||||||
|
if (streak >= threshold) {
|
||||||
|
const badge = {
|
||||||
|
type: 'STREAKER',
|
||||||
|
name: 'Streaker',
|
||||||
|
data: {
|
||||||
|
totalBettingStreak: threshold,
|
||||||
|
},
|
||||||
|
createdTime: Date.now(),
|
||||||
|
} as StreakerBadge
|
||||||
|
achievements.streaker.badges.push(badge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update user
|
||||||
|
await firestore.collection('users').doc(user.id).update({
|
||||||
|
achievements,
|
||||||
|
})
|
||||||
|
return achievements
|
||||||
|
}
|
|
@ -112,6 +112,12 @@ export const getAllPrivateUsers = async () => {
|
||||||
return users.docs.map((doc) => doc.data() as PrivateUser)
|
return users.docs.map((doc) => doc.data() as PrivateUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getAllUsers = async () => {
|
||||||
|
const firestore = admin.firestore()
|
||||||
|
const users = await firestore.collection('users').get()
|
||||||
|
return users.docs.map((doc) => doc.data() as User)
|
||||||
|
}
|
||||||
|
|
||||||
export const getUserByUsername = async (username: string) => {
|
export const getUserByUsername = async (username: string) => {
|
||||||
const firestore = admin.firestore()
|
const firestore = admin.firestore()
|
||||||
const snap = await firestore
|
const snap = await firestore
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
import { Modal } from 'web/components/layout/modal'
|
import { Modal } from 'web/components/layout/modal'
|
||||||
import { Col } from 'web/components/layout/col'
|
import { Col } from 'web/components/layout/col'
|
||||||
import { User } from 'common/user'
|
import { PAST_BETS, User } from 'common/user'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
|
bronzeClassName,
|
||||||
calculateBadgeRarity,
|
calculateBadgeRarity,
|
||||||
|
goldClassName,
|
||||||
MarketCreatorBadge,
|
MarketCreatorBadge,
|
||||||
ProvenCorrectBadge,
|
ProvenCorrectBadge,
|
||||||
rarities,
|
rarities,
|
||||||
|
silverClassName,
|
||||||
StreakerBadge,
|
StreakerBadge,
|
||||||
} from 'common/badge'
|
} from 'common/badge'
|
||||||
import { groupBy } from 'lodash'
|
import { groupBy } from 'lodash'
|
||||||
|
@ -15,9 +18,7 @@ import { Row } from 'web/components/layout/row'
|
||||||
import { SiteLink } from 'web/components/site-link'
|
import { SiteLink } from 'web/components/site-link'
|
||||||
import { contractPathWithoutContract } from 'web/lib/firebase/contracts'
|
import { contractPathWithoutContract } from 'web/lib/firebase/contracts'
|
||||||
import { Tooltip } from 'web/components/tooltip'
|
import { Tooltip } from 'web/components/tooltip'
|
||||||
const goldClassName = 'text-amber-400'
|
|
||||||
const silverClassName = 'text-gray-500'
|
|
||||||
const bronzeClassName = 'text-amber-900'
|
|
||||||
export function BadgesModal(props: {
|
export function BadgesModal(props: {
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
setOpen: (open: boolean) => void
|
setOpen: (open: boolean) => void
|
||||||
|
@ -154,8 +155,9 @@ function StreakerBadgeItem(props: { badge: StreakerBadge; rarity: rarities }) {
|
||||||
<Col className={'cursor-default text-center'}>
|
<Col className={'cursor-default text-center'}>
|
||||||
<Medal rarity={rarity} />
|
<Medal rarity={rarity} />
|
||||||
<Tooltip
|
<Tooltip
|
||||||
text={`Make predictions ${totalBettingStreak} day
|
text={`Make ${PAST_BETS} ${totalBettingStreak} day${
|
||||||
${totalBettingStreak > 1 ? 's' : ''} in a row`}
|
totalBettingStreak > 1 ? 's' : ''
|
||||||
|
} in a row`}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={
|
className={
|
||||||
|
@ -195,7 +197,7 @@ function MarketCreatorBadgeItem(props: {
|
||||||
: bronzeClassName
|
: bronzeClassName
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Market Maker
|
Market Creator
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
|
@ -37,7 +37,12 @@ import { BadgesModal } from 'web/components/profile/badges-modal'
|
||||||
import { copyToClipboard } from 'web/lib/util/copy'
|
import { copyToClipboard } from 'web/lib/util/copy'
|
||||||
import { track } from 'web/lib/service/analytics'
|
import { track } from 'web/lib/service/analytics'
|
||||||
import { DOMAIN } from 'common/envs/constants'
|
import { DOMAIN } from 'common/envs/constants'
|
||||||
import { calculateTotalUsersBadges } from 'common/badge'
|
import {
|
||||||
|
bronzeClassName,
|
||||||
|
getBadgesByRarity,
|
||||||
|
goldClassName,
|
||||||
|
silverClassName,
|
||||||
|
} from 'common/badge'
|
||||||
|
|
||||||
export function UserPage(props: { user: User }) {
|
export function UserPage(props: { user: User }) {
|
||||||
const { user } = props
|
const { user } = props
|
||||||
|
@ -101,9 +106,10 @@ export function UserPage(props: { user: User }) {
|
||||||
<span className="break-anywhere text-lg font-bold sm:text-2xl">
|
<span className="break-anywhere text-lg font-bold sm:text-2xl">
|
||||||
{user.name}
|
{user.name}
|
||||||
</span>
|
</span>
|
||||||
<span className="sm:text-md text-greyscale-4 text-sm">
|
<Row className="sm:text-md -mt-1 items-center gap-x-3 text-sm ">
|
||||||
@{user.username}
|
<span className={' text-greyscale-4'}>@{user.username}</span>
|
||||||
</span>
|
<BadgeDisplay user={user} router={router} />
|
||||||
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
{isCurrentUser && (
|
{isCurrentUser && (
|
||||||
<ProfilePrivateStats
|
<ProfilePrivateStats
|
||||||
|
@ -280,11 +286,8 @@ export function ProfilePrivateStats(props: {
|
||||||
}) {
|
}) {
|
||||||
const { profit, user, router } = props
|
const { profit, user, router } = props
|
||||||
const [showLoansModal, setShowLoansModal] = useState(false)
|
const [showLoansModal, setShowLoansModal] = useState(false)
|
||||||
const [showBadgesModal, setShowBadgesModal] = useState(false)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const showBadgesModal = router.query['show'] === 'badges'
|
|
||||||
setShowBadgesModal(showBadgesModal)
|
|
||||||
const showLoansModel = router.query['show'] === 'loans'
|
const showLoansModel = router.query['show'] === 'loans'
|
||||||
setShowLoansModal(showLoansModel)
|
setShowLoansModal(showLoansModel)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
@ -300,15 +303,6 @@ export function ProfilePrivateStats(props: {
|
||||||
</span>
|
</span>
|
||||||
<span className="mx-auto text-xs sm:text-sm">profit</span>
|
<span className="mx-auto text-xs sm:text-sm">profit</span>
|
||||||
</Col>
|
</Col>
|
||||||
<Col
|
|
||||||
className={'text-md flex-shrink-0 cursor-pointer sm:text-lg'}
|
|
||||||
onClick={() => setShowBadgesModal(true)}
|
|
||||||
>
|
|
||||||
<span>🏅 {calculateTotalUsersBadges(user)}</span>
|
|
||||||
<span className={'text-greyscale-4 mx-auto text-xs sm:text-sm'}>
|
|
||||||
badges
|
|
||||||
</span>
|
|
||||||
</Col>
|
|
||||||
<Col
|
<Col
|
||||||
className={
|
className={
|
||||||
'text-greyscale-4 text-md flex-shrink-0 cursor-pointer sm:text-lg'
|
'text-greyscale-4 text-md flex-shrink-0 cursor-pointer sm:text-lg'
|
||||||
|
@ -321,11 +315,6 @@ export function ProfilePrivateStats(props: {
|
||||||
<span className="mx-auto text-xs sm:text-sm">next loan</span>
|
<span className="mx-auto text-xs sm:text-sm">next loan</span>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<BadgesModal
|
|
||||||
isOpen={showBadgesModal}
|
|
||||||
setOpen={setShowBadgesModal}
|
|
||||||
user={user}
|
|
||||||
/>
|
|
||||||
{showLoansModal && (
|
{showLoansModal && (
|
||||||
<LoansModal isOpen={showLoansModal} setOpen={setShowLoansModal} />
|
<LoansModal isOpen={showLoansModal} setOpen={setShowLoansModal} />
|
||||||
)}
|
)}
|
||||||
|
@ -345,3 +334,49 @@ export function ProfilePublicStats(props: { user: User; className?: string }) {
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function BadgeDisplay(props: { user: User; router: NextRouter }) {
|
||||||
|
const { user, router } = props
|
||||||
|
const [showBadgesModal, setShowBadgesModal] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const showBadgesModal = router.query['show'] === 'badges'
|
||||||
|
setShowBadgesModal(showBadgesModal)
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [])
|
||||||
|
// get number of badges of each rarity type
|
||||||
|
const badgesByRarity = getBadgesByRarity(user)
|
||||||
|
const badgesByRarityItems = Object.entries(badgesByRarity).map(
|
||||||
|
([rarity, numBadges]) => {
|
||||||
|
return (
|
||||||
|
<Row
|
||||||
|
key={rarity}
|
||||||
|
className={clsx(
|
||||||
|
'items-center gap-2',
|
||||||
|
rarity === 'bronze'
|
||||||
|
? bronzeClassName
|
||||||
|
: rarity === 'silver'
|
||||||
|
? silverClassName
|
||||||
|
: goldClassName
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className={clsx('-m-0.5 text-lg')}>•</span>
|
||||||
|
<span className="text-xs">{numBadges}</span>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<Row
|
||||||
|
className={'cursor-pointer gap-2'}
|
||||||
|
onClick={() => setShowBadgesModal(true)}
|
||||||
|
>
|
||||||
|
{badgesByRarityItems}
|
||||||
|
<BadgesModal
|
||||||
|
isOpen={showBadgesModal}
|
||||||
|
setOpen={setShowBadgesModal}
|
||||||
|
user={user}
|
||||||
|
/>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user