Merge pull request #112 from stz184/component-class-to-function-migration
Migrate all class based react components to function based
This commit is contained in:
commit
514d9b1c2d
|
@ -1,4 +1,4 @@
|
|||
import React, { Component } from 'react'
|
||||
import React from 'react'
|
||||
import { createGlobalStyle } from 'styled-components'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import Routes from './components/routes/Routes'
|
||||
|
@ -23,22 +23,20 @@ const GlobalStyle = createGlobalStyle`
|
|||
|
||||
import 'semantic-ui-css/semantic.min.css'
|
||||
|
||||
class App extends Component {
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<meta
|
||||
name="description"
|
||||
content="Simple and User-friendly Photo Gallery for Personal Servers"
|
||||
/>
|
||||
</Helmet>
|
||||
<GlobalStyle />
|
||||
<Routes />
|
||||
<Messages />
|
||||
</>
|
||||
)
|
||||
}
|
||||
const App = () => {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<meta
|
||||
name="description"
|
||||
content="Simple and User-friendly Photo Gallery for Personal Servers"
|
||||
/>
|
||||
</Helmet>
|
||||
<GlobalStyle />
|
||||
<Routes />
|
||||
<Messages />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
|
|
|
@ -8,6 +8,7 @@ import { useQuery, gql } from '@apollo/client'
|
|||
import { Authorized } from './components/routes/AuthorizedRoute'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import Header from './components/header/Header'
|
||||
import { authToken } from './authentication'
|
||||
|
||||
export const ADMIN_QUERY = gql`
|
||||
query adminQuery {
|
||||
|
@ -94,13 +95,11 @@ const SideButtonLabel = styled.div`
|
|||
`
|
||||
|
||||
export const SideMenu = () => {
|
||||
const adminQuery = useQuery(ADMIN_QUERY)
|
||||
const mapboxQuery = useQuery(MAPBOX_QUERY)
|
||||
const adminQuery = authToken() ? useQuery(ADMIN_QUERY) : null
|
||||
const mapboxQuery = authToken() ? useQuery(MAPBOX_QUERY) : null
|
||||
|
||||
const isAdmin =
|
||||
adminQuery.data && adminQuery.data.myUser && adminQuery.data.myUser.admin
|
||||
|
||||
const mapboxEnabled = mapboxQuery.data && mapboxQuery.data.mapboxToken != null
|
||||
const isAdmin = adminQuery?.data?.myUser?.admin
|
||||
const mapboxEnabled = !!mapboxQuery?.data?.mapboxToken
|
||||
|
||||
return (
|
||||
<SideMenuContainer>
|
||||
|
|
|
@ -7,6 +7,10 @@ import { render, screen } from '@testing-library/react'
|
|||
import Layout, { ADMIN_QUERY, MAPBOX_QUERY, SideMenu } from './Layout'
|
||||
import { MemoryRouter } from 'react-router-dom'
|
||||
|
||||
import * as authentication from './authentication'
|
||||
|
||||
jest.mock('./authentication.js')
|
||||
|
||||
test('Layout component', async () => {
|
||||
render(
|
||||
<Layout>
|
||||
|
@ -18,7 +22,13 @@ test('Layout component', async () => {
|
|||
expect(screen.getByText('layout_content')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
authentication.authToken.mockClear()
|
||||
})
|
||||
|
||||
test('Layout sidebar component', async () => {
|
||||
authentication.authToken.mockImplementation(() => true)
|
||||
|
||||
const mockedGraphql = [
|
||||
{
|
||||
request: {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import React, { useCallback, useState, useEffect } from 'react'
|
||||
import ReactRouterPropTypes from 'react-router-prop-types'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { gql } from '@apollo/client'
|
||||
import { Query } from '@apollo/client/react/components'
|
||||
import { useQuery, gql } from '@apollo/client'
|
||||
import AlbumGallery from '../../components/albumGallery/AlbumGallery'
|
||||
import PropTypes from 'prop-types'
|
||||
import Layout from '../../Layout'
|
||||
|
@ -67,6 +66,15 @@ function AlbumPage({ match }) {
|
|||
orderDirection: urlParams.get('orderDirection') || 'ASC',
|
||||
})
|
||||
|
||||
const { loading, error, data, refetch } = useQuery(albumQuery, {
|
||||
variables: {
|
||||
id: albumId,
|
||||
onlyFavorites,
|
||||
mediaOrderBy: ordering.orderBy,
|
||||
mediaOrderDirection: ordering.orderDirection,
|
||||
},
|
||||
})
|
||||
|
||||
const setOrderingCallback = useCallback(
|
||||
ordering => {
|
||||
setOrdering(prevState => {
|
||||
|
@ -80,7 +88,7 @@ function AlbumPage({ match }) {
|
|||
)
|
||||
|
||||
const toggleFavorites = useCallback(
|
||||
(onlyFavorites, refetch) => {
|
||||
onlyFavorites => {
|
||||
if (
|
||||
(refetchNeededAll && !onlyFavorites) ||
|
||||
(refetchNeededFavorites && onlyFavorites)
|
||||
|
@ -97,7 +105,7 @@ function AlbumPage({ match }) {
|
|||
setOnlyFavorites(onlyFavorites)
|
||||
}
|
||||
},
|
||||
[setOnlyFavorites]
|
||||
[setOnlyFavorites, refetch]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -107,44 +115,22 @@ function AlbumPage({ match }) {
|
|||
history.replaceState({}, '', pathName + '?' + queryString)
|
||||
}, [onlyFavorites, ordering])
|
||||
|
||||
return (
|
||||
<Query
|
||||
query={albumQuery}
|
||||
variables={{
|
||||
id: albumId,
|
||||
onlyFavorites,
|
||||
mediaOrderBy: ordering.orderBy,
|
||||
mediaOrderDirection: ordering.orderDirection,
|
||||
}}
|
||||
>
|
||||
{({ loading, error, data, refetch }) => {
|
||||
const setOnlyFavorites = useCallback(
|
||||
checked => {
|
||||
toggleFavorites(checked, refetch)
|
||||
},
|
||||
[toggleFavorites, refetch]
|
||||
)
|
||||
if (error) return <div>Error</div>
|
||||
|
||||
if (error) return <div>Error</div>
|
||||
return (
|
||||
<Layout title={data ? data.album.title : 'Loading album'}>
|
||||
<AlbumGallery
|
||||
album={data && data.album}
|
||||
loading={loading}
|
||||
showFavoritesToggle
|
||||
setOnlyFavorites={setOnlyFavorites}
|
||||
onlyFavorites={onlyFavorites}
|
||||
onFavorite={() =>
|
||||
(refetchNeededAll = refetchNeededFavorites = true)
|
||||
}
|
||||
showFilter
|
||||
setOrdering={setOrderingCallback}
|
||||
ordering={ordering}
|
||||
/>
|
||||
</Layout>
|
||||
)
|
||||
}}
|
||||
</Query>
|
||||
return (
|
||||
<Layout title={data ? data.album.title : 'Loading album'}>
|
||||
<AlbumGallery
|
||||
album={data && data.album}
|
||||
loading={loading}
|
||||
showFavoritesToggle
|
||||
setOnlyFavorites={toggleFavorites}
|
||||
onlyFavorites={onlyFavorites}
|
||||
onFavorite={() => (refetchNeededAll = refetchNeededFavorites = true)}
|
||||
showFilter
|
||||
setOrdering={setOrderingCallback}
|
||||
ordering={ordering}
|
||||
/>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import React, { Component } from 'react'
|
||||
import React from 'react'
|
||||
import AlbumBoxes from '../../components/albumGallery/AlbumBoxes'
|
||||
import Layout from '../../Layout'
|
||||
import { gql } from '@apollo/client'
|
||||
import { Query } from '@apollo/client/react/components'
|
||||
import { useQuery, gql } from '@apollo/client'
|
||||
|
||||
const getAlbumsQuery = gql`
|
||||
query getMyAlbums {
|
||||
|
@ -18,23 +17,21 @@ const getAlbumsQuery = gql`
|
|||
}
|
||||
`
|
||||
|
||||
class AlbumsPage extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Layout title="Albums">
|
||||
<h1>Albums</h1>
|
||||
<Query query={getAlbumsQuery}>
|
||||
{({ loading, error, data }) => (
|
||||
<AlbumBoxes
|
||||
loading={loading}
|
||||
error={error}
|
||||
albums={data && data.myAlbums}
|
||||
/>
|
||||
)}
|
||||
</Query>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
const AlbumsPage = () => {
|
||||
const { loading, error, data } = useQuery(getAlbumsQuery)
|
||||
|
||||
return (
|
||||
<Layout title="Albums">
|
||||
<h1>Albums</h1>
|
||||
{!loading && (
|
||||
<AlbumBoxes
|
||||
loading={loading}
|
||||
error={error}
|
||||
albums={data && data.myAlbums}
|
||||
/>
|
||||
)}
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
export default AlbumsPage
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React, { Component } from 'react'
|
||||
import { gql } from '@apollo/client'
|
||||
import { Mutation, Query } from '@apollo/client/react/components'
|
||||
import React, { useState } from 'react'
|
||||
import { gql, useQuery, useMutation } from '@apollo/client'
|
||||
import { Redirect } from 'react-router-dom'
|
||||
import { Button, Form, Message, Header } from 'semantic-ui-react'
|
||||
import { Container } from './loginUtilities'
|
||||
|
@ -26,106 +25,101 @@ const initialSetupMutation = gql`
|
|||
}
|
||||
`
|
||||
|
||||
class InitialSetupPage extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
const InitialSetupPage = () => {
|
||||
const [state, setState] = useState({
|
||||
username: '',
|
||||
password: '',
|
||||
rootPath: '',
|
||||
})
|
||||
|
||||
this.state = {
|
||||
username: '',
|
||||
password: '',
|
||||
rootPath: '',
|
||||
}
|
||||
const handleChange = (event, key) => {
|
||||
const value = event.target.value
|
||||
setState(prevState => ({
|
||||
...prevState,
|
||||
[key]: value,
|
||||
}))
|
||||
}
|
||||
|
||||
handleChange(event, key) {
|
||||
this.setState({ [key]: event.target.value })
|
||||
}
|
||||
|
||||
signIn(event, authorize) {
|
||||
const signIn = (event, authorize) => {
|
||||
event.preventDefault()
|
||||
|
||||
authorize({
|
||||
variables: {
|
||||
username: this.state.username,
|
||||
password: this.state.password,
|
||||
rootPath: this.state.rootPath,
|
||||
username: state.username,
|
||||
password: state.password,
|
||||
rootPath: state.rootPath,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
if (authToken()) {
|
||||
return <Redirect to="/" />
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Container>
|
||||
<Header as="h1" textAlign="center">
|
||||
Initial Setup
|
||||
</Header>
|
||||
<Query query={checkInitialSetupQuery}>
|
||||
{({ data }) => {
|
||||
if (data && data.siteInfo && data.siteInfo.initialSetup) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <Redirect to="/" />
|
||||
}}
|
||||
</Query>
|
||||
<Mutation
|
||||
mutation={initialSetupMutation}
|
||||
onCompleted={data => {
|
||||
const { success, token } = data.initialSetupWizard
|
||||
|
||||
if (success) {
|
||||
login(token)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{(authorize, { loading, data }) => {
|
||||
let errorMessage = null
|
||||
if (data) {
|
||||
if (!data.initialSetupWizard.success)
|
||||
errorMessage = data.initialSetupWizard.status
|
||||
}
|
||||
|
||||
return (
|
||||
<Form
|
||||
style={{ width: 500, margin: 'auto' }}
|
||||
error={!!errorMessage}
|
||||
onSubmit={e => this.signIn(e, authorize)}
|
||||
loading={loading || (data && data.initialSetupWizard.success)}
|
||||
>
|
||||
<Form.Field>
|
||||
<label>Username</label>
|
||||
<input onChange={e => this.handleChange(e, 'username')} />
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<label>Password</label>
|
||||
<input
|
||||
type="password"
|
||||
onChange={e => this.handleChange(e, 'password')}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<label>Photo Path</label>
|
||||
<input
|
||||
placeholder="/path/to/photos"
|
||||
type="text"
|
||||
onChange={e => this.handleChange(e, 'rootPath')}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Message error content={errorMessage} />
|
||||
<Button type="submit">Setup Photoview</Button>
|
||||
</Form>
|
||||
)
|
||||
}}
|
||||
</Mutation>
|
||||
</Container>
|
||||
</div>
|
||||
)
|
||||
if (authToken()) {
|
||||
return <Redirect to="/" />
|
||||
}
|
||||
|
||||
const { data: initialSetupData } = useQuery(checkInitialSetupQuery)
|
||||
const initialSetupRedirect = initialSetupData?.siteInfo
|
||||
?.initialSetup ? null : (
|
||||
<Redirect to="/" />
|
||||
)
|
||||
|
||||
const [
|
||||
authorize,
|
||||
{ loading: authorizeLoading, data: authorizationData },
|
||||
] = useMutation(initialSetupMutation, {
|
||||
onCompleted: data => {
|
||||
const { success, token } = data.initialSetupWizard
|
||||
|
||||
if (success) {
|
||||
login(token)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
let errorMessage = null
|
||||
if (authorizationData && !authorizationData.initialSetupWizard.success) {
|
||||
errorMessage = authorizationData.initialSetupWizard.status
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{initialSetupRedirect}
|
||||
<Container>
|
||||
<Header as="h1" textAlign="center">
|
||||
Initial Setup
|
||||
</Header>
|
||||
<Form
|
||||
style={{ width: 500, margin: 'auto' }}
|
||||
error={!!errorMessage}
|
||||
onSubmit={e => signIn(e, authorize)}
|
||||
loading={
|
||||
authorizeLoading || authorizationData?.initialSetupWizard?.success
|
||||
}
|
||||
>
|
||||
<Form.Field>
|
||||
<label>Username</label>
|
||||
<input onChange={e => handleChange(e, 'username')} />
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<label>Password</label>
|
||||
<input
|
||||
type="password"
|
||||
onChange={e => handleChange(e, 'password')}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<label>Photo Path</label>
|
||||
<input
|
||||
placeholder="/path/to/photos"
|
||||
type="text"
|
||||
onChange={e => handleChange(e, 'rootPath')}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Message error content={errorMessage} />
|
||||
<Button type="submit">Setup Photoview</Button>
|
||||
</Form>
|
||||
</Container>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default InitialSetupPage
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React, { Component } from 'react'
|
||||
import { gql } from '@apollo/client'
|
||||
import { Mutation, Query } from '@apollo/client/react/components'
|
||||
import React, { useState, useCallback } from 'react'
|
||||
import { useQuery, gql, useMutation } from '@apollo/client'
|
||||
import { Redirect } from 'react-router-dom'
|
||||
import styled from 'styled-components'
|
||||
import { Button, Form, Message, Header } from 'semantic-ui-react'
|
||||
|
@ -34,98 +33,92 @@ const LogoHeaderStyled = styled(LogoHeader)`
|
|||
margin-bottom: 72px !important;
|
||||
`
|
||||
|
||||
class LoginPage extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
const LoginPage = () => {
|
||||
const [credentials, setCredentials] = useState({
|
||||
username: '',
|
||||
password: '',
|
||||
})
|
||||
|
||||
this.state = {
|
||||
username: '',
|
||||
password: '',
|
||||
}
|
||||
const handleChange = useCallback(
|
||||
(event, key) => {
|
||||
const value = event.target.value
|
||||
setCredentials(credentials => {
|
||||
return {
|
||||
...credentials,
|
||||
[key]: value,
|
||||
}
|
||||
})
|
||||
},
|
||||
[setCredentials]
|
||||
)
|
||||
|
||||
const signIn = useCallback(
|
||||
(event, authorize) => {
|
||||
event.preventDefault()
|
||||
|
||||
authorize({
|
||||
variables: {
|
||||
username: credentials.username,
|
||||
password: credentials.password,
|
||||
},
|
||||
})
|
||||
},
|
||||
[credentials]
|
||||
)
|
||||
|
||||
const { data: initialSetupData } = useQuery(checkInitialSetupQuery)
|
||||
|
||||
const [authorize, { loading, data }] = useMutation(authorizeMutation, {
|
||||
onCompleted: data => {
|
||||
const { success, token } = data.authorizeUser
|
||||
|
||||
if (success) {
|
||||
login(token)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const errorMessage =
|
||||
data && !data.authorizeUser.success ? data.authorizeUser.status : null
|
||||
|
||||
if (authToken()) {
|
||||
return <Redirect to="/" />
|
||||
}
|
||||
|
||||
handleChange(event, key) {
|
||||
this.setState({ [key]: event.target.value })
|
||||
}
|
||||
|
||||
signIn(event, authorize) {
|
||||
event.preventDefault()
|
||||
|
||||
authorize({
|
||||
variables: {
|
||||
username: this.state.username,
|
||||
password: this.state.password,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
if (authToken()) {
|
||||
return <Redirect to="/" />
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Container>
|
||||
<LogoHeaderStyled />
|
||||
<Query query={checkInitialSetupQuery}>
|
||||
{({ data }) => {
|
||||
if (data && data.siteInfo && data.siteInfo.initialSetup) {
|
||||
return <Redirect to="/initialSetup" />
|
||||
}
|
||||
|
||||
return null
|
||||
}}
|
||||
</Query>
|
||||
<Mutation
|
||||
mutation={authorizeMutation}
|
||||
onCompleted={data => {
|
||||
const { success, token } = data.authorizeUser
|
||||
|
||||
if (success) {
|
||||
login(token)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{(authorize, { loading, data }) => {
|
||||
let errorMessage = null
|
||||
if (data) {
|
||||
if (!data.authorizeUser.success)
|
||||
errorMessage = data.authorizeUser.status
|
||||
}
|
||||
|
||||
return (
|
||||
<Form
|
||||
style={{ width: 500, margin: 'auto' }}
|
||||
error={!!errorMessage}
|
||||
onSubmit={e => this.signIn(e, authorize)}
|
||||
loading={loading || (data && data.authorizeUser.success)}
|
||||
>
|
||||
<Form.Field>
|
||||
<label htmlFor="username_field">Username</label>
|
||||
<input
|
||||
id="username_field"
|
||||
onChange={e => this.handleChange(e, 'username')}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<label htmlFor="password_field">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="password_field"
|
||||
onChange={e => this.handleChange(e, 'password')}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Message error content={errorMessage} />
|
||||
<Button type="submit">Sign in</Button>
|
||||
</Form>
|
||||
)
|
||||
}}
|
||||
</Mutation>
|
||||
</Container>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Container>
|
||||
<LogoHeaderStyled />
|
||||
{initialSetupData?.siteInfo?.initialSetup && (
|
||||
<Redirect to="/initialSetup" />
|
||||
)}
|
||||
<Form
|
||||
style={{ width: 500, margin: 'auto' }}
|
||||
error={!!errorMessage}
|
||||
onSubmit={e => signIn(e, authorize)}
|
||||
loading={loading || (data && data.authorizeUser.success)}
|
||||
>
|
||||
<Form.Field>
|
||||
<label htmlFor="username_field">Username</label>
|
||||
<input
|
||||
id="username_field"
|
||||
onChange={e => handleChange(e, 'username')}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Form.Field>
|
||||
<label htmlFor="password_field">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="password_field"
|
||||
onChange={e => handleChange(e, 'password')}
|
||||
/>
|
||||
</Form.Field>
|
||||
<Message error content={errorMessage} />
|
||||
<Button type="submit">Sign in</Button>
|
||||
</Form>
|
||||
</Container>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default LoginPage
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { gql } from '@apollo/client'
|
||||
import { gql, useMutation } from '@apollo/client'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { useState } from 'react'
|
||||
import { Mutation } from '@apollo/client'
|
||||
import { Button, Checkbox, Input, Table } from 'semantic-ui-react'
|
||||
|
||||
const createUserMutation = gql`
|
||||
|
@ -28,6 +27,12 @@ const initialState = {
|
|||
const AddUserRow = ({ setShow, show, onUserAdded }) => {
|
||||
const [state, setState] = useState(initialState)
|
||||
|
||||
const [createUser, { loading }] = useMutation(createUserMutation, {
|
||||
onCompleted: () => {
|
||||
onUserAdded()
|
||||
},
|
||||
})
|
||||
|
||||
function updateInput(event, key) {
|
||||
setState({
|
||||
...state,
|
||||
|
@ -40,68 +45,59 @@ const AddUserRow = ({ setShow, show, onUserAdded }) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<Mutation
|
||||
mutation={createUserMutation}
|
||||
onCompleted={() => {
|
||||
onUserAdded()
|
||||
}}
|
||||
>
|
||||
{(createUser, { loading }) => (
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
placeholder="Username"
|
||||
value={state.username}
|
||||
onChange={e => updateInput(e, 'username')}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
placeholder="/path/to/photos"
|
||||
value={state.rootPath}
|
||||
onChange={e => updateInput(e, 'rootPath')}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Checkbox
|
||||
toggle
|
||||
checked={state.admin}
|
||||
onChange={(e, data) => {
|
||||
setState({
|
||||
...state,
|
||||
admin: data.checked,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button.Group>
|
||||
<Button negative onClick={() => setShow(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
positive
|
||||
onClick={() => {
|
||||
createUser({
|
||||
variables: {
|
||||
username: state.username,
|
||||
rootPath: state.rootPath,
|
||||
admin: state.admin,
|
||||
},
|
||||
})
|
||||
setState(initialState)
|
||||
}}
|
||||
>
|
||||
Add User
|
||||
</Button>
|
||||
</Button.Group>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
)}
|
||||
</Mutation>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
placeholder="Username"
|
||||
value={state.username}
|
||||
onChange={e => updateInput(e, 'username')}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
placeholder="/path/to/photos"
|
||||
value={state.rootPath}
|
||||
onChange={e => updateInput(e, 'rootPath')}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Checkbox
|
||||
toggle
|
||||
checked={state.admin}
|
||||
onChange={(e, data) => {
|
||||
setState({
|
||||
...state,
|
||||
admin: data.checked,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button.Group>
|
||||
<Button negative onClick={() => setShow(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
positive
|
||||
onClick={() => {
|
||||
createUser({
|
||||
variables: {
|
||||
username: state.username,
|
||||
rootPath: state.rootPath,
|
||||
admin: state.admin,
|
||||
},
|
||||
})
|
||||
setState(initialState)
|
||||
}}
|
||||
>
|
||||
Add User
|
||||
</Button>
|
||||
</Button.Group>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import PropTypes from 'prop-types'
|
||||
import React, { useState } from 'react'
|
||||
import { Mutation } from '@apollo/client/react/components'
|
||||
import { gql } from '@apollo/client'
|
||||
import { gql, useMutation } from '@apollo/client'
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
|
@ -61,50 +60,47 @@ const scanUserMutation = gql`
|
|||
const ChangePasswordModal = ({ onClose, user, ...props }) => {
|
||||
const [passwordInput, setPasswordInput] = useState('')
|
||||
|
||||
const [changePassword] = useMutation(changeUserPasswordMutation, {
|
||||
onCompleted: () => {
|
||||
onClose && onClose()
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<Mutation
|
||||
mutation={changeUserPasswordMutation}
|
||||
onCompleted={() => {
|
||||
onClose && onClose()
|
||||
}}
|
||||
>
|
||||
{changePassword => (
|
||||
<Modal {...props}>
|
||||
<Modal.Header>Change password</Modal.Header>
|
||||
<Modal.Content>
|
||||
<p>
|
||||
Change password for <b>{user.username}</b>
|
||||
</p>
|
||||
<Form>
|
||||
<Form.Field>
|
||||
<label>New password</label>
|
||||
<Input
|
||||
placeholder="password"
|
||||
onChange={e => setPasswordInput(e.target.value)}
|
||||
type="password"
|
||||
/>
|
||||
</Form.Field>
|
||||
</Form>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button onClick={() => onClose && onClose()}>Cancel</Button>
|
||||
<Button
|
||||
positive
|
||||
onClick={() => {
|
||||
changePassword({
|
||||
variables: {
|
||||
userId: user.id,
|
||||
password: passwordInput,
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
Change password
|
||||
</Button>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
)}
|
||||
</Mutation>
|
||||
<Modal {...props}>
|
||||
<Modal.Header>Change password</Modal.Header>
|
||||
<Modal.Content>
|
||||
<p>
|
||||
Change password for <b>{user.username}</b>
|
||||
</p>
|
||||
<Form>
|
||||
<Form.Field>
|
||||
<label>New password</label>
|
||||
<Input
|
||||
placeholder="password"
|
||||
onChange={e => setPasswordInput(e.target.value)}
|
||||
type="password"
|
||||
/>
|
||||
</Form.Field>
|
||||
</Form>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button onClick={() => onClose && onClose()}>Cancel</Button>
|
||||
<Button
|
||||
positive
|
||||
onClick={() => {
|
||||
changePassword({
|
||||
variables: {
|
||||
userId: user.id,
|
||||
password: passwordInput,
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
Change password
|
||||
</Button>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -119,7 +115,7 @@ const UserRow = ({ user, refetchUsers }) => {
|
|||
editing: false,
|
||||
})
|
||||
|
||||
const [showComfirmDelete, setConfirmDelete] = useState(false)
|
||||
const [showConfirmDelete, setConfirmDelete] = useState(false)
|
||||
const [showChangePassword, setChangePassword] = useState(false)
|
||||
|
||||
function updateInput(event, key) {
|
||||
|
@ -129,171 +125,168 @@ const UserRow = ({ user, refetchUsers }) => {
|
|||
})
|
||||
}
|
||||
|
||||
const [updateUser, { loading: updateUserLoading }] = useMutation(
|
||||
updateUserMutation,
|
||||
{
|
||||
onCompleted: data => {
|
||||
setState({
|
||||
...data.updateUser,
|
||||
editing: false,
|
||||
})
|
||||
refetchUsers()
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const [deleteUser] = useMutation(deleteUserMutation, {
|
||||
onCompleted: () => {
|
||||
refetchUsers()
|
||||
},
|
||||
})
|
||||
|
||||
const [scanUser, { called: scanUserCalled }] = useMutation(scanUserMutation, {
|
||||
onCompleted: () => {
|
||||
refetchUsers()
|
||||
},
|
||||
})
|
||||
|
||||
if (state.editing) {
|
||||
return (
|
||||
<Mutation
|
||||
mutation={updateUserMutation}
|
||||
onCompleted={data => {
|
||||
setState({
|
||||
...data.updateUser,
|
||||
editing: false,
|
||||
})
|
||||
refetchUsers()
|
||||
}}
|
||||
>
|
||||
{(updateUser, { loading }) => (
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
style={{ width: '100%' }}
|
||||
placeholder={user.username}
|
||||
value={state.username}
|
||||
onChange={e => updateInput(e, 'username')}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
style={{ width: '100%' }}
|
||||
placeholder={user.rootPath}
|
||||
value={state.rootPath}
|
||||
onChange={e => updateInput(e, 'rootPath')}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Checkbox
|
||||
toggle
|
||||
checked={state.admin}
|
||||
onChange={(_, data) => {
|
||||
setState({
|
||||
...state,
|
||||
admin: data.checked,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button.Group>
|
||||
<Button
|
||||
negative
|
||||
onClick={() =>
|
||||
setState({
|
||||
...state.oldState,
|
||||
})
|
||||
}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
positive
|
||||
onClick={() =>
|
||||
updateUser({
|
||||
variables: {
|
||||
id: user.id,
|
||||
username: state.username,
|
||||
rootPath: state.rootPath,
|
||||
admin: state.admin,
|
||||
},
|
||||
})
|
||||
}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Button.Group>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
)}
|
||||
</Mutation>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
style={{ width: '100%' }}
|
||||
placeholder={user.username}
|
||||
value={state.username}
|
||||
onChange={e => updateInput(e, 'username')}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
style={{ width: '100%' }}
|
||||
placeholder={user.rootPath}
|
||||
value={state.rootPath}
|
||||
onChange={e => updateInput(e, 'rootPath')}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Checkbox
|
||||
toggle
|
||||
checked={state.admin}
|
||||
onChange={(_, data) => {
|
||||
setState({
|
||||
...state,
|
||||
admin: data.checked,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button.Group>
|
||||
<Button
|
||||
negative
|
||||
onClick={() =>
|
||||
setState({
|
||||
...state.oldState,
|
||||
})
|
||||
}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
loading={updateUserLoading}
|
||||
disabled={updateUserLoading}
|
||||
positive
|
||||
onClick={() =>
|
||||
updateUser({
|
||||
variables: {
|
||||
id: user.id,
|
||||
username: state.username,
|
||||
rootPath: state.rootPath,
|
||||
admin: state.admin,
|
||||
},
|
||||
})
|
||||
}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Button.Group>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Mutation
|
||||
mutation={deleteUserMutation}
|
||||
onCompleted={() => {
|
||||
refetchUsers()
|
||||
}}
|
||||
>
|
||||
{deleteUser => (
|
||||
<Table.Row>
|
||||
<Table.Cell>{user.username}</Table.Cell>
|
||||
<Table.Cell>{user.rootPath}</Table.Cell>
|
||||
<Table.Cell>
|
||||
{user.admin ? <Icon name="checkmark" size="large" /> : null}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button.Group>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setState({ ...state, editing: true, oldState: state })
|
||||
}}
|
||||
>
|
||||
<Icon name="edit" />
|
||||
Edit
|
||||
</Button>
|
||||
<Mutation mutation={scanUserMutation}>
|
||||
{(scanUser, { called }) => (
|
||||
<Button
|
||||
disabled={called}
|
||||
onClick={() => scanUser({ variables: { userId: user.id } })}
|
||||
>
|
||||
<Icon name="sync" />
|
||||
Scan
|
||||
</Button>
|
||||
)}
|
||||
</Mutation>
|
||||
<Button onClick={() => setChangePassword(true)}>
|
||||
<Icon name="key" />
|
||||
Change password
|
||||
</Button>
|
||||
<ChangePasswordModal
|
||||
user={user}
|
||||
open={showChangePassword}
|
||||
onClose={() => setChangePassword(false)}
|
||||
/>
|
||||
<Table.Row>
|
||||
<Table.Cell>{user.username}</Table.Cell>
|
||||
<Table.Cell>{user.rootPath}</Table.Cell>
|
||||
<Table.Cell>
|
||||
{user.admin ? <Icon name="checkmark" size="large" /> : null}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button.Group>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setState({ ...state, editing: true, oldState: state })
|
||||
}}
|
||||
>
|
||||
<Icon name="edit" />
|
||||
Edit
|
||||
</Button>
|
||||
<Button
|
||||
disabled={scanUserCalled}
|
||||
onClick={() => scanUser({ variables: { userId: user.id } })}
|
||||
>
|
||||
<Icon name="sync" />
|
||||
Scan
|
||||
</Button>
|
||||
<Button onClick={() => setChangePassword(true)}>
|
||||
<Icon name="key" />
|
||||
Change password
|
||||
</Button>
|
||||
<ChangePasswordModal
|
||||
user={user}
|
||||
open={showChangePassword}
|
||||
onClose={() => setChangePassword(false)}
|
||||
/>
|
||||
<Button
|
||||
negative
|
||||
onClick={() => {
|
||||
setConfirmDelete(true)
|
||||
}}
|
||||
>
|
||||
<Icon name="delete" />
|
||||
Delete
|
||||
</Button>
|
||||
<Modal open={showConfirmDelete}>
|
||||
<Modal.Header>Delete user</Modal.Header>
|
||||
<Modal.Content>
|
||||
<p>
|
||||
{`Are you sure, you want to delete `}
|
||||
<b>{user.username}</b>?
|
||||
</p>
|
||||
<p>{`This action cannot be undone`}</p>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button onClick={() => setConfirmDelete(false)}>Cancel</Button>
|
||||
<Button
|
||||
negative
|
||||
onClick={() => {
|
||||
setConfirmDelete(true)
|
||||
setConfirmDelete(false)
|
||||
deleteUser({
|
||||
variables: {
|
||||
id: user.id,
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Icon name="delete" />
|
||||
Delete
|
||||
Delete {user.username}
|
||||
</Button>
|
||||
<Modal open={showComfirmDelete}>
|
||||
<Modal.Header>Delete user</Modal.Header>
|
||||
<Modal.Content>
|
||||
<p>
|
||||
{`Are you sure, you want to delete `}
|
||||
<b>{user.username}</b>?
|
||||
</p>
|
||||
<p>{`This action cannot be undone`}</p>
|
||||
</Modal.Content>
|
||||
<Modal.Actions>
|
||||
<Button onClick={() => setConfirmDelete(false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
negative
|
||||
onClick={() => {
|
||||
setConfirmDelete(false)
|
||||
deleteUser({
|
||||
variables: {
|
||||
id: user.id,
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
Delete {user.username}
|
||||
</Button>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
</Button.Group>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
)}
|
||||
</Mutation>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
</Button.Group>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -49,19 +49,16 @@ const PhotoImg = photoProps => {
|
|||
)
|
||||
}
|
||||
|
||||
class LazyPhoto extends React.Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return nextProps.src != this.props.src
|
||||
}
|
||||
|
||||
render() {
|
||||
const LazyPhoto = React.memo(
|
||||
props => {
|
||||
return (
|
||||
<LazyLoad scrollContainer="#layout-content">
|
||||
<PhotoImg {...this.props} />
|
||||
<PhotoImg {...props} />
|
||||
</LazyLoad>
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
(prevProps, nextProps) => prevProps.src === nextProps.src
|
||||
)
|
||||
|
||||
LazyPhoto.propTypes = {
|
||||
src: PropTypes.string,
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Route, Redirect } from 'react-router-dom'
|
||||
import { gql } from '@apollo/client'
|
||||
import { Query } from '@apollo/client/react/components'
|
||||
import { useQuery, gql } from '@apollo/client'
|
||||
import { authToken } from '../../authentication'
|
||||
|
||||
export const ADMIN_QUERY = gql`
|
||||
|
@ -29,19 +28,13 @@ const AuthorizedRoute = ({ component: Component, admin = false, ...props }) => {
|
|||
|
||||
let adminRedirect = null
|
||||
if (token && admin) {
|
||||
adminRedirect = (
|
||||
<Query query={ADMIN_QUERY}>
|
||||
{({ error, data }) => {
|
||||
if (error) alert(error)
|
||||
const { error, data } = useQuery(ADMIN_QUERY)
|
||||
|
||||
if (data && data.myUser && !data.myUser.admin) {
|
||||
return <Redirect to="/" />
|
||||
}
|
||||
if (error) alert(error)
|
||||
|
||||
return null
|
||||
}}
|
||||
</Query>
|
||||
)
|
||||
if (data && data.myUser && !data.myUser.admin) {
|
||||
adminRedirect = <Redirect to="/" />
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -54,7 +47,7 @@ const AuthorizedRoute = ({ component: Component, admin = false, ...props }) => {
|
|||
<Component {...routeProps} />
|
||||
</>
|
||||
)}
|
||||
></Route>
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -24,37 +24,35 @@ const SettingsPage = React.lazy(() =>
|
|||
import('../../Pages/SettingsPage/SettingsPage')
|
||||
)
|
||||
|
||||
class Routes extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<React.Suspense
|
||||
fallback={
|
||||
<Layout>
|
||||
<Loader active>Loading page</Loader>
|
||||
</Layout>
|
||||
}
|
||||
>
|
||||
<Switch>
|
||||
<Route path="/login" component={LoginPage} />
|
||||
<Route path="/logout">
|
||||
{() => {
|
||||
clearTokenCookie()
|
||||
location.href = '/'
|
||||
}}
|
||||
</Route>
|
||||
<Route path="/initialSetup" component={InitialSetupPage} />
|
||||
<Route path="/share" component={SharePage} />
|
||||
<AuthorizedRoute exact path="/albums" component={AlbumsPage} />
|
||||
<AuthorizedRoute path="/album/:id/:subPage?" component={AlbumPage} />
|
||||
<AuthorizedRoute path="/photos/:subPage?" component={PhotosPage} />
|
||||
<AuthorizedRoute path="/places" component={PlacesPage} />
|
||||
<AuthorizedRoute admin path="/settings" component={SettingsPage} />
|
||||
<Route path="/" exact render={() => <Redirect to="/photos" />} />
|
||||
<Route render={() => <div>Page not found</div>} />
|
||||
</Switch>
|
||||
</React.Suspense>
|
||||
)
|
||||
}
|
||||
const Routes = () => {
|
||||
return (
|
||||
<React.Suspense
|
||||
fallback={
|
||||
<Layout>
|
||||
<Loader active>Loading page</Loader>
|
||||
</Layout>
|
||||
}
|
||||
>
|
||||
<Switch>
|
||||
<Route path="/login" component={LoginPage} />
|
||||
<Route path="/logout">
|
||||
{() => {
|
||||
clearTokenCookie()
|
||||
location.href = '/'
|
||||
}}
|
||||
</Route>
|
||||
<Route path="/initialSetup" component={InitialSetupPage} />
|
||||
<Route path="/share" component={SharePage} />
|
||||
<AuthorizedRoute exact path="/albums" component={AlbumsPage} />
|
||||
<AuthorizedRoute path="/album/:id/:subPage?" component={AlbumPage} />
|
||||
<AuthorizedRoute path="/photos/:subPage?" component={PhotosPage} />
|
||||
<AuthorizedRoute path="/places" component={PlacesPage} />
|
||||
<AuthorizedRoute admin path="/settings" component={SettingsPage} />
|
||||
<Route path="/" exact render={() => <Redirect to="/photos" />} />
|
||||
<Route render={() => <div>Page not found</div>} />
|
||||
</Switch>
|
||||
</React.Suspense>
|
||||
)
|
||||
}
|
||||
|
||||
export default Routes
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Query } from '@apollo/client/react/components'
|
||||
import { gql } from '@apollo/client'
|
||||
import { useQuery, gql } from '@apollo/client'
|
||||
import SidebarShare from './Sharing'
|
||||
|
||||
const albumQuery = gql`
|
||||
|
@ -14,22 +13,20 @@ const albumQuery = gql`
|
|||
`
|
||||
|
||||
const AlbumSidebar = ({ albumId }) => {
|
||||
const { loading, error, data } = useQuery(albumQuery, {
|
||||
variables: { id: albumId },
|
||||
})
|
||||
|
||||
if (loading) return <div>Loading...</div>
|
||||
if (error) return <div>{error.message}</div>
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>Album options</p>
|
||||
<Query query={albumQuery} variables={{ id: albumId }}>
|
||||
{({ loading, error, data }) => {
|
||||
if (loading) return <div>Loading...</div>
|
||||
if (error) return <div>{error.message}</div>
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{data.album.title}</h1>
|
||||
<SidebarShare album={data.album} />
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
</Query>
|
||||
<div>
|
||||
<h1>{data.album.title}</h1>
|
||||
<SidebarShare album={data.album} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { createContext } from 'react'
|
||||
import React, { createContext, useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import styled from 'styled-components'
|
||||
import { Icon } from 'semantic-ui-react'
|
||||
|
@ -41,42 +41,36 @@ const SidebarDismissButton = styled(Icon)`
|
|||
export const SidebarContext = createContext()
|
||||
SidebarContext.displayName = 'SidebarContext'
|
||||
|
||||
class Sidebar extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
const Sidebar = ({ children }) => {
|
||||
const [state, setState] = useState({
|
||||
content: null,
|
||||
})
|
||||
|
||||
this.state = {
|
||||
content: null,
|
||||
}
|
||||
|
||||
this.update = content => {
|
||||
this.setState({ content })
|
||||
}
|
||||
const update = content => {
|
||||
setState({ content })
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SidebarContext.Provider
|
||||
value={{ updateSidebar: this.update, content: this.state.content }}
|
||||
>
|
||||
{this.props.children}
|
||||
<SidebarContext.Consumer>
|
||||
{value => (
|
||||
<SidebarContainer highlighted={value.content != null}>
|
||||
{value.content}
|
||||
<SidebarDismissButton
|
||||
name="angle double right"
|
||||
size="big"
|
||||
link
|
||||
onClick={() => this.setState({ content: null })}
|
||||
/>
|
||||
<div style={{ height: 100 }}></div>
|
||||
</SidebarContainer>
|
||||
)}
|
||||
</SidebarContext.Consumer>
|
||||
</SidebarContext.Provider>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<SidebarContext.Provider
|
||||
value={{ updateSidebar: update, content: state.content }}
|
||||
>
|
||||
{children}
|
||||
<SidebarContext.Consumer>
|
||||
{value => (
|
||||
<SidebarContainer highlighted={value.content != null}>
|
||||
{value.content}
|
||||
<SidebarDismissButton
|
||||
name="angle double right"
|
||||
size="big"
|
||||
link
|
||||
onClick={() => setState({ content: null })}
|
||||
/>
|
||||
<div style={{ height: 100 }}></div>
|
||||
</SidebarContainer>
|
||||
)}
|
||||
</SidebarContext.Consumer>
|
||||
</SidebarContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
Sidebar.propTypes = {
|
||||
|
|
Loading…
Reference in New Issue