From 034b85e1f7bf013f06f7f57425f4949df845a12c Mon Sep 17 00:00:00 2001 From: viktorstrate Date: Fri, 19 Jul 2019 21:39:51 +0200 Subject: [PATCH] Add right sidebar --- api/src/resolvers/photos.js | 54 ++++++++++++++++++ api/src/schema.graphql | 3 + ui/src/AuthorizedRoute.js | 25 +++++++++ ui/src/Layout.js | 59 ++++++++++++-------- ui/src/Pages/AlbumPage/AlbumPage.js | 52 +++++++++++++---- ui/src/Pages/AlbumPage/AlbumSidebar.js | 71 ++++++++++++++++++++++++ ui/src/Pages/AlbumPage/styledElements.js | 23 ++++++-- ui/src/Pages/HomePage/Albums.js | 2 +- ui/src/Routes.js | 13 +---- 9 files changed, 253 insertions(+), 49 deletions(-) create mode 100644 ui/src/AuthorizedRoute.js create mode 100644 ui/src/Pages/AlbumPage/AlbumSidebar.js diff --git a/api/src/resolvers/photos.js b/api/src/resolvers/photos.js index 45fff484..0a9e2e16 100644 --- a/api/src/resolvers/photos.js +++ b/api/src/resolvers/photos.js @@ -39,6 +39,27 @@ const myAlbumQuery = function(args, ctx, info) { return query } +const myPhotoQuery = function(args, ctx, info) { + const query = cypherQuery(args, ctx, info) + + const whereSplit = query[0].indexOf('RETURN') + + query[0] = injectAt( + query[0], + whereSplit, + `MATCH (u:User { id: {userid} }) WHERE (u)-[:OWNS]->(:Album)-[:CONTAINS]->(photo) ` + ) + query[1].userid = ctx.user.id + + query[0] = injectAt( + query[0], + query[0].indexOf('RETURN `photo` {') + 16, + query[0].indexOf('RETURN `photo` {}') == -1 ? ` .id, ` : ` .id ` + ) + + return query +} + const Query = { async myAlbums(root, args, ctx, info) { let query = myAlbumQuery(args, ctx, info) @@ -73,6 +94,39 @@ const Query = { return result.records[0].get('album') }, + async myPhotos(root, args, ctx, info) { + let query = myPhotoQuery(args, ctx, info) + console.log(query) + + const session = ctx.driver.session() + + const result = await session.run(...query) + + session.close() + + return result.records.map(record => record.get('photo')) + }, + async photo(root, args, ctx, info) { + const session = ctx.driver.session() + + let query = myPhotoQuery(args, ctx, info) + + const whereSplit = query[0].indexOf('RETURN') + + query[0] = injectAt(query[0], whereSplit, ` AND photo.id = {id} `) + query[1].id = args.id + console.log(query) + + const result = await session.run(...query) + + session.close() + + if (result.records.length == 0) { + throw new Error('Album was not found') + } + + return result.records[0].get('photo') + }, } function photoResolver(image) { diff --git a/api/src/schema.graphql b/api/src/schema.graphql index e549fad6..b1a4a35d 100644 --- a/api/src/schema.graphql +++ b/api/src/schema.graphql @@ -75,4 +75,7 @@ type Query { myAlbums: [Album] @isAuthenticated album(id: ID): Album @isAuthenticated + + myPhotos: [Photo] @isAuthenticated + photo(id: ID): Photo @isAuthenticated } diff --git a/ui/src/AuthorizedRoute.js b/ui/src/AuthorizedRoute.js new file mode 100644 index 00000000..05556d14 --- /dev/null +++ b/ui/src/AuthorizedRoute.js @@ -0,0 +1,25 @@ +import React from 'react' +import { Route, Redirect } from 'react-router-dom' + +const AuthorizedRoute = ({ component: Component, ...props }) => { + const token = localStorage.getItem('token') + + let unauthorizedRedirect = null + if (!token) { + unauthorizedRedirect = + } + + return ( + ( + <> + {unauthorizedRedirect} + + + )} + > + ) +} + +export default AuthorizedRoute diff --git a/ui/src/Layout.js b/ui/src/Layout.js index 9a44d4da..9eba734c 100644 --- a/ui/src/Layout.js +++ b/ui/src/Layout.js @@ -1,9 +1,11 @@ import React, { Component } from 'react' import styled from 'styled-components' -import { Link } from 'react-router-dom' +import { NavLink } from 'react-router-dom' +import { Icon } from 'semantic-ui-react' const Container = styled.div` height: 100%; + margin-right: 500px; /* display: grid; grid-template-columns: 80px 1fr 500px; */ ` @@ -17,31 +19,42 @@ const LeftSidebar = styled.div` padding-top: 10px; ` -const RightSidebar = styled.div` - height: 100%; - width: 500px; - position: fixed; - right: 0; - top: 60px; - background-color: white; -` - const Content = styled.div` margin-top: 60px; margin-left: 80px; - margin-right: 500px; padding: 12px 8px 0; ` -const SideButton = styled(Link)` - border: 1px solid #eee; - text-align: center; - padding-top: 17px; - border-radius: 50%; - display: block; - width: 60px; - height: 60px; - margin: 10px; +const SideButton = props => { + const StyledLink = styled(NavLink)` + text-align: center; + padding-top: 8px; + padding-left: 2px; + display: block; + width: 60px; + height: 60px; + margin: 10px; + + font-size: 28px; + + color: #888; + + transition: transform 200ms, box-shadow 200ms; + + :hover { + transform: scale(1.02); + } + ` + + return ( + + {props.children} + + ) +} + +const SideButtonLabel = styled.div` + font-size: 16px; ` const Header = styled.div` @@ -64,10 +77,12 @@ class Layout extends Component { return ( - Albums + + + Albums + {this.props.children} - Right
Photoview
diff --git a/ui/src/Pages/AlbumPage/AlbumPage.js b/ui/src/Pages/AlbumPage/AlbumPage.js index 45bc0254..b0956994 100644 --- a/ui/src/Pages/AlbumPage/AlbumPage.js +++ b/ui/src/Pages/AlbumPage/AlbumPage.js @@ -1,8 +1,16 @@ import React, { Component } from 'react' import gql from 'graphql-tag' import { Query } from 'react-apollo' -import { Gallery, Photo, PhotoFiller } from './styledElements' +import { + Gallery, + Photo, + PhotoFiller, + PhotoContainer, + PhotoOverlay, +} from './styledElements' import Layout from '../../Layout' +import { Loader } from 'semantic-ui-react' +import AlbumSidebar from './AlbumSidebar' const albumQuery = gql` query albumQuery($id: ID) { @@ -33,7 +41,7 @@ class AlbumPage extends Component { this.photoAmount = 1 this.previousActive = false - this.keyUpEvent = e => { + this.keyDownEvent = e => { const activeImage = this.state.activeImage if (activeImage != -1) { if (e.key == 'ArrowRight') { @@ -52,11 +60,11 @@ class AlbumPage extends Component { } componentDidMount() { - document.addEventListener('keyup', this.keyUpEvent) + document.addEventListener('keydown', this.keyDownEvent) } componentWillUnmount() { - document.removeEventListener('keyup', this.keyUpEvent) + document.removeEventListener('keydown', this.keyDownEvent) } setActiveImage(index) { @@ -80,23 +88,45 @@ class AlbumPage extends Component { {({ loading, error, data }) => { if (error) return
Error
- if (loading) return
Loading
- this.photoAmount = data.album.photos.length + let photos = null + if (data.album) { + this.photoAmount = data.album.photos.length - const { activeImage } = this.state + const { activeImage } = this.state - const photos = data.album.photos.map((photo, index) => { - return - }) + photos = data.album.photos.map((photo, index) => { + const active = activeImage == index + + return ( + { + this.setActiveImage(index) + }} + > + + + + ) + }) + } return (
-

{data.album.title}

+

{data.album ? data.album.title : ''}

+ Loading images {photos} +
) }} diff --git a/ui/src/Pages/AlbumPage/AlbumSidebar.js b/ui/src/Pages/AlbumPage/AlbumSidebar.js new file mode 100644 index 00000000..65a2a5fb --- /dev/null +++ b/ui/src/Pages/AlbumPage/AlbumSidebar.js @@ -0,0 +1,71 @@ +import React, { Component } from 'react' +import styled from 'styled-components' +import { Query } from 'react-apollo' +import gql from 'graphql-tag' + +const photoQuery = gql` + query sidebarPhoto($id: ID) { + photo(id: $id) { + title + original { + path + width + height + } + } + } +` + +const RightSidebar = styled.div` + height: 100%; + width: 500px; + position: fixed; + right: 0; + top: 60px; + background-color: white; + padding: 12px; + border-left: 1px solid #eee; +` + +const PreviewImage = styled.img` + width: 100%; + height: 333px; + object-fit: contain; +` + +const Name = styled.div` + text-align: center; + font-size: 16px; +` + +class AlbumSidebar extends Component { + render() { + const { imageId } = this.props + + if (!imageId) { + return + } + + return ( + + + {({ loading, error, data }) => { + if (loading) return 'Loading...' + if (error) return error + + const { photo } = data + + return ( +
+ + {photo.title} +
+ ) + }} +
+
+ ) + } +} + +export default AlbumSidebar diff --git a/ui/src/Pages/AlbumPage/styledElements.js b/ui/src/Pages/AlbumPage/styledElements.js index e729bcc8..d5d9ab98 100644 --- a/ui/src/Pages/AlbumPage/styledElements.js +++ b/ui/src/Pages/AlbumPage/styledElements.js @@ -6,19 +6,32 @@ export const Gallery = styled.div` align-items: center; ` -export const Photo = styled.img` +export const PhotoContainer = styled.div` + flex-grow: 1; + height: 200px; margin: 4px; background-color: #eee; + position: relative; +` + +export const Photo = styled.img` height: 200px; - flex-grow: 1; + min-width: 100%; + position: relative; object-fit: cover; +` + +export const PhotoOverlay = styled.div` + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; ${props => props.active && ` - will-change: transform; - position: relative; - z-index: 999; + border: 4px solid rgba(65, 131, 196, 0.6); `} ` diff --git a/ui/src/Pages/HomePage/Albums.js b/ui/src/Pages/HomePage/Albums.js index bf647a1d..be866b59 100644 --- a/ui/src/Pages/HomePage/Albums.js +++ b/ui/src/Pages/HomePage/Albums.js @@ -3,7 +3,7 @@ import gql from 'graphql-tag' import { Query } from 'react-apollo' import styled from 'styled-components' import { Link } from 'react-router-dom' -import { Dimmer, Loader } from 'semantic-ui-react' +import { Loader } from 'semantic-ui-react' const getAlbumsQuery = gql` query getMyAlbums { diff --git a/ui/src/Routes.js b/ui/src/Routes.js index 78cb17e2..94fd2d9c 100644 --- a/ui/src/Routes.js +++ b/ui/src/Routes.js @@ -4,22 +4,15 @@ import { Route, Switch } from 'react-router-dom' import LoginPage from './Pages/LoginPage' import HomePage from './Pages/HomePage/HomePage' import AlbumPage from './Pages/AlbumPage/AlbumPage' +import AuthorizedRoute from './AuthorizedRoute' class Routes extends Component { render() { - const token = localStorage.getItem('token') - - let unauthorizedRedirect = null - if (!token) { - unauthorizedRedirect = - } - return ( - {unauthorizedRedirect} - - + + ) }