Dashboard fixes; closes #1209, #1210, #1223, & #1224

* Remove connect to progress if the network is not mainnet

* Move xpHelpers to the utils

* Add tests to the utils package

* Add xpHelpers test

* Add convertToRoman util function

* Display current season label dynamically

* Add onboarding text if player does not have xp

* Remove ConnectToProgress import

* Add connect to progress if the network is not mainnet

* Show edit button if network is not main

* Refactor xp helper season start

* displaying modal when chain change is needed 🛬

* using Unicode Roman numerals Ⅲ

* slight formatting fixes 🗯
This commit is contained in:
Arsenije Savic
2022-05-18 07:48:07 +02:00
committed by GitHub
parent 15487abc42
commit ea42bea2c0
14 changed files with 201 additions and 35 deletions

View File

@@ -2,6 +2,7 @@ import {
Constants,
fetch,
getLatestEthAddress,
getNumWeeksInSeason,
isNotNullOrUndefined,
} from '@metafam/utils';
import bluebird from 'bluebird';
@@ -15,7 +16,6 @@ import {
import { client } from '../../../lib/hasuraClient';
import { computeRank } from '../../../lib/rankHelpers';
import { ledgerManager } from '../../../lib/sourcecredLedger';
import { getNumWeeksInSeason } from '../../../lib/xpHelpers';
const VALID_ACCOUNT_TYPES: Array<AccountType_Enum> = [
AccountType_Enum.Ethereum,

View File

@@ -1,23 +0,0 @@
export const getNumWeeksInSeason = (currentDate: Date = new Date()): number => {
const currentYear = currentDate.getFullYear();
const lastYear = currentDate.getFullYear() - 1;
const seasons = [
new Date(Date.parse(`${lastYear}-12-21T00:00:00+00:00`)), // 21 Dec last year
new Date(Date.parse(`${currentYear}-03-20T00:00:00+00:00`)), // 20 Mar this year
new Date(Date.parse(`${currentYear}-06-21T00:00:00+00:00`)), // 21 June this year
new Date(Date.parse(`${currentYear}-09-22T00:00:00+00:00`)), // 22 Sept this year
new Date(Date.parse(`${currentYear}-12-21T00:00:00+00:00`)), // 21 Dec this year
];
const currentSeason = seasons.reduce(
(t, a) => (a.getTime() < currentDate.getTime() ? a : t),
new Date('1970-01-01T00:00:00+00:00'),
);
return Math.ceil(
(currentDate.getTime() - currentSeason.getTime()) /
(1000 * 60 * 60 * 24 * 7),
);
};

View File

@@ -0,0 +1,3 @@
module.exports = {
testEnvironment: 'node',
};

View File

@@ -10,7 +10,8 @@
"build": "tsc -b",
"dev": "yarn build --watch",
"typecheck": "yarn build",
"precommit": "yarn lint-staged"
"precommit": "yarn lint-staged",
"test": "tsdx test --passWithNoTests"
},
"dependencies": {
"@datamodels/identity-profile-basic": "0.1.2",

View File

@@ -16,6 +16,7 @@ export * as numbers from './numbers';
export * from './promiseHelpers';
export * from './rankHelpers';
export * from './sourceCredHelpers';
export * from './xpHelpers';
export { uuidv4 as generateUUID };
export { extendedProfileModel };

View File

@@ -0,0 +1,65 @@
export const getNumWeeksInSeason = (currentDate: Date = new Date()): number => {
const currentYear = currentDate.getFullYear();
const lastYear = currentDate.getFullYear() - 1;
const seasons = [
new Date(Date.parse(`${lastYear}-12-21T00:00:00+00:00`)), // 21 Dec last year
new Date(Date.parse(`${currentYear}-03-20T00:00:00+00:00`)), // 20 Mar this year
new Date(Date.parse(`${currentYear}-06-21T00:00:00+00:00`)), // 21 June this year
new Date(Date.parse(`${currentYear}-09-22T00:00:00+00:00`)), // 22 Sept this year
new Date(Date.parse(`${currentYear}-12-21T00:00:00+00:00`)), // 21 Dec this year
];
const currentSeason = seasons.reduce(
(t, a) => (a.getTime() < currentDate.getTime() ? a : t),
new Date('1970-01-01T00:00:00+00:00'),
);
return Math.ceil(
(currentDate.getTime() - currentSeason.getTime()) /
(1000 * 60 * 60 * 24 * 7),
);
};
export const getSeasonNum = (currentDate: Date = new Date()): number => {
const GAME_STARTED = new Date('2020-12-21');
const seasons = [];
let activeDate = new Date(GAME_STARTED);
let activeIndex = 4;
const boundaries = [
{ month: 3, day: 19 },
{ month: 6, day: 20 },
{ month: 9, day: 22 },
{ month: 12, day: 20 },
];
const leapYear = activeDate.getFullYear() % 4 === 0;
const SEASON_START = boundaries.map(
({ month, day }) =>
new Date(activeDate.getFullYear(), month - 1, day + (leapYear ? 0 : 1)),
);
while (currentDate > activeDate) {
seasons.push(activeDate);
if (activeIndex === SEASON_START.length) {
const newDate = new Date(activeDate).setFullYear(
activeDate.getFullYear() + 1,
);
activeDate = new Date(newDate);
activeIndex = 0;
}
const nextDate = SEASON_START[activeIndex];
nextDate.setFullYear(activeDate.getFullYear());
activeDate = new Date(nextDate);
activeIndex += 1;
}
return seasons.length;
};

View File

@@ -1,4 +1,4 @@
import { getNumWeeksInSeason } from '../../src/lib/xpHelpers';
import { getNumWeeksInSeason, getSeasonNum } from '../../src/xpHelpers';
describe('getNumWeeksInSeason', () => {
const currentDate = new Date();
@@ -26,3 +26,15 @@ describe('getNumWeeksInSeason', () => {
expect(getNumWeeksInSeason(Jun21st)).toBe(1);
});
});
describe('getSeasonNum', () => {
it('Dec 22 should be season 1', () => {
const Dec22nd = new Date(Date.parse(`2020-12-22T00:01:00+00:00`));
expect(getSeasonNum(Dec22nd)).toBe(1);
});
it('April 1st 2022 should be season 6', () => {
const Apr1st = new Date(Date.parse(`2022-04-01T00:01:00+00:00`));
expect(getSeasonNum(Apr1st)).toBe(6);
});
});

View File

@@ -25,7 +25,12 @@ export const MetaGameLogo = () => (
export const ConnectToProgress: React.FC<{
showNote?: boolean;
showSwitchButton?: boolean;
}> = ({ showNote = false, showSwitchButton = true }) => {
header?: string;
}> = ({
showNote = false,
showSwitchButton = true,
header = 'Welcome to MetaGame!',
}) => {
const { connect, connecting, connected, chainId } = useWeb3();
const [open, { toggle }] = useBoolean();
const { fetching } = useUser();
@@ -35,7 +40,7 @@ export const ConnectToProgress: React.FC<{
return (
<Stack color="white" spacing={8} w="100%" maxW="30rem">
<MetaGameLogo />
<MetaHeading> Welcome to MetaGame! </MetaHeading>
{header && header !== '' && <MetaHeading>{header}</MetaHeading>}
<Text fontSize="md" w="100%" textAlign="center">
Please switch to <SwitchNetworkButton chainId="0x1" /> to progress
</Text>

View File

@@ -2,6 +2,7 @@ import {
Box,
Button,
ButtonGroup,
Link,
MetaTag,
Stat,
StatArrow,
@@ -33,7 +34,14 @@ export const XP = (): React.ReactElement => {
if (xpStats == null) {
return (
<Text fontStyle="italic" textAlign="center" p={4} w="100%">
Unknown
If you want your XP stats to appear, you gotta earn some XP first! Go{' '}
<Link
href="https://meta-game.notion.site/Welcome-to-MetaGame-349d9b6434d543b48539bccabf10b60a"
target="_tab"
>
here
</Link>{' '}
& join the onboarding call
</Text>
);
}

View File

@@ -458,7 +458,31 @@ export const EditProfileModal: React.FC<ProfileEditorProps> = ({
};
if (chainId !== '0x1') {
return <ConnectToProgress />;
return (
<Modal {...{ isOpen, onClose }}>
<ModalOverlay />
<ModalContent
maxW={['100%', 'min(80%, 60rem)']}
backgroundImage={`url(${BackgroundImage})`}
bgSize="cover"
bgAttachment="fixed"
p={[0, 8, 12]}
>
<ModalHeader>
<MetaHeading color="white">Wrong Chain</MetaHeading>
</ModalHeader>
<ModalCloseButton
color="pinkShadeOne"
size="xl"
p={{ base: 1, sm: 4 }}
_focus={{ boxShadow: 'none' }}
/>
<ModalBody p={[0, 2]} alignSelf="center">
<ConnectToProgress header="" />
</ModalBody>
</ModalContent>
</Modal>
);
}
return (

View File

@@ -16,7 +16,7 @@ import {
Wrap,
WrapItem,
} from '@metafam/ds';
import { Maybe } from '@metafam/utils';
import { getSeasonNum, Maybe } from '@metafam/utils';
import { PlayerAvatar } from 'components/Player/PlayerAvatar';
import { PlayerContacts } from 'components/Player/PlayerContacts';
import { PlayerTileMemberships } from 'components/Player/PlayerTileMemberships';
@@ -25,6 +25,7 @@ import { Player, Skill } from 'graphql/autogen/types';
import NextLink from 'next/link';
import React, { useMemo } from 'react';
import { FaGlobe } from 'react-icons/fa';
import { convertToRoman } from 'utils/formatHelpers';
import {
getPlayerBanner,
getPlayerDescription,
@@ -104,7 +105,7 @@ export const PlayerTile: React.FC<Props> = ({
{showSeasonalXP && (
<WrapItem>
<MetaTag size="md">
SEASON XP:{' '}
SEASON {convertToRoman(getSeasonNum())} XP:{' '}
{Math.floor(player.seasonXP).toLocaleString()}
</MetaTag>
</WrapItem>

View File

@@ -46,12 +46,13 @@ type DisplayComponentProps = {
export const PlayerHero: React.FC<HeroProps> = ({ player, editing }) => {
const { user } = useUser();
const isOwnProfile = user ? user.id === player?.id : null;
const { isOpen, onOpen, onClose } = useDisclosure();
// 9tails.eth: As there is no current way of editing Profile Accounts,
// and the MWW integratino happens on the EditProfileModal, to avoid
// adding a new column to the table just to do the followig, I decided
// adding a new column to the table just to do the following, I decided
// to this this cast as any to use the information from the form and
// don't do bigger changes on the data structure just because of it
const profileFields = useProfileField({

View File

@@ -65,8 +65,13 @@ const fetchOpenSeaData = async (
const res = await fetch(
`/api/opensea?owner=${owner}&offset=${offset}&limit=${limit}`,
);
const { assets, error } = await res.json();
if (error) throw new Error(error);
const body = await res.text();
const { assets, error } = JSON.parse(body);
if (error) {
// eslint-disable-next-line no-console
console.error({ error, body });
throw new Error(error);
}
if (!assets) throw new Error(`Received ${assets} assets`);
return assets;
};

View File

@@ -0,0 +1,63 @@
export const convertToRoman = (_num: number): string => {
let num = _num;
type RomanOptions = {
[key: string]: number;
};
const romans: RomanOptions = {
: 100_000,
: 50_000,
: 10_000,
: 5_000,
: 1_000,
: 900,
: 500,
: 400,
: 100,
: 90,
: 50,
: 40,
: 10,
: 9,
: 5,
: 4,
: 1,
};
const smallRomans = [
'',
'Ⅱ',
'Ⅲ',
'Ⅳ',
'',
'Ⅵ',
'Ⅶ',
'Ⅷ',
'Ⅸ',
'',
'Ⅺ',
'Ⅻ',
'ⅩⅢ',
'ⅩⅣ',
'',
'ⅩⅥ',
'ⅩⅦ',
'ⅩⅡⅩ',
'',
];
if (num < smallRomans.length) {
return smallRomans[num - 1];
}
let str = '';
// eslint-disable-next-line no-restricted-syntax
for (const i of Object.keys(romans)) {
const q = Math.floor(num / romans[i]);
num -= q * romans[i];
str += i.repeat(q);
}
return str;
};