Unverified Commit 8bef74ec authored by Michal Čaniga's avatar Michal Čaniga
Browse files

Add project starter

parent c14945dc
REACT_APP_FIREBASE_API_KEY=xxx
\ No newline at end of file
......@@ -21,3 +21,6 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# env file
.env
# PV247
Team project.
## Setup
Create `.env` in root of the project and paste here content from `.env-copy`. Replace placeholders with real values.
Team project.
\ No newline at end of file
......@@ -3,10 +3,9 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@emotion/react": "^11.4.1",
"@emotion/styled": "^11.3.0",
"@mui/icons-material": "^5.0.1",
"@mui/material": "^5.0.2",
"@ant-design/icons": "^4.7.0",
"antd": "^4.16.13",
"firebase": "^9.2.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.3.0",
......
@import '~antd/dist/antd.css';
@import-normalize;
html, body {
margin:0;
padding:0;
height:100%;
}
\ No newline at end of file
import {
AppBar,
Button,
Container,
CssBaseline,
ThemeProvider,
Toolbar
} from '@mui/material';
import { Button, Menu, Layout } from 'antd';
import { BrowserRouter, Link, Route, Switch } from 'react-router-dom';
import {
HomeOutlined,
RocketOutlined,
DollarOutlined,
LogoutOutlined,
LoginOutlined,
BookOutlined
} from '@ant-design/icons';
import './App.css';
const { Content } = Layout;
import Encyclopedia from './pages/Encyclopedia';
import Game from './pages/Game';
import Home from './pages/Home';
import theme from './theme';
import Leaderboard from './pages/Leaderboard';
import Login from './pages/Login';
import Store from './pages/Store';
const App = () => (
<ThemeProvider theme={theme}>
<BrowserRouter>
<CssBaseline />
const TopMenu = () => (
<Menu style={{ padding: '0 px' }} mode="horizontal">
<Menu.Item key="home" icon={<HomeOutlined />}>
<Link to="/">
<Button type="text">Home</Button>
</Link>
</Menu.Item>
<Menu.Item key="game" icon={<RocketOutlined />}>
<Link to="/game">
<Button type="text">Game</Button>
</Link>
</Menu.Item>
<Menu.Item key="store" icon={<DollarOutlined />}>
<Link to="/store">
<Button type="text">Store</Button>
</Link>
</Menu.Item>
<Menu.Item key="encyclopedia" icon={<BookOutlined />}>
<Link to="/encyclopedia">
<Button type="text">Encyclopedia</Button>
</Link>
</Menu.Item>
<Menu.Item key="login" icon={<LoginOutlined />}>
<Link to="/login">
<Button type="text">Login</Button>
</Link>
</Menu.Item>
<Menu.Item key="logout" icon={<LogoutOutlined />}>
<Button type="text">Logout</Button>
</Menu.Item>
</Menu>
);
<AppBar position="fixed">
<Container maxWidth="sm">
<Toolbar disableGutters sx={{ gap: 2 }}>
<Button component={Link} to="/">
Home
</Button>
</Toolbar>
</Container>
</AppBar>
const AppContent = () => (
<Content style={{ padding: '0 50px' }}>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/login" exact component={Login} />
<Route path="/store" exact component={Store} />
<Route path="/encyclopedia" exact component={Encyclopedia} />
<Route path="/leaderboard" exact component={Leaderboard} />
<Route path="/game" exact component={Game} />
</Switch>
</Content>
);
<Container
maxWidth="sm"
component="main"
sx={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
height: '100vh',
pt: 8,
gap: 2
}}
>
<Switch>
<Route path="/" exact component={Home} />
</Switch>
</Container>
</BrowserRouter>
</ThemeProvider>
const App = () => (
<BrowserRouter>
<TopMenu />
<AppContent />
</BrowserRouter>
);
export default App;
import { ChangeEvent, useCallback, useState } from 'react';
const useField = (id: string, required?: boolean) => {
const [value, setValue] = useState('');
const [touched, setTouched] = useState(false);
const error = required && touched && !value;
return [
// Current value for convenient access
value,
// Props for the TextField
{
id,
value,
onChange: useCallback(
(e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) =>
setValue(e.target.value),
[]
),
onBlur: useCallback(() => setTouched(true), []),
required,
error,
helperText: error ? 'Required' : undefined
}
] as const;
};
export default useField;
import { useEffect, useState } from 'react';
import { User } from 'firebase/auth';
import { onAuthChanged } from '../utils/firebase';
// Hook providing logged in user information
const useLoggedInUser = () => {
// Hold user info in state
const [user, setUser] = useState<User>();
// Setup onAuthChanged once when component is mounted
useEffect(() => {
onAuthChanged(u => setUser(u ?? undefined));
}, []);
return user;
};
export default useLoggedInUser;
import { useEffect } from 'react';
const usePageTitle = (title: string) => {
useEffect(() => {
document.title = `${title} | Tic Tac Toe`;
}, [title]);
};
export default usePageTitle;
import { Typography } from 'antd';
const Encyclopedia = () => <Typography>Encyclopedia</Typography>;
export default Encyclopedia;
import { Typography } from 'antd';
const Game = () => <Typography>Game</Typography>;
export default Game;
import { Box, Typography } from '@mui/material';
import { Typography } from 'antd';
const Home = () => (
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Typography variant="h1" fontWeight="bolder">
Pokemon game?
</Typography>
</Box>
);
const Home = () => <Typography>Home</Typography>;
export default Home;
import { Typography } from 'antd';
const Leaderboard = () => <Typography>Leaderboard</Typography>;
export default Leaderboard;
import { Button } from 'antd';
import useLoggedInUser from '../hooks/useLoggedInUser';
import { signIn, signOut } from '../utils/firebase';
const signInDev = async () => {
await signIn('dev@gmail.com', 'dev12345');
};
const Login = () => {
const user = useLoggedInUser();
return user === undefined ? (
<Button type="primary" onClick={signInDev} style={{ marginRight: 10 }}>
Sign in
</Button>
) : (
<>
<p>Already logged in as: {user.email}</p>
<Button onClick={signOut}>Logout</Button>
</>
);
};
export default Login;
import { Typography } from 'antd';
const Store = () => <Typography>Store</Typography>;
export default Store;
import { createTheme } from '@mui/material';
declare module '@mui/material/styles' {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
interface Palette {
playerX?: string;
playerO?: string;
}
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
interface PaletteOptions {
playerX?: string;
playerO?: string;
}
}
const theme = createTheme({
palette: {
primary: { main: '#f2d45c' },
playerX: '#f25a5a',
playerO: '#5a8cf2',
mode: 'dark'
}
});
export default theme;
import { initializeApp } from 'firebase/app';
import {
getAuth,
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut as authSignOut,
onAuthStateChanged,
User
} from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
// Initialize Firebase
initializeApp({
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: 'pv247-pokemon-game.firebaseapp.com',
projectId: 'pv247-pokemon-game',
storageBucket: 'pv247-pokemon-game.appspot.com',
messagingSenderId: '653405971003',
appId: '1:653405971003:web:ed7875c10164cc3240b21e'
});
// Authentication
const auth = getAuth();
// Sign up handler
export const signUp = (email: string, password: string) =>
createUserWithEmailAndPassword(auth, email, password);
// Sign in handler
export const signIn = (email: string, password: string) =>
signInWithEmailAndPassword(auth, email, password);
// Sign out handler
export const signOut = () => authSignOut(auth);
// Subscribe to auth state changes
export const onAuthChanged = (callback: (u: User | null) => void) =>
onAuthStateChanged(auth, callback);
// Firestore
const db = getFirestore();
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment