diff --git a/ui/src/App.js b/ui/src/App.js index f6d5891c..1bdfeb2d 100644 --- a/ui/src/App.js +++ b/ui/src/App.js @@ -1,22 +1,23 @@ import React, { Component } from 'react' -import { Redirect } from 'react-router-dom' +import { createGlobalStyle } from 'styled-components' + +const GlobalStyle = createGlobalStyle` + #root, body { + height: 100%; + margin: 0; + } +` import Routes from './Routes' class App extends Component { render() { - const token = localStorage.getItem('token') - - if (!token) { - return ( - <> - - - - ) - } - - return + return ( + <> + + + + ) } } diff --git a/ui/src/Layout.js b/ui/src/Layout.js new file mode 100644 index 00000000..d3b8dba2 --- /dev/null +++ b/ui/src/Layout.js @@ -0,0 +1,46 @@ +import React, { Component } from 'react' +import styled from 'styled-components' + +const Container = styled.div` + height: 100%; + /* display: grid; + grid-template-columns: 80px 1fr 500px; */ +` + +const LeftSidebar = styled.div` + height: 100%; + width: 80px; + position: fixed; + top: 0; + left: 0; + background-color: #eee; +` + +const RightSidebar = styled.div` + height: 100%; + width: 500px; + position: fixed; + right: 0; + top: 0; + background-color: #eee; +` + +const Content = styled.div` + margin-left: 80px; + margin-right: 500px; + padding: 0 8px; +` + +class Layout extends Component { + render() { + return ( + + Left + {this.props.children} + Right + + ) + } +} + +export default Layout diff --git a/ui/src/Pages/AlbumPage.js b/ui/src/Pages/AlbumPage.js deleted file mode 100644 index bc6d719e..00000000 --- a/ui/src/Pages/AlbumPage.js +++ /dev/null @@ -1,230 +0,0 @@ -import React, { Component } from 'react' -import gql from 'graphql-tag' -import { Query } from 'react-apollo' -import styled from 'styled-components' -import { useSpring, animated } from 'react-spring' - -const albumQuery = gql` - query albumQuery($id: ID) { - album(id: $id) { - title - photos { - id - thumbnail { - path - width - height - } - } - } - } -` - -const Gallery = styled.div` - display: flex; - flex-wrap: wrap; - align-items: center; -` - -const Photo = styled.img` - margin: 4px; - background-color: #eee; - display: inline-block; - height: 200px; - flex-grow: 1; - object-fit: cover; - - &:nth-last-child(6) ~ img { - flex-grow: 0; - } - - ${props => - props.active && - ` - will-change: transform; - position: relative; - z-index: 999; - `} -` - -const Dimmer = ({ onClick, active }) => { - const [props, set, stop] = useSpring(() => ({ opacity: 0 })) - - set({ - opacity: active ? 1 : 0, - }) - - const AnimatedDimmer = styled(animated.div)` - position: fixed; - width: 100%; - height: 100%; - background-color: black; - margin: 0; - z-index: 10; - ` - - return ( - - ) -} - -class AlbumPage extends Component { - constructor(props) { - super(props) - - this.state = { - activeImage: -1, - } - - this.setActiveImage = this.setActiveImage.bind(this) - this.animateImage = this.animateImage.bind(this) - - this.photoAmount = 1 - this.previousActive = false - - this.scrollEvent = () => { - if (this.state.activeImage != -1) { - this.dismissPopup() - } - } - - this.keyUpEvent = e => { - const activeImage = this.state.activeImage - if (activeImage != -1) { - if (e.key == 'ArrowRight') { - this.setActiveImage((activeImage + 1) % this.photoAmount) - } - - if (e.key == 'ArrowLeft') { - if (activeImage <= 0) { - this.setActiveImage(this.photoAmount - 1) - } else { - this.setActiveImage(activeImage - 1) - } - } - } - } - } - - componentDidMount() { - document.addEventListener('scroll', this.scrollEvent) - document.addEventListener('keyup', this.keyUpEvent) - } - - componentWillUnmount() { - document.removeEventListener('scroll', this.scrollEvent) - document.removeEventListener('keyup', this.keyUpEvent) - } - - setActiveImage(index) { - this.previousActive = this.state.activeImage != -1 - this.setState({ - activeImage: index, - }) - } - - dismissPopup() { - this.previousActive = true - this.setState({ - activeImage: -1, - }) - } - - animateImage(target) { - const margin = 32 - - const windowWidth = window.innerWidth - const windowHeight = window.innerHeight - - const viewportWidth = windowWidth - margin * 2 - const viewportHeight = windowHeight - margin * 2 - - const { naturalWidth, naturalHeight } = target - const { width, height, top, left } = target.getBoundingClientRect() - - const scaleX = Math.min(naturalWidth, viewportWidth) / width - const scaleY = Math.min(naturalHeight, viewportHeight) / height - const scale = Math.min(scaleX, scaleY) - - const translateX = (-left + (windowWidth - width) / 2) / scale - const translateY = (-top + (windowHeight - height) / 2) / scale - - if (!this.previousActive) target.style.transition = `transform 0.4s` - - requestAnimationFrame(() => { - target.style.transform = `scale(${scale}) - translate3d(${translateX}px, ${translateY}px, 0)` - }) - } - - render() { - const albumId = this.props.match.params.id - - // let dimmer = this.state.activeImage != -1 && ( - // this.setActiveImage(-1)} /> - // ) - - return ( -
- this.setActiveImage(-1)} - active={this.state.activeImage != -1} - /> - - {({ loading, error, data }) => { - if (error) return
Error
- if (loading) return
Loading
- - this.photoAmount = data.album.photos.length - - const { activeImage } = this.state - - const photos = data.album.photos.map((photo, index) => { - const active = index == activeImage - let ref = null - let style = {} - - if (active) { - ref = target => { - if (target == null) return - this.animateImage(target) - } - } else { - style.transform = null - } - - return ( - { - requestAnimationFrame(() => { - if (activeImage != index) { - this.setActiveImage(index) - } else { - this.dismissPopup() - } - }) - }} - style={style} - src={photo.thumbnail.path} - > - ) - }) - - return {photos} - }} -
-
- ) - } -} - -export default AlbumPage diff --git a/ui/src/Pages/AlbumPage/AlbumPage.js b/ui/src/Pages/AlbumPage/AlbumPage.js new file mode 100644 index 00000000..f54a7848 --- /dev/null +++ b/ui/src/Pages/AlbumPage/AlbumPage.js @@ -0,0 +1,106 @@ +import React, { Component } from 'react' +import gql from 'graphql-tag' +import { Query } from 'react-apollo' +import { Gallery, Photo, PhotoFiller } from './styledElements' +import Layout from '../../Layout' + +const albumQuery = gql` + query albumQuery($id: ID) { + album(id: $id) { + title + photos { + id + thumbnail { + path + width + height + } + } + } + } +` + +class AlbumPage extends Component { + constructor(props) { + super(props) + + this.state = { + activeImage: -1, + } + + this.setActiveImage = this.setActiveImage.bind(this) + + this.photoAmount = 1 + this.previousActive = false + + this.keyUpEvent = e => { + const activeImage = this.state.activeImage + if (activeImage != -1) { + if (e.key == 'ArrowRight') { + this.setActiveImage((activeImage + 1) % this.photoAmount) + } + + if (e.key == 'ArrowLeft') { + if (activeImage <= 0) { + this.setActiveImage(this.photoAmount - 1) + } else { + this.setActiveImage(activeImage - 1) + } + } + } + } + } + + componentDidMount() { + document.addEventListener('keyup', this.keyUpEvent) + } + + componentWillUnmount() { + document.removeEventListener('keyup', this.keyUpEvent) + } + + setActiveImage(index) { + this.previousActive = this.state.activeImage != -1 + this.setState({ + activeImage: index, + }) + } + + dismissPopup() { + this.setState({ + activeImage: -1, + }) + } + + render() { + const albumId = this.props.match.params.id + + return ( + + + {({ loading, error, data }) => { + if (error) return
Error
+ if (loading) return
Loading
+ + this.photoAmount = data.album.photos.length + + const { activeImage } = this.state + + const photos = data.album.photos.map((photo, index) => { + return + }) + + return ( + + {photos} + + + ) + }} +
+
+ ) + } +} + +export default AlbumPage diff --git a/ui/src/Pages/AlbumPage/Dimmer.js b/ui/src/Pages/AlbumPage/Dimmer.js new file mode 100644 index 00000000..96f863ac --- /dev/null +++ b/ui/src/Pages/AlbumPage/Dimmer.js @@ -0,0 +1,31 @@ +import React from 'react' +import { useSpring, animated } from 'react-spring' + +const Dimmer = ({ onClick, active }) => { + const [props, set, stop] = useSpring(() => ({ opacity: 0 })) + + set({ + opacity: active ? 1 : 0, + }) + + const AnimatedDimmer = styled(animated.div)` + position: fixed; + width: 100%; + height: 100%; + background-color: black; + margin: 0; + z-index: 10; + ` + + return ( + + ) +} + +export default Dimmer diff --git a/ui/src/Pages/AlbumPage/styledElements.js b/ui/src/Pages/AlbumPage/styledElements.js new file mode 100644 index 00000000..e729bcc8 --- /dev/null +++ b/ui/src/Pages/AlbumPage/styledElements.js @@ -0,0 +1,28 @@ +import styled from 'styled-components' + +export const Gallery = styled.div` + display: flex; + flex-wrap: wrap; + align-items: center; +` + +export const Photo = styled.img` + margin: 4px; + background-color: #eee; + height: 200px; + flex-grow: 1; + object-fit: cover; + + ${props => + props.active && + ` + will-change: transform; + position: relative; + z-index: 999; + `} +` + +export const PhotoFiller = styled.div` + height: 200px; + flex-grow: 999999; +` diff --git a/ui/src/Albums.js b/ui/src/Pages/HomePage/Albums.js similarity index 100% rename from ui/src/Albums.js rename to ui/src/Pages/HomePage/Albums.js diff --git a/ui/src/Pages/HomePage.js b/ui/src/Pages/HomePage/HomePage.js similarity index 65% rename from ui/src/Pages/HomePage.js rename to ui/src/Pages/HomePage/HomePage.js index 817e78c0..33b3ff30 100644 --- a/ui/src/Pages/HomePage.js +++ b/ui/src/Pages/HomePage/HomePage.js @@ -1,13 +1,14 @@ import React, { Component } from 'react' -import Albums from '../Albums' +import Albums from './Albums' +import Layout from '../../Layout' class HomePage extends Component { render() { return ( -
+

Home

-
+ ) } } diff --git a/ui/src/Routes.js b/ui/src/Routes.js index 5937b832..78cb17e2 100644 --- a/ui/src/Routes.js +++ b/ui/src/Routes.js @@ -2,13 +2,21 @@ import React, { Component } from 'react' import { Route, Switch } from 'react-router-dom' import LoginPage from './Pages/LoginPage' -import HomePage from './Pages/HomePage' -import AlbumPage from './Pages/AlbumPage' +import HomePage from './Pages/HomePage/HomePage' +import AlbumPage from './Pages/AlbumPage/AlbumPage' class Routes extends Component { render() { + const token = localStorage.getItem('token') + + let unauthorizedRedirect = null + if (!token) { + unauthorizedRedirect = + } + return ( + {unauthorizedRedirect}