reuse SetupPersonalityTypes for edit profile modal

This commit is contained in:
vidvidvid
2021-10-11 18:06:40 +02:00
committed by Alec LaLonde
parent dc0a3b015c
commit 45318dca43
6 changed files with 242 additions and 191 deletions

View File

@@ -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>
);

View File

@@ -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>
);
};

View File

@@ -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} />;
}

View File

@@ -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&#xAD;ality Type
</MetaHeading>
<Text mb={10}>
{isWizard && (
<MetaHeading mb={5} textAlign="center">
Person&#xAD;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>
);
};

View File

@@ -48,8 +48,6 @@ export const SetupPlayerType: React.FC<Props> = ({ isEdit, onClose }) => {
}, [playerTypeChoices]);
const handleNextPress = async () => {
if (!user) return;
setLoading(true);
save();

View File

@@ -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;