Skip to content

Commit

Permalink
Use firestore as db
Browse files Browse the repository at this point in the history
rename

lowercase

auth
  • Loading branch information
sam-hu committed Nov 21, 2023
1 parent 20ad0e6 commit 3438ee7
Show file tree
Hide file tree
Showing 8 changed files with 1,276 additions and 491 deletions.
15 changes: 15 additions & 0 deletions netlify/firestore/firestore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* eslint-disable import/prefer-default-export */
import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';

const firebaseApp = initializeApp({
apiKey: process.env.FIRESTORE_KEY,
authDomain: `${process.env.FIRESTORE_PROJECT}.firebaseapp.com`,
projectId: process.env.FIRESTORE_PROJECT,
storageBucket: `${process.env.FIRESTORE_PROJECT}.appspot.com`,
messagingSenderId: '690303112259',
appId: '1:690303112259:web:d5331dcec578b687c5b0f6',
measurementId: 'G-3TEM8CLL4P',
});

export const db = getFirestore(firebaseApp);
29 changes: 29 additions & 0 deletions netlify/functions/load-game/load-game.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* eslint-disable import/prefer-default-export */
import { Handler } from '@netlify/functions';
import { doc, getDoc } from 'firebase/firestore';
import { db } from '../../firestore/firestore';

export const handler: Handler = async (event) => {
try {
const id = event.queryStringParameters?.id;
if (!id) {
return {
statusCode: 400,
body: 'No id provided',
};
}

const resp = await getDoc(doc(db, 'games', id));
const game = JSON.stringify(resp.data());
return {
statusCode: 200,
body: game,
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
return {
statusCode: 500,
body: e.message,
};
}
};
31 changes: 31 additions & 0 deletions netlify/functions/save-game/save-game.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* eslint-disable import/prefer-default-export */
import { Handler } from '@netlify/functions';
import { humanId } from 'human-id';
import { setDoc, doc } from 'firebase/firestore';
import { db } from '../../firestore/firestore';

export const handler: Handler = async (event) => {
try {
const payload = event.body;
if (!payload) {
return {
statusCode: 400,
body: 'No body provided',
};
}

const requestBody = JSON.parse(payload);
const id = humanId({ separator: '-', capitalize: false });
await setDoc(doc(db, 'games', id), requestBody);
return {
statusCode: 200,
body: id,
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
return {
statusCode: 500,
body: e.message,
};
}
};
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@ant-design/icons": "^5.2.6",
"@netlify/functions": "^1.6.0",
"@types/axios": "^0.14.0",
"@types/firebase": "^3.2.1",
"@types/node": "^14.18.54",
"@types/papaparse": "^5.3.7",
"@types/react-beautiful-dnd": "^13.1.4",
Expand All @@ -22,6 +23,8 @@
"@vitejs/plugin-react": "^4.0.3",
"antd": "^5.8.2",
"axios": "^1.6.0",
"firebase": "^10.6.0",
"human-id": "^4.1.0",
"js-base64": "^3.7.5",
"papaparse": "^5.4.1",
"react": "^18.2.0",
Expand Down
9 changes: 7 additions & 2 deletions src/Connections/ConnectionsCreate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ function ConnectionsCreate() {
const [author, setAuthor] = useState<string>(locationGame?.author);
const [categories, setCategories] = useState<ConnectionCategories>(locationGame?.categories || defaultCategories);
const [clearInputs, setClearInputs] = useState(false);
const [loadingCreate, setLoadingCreate] = useState(false);
const navigate = useNavigate();
const validCategories = validateCategories(categories);

Expand Down Expand Up @@ -358,11 +359,15 @@ function ConnectionsCreate() {
disabled={!validCategories}
onClick={() => {
if (validCategories) {
setLoadingCreate(true);
const game = { title, author, categories };
const link = generateLink(game);
navigate(link, { state: { backTo: 'edit' } });
generateLink(game).then((link) => {
setLoadingCreate(false);
navigate(link, { state: { backTo: 'edit' } });
});
}
}}
loading={loadingCreate}
type="primary"
>
Create puzzle
Expand Down
28 changes: 26 additions & 2 deletions src/Connections/ConnectionsRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useLocation, Navigate } from 'react-router-dom';
import { useContext } from 'react';
import { useContext, useEffect, useState } from 'react';
import {
ConnectionsGame, decodeCategories, isDebug, normalizeGame, toInt, validateCategories,
ConnectionsGame, decodeCategories, isDebug, loadGame, normalizeGame, toInt, validateCategories,
} from './utils';
import ConnectionsPlay from './ConnectionsPlay';
import { ConnectionsContext } from './ConnectionsContext';
Expand All @@ -12,10 +12,34 @@ function ConnectionsRouter() {
const { nytConnections, loadedConnections } = useContext(ConnectionsContext);
const searchParams = new URLSearchParams(location.search);

const shouldLoadSavedGame = searchParams.get('game')?.split('-').length === 3;
const [savedGame, setSavedGame] = useState<ConnectionsGame | null>(null);
const [loadingSavedGame, setLoadingSavedGame] = useState(shouldLoadSavedGame);

let game: ConnectionsGame = { categories: [] };
const urlGame = decodeCategories(searchParams.get('categories') || searchParams.get('game'));

useEffect(() => {
if (!loadingSavedGame) {
return;
}

loadGame(searchParams.get('game')!)
.then((game) => {
if (game) {
setSavedGame(game);
}
})
.finally(() => setLoadingSavedGame(false));
}, [loadingSavedGame, searchParams]);

if (urlGame) {
game = urlGame;
} else if (shouldLoadSavedGame) {
if (loadingSavedGame) {
return <LoadingSpinner />;
}
game = savedGame!;
} else if (searchParams.has('id')) {
if (!loadedConnections) {
return <LoadingSpinner />;
Expand Down
20 changes: 15 additions & 5 deletions src/Connections/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { encodeURI, decode } from 'js-base64';
import axios from 'axios';
import { decode } from 'js-base64';

export type ConnectionCategory = {
description: string;
Expand Down Expand Up @@ -61,11 +62,10 @@ export const normalizeGame = (game: ConnectionsGame, reset: boolean): Connection
categories: normalizeCategories(game.categories, reset),
});

export const generateLink = (game: ConnectionsGame): string => {
export const generateLink = async (game: ConnectionsGame): Promise<string> => {
const normalizedGame = normalizeGame(game, true);
const jsonString = JSON.stringify(normalizedGame);
const encodedBase64String = encodeURI(jsonString);
return `/connections/play?game=${encodedBase64String}`;
const id = await saveGame(normalizedGame);
return `/connections/play?game=${id}`;
};

export const decodeCategories = (encodedValue: string | null): ConnectionsGame | null => {
Expand Down Expand Up @@ -221,6 +221,16 @@ export const toInt = (str: string): number | null => {
return Number.isNaN(num) ? null : num;
};

const saveGame = async (game: ConnectionsGame): Promise<string> => {
const resp = await axios.post('/.netlify/functions/save-game', game);
return resp.data;
};

export const loadGame = async (id: string): Promise<ConnectionsGame | null> => {
const resp = axios.get('/.netlify/functions/load-game', { params: { id } });
return (await resp).data;
};

export const isDebug = (): boolean => {
const searchParams = new URLSearchParams(window.location.search);
return searchParams.has('debug');
Expand Down
Loading

0 comments on commit 3438ee7

Please sign in to comment.