Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added pretty links for citizens and teams #245

Merged
merged 8 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions ui/components/layout/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import Image from 'next/image'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { ReactNode, useEffect, useState } from 'react'
import { MeshStandardMaterial } from 'three'
import Frame from '../layout/Frame'
import StandardButton from '../layout/StandardButton'

Expand All @@ -26,6 +25,7 @@ interface CardProps {
horizontalscroll?: boolean
role?: string
profile?: boolean
prettyLink?: string
}

export default function Card({
Expand All @@ -46,6 +46,7 @@ export default function Card({
role,
horizontalscroll = false,
profile = false,
prettyLink,
}: CardProps) {
icon =
type === 'team'
Expand Down Expand Up @@ -253,7 +254,9 @@ export default function Card({
onClick={async () => {
setIsLoadingRoute(true)
const route = await router.push(
`/${type === 'team' ? 'team' : 'citizen'}/${metadata.id}`
`/${type === 'team' ? 'team' : 'citizen'}/${
prettyLink || metadata.id
}`
)
if (route) setIsLoadingRoute(false)
}}
Expand Down
82 changes: 82 additions & 0 deletions ui/cypress/integration/subscription/pretty-links.cy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { generatePrettyLinks } from '@/lib/subscription/pretty-links'

describe('generatePrettyLinks', () => {
it('Should generate pretty links correctly', () => {
const input: any = [
{ name: 'Test Link', id: 1 },
{ name: 'Another Link', id: 2 },
{ name: 'Test Link', id: 3 },
]

const expectedOutput = {
prettyLinks: {
'test-link': 1,
'another-link': 2,
'test-link-3': 3,
},
idToPrettyLink: {
1: 'test-link',
2: 'another-link',
3: 'test-link-3',
},
}

const result = generatePrettyLinks(input)
expect(result).to.deep.equal(expectedOutput)
})

it('Should generate all pretty links with tokenId appended', () => {
const input: any = [
{ name: 'Test Link', id: 1 },
{ name: 'Another Link', id: 2 },
{ name: 'Test Link', id: 3 },
]

const expectedOutput = {
prettyLinks: {
'test-link-1': 1,
'another-link-2': 2,
'test-link-3': 3,
},
idToPrettyLink: {
1: 'test-link-1',
2: 'another-link-2',
3: 'test-link-3',
},
}

const result = generatePrettyLinks(input, { allHaveTokenId: true })
expect(result).to.deep.equal(expectedOutput)
})

it('Should handle empty inputs', () => {
const input: any = []
const expectedOutput = {
prettyLinks: {},
idToPrettyLink: {},
}

const result = generatePrettyLinks(input)
expect(result).to.deep.equal(expectedOutput)
})

it('Should handle input with missing names or ids', () => {
const input: any = [
{ name: '', id: 1 },
{ name: 'Valid Link', id: null },
{ name: 'Another Valid Link', id: 2 },
]

const expectedOutput = {
prettyLinks: {
'another-valid-link': 2,
},
idToPrettyLink: {
2: 'another-valid-link',
},
}

const result = generatePrettyLinks(input)
expect(result).to.deep.equal(expectedOutput)
})
})
39 changes: 39 additions & 0 deletions ui/lib/subscription/pretty-links.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
type PrettyLinkData = {
name: string
id: string | number
}

type Options = {
allHaveTokenId?: boolean
}

export function generatePrettyLinks(
prettyLinkData: PrettyLinkData[],
options: Options = {}
) {
const prettyLinks: Record<string, string | number> = {}
const idToPrettyLink: Record<string | number, string> = {}

for (let i = 0; i < prettyLinkData.length; i++) {
const name = prettyLinkData[i]?.name?.toLowerCase()
const id = prettyLinkData[i]?.id

if (name && id !== null && id !== undefined) {
let prettyLink = name.replace(/\s+/g, '-')

if (options?.allHaveTokenId) {
prettyLink = `${prettyLink}-${id}`
} else {
while (prettyLinks.hasOwnProperty(prettyLink)) {
prettyLink = `${prettyLink}-${id}`
}
}

prettyLinks[prettyLink] = id
idToPrettyLink[id] = prettyLink
}
}

// Return the object
return { prettyLinks, idToPrettyLink }
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import { Arbitrum, Sepolia } from '@thirdweb-dev/chains'
import { ThirdwebNftMedia, useAddress, useContract } from '@thirdweb-dev/react'
import {
CITIZEN_ADDRESSES,
CITIZEN_TABLE_ADDRESSES,
MOONEY_ADDRESSES,
TABLELAND_ENDPOINT,
TEAM_ADDRESSES,
VMOONEY_ADDRESSES,
} from 'const/config'
Expand All @@ -26,6 +28,7 @@ import { useContext, useEffect, useState } from 'react'
import toast from 'react-hot-toast'
import { useCitizenData } from '@/lib/citizen/useCitizenData'
import { useTeamWearer } from '@/lib/hats/useTeamWearer'
import { generatePrettyLinks } from '@/lib/subscription/pretty-links'
import { useTeamData } from '@/lib/team/useTeamData'
import ChainContext from '@/lib/thirdweb/chain-context'
import { useHandleRead } from '@/lib/thirdweb/hooks'
Expand Down Expand Up @@ -508,10 +511,33 @@ export default function CitizenDetailPage({
}

export const getServerSideProps: GetServerSideProps = async ({ params }) => {
const tokenId: any = params?.tokenId
const tokenIdOrName: any = params?.tokenIdOrName

const chain = process.env.NEXT_PUBLIC_CHAIN === 'mainnet' ? Arbitrum : Sepolia
const sdk = initSDK(chain)

const citizenTableContract = await sdk?.getContract(
CITIZEN_TABLE_ADDRESSES[chain.slug]
)
const citizenTableName = await citizenTableContract?.call('getTableName')

const statement = `SELECT name, id FROM ${citizenTableName}`
const allCitizensRes = await fetch(
`${TABLELAND_ENDPOINT}?statement=${statement}`
)
const allCitizens = await allCitizensRes.json()

const { prettyLinks } = generatePrettyLinks(allCitizens, {
allHaveTokenId: true,
})

let tokenId
if (!Number.isNaN(Number(tokenIdOrName))) {
tokenId = tokenIdOrName
} else {
tokenId = prettyLinks[tokenIdOrName]
}

const teamContract = await sdk.getContract(CITIZEN_ADDRESSES[chain.slug])
const nft = await teamContract.erc721.get(tokenId)

Expand Down
33 changes: 33 additions & 0 deletions ui/pages/network.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Image from 'next/image'
import Link from 'next/link'
import { useRouter } from 'next/router'
import React, { useState, useEffect, useCallback } from 'react'
import { generatePrettyLinks } from '@/lib/subscription/pretty-links'
import { useChainDefault } from '@/lib/thirdweb/hooks/useChainDefault'
import { initSDK } from '@/lib/thirdweb/thirdweb'
import { useShallowQueryRoute } from '@/lib/utils/hooks'
Expand All @@ -29,11 +30,16 @@ import Tab from '@/components/layout/Tab'
type NetworkProps = {
filteredTeams: NFT[]
filteredCitizens: NFT[]
prettyLinks: {
team: Record<number, string>
citizen: Record<number, string>
}
}

export default function Network({
filteredTeams,
filteredCitizens,
prettyLinks,
}: NetworkProps) {
const router = useRouter()
const shallowQueryRoute = useShallowQueryRoute()
Expand Down Expand Up @@ -214,6 +220,7 @@ export default function Network({
owner={nft.owner}
type={type}
hovertext="Explore Profile"
prettyLink={prettyLinks?.[type]?.[nft.metadata.id]}
/>
</div>
)
Expand Down Expand Up @@ -341,10 +348,36 @@ export async function getStaticProps() {
}
)

//Generate pretty links
const prettyLinks = {
team: {},
citizen: {},
}
const teamPrettyLinkData = teams.map((nft: any) => ({
name: nft?.metadata?.name,
id: nft?.metadata?.id,
}))
const { idToPrettyLink: teamIdToPrettyLink } =
generatePrettyLinks(teamPrettyLinkData)
namedotget marked this conversation as resolved.
Show resolved Hide resolved

prettyLinks.team = teamIdToPrettyLink

const citizenPrettyLinkData = citizens.map((nft: any) => ({
name: nft?.metadata?.name,
id: nft?.metadata?.id,
}))
const { idToPrettyLink: citizenIdToPrettyLink } = generatePrettyLinks(
citizenPrettyLinkData,
{ allHaveTokenId: true }
)

prettyLinks.citizen = citizenIdToPrettyLink

return {
props: {
filteredTeams: filteredValidTeams.reverse(),
filteredCitizens: filteredValidCitizens.reverse(),
prettyLinks,
},
revalidate: 60,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {
JOBS_TABLE_ADDRESSES,
MOONEY_ADDRESSES,
MARKETPLACE_TABLE_ADDRESSES,
TEAM_TABLE_ADDRESSES,
TABLELAND_ENDPOINT,
} from 'const/config'
import { blockedTeams } from 'const/whitelist'
import { GetServerSideProps } from 'next'
Expand All @@ -33,6 +35,7 @@ import { useContext, useEffect, useState } from 'react'
import toast from 'react-hot-toast'
import CitizenContext from '@/lib/citizen/citizen-context'
import { useSubHats } from '@/lib/hats/useSubHats'
import { generatePrettyLinks } from '@/lib/subscription/pretty-links'
import { useTeamData } from '@/lib/team/useTeamData'
import ChainContext from '@/lib/thirdweb/chain-context'
import { useChainDefault } from '@/lib/thirdweb/hooks/useChainDefault'
Expand Down Expand Up @@ -560,10 +563,30 @@ export default function TeamDetailPage({ tokenId, nft, imageIpfsLink }: any) {
}

export const getServerSideProps: GetServerSideProps = async ({ params }) => {
const tokenId: any = params?.tokenId
const tokenIdOrName: any = params?.tokenIdOrName

const chain = process.env.NEXT_PUBLIC_CHAIN === 'mainnet' ? Arbitrum : Sepolia
const sdk = initSDK(chain)

//Generate pretty links
const teamTableContract = await sdk.getContract(
TEAM_TABLE_ADDRESSES[chain.slug]
)
const teamTableName = await teamTableContract.call('getTableName')
const statement = `SELECT name, id FROM ${teamTableName}`
const allTeamsRes = await fetch(
`${TABLELAND_ENDPOINT}?statement=${statement}`
)
const allTeams = await allTeamsRes.json()
const { prettyLinks } = generatePrettyLinks(allTeams)

let tokenId
if (!Number.isNaN(Number(tokenIdOrName))) {
tokenId = tokenIdOrName
} else {
tokenId = prettyLinks[tokenIdOrName]
}

const teamContract = await sdk.getContract(TEAM_ADDRESSES[chain.slug])
const nft = await teamContract.erc721.get(tokenId)

Expand Down
Loading