Show empty PosePicker for Support users
For most users, I want to hide the pose picker if there's not actually anything to pick from. But I want Support users to be able to open it and use Support mode, to inspect and label Unknown poses! In this change, I add new UI to show a question mark pose picker button, and a note explaining the empty picker; but only show them for Support users. I also made a new `useSupport` hook, to replace `useSupportSecret`, now that I have a use case for "is support user?" that doesn't require the secret. Will migrate the rest!
This commit is contained in:
parent
5f3b627187
commit
4e923746ce
5 changed files with 76 additions and 6 deletions
|
@ -23,12 +23,14 @@ import {
|
||||||
} from "../components/useOutfitAppearance";
|
} from "../components/useOutfitAppearance";
|
||||||
import { OutfitLayers } from "../components/OutfitPreview";
|
import { OutfitLayers } from "../components/OutfitPreview";
|
||||||
import SupportOnly from "./support/SupportOnly";
|
import SupportOnly from "./support/SupportOnly";
|
||||||
|
import useSupport from "./support/useSupport";
|
||||||
import { useLocalStorage } from "../util";
|
import { useLocalStorage } from "../util";
|
||||||
|
|
||||||
// From https://twemoji.twitter.com/, thank you!
|
// From https://twemoji.twitter.com/, thank you!
|
||||||
import twemojiSmile from "../../images/twemoji/smile.svg";
|
import twemojiSmile from "../../images/twemoji/smile.svg";
|
||||||
import twemojiCry from "../../images/twemoji/cry.svg";
|
import twemojiCry from "../../images/twemoji/cry.svg";
|
||||||
import twemojiSick from "../../images/twemoji/sick.svg";
|
import twemojiSick from "../../images/twemoji/sick.svg";
|
||||||
|
import twemojiQuestion from "../../images/twemoji/question.svg";
|
||||||
import twemojiMasc from "../../images/twemoji/masc.svg";
|
import twemojiMasc from "../../images/twemoji/masc.svg";
|
||||||
import twemojiFem from "../../images/twemoji/fem.svg";
|
import twemojiFem from "../../images/twemoji/fem.svg";
|
||||||
|
|
||||||
|
@ -67,6 +69,7 @@ function PosePicker({
|
||||||
"DTIPosePickerIsInSupportMode",
|
"DTIPosePickerIsInSupportMode",
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
const { isSupportUser } = useSupport();
|
||||||
|
|
||||||
// Resize the Popover when we toggle support mode, because it probably will
|
// Resize the Popover when we toggle support mode, because it probably will
|
||||||
// affect the content size.
|
// affect the content size.
|
||||||
|
@ -89,10 +92,12 @@ function PosePicker({
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there's only one pose anyway, don't bother showing a picker!
|
// If there's only one pose anyway, don't bother showing a picker!
|
||||||
|
// (Unless we're Support, in which case we want the ability to pop it open to
|
||||||
|
// inspect and label the Unknown poses!)
|
||||||
const numAvailablePoses = Object.values(poseInfos).filter(
|
const numAvailablePoses = Object.values(poseInfos).filter(
|
||||||
(p) => p.isAvailable
|
(p) => p.isAvailable
|
||||||
).length;
|
).length;
|
||||||
if (numAvailablePoses <= 1) {
|
if (numAvailablePoses <= 1 && !isSupportUser) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,6 +152,9 @@ function PosePicker({
|
||||||
{getEmotion(pose) === "SICK" && (
|
{getEmotion(pose) === "SICK" && (
|
||||||
<EmojiImage src={twemojiSick} alt="Choose a pose" />
|
<EmojiImage src={twemojiSick} alt="Choose a pose" />
|
||||||
)}
|
)}
|
||||||
|
{getEmotion(pose) === null && (
|
||||||
|
<EmojiImage src={twemojiQuestion} alt="Choose a pose" />
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<Portal>
|
<Portal>
|
||||||
|
@ -161,11 +169,28 @@ function PosePicker({
|
||||||
dispatchToOutfit={dispatchToOutfit}
|
dispatchToOutfit={dispatchToOutfit}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
<>
|
||||||
<PosePickerTable
|
<PosePickerTable
|
||||||
poseInfos={poseInfos}
|
poseInfos={poseInfos}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
checkedInputRef={checkedInputRef}
|
checkedInputRef={checkedInputRef}
|
||||||
/>
|
/>
|
||||||
|
{numAvailablePoses <= 1 && (
|
||||||
|
<SupportOnly>
|
||||||
|
<Box
|
||||||
|
fontSize="xs"
|
||||||
|
fontStyle="italic"
|
||||||
|
textAlign="center"
|
||||||
|
opacity="0.7"
|
||||||
|
marginTop="2"
|
||||||
|
>
|
||||||
|
The empty picker is hidden for most users!
|
||||||
|
<br />
|
||||||
|
You can see it because you're a Support user.
|
||||||
|
</Box>
|
||||||
|
</SupportOnly>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<SupportOnly>
|
<SupportOnly>
|
||||||
<Box position="absolute" top="5" left="3">
|
<Box position="absolute" top="5" left="3">
|
||||||
|
|
|
@ -71,6 +71,13 @@ function PosePickerSupport({
|
||||||
) {
|
) {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
unknown: petAppearance(
|
||||||
|
speciesId: $speciesId
|
||||||
|
colorId: $colorId
|
||||||
|
pose: UNKNOWN
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
{ variables: { speciesId, colorId } }
|
{ variables: { speciesId, colorId } }
|
||||||
|
@ -113,6 +120,7 @@ function PosePickerSupport({
|
||||||
HAPPY_FEM: data.happyFem?.id,
|
HAPPY_FEM: data.happyFem?.id,
|
||||||
SAD_FEM: data.sadFem?.id,
|
SAD_FEM: data.sadFem?.id,
|
||||||
SICK_FEM: data.sickFem?.id,
|
SICK_FEM: data.sickFem?.id,
|
||||||
|
UNKNOWN: data.unknown?.id,
|
||||||
};
|
};
|
||||||
const canonicalAppearanceIds = Object.values(
|
const canonicalAppearanceIds = Object.values(
|
||||||
canonicalAppearanceIdsByPose
|
canonicalAppearanceIdsByPose
|
||||||
|
|
29
src/app/WardrobePage/support/useSupport.js
Normal file
29
src/app/WardrobePage/support/useSupport.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* useSupport returns the Support secret that the server requires for Support
|
||||||
|
* actions... if the user has it set. For most users, this returns nothing!
|
||||||
|
*
|
||||||
|
* Specifically, we return an object of:
|
||||||
|
* - isSupportUser: true iff the `supportSecret` is set
|
||||||
|
* - supportSecret: the secret saved to this device, or null if not set
|
||||||
|
*
|
||||||
|
* To become a Support user, you visit /?supportSecret=..., which saves the
|
||||||
|
* secret to your device.
|
||||||
|
*
|
||||||
|
* Note that this hook doesn't check that the secret is *correct*, so it's
|
||||||
|
* possible that it will return an invalid secret. That's okay, because
|
||||||
|
* the server checks the provided secret for each Support request.
|
||||||
|
*/
|
||||||
|
function useSupport() {
|
||||||
|
const supportSecret = React.useMemo(
|
||||||
|
() => localStorage.getItem("supportSecret"),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const isSupportUser = supportSecret != null;
|
||||||
|
|
||||||
|
return { isSupportUser, supportSecret };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useSupport;
|
|
@ -11,9 +11,16 @@ import * as React from "react";
|
||||||
* Note that this hook doesn't check that the secret is *correct*, so it's
|
* Note that this hook doesn't check that the secret is *correct*, so it's
|
||||||
* possible that it will return an invalid secret. That's okay, because
|
* possible that it will return an invalid secret. That's okay, because
|
||||||
* the server checks the provided secret for each Support request.
|
* the server checks the provided secret for each Support request.
|
||||||
|
*
|
||||||
|
* DEPRECATED: Use `useSupport` instead!
|
||||||
*/
|
*/
|
||||||
function useSupportSecret() {
|
function useSupportSecret() {
|
||||||
return React.useMemo(() => localStorage.getItem("supportSecret"), []);
|
return React.useMemo(() => localStorage.getItem("supportSecret"), []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useIsSupportUser() {
|
||||||
|
const supportSecret = useSupportSecret();
|
||||||
|
return supportSecret != null;
|
||||||
|
}
|
||||||
|
|
||||||
export default useSupportSecret;
|
export default useSupportSecret;
|
||||||
|
|
1
src/images/twemoji/question.svg
Normal file
1
src/images/twemoji/question.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#CCD6DD" d="M17 27c-1.657 0-3-1.343-3-3v-4c0-1.657 1.343-3 3-3 .603-.006 6-1 6-5 0-2-2-4-5-4-2.441 0-4 2-4 3 0 1.657-1.343 3-3 3s-3-1.343-3-3c0-4.878 4.58-9 10-9 8 0 11 5.982 11 11 0 4.145-2.277 7.313-6.413 8.92-.9.351-1.79.587-2.587.747V24c0 1.657-1.343 3-3 3z"/><circle fill="#CCD6DD" cx="17" cy="32" r="3"/></svg>
|
After Width: | Height: | Size: 388 B |
Loading…
Reference in a new issue