From b2a114bbddae447d9b875b296dcccbe076d6b518 Mon Sep 17 00:00:00 2001 From: Hammad Jutt Date: Mon, 17 Aug 2020 01:17:14 -0600 Subject: [PATCH] Setup URQL with own backend and create basic player list page --- package.json | 3 ++ packages/@types/fake-tag/index.d.ts | 7 +++ packages/codegen/package.json | 2 +- packages/design-system/src/MetaTag.tsx | 1 + packages/design-system/src/index.ts | 1 + packages/design-system/src/theme.ts | 12 +++++ packages/web/codegen.yml | 17 +++++++ packages/web/components/Container.tsx | 15 ++---- packages/web/graphql/client.ts | 4 +- packages/web/graphql/fragments.ts | 20 ++++++++ packages/web/graphql/getPlayer.ts | 23 +++++++++ packages/web/graphql/getPlayers.ts | 32 ++++++++++++ packages/web/graphql/getPokemon.ts | 22 -------- packages/web/graphql/getPokemons.ts | 22 -------- packages/web/package.json | 5 +- packages/web/pages/index.tsx | 64 +++++++++++++++++------- packages/web/pages/login.tsx | 5 +- packages/web/pages/player/[username].tsx | 62 +++++++++++++++++++++++ packages/web/pages/pokemon/[name].tsx | 52 ------------------- packages/web/utils/playerHelpers.ts | 8 +++ yarn.lock | 16 ++++++ 21 files changed, 264 insertions(+), 129 deletions(-) create mode 100644 packages/@types/fake-tag/index.d.ts create mode 100644 packages/web/codegen.yml create mode 100644 packages/web/graphql/fragments.ts create mode 100644 packages/web/graphql/getPlayer.ts create mode 100644 packages/web/graphql/getPlayers.ts delete mode 100644 packages/web/graphql/getPokemon.ts delete mode 100644 packages/web/graphql/getPokemons.ts create mode 100644 packages/web/pages/player/[username].tsx delete mode 100644 packages/web/pages/pokemon/[name].tsx create mode 100644 packages/web/utils/playerHelpers.ts diff --git a/package.json b/package.json index 83466930..72d480d5 100644 --- a/package.json +++ b/package.json @@ -21,11 +21,13 @@ "hasura:migrate:init": "npm run hasura migrate create \"init\" -- --from-server", "hasura:migrate:apply": "npm run hasura migrate apply", "test": "lerna run test --parallel --", + "generate": "lerna run generate --parallel --", "test:full": "yarn lint && yarn typecheck && yarn test", "clean": "lerna clean", "format": "prettier --ignore-path .gitignore --write \"{*,**/*}.{ts,tsx,js,jsx,json,yml,yaml,md}\"", "lint": "eslint --ignore-path .gitignore \"./packages/**/*.{ts,tsx,js,jsx}\"", "typecheck": "lerna run typecheck", + "prepare": "lerna run prepare", "precommit": "lerna run --concurrency 1 --stream precommit", "prepush": "yarn typecheck", "backend": "yarn --cwd packages/backend/", @@ -46,6 +48,7 @@ "@graphql-codegen/typescript-operations": "^1.17.7", "@graphql-codegen/typescript-react-apollo": "^1.17.7", "@graphql-codegen/typescript-resolvers": "^1.17.7", + "@graphql-codegen/typescript-urql": "^2.0.1", "@types/node": "^13.11.1", "@types/react": "^16.9.43", "@types/react-dom": "^16.9.8", diff --git a/packages/@types/fake-tag/index.d.ts b/packages/@types/fake-tag/index.d.ts new file mode 100644 index 00000000..60c16d3e --- /dev/null +++ b/packages/@types/fake-tag/index.d.ts @@ -0,0 +1,7 @@ +declare module 'fake-tag' { + function gql( + literals: TemplateStringsArray, + ...placeholders: string[] + ): string; + export = gql; +} diff --git a/packages/codegen/package.json b/packages/codegen/package.json index a9b4ab3e..612caf1c 100644 --- a/packages/codegen/package.json +++ b/packages/codegen/package.json @@ -9,7 +9,7 @@ "get-schema": "env-cmd -f ../../.env -x get-graphql-schema -h x-hasura-admin-secret=\\$HASURA_GRAPHQL_ADMIN_SECRET http://localhost:8080/v1/graphql > schema.graphql", "update-schema": "yarn get-schema && yarn generate", "generate": "graphql-codegen --config=graphql-codegen-gql.yaml && graphql-codegen --config=graphql-codegen-typescript.yaml", - "typecheck": "yarn generate" + "prepare": "yarn generate" }, "dependencies": { "graphql-tag": "^2.10.4" diff --git a/packages/design-system/src/MetaTag.tsx b/packages/design-system/src/MetaTag.tsx index 00e7cc6e..2713eeb2 100644 --- a/packages/design-system/src/MetaTag.tsx +++ b/packages/design-system/src/MetaTag.tsx @@ -5,6 +5,7 @@ export const MetaTag: React.FC = ({ children, ...props }) => ( +type Props = React.ComponentProps; -export const PageContainer: React.FC = ({ - children, - ...props -}) => ( +export const PageContainer: React.FC = ({ children, ...props }) => ( {children} ); -export const FlexContainer: React.FC = ({ - children, - ...props -}) => ( +export const FlexContainer: React.FC = ({ children, ...props }) => ( {children} diff --git a/packages/web/graphql/client.ts b/packages/web/graphql/client.ts index f2089c8a..6615bc35 100644 --- a/packages/web/graphql/client.ts +++ b/packages/web/graphql/client.ts @@ -1,5 +1,7 @@ import { createClient } from 'urql'; +import { CONFIG } from '../config'; + export const client = createClient({ - url: 'https://graphql-pokemon.now.sh/', + url: CONFIG.graphqlURL, }); diff --git a/packages/web/graphql/fragments.ts b/packages/web/graphql/fragments.ts new file mode 100644 index 00000000..f6850737 --- /dev/null +++ b/packages/web/graphql/fragments.ts @@ -0,0 +1,20 @@ +import gql from 'fake-tag'; + +export const PlayerFragment = gql` + fragment PlayerFragment on Player { + id + username + totalXp + rank + ethereum_address + box_profile { + description + emoji + ethereumAddress + imageUrl + job + location + name + } + } +`; diff --git a/packages/web/graphql/getPlayer.ts b/packages/web/graphql/getPlayer.ts new file mode 100644 index 00000000..051b813d --- /dev/null +++ b/packages/web/graphql/getPlayer.ts @@ -0,0 +1,23 @@ +import gql from 'fake-tag'; + +import { GetPlayerQuery, GetPlayerQueryVariables } from './autogen/types'; +import { client } from './client'; +import { PlayerFragment } from './fragments'; + +const playerQuery = gql` + query GetPlayer($username: String!) { + Player(where: { username: { _eq: $username } }) { + ...PlayerFragment + } + } + ${PlayerFragment} +`; + +export const getPlayer = async (username: string | undefined) => { + if (!username) return null; + const { data } = await client + .query(playerQuery, { username }) + .toPromise(); + + return data?.Player[0]; +}; diff --git a/packages/web/graphql/getPlayers.ts b/packages/web/graphql/getPlayers.ts new file mode 100644 index 00000000..de10df0a --- /dev/null +++ b/packages/web/graphql/getPlayers.ts @@ -0,0 +1,32 @@ +import gql from 'fake-tag'; + +import { GetPlayersQuery, GetPlayersQueryVariables } from './autogen/types'; +import { client } from './client'; +import { PlayerFragment } from './fragments'; + +const playersQuery = gql` + query GetPlayers($limit: Int) { + Player(order_by: { totalXp: desc }, limit: $limit) { + ...PlayerFragment + } + } + ${PlayerFragment} +`; + +export const getPlayers = async (limit = 50) => { + const { data, error } = await client + .query(playersQuery, { limit }) + .toPromise(); + + if (!data) { + if (error) { + throw new Error( + `${error.message}${JSON.stringify(error.graphQLErrors, null, 2)}`, + ); + } + + return []; + } + + return data.Player; +}; diff --git a/packages/web/graphql/getPokemon.ts b/packages/web/graphql/getPokemon.ts deleted file mode 100644 index e5722317..00000000 --- a/packages/web/graphql/getPokemon.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Pokemon } from '../types/pokemon'; -import { client } from './client'; - -const pokemonQuery = ` - query firstTwentyPokemons($name: String!) { - pokemon(name: $name) { - name - image - } - } -`; - -export const getPokemon = async ( - name: string | undefined, -): Promise => { - if (!name) return null; - const { - data: { pokemon }, - } = await client.query(pokemonQuery, { name }).toPromise(); - - return pokemon; -}; diff --git a/packages/web/graphql/getPokemons.ts b/packages/web/graphql/getPokemons.ts deleted file mode 100644 index 52e92390..00000000 --- a/packages/web/graphql/getPokemons.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Pokemon } from '../types/pokemon'; -import { client } from './client'; - -const firstTwentyPokemonsQuery = ` - query firstTwentyPokemons { - pokemons(first: 20) { - image - name - } - } -`; - -export const getPokemons = async (): Promise> => { - const { - data: { pokemons }, - } = await client.query(firstTwentyPokemonsQuery).toPromise(); - - return pokemons.map((pokemon: Pokemon) => ({ - ...pokemon, - name: pokemon.name.toLowerCase(), - })); -}; diff --git a/packages/web/package.json b/packages/web/package.json index 229af465..d8c6df15 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -7,10 +7,13 @@ "build": "next build && next export", "start": "next start", "typecheck": "tsc", - "precommit": "yarn lint-staged" + "precommit": "yarn lint-staged", + "generate": "graphql-codegen --config=codegen.yml", + "prepare": "yarn generate" }, "dependencies": { "@metafam/ds": "0.1.0", + "fake-tag": "2.0.0", "graphql": "^15.0.0", "isomorphic-unfetch": "^3.0.0", "next": "latest", diff --git a/packages/web/pages/index.tsx b/packages/web/pages/index.tsx index a5911ce3..0a65523b 100644 --- a/packages/web/pages/index.tsx +++ b/packages/web/pages/index.tsx @@ -1,34 +1,60 @@ -import { Box, Heading, Image, SimpleGrid } from '@metafam/ds'; +import { Avatar, Box, Flex, Heading, MetaTag, Stack } from '@metafam/ds'; +import { PageContainer } from 'components/Container'; import { MetaLink } from 'components/Link'; -import { getPokemons } from 'graphql/getPokemons'; +import { getPlayers } from 'graphql/getPlayers'; import { InferGetStaticPropsType } from 'next'; +import React from 'react'; + +import BackgroundImage from '../public/images/login-background.jpg'; +import { getPlayerImage, getPlayerName } from '../utils/playerHelpers'; type Props = InferGetStaticPropsType; export const getStaticProps = async () => { - const pokemon = await getPokemons(); + const players = await getPlayers(); return { props: { - pokemon, + players, }, }; }; -const Home: React.FC = ({ pokemon }) => ( - - {pokemon.map((p, index) => ( - - - {p.name} - {p.name} - - - ))} - +const Home: React.FC = ({ players }) => ( + + + {players.map((p) => ( + + + + + {getPlayerName(p)} + + + {p.rank} + + XP: {Math.floor(p.totalXp)} + + + + + ))} + + ); export default Home; diff --git a/packages/web/pages/login.tsx b/packages/web/pages/login.tsx index bcd327cd..fd73172d 100644 --- a/packages/web/pages/login.tsx +++ b/packages/web/pages/login.tsx @@ -11,7 +11,10 @@ import MetaGameImage from '../public/images/metagame.png'; const Login: React.FC = () => { const [step, setStep] = useState(0); return ( - + ; + +const PlayerPage: React.FC = ({ player }) => { + if (!player) { + return ; + } + + return ( + + + + {player.username} + + + + + ); +}; + +export default PlayerPage; + +export const getStaticPaths: GetStaticPaths = async () => { + const players = await getPlayers(); + + return { + paths: players.map(({ username }) => ({ + params: { username }, + })), + fallback: false, + }; +}; + +export const getStaticProps = async ( + context: GetStaticPropsContext<{ username: string }>, +) => { + const username = context.params?.username; + const player = await getPlayer(username); + + return { + props: { + player, + }, + }; +}; diff --git a/packages/web/pages/pokemon/[name].tsx b/packages/web/pages/pokemon/[name].tsx deleted file mode 100644 index 5e80b057..00000000 --- a/packages/web/pages/pokemon/[name].tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Box, Flex, Heading, Image } from '@metafam/ds'; -import { GetStaticPaths, GetStaticProps } from 'next'; -import Error from 'next/error'; - -import { getPokemon } from '../../graphql/getPokemon'; -import { getPokemons } from '../../graphql/getPokemons'; -import { Pokemon } from '../../types/pokemon'; - -type Props = { - pokemon: Pokemon | null; -}; - -const PokemonPage: React.FC = ({ pokemon }) => { - if (!pokemon) { - return ; - } - - return ( - - - {pokemon.name} - {pokemon.name} - - - ); -}; - -export default PokemonPage; - -export const getStaticPaths: GetStaticPaths = async () => { - const pokemons = await getPokemons(); - - return { - paths: pokemons.map(({ name }) => ({ - params: { name }, - })), - fallback: false, - }; -}; - -export const getStaticProps: GetStaticProps = async ( - context, -) => { - const name = context.params?.name; - const pokemon = await getPokemon(name); - - return { - props: { - pokemon, - }, - }; -}; diff --git a/packages/web/utils/playerHelpers.ts b/packages/web/utils/playerHelpers.ts new file mode 100644 index 00000000..1cda6f69 --- /dev/null +++ b/packages/web/utils/playerHelpers.ts @@ -0,0 +1,8 @@ +import { PlayerFragmentFragment } from '../graphql/autogen/types'; + +export const getPlayerImage = (player: PlayerFragmentFragment): string => + player.box_profile?.imageUrl || + `https://avatars.dicebear.com/api/jdenticon/${player.username}.svg`; + +export const getPlayerName = (player: PlayerFragmentFragment): string => + player.box_profile?.name || player.username; diff --git a/yarn.lock b/yarn.lock index db7691d5..6b66c1c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2890,6 +2890,17 @@ auto-bind "~4.0.0" tslib "~2.0.0" +"@graphql-codegen/typescript-urql@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-urql/-/typescript-urql-2.0.1.tgz#1232e7338df4eccb9e764d2c7f53d52d22081d14" + integrity sha512-5yR1bQmf3UTxrcgZnifa5QwIL/sqadHmW0I69zkkXkvR+ao+y9VLeY/Dlfxb1fO4BE/BwEN+G5sBnRY5n6bvdg== + dependencies: + "@graphql-codegen/plugin-helpers" "^1.17.8" + "@graphql-codegen/visitor-plugin-common" "^1.17.13" + auto-bind "~4.0.0" + pascal-case "^3.1.1" + tslib "~2.0.0" + "@graphql-codegen/typescript@1.17.7", "@graphql-codegen/typescript@^1.17.7": version "1.17.7" resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-1.17.7.tgz#35d92da76d4e07b612ab1ac0bd9726fbaac1fb0d" @@ -12500,6 +12511,11 @@ fake-merkle-patricia-tree@^1.0.1: dependencies: checkpoint-store "^1.1.0" +fake-tag@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fake-tag/-/fake-tag-2.0.0.tgz#08ea5df950ef8635833186247f569e8406ffb4da" + integrity sha512-QDz+8qiNQ9AfBZ31EXlID5JIbUkU3e1nXDWk4tidFzd2gy8XJaEUW1HCuDY6DUy6t2Y0nvhD6PsUc+2WYy5w0w== + fast-deep-equal@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"