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

feat: add quest leaderboard change #46

Merged
merged 20 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from 12 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
16 changes: 13 additions & 3 deletions apps/evm/src/pages/Fusion/Fusion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@ import { useLocalStorage } from '@uidotdev/usehooks';
import { Geoblock, Main } from '../../components';
import { LocalStorageKey } from '../../constants';

import { Challenges, Dashboard, Info, Leaderboard, PartnersSection } from './components';
import {
AllUsersLeaderboard,
Challenges,
Dashboard,
Info,
IntractBanner,
PartnersSection,
QuestUsersLeaderboard
} from './components';
import { StyledUpdateMark } from './Fusion.style';
import { IntractBanner } from './components/IntractBanner';

const Fusion = () => {
const [searchParams, setSearchParams] = useSearchParams(new URLSearchParams('tab=dashboard'));
Expand Down Expand Up @@ -59,7 +66,10 @@ const Fusion = () => {
</Flex>
</TabsItem>
<TabsItem key='leaderboard' title='Leaderboard'>
<Leaderboard />
<AllUsersLeaderboard />
</TabsItem>
<TabsItem key='quest-leaderboard' title='Quest Leaderboard'>
<QuestUsersLeaderboard />
</TabsItem>
<TabsItem
key='info'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { INTERVAL, useQuery } from '@gobob/react-query';
import { useId, useMemo } from 'react';
import { Flex, Spinner } from '@gobob/ui';
import { useAccount } from '@gobob/wagmi';
import { useLocale } from '@gobob/ui';

import { QuestRefCodes, apiClient } from '../../../../utils';
import { useGetUser } from '../../../../hooks';
import { QuestOwnerIcon } from '../QuestOwnerAvatar';

import { Leaderboard, LeaderboardColumns, LeaderboardRow } from './Leaderboard';

const userRankKey = 'userRankKey';

const AllUsersLeaderboard = (): JSX.Element => {
const { address } = useAccount();
const id = useId();
const { locale } = useLocale();

const { data: user } = useGetUser();

const { data, isLoading } = useQuery({
queryKey: ['allUsersLeaderboard'],
queryFn: async () => {
const fetchedData = await apiClient.getLeaderboard(100, 0);

return fetchedData.leaderboard.map((item, idx) => {
return {
id: `${item.username}${idx}`,
[LeaderboardColumns.RANK]: <Flex paddingY='md'>{item.rank}</Flex>,
[LeaderboardColumns.INVITED_BY]: item.referred_by || '-',
[LeaderboardColumns.NAME]: item.username,
[LeaderboardColumns.QUESTS]: item.quests_breakdown && (
<Flex gap='xxs'>
{item.quests_breakdown[QuestRefCodes.GALXE] && <QuestOwnerIcon name='galxe' />}
{item.quests_breakdown[QuestRefCodes.INTRACT] && <QuestOwnerIcon name='intract' />}
</Flex>
),
[LeaderboardColumns.SPICE]: Intl.NumberFormat(locale).format(Number(item.total_points))
};
});
},
refetchOnWindowFocus: false,
refetchInterval: INTERVAL.MINUTE
});

const flatData: LeaderboardRow[] = useMemo(() => {
const userData =
address && user
? [
{
id: userRankKey,
invitedBy: user.referred_by,
name: user.username,
spice: Intl.NumberFormat().format(user.leaderboardRank?.total_reward_points || 0),
quests: user.quests_breakdown && (
<Flex gap='xxs'>
{user.quests_breakdown[QuestRefCodes.GALXE] && <QuestOwnerIcon name='galxe' />}
{user.quests_breakdown[QuestRefCodes.INTRACT] && <QuestOwnerIcon name='intract' />}
</Flex>
),
rank: <Flex paddingY='md'>{user.leaderboardRank?.rank || '-'}</Flex>
}
]
: [];

return [...userData, ...(data || [])];
}, [data, address, user]);

return (
<>
{isLoading ? (
<Flex justifyContent='center' marginTop='8xl'>
<Spinner size='36' thickness={5} />
</Flex>
) : (
<Leaderboard id={id} rows={flatData} />
)}
</>
);
};

export { AllUsersLeaderboard };
163 changes: 32 additions & 131 deletions apps/evm/src/pages/Fusion/components/Leaderboard/Leaderboard.tsx
Original file line number Diff line number Diff line change
@@ -1,155 +1,56 @@
import { INTERVAL, useQuery } from '@gobob/react-query';
import { ReactNode, useId, useMemo, useRef } from 'react';
import { Flex, Spinner } from '@gobob/ui';
import { useAccount } from '@gobob/wagmi';
import { useLocale } from '@gobob/ui';

import { apiClient } from '../../../../utils';
import { useGetUser } from '../../../../hooks';
import { ReactNode } from 'react';

import { StyledTable } from './Leaderboard.style';

enum LeaderboardColumns {
export enum LeaderboardColumns {
RANK = 'rank',
NAME = 'name',
INVITED_BY = 'invitedBy',
QUESTS = 'quests',
SPICE = 'spice'
}

type LeaderboardRow = {
export type LeaderboardRow = {
id: string;
[LeaderboardColumns.RANK]: ReactNode;
[LeaderboardColumns.NAME]: ReactNode;
[LeaderboardColumns.INVITED_BY]: ReactNode;
[LeaderboardColumns.QUESTS]: ReactNode;
[LeaderboardColumns.SPICE]: ReactNode;
};

// const fetchSize = 50;

const userRankKey = 'userRankKey';

const Leaderboard = (): JSX.Element => {
const { address } = useAccount();
const tableContainerRef = useRef(null);
const id = useId();
const { locale } = useLocale();

const { data: user } = useGetUser();

const columns = [
{ name: 'Rank', id: LeaderboardColumns.RANK },
{ name: 'Name', id: LeaderboardColumns.NAME },
{ name: 'Invited By', id: LeaderboardColumns.INVITED_BY },
{ name: 'Spice', id: LeaderboardColumns.SPICE }
];

const { data, isLoading } = useQuery({
queryKey: ['leaderboard'],
queryFn: async () => {
const fetchedData = await apiClient.getLeaderboard(100, 0);

return fetchedData.leaderboard.map((item, idx) => {
return {
id: `${item.deposit_owner}${idx}`,
[LeaderboardColumns.RANK]: <Flex paddingY='md'>{item.rank}</Flex>,
[LeaderboardColumns.INVITED_BY]: item.referred_by || '-',
[LeaderboardColumns.NAME]: item.username,
[LeaderboardColumns.SPICE]: Intl.NumberFormat(locale).format(Number(item.total_points))
};
});
},
refetchOnWindowFocus: false,
refetchInterval: INTERVAL.MINUTE
});

// const { data, fetchNextPage, isFetching, isLoading } = useInfiniteQuery({
// queryKey: ['leaderboard'],
// queryFn: async ({ pageParam = 0 }) => {
// const start = (pageParam as number) * fetchSize;
// const fetchedData = await apiClient.getLeaderboard(4, start);
const columns = [
{ name: 'Rank', id: LeaderboardColumns.RANK },
{ name: 'Name', id: LeaderboardColumns.NAME },
{ name: 'Invited By', id: LeaderboardColumns.INVITED_BY },
{ name: 'Quests', id: LeaderboardColumns.QUESTS },
{ name: 'Spice', id: LeaderboardColumns.SPICE }
];

// const rows = fetchedData.leaderboard.map((item, idx) => {
// return {
// id: `${item.deposit_owner}${idx}`,
// [LeaderboardColumns.RANK]: <Flex paddingY='md'>{item.rank}</Flex>,
// [LeaderboardColumns.INVITED_BY]: item.referred_by || '-',
// [LeaderboardColumns.NAME]: item.username,
// [LeaderboardColumns.POINTS]: Intl.NumberFormat(locale).format(Number(item.total_points))
// };
// });

// return { data: rows, total: fetchedData.total };
// },
// getNextPageParam: (_lastGroup, groups) => groups.length,
// refetchOnWindowFocus: false
// });

const flatData: LeaderboardRow[] = useMemo(() => {
const userData =
address && user
? [
{
id: userRankKey,
invitedBy: user.referred_by,
name: user.username,
spice: Intl.NumberFormat().format(user.leaderboardRank?.total_reward_points || 0),
rank: <Flex paddingY='md'>{user.leaderboardRank?.rank || '-'}</Flex>
}
]
: [];

return [...userData, ...(data || [])];
}, [data, address, user]);

// const totalFetched = flatData.length;

// const fetchMoreOnBottomReached = useCallback(
// (containerRefElement?: HTMLDivElement | null) => {
// if (containerRefElement) {
// const { scrollHeight, scrollTop, clientHeight } = containerRefElement;
type Props = {
id: string;
rows: LeaderboardRow[];
};

// //once the user has scrolled within 500px of the bottom of the table, fetch more data if we can
// if (
// scrollHeight - scrollTop - clientHeight < 500 &&
// !isFetching &&
// totalFetched < (data?.pages[0].total || 0)
// ) {
// fetchNextPage();
// }
// }
// },
// [fetchNextPage, isFetching, totalFetched]
// );
type LeaderboardProps = Props;

// //a check on mount and after a fetch to see if the table is already scrolled to the bottom and immediately needs to fetch more data
// useEffect(() => {
// fetchMoreOnBottomReached(tableContainerRef.current);
// }, [fetchMoreOnBottomReached]);
const userRankKey = 'userRankKey';

const Leaderboard = ({ id, rows }: LeaderboardProps): JSX.Element => {
return (
<>
{isLoading ? (
<Flex justifyContent='center' marginTop='8xl'>
<Spinner size='36' thickness={5} />
</Flex>
) : (
<StyledTable
isStickyHeader
aria-labelledby={id}
columns={columns}
rows={flatData || []}
selectedKeys={[userRankKey]}
selectionMode='single'
wrapperProps={
{
ref: tableContainerRef as any,
// onScroll: (e: any) => fetchMoreOnBottomReached(e.target as HTMLDivElement),
marginTop: '4xl'
} as any
}
/>
)}
</>
<StyledTable
isStickyHeader
aria-labelledby={id}
columns={columns}
rows={rows}
selectedKeys={[userRankKey]}
selectionMode='single'
wrapperProps={
{
marginTop: '4xl'
} as any
}
/>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { INTERVAL, useQuery } from '@gobob/react-query';
import { useId, useMemo } from 'react';
import { Flex, Spinner } from '@gobob/ui';
import { useAccount } from '@gobob/wagmi';
import { useLocale } from '@gobob/ui';

import { QuestRefCodes, apiClient } from '../../../../utils';
import { useGetUser } from '../../../../hooks';
import { QuestOwnerIcon } from '../QuestOwnerAvatar';

import { Leaderboard, LeaderboardColumns, LeaderboardRow } from './Leaderboard';

const userRankKey = 'userRankKey';

const QuestUsersLeaderboard = (): JSX.Element => {
const { address } = useAccount();
const id = useId();
const { locale } = useLocale();

const { data: user } = useGetUser();

const { data, isLoading } = useQuery({
queryKey: ['questUsersLeaderboard'],
queryFn: async () => {
const fetchedData = await apiClient.getQuestLeaderboard(100, 0);

return fetchedData.leaderboard.map((item, idx) => {
return {
id: `${item.username}${idx}`,
[LeaderboardColumns.RANK]: <Flex paddingY='md'>{item.rank}</Flex>,
[LeaderboardColumns.INVITED_BY]: item.referred_by || '-',
[LeaderboardColumns.NAME]: item.username,
[LeaderboardColumns.QUESTS]: item.points_breakdown && (
<Flex gap='xxs'>
{item.points_breakdown[QuestRefCodes.GALXE] && <QuestOwnerIcon name='galxe' />}
{item.points_breakdown[QuestRefCodes.INTRACT] && <QuestOwnerIcon name='intract' />}
</Flex>
),
[LeaderboardColumns.SPICE]: Intl.NumberFormat(locale).format(Number(item.total_points))
};
});
},
refetchOnWindowFocus: false,
refetchInterval: INTERVAL.MINUTE
});

const flatData: LeaderboardRow[] = useMemo(() => {
const userData =
address && user
? [
{
id: userRankKey,
invitedBy: user.referred_by,
name: user.username,
spice: Intl.NumberFormat().format(user.leaderboardRank?.total_quest_points || 0),
quests: user.quests_breakdown && (
<Flex gap='xxs'>
{user.quests_breakdown[QuestRefCodes.GALXE] && <QuestOwnerIcon name='galxe' />}
{user.quests_breakdown[QuestRefCodes.INTRACT] && <QuestOwnerIcon name='intract' />}
</Flex>
),
rank: <Flex paddingY='md'>{user.leaderboardRank?.rank || '-'}</Flex>
}
]
: [];

return [...userData, ...(data || [])];
}, [data, address, user]);

return (
<>
{isLoading ? (
<Flex justifyContent='center' marginTop='8xl'>
<Spinner size='36' thickness={5} />
</Flex>
) : (
<Leaderboard id={id} rows={flatData} />
)}
</>
);
};

export { QuestUsersLeaderboard };
3 changes: 2 additions & 1 deletion apps/evm/src/pages/Fusion/components/Leaderboard/index.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { Leaderboard } from './Leaderboard';
export { AllUsersLeaderboard } from './AllUsersLeaderboard';
export { QuestUsersLeaderboard } from './QuestUsersLeaderboard';
Loading