Skip to content

Commit

Permalink
Add the room list
Browse files Browse the repository at this point in the history
  • Loading branch information
fatfisz committed May 16, 2020
1 parent 961f09d commit 4e90248
Show file tree
Hide file tree
Showing 16 changed files with 350 additions and 173 deletions.
3 changes: 2 additions & 1 deletion client/components/Drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useContext, ReactNode } from 'react';

import { Nick } from 'components/Nick';
import { SocketContext } from 'components/SocketContext';
import { translucentBlack } from 'config/colors';

export function Drawer({
options,
Expand Down Expand Up @@ -50,7 +51,7 @@ export function Drawer({
<style jsx>{`
.drawer {
aligin-items: start;
background: #000000a0;
background: ${translucentBlack};
cursor: default;
display: grid;
gap: 16px;
Expand Down
98 changes: 21 additions & 77 deletions client/components/SocketContext.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,46 @@
import {
ContextType,
createContext,
DependencyList,
ReactNode,
useCallback,
useEffect,
useState,
} from 'react';
import io from 'socket.io-client';
import { ContextType, createContext, ReactNode, useState } from 'react';

import { serverPort } from 'shared/serverPort';
import { RoomState } from 'shared/types/RoomState';
import { ServerEvents } from 'shared/types/ServerEvents';
import { storageIdKey } from 'config/storage';
import {
ClientSocket,
EmittedEvents,
ReceivedEvents,
} from 'shared/types/Socket';
import { validators } from 'validators';
useSocket,
useSocketEmitter,
useSocketListener,
} from 'hooks/useSocket';
import { LobbyState } from 'shared/types/LobbyState';
import { RoomOptions } from 'shared/types/RoomOptions';
import { RoomState } from 'shared/types/RoomState';

export const SocketContext = createContext<{
name: string;
currentSessionId: string;
lobbyState: LobbyState | undefined;
roomState: RoomState | undefined;
addNextCard(): void;
joinRoom(): void;
createRoom(options: RoomOptions): void;
joinRoom(roomId: string): void;
selectSet(cards: Readonly<number[]>): void;
setName(name: string): void;
}>({
name: '',
currentSessionId: '',
lobbyState: undefined,
roomState: undefined,
addNextCard() {},
createRoom() {},
joinRoom() {},
selectSet() {},
setName() {},
});

const storageIdKey = 'sessionId';
const initialSession = {
id: '',
name: '',
};

export function SocketContextProvider({ children }: { children: ReactNode }) {
export function SocketProvider({ children }: { children: ReactNode }) {
const socket = useSocket();
const [session, setSession] = useState(initialSession);
const [lobbyState, setLobbyState] = useState<LobbyState | undefined>();
const [roomState, setRoomState] = useState<RoomState | undefined>();

useSocketListener(socket, 'session estabilished', (session) => {
Expand All @@ -54,13 +49,17 @@ export function SocketContextProvider({ children }: { children: ReactNode }) {
socket?.emit('confirm session', session.id);
});

useSocketListener(socket, 'room state changed', setRoomState, [session.id]);
useSocketListener(socket, 'lobby state changed', setLobbyState);

useSocketListener(socket, 'room state changed', setRoomState);

const value: ContextType<typeof SocketContext> = {
currentSessionId: session.id,
name: session.name,
lobbyState,
roomState,
addNextCard: useSocketEmitter(socket, 'add next card'),
createRoom: useSocketEmitter(socket, 'create room'),
joinRoom: useSocketEmitter(socket, 'join room'),
selectSet: useSocketEmitter(socket, 'select set'),
setName: useSocketEmitter(socket, 'set name'),
Expand All @@ -70,58 +69,3 @@ export function SocketContextProvider({ children }: { children: ReactNode }) {
<SocketContext.Provider value={value}>{children}</SocketContext.Provider>
);
}

function useSocket() {
const [socket, setSocket] = useState<ClientSocket<ServerEvents>>();

useEffect(() => {
const sessionId = localStorage.getItem(storageIdKey) ?? '';
const socket = io(`localhost:${serverPort}`, {
transports: ['websocket'],
query: { sessionId },
});

setSocket(socket);

return () => {
socket.close();
};
}, []);

return socket;
}

function useSocketListener<EventName extends keyof EmittedEvents<ServerEvents>>(
socket: ClientSocket<ServerEvents> | undefined,
name: EventName,
listener: (...args: EmittedEvents<ServerEvents>[EventName]) => void,
deps: DependencyList = []
) {
useEffect(() => {
function listenerWithValidator(
...args: EmittedEvents<ServerEvents>[EventName]
) {
if (process.env.NODE_ENV !== 'production') {
console.log(`[${name}]`, ...args);
}
validators[name](...args);
listener(...args);
}

socket?.on(name, listenerWithValidator);
return () => {
socket?.off(name, listenerWithValidator);
};
}, [socket, ...deps]);
}

function useSocketEmitter<EventName extends keyof ReceivedEvents<ServerEvents>>(
socket: ClientSocket<ServerEvents> | undefined,
name: EventName
) {
return useCallback(
(...args: ReceivedEvents<ServerEvents>[EventName]) =>
socket?.emit(name, ...args),
[socket]
);
}
2 changes: 2 additions & 0 deletions client/config/colors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ export const blue = '#2bb8e3';
export const lightGrey = '#ccc';

export const darkGrey = '#444';

export const translucentBlack = '#000000a0';
1 change: 1 addition & 0 deletions client/config/storage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const storageIdKey = 'sessionId';
68 changes: 68 additions & 0 deletions client/hooks/useSocket.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { DependencyList, useCallback, useEffect, useState } from 'react';
import io from 'socket.io-client';

import { storageIdKey } from 'config/storage';
import { serverPort } from 'shared/serverPort';
import { ServerEvents } from 'shared/types/ServerEvents';
import {
ClientSocket,
EmittedEvents,
ReceivedEvents,
} from 'shared/types/Socket';
import { validators } from 'validators';

export function useSocket() {
const [socket, setSocket] = useState<ClientSocket<ServerEvents>>();

useEffect(() => {
const sessionId = localStorage.getItem(storageIdKey) ?? '';
const socket = io(`localhost:${serverPort}`, {
transports: ['websocket'],
query: { sessionId },
});

setSocket(socket);

return () => {
socket.close();
};
}, []);

return socket;
}

export function useSocketListener<
EventName extends keyof EmittedEvents<ServerEvents>
>(
socket: ClientSocket<ServerEvents> | undefined,
name: EventName,
listener: (...args: EmittedEvents<ServerEvents>[EventName]) => void,
deps: DependencyList = []
) {
useEffect(() => {
function listenerWithValidator(
...args: EmittedEvents<ServerEvents>[EventName]
) {
if (process.env.NODE_ENV !== 'production') {
console.log(`[${name}]`, ...args);
}
validators[name](...args);
listener(...args);
}

socket?.on(name, listenerWithValidator);
return () => {
socket?.off(name, listenerWithValidator);
};
}, [socket, ...deps]);
}

export function useSocketEmitter<
EventName extends keyof ReceivedEvents<ServerEvents>
>(socket: ClientSocket<ServerEvents> | undefined, name: EventName) {
return useCallback(
(...args: ReceivedEvents<ServerEvents>[EventName]) =>
socket?.emit(name, ...args),
[socket]
);
}
32 changes: 32 additions & 0 deletions client/utils/ajvHelpers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export function object(object: {}) {
return {
type: 'object',
properties: object,
required: Object.keys(object),
};
}

export function array(items: any) {
return {
type: 'array',
items,
};
}

export function boolean() {
return {
type: 'boolean',
};
}

export function number() {
return {
type: 'number',
};
}

export function string() {
return {
type: 'string',
};
}
87 changes: 33 additions & 54 deletions client/validators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,67 +2,46 @@ import Ajv, { ErrorObject } from 'ajv';

import { EmittedEvents } from 'shared/types/Socket';
import { ServerEvents } from 'shared/types/ServerEvents';
import { array, boolean, number, object, string } from 'utils/ajvHelpers';

const ajv = new Ajv();

export const validators: Record<
keyof EmittedEvents<ServerEvents>,
(...args: any[]) => void
> = {
'room state changed': getValidator({
type: 'object',
properties: {
cards: {
type: 'array',
items: {
type: 'number',
},
},
options: {
type: 'object',
properties: {
autoAddCard: {
type: 'boolean',
},
},
required: ['autoAddCard'],
},
remainingCardCount: {
type: 'number',
},
scores: {
type: 'array',
items: {
type: 'object',
properties: {
sessionId: {
type: 'string',
},
name: {
type: 'string',
},
score: {
type: 'number',
},
},
required: ['sessionId', 'name', 'score'],
},
},
},
required: ['cards', 'remainingCardCount', 'scores'],
}),
'session estabilished': getValidator({
type: 'object',
properties: {
id: {
type: 'string',
},
name: {
type: 'string',
},
},
required: ['id', 'name'],
}),
'lobby state changed': getValidator(
object({
rooms: array(
object({
id: string(),
name: string(),
})
),
})
),
'room state changed': getValidator(
object({
cards: array(number()),
options: object({
autoAddCard: boolean(),
}),
remainingCardCount: number(),
scores: array(
object({
sessionId: string(),
name: string(),
score: number(),
})
),
})
),
'session estabilished': getValidator(
object({
id: string(),
name: string(),
})
),
};

function getValidator(...schemas: any[]) {
Expand Down
Loading

0 comments on commit 4e90248

Please sign in to comment.