Skip to content

Commit

Permalink
fragment masking
Browse files Browse the repository at this point in the history
  • Loading branch information
n1ru4l committed Dec 8, 2021
1 parent fe4ee5f commit 637da5a
Show file tree
Hide file tree
Showing 13 changed files with 337 additions and 257 deletions.
7 changes: 5 additions & 2 deletions GraphQLTypeDefinitions.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
Instruction for establishing a live connection that is updated once the underlying data changes.
"""
directive @live(
"""Whether the query should be live or not."""
if: Boolean = true

"""
Whether the query should be live or not.
Propose a desired throttle interval ot the server in order to receive updates to at most once per "throttle" milliseconds. The server must not accept this value.
"""
if: Boolean = true
throttle: Int
) on QUERY

type Query {
Expand Down
2 changes: 2 additions & 0 deletions codegen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ documents:
generates:
./src/gql/:
preset: gql-tag-operations-preset
presetConfig:
fragmentMasking: true
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"@n1ru4l/graphql-live-query-patch-json-patch": "0.5.1",
"@n1ru4l/in-memory-live-query-store": "0.7.3",
"@n1ru4l/socket-io-graphql-server": "0.11.0",
"@tinyhttp/app": "2.0.5",
"@tinyhttp/app": "1.3.15",
"@types/better-sqlite3": "7.4.0",
"better-sqlite3": "7.4.3",
"gqtx": "0.7.0",
Expand All @@ -24,7 +24,7 @@
"@emotion/styled": "11.3.0",
"@feedback-fish/react": "1.2.1",
"@graphql-codegen/cli": "2.2.0",
"@graphql-codegen/gql-tag-operations-preset": "1.2.0",
"@graphql-codegen/gql-tag-operations-preset": "1.3.0-alpha-1f4053433.0",
"@n1ru4l/push-pull-async-iterable-iterator": "3.1.0",
"@n1ru4l/socket-io-graphql-client": "0.11.0",
"@testing-library/jest-dom": "5.14.1",
Expand Down
1 change: 1 addition & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const CharacterQuery = gql(/* GraphQL */ `
character(id: $characterId) {
id
...CharacterViewFragment
...CharacterOverlayFragment
}
}
`);
Expand Down
19 changes: 7 additions & 12 deletions src/CharacterEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
Text,
VStack,
} from "@chakra-ui/react";
import { gql } from "./gql";
import { FragmentType, gql, useFragment } from "./gql";
import { useMutation, useQuery } from "urql";
import { AddIcon, MinusIcon } from "@chakra-ui/icons";
import styled from "@emotion/styled";
Expand Down Expand Up @@ -146,16 +146,11 @@ const UpdateCharacterMutation = gql(/* GraphQL */ `
}
`);

const Editor = ({
character,
editHash,
}: {
character: Exclude<
typeof CharacterViewFragment["__resultType"],
null | undefined
>;
const Editor = (props: {
character: FragmentType<typeof CharacterViewFragment>;
editHash: string;
}) => {
const character = useFragment(CharacterViewFragment, props.character);
const [, updateCharacter] = useMutation(UpdateCharacterMutation);
const [imageUrl, setImageUrl] = useResetState(
() => character.imageUrl,
Expand Down Expand Up @@ -201,12 +196,12 @@ const Editor = ({
isFirstRun.current = false;
return;
}
if (!editHash) {
if (!props.editHash) {
return;
}
updateCharacter({
input: {
editHash,
editHash: props.editHash,
updates: {
name,
currentHealth,
Expand All @@ -223,7 +218,7 @@ const Editor = ({
});
}, [
updateCharacter,
editHash,
props.editHash,
name,
currentHealth,
maximumHealth,
Expand Down
47 changes: 31 additions & 16 deletions src/CharacterView.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
import styled from "@emotion/styled";
import * as React from "react";
import { Box, HStack, Text, Stack } from "@chakra-ui/react";
import { CharacterViewFragment } from "./CharacterViewFragment";
import { ProgressBar } from "./ProgressBar";
import { FatePoints } from "./FatePointsIndicator";
import { FragmentType, gql, useFragment } from "./gql";

const CharacterOverlayFragment = gql(/* GraphQL */ `
fragment CharacterOverlayFragment on Character {
id
name
currentHealth
maximumHealth
hasMana
currentMana
maximumMana
hasFatePoints
currentFatePoints
maximumFatePoints
imageUrl
}
`);

export const CharacterOverlay = (props: {
character: CharacterViewFragment;
character: FragmentType<typeof CharacterOverlayFragment>;
size: "sm" | "lg";
}): React.ReactElement => {
const character = useFragment(CharacterOverlayFragment, props.character);
const imageSize = (props.size === "sm" ? 75 : 125) + "px";
return (
<HStack>
Expand All @@ -19,47 +36,45 @@ export const CharacterOverlay = (props: {
textShadow="-1px 0 black, 0 1px black, 1px 0 black, 0 -1px black"
width="100%"
>
{props.character.name}
{character.name}
</Text>
<Stack spacing="2">
<ProgressBar
current={props.character.currentHealth}
maximum={props.character.maximumHealth}
current={character.currentHealth}
maximum={character.maximumHealth}
colors={["#ec008c", "#ff0000"]}
label={
<Text as="span">
<Text fontWeight="bold" as="span">
LeP
</Text>{" "}
<Text as="span">
{props.character.currentHealth} /{" "}
{props.character.maximumHealth}
{character.currentHealth} / {character.maximumHealth}
</Text>
</Text>
}
/>
{props.character.hasMana ? (
{character.hasMana ? (
<ProgressBar
current={props.character.currentMana}
maximum={props.character.maximumMana}
current={character.currentMana}
maximum={character.maximumMana}
colors={["#3c99dc", "#66D3FA"]}
label={
<Text as="span">
<Text fontWeight="bold" as="span">
AsP
</Text>{" "}
<Text as="span">
{props.character.currentMana} /{" "}
{props.character.maximumMana}
{character.currentMana} / {character.maximumMana}
</Text>
</Text>
}
/>
) : null}
{props.character.hasFatePoints ? (
{character.hasFatePoints ? (
<FatePoints
current={props.character.currentFatePoints}
maximum={props.character.maximumFatePoints}
current={character.currentFatePoints}
maximum={character.maximumFatePoints}
/>
) : null}
</Stack>
Expand All @@ -69,7 +84,7 @@ export const CharacterOverlay = (props: {
height: imageSize,
width: imageSize,
}}
src={props.character.imageUrl ?? ""}
src={character.imageUrl ?? ""}
/>
</HStack>
);
Expand Down
28 changes: 15 additions & 13 deletions src/LandingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,19 +143,21 @@ export const LandingPage = (): React.ReactElement => {
<OverlayContainer>
<CharacterOverlay
size="sm"
character={{
id: "__local-example",
name: "Robin Hood",
maximumHealth: 30,
currentHealth: 22,
imageUrl: "/sample-portrait.jpg",
currentMana: 10,
maximumMana: 20,
hasMana: false,
hasFatePoints: false,
currentFatePoints: 0,
maximumFatePoints: 0,
}}
character={
{
id: "__local-example",
name: "Robin Hood",
maximumHealth: 30,
currentHealth: 22,
imageUrl: "/sample-portrait.jpg",
currentMana: 10,
maximumMana: 20,
hasMana: false,
hasFatePoints: false,
currentFatePoints: 0,
maximumFatePoints: 0,
} as any
}
/>
</OverlayContainer>
<WebcamTriggerContainer>
Expand Down
21 changes: 21 additions & 0 deletions src/gql/fragment-masking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';


export type FragmentType<TDocumentType extends DocumentNode<any, any>> = TDocumentType extends DocumentNode<
infer TType,
any
>
? TType extends { ' $fragmentName': infer TKey }
? TKey extends string
? { ' $fragmentRefs': { [key in TKey]: TType } }
: never
: never
: never;


export function useFragment<TType>(
_documentNode: DocumentNode<TType, any>,
fragmentType: FragmentType<DocumentNode<TType, any>>
): TType {
return fragmentType as any
}
26 changes: 26 additions & 0 deletions src/gql/gql.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* eslint-disable */
import * as graphql from './graphql';
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';

const documents = {
"\n query CharacterQuery($characterId: ID!) @live {\n character(id: $characterId) {\n id\n ...CharacterViewFragment\n ...CharacterOverlayFragment\n }\n }\n": graphql.CharacterQueryDocument,
"\n fragment CharacterViewFragment on Character {\n id\n name\n imageUrl\n maximumHealth\n currentHealth\n hasMana\n maximumMana\n currentMana\n hasFatePoints\n maximumFatePoints\n currentFatePoints\n }\n": graphql.CharacterViewFragmentFragmentDoc,
"\n query CharacterEditorQuery($editHash: ID!) @live {\n characterEditor(editHash: $editHash) {\n __typename\n ... on Error {\n reason\n }\n ... on CharacterEditorView {\n character {\n id\n ...CharacterViewFragment\n }\n }\n }\n }\n": graphql.CharacterEditorQueryDocument,
"\n mutation UpdateCharacterMutation($input: UpdateCharacterInput!) {\n updateCharacter(input: $input)\n }\n": graphql.UpdateCharacterMutationDocument,
"\n fragment CharacterOverlayFragment on Character {\n id\n name\n currentHealth\n maximumHealth\n hasMana\n currentMana\n maximumMana\n hasFatePoints\n currentFatePoints\n maximumFatePoints\n imageUrl\n }\n": graphql.CharacterOverlayFragmentFragmentDoc,
"\n mutation CreateCharacterMutation {\n createCharacter {\n __typename\n ... on Error {\n reason\n }\n ... on CreateCharacterSuccess {\n editHash\n }\n }\n }\n": graphql.CreateCharacterMutationDocument,
};

export function gql(source: "\n query CharacterQuery($characterId: ID!) @live {\n character(id: $characterId) {\n id\n ...CharacterViewFragment\n ...CharacterOverlayFragment\n }\n }\n"): (typeof documents)["\n query CharacterQuery($characterId: ID!) @live {\n character(id: $characterId) {\n id\n ...CharacterViewFragment\n ...CharacterOverlayFragment\n }\n }\n"];
export function gql(source: "\n fragment CharacterViewFragment on Character {\n id\n name\n imageUrl\n maximumHealth\n currentHealth\n hasMana\n maximumMana\n currentMana\n hasFatePoints\n maximumFatePoints\n currentFatePoints\n }\n"): (typeof documents)["\n fragment CharacterViewFragment on Character {\n id\n name\n imageUrl\n maximumHealth\n currentHealth\n hasMana\n maximumMana\n currentMana\n hasFatePoints\n maximumFatePoints\n currentFatePoints\n }\n"];
export function gql(source: "\n query CharacterEditorQuery($editHash: ID!) @live {\n characterEditor(editHash: $editHash) {\n __typename\n ... on Error {\n reason\n }\n ... on CharacterEditorView {\n character {\n id\n ...CharacterViewFragment\n }\n }\n }\n }\n"): (typeof documents)["\n query CharacterEditorQuery($editHash: ID!) @live {\n characterEditor(editHash: $editHash) {\n __typename\n ... on Error {\n reason\n }\n ... on CharacterEditorView {\n character {\n id\n ...CharacterViewFragment\n }\n }\n }\n }\n"];
export function gql(source: "\n mutation UpdateCharacterMutation($input: UpdateCharacterInput!) {\n updateCharacter(input: $input)\n }\n"): (typeof documents)["\n mutation UpdateCharacterMutation($input: UpdateCharacterInput!) {\n updateCharacter(input: $input)\n }\n"];
export function gql(source: "\n fragment CharacterOverlayFragment on Character {\n id\n name\n currentHealth\n maximumHealth\n hasMana\n currentMana\n maximumMana\n hasFatePoints\n currentFatePoints\n maximumFatePoints\n imageUrl\n }\n"): (typeof documents)["\n fragment CharacterOverlayFragment on Character {\n id\n name\n currentHealth\n maximumHealth\n hasMana\n currentMana\n maximumMana\n hasFatePoints\n currentFatePoints\n maximumFatePoints\n imageUrl\n }\n"];
export function gql(source: "\n mutation CreateCharacterMutation {\n createCharacter {\n __typename\n ... on Error {\n reason\n }\n ... on CreateCharacterSuccess {\n editHash\n }\n }\n }\n"): (typeof documents)["\n mutation CreateCharacterMutation {\n createCharacter {\n __typename\n ... on Error {\n reason\n }\n ... on CreateCharacterSuccess {\n editHash\n }\n }\n }\n"];

export function gql(source: string): unknown;
export function gql(source: string) {
return (documents as any)[source] ?? {};
}

export type DocumentType<TDocumentNode extends DocumentNode<any, any>> = TDocumentNode extends DocumentNode< infer TType, any> ? TType : never;
Loading

1 comment on commit 637da5a

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This image has been published to DockerHub. 🐋
You can easily try this build out locally with Docker.
docker run -p 4000:4000 n1ru4l/character-overlay:637da5aa17988f6e633941e06ac602c656667215

Please sign in to comment.