mirror of
https://github.com/MetaFam/TheGame.git
synced 2026-04-24 03:00:09 -04:00
feat: profile layout edit + section add/remove
This commit is contained in:
@@ -7,16 +7,16 @@ export const Profile = createIcon({
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M16 9C16 11.2091 14.2091 13 12 13C9.79086 13 8 11.2091 8 9C8 6.79086 9.79086 5 12 5C14.2091 5 16 6.79086 16 9ZM14 9C14 10.1046 13.1046 11 12 11C10.8954 11 10 10.1046 10 9C10 7.89543 10.8954 7 12 7C13.1046 7 14 7.89543 14 9Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M12 1C5.92487 1 1 5.92487 1 12C1 18.0751 5.92487 23 12 23C18.0751 23 23 18.0751 23 12C23 5.92487 18.0751 1 12 1ZM3 12C3 14.0902 3.71255 16.014 4.90798 17.5417C6.55245 15.3889 9.14627 14 12.0645 14C14.9448 14 17.5092 15.3531 19.1565 17.4583C20.313 15.9443 21 14.0524 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12ZM12 21C9.84977 21 7.87565 20.2459 6.32767 18.9878C7.59352 17.1812 9.69106 16 12.0645 16C14.4084 16 16.4833 17.1521 17.7538 18.9209C16.1939 20.2191 14.1881 21 12 21Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
|
||||
@@ -11,6 +11,7 @@ export type MetaColors = ChakraTheme['colors'] & {
|
||||
purpleBoxLight: string;
|
||||
purpleTag: string;
|
||||
purpleTag30: string;
|
||||
purpleTag50: string;
|
||||
purpleTag70: string;
|
||||
blueLight: string;
|
||||
cyanText: string;
|
||||
@@ -47,6 +48,7 @@ export const colors: MetaColors = {
|
||||
purpleBoxLight: '#392373',
|
||||
purpleTag: '#40347C',
|
||||
purpleTag30: 'rgba(64, 52, 124, 0.3)',
|
||||
purpleTag50: 'rgba(64, 52, 124, 0.5)',
|
||||
purpleTag70: 'rgba(64, 52, 124, 0.7)',
|
||||
blueLight: '#A5B9F6',
|
||||
cyanText: '#79F8FB',
|
||||
|
||||
@@ -57,6 +57,7 @@ export const gridConfig = {
|
||||
pl: 6,
|
||||
},
|
||||
'& > div': {
|
||||
pointerEvents: editable ? 'none' : 'initial',
|
||||
bg: editable ? 'blackAlpha.500' : 'blackAlpha.300',
|
||||
backdropFilter: 'blur(10px)',
|
||||
borderBottomRadius: 'lg',
|
||||
|
||||
@@ -9,13 +9,12 @@ type Props = {
|
||||
const SHOW_MEMBERSHIPS = 4;
|
||||
|
||||
export const PlayerTileMemberships: React.FC<Props> = ({ player }) => {
|
||||
const displayMemberships =
|
||||
useMemo(
|
||||
() => (
|
||||
player.daohausMemberships?.filter(({ moloch: { title } }) => !!title) ?? []
|
||||
),
|
||||
[player.daohausMemberships],
|
||||
);
|
||||
const displayMemberships = useMemo(
|
||||
() =>
|
||||
player.daohausMemberships?.filter(({ moloch: { title } }) => !!title) ??
|
||||
[],
|
||||
[player.daohausMemberships],
|
||||
);
|
||||
return displayMemberships.length > 0 ? (
|
||||
<VStack spacing={2} align="stretch">
|
||||
<Text textStyle="caption">MEMBER OF</Text>
|
||||
|
||||
@@ -1,21 +1,38 @@
|
||||
import { HStack, Text } from '@metafam/ds';
|
||||
import { PlayerFragmentFragment } from 'graphql/autogen/types';
|
||||
import React from 'react';
|
||||
import { FaMedal } from 'react-icons/fa';
|
||||
import { BoxType } from 'utils/boxTypes';
|
||||
|
||||
import { ProfileSection } from '../../ProfileSection';
|
||||
|
||||
// TODO Fake data
|
||||
type Props = { onRemoveClick?: () => void };
|
||||
export const PlayerAchievements: React.FC<Props> = ({ onRemoveClick }) => {
|
||||
type Props = {
|
||||
player: PlayerFragmentFragment;
|
||||
isOwnProfile?: boolean;
|
||||
canEdit?: boolean;
|
||||
onRemoveClick?: () => void;
|
||||
};
|
||||
export const PlayerAchievements: React.FC<Props> = ({
|
||||
isOwnProfile,
|
||||
canEdit,
|
||||
onRemoveClick,
|
||||
}) => {
|
||||
const [show, setShow] = React.useState(false);
|
||||
const fakeData = [
|
||||
'Fake Achievement No. 1',
|
||||
'Founding Father of MetaGame',
|
||||
'Summoner of Meta Fam',
|
||||
'Dragon Quests Quest',
|
||||
];
|
||||
|
||||
return (
|
||||
<ProfileSection title="Achievements" onRemoveClick={onRemoveClick}>
|
||||
<ProfileSection
|
||||
title="Achievements"
|
||||
onRemoveClick={onRemoveClick}
|
||||
isOwnProfile={isOwnProfile}
|
||||
canEdit={canEdit}
|
||||
boxType={BoxType.PLAYER_ACHIEVEMENTS}
|
||||
>
|
||||
{(fakeData || []).slice(0, show ? 999 : 3).map((title) => (
|
||||
<HStack alignItems="baseline" mb={3}>
|
||||
<FaMedal color="#FBB112" />
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Button, Flex, FlexProps, HStack, Select } from '@metafam/ds';
|
||||
import React from 'react';
|
||||
import { BoxType } from 'utils/boxTypes';
|
||||
|
||||
type Props = FlexProps & {
|
||||
boxList: string[];
|
||||
setNewBox: (name: string) => void;
|
||||
boxList: BoxType[];
|
||||
setNewBox: (name: BoxType) => void;
|
||||
};
|
||||
|
||||
export const PlayerAddSection: React.FC<Props> = ({
|
||||
@@ -14,7 +15,7 @@ export const PlayerAddSection: React.FC<Props> = ({
|
||||
const [show, setShow] = React.useState(false);
|
||||
const addSection = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
setShow(false);
|
||||
setNewBox(e.target.value);
|
||||
setNewBox(e.target.value as BoxType);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -25,6 +26,7 @@ export const PlayerAddSection: React.FC<Props> = ({
|
||||
borderTopRadius="lg"
|
||||
py={12}
|
||||
boxShadow="md"
|
||||
w="100%"
|
||||
css={{ backdropFilter: 'blur(8px)' }}
|
||||
{...props}
|
||||
>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getPersonalityInfo } from 'graphql/queries/enums/getPersonalityInfo';
|
||||
import { PersonalityOption } from 'graphql/types';
|
||||
import { useAnimateProfileChanges } from 'lib/hooks/players';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { BOX_TYPE } from 'utils/boxTypes';
|
||||
import { BoxType } from 'utils/boxTypes';
|
||||
|
||||
import { FlexContainer } from '../../Container';
|
||||
import { ProfileSection } from '../../ProfileSection';
|
||||
@@ -13,11 +13,13 @@ import { ColorBar } from '../ColorBar';
|
||||
type Props = {
|
||||
player: PlayerFragmentFragment;
|
||||
isOwnProfile?: boolean;
|
||||
canEdit?: boolean;
|
||||
onRemoveClick?: () => void;
|
||||
};
|
||||
export const PlayerColorDisposition: React.FC<Props> = ({
|
||||
player,
|
||||
isOwnProfile,
|
||||
canEdit,
|
||||
onRemoveClick,
|
||||
}) => {
|
||||
const [types, setTypes] = useState<{
|
||||
@@ -45,7 +47,8 @@ export const PlayerColorDisposition: React.FC<Props> = ({
|
||||
title="Color Disposition"
|
||||
onRemoveClick={onRemoveClick}
|
||||
isOwnProfile={isOwnProfile}
|
||||
boxType={BOX_TYPE.PLAYER.COLOR_DISPOSITION}
|
||||
canEdit={canEdit}
|
||||
boxType={BoxType.PLAYER_COLOR_DISPOSITION}
|
||||
>
|
||||
{colorDisposition && types && (
|
||||
<FlexContainer
|
||||
|
||||
@@ -16,10 +16,9 @@ import { ProfileSection } from 'components/ProfileSection';
|
||||
import { PlayerFragmentFragment } from 'graphql/autogen/types';
|
||||
import { Collectible, useOpenSeaCollectibles } from 'lib/hooks/opensea';
|
||||
import React from 'react';
|
||||
import { BoxType } from 'utils/boxTypes';
|
||||
import { isBackdropFilterSupported } from 'utils/compatibilityHelpers';
|
||||
|
||||
type Props = { player: PlayerFragmentFragment; onRemoveClick?: () => void };
|
||||
|
||||
const GalleryItem: React.FC<{ nft: Collectible; noMargin?: boolean }> = ({
|
||||
nft,
|
||||
noMargin = false,
|
||||
@@ -60,7 +59,19 @@ const GalleryItem: React.FC<{ nft: Collectible; noMargin?: boolean }> = ({
|
||||
</Link>
|
||||
);
|
||||
|
||||
export const PlayerGallery: React.FC<Props> = ({ player, onRemoveClick }) => {
|
||||
type Props = {
|
||||
player: PlayerFragmentFragment;
|
||||
isOwnProfile?: boolean;
|
||||
canEdit?: boolean;
|
||||
onRemoveClick?: () => void;
|
||||
};
|
||||
|
||||
export const PlayerGallery: React.FC<Props> = ({
|
||||
player,
|
||||
onRemoveClick,
|
||||
isOwnProfile,
|
||||
canEdit,
|
||||
}) => {
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { favorites, data, loading } = useOpenSeaCollectibles({ player });
|
||||
|
||||
@@ -74,7 +85,13 @@ export const PlayerGallery: React.FC<Props> = ({ player, onRemoveClick }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<ProfileSection title="NFT Gallery" onRemoveClick={onRemoveClick}>
|
||||
<ProfileSection
|
||||
title="NFT Gallery"
|
||||
onRemoveClick={onRemoveClick}
|
||||
isOwnProfile={isOwnProfile}
|
||||
canEdit={canEdit}
|
||||
boxType={BoxType.PLAYER_NFT_GALLERY}
|
||||
>
|
||||
{!loading &&
|
||||
favorites?.map((nft) => <GalleryItem nft={nft} key={nft.tokenId} />)}
|
||||
{!loading && data?.length > 3 && (
|
||||
|
||||
@@ -24,6 +24,7 @@ import { useUser } from 'lib/hooks';
|
||||
import { useAnimateProfileChanges } from 'lib/hooks/players';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { FaClock, FaGlobe } from 'react-icons/fa';
|
||||
import { BoxType } from 'utils/boxTypes';
|
||||
import { getPlayerTimeZoneDisplay } from 'utils/dateHelpers';
|
||||
import { getPlayerDescription, getPlayerName } from 'utils/playerHelpers';
|
||||
|
||||
@@ -35,13 +36,21 @@ import { PlayerPronouns } from './PlayerPronouns';
|
||||
|
||||
const MAX_BIO_LENGTH = 240;
|
||||
|
||||
type Props = { player: PlayerFragmentFragment; isOwnProfile: boolean };
|
||||
type Props = {
|
||||
player: PlayerFragmentFragment;
|
||||
isOwnProfile?: boolean;
|
||||
canEdit?: boolean;
|
||||
};
|
||||
type AvailabilityProps = { person: PlayerFragmentFragment | null | undefined };
|
||||
type TimeZoneDisplayProps = {
|
||||
person: PlayerFragmentFragment | null | undefined;
|
||||
};
|
||||
|
||||
export const PlayerHero: React.FC<Props> = ({ player, isOwnProfile }) => {
|
||||
export const PlayerHero: React.FC<Props> = ({
|
||||
player,
|
||||
isOwnProfile,
|
||||
canEdit,
|
||||
}) => {
|
||||
const description = getPlayerDescription(player);
|
||||
const [show, setShow] = useState(description.length <= MAX_BIO_LENGTH);
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
@@ -58,8 +67,8 @@ export const PlayerHero: React.FC<Props> = ({ player, isOwnProfile }) => {
|
||||
}, [person]);
|
||||
|
||||
return (
|
||||
<ProfileSection>
|
||||
{isOwnProfile && (
|
||||
<ProfileSection canEdit={canEdit} boxType={BoxType.PLAYER_HERO}>
|
||||
{isOwnProfile && !canEdit && (
|
||||
<Box pos="absolute" right={5} top={5}>
|
||||
<IconButton
|
||||
_focus={{
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
import { PlayerFragmentFragment } from 'graphql/autogen/types';
|
||||
import { getAllMemberships, GuildMembership } from 'graphql/getMemberships';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { BoxType } from 'utils/boxTypes';
|
||||
import { isBackdropFilterSupported } from 'utils/compatibilityHelpers';
|
||||
|
||||
import { ProfileSection } from '../../ProfileSection';
|
||||
@@ -93,12 +94,16 @@ const DaoListing: React.FC<DaoListingProps> = ({ membership }) => {
|
||||
|
||||
type MembershipSectionProps = {
|
||||
player: PlayerFragmentFragment;
|
||||
isOwnProfile?: boolean;
|
||||
canEdit?: boolean;
|
||||
onRemoveClick?: () => void;
|
||||
};
|
||||
|
||||
export const PlayerMemberships: React.FC<MembershipSectionProps> = ({
|
||||
player,
|
||||
onRemoveClick,
|
||||
isOwnProfile,
|
||||
canEdit,
|
||||
}) => {
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const [guildMemberships, setGuildMemberships] = useState<GuildMembership[]>(
|
||||
@@ -123,7 +128,13 @@ export const PlayerMemberships: React.FC<MembershipSectionProps> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<ProfileSection title="Memberships" onRemoveClick={onRemoveClick}>
|
||||
<ProfileSection
|
||||
title="Memberships"
|
||||
onRemoveClick={onRemoveClick}
|
||||
isOwnProfile={isOwnProfile}
|
||||
canEdit={canEdit}
|
||||
boxType={BoxType.PLAYER_DAO_MEMBERSHIPS}
|
||||
>
|
||||
{loadingMemberships && <LoadingState />}
|
||||
|
||||
{guildMemberships.slice(0, 4).map((membership) => (
|
||||
|
||||
@@ -1,15 +1,52 @@
|
||||
import { Wrap } from '@metafam/ds';
|
||||
import { BoxedNextImage, MetaTag, Text, Wrap } from '@metafam/ds';
|
||||
import { PlayerFragmentFragment } from 'graphql/autogen/types';
|
||||
import React from 'react';
|
||||
import { BoxType } from 'utils/boxTypes';
|
||||
|
||||
import { ProfileSection } from '../../ProfileSection';
|
||||
|
||||
type Props = {
|
||||
player: PlayerFragmentFragment;
|
||||
isOwnProfile?: boolean;
|
||||
canEdit?: boolean;
|
||||
onRemoveClick?: () => void;
|
||||
};
|
||||
export const PlayerRoles: React.FC<Props> = ({ onRemoveClick }) => (
|
||||
<ProfileSection title="Roles" onRemoveClick={onRemoveClick}>
|
||||
<Wrap />
|
||||
export const PlayerRoles: React.FC<Props> = ({
|
||||
player,
|
||||
isOwnProfile,
|
||||
canEdit,
|
||||
onRemoveClick,
|
||||
}) => (
|
||||
<ProfileSection
|
||||
title="Roles"
|
||||
onRemoveClick={onRemoveClick}
|
||||
isOwnProfile={isOwnProfile}
|
||||
canEdit={canEdit}
|
||||
boxType={BoxType.PLAYER_ROLES}
|
||||
>
|
||||
<Wrap>
|
||||
{player.roles &&
|
||||
player.roles
|
||||
.sort((a, b) => (a.rank > b.rank ? 1 : -1))
|
||||
.map(({ role, rank, PlayerRole }) => (
|
||||
<MetaTag>
|
||||
<BoxedNextImage
|
||||
src={`/assets/roles/${role.toLowerCase()}.svg`}
|
||||
alt={PlayerRole.label}
|
||||
h="4"
|
||||
w="4"
|
||||
mr={2}
|
||||
/>
|
||||
<Text
|
||||
color={rank === 0 ? 'cyan.500' : 'white'}
|
||||
fontWeight="bold"
|
||||
casing="uppercase"
|
||||
my={{ base: 0, md: 2 }}
|
||||
>
|
||||
{PlayerRole.label}
|
||||
</Text>
|
||||
</MetaTag>
|
||||
))}
|
||||
</Wrap>
|
||||
</ProfileSection>
|
||||
);
|
||||
|
||||
112
packages/web/components/Player/Section/PlayerSection.tsx
Normal file
112
packages/web/components/Player/Section/PlayerSection.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
import { PlayerAchievements } from 'components/Player/Section/PlayerAchievements';
|
||||
import { PlayerColorDisposition } from 'components/Player/Section/PlayerColorDisposition';
|
||||
import { PlayerGallery } from 'components/Player/Section/PlayerGallery';
|
||||
import { PlayerHero } from 'components/Player/Section/PlayerHero';
|
||||
import { PlayerMemberships } from 'components/Player/Section/PlayerMemberships';
|
||||
import { PlayerRoles } from 'components/Player/Section/PlayerRoles';
|
||||
import { PlayerSkills } from 'components/Player/Section/PlayerSkills';
|
||||
import { PlayerType } from 'components/Player/Section/PlayerType';
|
||||
import { PlayerFragmentFragment } from 'graphql/autogen/types';
|
||||
import { BoxType } from 'utils/boxTypes';
|
||||
|
||||
import { PlayerAddSection } from './PlayerAddSection';
|
||||
|
||||
type Props = {
|
||||
boxType: BoxType;
|
||||
player: PlayerFragmentFragment;
|
||||
availableBoxList: BoxType[];
|
||||
setNewBox: (arg0: BoxType) => void;
|
||||
isOwnProfile?: boolean;
|
||||
canEdit?: boolean;
|
||||
removeBox?: (boxType: BoxType) => void;
|
||||
};
|
||||
|
||||
export const PlayerSection: React.FC<Props> = ({
|
||||
boxType,
|
||||
player,
|
||||
availableBoxList,
|
||||
setNewBox,
|
||||
isOwnProfile,
|
||||
canEdit,
|
||||
removeBox,
|
||||
}) => {
|
||||
switch (boxType) {
|
||||
case BoxType.PLAYER_HERO:
|
||||
return (
|
||||
<PlayerHero
|
||||
player={player}
|
||||
isOwnProfile={isOwnProfile}
|
||||
canEdit={canEdit}
|
||||
/>
|
||||
);
|
||||
case BoxType.PLAYER_SKILLS:
|
||||
return (
|
||||
<PlayerSkills
|
||||
player={player}
|
||||
isOwnProfile={isOwnProfile}
|
||||
canEdit={canEdit}
|
||||
onRemoveClick={() => removeBox?.(boxType)}
|
||||
/>
|
||||
);
|
||||
case BoxType.PLAYER_NFT_GALLERY:
|
||||
return (
|
||||
<PlayerGallery
|
||||
player={player}
|
||||
isOwnProfile={isOwnProfile}
|
||||
canEdit={canEdit}
|
||||
onRemoveClick={() => removeBox?.(boxType)}
|
||||
/>
|
||||
);
|
||||
case BoxType.PLAYER_DAO_MEMBERSHIPS:
|
||||
return (
|
||||
<PlayerMemberships
|
||||
player={player}
|
||||
isOwnProfile={isOwnProfile}
|
||||
canEdit={canEdit}
|
||||
onRemoveClick={() => removeBox?.(boxType)}
|
||||
/>
|
||||
);
|
||||
case BoxType.PLAYER_COLOR_DISPOSITION:
|
||||
return (
|
||||
<PlayerColorDisposition
|
||||
player={player}
|
||||
isOwnProfile={isOwnProfile}
|
||||
canEdit={canEdit}
|
||||
onRemoveClick={() => removeBox?.(boxType)}
|
||||
/>
|
||||
);
|
||||
case BoxType.PLAYER_TYPE:
|
||||
return (
|
||||
<PlayerType
|
||||
player={player}
|
||||
isOwnProfile={isOwnProfile}
|
||||
canEdit={canEdit}
|
||||
onRemoveClick={() => removeBox?.(boxType)}
|
||||
/>
|
||||
);
|
||||
case BoxType.PLAYER_ROLES:
|
||||
return (
|
||||
<PlayerRoles
|
||||
player={player}
|
||||
isOwnProfile={isOwnProfile}
|
||||
canEdit={canEdit}
|
||||
onRemoveClick={() => removeBox?.(boxType)}
|
||||
/>
|
||||
);
|
||||
case BoxType.PLAYER_ACHIEVEMENTS:
|
||||
return (
|
||||
<PlayerAchievements
|
||||
player={player}
|
||||
isOwnProfile={isOwnProfile}
|
||||
canEdit={canEdit}
|
||||
onRemoveClick={() => removeBox?.(boxType)}
|
||||
/>
|
||||
);
|
||||
case BoxType.PLAYER_ADD_BOX:
|
||||
return (
|
||||
<PlayerAddSection boxList={availableBoxList} setNewBox={setNewBox} />
|
||||
);
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MetaTag, Wrap, WrapItem } from '@metafam/ds';
|
||||
import { ProfileSection } from 'components/ProfileSection';
|
||||
import {
|
||||
PlayerFragmentFragment,
|
||||
SkillCategory_Enum,
|
||||
@@ -6,18 +7,18 @@ import {
|
||||
import { SkillColors } from 'graphql/types';
|
||||
import { useAnimateProfileChanges } from 'lib/hooks/players';
|
||||
import React, { useState } from 'react';
|
||||
import { BOX_TYPE } from 'utils/boxTypes';
|
||||
|
||||
import { ProfileSection } from '../../ProfileSection';
|
||||
import { BoxType } from 'utils/boxTypes';
|
||||
|
||||
type Props = {
|
||||
player: PlayerFragmentFragment;
|
||||
isOwnProfile?: boolean;
|
||||
canEdit?: boolean;
|
||||
onRemoveClick?: () => void;
|
||||
};
|
||||
export const PlayerSkills: React.FC<Props> = ({
|
||||
player,
|
||||
isOwnProfile,
|
||||
canEdit,
|
||||
onRemoveClick,
|
||||
}) => {
|
||||
const [playerSkills, setPlayerSkills] = useState<
|
||||
@@ -47,7 +48,8 @@ export const PlayerSkills: React.FC<Props> = ({
|
||||
title="Skills"
|
||||
onRemoveClick={onRemoveClick}
|
||||
isOwnProfile={isOwnProfile}
|
||||
boxType={BOX_TYPE.PLAYER.SKILLS}
|
||||
canEdit={canEdit}
|
||||
boxType={BoxType.PLAYER_SKILLS}
|
||||
>
|
||||
<Wrap transition=" opacity 0.4s" opacity={animation === 'fadeIn' ? 1 : 0}>
|
||||
{(playerSkills || []).map(({ id, name, category }) => (
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Text } from '@metafam/ds';
|
||||
import { Player_Type, PlayerFragmentFragment } from 'graphql/autogen/types';
|
||||
import { useAnimateProfileChanges } from 'lib/hooks/players';
|
||||
import React, { useState } from 'react';
|
||||
import { BOX_TYPE } from 'utils/boxTypes';
|
||||
import { BoxType } from 'utils/boxTypes';
|
||||
|
||||
import { FlexContainer } from '../../Container';
|
||||
import { ProfileSection } from '../../ProfileSection';
|
||||
@@ -10,12 +10,14 @@ import { ProfileSection } from '../../ProfileSection';
|
||||
type Props = {
|
||||
player: PlayerFragmentFragment;
|
||||
isOwnProfile?: boolean;
|
||||
canEdit?: boolean;
|
||||
onRemoveClick?: () => void;
|
||||
};
|
||||
|
||||
export const PlayerType: React.FC<Props> = ({
|
||||
player,
|
||||
isOwnProfile,
|
||||
canEdit,
|
||||
onRemoveClick,
|
||||
}) => {
|
||||
const [playerType, setPlayerType] = useState<Player_Type | null>();
|
||||
@@ -28,7 +30,8 @@ export const PlayerType: React.FC<Props> = ({
|
||||
title="Player type"
|
||||
onRemoveClick={onRemoveClick}
|
||||
isOwnProfile={isOwnProfile}
|
||||
boxType={BOX_TYPE.PLAYER.TYPE}
|
||||
canEdit={canEdit}
|
||||
boxType={BoxType.PLAYER_TYPE}
|
||||
>
|
||||
{playerType && (
|
||||
<FlexContainer
|
||||
|
||||
@@ -1,80 +1,138 @@
|
||||
export const tokenId = 'metagame';
|
||||
export const apiUrl = 'https://api.coingecko.com/api/v3/';
|
||||
export const tokenQuery = '?localization=false&tickers=true&market_data=true';
|
||||
export const chartQuery =
|
||||
'/market_chart?vs_currency=usd&days=30&interval=daily';
|
||||
export const podcastRSSURL = 'https://anchor.fm/s/57a641c/podcast/rss';
|
||||
import { Layout, Layouts } from 'react-grid-layout';
|
||||
import { BoxType } from 'utils/boxTypes';
|
||||
|
||||
export const gridData = [
|
||||
{ i: 'hero', x: 0, y: 0, w: 4, h: 6, static: true, minW: 4, maxW: 4 },
|
||||
{ i: 'colors', x: 4, y: 1, w: 4, h: 2, minH: 2, maxH: 2, minW: 4, maxW: 4 },
|
||||
{ i: 'skills', x: 8, y: 1, w: 4, h: 2.5, minH: 2.5, minW: 4, maxW: 4 },
|
||||
{ i: 'type', x: 4, y: 3, w: 4, h: 2, minH: 2, minW: 4, maxW: 4 },
|
||||
{ i: 'memberships', x: 8, y: 4, w: 4, h: 4, minW: 4, maxW: 4 },
|
||||
export const getBoxLayoutItemDefaults = (boxId: BoxType): Layout => {
|
||||
switch (boxId) {
|
||||
case BoxType.PLAYER_HERO:
|
||||
return {
|
||||
i: BoxType.PLAYER_HERO,
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 1,
|
||||
h: 14,
|
||||
static: true,
|
||||
maxW: 1,
|
||||
};
|
||||
case BoxType.PLAYER_SKILLS:
|
||||
return { i: BoxType.PLAYER_SKILLS, x: 0, y: 0, w: 1, h: 7, maxW: 1 };
|
||||
case BoxType.PLAYER_NFT_GALLERY:
|
||||
return {
|
||||
i: BoxType.PLAYER_NFT_GALLERY,
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 1,
|
||||
h: 10,
|
||||
maxW: 1,
|
||||
};
|
||||
case BoxType.PLAYER_DAO_MEMBERSHIPS:
|
||||
return {
|
||||
i: BoxType.PLAYER_DAO_MEMBERSHIPS,
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 1,
|
||||
h: 9,
|
||||
maxW: 1,
|
||||
};
|
||||
case BoxType.PLAYER_ACHIEVEMENTS:
|
||||
return {
|
||||
i: BoxType.PLAYER_ACHIEVEMENTS,
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 1,
|
||||
h: 4,
|
||||
maxW: 1,
|
||||
};
|
||||
case BoxType.PLAYER_TYPE:
|
||||
return { i: BoxType.PLAYER_TYPE, x: 0, y: 0, w: 1, h: 6, maxW: 1 };
|
||||
case BoxType.PLAYER_COLOR_DISPOSITION:
|
||||
return {
|
||||
i: BoxType.PLAYER_COLOR_DISPOSITION,
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 1,
|
||||
h: 5,
|
||||
maxW: 1,
|
||||
};
|
||||
case BoxType.PLAYER_ROLES:
|
||||
return { i: BoxType.PLAYER_ROLES, x: 0, y: 0, w: 1, h: 3, maxW: 1 };
|
||||
case BoxType.PLAYER_ADD_BOX:
|
||||
return {
|
||||
i: BoxType.PLAYER_ADD_BOX,
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 1,
|
||||
h: 3,
|
||||
maxW: 1,
|
||||
isResizable: false,
|
||||
isDraggable: false,
|
||||
};
|
||||
default:
|
||||
return { i: '', x: 0, y: 0, w: 1, h: 1, maxW: 1 };
|
||||
}
|
||||
};
|
||||
|
||||
const gridDataLg: Layout[] = [
|
||||
{ ...getBoxLayoutItemDefaults(BoxType.PLAYER_HERO), x: 0, y: -3 },
|
||||
{ ...getBoxLayoutItemDefaults(BoxType.PLAYER_SKILLS), x: 1, y: 0 },
|
||||
{
|
||||
...getBoxLayoutItemDefaults(BoxType.PLAYER_COLOR_DISPOSITION),
|
||||
x: 1,
|
||||
y: 7,
|
||||
},
|
||||
{ ...getBoxLayoutItemDefaults(BoxType.PLAYER_TYPE), x: 1, y: 12 },
|
||||
{ ...getBoxLayoutItemDefaults(BoxType.PLAYER_NFT_GALLERY), x: 2, y: 0 },
|
||||
{ ...getBoxLayoutItemDefaults(BoxType.PLAYER_DAO_MEMBERSHIPS), x: 2, y: 10 },
|
||||
];
|
||||
|
||||
export const gridDataMd = [
|
||||
{ i: 'hero', x: 0, y: 0, w: 6, h: 4, static: true },
|
||||
{ i: 'colors', x: 6, y: 0, w: 6, h: 2, minH: 2 },
|
||||
{ i: 'skills', x: 6, y: 1, w: 6, h: 2, minH: 2 },
|
||||
{ i: 'type', x: 0, y: 4, w: 6, h: 4 },
|
||||
{ i: 'memberships', x: 6, y: 4, w: 6, h: 4 },
|
||||
const gridDataMd: Layout[] = [
|
||||
{ ...getBoxLayoutItemDefaults(BoxType.PLAYER_HERO), x: 0, y: -3 },
|
||||
{
|
||||
...getBoxLayoutItemDefaults(BoxType.PLAYER_COLOR_DISPOSITION),
|
||||
x: 1,
|
||||
y: 0,
|
||||
},
|
||||
{ ...getBoxLayoutItemDefaults(BoxType.PLAYER_SKILLS), x: 1, y: 5 },
|
||||
{ ...getBoxLayoutItemDefaults(BoxType.PLAYER_TYPE), x: 1, y: 6 },
|
||||
{ ...getBoxLayoutItemDefaults(BoxType.PLAYER_NFT_GALLERY), x: 0, y: 7 },
|
||||
{ ...getBoxLayoutItemDefaults(BoxType.PLAYER_DAO_MEMBERSHIPS), x: 0, y: 16 },
|
||||
];
|
||||
|
||||
export const gridDataSm = [
|
||||
{ i: 'hero', x: 0, y: 4, w: 4, h: 3, static: true },
|
||||
{ i: 'colors', x: 0, y: 0, w: 4, h: 2 },
|
||||
{ i: 'skills', x: 0, y: 2, w: 4, h: 2 },
|
||||
{ i: 'type', x: 0, y: 7, w: 4, h: 4 },
|
||||
{ i: 'memberships', x: 0, y: 11, w: 4, h: 4 },
|
||||
const gridDataSm: Layout[] = [
|
||||
{ ...getBoxLayoutItemDefaults(BoxType.PLAYER_HERO), x: 0, y: 0 },
|
||||
{
|
||||
...getBoxLayoutItemDefaults(BoxType.PLAYER_COLOR_DISPOSITION),
|
||||
x: 0,
|
||||
y: 10,
|
||||
},
|
||||
{ ...getBoxLayoutItemDefaults(BoxType.PLAYER_SKILLS), x: 0, y: 15 },
|
||||
{ ...getBoxLayoutItemDefaults(BoxType.PLAYER_TYPE), x: 0, y: 19 },
|
||||
{ ...getBoxLayoutItemDefaults(BoxType.PLAYER_NFT_GALLERY), x: 0, y: 7 },
|
||||
{ ...getBoxLayoutItemDefaults(BoxType.PLAYER_DAO_MEMBERSHIPS), x: 0, y: 16 },
|
||||
];
|
||||
|
||||
export const initLayouts = {
|
||||
lg: gridData,
|
||||
export const initLayouts: Layouts = {
|
||||
lg: gridDataLg,
|
||||
md: gridDataMd,
|
||||
sm: gridDataSm,
|
||||
xs: gridDataSm,
|
||||
};
|
||||
|
||||
export const gridConfig = {
|
||||
wrapper: (editable: boolean): Record<string, unknown> => ({
|
||||
'.gridItem': {
|
||||
boxShadow: editable
|
||||
? '0 0 10px rgba(0,0,0,0.4)'
|
||||
? '0 0 10px rgba(0,0,0,0.6)'
|
||||
: '0 0 0 rgba(0,0,0,0.4)',
|
||||
borderTopRadius: 'lg',
|
||||
height: 'unset',
|
||||
bg: editable ? 'blackAlpha.600' : 'blackAlpha.300',
|
||||
overflow: 'hidden',
|
||||
borderRadius: 'lg',
|
||||
transition: 'boxShadow 0.2s 0.3s ease',
|
||||
p: {
|
||||
fontSize: 'md',
|
||||
pb: 2,
|
||||
mr: 'auto',
|
||||
},
|
||||
ul: {
|
||||
fontSize: 'sm',
|
||||
pb: 2,
|
||||
pl: 6,
|
||||
},
|
||||
'& > div': {
|
||||
bg: editable ? 'blackAlpha.500' : 'blackAlpha.300',
|
||||
backdropFilter: 'blur(10px)',
|
||||
borderBottomRadius: 'lg',
|
||||
overflow: 'hidden',
|
||||
h: '100%',
|
||||
transition: 'bg 0.2s 0.3s ease',
|
||||
},
|
||||
'.container': {
|
||||
overflowY: 'auto',
|
||||
overflowX: 'hidden',
|
||||
height: '100%',
|
||||
},
|
||||
h2: {
|
||||
fontFamily: 'exo2',
|
||||
fontSize: 'lg',
|
||||
fontWeight: '700',
|
||||
textAlign: 'left',
|
||||
textTransform: 'uppercase',
|
||||
h: '100%',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
},
|
||||
'.react-grid-placeholder': {
|
||||
@@ -82,113 +140,18 @@ export const gridConfig = {
|
||||
boxShadow: '0 0 0 solid rgba(0, 0, 0, 0.8)',
|
||||
borderRadius: 'lg',
|
||||
},
|
||||
'.react-resizable-handle': {
|
||||
width: '1rem',
|
||||
height: '1rem',
|
||||
background: 'none',
|
||||
borderStyle: 'solid',
|
||||
borderColor: 'pinkShadeOne',
|
||||
borderWidth: '0 2px 2px 0',
|
||||
borderRadius: '0 0 6px 0',
|
||||
margin: '2px',
|
||||
},
|
||||
'.react-resizable-handle::after': {
|
||||
border: 'none',
|
||||
},
|
||||
}),
|
||||
memberships: {
|
||||
'.player': {
|
||||
transition: 'all 0.3s ease',
|
||||
'&__score': {
|
||||
fontWeight: '400',
|
||||
},
|
||||
'&:hover': {
|
||||
boxShadow: '0 0 8px rgba(0,0,0,0.3)',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
},
|
||||
'&.container': {
|
||||
'&__xxs': {
|
||||
'.player': {
|
||||
px: 3,
|
||||
py: 2,
|
||||
fontSize: 'sm',
|
||||
justifyContent: 'center',
|
||||
opacity: 1,
|
||||
'&__position, &__name, &__score': {
|
||||
visibility: 'hidden',
|
||||
maxW: 0,
|
||||
maxH: 0,
|
||||
mr: 0,
|
||||
},
|
||||
'&__avatar': {
|
||||
mr: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
'&__xs': {
|
||||
'.player': {
|
||||
px: 3,
|
||||
fontSize: 'sm',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
skills: {
|
||||
'&.container': {
|
||||
'&__xxs': {
|
||||
'.chakra-stack': {
|
||||
flexFlow: 'column',
|
||||
alignItems: 'flex-start',
|
||||
p: {
|
||||
mx: 0,
|
||||
},
|
||||
},
|
||||
'.chakra-stat': {
|
||||
'&__group': {
|
||||
mt: 3,
|
||||
mb: 0,
|
||||
},
|
||||
'&__label': {
|
||||
fontSize: 'xs',
|
||||
},
|
||||
'&__number': {
|
||||
fontSize: 'lg',
|
||||
},
|
||||
'&:nth-of-type(3n)': {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
'.infoLink': {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
'&__xs': {
|
||||
'.chakra-stat': {
|
||||
'&__label': {
|
||||
fontSize: 'xs',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
colors: {
|
||||
'&.container': {
|
||||
'&__xxs': {
|
||||
'.chakra-stack': {
|
||||
flexFlow: 'column wrap',
|
||||
alignItems: 'flex-start',
|
||||
p: {
|
||||
mx: 0,
|
||||
},
|
||||
},
|
||||
'.chakra-stat': {
|
||||
flex: '0 1 100%',
|
||||
'&__group': {
|
||||
mt: 1,
|
||||
},
|
||||
'&__label': {
|
||||
fontSize: 'xs',
|
||||
},
|
||||
'&:nth-of-type(3), &:last-of-type': {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
'&__xs': {
|
||||
'.chakra-stat': {
|
||||
'&__label': {
|
||||
fontSize: 'xs',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -18,14 +18,14 @@ import { SetupPlayerType } from 'components/Setup/SetupPlayerType';
|
||||
import { SetupSkills } from 'components/Setup/SetupSkills';
|
||||
import React from 'react';
|
||||
import { FaTimes } from 'react-icons/fa';
|
||||
import { BOX_TYPE } from 'utils/boxTypes';
|
||||
import { BoxType } from 'utils/boxTypes';
|
||||
|
||||
export type ProfileSectionProps = {
|
||||
children?: React.ReactNode;
|
||||
onRemoveClick?: () => void;
|
||||
isOwnProfile?: boolean;
|
||||
canEdit?: boolean;
|
||||
boxType?: string;
|
||||
boxType?: BoxType;
|
||||
title?: string;
|
||||
};
|
||||
|
||||
@@ -54,7 +54,10 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
|
||||
>
|
||||
{title.toUpperCase()}
|
||||
</Text>
|
||||
{isOwnProfile ? (
|
||||
{isOwnProfile &&
|
||||
!canEdit &&
|
||||
boxType &&
|
||||
isBoxDataEditable(boxType) ? (
|
||||
<IconButton
|
||||
aria-label="Edit Profile Info"
|
||||
size="lg"
|
||||
@@ -74,12 +77,24 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
|
||||
isRound
|
||||
/>
|
||||
) : null}
|
||||
{canEdit ? (
|
||||
<FaTimes
|
||||
color="blueLight"
|
||||
opacity="0.4"
|
||||
cursor="pointer"
|
||||
{canEdit && boxType && boxType !== BoxType.PLAYER_HERO ? (
|
||||
<IconButton
|
||||
aria-label="Edit Profile Info"
|
||||
size="lg"
|
||||
background="transparent"
|
||||
color="pinkShadeOne"
|
||||
icon={<FaTimes />}
|
||||
_hover={{ color: 'white' }}
|
||||
onClick={onRemoveClick}
|
||||
_focus={{
|
||||
boxShadow: 'none',
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
_active={{
|
||||
transform: 'scale(0.8)',
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
isRound
|
||||
/>
|
||||
) : null}
|
||||
</HStack>
|
||||
@@ -94,8 +109,20 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
|
||||
css={{ backdropFilter: 'blur(8px)' }}
|
||||
w="100%"
|
||||
h="100%"
|
||||
pos="relative"
|
||||
pointerEvents={canEdit ? 'none' : 'initial'}
|
||||
>
|
||||
{children}
|
||||
{canEdit && (
|
||||
<Box
|
||||
w="100%"
|
||||
h="100%"
|
||||
bg="purpleTag50"
|
||||
pos="absolute"
|
||||
top="0"
|
||||
left="0"
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
{boxType && (
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
@@ -131,16 +158,23 @@ export const ProfileSection: React.FC<ProfileSectionProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
const isBoxDataEditable = (boxType: BoxType) =>
|
||||
[
|
||||
BoxType.PLAYER_TYPE,
|
||||
BoxType.PLAYER_COLOR_DISPOSITION,
|
||||
BoxType.PLAYER_SKILLS,
|
||||
].includes(boxType);
|
||||
|
||||
const getEditSectionBox = (
|
||||
boxType: string,
|
||||
onClose: () => void,
|
||||
): React.ReactNode => {
|
||||
switch (boxType) {
|
||||
case BOX_TYPE.PLAYER.TYPE:
|
||||
case BoxType.PLAYER_TYPE:
|
||||
return <SetupPlayerType isEdit onClose={onClose} />;
|
||||
case BOX_TYPE.PLAYER.COLOR_DISPOSITION:
|
||||
case BoxType.PLAYER_COLOR_DISPOSITION:
|
||||
return <SetupPersonalityType isEdit onClose={onClose} />;
|
||||
case BOX_TYPE.PLAYER.SKILLS:
|
||||
case BoxType.PLAYER_SKILLS:
|
||||
return <SetupSkills isEdit onClose={onClose} />;
|
||||
default:
|
||||
return <></>;
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
import Error from 'next/error';
|
||||
import { useRouter } from 'next/router';
|
||||
import React from 'react';
|
||||
import { BOX_TYPE } from 'utils/boxTypes';
|
||||
import { BoxType } from 'utils/boxTypes';
|
||||
import { getGuildCoverImageFull } from 'utils/playerHelpers';
|
||||
|
||||
import { PageContainer } from '../../components/Container';
|
||||
@@ -26,14 +26,14 @@ const GuildPage: React.FC<Props> = ({ guild }) => {
|
||||
const router = useRouter();
|
||||
|
||||
// Hidden until implemented
|
||||
// BOX_TYPE.GUILD.SKILLS,
|
||||
// BOX_TYPE.GUILD.STATS,
|
||||
// BOX_TYPE.GUILD.QUESTS,
|
||||
// BOX_TYPE.GUILD.GALLERY,
|
||||
// BoxType.GUILD_SKILLS,
|
||||
// BoxType.GUILD_STATS,
|
||||
// BoxType.GUILD_QUESTS,
|
||||
// BoxType.GUILD_GALLERY,
|
||||
|
||||
const boxes = [
|
||||
[BOX_TYPE.GUILD.PLAYERS],
|
||||
[BOX_TYPE.GUILD.ANNOUNCEMENTS, BOX_TYPE.GUILD.LINKS],
|
||||
[BoxType.GUILD_PLAYERS],
|
||||
[BoxType.GUILD_ANNOUNCEMENTS, BoxType.GUILD_LINKS],
|
||||
];
|
||||
|
||||
if (router.isFallback) {
|
||||
@@ -46,11 +46,11 @@ const GuildPage: React.FC<Props> = ({ guild }) => {
|
||||
|
||||
const getBox = (name: string): React.ReactNode => {
|
||||
switch (name) {
|
||||
case BOX_TYPE.GUILD.PLAYERS:
|
||||
case BoxType.GUILD_PLAYERS:
|
||||
return <GuildPlayers guildId={guild.id} guildname={guild.guildname} />;
|
||||
case BOX_TYPE.GUILD.LINKS:
|
||||
case BoxType.GUILD_LINKS:
|
||||
return <GuildLinks guild={guild} />;
|
||||
case BOX_TYPE.GUILD.ANNOUNCEMENTS:
|
||||
case BoxType.GUILD_ANNOUNCEMENTS:
|
||||
return (
|
||||
<ProfileSection title="Announcements">
|
||||
<p>No announcements.</p>
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import Error from 'next/error';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { BOX_TYPE } from 'utils/boxTypes';
|
||||
import { BoxType } from 'utils/boxTypes';
|
||||
import {
|
||||
getPlayerCoverImageFull,
|
||||
getPlayerDescription,
|
||||
@@ -36,7 +36,7 @@ const PlayerPage: React.FC<Props> = ({ player }) => {
|
||||
const router = useRouter();
|
||||
|
||||
const [boxAvailableList, setBoxAvailableList] = useState<string[]>([]);
|
||||
const [canEdit] = useState(true);
|
||||
const [canEdit] = useState(false);
|
||||
const [, invalidateCache] = useInsertCacheInvalidationMutation();
|
||||
const { user, fetching } = useUser();
|
||||
const { connected } = useWeb3();
|
||||
@@ -44,15 +44,11 @@ const PlayerPage: React.FC<Props> = ({ player }) => {
|
||||
const [fakeData, setFakeData] = useState([
|
||||
[],
|
||||
[
|
||||
BOX_TYPE.PLAYER.SKILLS,
|
||||
BOX_TYPE.PLAYER.COLOR_DISPOSITION,
|
||||
BOX_TYPE.PLAYER.TYPE,
|
||||
],
|
||||
[
|
||||
// BOX_TYPE.PLAYER.ROLES,
|
||||
BOX_TYPE.PLAYER.GALLERY,
|
||||
BOX_TYPE.PLAYER.MEMBERSHIPS,
|
||||
BoxType.PLAYER_SKILLS,
|
||||
BoxType.PLAYER_COLOR_DISPOSITION,
|
||||
BoxType.PLAYER_TYPE,
|
||||
],
|
||||
[BoxType.PLAYER_NFT_GALLERY, BoxType.PLAYER_DAO_MEMBERSHIPS],
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -84,7 +80,7 @@ const PlayerPage: React.FC<Props> = ({ player }) => {
|
||||
const addBox = (column: number, name: string) => {
|
||||
setBoxAvailableList(boxAvailableList.filter((box) => box !== name));
|
||||
const updatedFakeData = [...fakeData];
|
||||
updatedFakeData[column].push(name);
|
||||
updatedFakeData[column].push(name as BoxType);
|
||||
setFakeData(updatedFakeData);
|
||||
};
|
||||
|
||||
@@ -100,7 +96,7 @@ const PlayerPage: React.FC<Props> = ({ player }) => {
|
||||
const getBox = (column: number, name: string): React.ReactNode => {
|
||||
const person = isOwnProfile ? user?.player : player;
|
||||
switch (name) {
|
||||
case BOX_TYPE.PLAYER.SKILLS:
|
||||
case BoxType.PLAYER_SKILLS:
|
||||
return (
|
||||
<PlayerSkills
|
||||
player={person}
|
||||
@@ -108,21 +104,21 @@ const PlayerPage: React.FC<Props> = ({ player }) => {
|
||||
onRemoveClick={() => removeBox(column, name)}
|
||||
/>
|
||||
);
|
||||
case BOX_TYPE.PLAYER.GALLERY:
|
||||
case BoxType.PLAYER_NFT_GALLERY:
|
||||
return (
|
||||
<PlayerGallery
|
||||
player={person}
|
||||
onRemoveClick={() => removeBox(column, name)}
|
||||
/>
|
||||
);
|
||||
case BOX_TYPE.PLAYER.MEMBERSHIPS:
|
||||
case BoxType.PLAYER_DAO_MEMBERSHIPS:
|
||||
return (
|
||||
<PlayerMemberships
|
||||
player={person}
|
||||
onRemoveClick={() => removeBox(column, name)}
|
||||
/>
|
||||
);
|
||||
case BOX_TYPE.PLAYER.COLOR_DISPOSITION:
|
||||
case BoxType.PLAYER_COLOR_DISPOSITION:
|
||||
return (
|
||||
<PlayerColorDisposition
|
||||
player={person}
|
||||
@@ -130,7 +126,7 @@ const PlayerPage: React.FC<Props> = ({ player }) => {
|
||||
onRemoveClick={() => removeBox(column, name)}
|
||||
/>
|
||||
);
|
||||
case BOX_TYPE.PLAYER.TYPE:
|
||||
case BoxType.PLAYER_TYPE:
|
||||
return (
|
||||
<PlayerType
|
||||
player={person}
|
||||
@@ -138,16 +134,19 @@ const PlayerPage: React.FC<Props> = ({ player }) => {
|
||||
onRemoveClick={() => removeBox(column, name)}
|
||||
/>
|
||||
);
|
||||
case BOX_TYPE.PLAYER.ROLES:
|
||||
case BoxType.PLAYER_ROLES:
|
||||
return (
|
||||
<PlayerRoles
|
||||
player={person}
|
||||
onRemoveClick={() => removeBox(column, name)}
|
||||
/>
|
||||
);
|
||||
case BOX_TYPE.PLAYER.ACHIEVEMENTS:
|
||||
case BoxType.PLAYER_ACHIEVEMENTS:
|
||||
return (
|
||||
<PlayerAchievements onRemoveClick={() => removeBox(column, name)} />
|
||||
<PlayerAchievements
|
||||
player={person}
|
||||
onRemoveClick={() => removeBox(column, name)}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return <></>;
|
||||
@@ -226,7 +225,7 @@ const PlayerPage: React.FC<Props> = ({ player }) => {
|
||||
))}
|
||||
{canEdit ? (
|
||||
<PlayerAddSection
|
||||
boxList={boxAvailableList}
|
||||
boxList={boxAvailableList as BoxType[]}
|
||||
setNewBox={(name) => addBox(0, name)}
|
||||
mb={6}
|
||||
display={{ base: 'none', md: 'flex' }}
|
||||
@@ -256,7 +255,7 @@ const PlayerPage: React.FC<Props> = ({ player }) => {
|
||||
))}
|
||||
{canEdit ? (
|
||||
<PlayerAddSection
|
||||
boxList={boxAvailableList}
|
||||
boxList={boxAvailableList as BoxType[]}
|
||||
setNewBox={(name) => addBox(1, name)}
|
||||
mb={6}
|
||||
display={{ base: 'none', lg: 'flex' }}
|
||||
@@ -274,7 +273,7 @@ const PlayerPage: React.FC<Props> = ({ player }) => {
|
||||
))}
|
||||
{canEdit ? (
|
||||
<PlayerAddSection
|
||||
boxList={boxAvailableList}
|
||||
boxList={boxAvailableList as BoxType[]}
|
||||
setNewBox={(name) => addBox(2, name)}
|
||||
mb={6}
|
||||
/>
|
||||
|
||||
@@ -7,19 +7,16 @@ import {
|
||||
DeleteIcon,
|
||||
EditIcon,
|
||||
Flex,
|
||||
GridItem,
|
||||
MetaButton,
|
||||
ResponsiveText,
|
||||
} from '@metafam/ds';
|
||||
import { PageContainer } from 'components/Container';
|
||||
import { gridConfig, initLayouts } from 'components/Player/Section/config';
|
||||
// import { PlayerAchievements } from 'components/Player/Section/PlayerAchievements';
|
||||
import { PlayerColorDisposition } from 'components/Player/Section/PlayerColorDisposition';
|
||||
// import { PlayerGallery } from 'components/Player/Section/PlayerGallery';
|
||||
import { PlayerHero } from 'components/Player/Section/PlayerHero';
|
||||
import { PlayerMemberships } from 'components/Player/Section/PlayerMemberships';
|
||||
// import { PlayerRoles } from 'components/Player/Section/PlayerRoles';
|
||||
import { PlayerSkills } from 'components/Player/Section/PlayerSkills';
|
||||
import { PlayerType } from 'components/Player/Section/PlayerType';
|
||||
import {
|
||||
getBoxLayoutItemDefaults,
|
||||
gridConfig,
|
||||
initLayouts,
|
||||
} from 'components/Player/Section/config';
|
||||
import { PlayerSection } from 'components/Player/Section/PlayerSection';
|
||||
import { HeadComponent } from 'components/Seo';
|
||||
import { useInsertCacheInvalidationMutation } from 'graphql/autogen/types';
|
||||
import { getPlayer } from 'graphql/getPlayer';
|
||||
@@ -30,8 +27,9 @@ import {
|
||||
GetStaticPropsContext,
|
||||
InferGetStaticPropsType,
|
||||
} from 'next';
|
||||
import { ReactElement, useEffect, useState } from 'react';
|
||||
import { Layout, Layouts, Responsive, WidthProvider } from 'react-grid-layout';
|
||||
import { ReactElement, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Layouts, Responsive, WidthProvider } from 'react-grid-layout';
|
||||
import { BoxType } from 'utils/boxTypes';
|
||||
import {
|
||||
getPlayerCoverImageFull,
|
||||
getPlayerDescription,
|
||||
@@ -53,8 +51,6 @@ export interface ContainerQueries {
|
||||
|
||||
const ResponsiveGridLayout = WidthProvider(Responsive);
|
||||
|
||||
export const originalLayouts = initLayouts;
|
||||
|
||||
type Props = InferGetStaticPropsType<typeof getStaticProps>;
|
||||
|
||||
const PlayerPage: React.FC<Props> = ({ player }): ReactElement => (
|
||||
@@ -77,7 +73,8 @@ const PlayerPage: React.FC<Props> = ({ player }): ReactElement => (
|
||||
w="100%"
|
||||
h="100%"
|
||||
minH="100vh"
|
||||
p={[4, 8, 12]}
|
||||
p="4"
|
||||
pt="8"
|
||||
direction="column"
|
||||
align="center"
|
||||
>
|
||||
@@ -88,9 +85,64 @@ const PlayerPage: React.FC<Props> = ({ player }): ReactElement => (
|
||||
|
||||
export default PlayerPage;
|
||||
|
||||
type CurrentLayoutType = {
|
||||
layout: Layout[];
|
||||
layouts: Layouts;
|
||||
const makeLayouts = (editable: boolean, layouts: Layouts) => {
|
||||
const newLayouts: Layouts = {};
|
||||
Object.keys(layouts).map((key) => {
|
||||
newLayouts[key] = layouts[key].map((item) =>
|
||||
item.i === 'hero' ? { ...item, isResizable: editable } : item,
|
||||
);
|
||||
return key;
|
||||
});
|
||||
return newLayouts;
|
||||
};
|
||||
|
||||
const ALL_BOXES = [
|
||||
BoxType.PLAYER_HERO,
|
||||
BoxType.PLAYER_SKILLS,
|
||||
BoxType.PLAYER_COLOR_DISPOSITION,
|
||||
BoxType.PLAYER_TYPE,
|
||||
BoxType.PLAYER_NFT_GALLERY,
|
||||
BoxType.PLAYER_DAO_MEMBERSHIPS,
|
||||
BoxType.PLAYER_ROLES,
|
||||
];
|
||||
|
||||
const DEFAULT_BOXES = [
|
||||
BoxType.PLAYER_HERO,
|
||||
BoxType.PLAYER_SKILLS,
|
||||
BoxType.PLAYER_COLOR_DISPOSITION,
|
||||
BoxType.PLAYER_TYPE,
|
||||
BoxType.PLAYER_NFT_GALLERY,
|
||||
BoxType.PLAYER_DAO_MEMBERSHIPS,
|
||||
];
|
||||
|
||||
const removeBoxFromLayouts = (
|
||||
boxType: BoxType,
|
||||
pastLayouts: Layouts,
|
||||
): Layouts => {
|
||||
const layouts = { ...pastLayouts };
|
||||
Object.keys(layouts).map((key) => {
|
||||
layouts[key] = layouts[key].filter(
|
||||
(item) => (item.i as BoxType) !== boxType,
|
||||
);
|
||||
return key;
|
||||
});
|
||||
return layouts;
|
||||
};
|
||||
|
||||
const addBoxToLayouts = (boxType: BoxType, pastLayouts: Layouts): Layouts => {
|
||||
const layouts = { ...pastLayouts };
|
||||
Object.keys(layouts).map((key) => {
|
||||
const heroItem = layouts[key].find(
|
||||
(item) => item.i === BoxType.PLAYER_HERO,
|
||||
);
|
||||
layouts[key].push({
|
||||
...getBoxLayoutItemDefaults(boxType),
|
||||
x: 0,
|
||||
y: heroItem ? heroItem.y + heroItem.h : 0,
|
||||
});
|
||||
return key;
|
||||
});
|
||||
return layouts;
|
||||
};
|
||||
|
||||
export const Grid: React.FC<Props> = ({ player }): ReactElement => {
|
||||
@@ -109,18 +161,31 @@ export const Grid: React.FC<Props> = ({ player }): ReactElement => {
|
||||
invalidateCache({ playerId: player.id });
|
||||
}
|
||||
}, [player, invalidateCache]);
|
||||
const [gridLayouts, setGridLayouts] = useState(
|
||||
JSON.parse(JSON.stringify(originalLayouts)), // TODO: persist in hasura
|
||||
const [savedLayouts, setSavedLayouts] = useState<Layouts>(
|
||||
JSON.parse(JSON.stringify(initLayouts)), // TODO: persist in hasura
|
||||
);
|
||||
const [currentLayouts, setCurrentLayouts] = useState<Layouts>(
|
||||
JSON.parse(JSON.stringify(initLayouts)),
|
||||
);
|
||||
const [ownLayout, setOwnLayout] = useState(false);
|
||||
const [changed, setChanged] = useState(false);
|
||||
const [current, setCurrent] = useState<CurrentLayoutType>({
|
||||
layout: [],
|
||||
layouts: {},
|
||||
});
|
||||
|
||||
const [editable, setEditable] = useState(false);
|
||||
|
||||
const toggleEditLayout = () => setEditable(!editable);
|
||||
const toggleEditLayout = useCallback(() => {
|
||||
if (editable) {
|
||||
const layouts = removeBoxFromLayouts(
|
||||
BoxType.PLAYER_ADD_BOX,
|
||||
currentLayouts,
|
||||
);
|
||||
setCurrentLayouts(layouts);
|
||||
setSavedLayouts(layouts);
|
||||
} else {
|
||||
const layouts = addBoxToLayouts(BoxType.PLAYER_ADD_BOX, currentLayouts);
|
||||
setCurrentLayouts(layouts);
|
||||
}
|
||||
setEditable(!editable);
|
||||
setChanged(false);
|
||||
}, [editable, currentLayouts]);
|
||||
|
||||
const toggleScrollLock = () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
@@ -130,91 +195,137 @@ export const Grid: React.FC<Props> = ({ player }): ReactElement => {
|
||||
return null;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
function handleLayoutChange(layouts: Layouts) {
|
||||
const parsedLayouts = JSON.parse(JSON.stringify(layouts));
|
||||
// TODO: save parsedLayouts to hasura
|
||||
setGridLayouts(parsedLayouts);
|
||||
}
|
||||
if (changed) handleLayoutChange(current.layouts);
|
||||
}, [current, changed]);
|
||||
const handleLayoutChange = useCallback((layouts: Layouts) => {
|
||||
const parsedLayouts = JSON.parse(JSON.stringify(layouts));
|
||||
setCurrentLayouts(parsedLayouts);
|
||||
setChanged(true);
|
||||
}, []);
|
||||
|
||||
function handleReset() {
|
||||
setGridLayouts(JSON.parse(JSON.stringify(initLayouts)));
|
||||
const handleReset = useCallback(() => {
|
||||
const parsedLayouts = JSON.parse(JSON.stringify(savedLayouts));
|
||||
const layouts = addBoxToLayouts(BoxType.PLAYER_ADD_BOX, parsedLayouts);
|
||||
setCurrentLayouts(layouts);
|
||||
|
||||
setTimeout(() => {
|
||||
setOwnLayout(false);
|
||||
setChanged(false);
|
||||
// resetLayouts();
|
||||
}, 300);
|
||||
}
|
||||
}, [savedLayouts]);
|
||||
|
||||
const wrapperSX = useMemo(() => gridConfig.wrapper(editable), [editable]);
|
||||
|
||||
const displayLayouts = useMemo(() => makeLayouts(editable, currentLayouts), [
|
||||
editable,
|
||||
currentLayouts,
|
||||
]);
|
||||
|
||||
const onRemoveBox = useCallback(
|
||||
(boxType: BoxType): void => {
|
||||
const layouts = removeBoxFromLayouts(boxType, currentLayouts);
|
||||
setCurrentLayouts(layouts);
|
||||
setChanged(true);
|
||||
},
|
||||
[currentLayouts],
|
||||
);
|
||||
|
||||
const onAddBox = useCallback(
|
||||
(boxType: BoxType): void => {
|
||||
const layouts = addBoxToLayouts(boxType, currentLayouts);
|
||||
setCurrentLayouts(layouts);
|
||||
setChanged(true);
|
||||
},
|
||||
[currentLayouts],
|
||||
);
|
||||
|
||||
const boxes = useMemo(() => {
|
||||
const boxIds = new Set<BoxType>();
|
||||
Object.keys(currentLayouts).map((key) => {
|
||||
const layout = currentLayouts[key];
|
||||
layout.forEach((item) => boxIds.add(item.i as BoxType));
|
||||
return key;
|
||||
});
|
||||
const boxIdArray = Array.from(boxIds);
|
||||
return boxIdArray.length > 0 ? boxIdArray : DEFAULT_BOXES;
|
||||
}, [currentLayouts]);
|
||||
|
||||
const availableBoxList = useMemo(
|
||||
() => ALL_BOXES.filter((box) => !boxes.includes(box)),
|
||||
[boxes],
|
||||
);
|
||||
|
||||
return (
|
||||
<Box
|
||||
className="gridWrapper"
|
||||
width="100%"
|
||||
height="100%"
|
||||
sx={gridConfig.wrapper(editable)}
|
||||
sx={wrapperSX}
|
||||
maxW="96rem"
|
||||
mb="12rem"
|
||||
pt={isOwnProfile ? '0rem' : '10rem'}
|
||||
>
|
||||
<ButtonGroup
|
||||
w="100%"
|
||||
justifyContent={'end'}
|
||||
variant="ghost"
|
||||
zIndex={10}
|
||||
isAttached
|
||||
>
|
||||
{(changed || ownLayout) && editable && (
|
||||
{isOwnProfile && (
|
||||
<ButtonGroup
|
||||
w="100%"
|
||||
px="2rem"
|
||||
justifyContent={'end'}
|
||||
variant="ghost"
|
||||
zIndex={10}
|
||||
isAttached
|
||||
mb="7rem"
|
||||
>
|
||||
{changed && editable && (
|
||||
<MetaButton
|
||||
aria-label="Edit layout"
|
||||
colorScheme="purple"
|
||||
_hover={{ background: 'purple.600' }}
|
||||
textTransform="uppercase"
|
||||
px={12}
|
||||
letterSpacing="0.1em"
|
||||
size="lg"
|
||||
fontSize="sm"
|
||||
onClick={handleReset}
|
||||
leftIcon={<DeleteIcon />}
|
||||
>
|
||||
Reset
|
||||
</MetaButton>
|
||||
)}
|
||||
<MetaButton
|
||||
aria-label="Edit layout"
|
||||
colorScheme="purple"
|
||||
borderColor="transparent"
|
||||
background="rgba(17, 17, 17, 0.9)"
|
||||
_hover={{ color: 'white', borderColor: 'transparent' }}
|
||||
variant="outline"
|
||||
textTransform="uppercase"
|
||||
px={12}
|
||||
letterSpacing="0.1em"
|
||||
size="lg"
|
||||
fontSize="sm"
|
||||
bg="transparent"
|
||||
color="purple.400"
|
||||
onClick={handleReset}
|
||||
leftIcon={<DeleteIcon />}
|
||||
color={editable ? 'red.400' : 'pinkShadeOne'}
|
||||
leftIcon={<EditIcon />}
|
||||
transition="color 0.2s ease"
|
||||
onClick={toggleEditLayout}
|
||||
>
|
||||
Reset
|
||||
<ResponsiveText
|
||||
content={{
|
||||
base: editable ? 'Save' : 'Edit',
|
||||
md: `${editable ? 'Save' : 'Edit'} layout`,
|
||||
}}
|
||||
/>
|
||||
</MetaButton>
|
||||
)}
|
||||
<MetaButton
|
||||
aria-label="Edit layout"
|
||||
borderColor="transparent"
|
||||
background="rgba(17, 17, 17, 0.9)"
|
||||
_hover={{ color: 'white', borderColor: 'transparent' }}
|
||||
variant="outline"
|
||||
textTransform="uppercase"
|
||||
px={12}
|
||||
letterSpacing="0.1em"
|
||||
size="lg"
|
||||
fontSize="sm"
|
||||
bg="transparent"
|
||||
color={editable ? 'red.400' : 'pinkShadeOne'}
|
||||
leftIcon={<EditIcon />}
|
||||
transition="color 0.2s ease"
|
||||
onClick={toggleEditLayout}
|
||||
>
|
||||
{editable ? 'Save' : 'Edit'} layout
|
||||
</MetaButton>
|
||||
</ButtonGroup>
|
||||
</ButtonGroup>
|
||||
)}
|
||||
|
||||
<ResponsiveGridLayout
|
||||
className="grid"
|
||||
onLayoutChange={(layout, layouts) => {
|
||||
setCurrent({ layout, layouts });
|
||||
setChanged(true);
|
||||
onLayoutChange={(_layout, layouts) => {
|
||||
handleLayoutChange(layouts);
|
||||
}}
|
||||
verticalCompact
|
||||
layouts={gridLayouts}
|
||||
breakpoints={{ xl: 1920, lg: 1180, md: 900, sm: 768, xs: 480, xxs: 0 }}
|
||||
layouts={displayLayouts}
|
||||
breakpoints={{ lg: 1180, md: 900, sm: 768, xxs: 0 }}
|
||||
preventCollision={false}
|
||||
cols={{ xl: 12, lg: 12, md: 12, sm: 4, xs: 4, xxs: 4 }}
|
||||
rowHeight={135}
|
||||
autoSize
|
||||
// isBounded
|
||||
cols={{ lg: 3, md: 2, sm: 1, xxs: 1 }}
|
||||
rowHeight={32}
|
||||
isDraggable={!!editable}
|
||||
isResizable={!!editable}
|
||||
onDragStart={toggleScrollLock}
|
||||
@@ -223,55 +334,32 @@ export const Grid: React.FC<Props> = ({ player }): ReactElement => {
|
||||
onResizeStop={toggleScrollLock}
|
||||
transformScale={1}
|
||||
margin={{
|
||||
xl: [30, 30],
|
||||
lg: [30, 30],
|
||||
md: [30, 30],
|
||||
sm: [30, 30],
|
||||
xs: [30, 30],
|
||||
xxs: [30, 30],
|
||||
}}
|
||||
containerPadding={{
|
||||
xl: [30, 30],
|
||||
lg: [30, 30],
|
||||
md: [20, 20],
|
||||
sm: [20, 20],
|
||||
xs: [15, 15],
|
||||
xxs: [15, 15],
|
||||
}}
|
||||
>
|
||||
{/* <DashboardSection key="latest" id="latest" containerQuery={queryData}> */}
|
||||
<Box key="hero" className="gridItem">
|
||||
<GridItem>
|
||||
<PlayerHero player={player} isOwnProfile={isOwnProfile} />
|
||||
</GridItem>
|
||||
</Box>
|
||||
<Box key="colors" className="gridItem">
|
||||
<GridItem>
|
||||
<PlayerColorDisposition player={player} />
|
||||
</GridItem>
|
||||
</Box>
|
||||
<Box key="skills" className="gridItem">
|
||||
<GridItem>
|
||||
<PlayerSkills player={player} />
|
||||
</GridItem>
|
||||
</Box>
|
||||
<Box key="type" className="gridItem">
|
||||
<GridItem>
|
||||
<PlayerType player={player} />
|
||||
</GridItem>
|
||||
</Box>
|
||||
<Box key="memberships" className="gridItem">
|
||||
<GridItem>
|
||||
<PlayerMemberships player={player} />
|
||||
</GridItem>
|
||||
</Box>
|
||||
{/*
|
||||
<Box key="gallery" className="gridItem">
|
||||
<GridItem >
|
||||
<PlayerGallery player={player} />
|
||||
</GridItem>
|
||||
</Box>
|
||||
*/}
|
||||
{boxes.map((item) => (
|
||||
// item === BoxType.PLAYER_ADD_BOX && !editable ? null : (
|
||||
<Flex key={item} className="gridItem">
|
||||
<PlayerSection
|
||||
boxType={item}
|
||||
player={player}
|
||||
isOwnProfile={isOwnProfile}
|
||||
canEdit={editable}
|
||||
removeBox={onRemoveBox}
|
||||
availableBoxList={availableBoxList}
|
||||
setNewBox={onAddBox}
|
||||
/>
|
||||
</Flex>
|
||||
))}
|
||||
</ResponsiveGridLayout>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
export const BOX_TYPE = {
|
||||
PLAYER: {
|
||||
SKILLS: 'Skills',
|
||||
GALLERY: 'Gallery',
|
||||
MEMBERSHIPS: 'Memberships',
|
||||
ACHIEVEMENTS: 'Achievements',
|
||||
TYPE: 'Player Type',
|
||||
COLOR_DISPOSITION: 'Color disposition',
|
||||
ROLES: 'Roles',
|
||||
},
|
||||
GUILD: {
|
||||
SKILLS: 'Skills',
|
||||
GALLERY: 'Gallery',
|
||||
ANNOUNCEMENTS: 'Announcements',
|
||||
PLAYERS: 'Players',
|
||||
QUESTS: 'Quests',
|
||||
STATS: 'Stats',
|
||||
LINKS: 'Links',
|
||||
},
|
||||
};
|
||||
export enum BoxType {
|
||||
PLAYER_HERO = 'player-hero',
|
||||
PLAYER_SKILLS = 'player-skills',
|
||||
PLAYER_NFT_GALLERY = 'player-nft-gallery',
|
||||
PLAYER_DAO_MEMBERSHIPS = 'player-dao-memberships',
|
||||
PLAYER_ACHIEVEMENTS = 'player-achievements',
|
||||
PLAYER_TYPE = 'player-type',
|
||||
PLAYER_COLOR_DISPOSITION = 'player-color-disposition',
|
||||
PLAYER_ROLES = 'player-roles',
|
||||
PLAYER_ADD_BOX = 'player-add-box',
|
||||
GUILD_SKILLS = 'guild-skills',
|
||||
GUILD_GALLERY = 'guild-gallery',
|
||||
GUILD_ANNOUNCEMENTS = 'guild-announcements',
|
||||
GUILD_PLAYERS = 'guild-players',
|
||||
GUILD_QUESTS = 'quild-quests',
|
||||
GUILD_STATS = 'guild-stats',
|
||||
GUILD_LINKS = 'guild-links',
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user