Loading save files done.
This commit is contained in:
@ -9,6 +9,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/client": "^3.13.4",
|
"@apollo/client": "^3.13.4",
|
||||||
|
"@graphql-tools/merge": "^9.0.22",
|
||||||
|
"@graphql-tools/schema": "^10.0.21",
|
||||||
"apollo-server-micro": "^3.13.0",
|
"apollo-server-micro": "^3.13.0",
|
||||||
"graphql": "^16.10.0",
|
"graphql": "^16.10.0",
|
||||||
"micro": "^10.0.1",
|
"micro": "^10.0.1",
|
||||||
|
@ -63,12 +63,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
EJS_defaultOptions = {
|
EJS_defaultOptions = {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const emulatorInit = data => {
|
const emulatorInit = data => {
|
||||||
EJS_gameUrl = `/api/v1/rom?id=${data.gameId}`;
|
EJS_gameUrl = `/api/v1/rom?id=${data.gameId}`;
|
||||||
EJS_core = data.core;
|
EJS_core = data.core;
|
||||||
EJS_gameName = data.gameName;
|
EJS_gameName = data.gameName;
|
||||||
|
EJS_externalFiles = {
|
||||||
|
'/homeplay/saves/': `/api/v1/save?id=${data.gameId}`
|
||||||
|
}
|
||||||
|
|
||||||
scriptElement = document.createElement('script');
|
scriptElement = document.createElement('script');
|
||||||
scriptElement.src = 'https://cdn.emulatorjs.org/stable/data/loader.js';
|
scriptElement.src = 'https://cdn.emulatorjs.org/stable/data/loader.js';
|
||||||
@ -103,6 +107,31 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
EJS_ready = () => {
|
EJS_ready = () => {
|
||||||
|
// Modify the EJS stuff
|
||||||
|
const oldGetRetroArchCfg = window.EJS_GameManager.prototype.getRetroArchCfg;
|
||||||
|
const oldMountFileSystems = window.EJS_GameManager.prototype.mountFileSystems;
|
||||||
|
|
||||||
|
EJS_GameManager.prototype.getRetroArchCfg = function() {
|
||||||
|
const ejsConfig = oldGetRetroArchCfg.call(this);
|
||||||
|
|
||||||
|
// Parse back into key value pair
|
||||||
|
const raConfig = ejsConfig.split('\n').reduce((acc, line) => {
|
||||||
|
if(!line.length) return acc;
|
||||||
|
const [key, value] = line.split('=');
|
||||||
|
acc[key.trim()] = value.trim();
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
// Add new options
|
||||||
|
raConfig['savefiles_in_content_dir'] = false;
|
||||||
|
raConfig['sort_savefiles_by_content_enable'] = false;
|
||||||
|
raConfig['sort_savefiles_enable'] = false;
|
||||||
|
raConfig['savefile_directory'] = '/homeplay/saves';
|
||||||
|
|
||||||
|
// Return back as RA config string
|
||||||
|
return Object.entries(raConfig).map(([k, v]) => `${k} = ${v}`).join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
send({ message: 'ready' });
|
send({ message: 'ready' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,11 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import styles from './Emulator.module.scss';
|
import styles from './Emulator.module.scss';
|
||||||
|
import { GAME_SYSTEM_CORES, GameSystem, GameSystemCore } from '@/lib/game';
|
||||||
type EmulatorSystem = (
|
|
||||||
'gb' | 'gbc' | 'gba'
|
|
||||||
);
|
|
||||||
|
|
||||||
type EmulatorCore = (
|
|
||||||
'gambatte' | 'mgba'
|
|
||||||
);
|
|
||||||
|
|
||||||
type SendEmulatorMessageInit = {
|
type SendEmulatorMessageInit = {
|
||||||
message:'init';
|
message:'init';
|
||||||
core:EmulatorCore;
|
core:GameSystemCore;
|
||||||
system:EmulatorSystem;
|
system:GameSystem;
|
||||||
gameId:string;
|
gameId:string;
|
||||||
gameName:string;
|
gameName:string;
|
||||||
}
|
}
|
||||||
@ -35,13 +28,9 @@ type ReceiveEmulatorMessage = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
export type EmulatorProps = {
|
export type EmulatorProps = {
|
||||||
system:EmulatorSystem;
|
system:GameSystem;
|
||||||
};
|
gameId:string;
|
||||||
|
gameName:string;
|
||||||
const EMULATOR_SYSTEM_CORES:{ [key in EmulatorSystem]:EmulatorCore } = {
|
|
||||||
'gb': 'gambatte',
|
|
||||||
'gbc': 'gambatte',
|
|
||||||
'gba': 'mgba'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Emulator:React.FC<EmulatorProps> = props => {
|
export const Emulator:React.FC<EmulatorProps> = props => {
|
||||||
@ -58,13 +47,13 @@ export const Emulator:React.FC<EmulatorProps> = props => {
|
|||||||
|
|
||||||
send({
|
send({
|
||||||
message: 'init',
|
message: 'init',
|
||||||
core: EMULATOR_SYSTEM_CORES[props.system],
|
core: GAME_SYSTEM_CORES[props.system],
|
||||||
system: props.system,
|
system: props.system,
|
||||||
gameId: '0',
|
gameId: props.gameId,
|
||||||
gameName: 'Pokemon - Crystal Version'
|
gameName: props.gameName
|
||||||
});
|
});
|
||||||
|
|
||||||
window.onmessage = (e) => {
|
window.onmessage = e => {
|
||||||
if(!e.data) {
|
if(!e.data) {
|
||||||
console.error('Invalid message received:', e.data);
|
console.error('Invalid message received:', e.data);
|
||||||
return;
|
return;
|
||||||
|
77
src/graphql/game.ts
Normal file
77
src/graphql/game.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { GAME_SYSTEMS } from "@/lib/game";
|
||||||
|
import { ApolloError, gql } from "apollo-server-micro";
|
||||||
|
import { pageTypeDefs } from "./page";
|
||||||
|
|
||||||
|
export const gameTypeDefs = gql`
|
||||||
|
${pageTypeDefs}
|
||||||
|
|
||||||
|
enum GameSystem {
|
||||||
|
${GAME_SYSTEMS.join('\n')}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Game {
|
||||||
|
id: ID!
|
||||||
|
name: String!
|
||||||
|
system: GameSystem!
|
||||||
|
}
|
||||||
|
|
||||||
|
input GameFilter {
|
||||||
|
system: GameSystem
|
||||||
|
}
|
||||||
|
|
||||||
|
enum GameOrderBy {
|
||||||
|
NAME_ASC
|
||||||
|
NAME_DESC
|
||||||
|
}
|
||||||
|
|
||||||
|
type GameEdge {
|
||||||
|
node: Game
|
||||||
|
cursor: String
|
||||||
|
}
|
||||||
|
|
||||||
|
type GameConnection {
|
||||||
|
edges: [GameEdge]
|
||||||
|
pageInfo: PageInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type Query {
|
||||||
|
game(id: ID!): Game
|
||||||
|
games(first: Int!, after: String, orderBy: GameOrderBy, filter: GameFilter): GameConnection
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const GAME = {
|
||||||
|
id: '0',
|
||||||
|
name: 'Pokemon Crystal Version',
|
||||||
|
system: 'gbc'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const gameResolvers = {
|
||||||
|
Query: {
|
||||||
|
game: (_:any, { id }:{ id:string }) => {
|
||||||
|
if(!id) throw new ApolloError('Invalid ID');
|
||||||
|
|
||||||
|
switch(id) {
|
||||||
|
case '0':
|
||||||
|
return GAME;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
games: (_: any, { first, after, orderBy, filter }: { first: number, after?: string, orderBy?: string, filter?: { system?: string } }) => {
|
||||||
|
if(first > 100) throw new ApolloError('first cannot be greater than 100');
|
||||||
|
return {
|
||||||
|
edges: [
|
||||||
|
{ node: GAME }
|
||||||
|
],
|
||||||
|
pageInfo: {
|
||||||
|
hasNextPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
startCursor: '0'
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
10
src/graphql/page.ts
Normal file
10
src/graphql/page.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { gql } from "apollo-server-micro";
|
||||||
|
|
||||||
|
export const pageTypeDefs = gql`
|
||||||
|
type PageInfo {
|
||||||
|
endCursor: String
|
||||||
|
hasNextPage: Boolean!
|
||||||
|
hasPreviousPage: Boolean!
|
||||||
|
startCursor: String
|
||||||
|
}
|
||||||
|
`;
|
@ -1,13 +1,17 @@
|
|||||||
import { gql } from 'apollo-server-micro';
|
import { gql } from 'apollo-server-micro';
|
||||||
|
import { gameTypeDefs, gameResolvers } from './game';
|
||||||
|
import { mergeTypeDefs } from '@graphql-tools/merge';
|
||||||
|
import { makeExecutableSchema } from '@graphql-tools/schema';
|
||||||
|
|
||||||
export const typeDefs = gql`
|
export const typeDefs = mergeTypeDefs([
|
||||||
type Query {
|
gameTypeDefs
|
||||||
hello: String
|
]);
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const resolvers = {
|
export const resolvers = [
|
||||||
Query: {
|
gameResolvers
|
||||||
hello: () => 'Very cool!',
|
];
|
||||||
},
|
|
||||||
};
|
export const schema = makeExecutableSchema({
|
||||||
|
typeDefs,
|
||||||
|
resolvers
|
||||||
|
});
|
29
src/lib/fragment.ts
Normal file
29
src/lib/fragment.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { gql } from "apollo-server-micro";
|
||||||
|
import { DocumentNode } from "graphql";
|
||||||
|
|
||||||
|
type FragmentDefinition<T> = {
|
||||||
|
query:DocumentNode;
|
||||||
|
name:string;
|
||||||
|
needsInclude:boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createFragment = <T>(params:{
|
||||||
|
query:DocumentNode;
|
||||||
|
name:string;
|
||||||
|
needsInclude?:boolean;
|
||||||
|
}):FragmentDefinition<T> => {
|
||||||
|
return {
|
||||||
|
...params,
|
||||||
|
needsInclude: typeof params.needsInclude === 'undefined' ? true : params.needsInclude
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const includeFragment = <T>(fragment:FragmentDefinition<T>) => {
|
||||||
|
if(!fragment.needsInclude) return '';
|
||||||
|
return fragment.query;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const extendFragment = <T>(fragment:FragmentDefinition<T>) => {
|
||||||
|
if(!fragment.needsInclude) return fragment.query;
|
||||||
|
return `...${fragment.name}`;
|
||||||
|
}
|
45
src/lib/game.ts
Normal file
45
src/lib/game.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { createFragment } from "@/lib/fragment";
|
||||||
|
import { gql } from "apollo-server-micro";
|
||||||
|
|
||||||
|
export const GAME_SYSTEM_CORES = <const>{
|
||||||
|
'gb': 'gambatte',
|
||||||
|
'gbc': 'gambatte',
|
||||||
|
'gba': 'mgba'
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GameSystem = keyof typeof GAME_SYSTEM_CORES;
|
||||||
|
export type GameSystemCore = typeof GAME_SYSTEM_CORES[GameSystem];
|
||||||
|
|
||||||
|
export type GameLight = {
|
||||||
|
id:string;
|
||||||
|
name:string;
|
||||||
|
system:GameSystem;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GameLightFragment = createFragment<GameLight>({
|
||||||
|
name: `GameLight`,
|
||||||
|
query: gql`
|
||||||
|
fragment GameLight on Game {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
system
|
||||||
|
}
|
||||||
|
`
|
||||||
|
});
|
||||||
|
|
||||||
|
export type GameHeavy = {
|
||||||
|
id:string;
|
||||||
|
name:string;
|
||||||
|
system:GameSystem;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GameHeavyFragment = createFragment<GameHeavy>({
|
||||||
|
name: `GameHeavy`,
|
||||||
|
query: gql`
|
||||||
|
fragment GameHeavy on Game {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
system
|
||||||
|
}
|
||||||
|
`
|
||||||
|
});
|
30
src/lib/page.ts
Normal file
30
src/lib/page.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { gql } from "apollo-server-micro";
|
||||||
|
import { DocumentNode } from "graphql";
|
||||||
|
|
||||||
|
export type Paginated<T> = {
|
||||||
|
edges: { node: T }[];
|
||||||
|
pageInfo:{
|
||||||
|
startCursor:string|null;
|
||||||
|
endCursor:string|null;
|
||||||
|
hasNextPage:boolean;
|
||||||
|
hasPreviousPage:boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const paginationQuery = (nodeDefs:DocumentNode|string) => {
|
||||||
|
return `
|
||||||
|
edges {
|
||||||
|
cursor
|
||||||
|
node {
|
||||||
|
${nodeDefs}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pageInfo {
|
||||||
|
startCursor
|
||||||
|
endCursor
|
||||||
|
hasNextPage
|
||||||
|
hasPreviousPage
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
};
|
13
src/pages/404.tsx
Normal file
13
src/pages/404.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export type Error404Props = {
|
||||||
|
error:404;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Error404:React.FC<Error404Props> = props => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Error 404</h1>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Error404;
|
14
src/pages/500.tsx
Normal file
14
src/pages/500.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export type Error500Props = {
|
||||||
|
code:500;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Error500:React.FC<Error500Props> = props => {
|
||||||
|
console.log('props', props);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Error 500</h1>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Error500;
|
@ -1,8 +1,8 @@
|
|||||||
import { typeDefs, resolvers } from "@/graphql/schema";
|
import { typeDefs, schema } from "@/graphql/schema";
|
||||||
import { ApolloServer } from "apollo-server-micro";
|
import { ApolloServer } from "apollo-server-micro";
|
||||||
import { NextApiRequest, NextApiResponse } from "next";
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
const apolloServer = new ApolloServer({ typeDefs, resolvers });
|
const apolloServer = new ApolloServer({ typeDefs, schema });
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
api: {
|
api: {
|
||||||
|
44
src/pages/api/v1/save.ts
Normal file
44
src/pages/api/v1/save.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { NextApiHandler } from "next";
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
const PATH_SAVES = path.resolve('.', 'data', 'saves');
|
||||||
|
|
||||||
|
const handler:NextApiHandler = async (req, res) => {
|
||||||
|
if (req.method !== 'GET') {
|
||||||
|
res.setHeader('Allow', ['GET']);
|
||||||
|
res.status(405).end(`Method Not Allowed`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID query param
|
||||||
|
if(!req.query.id || typeof req.query.id !== 'string') {
|
||||||
|
res.status(404).end(`Not Found`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id } = req.query;
|
||||||
|
if(!id) {
|
||||||
|
res.status(404).end(`Not Found`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does rom exist?
|
||||||
|
const pathSave = path.resolve(PATH_SAVES, `${id}.zip`);
|
||||||
|
if(!fs.existsSync(pathSave)) {
|
||||||
|
res.status(404).end(`Not Found`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read save
|
||||||
|
try {
|
||||||
|
const romStream = fs.createReadStream(pathSave);
|
||||||
|
res.setHeader('Content-Type', 'application/octet-stream');
|
||||||
|
res.setHeader('Content-Disposition', `attachment; filename="${id}.zip"`);
|
||||||
|
romStream.pipe(res);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).end(`Internal Server Error`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default handler;
|
@ -1,4 +1,9 @@
|
|||||||
import { GetServerSideProps, GetStaticPaths, GetStaticProps } from 'next';
|
import { apiClientGet } from '@/lib/apiClient';
|
||||||
|
import { includeFragment, extendFragment } from '@/lib/fragment';
|
||||||
|
import { GameHeavy, GameLightFragment } from '@/lib/game';
|
||||||
|
import { Error500, Error500Props } from '@/pages/500';
|
||||||
|
import { gql } from 'apollo-server-micro';
|
||||||
|
import { GetServerSideProps } from 'next';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
type PageParams = {
|
type PageParams = {
|
||||||
@ -6,31 +11,48 @@ type PageParams = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PageProps = {
|
type PageProps = {
|
||||||
id:string;
|
game:GameHeavy;
|
||||||
}
|
} | Error500Props;
|
||||||
|
|
||||||
export const getServerSideProps:GetServerSideProps<> = async ({ params }) => {
|
export const getServerSideProps:GetServerSideProps<PageProps, PageParams> = async ({ params }) => {
|
||||||
const { id } = params as PageParams;
|
try {
|
||||||
|
if(!params || !params.id) return { notFound: true };
|
||||||
|
|
||||||
// if(id !== '0') {
|
const client = await apiClientGet();
|
||||||
// return {
|
const res = await client.query<{ game:GameHeavy }>({
|
||||||
// notFound: true
|
variables: { id: params.id },
|
||||||
// };
|
query: gql`
|
||||||
// }
|
${includeFragment(GameLightFragment)}
|
||||||
|
|
||||||
return {
|
query getGame($id: ID!) {
|
||||||
props: {
|
game(id: $id) {
|
||||||
id
|
${extendFragment(GameLightFragment)}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
if(!res.data.game) return { notFound: true };
|
||||||
|
|
||||||
|
return { props: { ...res.data } };
|
||||||
|
} catch(e) {
|
||||||
|
console.error('Error', e);
|
||||||
|
return { props: { code: 500 } };
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Page:React.FC<PageProps> = ({ id }) => {
|
export const Page:React.FC<PageProps> = props => {
|
||||||
|
if('code' in props) {
|
||||||
|
if(props.code === 500) return <Error500 {...props} />;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { game } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>Viewing Game ID: {id}</h1>
|
<h1>{ game.name }</h1>
|
||||||
|
|
||||||
<Link href={`/games/${id}/play`}>
|
<Link href={`/games/${game.id}/play`}>
|
||||||
Play Game
|
Play Game
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,7 +27,9 @@ export const Page:React.FC<PageProps> = ({ id }) => {
|
|||||||
|
|
||||||
<div className={styles.play__emulator}>
|
<div className={styles.play__emulator}>
|
||||||
<Emulator
|
<Emulator
|
||||||
system='gbc'
|
system="gbc"
|
||||||
|
gameId="0"
|
||||||
|
gameName="Pokemon Crystal Version"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,25 +1,73 @@
|
|||||||
import { GetServerSideProps, GetStaticPaths, GetStaticProps } from 'next';
|
import { GetServerSideProps } from 'next';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import { apiClientGet } from '@/lib/apiClient';
|
||||||
|
import { gql } from 'apollo-server-micro';
|
||||||
|
import { GameLight, GameLightFragment } from '@/lib/game';
|
||||||
|
import Error500, { Error500Props } from '../500';
|
||||||
|
import { extendFragment, includeFragment } from '@/lib/fragment';
|
||||||
|
import { Paginated, paginationQuery } from '@/lib/page';
|
||||||
|
|
||||||
type PageProps = {
|
type Game = {
|
||||||
}
|
id: string;
|
||||||
|
name: string;
|
||||||
export const getServerSideProps:GetServerSideProps = async ({ params }) => {
|
system: string;
|
||||||
return {
|
|
||||||
props: {
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Page:React.FC<PageProps> = ({ }) => {
|
type PageProps = {
|
||||||
|
games:Paginated<GameLight>|null;
|
||||||
|
} | Error500Props;
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps<PageProps> = async () => {
|
||||||
|
try {
|
||||||
|
const client = await apiClientGet();
|
||||||
|
const res = await client.query<{ games:Paginated<GameLight> }>({
|
||||||
|
variables: { },
|
||||||
|
query: gql`
|
||||||
|
${includeFragment(GameLightFragment)}
|
||||||
|
query getGames {
|
||||||
|
games(first: 100) {
|
||||||
|
${paginationQuery(extendFragment(GameLightFragment))}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!res || !res.data || !res.data.games) {
|
||||||
|
return { props: { games:null } };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { props: { games: res.data.games } };
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error', e);
|
||||||
|
return { props: { code: 500 } };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Page: React.FC<PageProps> = props => {
|
||||||
|
if ('code' in props) {
|
||||||
|
if (props.code === 500) return <Error500 {...props} />;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { games } = props;
|
||||||
|
if(!games) {
|
||||||
|
return <div>No games found</div>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>Games</h1>
|
<h1>Games</h1>
|
||||||
<div>
|
<ul>
|
||||||
<Link href="/games/0">
|
{games.edges.map(({ node }) => {
|
||||||
Test Game
|
return (
|
||||||
</Link>
|
<li key={node.id}>
|
||||||
</div>
|
<Link href={`/games/${node.id}`}>
|
||||||
|
{node.name} ({node.system})
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -4,9 +4,11 @@ import { GetServerSideProps } from 'next';
|
|||||||
import { apiClientGet } from '@/lib/apiClient';
|
import { apiClientGet } from '@/lib/apiClient';
|
||||||
import { ApolloQueryResult, gql } from '@apollo/client';
|
import { ApolloQueryResult, gql } from '@apollo/client';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import { GameLightFragment, GameLight } from '@/lib/game';
|
||||||
|
import { extendFragment, includeFragment } from '@/lib/fragment';
|
||||||
|
|
||||||
type PageData = {
|
type PageData = {
|
||||||
hello:string;
|
game:GameLight;
|
||||||
}
|
}
|
||||||
|
|
||||||
type PageProps = ApolloQueryResult<PageData>
|
type PageProps = ApolloQueryResult<PageData>
|
||||||
@ -16,10 +18,17 @@ export const getServerSideProps:GetServerSideProps<PageProps> = async () => {
|
|||||||
|
|
||||||
const res = await client.query<PageData>({
|
const res = await client.query<PageData>({
|
||||||
query: gql`
|
query: gql`
|
||||||
query {
|
${includeFragment(GameLightFragment)}
|
||||||
hello
|
|
||||||
|
query getGame($id: ID!) {
|
||||||
|
game(id: $id) {
|
||||||
|
${extendFragment(GameLightFragment)}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
|
variables: {
|
||||||
|
id: '0'
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -34,11 +43,10 @@ const HomePage: React.FC<PageProps> = props => {
|
|||||||
<title>HomePlay, your personal video game library</title>
|
<title>HomePlay, your personal video game library</title>
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<Link href="/game/1/play">
|
<Link href="/games">
|
||||||
Play Game
|
Games
|
||||||
</Link>
|
</Link>
|
||||||
|
{ JSON.stringify(props) }
|
||||||
{ props.data?.hello }
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
0
src/queries/game.ts
Normal file
0
src/queries/game.ts
Normal file
49
yarn.lock
49
yarn.lock
@ -152,6 +152,14 @@
|
|||||||
"@graphql-tools/utils" "^9.2.1"
|
"@graphql-tools/utils" "^9.2.1"
|
||||||
tslib "^2.4.0"
|
tslib "^2.4.0"
|
||||||
|
|
||||||
|
"@graphql-tools/merge@^9.0.22":
|
||||||
|
version "9.0.22"
|
||||||
|
resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-9.0.22.tgz#f8d316604360021c71d8a9397784f4eb178741fe"
|
||||||
|
integrity sha512-bjOs9DlTbo1Yz2UzQcJ78Dn9/pKyY2zNaoqNLfRTTSkO56QFkvqhfjQuqJcqu+V3rtaB2o0VMpWaY6JT8ZTvQA==
|
||||||
|
dependencies:
|
||||||
|
"@graphql-tools/utils" "^10.8.4"
|
||||||
|
tslib "^2.4.0"
|
||||||
|
|
||||||
"@graphql-tools/mock@^8.1.2":
|
"@graphql-tools/mock@^8.1.2":
|
||||||
version "8.7.20"
|
version "8.7.20"
|
||||||
resolved "https://registry.yarnpkg.com/@graphql-tools/mock/-/mock-8.7.20.tgz#c83ae0f1940d194a3982120c9c85f3ac6b4f7f20"
|
resolved "https://registry.yarnpkg.com/@graphql-tools/mock/-/mock-8.7.20.tgz#c83ae0f1940d194a3982120c9c85f3ac6b4f7f20"
|
||||||
@ -162,6 +170,15 @@
|
|||||||
fast-json-stable-stringify "^2.1.0"
|
fast-json-stable-stringify "^2.1.0"
|
||||||
tslib "^2.4.0"
|
tslib "^2.4.0"
|
||||||
|
|
||||||
|
"@graphql-tools/schema@^10.0.21":
|
||||||
|
version "10.0.21"
|
||||||
|
resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-10.0.21.tgz#197584222c2fba507dfacf26dce96b0b1f67b746"
|
||||||
|
integrity sha512-AECSlNnD0WNxICwfJs93gYn2oHxPmztn1MYBETIQXrJJcymfD6BoUrDlYPa6F27RzRc+gbPZPHMWL26uujfKBg==
|
||||||
|
dependencies:
|
||||||
|
"@graphql-tools/merge" "^9.0.22"
|
||||||
|
"@graphql-tools/utils" "^10.8.4"
|
||||||
|
tslib "^2.4.0"
|
||||||
|
|
||||||
"@graphql-tools/schema@^8.0.0":
|
"@graphql-tools/schema@^8.0.0":
|
||||||
version "8.5.1"
|
version "8.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-8.5.1.tgz#c2f2ff1448380919a330312399c9471db2580b58"
|
resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-8.5.1.tgz#c2f2ff1448380919a330312399c9471db2580b58"
|
||||||
@ -189,6 +206,17 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
tslib "^2.4.0"
|
tslib "^2.4.0"
|
||||||
|
|
||||||
|
"@graphql-tools/utils@^10.8.4":
|
||||||
|
version "10.8.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-10.8.4.tgz#59dc5ea949e26c6bae28b91ad9f5c00e992de072"
|
||||||
|
integrity sha512-HpHBgcmLIE79jWk1v5Bm0Eb8MaPiwSJT/Iy5xIJ+GMe7yAKpCYrbjf7wb+UMDMkLkfEryvo3syCx8k+TMAZ9bA==
|
||||||
|
dependencies:
|
||||||
|
"@graphql-typed-document-node/core" "^3.1.1"
|
||||||
|
"@whatwg-node/promise-helpers" "^1.0.0"
|
||||||
|
cross-inspect "1.0.1"
|
||||||
|
dset "^3.1.4"
|
||||||
|
tslib "^2.4.0"
|
||||||
|
|
||||||
"@graphql-tools/utils@^9.2.1":
|
"@graphql-tools/utils@^9.2.1":
|
||||||
version "9.2.1"
|
version "9.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-9.2.1.tgz#1b3df0ef166cfa3eae706e3518b17d5922721c57"
|
resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-9.2.1.tgz#1b3df0ef166cfa3eae706e3518b17d5922721c57"
|
||||||
@ -568,6 +596,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
csstype "^3.0.2"
|
csstype "^3.0.2"
|
||||||
|
|
||||||
|
"@whatwg-node/promise-helpers@^1.0.0":
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@whatwg-node/promise-helpers/-/promise-helpers-1.2.4.tgz#4f62d48e27059e8e655add21faa82d177abf5a0c"
|
||||||
|
integrity sha512-daEUfaHbaMuAcor+FPAVK+pOCSzsAYhK6LN1y81EcakdqQEPQvjm74PTmfwfv8POg8pw4RyCv9LXB1e+mQDwqg==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.6.3"
|
||||||
|
|
||||||
"@wry/caches@^1.0.0":
|
"@wry/caches@^1.0.0":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@wry/caches/-/caches-1.0.1.tgz#8641fd3b6e09230b86ce8b93558d44cf1ece7e52"
|
resolved "https://registry.yarnpkg.com/@wry/caches/-/caches-1.0.1.tgz#8641fd3b6e09230b86ce8b93558d44cf1ece7e52"
|
||||||
@ -765,6 +800,13 @@ content-type@1.0.4:
|
|||||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
||||||
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
||||||
|
|
||||||
|
cross-inspect@1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/cross-inspect/-/cross-inspect-1.0.1.tgz#15f6f65e4ca963cf4cc1a2b5fef18f6ca328712b"
|
||||||
|
integrity sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.4.0"
|
||||||
|
|
||||||
cssfilter@0.0.10:
|
cssfilter@0.0.10:
|
||||||
version "0.0.10"
|
version "0.0.10"
|
||||||
resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae"
|
resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae"
|
||||||
@ -790,6 +832,11 @@ detect-libc@^2.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700"
|
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700"
|
||||||
integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==
|
integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==
|
||||||
|
|
||||||
|
dset@^3.1.4:
|
||||||
|
version "3.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.4.tgz#f8eaf5f023f068a036d08cd07dc9ffb7d0065248"
|
||||||
|
integrity sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==
|
||||||
|
|
||||||
fast-json-stable-stringify@^2.1.0:
|
fast-json-stable-stringify@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
||||||
@ -1205,7 +1252,7 @@ ts-invariant@^0.10.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
tslib "^2.1.0"
|
tslib "^2.1.0"
|
||||||
|
|
||||||
tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.8.0:
|
tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.6.3, tslib@^2.8.0:
|
||||||
version "2.8.1"
|
version "2.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
|
||||||
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
|
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
|
||||||
|
Reference in New Issue
Block a user