mirror of
https://github.com/MetaFam/TheGame.git
synced 2026-01-15 01:17:57 -05:00
reuse SetupPersonalityTypes for edit profile modal
This commit is contained in:
@@ -2,8 +2,10 @@ import { Link } from '@metafam/ds';
|
||||
import { PlayerFragmentFragment } from 'graphql/autogen/types';
|
||||
import { getPersonalityInfo } from 'graphql/getPersonalityInfo';
|
||||
import { PersonalityOption } from 'graphql/types';
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { BOX_TYPE } from 'utils/boxTypes';
|
||||
|
||||
import { FlexContainer } from '../../Container';
|
||||
import { ProfileSection } from '../../ProfileSection';
|
||||
import { ColorBar } from '../ColorBar';
|
||||
|
||||
@@ -20,35 +22,54 @@ export const PlayerColorDisposition: React.FC<Props> = ({
|
||||
const [types, setTypes] = React.useState<{
|
||||
[any: string]: PersonalityOption;
|
||||
}>();
|
||||
const [colorDisposition, setColorDisposition] = useState<
|
||||
0 | PersonalityOption | undefined
|
||||
>();
|
||||
const [animation, setAnimation] = useState<string>('fadeIn');
|
||||
const mask = player?.color_aspect?.mask;
|
||||
const type = mask && types?.[mask];
|
||||
|
||||
const loadTypes = async () => {
|
||||
const { types: list } = await getPersonalityInfo();
|
||||
setTypes(list);
|
||||
};
|
||||
useEffect(() => {
|
||||
const loadTypes = async () => {
|
||||
const { types: list } = await getPersonalityInfo();
|
||||
setTypes(list);
|
||||
};
|
||||
loadTypes();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setAnimation('fadeOut');
|
||||
setTimeout(() => {
|
||||
setColorDisposition(type);
|
||||
setAnimation('fadeIn');
|
||||
}, 400);
|
||||
}, [mask, type]);
|
||||
|
||||
return (
|
||||
<ProfileSection
|
||||
title="Color Disposition"
|
||||
onRemoveClick={onRemoveClick}
|
||||
isOwnProfile={isOwnProfile}
|
||||
boxType={BOX_TYPE.PLAYER_COLOR_DISPOSITION}
|
||||
>
|
||||
{type && types && (
|
||||
<Link
|
||||
isExternal
|
||||
href={`//dysbulic.github.io/5-color-radar/#/combos/${type.mask.toString(
|
||||
2,
|
||||
)}`}
|
||||
maxH="6rem"
|
||||
fontSize={{ base: 'md', sm: 'lg' }}
|
||||
fontWeight="600"
|
||||
{colorDisposition && types && (
|
||||
<FlexContainer
|
||||
align="stretch"
|
||||
transition=" opacity 0.4s"
|
||||
opacity={animation === 'fadeIn' ? 1 : 0}
|
||||
>
|
||||
<ColorBar mask={type.mask} />
|
||||
</Link>
|
||||
<Link
|
||||
isExternal
|
||||
href={`//dysbulic.github.io/5-color-radar/#/combos/${colorDisposition?.mask.toString(
|
||||
2,
|
||||
)}`}
|
||||
maxH="6rem"
|
||||
fontSize={{ base: 'md', sm: 'lg' }}
|
||||
fontWeight="600"
|
||||
>
|
||||
<ColorBar mask={colorDisposition?.mask} />
|
||||
</Link>
|
||||
</FlexContainer>
|
||||
)}
|
||||
</ProfileSection>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Text } from '@metafam/ds';
|
||||
import { Player_Type, PlayerFragmentFragment } from 'graphql/autogen/types';
|
||||
import { useUser } from 'lib/hooks';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { BOX_TYPE } from 'utils/boxTypes';
|
||||
|
||||
import { FlexContainer } from '../../Container';
|
||||
@@ -18,19 +17,32 @@ export const PlayerType: React.FC<Props> = ({
|
||||
isOwnProfile,
|
||||
onRemoveClick,
|
||||
}) => {
|
||||
const { user } = useUser();
|
||||
const [playerType, setPlayerType] = useState<Player_Type | null>();
|
||||
const [animation, setAnimation] = useState<string>('fadeIn');
|
||||
|
||||
const type = player?.type;
|
||||
|
||||
const usePrevious = <T extends unknown>(value: T): T | undefined => {
|
||||
const ref = useRef<T>();
|
||||
useEffect(() => {
|
||||
ref.current = value;
|
||||
});
|
||||
return ref.current;
|
||||
};
|
||||
const previousType = usePrevious(type);
|
||||
|
||||
// todo refactor so that usePrevious won't be needed anymore
|
||||
// something is retriggering useEffect when color disposition is changed, for example
|
||||
// i suspect the type object's reference is changing
|
||||
useEffect(() => {
|
||||
setAnimation('fadeOut');
|
||||
setTimeout(() => {
|
||||
setPlayerType(type);
|
||||
setAnimation('fadeIn');
|
||||
}, 400);
|
||||
}, [user, type]);
|
||||
if (previousType?.title !== type?.title) {
|
||||
setAnimation('fadeOut');
|
||||
setTimeout(() => {
|
||||
setPlayerType(type);
|
||||
setAnimation('fadeIn');
|
||||
}, 400);
|
||||
}
|
||||
}, [type, previousType]);
|
||||
|
||||
return (
|
||||
<ProfileSection
|
||||
@@ -39,23 +51,25 @@ export const PlayerType: React.FC<Props> = ({
|
||||
isOwnProfile={isOwnProfile}
|
||||
boxType={BOX_TYPE.PLAYER_TYPE}
|
||||
>
|
||||
<FlexContainer
|
||||
align="stretch"
|
||||
transition=" opacity 0.4s"
|
||||
opacity={animation === 'fadeIn' ? 1 : 0}
|
||||
>
|
||||
<Text
|
||||
color="white"
|
||||
fontWeight="600"
|
||||
casing="uppercase"
|
||||
fontSize={{ base: 'md', sm: 'lg' }}
|
||||
{playerType && (
|
||||
<FlexContainer
|
||||
align="stretch"
|
||||
transition=" opacity 0.4s"
|
||||
opacity={animation === 'fadeIn' ? 1 : 0}
|
||||
>
|
||||
{playerType?.title}
|
||||
</Text>
|
||||
<Text fontSize={{ base: 'sm', sm: 'md' }} color="blueLight">
|
||||
{playerType?.description}
|
||||
</Text>
|
||||
</FlexContainer>
|
||||
<Text
|
||||
color="white"
|
||||
fontWeight="600"
|
||||
casing="uppercase"
|
||||
fontSize={{ base: 'md', sm: 'lg' }}
|
||||
>
|
||||
{playerType.title}
|
||||
</Text>
|
||||
<Text fontSize={{ base: 'sm', sm: 'md' }} color="blueLight">
|
||||
{playerType.description}
|
||||
</Text>
|
||||
</FlexContainer>
|
||||
)}
|
||||
</ProfileSection>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
useDisclosure,
|
||||
} from '@metafam/ds';
|
||||
import BackgroundImage from 'assets/main-background.jpg';
|
||||
import { SetupPersonalityType } from 'components/Setup/SetupPersonalityType';
|
||||
import { SetupPlayerType } from 'components/Setup/SetupPlayerType';
|
||||
import React from 'react';
|
||||
import { FaTimes } from 'react-icons/fa';
|
||||
@@ -117,6 +118,8 @@ const getEditSectionBox = (
|
||||
switch (boxType) {
|
||||
case BOX_TYPE.PLAYER_TYPE:
|
||||
return <SetupPlayerType isEdit onClose={onClose} />;
|
||||
case BOX_TYPE.PLAYER_COLOR_DISPOSITION:
|
||||
return <SetupPersonalityType isEdit onClose={onClose} />;
|
||||
default:
|
||||
return <SetupPlayerType isEdit onClose={onClose} />;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
Image,
|
||||
MetaButton,
|
||||
MetaHeading,
|
||||
ModalFooter,
|
||||
Text,
|
||||
useToast,
|
||||
} from '@metafam/ds';
|
||||
@@ -12,33 +13,70 @@ import { MetaLink } from 'components/Link';
|
||||
import { ColorBar } from 'components/Player/ColorBar';
|
||||
import { useSetupFlow } from 'contexts/SetupContext';
|
||||
import { useUpdateAboutYouMutation } from 'graphql/autogen/types';
|
||||
import { images as BaseImages } from 'graphql/queries/enums/getPersonalityInfo';
|
||||
import {
|
||||
getPersonalityInfo,
|
||||
images as BaseImages,
|
||||
} from 'graphql/queries/enums/getPersonalityInfo';
|
||||
import { PersonalityOption } from 'graphql/types';
|
||||
import { useUser } from 'lib/hooks';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
export type SetupPersonalityTypeProps = {
|
||||
// keyed on a bitmask of the format 0bWUBRG
|
||||
personalityTypes: { [x: number]: PersonalityOption };
|
||||
colorMask: number | undefined;
|
||||
setColorMask: React.Dispatch<React.SetStateAction<number | undefined>>;
|
||||
isEdit?: boolean;
|
||||
onClose?: () => void;
|
||||
};
|
||||
|
||||
export const SetupPersonalityType: React.FC<SetupPersonalityTypeProps> = ({
|
||||
personalityTypes,
|
||||
colorMask,
|
||||
setColorMask,
|
||||
isEdit,
|
||||
onClose,
|
||||
}) => {
|
||||
const { onNextPress, nextButtonLabel } = useSetupFlow();
|
||||
const { user } = useUser({ redirectTo: '/' });
|
||||
const toast = useToast();
|
||||
const [updateAboutYouRes, updateAboutYou] = useUpdateAboutYouMutation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [personalityTypes, setPersonalityTypes] = useState<{
|
||||
[x: number]: PersonalityOption;
|
||||
}>([]);
|
||||
const isWizard = !isEdit;
|
||||
|
||||
const handleNextPress = useCallback(async () => {
|
||||
if (!user) return;
|
||||
const [colorMask, setColorMask] = useState<number | undefined>(
|
||||
user?.player?.color_aspect?.mask,
|
||||
);
|
||||
|
||||
const load = () => {
|
||||
const { player } = user ?? {};
|
||||
if (player) {
|
||||
if (colorMask === undefined && player.color_aspect !== null) {
|
||||
setColorMask(player.color_aspect?.mask);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(load, [user, colorMask]);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchMyAPI() {
|
||||
const {
|
||||
types,
|
||||
// parts: personalityParts,
|
||||
} = await getPersonalityInfo();
|
||||
setPersonalityTypes(types);
|
||||
}
|
||||
|
||||
fetchMyAPI();
|
||||
}, []);
|
||||
|
||||
const handleNextPress = async () => {
|
||||
setLoading(true);
|
||||
|
||||
save();
|
||||
|
||||
onNextPress();
|
||||
};
|
||||
|
||||
const save = async () => {
|
||||
if (!user) return;
|
||||
if (user.player?.color_aspect?.mask !== colorMask) {
|
||||
const { error } = await updateAboutYou({
|
||||
playerId: user.id,
|
||||
@@ -56,12 +94,9 @@ export const SetupPersonalityType: React.FC<SetupPersonalityTypeProps> = ({
|
||||
isClosable: true,
|
||||
});
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
onNextPress();
|
||||
}, [colorMask, onNextPress, toast, updateAboutYou, user]);
|
||||
};
|
||||
|
||||
// mask should always only have at most a single bit set
|
||||
const toggleMaskElement = (mask = 0): void => {
|
||||
@@ -77,10 +112,12 @@ export const SetupPersonalityType: React.FC<SetupPersonalityTypeProps> = ({
|
||||
return (
|
||||
<FlexContainer maxW="100%">
|
||||
<Flex direction="column">
|
||||
<MetaHeading mb={5} textAlign="center">
|
||||
Person­ality Type
|
||||
</MetaHeading>
|
||||
<Text mb={10}>
|
||||
{isWizard && (
|
||||
<MetaHeading mb={5} textAlign="center">
|
||||
Person­ality Type
|
||||
</MetaHeading>
|
||||
)}
|
||||
<Text mb={10} color={isWizard ? 'current' : 'white'}>
|
||||
Please select your personality components below. Not sure what type
|
||||
you are?
|
||||
<Text as="span"> Take </Text>
|
||||
@@ -108,88 +145,110 @@ export const SetupPersonalityType: React.FC<SetupPersonalityTypeProps> = ({
|
||||
wrap="wrap"
|
||||
id="colors"
|
||||
>
|
||||
{Object.entries(BaseImages)
|
||||
.reverse()
|
||||
.map(([orig, image], idx) => {
|
||||
const option = personalityTypes[parseInt(orig, 10)];
|
||||
const { mask = 0 } = option ?? {};
|
||||
const selected = ((colorMask ?? 0) & mask) > 0;
|
||||
{Object.keys(personalityTypes).length &&
|
||||
Object.entries(BaseImages)
|
||||
.reverse()
|
||||
.map(([orig, image], idx) => {
|
||||
const option = personalityTypes[parseInt(orig, 10)];
|
||||
const { mask = 0 } = option ?? {};
|
||||
const selected = ((colorMask ?? 0) & mask) > 0;
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={mask}
|
||||
display="flex"
|
||||
direction="row"
|
||||
p={6}
|
||||
m={2}
|
||||
h="auto"
|
||||
spacing={4}
|
||||
borderRadius={8}
|
||||
cursor="pointer"
|
||||
onClick={() => toggleMaskElement(mask)}
|
||||
autoFocus={idx === 0} // Doesn't work
|
||||
ref={(input) => {
|
||||
if (idx === 0 && !input?.getAttribute('focused-once')) {
|
||||
input?.focus();
|
||||
input?.setAttribute('focused-once', 'true');
|
||||
}
|
||||
}}
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleNextPress();
|
||||
e.preventDefault();
|
||||
}
|
||||
}}
|
||||
transition="background 0.25s, filter 0.5s"
|
||||
bgColor={selected ? 'purpleBoxDark' : 'purpleBoxLight'}
|
||||
_hover={{
|
||||
filter: 'hue-rotate(25deg)',
|
||||
}}
|
||||
_focus={{
|
||||
borderColor: '#FFFFFF55',
|
||||
outline: 'none',
|
||||
}}
|
||||
_active={{ bg: selected ? 'purpleBoxDark' : 'purpleBoxLight' }}
|
||||
borderWidth={2}
|
||||
borderColor={selected ? 'purple.400' : 'transparent'}
|
||||
>
|
||||
<Image
|
||||
w="100%"
|
||||
maxW={16}
|
||||
h={16}
|
||||
src={image}
|
||||
alt={option.name}
|
||||
filter="drop-shadow(0px 0px 3px black)"
|
||||
/>
|
||||
<FlexContainer align="stretch" ml={2}>
|
||||
<Text color="white" casing="uppercase" textAlign="left">
|
||||
{option.name}
|
||||
</Text>
|
||||
<Text
|
||||
color="blueLight"
|
||||
fontWeight="normal"
|
||||
whiteSpace="initial"
|
||||
textAlign="left"
|
||||
>
|
||||
{option.description}
|
||||
</Text>
|
||||
</FlexContainer>
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
return (
|
||||
<Button
|
||||
key={mask}
|
||||
display="flex"
|
||||
direction="row"
|
||||
p={6}
|
||||
m={2}
|
||||
h="auto"
|
||||
spacing={4}
|
||||
borderRadius={8}
|
||||
cursor="pointer"
|
||||
onClick={() => toggleMaskElement(mask)}
|
||||
autoFocus={idx === 0} // Doesn't work
|
||||
ref={(input) => {
|
||||
if (idx === 0 && !input?.getAttribute('focused-once')) {
|
||||
input?.focus();
|
||||
input?.setAttribute('focused-once', 'true');
|
||||
}
|
||||
}}
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
if (isWizard) handleNextPress();
|
||||
if (isEdit) save();
|
||||
e.preventDefault();
|
||||
}
|
||||
}}
|
||||
transition="background 0.25s, filter 0.5s"
|
||||
bgColor={selected ? 'purpleBoxDark' : 'purpleBoxLight'}
|
||||
_hover={{
|
||||
filter: 'hue-rotate(25deg)',
|
||||
}}
|
||||
_focus={{
|
||||
borderColor: '#FFFFFF55',
|
||||
outline: 'none',
|
||||
}}
|
||||
_active={{
|
||||
bg: selected ? 'purpleBoxDark' : 'purpleBoxLight',
|
||||
}}
|
||||
borderWidth={2}
|
||||
borderColor={selected ? 'purple.400' : 'transparent'}
|
||||
>
|
||||
<Image
|
||||
w="100%"
|
||||
maxW={16}
|
||||
h={16}
|
||||
src={image}
|
||||
alt={option.name}
|
||||
filter="drop-shadow(0px 0px 3px black)"
|
||||
/>
|
||||
<FlexContainer align="stretch" ml={2}>
|
||||
<Text color="white" casing="uppercase" textAlign="left">
|
||||
{option.name}
|
||||
</Text>
|
||||
<Text
|
||||
color="blueLight"
|
||||
fontWeight="normal"
|
||||
whiteSpace="initial"
|
||||
textAlign="left"
|
||||
>
|
||||
{option.description}
|
||||
</Text>
|
||||
</FlexContainer>
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</FlexContainer>
|
||||
|
||||
<ColorBar mask={colorMask} mt={8} w="min(100vw, 30rem)" />
|
||||
|
||||
<MetaButton
|
||||
onClick={handleNextPress}
|
||||
mt={10}
|
||||
isDisabled={colorMask === undefined}
|
||||
isLoading={updateAboutYouRes.fetching || loading}
|
||||
loadingText="Saving"
|
||||
>
|
||||
{nextButtonLabel}
|
||||
</MetaButton>
|
||||
{isEdit && (
|
||||
<ModalFooter mt={6}>
|
||||
<Button colorScheme="blue" mr={3} onClick={save}>
|
||||
Save Changes
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={onClose}
|
||||
color="white"
|
||||
_hover={{ bg: 'none' }}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
)}
|
||||
|
||||
{isWizard && (
|
||||
<MetaButton
|
||||
onClick={handleNextPress}
|
||||
mt={10}
|
||||
isDisabled={colorMask === undefined}
|
||||
isLoading={updateAboutYouRes.fetching || loading}
|
||||
loadingText="Saving"
|
||||
>
|
||||
{nextButtonLabel}
|
||||
</MetaButton>
|
||||
)}
|
||||
</FlexContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -48,8 +48,6 @@ export const SetupPlayerType: React.FC<Props> = ({ isEdit, onClose }) => {
|
||||
}, [playerTypeChoices]);
|
||||
|
||||
const handleNextPress = async () => {
|
||||
if (!user) return;
|
||||
|
||||
setLoading(true);
|
||||
|
||||
save();
|
||||
|
||||
@@ -1,57 +1,13 @@
|
||||
import { SetupPersonalityType } from 'components/Setup/SetupPersonalityType';
|
||||
import { SetupProfile } from 'components/Setup/SetupProfile';
|
||||
import { SetupContextProvider } from 'contexts/SetupContext';
|
||||
import { getPersonalityInfo } from 'graphql/queries/enums/getPersonalityInfo';
|
||||
import { useUser } from 'lib/hooks';
|
||||
import { InferGetStaticPropsType } from 'next';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
export const getStaticProps = async () => {
|
||||
const {
|
||||
types: personalityTypes,
|
||||
parts: personalityParts,
|
||||
} = await getPersonalityInfo();
|
||||
|
||||
return {
|
||||
props: {
|
||||
personalityParts,
|
||||
personalityTypes,
|
||||
hideTopMenu: true,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
type Props = InferGetStaticPropsType<typeof getStaticProps>;
|
||||
|
||||
const PersonalityTypeSetup: React.FC<Props> = (props) => {
|
||||
const { personalityTypes } = props;
|
||||
const { user } = useUser({ redirectTo: '/' });
|
||||
const [colorMask, setColorMask] = useState<number | undefined>(
|
||||
user?.player?.color_aspect?.mask,
|
||||
);
|
||||
|
||||
const load = () => {
|
||||
const { player } = user ?? {};
|
||||
if (player) {
|
||||
if (colorMask === undefined && player.color_aspect !== null) {
|
||||
setColorMask(player.color_aspect?.mask);
|
||||
}
|
||||
}
|
||||
};
|
||||
useEffect(load, [user, colorMask]);
|
||||
|
||||
return (
|
||||
<SetupContextProvider>
|
||||
<SetupProfile>
|
||||
<SetupPersonalityType
|
||||
{...{
|
||||
personalityTypes,
|
||||
colorMask,
|
||||
setColorMask,
|
||||
}}
|
||||
/>
|
||||
</SetupProfile>
|
||||
</SetupContextProvider>
|
||||
);
|
||||
};
|
||||
const PersonalityTypeSetup: React.FC = () => (
|
||||
<SetupContextProvider>
|
||||
<SetupProfile>
|
||||
<SetupPersonalityType />
|
||||
</SetupProfile>
|
||||
</SetupContextProvider>
|
||||
);
|
||||
export default PersonalityTypeSetup;
|
||||
|
||||
Reference in New Issue
Block a user