Skip to content

Commit aa8bfa6

Browse files
authored
Merge pull request #39 from TaloDev/develop
Release 0.10.0
2 parents 18c2db4 + 5b2f881 commit aa8bfa6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1227
-356
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"randomcolor": "^0.6.2",
2424
"react": "^17.0.2",
2525
"react-dom": "^17.0.2",
26+
"react-focus-lock": "^2.8.1",
2627
"react-hook-form": "^7.28.1",
2728
"react-required-if": "^1.0.3",
2829
"react-resize-detector": "^6.7.1",
@@ -66,5 +67,5 @@
6667
"lint-staged": {
6768
"*.{js,jsx}": "eslint --fix"
6869
},
69-
"version": "0.9.0"
70+
"version": "0.10.0"
7071
}

src/App.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ const RecoverAccount = lazy(() => import(/* webpackChunkName: 'recover-account'
3131
const Activity = lazy(() => import(/* webpackChunkName: 'activity' */ './pages/Activity'))
3232
const Stats = lazy(() => import(/* webpackChunkName: 'stats' */ './pages/Stats'))
3333
const PlayerStats = lazy(() => import(/* webpackChunkName: 'player-stats' */ './pages/PlayerStats'))
34+
const AcceptInvite = lazy(() => import(/* webpackChunkName: 'accept-invite' */ './pages/AcceptInvite'))
35+
const Organisation = lazy(() => import(/* webpackChunkName: 'organisation' */ './pages/Organisation'))
3436

3537
const App = () => {
3638
const [user, setUser] = useRecoilState(userState)
@@ -93,6 +95,7 @@ const App = () => {
9395
<Route exact path={routes.demo} component={Demo} />
9496
<Route exact path={routes.verify2FA} component={Verify2FA} />
9597
<Route exact path={routes.recover} component={RecoverAccount} />
98+
<Route exact path={routes.acceptInvite} component={AcceptInvite} />
9699

97100
<Redirect to={`${routes.login}?next=${intendedUrl}`} />
98101
</Switch>
@@ -109,6 +112,7 @@ const App = () => {
109112
<Route exact path={routes.dashboard} component={Dashboard} />
110113
<Route exact path={routes.account} component={Account} />
111114
<Route exact path={routes.confirmPassword} component={ConfirmPassword} />
115+
{canViewPage(user, routes.organisation) && <Route exact path={routes.organisation} component={Organisation} />}
112116

113117
{activeGame &&
114118
<>

src/api/createInvite.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import api from './api'
2+
3+
export default (email, type) => api.post('/invites', { email, type })

src/api/getInvite.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import api from './api'
2+
3+
export default (token) => api.get(`/public/invites/${token}`)

src/api/useOrganisation.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import useSWR from 'swr'
2+
import buildError from '../utils/buildError'
3+
import api from './api'
4+
5+
const useOrganisation = () => {
6+
const fetcher = async (url) => {
7+
const res = await api.get(url)
8+
return res.data
9+
}
10+
11+
const { data, error, mutate } = useSWR(
12+
'organisations/current',
13+
fetcher
14+
)
15+
16+
return {
17+
games: data?.games ?? [],
18+
members: data?.members ?? [],
19+
pendingInvites: data?.pendingInvites ?? [],
20+
loading: !data && !error,
21+
error: error && buildError(error),
22+
mutate
23+
}
24+
}
25+
26+
export default useOrganisation

src/components/DevDataStatus.jsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import classNames from 'classnames'
2+
import React from 'react'
3+
import { useRecoilState } from 'recoil'
4+
import devDataState from '../state/devDataState'
5+
import Toggle from './Toggle'
6+
7+
function DevDataStatus() {
8+
const [includeDevData, setIncludeDevData] = useRecoilState(devDataState)
9+
10+
return (
11+
<div className='space-y-4'>
12+
<h2 className='text-2xl mt-4 md:mt-0'>Dev data is currently
13+
<span className={classNames('font-semibold', { 'text-orange-400': includeDevData })}>
14+
{' '}{includeDevData ? 'enabled' : 'not enabled'}
15+
</span>
16+
</h2>
17+
18+
<p>When enabled, you&apos;ll see data submitted by players from dev builds of your game.</p>
19+
20+
<Toggle id='dev-data' enabled={includeDevData} onToggle={setIncludeDevData} />
21+
</div>
22+
)
23+
}
24+
25+
export default DevDataStatus

src/components/GameSwitcher.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ const GameSwitcher = () => {
6868
</motion.ul>
6969
)}
7070
>
71-
<div className={classNames('bg-indigo-300 rounded p-2 flex items-center justify-between w-60 md:w-80', { 'rounded-b-none': isOpen })}>
71+
<div className={classNames('bg-indigo-300 rounded p-2 flex items-center justify-between w-60 lg:w-80', { 'rounded-b-none': isOpen })}>
7272
<div className='flex items-center'>
7373
<span
7474
style={{ backgroundColor: randomColor({ seed: activeGame.name, luminosity: 'dark' }) }}

src/components/Modal.jsx

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Button from './Button'
55
import classNames from 'classnames'
66
import usePortal from '../utils/usePortal'
77
import { createPortal } from 'react-dom'
8+
import FocusLock from 'react-focus-lock'
89

910
const Modal = (props) => {
1011
const [, setOpen] = props.modalState
@@ -24,27 +25,36 @@ const Modal = (props) => {
2425
const target = usePortal(props.id)
2526

2627
return createPortal(
27-
<div className='fixed w-screen md:p-4 bg-gray-900 bg-opacity-60 flex items-start md:items-center justify-center inset-0 z-50 text-black transition-colors'>
28-
<dialog className='overflow-scroll block w-full h-full md:h-auto md:w-[600px] bg-white md:rounded p-0' aria-modal='true' aria-labelledby={`modal-${props.id}-label`}>
29-
<div className='p-4 border-b border-gray-200 flex items-center justify-between'>
30-
<h2
31-
id={`modal-${props.id}-label`}
32-
className={classNames('text-xl font-semibold', { 'hidden': props.hideTitle })}
33-
>
34-
{props.title}
35-
</h2>
36-
37-
<Button
38-
variant='icon'
39-
onClick={() => setOpen(false)}
40-
icon={<IconX />}
41-
extra={{ 'aria-label': 'Close modal' }}
42-
/>
43-
</div>
44-
45-
{props.children}
46-
</dialog>
47-
</div>,
28+
<FocusLock>
29+
<div className='fixed w-screen md:p-4 bg-gray-900 bg-opacity-60 flex items-start md:items-center justify-center inset-0 z-50 text-black transition-colors'>
30+
<dialog
31+
className={classNames('block w-full h-full md:h-auto md:w-[600px] bg-white md:rounded p-0', {
32+
'overflow-scroll': props.scroll,
33+
'overflow-visible': !props.scroll
34+
})}
35+
aria-modal='true'
36+
aria-labelledby={`modal-${props.id}-label`}
37+
>
38+
<div className='p-4 border-b border-gray-200 flex items-center justify-between'>
39+
<h2
40+
id={`modal-${props.id}-label`}
41+
className={classNames('text-xl font-semibold', { 'hidden': props.hideTitle })}
42+
>
43+
{props.title}
44+
</h2>
45+
46+
<Button
47+
variant='icon'
48+
onClick={() => setOpen(false)}
49+
icon={<IconX />}
50+
extra={{ 'aria-label': 'Close modal' }}
51+
/>
52+
</div>
53+
54+
{props.children}
55+
</dialog>
56+
</div>
57+
</FocusLock>,
4858
target
4959
)
5060
}
@@ -54,7 +64,12 @@ Modal.propTypes = {
5464
title: PropTypes.string.isRequired,
5565
hideTitle: PropTypes.bool,
5666
children: PropTypes.any.isRequired,
57-
modalState: PropTypes.array.isRequired
67+
modalState: PropTypes.array.isRequired,
68+
scroll: PropTypes.bool
69+
}
70+
71+
Modal.defaultProps = {
72+
scroll: true
5873
}
5974

6075
export default Modal

src/components/Page.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import Loading from './Loading'
66

77
function Page({ title, showBackButton, isLoading, containerClassName, extraTitleComponent, children }) {
88
return (
9-
<div className={classNames('space-y-4 md:space-y-8', containerClassName)}>
9+
<div className={classNames('space-y-8', containerClassName)}>
1010
<div className='flex items-center'>
1111
<Title showBackButton={showBackButton}>{title}</Title>
1212

src/components/SecondaryNav.jsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from 'react'
2+
import PropTypes from 'prop-types'
3+
import Link from './Link'
4+
import classNames from 'classnames'
5+
import { useLocation } from 'react-router-dom'
6+
import canViewPage from '../utils/canViewPage'
7+
import userState from '../state/userState'
8+
import { useRecoilValue } from 'recoil'
9+
10+
function SecondaryNav({ routes }) {
11+
const location = useLocation()
12+
const user = useRecoilValue(userState)
13+
14+
const availableRoutes = routes.filter(({ to }) => canViewPage(user, to))
15+
if (availableRoutes.length < 2) return null
16+
17+
return (
18+
<nav className='bg-gray-700 w-screen px-4 md:px-8 -mx-4 md:-mx-8 -mt-4 md:-mt-8 mb-8 py-4 border-b-2 border-b-gray-600 flex justify-between items-center'>
19+
<ul className='flex space-x-8'>
20+
{availableRoutes.map(({ title, to }) => (
21+
<li key={to} className='relative'>
22+
<Link to={to} className={classNames({ '!text-white': location.pathname === to, '!text-indigo-300': location.pathname !== to })}>{title}</Link>
23+
{location.pathname === to && <div className='absolute bottom-[-16px] left-0 right-0 h-[2px] bg-white transform translate-y-full' />}
24+
</li>
25+
))}
26+
</ul>
27+
</nav>
28+
)
29+
}
30+
31+
SecondaryNav.propTypes = {
32+
routes: PropTypes.arrayOf(PropTypes.shape({
33+
title: PropTypes.string.isRequired,
34+
to: PropTypes.string.isRequired
35+
})).isRequired
36+
}
37+
38+
export default SecondaryNav

0 commit comments

Comments
 (0)