mirror of
https://github.com/acid-info/logos-ordinals-dashboard.git
synced 2025-01-25 20:59:12 +00:00
feat: implement operator apis
This commit is contained in:
parent
d03ae125d2
commit
562789ec78
43
apis/operators/useGetUserInfo.ts
Normal file
43
apis/operators/useGetUserInfo.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { api } from '../../common/api'
|
||||
|
||||
const useQueryOptions = {
|
||||
refetchOnWindowFocus: false,
|
||||
staleTime: 60 * 1000,
|
||||
retry: 1,
|
||||
}
|
||||
|
||||
interface Props {
|
||||
walletAddress: string | null
|
||||
setUserInfo?: (userInfo: any) => void
|
||||
}
|
||||
|
||||
const useGetUserInfo = ({ walletAddress, setUserInfo }: Props) => {
|
||||
const queryKey = ['getUserInfo']
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const fetchData = async () => {
|
||||
const token = sessionStorage.getItem('accessToken')
|
||||
api.defaults.headers.common['Authorization'] = `Bearer ${token}`
|
||||
|
||||
return await api.get('/user').then((res) => {
|
||||
setUserInfo && setUserInfo(res.data)
|
||||
return res.data
|
||||
})
|
||||
}
|
||||
|
||||
const updateCache = (newData: any) => {
|
||||
queryClient.setQueryData(queryKey, newData)
|
||||
}
|
||||
|
||||
const response = useQuery({
|
||||
queryKey: queryKey,
|
||||
queryFn: fetchData,
|
||||
enabled: !!walletAddress,
|
||||
...useQueryOptions,
|
||||
})
|
||||
|
||||
return { ...response, updateCache }
|
||||
}
|
||||
|
||||
export default useGetUserInfo
|
@ -4,6 +4,7 @@ import { api } from '../../common/api'
|
||||
|
||||
interface StakeRequest {
|
||||
operator_id: string
|
||||
setIsStaked: (isStaked: boolean) => void
|
||||
}
|
||||
|
||||
interface StakeResponse {
|
||||
@ -13,11 +14,35 @@ interface StakeResponse {
|
||||
|
||||
const postStake = async ({
|
||||
operator_id,
|
||||
setIsStaked,
|
||||
}: StakeRequest): Promise<StakeResponse> => {
|
||||
const response: AxiosResponse<StakeResponse> = await api.post(
|
||||
`/operators/${operator_id}/stake`,
|
||||
)
|
||||
return response.data
|
||||
try {
|
||||
const token = sessionStorage.getItem('accessToken')
|
||||
api.defaults.headers.common['Authorization'] = `Bearer ${token}`
|
||||
|
||||
const response: AxiosResponse<StakeResponse> = await api.post(
|
||||
`/user/operators/${operator_id}/stake`,
|
||||
)
|
||||
|
||||
setIsStaked(true)
|
||||
return response.data
|
||||
} catch (error: any) {
|
||||
if (error.response) {
|
||||
if (error.response.status === 403) {
|
||||
alert(
|
||||
'Access Denied: You do not have permission to perform this action.',
|
||||
)
|
||||
throw new Error(
|
||||
'Access Denied: You do not have permission to perform this action.',
|
||||
)
|
||||
}
|
||||
if (error.response.status === 404) {
|
||||
alert('Operator not found. Please check the operator ID.')
|
||||
throw new Error('Operator not found. Please check the operator ID.')
|
||||
}
|
||||
}
|
||||
throw new Error('An unexpected error occurred. Please try again later.')
|
||||
}
|
||||
}
|
||||
|
||||
export const useStakeOperator = () => {
|
||||
|
@ -4,6 +4,7 @@ import { api } from '../../common/api'
|
||||
|
||||
interface UnstakeRequest {
|
||||
operator_id: string
|
||||
setIsStaked: (isStaked: boolean) => void
|
||||
}
|
||||
|
||||
interface UnstakeResponse {
|
||||
@ -13,11 +14,35 @@ interface UnstakeResponse {
|
||||
|
||||
const postUnstake = async ({
|
||||
operator_id,
|
||||
setIsStaked,
|
||||
}: UnstakeRequest): Promise<UnstakeResponse> => {
|
||||
const response: AxiosResponse<UnstakeResponse> = await api.post(
|
||||
`/operators/${operator_id}/unstake`,
|
||||
)
|
||||
return response.data
|
||||
try {
|
||||
const token = sessionStorage.getItem('accessToken')
|
||||
api.defaults.headers.common['Authorization'] = `Bearer ${token}`
|
||||
|
||||
const response: AxiosResponse<UnstakeResponse> = await api.post(
|
||||
`/user/operators/${operator_id}/unstake`,
|
||||
)
|
||||
|
||||
setIsStaked(false)
|
||||
return response.data
|
||||
} catch (error: any) {
|
||||
if (error.response) {
|
||||
if (error.response.status === 403) {
|
||||
alert(
|
||||
'Access Denied: You do not have permission to perform this action.',
|
||||
)
|
||||
throw new Error(
|
||||
'Access Denied: You do not have permission to perform this action.',
|
||||
)
|
||||
}
|
||||
if (error.response.status === 404) {
|
||||
alert('Operator not found. Please check the operator ID.')
|
||||
throw new Error('Operator not found. Please check the operator ID.')
|
||||
}
|
||||
}
|
||||
throw new Error('An unexpected error occurred. Please try again later.')
|
||||
}
|
||||
}
|
||||
|
||||
export const useUnstakeOperator = () => {
|
||||
|
0
atoms/filter.ts
Normal file
0
atoms/filter.ts
Normal file
3
atoms/userInfo.ts
Normal file
3
atoms/userInfo.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { atom } from 'jotai'
|
||||
|
||||
export const userInfo = atom<any>(null)
|
3
atoms/wallet.ts
Normal file
3
atoms/wallet.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { atom } from 'jotai'
|
||||
|
||||
export const walletAddressAtom = atom<string | null>(null)
|
@ -25,7 +25,7 @@ api.interceptors.response.use(
|
||||
|
||||
if (error.response?.status === 401) {
|
||||
try {
|
||||
const refreshToken = await localStorage.getItem('refreshToken')
|
||||
const refreshToken = await sessionStorage.getItem('refreshToken')
|
||||
await api
|
||||
.post('/token/refresh', {
|
||||
refreshToken,
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { ProcessedOperator } from '@/containers/Dashboard/DashboardContainer'
|
||||
import styled from '@emotion/styled'
|
||||
import Link from 'next/link'
|
||||
import React, { useState } from 'react'
|
||||
import { useStakeOperator } from '../../../../apis/operators/useStakeOperator'
|
||||
import { useUnstakeOperator } from '../../../../apis/operators/useUnstakeOperator'
|
||||
import { ProcessedOperator } from '../../../../types/operators'
|
||||
|
||||
interface OperatorCardProps {
|
||||
operator: ProcessedOperator
|
||||
@ -21,15 +21,13 @@ const OperatorCard: React.FC<OperatorCardProps> = ({
|
||||
if (isStaked) {
|
||||
unstake.mutate({
|
||||
operator_id: operatorId,
|
||||
setIsStaked,
|
||||
})
|
||||
|
||||
setIsStaked(false)
|
||||
} else {
|
||||
stake.mutate({
|
||||
operator_id: operatorId,
|
||||
setIsStaked,
|
||||
})
|
||||
|
||||
setIsStaked(true)
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,14 +39,14 @@ const OperatorCard: React.FC<OperatorCardProps> = ({
|
||||
<OperatorInfo>
|
||||
<OperatorName>{operator.name}</OperatorName>
|
||||
<PointsPerHour>
|
||||
<Label>Per Hour</Label>
|
||||
<Value>{operator.pointsPerHour} rp</Value>
|
||||
<Label>XP/Block</Label>
|
||||
<Value>{operator.pointsPerHour} XP</Value>
|
||||
</PointsPerHour>
|
||||
</OperatorInfo>
|
||||
<Actions>
|
||||
<ActionButton
|
||||
onClick={() => handleStake(operator.id)}
|
||||
isStaked={isStaked}
|
||||
isStaked={!!isStaked}
|
||||
>
|
||||
{isStaked ? 'Unstake' : 'Stake'}
|
||||
</ActionButton>
|
||||
@ -64,6 +62,17 @@ const OperatorCard: React.FC<OperatorCardProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
color: rgb(var(--lsd-text-primary));
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
}
|
||||
`
|
||||
|
||||
const IconButton = styled.button`
|
||||
background-color: transparent;
|
||||
border-left: none;
|
||||
@ -92,17 +101,6 @@ const Value = styled.div`
|
||||
line-height: 20px;
|
||||
`
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
color: rgb(var(--lsd-text-primary));
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
}
|
||||
`
|
||||
|
||||
const OperatorImage = styled.img`
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { breakpoints } from '@/configs/ui.configs'
|
||||
import { ProcessedOperator } from '@/containers/Dashboard/DashboardContainer'
|
||||
import styled from '@emotion/styled'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import { ProcessedOperator } from '../../../../types/operators'
|
||||
import OperatorCard from './OperatorCard'
|
||||
|
||||
interface OperatorGridProps {
|
||||
@ -13,6 +14,13 @@ const OperatorGrid: React.FC<OperatorGridProps> = ({
|
||||
isLoading,
|
||||
data,
|
||||
}: OperatorGridProps) => {
|
||||
const stakedOperators = data?.filter((operator) => operator.isStaked)
|
||||
|
||||
const totalXpPerBlock = data?.reduce(
|
||||
(acc, operator) => acc + (operator.pointsPerHour ?? 0),
|
||||
0,
|
||||
)
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Header>
|
||||
@ -31,31 +39,45 @@ const OperatorGrid: React.FC<OperatorGridProps> = ({
|
||||
<Stats>
|
||||
<Stat>
|
||||
<Label>Total Operators</Label>
|
||||
<Value>7</Value>
|
||||
<Value>{data?.length || 0}</Value>
|
||||
</Stat>
|
||||
<Stat>
|
||||
<Label>Staked</Label>
|
||||
<Value>6</Value>
|
||||
<Value>{stakedOperators?.length || 0}</Value>
|
||||
</Stat>
|
||||
<Stat>
|
||||
<Label>Unstaked</Label>
|
||||
<Value>1</Value>
|
||||
<Value>{data?.length - (stakedOperators?.length || 0)}</Value>
|
||||
</Stat>
|
||||
<Stat>
|
||||
<Label>XP/Block</Label>
|
||||
<Value>912</Value>
|
||||
<Value>{totalXpPerBlock || 0}</Value>
|
||||
</Stat>
|
||||
</Stats>
|
||||
<Grid>
|
||||
{isLoading
|
||||
? Array.from({ length: 12 }).map((_, index) => (
|
||||
<PlaceholderCard key={index}>
|
||||
<Placeholder />
|
||||
</PlaceholderCard>
|
||||
))
|
||||
: data?.map((operator) => (
|
||||
<OperatorCard key={operator.id} operator={operator} />
|
||||
))}
|
||||
{isLoading ? (
|
||||
Array.from({ length: 12 }).map((_, index) => (
|
||||
<PlaceholderCard key={index}>
|
||||
<Placeholder />
|
||||
</PlaceholderCard>
|
||||
))
|
||||
) : data?.length === 0 ? (
|
||||
<AddOperator href="https://logos.co/exit" target="_blank">
|
||||
<PlusIcon>
|
||||
<img
|
||||
src="/assets/plus.svg"
|
||||
width={10}
|
||||
height={10}
|
||||
alt="Add Operator"
|
||||
/>
|
||||
</PlusIcon>
|
||||
<span>Add Operator</span>
|
||||
</AddOperator>
|
||||
) : (
|
||||
data?.map((operator) => (
|
||||
<OperatorCard key={operator.id} operator={operator} />
|
||||
))
|
||||
)}
|
||||
</Grid>
|
||||
</Container>
|
||||
)
|
||||
@ -162,4 +184,26 @@ const Placeholder = styled.div`
|
||||
opacity: 0.5;
|
||||
`
|
||||
|
||||
const AddOperator = styled(Link)`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
width: 100%;
|
||||
height: 258px;
|
||||
padding: 24px;
|
||||
|
||||
span {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
span {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export default OperatorGrid
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { defaultFilterState } from '@/states/filterState'
|
||||
import { defaultFilterState, filterAtom } from '@/states/filterState'
|
||||
import styled from '@emotion/styled'
|
||||
import { atom, useAtom } from 'jotai'
|
||||
import { useSetAtom } from 'jotai'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import Checkbox from './Checkbox'
|
||||
|
||||
@ -15,8 +15,6 @@ interface DropdownProps {
|
||||
prefill?: string[]
|
||||
}
|
||||
|
||||
const filterAtom = atom(defaultFilterState)
|
||||
|
||||
const Dropdown: React.FC<DropdownProps> = ({
|
||||
title,
|
||||
options,
|
||||
@ -30,7 +28,7 @@ const Dropdown: React.FC<DropdownProps> = ({
|
||||
|
||||
const dropdownRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const [filter, setFilter] = useAtom(filterAtom)
|
||||
const setFilter = useSetAtom(filterAtom)
|
||||
|
||||
const defaultState = defaultFilterState
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { breakpoints } from '@/configs/ui.configs'
|
||||
import { ProcessedOperator } from '@/containers/Dashboard/DashboardContainer'
|
||||
import styled from '@emotion/styled'
|
||||
import 'lazysizes'
|
||||
import Link from 'next/link'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { ProcessedOperator } from '../../../../types/operators'
|
||||
|
||||
interface OperatorGridProps {
|
||||
isLoading: boolean
|
||||
|
@ -6,7 +6,14 @@ import React from 'react'
|
||||
|
||||
interface NavbarProps {}
|
||||
|
||||
export const navItems = [
|
||||
interface NavItem {
|
||||
label: string
|
||||
href: string
|
||||
isDisabled?: boolean
|
||||
isSoon?: boolean
|
||||
}
|
||||
|
||||
export const navItems: NavItem[] = [
|
||||
{
|
||||
label: 'Countdown',
|
||||
href: '/countdown',
|
||||
@ -18,8 +25,8 @@ export const navItems = [
|
||||
{
|
||||
label: 'Dashboard',
|
||||
href: '/dashboard',
|
||||
isDisabled: true,
|
||||
isSoon: true,
|
||||
// isDisabled: true,
|
||||
// isSoon: true,
|
||||
},
|
||||
// {
|
||||
// label: 'Leaderboard',
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { truncateString } from '@/utils/general.utils'
|
||||
import styled from '@emotion/styled'
|
||||
import { useAtom } from 'jotai'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { walletAddressAtom } from '../../../atoms/wallet'
|
||||
import { api } from '../../../common/api'
|
||||
import { getMEAddressAndSignature } from './magicEden'
|
||||
import { getOKXAddressAndSignature } from './okx'
|
||||
@ -17,7 +19,7 @@ const options = [
|
||||
|
||||
const Dropdown: React.FC = () => {
|
||||
const [isExpanded, setIsExpanded] = useState(false)
|
||||
const [walletAddress, setWalletAddress] = useState<string | null>(null)
|
||||
const [walletAddress, setWalletAddress] = useAtom(walletAddressAtom)
|
||||
|
||||
const walletHandlers = {
|
||||
okx: getOKXAddressAndSignature,
|
||||
@ -40,7 +42,10 @@ const Dropdown: React.FC = () => {
|
||||
setWalletAddress(address)
|
||||
|
||||
const response = await api.post('/token/pair', { address, signature })
|
||||
console.log('Token pair response:', response)
|
||||
const { access, refresh } = response.data.token
|
||||
|
||||
sessionStorage.setItem('accessToken', access)
|
||||
sessionStorage.setItem('refreshToken', refresh)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to connect or disconnect wallet:', error)
|
||||
|
@ -3,59 +3,34 @@ import { OperatorPanel } from '@/components/Dashboard/OperatorPanel'
|
||||
import { ProgressBar } from '@/components/Dashboard/ProgressBar'
|
||||
import { breakpoints } from '@/configs/ui.configs'
|
||||
import styled from '@emotion/styled'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
import React from 'react'
|
||||
import useGetOperators from '../../../apis/operators/useGetOperators'
|
||||
import { getRandomSubset, processOperators } from '../../../utils/operators'
|
||||
import useGetUserInfo from '../../../apis/operators/useGetUserInfo'
|
||||
import { userInfo } from '../../../atoms/userInfo'
|
||||
import { walletAddressAtom } from '../../../atoms/wallet'
|
||||
import { processMyOperators } from '../../../utils/operators'
|
||||
|
||||
export type DashboardPageProps = React.DetailedHTMLProps<
|
||||
React.HTMLAttributes<HTMLDivElement>,
|
||||
HTMLDivElement
|
||||
>
|
||||
|
||||
export interface Operator {
|
||||
id: number
|
||||
name: string
|
||||
archetype: string
|
||||
image_400_url: string
|
||||
image_400_jpeg_url: string
|
||||
comp: string
|
||||
background: string
|
||||
skin: string
|
||||
helmet: string
|
||||
jacket: string
|
||||
}
|
||||
|
||||
export interface Group {
|
||||
id: number
|
||||
name: string
|
||||
operators: Operator[]
|
||||
}
|
||||
|
||||
export interface ProcessedOperator {
|
||||
id: string
|
||||
image: string
|
||||
name: string
|
||||
archetype: string
|
||||
gif: string
|
||||
comp: string
|
||||
background: string
|
||||
skin: string
|
||||
helmet: string
|
||||
jacket: string
|
||||
pointsPerHour: number
|
||||
isStaked: boolean
|
||||
isPinned: boolean
|
||||
}
|
||||
|
||||
const DashboardContainer: React.FC<DashboardPageProps> = ({
|
||||
children,
|
||||
...props
|
||||
}) => {
|
||||
const { data, isLoading } = useGetOperators()
|
||||
const setUserInfo = useSetAtom(userInfo)
|
||||
|
||||
const processedOperators = processOperators(data as Group[], [])
|
||||
const walletAddress = useAtomValue(walletAddressAtom)
|
||||
|
||||
const random20Operators = getRandomSubset(processedOperators, 20)
|
||||
const { data: userInfoData, isLoading: isUserInfoLoading } = useGetUserInfo({
|
||||
walletAddress,
|
||||
setUserInfo,
|
||||
})
|
||||
|
||||
// console.log('userInfoData', userInfoData)
|
||||
|
||||
const processedOperators = processMyOperators(userInfoData?.operators)
|
||||
|
||||
return (
|
||||
<Container {...props}>
|
||||
@ -70,7 +45,10 @@ const DashboardContainer: React.FC<DashboardPageProps> = ({
|
||||
<DesktopProgressBar>
|
||||
<ProgressBar progress={30} claimPosition={76} />
|
||||
</DesktopProgressBar>
|
||||
<OperatorGrid data={random20Operators} isLoading={isLoading} />
|
||||
<OperatorGrid
|
||||
data={processedOperators}
|
||||
isLoading={isUserInfoLoading}
|
||||
/>
|
||||
</RightColumn>
|
||||
</Wrapper>
|
||||
</Container>
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { Dropdown } from '@/components/Dropdown'
|
||||
import { OperatorGrid } from '@/components/Explore/OperatorGrid'
|
||||
import { defaultFilterState, FilterState } from '@/states/filterState'
|
||||
import { defaultFilterState, filterAtom } from '@/states/filterState'
|
||||
import styled from '@emotion/styled'
|
||||
import { atom, useAtom } from 'jotai'
|
||||
import { useAtom } from 'jotai'
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import useGetOperators from '../../../apis/operators/useGetOperators'
|
||||
|
||||
import {
|
||||
ARCHETYPE,
|
||||
BACKGROUND,
|
||||
@ -17,19 +18,18 @@ import { processOperators, shuffleOperators } from '../../../utils/operators'
|
||||
|
||||
interface ExploreSectionProps {}
|
||||
|
||||
const filterAtom = atom<FilterState>(defaultFilterState)
|
||||
|
||||
const ExploreSection: React.FC<ExploreSectionProps> = () => {
|
||||
const ExploreContainer: React.FC<ExploreSectionProps> = () => {
|
||||
const { data, isLoading } = useGetOperators()
|
||||
|
||||
const [filter, setFilter] = useAtom(filterAtom)
|
||||
|
||||
const processedOperators = processOperators(
|
||||
data as any,
|
||||
filter.archetype.slice(),
|
||||
filter?.archetype?.slice(),
|
||||
)
|
||||
|
||||
const selectedOperators = useMemo(() => {
|
||||
if (!processedOperators || !filter) return []
|
||||
const filterCopied = JSON.parse(JSON.stringify(filter))
|
||||
|
||||
return processedOperators
|
||||
@ -65,42 +65,42 @@ const ExploreSection: React.FC<ExploreSectionProps> = () => {
|
||||
options={ARCHETYPE}
|
||||
onSelectionChange={handleFilterChange}
|
||||
filterType="archetype"
|
||||
prefill={filter.archetype.slice()}
|
||||
prefill={filter?.archetype.slice()}
|
||||
/>
|
||||
<Dropdown
|
||||
title="Comp"
|
||||
options={COMP}
|
||||
onSelectionChange={handleFilterChange}
|
||||
filterType="comp"
|
||||
prefill={filter.comp.slice()}
|
||||
prefill={filter?.comp.slice()}
|
||||
/>
|
||||
<Dropdown
|
||||
title="Skin"
|
||||
options={SKIN}
|
||||
onSelectionChange={handleFilterChange}
|
||||
filterType="skin"
|
||||
prefill={filter.skin.slice()}
|
||||
prefill={filter?.skin.slice()}
|
||||
/>
|
||||
<Dropdown
|
||||
title="Helmet"
|
||||
options={HELMET}
|
||||
onSelectionChange={handleFilterChange}
|
||||
filterType="helmet"
|
||||
prefill={filter.helmet.slice()}
|
||||
prefill={filter?.helmet.slice()}
|
||||
/>
|
||||
<Dropdown
|
||||
title="Jacket"
|
||||
options={JACKET}
|
||||
onSelectionChange={handleFilterChange}
|
||||
filterType="jacket"
|
||||
prefill={filter.jacket.slice()}
|
||||
prefill={filter?.jacket.slice()}
|
||||
/>
|
||||
<Dropdown
|
||||
title="Background"
|
||||
options={BACKGROUND}
|
||||
onSelectionChange={handleFilterChange}
|
||||
filterType="background"
|
||||
prefill={filter.background.slice()}
|
||||
prefill={filter?.background.slice()}
|
||||
/>
|
||||
<ResetAll onClick={handleResetAll}>
|
||||
Reset All <img src="/assets/close-black.svg" alt="close-black" />
|
||||
@ -181,4 +181,4 @@ const ResetAll = styled.button`
|
||||
}
|
||||
`
|
||||
|
||||
export default ExploreSection
|
||||
export default ExploreContainer
|
||||
|
@ -27,7 +27,7 @@ export const defaultFilterState: FilterState = {
|
||||
background: BACKGROUND,
|
||||
}
|
||||
|
||||
const filterAtom = atom<FilterState>(defaultFilterState)
|
||||
export const filterAtom = atom<FilterState>(defaultFilterState)
|
||||
|
||||
const wrapFilterState = (
|
||||
filter: FilterState,
|
||||
|
@ -9,3 +9,38 @@ export type Archetype =
|
||||
| 'Outlaw'
|
||||
| 'Philosopher'
|
||||
| 'Polymath'
|
||||
|
||||
export interface Operator {
|
||||
id: number
|
||||
name: string
|
||||
archetype: string
|
||||
image_400_url: string
|
||||
image_400_jpeg_url: string
|
||||
comp: string
|
||||
background: string
|
||||
skin: string
|
||||
helmet: string
|
||||
jacket: string
|
||||
}
|
||||
|
||||
export interface Group {
|
||||
id: number
|
||||
name: string
|
||||
operators: Operator[]
|
||||
}
|
||||
|
||||
export interface ProcessedOperator {
|
||||
id: string
|
||||
image: string
|
||||
name: string
|
||||
archetype: string
|
||||
gif: string
|
||||
comp: string
|
||||
background: string
|
||||
skin: string
|
||||
helmet: string
|
||||
jacket: string
|
||||
pointsPerHour?: number
|
||||
isStaked?: boolean
|
||||
isPinned?: boolean
|
||||
}
|
||||
|
@ -1,14 +1,10 @@
|
||||
import {
|
||||
Group,
|
||||
ProcessedOperator,
|
||||
} from '@/containers/Dashboard/DashboardContainer'
|
||||
import { Archetype } from '../types/operators'
|
||||
import { Archetype, Group, ProcessedOperator } from '../types/operators'
|
||||
|
||||
export function processOperators(
|
||||
data: Group[],
|
||||
selectedArchetypes: Archetype[],
|
||||
): ProcessedOperator[] {
|
||||
const hasSelectedArchetypes = selectedArchetypes.length > 0
|
||||
const hasSelectedArchetypes = selectedArchetypes?.length > 0
|
||||
|
||||
return data?.flatMap((group) => {
|
||||
const groupArchetype = group.name.slice(0, -1) as Archetype
|
||||
@ -23,7 +19,6 @@ export function processOperators(
|
||||
image: operator.image_400_jpeg_url,
|
||||
gif: operator.image_400_url,
|
||||
name: operator.name,
|
||||
pointsPerHour: Math.floor(Math.random() * 500),
|
||||
comp: operator.comp,
|
||||
background: operator.background,
|
||||
skin: operator.skin,
|
||||
@ -39,6 +34,29 @@ export function processOperators(
|
||||
})
|
||||
}
|
||||
|
||||
export function processMyOperators(operators: any[]) {
|
||||
if (!operators) {
|
||||
return []
|
||||
}
|
||||
|
||||
return operators?.map((operator) => ({
|
||||
id: operator.id.toString(),
|
||||
arcgetypeId: operator.archetype_id,
|
||||
image: operator.image_400_jpeg_url,
|
||||
gif: operator.image_400_url,
|
||||
name: operator.name,
|
||||
pointsPerHour: operator.staking_xp_per_block,
|
||||
comp: operator.comp,
|
||||
background: operator.background,
|
||||
skin: operator.skin,
|
||||
helmet: operator.helmet,
|
||||
jacket: operator.jacket,
|
||||
archetype: operator.archetype,
|
||||
isStaked: operator.is_currently_staked,
|
||||
isPinned: operator.is_user_pinned,
|
||||
}))
|
||||
}
|
||||
|
||||
export function getRandomSubset<T>(array: T[], count: number): T[] {
|
||||
const shuffled = array?.sort(() => 0.5 - Math.random())
|
||||
return shuffled?.slice(0, count)
|
||||
|
Loading…
x
Reference in New Issue
Block a user