mirror of
https://github.com/MetaFam/TheGame.git
synced 2026-04-02 03:00:32 -04:00
added filters for /players page (#566)
* player filters for type and skills * fetching multiple players in parallel * removed tile fragment * search by username or address * better spacing * availability filter * timezone filter * passing tests in ds * submit form in search bar * added better labels for timezone * fixed test issue * searching only if search >= 2 char * meta select ds * updated metabutton bg color * parallel for > 50 only * fix reset search filter
This commit is contained in:
7
packages/design-system/babel.config.js
Normal file
7
packages/design-system/babel.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
test: {
|
||||
presets: ['@babel/preset-env', '@babel/preset-react'],
|
||||
},
|
||||
},
|
||||
};
|
||||
3
packages/design-system/jest.config.js
Normal file
3
packages/design-system/jest.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
transformIgnorePatterns: ['SelectTimezone'],
|
||||
};
|
||||
@@ -24,18 +24,20 @@
|
||||
"react": ">=16"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/react": "17.0.5",
|
||||
"@types/react-dom": "17.0.5",
|
||||
"@types/react-select": "3.0.22",
|
||||
"@chakra-ui/icons": "1.0.13",
|
||||
"@chakra-ui/react": "1.6.1",
|
||||
"@chakra-ui/theme-tools": "1.1.7",
|
||||
"@emotion/react": "11.4.0",
|
||||
"@emotion/styled": "11.3.0",
|
||||
"framer-motion": "4.1.16",
|
||||
"@types/react": "17.0.5",
|
||||
"@types/react-dom": "17.0.5",
|
||||
"@types/react-select": "3.0.22",
|
||||
"framer-motion": "4.1.17",
|
||||
"next": "10.2.0",
|
||||
"react-select": "4.3.1",
|
||||
"react-timezone-select": "0.9.8",
|
||||
"react-timezone-select": "1.0.3",
|
||||
"spacetime": "6.16.1",
|
||||
"spacetime-informal": "0.6.1",
|
||||
"storybook-addon-performance": "0.15.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -14,6 +14,7 @@ export const MetaButton: React.FC<
|
||||
letterSpacing="0.1em"
|
||||
size="lg"
|
||||
fontSize="sm"
|
||||
bg="purple.400"
|
||||
ref={ref}
|
||||
{...props}
|
||||
>
|
||||
|
||||
19
packages/design-system/src/MetaSelect.tsx
Normal file
19
packages/design-system/src/MetaSelect.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Select, SelectProps } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import { DropDownIcon } from './icons/DropDownIcon';
|
||||
|
||||
export const MetaSelect: React.FC<SelectProps> = (props) => (
|
||||
<Select
|
||||
textTransform="uppercase"
|
||||
maxW="48"
|
||||
bg="dark"
|
||||
iconColor="purple.400"
|
||||
iconSize="xs"
|
||||
icon={<DropDownIcon boxSize={2} />}
|
||||
borderColor="purple.400"
|
||||
borderWidth="2px"
|
||||
borderRadius="4px"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -1,10 +1,46 @@
|
||||
/* istanbul ignore file */
|
||||
import React from 'react';
|
||||
import { Styles } from 'react-select';
|
||||
import TimezoneSelect, { TimezoneSelectProps } from 'react-timezone-select';
|
||||
import { i18nTimezones } from 'react-timezone-select/dist/index.js';
|
||||
import spacetime from 'spacetime';
|
||||
import informal from 'spacetime-informal';
|
||||
|
||||
import { theme } from './theme';
|
||||
|
||||
export const selectStyles: Styles = {
|
||||
export type TimezoneType = {
|
||||
id: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export const TimezoneOptions: TimezoneType[] = Object.entries(
|
||||
i18nTimezones,
|
||||
).map((zone) => {
|
||||
const now = spacetime.now().goto(zone[0]);
|
||||
const tz = now.timezone();
|
||||
const tzStrings = informal.display(zone[0]);
|
||||
|
||||
let abbrev = zone[0];
|
||||
|
||||
if (tzStrings && tzStrings.daylight && tzStrings.standard) {
|
||||
abbrev = now.isDST()
|
||||
? tzStrings.daylight.abbrev
|
||||
: tzStrings.standard.abbrev;
|
||||
}
|
||||
|
||||
const min = tz.current.offset * 60;
|
||||
const hr = `${(min / 60) ^ 0}:${min % 60 === 0 ? '00' : Math.abs(min % 60)}`;
|
||||
const prefix = `(GMT${hr.includes('-') ? hr : `+${hr}`}) ${zone[1]}`;
|
||||
|
||||
const label = `${prefix} ${abbrev.length < 5 ? `(${abbrev})` : ''}`;
|
||||
|
||||
return {
|
||||
id: zone[0],
|
||||
label,
|
||||
};
|
||||
});
|
||||
|
||||
const selectStyles: Styles = {
|
||||
menu: (styles) => ({
|
||||
...styles,
|
||||
background: theme.colors.dark,
|
||||
|
||||
13
packages/design-system/src/icons/DropDownIcon.tsx
Normal file
13
packages/design-system/src/icons/DropDownIcon.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { createIcon } from '@chakra-ui/icons';
|
||||
import * as React from 'react';
|
||||
|
||||
export const DropDownIcon = createIcon({
|
||||
displayName: 'DropDownIcon',
|
||||
path: (
|
||||
<path
|
||||
d="M10 0H2C1.17595 0 0.705573 0.940764 1.2 1.6L5.2 6.93333C5.6 7.46667 6.4 7.46667 6.8 6.93333L10.8 1.6C11.2944 0.940764 10.824 0 10 0Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
),
|
||||
viewBox: '0 0 12 8',
|
||||
});
|
||||
@@ -1,2 +1,3 @@
|
||||
export { BrightIdIcon } from './BrightIdIcon';
|
||||
export { DropDownIcon } from './DropDownIcon';
|
||||
export { Icon3box } from './Icon3box';
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
export { BoxedNextImage } from './BoxedNextImage';
|
||||
export { ConfirmModal } from './ConfirmModal';
|
||||
export { BrightIdIcon, Icon3box } from './icons';
|
||||
export * from './icons';
|
||||
export { LoadingState } from './LoadingState';
|
||||
export { MetaBox } from './MetaBox';
|
||||
export { MetaButton } from './MetaButton';
|
||||
export { MetaHeading } from './MetaHeading';
|
||||
export { MetaSelect } from './MetaSelect';
|
||||
export { MetaTag } from './MetaTag';
|
||||
export { MetaTile, MetaTileBody, MetaTileHeader } from './MetaTile';
|
||||
export { ResponsiveText } from './ResponsiveText';
|
||||
export { SelectSearch, selectStyles } from './SelectSearch';
|
||||
export { SelectTimeZone } from './SelectTimeZone';
|
||||
export {
|
||||
SelectTimeZone,
|
||||
TimezoneOptions,
|
||||
TimezoneType,
|
||||
} from './SelectTimeZone';
|
||||
export { SVG } from './SVG';
|
||||
export { theme as MetaTheme } from './theme';
|
||||
export { H1, P } from './typography';
|
||||
|
||||
@@ -9,6 +9,7 @@ export type MetaColors = ChakraTheme['colors'] & {
|
||||
purpleBoxDark: string;
|
||||
purpleBoxLight: string;
|
||||
purpleTag: string;
|
||||
purpleTag30: string;
|
||||
blueLight: string;
|
||||
cyanText: string;
|
||||
diamond: string;
|
||||
@@ -36,6 +37,7 @@ export const colors: MetaColors = {
|
||||
purpleBoxDark: '#261943',
|
||||
purpleBoxLight: '#392373',
|
||||
purpleTag: '#40347C',
|
||||
purpleTag30: 'rgba(64, 52, 124, 0.3)',
|
||||
blueLight: '#A5B9F6',
|
||||
cyanText: '#79F8FB',
|
||||
discord: '#7289da',
|
||||
|
||||
@@ -14,6 +14,10 @@ export const theme: Theme = extendTheme({
|
||||
background: 'dark',
|
||||
color: 'white',
|
||||
minHeight: '100vh',
|
||||
option: {
|
||||
background: 'dark',
|
||||
color: 'white',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"noEmit": true,
|
||||
|
||||
222
packages/web/components/Player/PlayerFilter.tsx
Normal file
222
packages/web/components/Player/PlayerFilter.tsx
Normal file
@@ -0,0 +1,222 @@
|
||||
import {
|
||||
Input,
|
||||
MetaButton,
|
||||
MetaSelect,
|
||||
Stack,
|
||||
Text,
|
||||
TimezoneOptions,
|
||||
TimezoneType,
|
||||
VStack,
|
||||
Wrap,
|
||||
WrapItem,
|
||||
} from '@metafam/ds';
|
||||
import {
|
||||
GetPlayersQueryVariables,
|
||||
PlayerFragmentFragment,
|
||||
} from 'graphql/autogen/types';
|
||||
import { PlayerAggregates, QueryVariableSetter } from 'lib/hooks/players';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
type Props = {
|
||||
fetching: boolean;
|
||||
players: PlayerFragmentFragment[];
|
||||
aggregates: PlayerAggregates;
|
||||
queryVariables: GetPlayersQueryVariables;
|
||||
setQueryVariable: QueryVariableSetter;
|
||||
};
|
||||
|
||||
export const PlayerFilter: React.FC<Props> = ({
|
||||
fetching,
|
||||
players,
|
||||
aggregates,
|
||||
queryVariables,
|
||||
setQueryVariable,
|
||||
}) => {
|
||||
const [search, setSearch] = useState<string>('');
|
||||
const onSearch = (e: React.ChangeEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (search.length >= 2) {
|
||||
setQueryVariable('search', `%${search}%`);
|
||||
} else {
|
||||
setQueryVariable('search', `%%`);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<form onSubmit={onSearch}>
|
||||
<Stack
|
||||
spacing="4"
|
||||
w="100%"
|
||||
maxW="2xl"
|
||||
direction={{ base: 'column', md: 'row' }}
|
||||
align="center"
|
||||
>
|
||||
<Input
|
||||
background="dark"
|
||||
w="100%"
|
||||
type="text"
|
||||
minW={{ base: 'sm', sm: 'md', md: 'lg', lg: 'xl' }}
|
||||
placeholder="SEARCH PLAYERS BY USERNAME OR ETHEREUM ADDRESS"
|
||||
_placeholder={{ color: 'whiteAlpha.500' }}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
size="lg"
|
||||
borderRadius="0"
|
||||
borderColor="purple.400"
|
||||
fontSize="md"
|
||||
borderWidth="2px"
|
||||
/>
|
||||
<MetaButton type="submit" size="lg" isLoading={fetching} px="16">
|
||||
SEARCH
|
||||
</MetaButton>
|
||||
</Stack>
|
||||
</form>
|
||||
<Wrap
|
||||
justify="space-between"
|
||||
w="100%"
|
||||
bg="whiteAlpha.200"
|
||||
style={{ backdropFilter: 'blur(7px)' }}
|
||||
p="6"
|
||||
borderRadius="6px"
|
||||
maxW="79rem"
|
||||
>
|
||||
<WrapItem>
|
||||
<Wrap spacing="4">
|
||||
<WrapItem>
|
||||
<VStack spacing="2" w="100%">
|
||||
<Text
|
||||
textTransform="uppercase"
|
||||
color="blueLight"
|
||||
w="100%"
|
||||
fontSize="xs"
|
||||
>
|
||||
Show
|
||||
</Text>
|
||||
<MetaSelect
|
||||
value={queryVariables.limit as number}
|
||||
onChange={(e) =>
|
||||
setQueryVariable('limit', Number(e.target.value))
|
||||
}
|
||||
minW="3rem"
|
||||
>
|
||||
<option value={10}>10</option>
|
||||
<option value={20}>20</option>
|
||||
<option value={50}>50</option>
|
||||
<option value={150}>150</option>
|
||||
</MetaSelect>
|
||||
</VStack>
|
||||
</WrapItem>
|
||||
<WrapItem>
|
||||
<VStack spacing="2" w="100%">
|
||||
<Text
|
||||
textTransform="uppercase"
|
||||
color="blueLight"
|
||||
w="100%"
|
||||
fontSize="xs"
|
||||
>
|
||||
Player Type
|
||||
</Text>
|
||||
<MetaSelect
|
||||
value={(queryVariables.playerType as number) || ''}
|
||||
onChange={(e) =>
|
||||
setQueryVariable('playerType', e.target.value)
|
||||
}
|
||||
>
|
||||
<option value="">All Types</option>
|
||||
{aggregates.playerTypes &&
|
||||
aggregates.playerTypes.map(({ id, title }) => (
|
||||
<option key={id} value={id}>
|
||||
{title}
|
||||
</option>
|
||||
))}
|
||||
</MetaSelect>
|
||||
</VStack>
|
||||
</WrapItem>
|
||||
<WrapItem>
|
||||
<VStack spacing="2" w="100%">
|
||||
<Text
|
||||
textTransform="uppercase"
|
||||
color="blueLight"
|
||||
w="100%"
|
||||
fontSize="xs"
|
||||
>
|
||||
Skills
|
||||
</Text>
|
||||
<MetaSelect
|
||||
value={(queryVariables.skillCategory as string) || ''}
|
||||
onChange={(e) =>
|
||||
setQueryVariable('skillCategory', e.target.value)
|
||||
}
|
||||
>
|
||||
<option value="">All Skills</option>
|
||||
{aggregates.skillCategories &&
|
||||
aggregates.skillCategories.map(({ name }) => (
|
||||
<option key={name} value={name}>
|
||||
{name}
|
||||
</option>
|
||||
))}
|
||||
</MetaSelect>
|
||||
</VStack>
|
||||
</WrapItem>
|
||||
<WrapItem>
|
||||
<VStack spacing="2" w="100%">
|
||||
<Text
|
||||
textTransform="uppercase"
|
||||
color="blueLight"
|
||||
w="100%"
|
||||
fontSize="xs"
|
||||
>
|
||||
Availability
|
||||
</Text>
|
||||
<MetaSelect
|
||||
value={queryVariables.availability as number}
|
||||
onChange={(e) =>
|
||||
setQueryVariable('availability', e.target.value)
|
||||
}
|
||||
>
|
||||
<option value={0}>Any h/week</option>
|
||||
<option value={1}>{'> 1 h/week'}</option>
|
||||
<option value={5}>{'> 5 h/week'}</option>
|
||||
<option value={10}>{'> 10 h/week'}</option>
|
||||
<option value={20}>{'> 20 h/week'}</option>
|
||||
<option value={30}>{'> 30 h/week'}</option>
|
||||
<option value={40}>{'> 40 h/week'}</option>
|
||||
</MetaSelect>
|
||||
</VStack>
|
||||
</WrapItem>
|
||||
<WrapItem>
|
||||
<VStack spacing="2" w="100%">
|
||||
<Text
|
||||
textTransform="uppercase"
|
||||
color="blueLight"
|
||||
w="100%"
|
||||
fontSize="xs"
|
||||
>
|
||||
Timezone
|
||||
</Text>
|
||||
<MetaSelect
|
||||
value={(queryVariables.timezone as string) || ''}
|
||||
onChange={(e) => setQueryVariable('timezone', e.target.value)}
|
||||
>
|
||||
<option value="">All timezones</option>
|
||||
{TimezoneOptions.map((z: TimezoneType) => (
|
||||
<option key={z.id} value={z.id}>
|
||||
{z.label}
|
||||
</option>
|
||||
))}
|
||||
</MetaSelect>
|
||||
</VStack>
|
||||
</WrapItem>
|
||||
</Wrap>
|
||||
</WrapItem>
|
||||
{players && !fetching && (
|
||||
<WrapItem>
|
||||
<Text align="center" fontWeight="bold">
|
||||
{players.length} players
|
||||
</Text>
|
||||
</WrapItem>
|
||||
)}
|
||||
</Wrap>
|
||||
</>
|
||||
);
|
||||
};
|
||||
23
packages/web/components/Player/PlayerList.tsx
Normal file
23
packages/web/components/Player/PlayerList.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { SimpleGrid, Text } from '@metafam/ds';
|
||||
import { PlayerTile } from 'components/Player/PlayerTile';
|
||||
import { PlayerFragmentFragment } from 'graphql/autogen/types';
|
||||
import React from 'react';
|
||||
|
||||
type Props = {
|
||||
players: PlayerFragmentFragment[];
|
||||
};
|
||||
|
||||
export const PlayerList: React.FC<Props> = ({ players }) =>
|
||||
players.length > 0 ? (
|
||||
<SimpleGrid
|
||||
columns={[1, null, 2, 3]}
|
||||
spacing="8"
|
||||
autoRows="minmax(35rem, auto)"
|
||||
>
|
||||
{players.map((p) => (
|
||||
<PlayerTile key={p.username} player={p} />
|
||||
))}
|
||||
</SimpleGrid>
|
||||
) : (
|
||||
<Text>No players found</Text>
|
||||
);
|
||||
@@ -1,20 +0,0 @@
|
||||
import { SimpleGrid } from '@metafam/ds';
|
||||
import { PlayerTile } from 'components/Player/PlayerTile';
|
||||
import { PlayerFragmentFragment } from 'graphql/autogen/types';
|
||||
import React from 'react';
|
||||
|
||||
type Props = {
|
||||
players: PlayerFragmentFragment[];
|
||||
};
|
||||
|
||||
export const PlayerList: React.FC<Props> = ({ players }) => (
|
||||
<SimpleGrid
|
||||
columns={[1, null, 2, 3]}
|
||||
spacing="8"
|
||||
autoRows="minmax(35rem, auto)"
|
||||
>
|
||||
{players.map((p) => (
|
||||
<PlayerTile key={p.id} player={p} />
|
||||
))}
|
||||
</SimpleGrid>
|
||||
);
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
Flex,
|
||||
MetaButton,
|
||||
Select,
|
||||
MetaSelect,
|
||||
Switch,
|
||||
Text,
|
||||
Wrap,
|
||||
@@ -43,7 +43,7 @@ export const QuestFilter: React.FC<Props> = ({
|
||||
<WrapItem>
|
||||
<Wrap>
|
||||
<WrapItem>
|
||||
<Select
|
||||
<MetaSelect
|
||||
value={queryVariables.limit as number}
|
||||
onChange={(e) =>
|
||||
setQueryVariable('limit', Number(e.target.value))
|
||||
@@ -52,28 +52,28 @@ export const QuestFilter: React.FC<Props> = ({
|
||||
<option value={10}>10</option>
|
||||
<option value={20}>20</option>
|
||||
<option value={50}>50</option>
|
||||
</Select>
|
||||
</MetaSelect>
|
||||
</WrapItem>
|
||||
<WrapItem>
|
||||
<Select
|
||||
<MetaSelect
|
||||
value={queryVariables.order as string}
|
||||
onChange={(e) => setQueryVariable('order', e.target.value)}
|
||||
>
|
||||
<option value={Order_By.Desc}>Newest</option>
|
||||
<option value={Order_By.Asc}>Oldest</option>
|
||||
</Select>
|
||||
</MetaSelect>
|
||||
</WrapItem>
|
||||
<WrapItem>
|
||||
<Select
|
||||
<MetaSelect
|
||||
value={queryVariables.status as string}
|
||||
onChange={(e) => setQueryVariable('status', e.target.value)}
|
||||
>
|
||||
<option value={QuestStatus_Enum.Open}>Open</option>
|
||||
<option value={QuestStatus_Enum.Closed}>Closed</option>
|
||||
</Select>
|
||||
</MetaSelect>
|
||||
</WrapItem>
|
||||
<WrapItem>
|
||||
<Select
|
||||
<MetaSelect
|
||||
value={(queryVariables.guild_id as string) || ''}
|
||||
onChange={(e) => setQueryVariable('guild_id', e.target.value)}
|
||||
>
|
||||
@@ -84,7 +84,7 @@ export const QuestFilter: React.FC<Props> = ({
|
||||
{g.name}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</MetaSelect>
|
||||
</WrapItem>
|
||||
|
||||
{myId && (
|
||||
@@ -94,6 +94,8 @@ export const QuestFilter: React.FC<Props> = ({
|
||||
size="md"
|
||||
colorScheme="cyan"
|
||||
variant="outline"
|
||||
borderWidth="2px"
|
||||
borderRadius="4px"
|
||||
px={4}
|
||||
onClick={() =>
|
||||
setQueryVariable(
|
||||
@@ -107,13 +109,6 @@ export const QuestFilter: React.FC<Props> = ({
|
||||
isChecked={
|
||||
myId && queryVariables.created_by_player_id === myId
|
||||
}
|
||||
onClick={(e) => {
|
||||
setQueryVariable(
|
||||
'created_by_player_id',
|
||||
queryVariables.created_by_player_id ? '' : myId,
|
||||
);
|
||||
e.preventDefault();
|
||||
}}
|
||||
/>
|
||||
</MetaButton>
|
||||
</Flex>
|
||||
@@ -123,7 +118,9 @@ export const QuestFilter: React.FC<Props> = ({
|
||||
</WrapItem>
|
||||
{quests && (
|
||||
<WrapItem>
|
||||
<Text align="center">{quests.length} quests</Text>
|
||||
<Text align="center" fontWeight="bold">
|
||||
{quests.length} quests
|
||||
</Text>
|
||||
</WrapItem>
|
||||
)}
|
||||
</Wrap>
|
||||
|
||||
@@ -23,18 +23,14 @@ const errorHasResponseTimeout = (err: CombinedError): boolean =>
|
||||
err.graphQLErrors.length > 0 &&
|
||||
!!err.graphQLErrors.find((_err) => _err.message === 'ResponseTimeout');
|
||||
|
||||
const retryExchangeFunc = retryExchange({
|
||||
retryIf: (error) => !!(errorHasResponseTimeout(error) || error.networkError),
|
||||
});
|
||||
|
||||
export const client = createClient({
|
||||
url: CONFIG.graphqlURL,
|
||||
suspense: false,
|
||||
exchanges: [
|
||||
dedupExchange,
|
||||
cacheExchange,
|
||||
retryExchange({
|
||||
retryIf: (error) =>
|
||||
!!(errorHasResponseTimeout(error) || error.networkError),
|
||||
}),
|
||||
fetchExchange,
|
||||
],
|
||||
exchanges: [dedupExchange, cacheExchange, retryExchangeFunc, fetchExchange],
|
||||
});
|
||||
|
||||
export const getSsrClient = (): [Client, ReturnType<typeof ssrExchange>] => {
|
||||
@@ -43,7 +39,13 @@ export const getSsrClient = (): [Client, ReturnType<typeof ssrExchange>] => {
|
||||
const ssrClient = initUrqlClient(
|
||||
{
|
||||
url: CONFIG.graphqlURL,
|
||||
exchanges: [dedupExchange, cacheExchange, ssrCache, fetchExchange],
|
||||
exchanges: [
|
||||
dedupExchange,
|
||||
cacheExchange,
|
||||
ssrCache,
|
||||
retryExchangeFunc,
|
||||
fetchExchange,
|
||||
],
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
@@ -1,77 +1,92 @@
|
||||
import gql from 'fake-tag';
|
||||
import { Client } from 'urql';
|
||||
|
||||
import {
|
||||
GetPlayersDocument,
|
||||
GetPlayersQuery,
|
||||
GetPlayersQueryVariables,
|
||||
GetPlayerUsernamesQuery,
|
||||
GetPlayerUsernamesQueryVariables,
|
||||
PlayerFragmentFragment,
|
||||
} from './autogen/types';
|
||||
import { client } from './client';
|
||||
import { client as defaultClient } from './client';
|
||||
import { PlayerFragment } from './fragments';
|
||||
|
||||
const playersQuery = gql`
|
||||
query GetPlayers($limit: Int, $offset: Int) {
|
||||
player(order_by: { total_xp: desc }, limit: $limit, offset: $offset) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
gql`
|
||||
query GetPlayers(
|
||||
$offset: Int
|
||||
$limit: Int
|
||||
$skillCategory: SkillCategory_enum
|
||||
$playerType: Int
|
||||
$availability: Int
|
||||
$timezone: String
|
||||
$search: String
|
||||
) {
|
||||
player(
|
||||
order_by: { total_xp: desc }
|
||||
offset: $offset
|
||||
limit: $limit
|
||||
where: {
|
||||
Player_Skills: { Skill: { category: { _eq: $skillCategory } } }
|
||||
playerType: { id: { _eq: $playerType } }
|
||||
availability_hours: { _gte: $availability }
|
||||
timezone: { _eq: $timezone }
|
||||
_or: [
|
||||
{ username: { _ilike: $search } }
|
||||
{ ethereum_address: { _ilike: $search } }
|
||||
]
|
||||
}
|
||||
) {
|
||||
...PlayerFragment
|
||||
}
|
||||
}
|
||||
${PlayerFragment}
|
||||
`;
|
||||
|
||||
export const getPlayers = async (
|
||||
limit = 50,
|
||||
offset = 0,
|
||||
): Promise<PlayerFragmentFragment[]> => {
|
||||
const { data, error } = await client
|
||||
.query<GetPlayersQuery, GetPlayersQueryVariables>(playersQuery, {
|
||||
limit,
|
||||
offset,
|
||||
})
|
||||
.toPromise();
|
||||
|
||||
if (!data) {
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
return data.player;
|
||||
export const defaultQueryVariables: GetPlayersQueryVariables = {
|
||||
offset: 0,
|
||||
limit: 50,
|
||||
skillCategory: undefined,
|
||||
playerType: undefined,
|
||||
availability: 0,
|
||||
timezone: undefined,
|
||||
search: '%%',
|
||||
};
|
||||
|
||||
const LIMIT = 50;
|
||||
const TOTAL_PLAYERS = 150;
|
||||
export type PlayersResponse = {
|
||||
error: Error | undefined;
|
||||
players: PlayerFragmentFragment[];
|
||||
};
|
||||
|
||||
export const getTopPlayers = async (): Promise<PlayerFragmentFragment[]> => {
|
||||
const promises: Promise<PlayerFragmentFragment[]>[] = new Array(
|
||||
TOTAL_PLAYERS / LIMIT,
|
||||
)
|
||||
.fill(false)
|
||||
.map((_, i) => getPlayers(LIMIT, i * LIMIT));
|
||||
const playersArr = await Promise.all(promises);
|
||||
return playersArr.reduce((_total, _players) => [..._total, ..._players], []);
|
||||
export const getPlayers = async (
|
||||
queryVariables = defaultQueryVariables,
|
||||
client: Client = defaultClient,
|
||||
): Promise<PlayersResponse> => {
|
||||
const { data, error } = await client
|
||||
.query<GetPlayersQuery, GetPlayersQueryVariables>(
|
||||
GetPlayersDocument,
|
||||
queryVariables,
|
||||
)
|
||||
.toPromise();
|
||||
|
||||
return { players: data?.player || [], error };
|
||||
};
|
||||
|
||||
const playerUsernamesQuery = gql`
|
||||
query GetPlayerUsernames($limit: Int, $offset: Int) {
|
||||
player(order_by: { total_xp: desc }, limit: $limit, offset: $offset) {
|
||||
query GetPlayerUsernames($limit: Int) {
|
||||
player(order_by: { total_xp: desc }, limit: $limit) {
|
||||
username
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const getPlayerUsernames = async (
|
||||
limit = 50,
|
||||
offset = 0,
|
||||
): Promise<string[]> => {
|
||||
const { data, error } = await client
|
||||
export const getPlayerUsernames = async (limit = 150): Promise<string[]> => {
|
||||
const { data, error } = await defaultClient
|
||||
.query<GetPlayerUsernamesQuery, GetPlayerUsernamesQueryVariables>(
|
||||
playerUsernamesQuery,
|
||||
{
|
||||
limit,
|
||||
offset,
|
||||
},
|
||||
)
|
||||
.toPromise();
|
||||
@@ -87,10 +102,47 @@ export const getPlayerUsernames = async (
|
||||
return data.player.map((p) => p.username);
|
||||
};
|
||||
|
||||
export const getTopPlayerUsernames = async (): Promise<string[]> => {
|
||||
const promises: Promise<string[]>[] = new Array(TOTAL_PLAYERS / LIMIT)
|
||||
export const getTopPlayerUsernames = getPlayerUsernames;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
gql`
|
||||
query GetPlayerFilters {
|
||||
skill_aggregate(distinct_on: category) {
|
||||
nodes {
|
||||
name: category
|
||||
}
|
||||
}
|
||||
player_type(distinct_on: id) {
|
||||
id
|
||||
title
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const getPlayersInParallel = async (
|
||||
variables: GetPlayersQueryVariables,
|
||||
): Promise<PlayersResponse> => {
|
||||
const limit = 50;
|
||||
const total = variables?.limit as number;
|
||||
if (total <= limit) {
|
||||
return getPlayers(variables);
|
||||
}
|
||||
const len = Math.ceil(total / limit);
|
||||
const variablesArr: GetPlayersQueryVariables[] = new Array<boolean>(len)
|
||||
.fill(false)
|
||||
.map((_, i) => getPlayerUsernames(LIMIT, i * LIMIT));
|
||||
const playersArr = await Promise.all(promises);
|
||||
return playersArr.reduce((_total, _players) => [..._total, ..._players], []);
|
||||
.map((_, i) => ({
|
||||
...variables,
|
||||
offset: i * limit,
|
||||
limit: i < len - 1 ? limit : total - limit * (len - 1),
|
||||
}));
|
||||
|
||||
const promises = variablesArr.map((vars) => getPlayers(vars));
|
||||
const playersRespArr = await Promise.all(promises);
|
||||
return playersRespArr.reduce(
|
||||
(totalRes, response) => ({
|
||||
error: totalRes.error || response.error,
|
||||
players: [...totalRes.players, ...response.players],
|
||||
}),
|
||||
{ error: undefined, players: [] },
|
||||
);
|
||||
};
|
||||
|
||||
@@ -43,14 +43,6 @@ gql`
|
||||
) {
|
||||
...QuestFragment
|
||||
}
|
||||
quest_aggregate(distinct_on: guild_id) {
|
||||
nodes {
|
||||
guild_id
|
||||
guild {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
${QuestFragment}
|
||||
|
||||
112
packages/web/lib/hooks/players.ts
Normal file
112
packages/web/lib/hooks/players.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import {
|
||||
GetPlayersQueryVariables,
|
||||
PlayerFragmentFragment,
|
||||
useGetPlayerFiltersQuery,
|
||||
useGetPlayersQuery,
|
||||
} from 'graphql/autogen/types';
|
||||
import {
|
||||
defaultQueryVariables,
|
||||
getPlayersInParallel,
|
||||
PlayersResponse,
|
||||
} from 'graphql/getPlayers';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type QueryVariableSetter = (key: string, value: any) => void;
|
||||
|
||||
export interface PlayerAggregates {
|
||||
skillCategories: { name: string }[];
|
||||
playerTypes: { id: number; title: string }[];
|
||||
}
|
||||
|
||||
interface PlayerFilter {
|
||||
players: PlayerFragmentFragment[];
|
||||
fetching: boolean;
|
||||
aggregates: PlayerAggregates;
|
||||
queryVariables: GetPlayersQueryVariables;
|
||||
setQueryVariable: QueryVariableSetter;
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
const usePlayerAggregates = () => {
|
||||
const [{ data }] = useGetPlayerFiltersQuery();
|
||||
return {
|
||||
skillCategories: data?.skill_aggregate.nodes || [],
|
||||
playerTypes: data?.player_type || [],
|
||||
};
|
||||
};
|
||||
|
||||
const usePlayersSingle = (
|
||||
run: boolean,
|
||||
variables: GetPlayersQueryVariables,
|
||||
) => {
|
||||
const [{ fetching, data, error }] = useGetPlayersQuery({
|
||||
variables,
|
||||
pause: !run,
|
||||
});
|
||||
const players = data?.player || [];
|
||||
return { fetching, players, error };
|
||||
};
|
||||
|
||||
const usePlayersParallel = (
|
||||
run: boolean,
|
||||
variables: GetPlayersQueryVariables,
|
||||
) => {
|
||||
const [fetching, setFetching] = useState(true);
|
||||
const [{ players, error }, setResponse] = useState<PlayersResponse>({
|
||||
error: undefined,
|
||||
players: [],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const load = async () => {
|
||||
if (run) {
|
||||
setFetching(true);
|
||||
const response = await getPlayersInParallel(variables);
|
||||
setResponse(response);
|
||||
setFetching(false);
|
||||
}
|
||||
};
|
||||
load();
|
||||
}, [run, variables]);
|
||||
|
||||
return { fetching, players, error };
|
||||
};
|
||||
|
||||
const useFilteredPlayers = (variables: GetPlayersQueryVariables) => {
|
||||
const runParallel = (variables.limit as number) > 50; // if limit is 150 then hasura is unable to handle in one query
|
||||
const playersParallel = usePlayersParallel(runParallel, variables);
|
||||
const playersSingle = usePlayersSingle(!runParallel, variables);
|
||||
return runParallel ? playersParallel : playersSingle;
|
||||
};
|
||||
|
||||
export const usePlayerFilter = (): PlayerFilter => {
|
||||
const [
|
||||
queryVariables,
|
||||
setQueryVariables,
|
||||
] = useState<GetPlayersQueryVariables>(defaultQueryVariables);
|
||||
|
||||
const aggregates = usePlayerAggregates();
|
||||
|
||||
const setQueryVariable: QueryVariableSetter = useCallback(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(key: string, value: any) => {
|
||||
setQueryVariables((oldQueryVariables) => ({
|
||||
...oldQueryVariables,
|
||||
[key]: value !== '' ? value : null,
|
||||
}));
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const { fetching, players, error } = useFilteredPlayers(queryVariables);
|
||||
|
||||
return {
|
||||
players,
|
||||
aggregates,
|
||||
fetching,
|
||||
error,
|
||||
queryVariables,
|
||||
setQueryVariable,
|
||||
};
|
||||
};
|
||||
@@ -1,14 +1,17 @@
|
||||
// eslint-disable-next-line
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const withTM = require('next-transpile-modules')(['react-timezone-select']);
|
||||
const withImages = require('next-images');
|
||||
|
||||
module.exports = withImages({
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: '/',
|
||||
destination: '/players',
|
||||
permanent: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
});
|
||||
module.exports = withTM(
|
||||
withImages({
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: '/',
|
||||
destination: '/players',
|
||||
permanent: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"moment": "2.29.1",
|
||||
"next": "10.2.0",
|
||||
"next-images": "1.7.0",
|
||||
"next-transpile-modules": "7.0.0",
|
||||
"next-urql": "3.1.0",
|
||||
"node-fetch": "2.6.1",
|
||||
"opensea-js": "1.1.11",
|
||||
@@ -33,9 +34,9 @@
|
||||
"react-hook-form": "6.15.5",
|
||||
"react-icons": "4.2.0",
|
||||
"react-qr-svg": "2.3.0",
|
||||
"spacetime": "6.16.0",
|
||||
"spacetime-informal": "0.6.1",
|
||||
"urql": "2.0.2",
|
||||
"web3modal": "1.9.3"
|
||||
"web3modal": "1.9.3",
|
||||
"spacetime": "6.16.1",
|
||||
"spacetime-informal": "0.6.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PageContainer } from 'components/Container';
|
||||
import { GuildList } from 'components/GuildList';
|
||||
import { GuildList } from 'components/Guild/GuildList';
|
||||
import { getGuilds } from 'graphql/getGuilds';
|
||||
import { InferGetStaticPropsType } from 'next';
|
||||
import React from 'react';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PageContainer } from 'components/Container';
|
||||
import { PatronList } from 'components/PatronList';
|
||||
import { PatronList } from 'components/Patron/PatronList';
|
||||
import { getPatrons } from 'graphql/getPatrons';
|
||||
import { InferGetStaticPropsType } from 'next';
|
||||
import React from 'react';
|
||||
|
||||
@@ -1,25 +1,52 @@
|
||||
import { LoadingState, Text, VStack } from '@metafam/ds';
|
||||
import { PageContainer } from 'components/Container';
|
||||
import { PlayerList } from 'components/PlayerList';
|
||||
import { getTopPlayers } from 'graphql/getPlayers';
|
||||
import { PlayerFilter } from 'components/Player/PlayerFilter';
|
||||
import { PlayerList } from 'components/Player/PlayerList';
|
||||
import { getSsrClient } from 'graphql/client';
|
||||
import { getPlayers } from 'graphql/getPlayers';
|
||||
import { usePlayerFilter } from 'lib/hooks/players';
|
||||
import { InferGetStaticPropsType } from 'next';
|
||||
import React from 'react';
|
||||
|
||||
type Props = InferGetStaticPropsType<typeof getStaticProps>;
|
||||
|
||||
export const getStaticProps = async () => {
|
||||
const players = await getTopPlayers();
|
||||
const [ssrClient, ssrCache] = getSsrClient();
|
||||
// This populate the cache server-side
|
||||
await getPlayers(undefined, ssrClient);
|
||||
return {
|
||||
props: {
|
||||
players,
|
||||
urqlState: ssrCache.extractData(),
|
||||
},
|
||||
revalidate: 1,
|
||||
};
|
||||
};
|
||||
|
||||
const Players: React.FC<Props> = ({ players }) => (
|
||||
<PageContainer>
|
||||
<PlayerList players={players} />
|
||||
</PageContainer>
|
||||
);
|
||||
const Players: React.FC<Props> = () => {
|
||||
const {
|
||||
players,
|
||||
aggregates,
|
||||
fetching,
|
||||
error,
|
||||
queryVariables,
|
||||
setQueryVariable,
|
||||
} = usePlayerFilter();
|
||||
return (
|
||||
<PageContainer>
|
||||
<VStack w="100%" spacing="8">
|
||||
<PlayerFilter
|
||||
fetching={fetching}
|
||||
aggregates={aggregates}
|
||||
queryVariables={queryVariables}
|
||||
setQueryVariable={setQueryVariable}
|
||||
players={players || []}
|
||||
/>
|
||||
{error && <Text>{`Error: ${error.message}`}</Text>}
|
||||
{fetching && <LoadingState />}
|
||||
{players && !fetching && !error && <PlayerList players={players} />}
|
||||
</VStack>
|
||||
</PageContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Players;
|
||||
|
||||
@@ -10,15 +10,14 @@ import {
|
||||
import { PageContainer } from 'components/Container';
|
||||
import { QuestFilter } from 'components/Quest/QuestFilter';
|
||||
import { QuestList } from 'components/Quest/QuestList';
|
||||
import { getSsrClient } from 'graphql/client';
|
||||
import { getQuests } from 'graphql/getQuests';
|
||||
import { usePSeedBalance } from 'lib/hooks/balances';
|
||||
import { useQuestFilter } from 'lib/hooks/quests';
|
||||
import { InferGetStaticPropsType } from 'next';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { getSsrClient } from '../graphql/client';
|
||||
import { usePSeedBalance } from '../lib/hooks/balances';
|
||||
import { useQuestFilter } from '../lib/hooks/quests';
|
||||
import { isAllowedToCreateQuest } from '../utils/questHelpers';
|
||||
import { isAllowedToCreateQuest } from 'utils/questHelpers';
|
||||
|
||||
type Props = InferGetStaticPropsType<typeof getStaticProps>;
|
||||
|
||||
|
||||
80
yarn.lock
80
yarn.lock
@@ -2026,7 +2026,7 @@
|
||||
source-map "^0.5.7"
|
||||
stylis "^4.0.3"
|
||||
|
||||
"@emotion/cache@^10.0.27", "@emotion/cache@^10.0.9":
|
||||
"@emotion/cache@^10.0.27":
|
||||
version "10.0.29"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0"
|
||||
integrity sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==
|
||||
@@ -2047,7 +2047,7 @@
|
||||
"@emotion/weak-memoize" "^0.2.5"
|
||||
stylis "^4.0.3"
|
||||
|
||||
"@emotion/core@^10.0.9", "@emotion/core@^10.1.1":
|
||||
"@emotion/core@^10.1.1":
|
||||
version "10.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.1.1.tgz#c956c1365f2f2481960064bcb8c4732e5fb612c3"
|
||||
integrity sha512-ZMLG6qpXR8x031NXD8HJqugy/AZSkAuMxxqB46pmAR7ze47MhNJ56cdoX243QPZdGctrdfo+s08yZTiwaUcRKA==
|
||||
@@ -2059,7 +2059,7 @@
|
||||
"@emotion/sheet" "0.9.4"
|
||||
"@emotion/utils" "0.11.3"
|
||||
|
||||
"@emotion/css@^10.0.27", "@emotion/css@^10.0.9":
|
||||
"@emotion/css@^10.0.27":
|
||||
version "10.0.27"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/css/-/css-10.0.27.tgz#3a7458198fbbebb53b01b2b87f64e5e21241e14c"
|
||||
integrity sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==
|
||||
@@ -12009,6 +12009,14 @@ enhanced-resolve@^4.0.0, enhanced-resolve@^4.5.0:
|
||||
memory-fs "^0.5.0"
|
||||
tapable "^1.0.0"
|
||||
|
||||
enhanced-resolve@^5.7.0:
|
||||
version "5.8.2"
|
||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz#15ddc779345cbb73e97c611cd00c01c1e7bf4d8b"
|
||||
integrity sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.4"
|
||||
tapable "^2.2.0"
|
||||
|
||||
enquirer@^2.3.4, enquirer@^2.3.5, enquirer@^2.3.6:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
|
||||
@@ -13804,10 +13812,10 @@ fragment-cache@^0.2.1:
|
||||
dependencies:
|
||||
map-cache "^0.2.2"
|
||||
|
||||
framer-motion@4.1.16:
|
||||
version "4.1.16"
|
||||
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-4.1.16.tgz#dc715334847d0a146acf47f61019222d0d1c46c9"
|
||||
integrity sha512-sEc3UI3oncwE+RUzdd86TxbmpEaX/Ki/T0AmFYSsbxEqGZ3feLvzGL7BJlkhERIyyuAC9+OzI4BnhJM0GSUAMA==
|
||||
framer-motion@4.1.17:
|
||||
version "4.1.17"
|
||||
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-4.1.17.tgz#4029469252a62ea599902e5a92b537120cc89721"
|
||||
integrity sha512-thx1wvKzblzbs0XaK2X0G1JuwIdARcoNOW7VVwjO8BUltzXPyONGAElLu6CiCScsOQRI7FIk/45YTFtJw5Yozw==
|
||||
dependencies:
|
||||
framesync "5.3.0"
|
||||
hey-listen "^1.0.8"
|
||||
@@ -20658,6 +20666,14 @@ next-tick@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
|
||||
integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
|
||||
|
||||
next-transpile-modules@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/next-transpile-modules/-/next-transpile-modules-7.0.0.tgz#5a48988cede89bc5920defb15378093d0d8562eb"
|
||||
integrity sha512-HgVczU5ajXKvE7HO3ZLmBmxXj79aq8jSZNYpCttim+MZ+b0GIsdk7AV2w7Ax/tIM1/dJA+vV/6loXCRYlbsGGA==
|
||||
dependencies:
|
||||
enhanced-resolve "^5.7.0"
|
||||
escalade "^3.1.1"
|
||||
|
||||
next-urql@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/next-urql/-/next-urql-3.1.0.tgz#9c354cfd54cdf886988a2cb1ea0fd89f48ee7555"
|
||||
@@ -23613,7 +23629,7 @@ react-router@5.2.0, react-router@^5.1.0, react-router@^5.2.0:
|
||||
tiny-invariant "^1.0.2"
|
||||
tiny-warning "^1.0.0"
|
||||
|
||||
react-select@4.3.1:
|
||||
react-select@4.3.1, react-select@^4.2.1:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react-select/-/react-select-4.3.1.tgz#389fc07c9bc7cf7d3c377b7a05ea18cd7399cb81"
|
||||
integrity sha512-HBBd0dYwkF5aZk1zP81Wx5UsLIIT2lSvAY2JiJo199LjoLHoivjn9//KsmvQMEFGNhe58xyuOITjfxKCcGc62Q==
|
||||
@@ -23626,20 +23642,6 @@ react-select@4.3.1:
|
||||
react-input-autosize "^3.0.0"
|
||||
react-transition-group "^4.3.0"
|
||||
|
||||
react-select@^3.1.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-select/-/react-select-3.2.0.tgz#de9284700196f5f9b5277c5d850a9ce85f5c72fe"
|
||||
integrity sha512-B/q3TnCZXEKItO0fFN/I0tWOX3WJvi/X2wtdffmwSQVRwg5BpValScTO1vdic9AxlUgmeSzib2hAZAwIUQUZGQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.4.4"
|
||||
"@emotion/cache" "^10.0.9"
|
||||
"@emotion/core" "^10.0.9"
|
||||
"@emotion/css" "^10.0.9"
|
||||
memoize-one "^5.0.0"
|
||||
prop-types "^15.6.0"
|
||||
react-input-autosize "^3.0.0"
|
||||
react-transition-group "^4.3.0"
|
||||
|
||||
react-sizeme@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-sizeme/-/react-sizeme-3.0.1.tgz#4d12f4244e0e6a0fb97253e7af0314dc7c83a5a0"
|
||||
@@ -23684,14 +23686,14 @@ react-textarea-autosize@^8.3.0:
|
||||
use-composed-ref "^1.0.0"
|
||||
use-latest "^1.0.0"
|
||||
|
||||
react-timezone-select@0.9.8:
|
||||
version "0.9.8"
|
||||
resolved "https://registry.yarnpkg.com/react-timezone-select/-/react-timezone-select-0.9.8.tgz#a5d9ead1fa0b40dab1b3a77d9cdcc20354fe2ec4"
|
||||
integrity sha512-CzIOs9IwAb5hmjs5jnB1uShQvw9zJGUVeH5dIpRVy1dgVnwkasvXc2fGwnggI6nYXAmN7uOCt5kFIcQOpZmUNw==
|
||||
react-timezone-select@1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/react-timezone-select/-/react-timezone-select-1.0.3.tgz#e257f7204c8ee74c30ab033f34a1d2d3a45963b9"
|
||||
integrity sha512-wpsu2jhEkn+/G91m24oqSlT/iAmcE4pQot9tOTUcV9kI+uA1kc+0YpvJFL5aT2jMVJ/uwpQkxUWAhRwa8U9YKg==
|
||||
dependencies:
|
||||
react-select "^3.1.0"
|
||||
spacetime "^6.6.2"
|
||||
spacetime-informal "^0.3.0"
|
||||
react-select "^4.2.1"
|
||||
spacetime "^6.14.0"
|
||||
spacetime-informal "^0.6.1"
|
||||
|
||||
react-transition-group@^4.3.0, react-transition-group@^4.4.0, react-transition-group@^4.4.1:
|
||||
version "4.4.1"
|
||||
@@ -25409,22 +25411,17 @@ space-separated-tokens@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899"
|
||||
integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==
|
||||
|
||||
spacetime-informal@0.6.1:
|
||||
spacetime-informal@0.6.1, spacetime-informal@^0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/spacetime-informal/-/spacetime-informal-0.6.1.tgz#621b4156a98f20f5c3f0151475d2ea886e1c19c1"
|
||||
integrity sha512-fOA+RMi2mpCbkLkjBIzWQttmkYE/v9VfdbbrvE2Pus/WoiQNmHO20YGNEqJOuFADsrrZOd/hJVuBwAkV3s6BHg==
|
||||
dependencies:
|
||||
efrt-unpack "2.2.0"
|
||||
|
||||
spacetime-informal@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/spacetime-informal/-/spacetime-informal-0.3.0.tgz#6d0feffe291697f686b737b678b59ee2dcef3297"
|
||||
integrity sha512-HtFTwtkzDl7gswCfeWPQAxvQ+Fmrdt7WqoDT0Lq2FX7RVZKZ/+zmgBOAVAuH8oVcf4uXza9NvdX9QppGRiG8oQ==
|
||||
|
||||
spacetime@6.16.0, spacetime@^6.6.2:
|
||||
version "6.16.0"
|
||||
resolved "https://registry.yarnpkg.com/spacetime/-/spacetime-6.16.0.tgz#f213963392eafc380716c3857b23251de87db97f"
|
||||
integrity sha512-mkuniNOp6ssfPyJidj81tb54zKxK4vEKPTmcUsC/NEGIF8S07ppoSotdg6numT1/26rthQYmdxMY/M5a9WeJVQ==
|
||||
spacetime@6.16.1, spacetime@^6.14.0:
|
||||
version "6.16.1"
|
||||
resolved "https://registry.yarnpkg.com/spacetime/-/spacetime-6.16.1.tgz#e032dc77bea494367a7a321a03458ebbf8f625b0"
|
||||
integrity sha512-+aK5oTrCCfXZsg73NEoy3V2cLa0xY3obddFNbWSJ8UcbuPb7f8zWOV4vH3hEAncznUJQqA7tGHAuVR4TysfgIA==
|
||||
|
||||
sparse-array@^1.3.1:
|
||||
version "1.3.2"
|
||||
@@ -26171,6 +26168,11 @@ tapable@^1.0.0, tapable@^1.1.3:
|
||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
|
||||
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
|
||||
|
||||
tapable@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b"
|
||||
integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==
|
||||
|
||||
tape@^4.4.0, tape@^4.6.3:
|
||||
version "4.13.3"
|
||||
resolved "https://registry.yarnpkg.com/tape/-/tape-4.13.3.tgz#51b3d91c83668c7a45b1a594b607dee0a0b46278"
|
||||
|
||||
Reference in New Issue
Block a user