Skip to content

Commit

Permalink
Merge pull request cozy#91 from goldoraf/master
Browse files Browse the repository at this point in the history
Shared album public page
  • Loading branch information
goldoraf committed Jun 1, 2017
2 parents 7d9478a + f3e4936 commit b465de8
Show file tree
Hide file tree
Showing 22 changed files with 454 additions and 392 deletions.
1 change: 0 additions & 1 deletion src/actions/index.js
@@ -1,2 +1 @@
export * from './photos'
export * from './selection'
7 changes: 0 additions & 7 deletions src/actions/photos.js

This file was deleted.

3 changes: 3 additions & 0 deletions src/assets/icons/icon-download-16.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 26 additions & 42 deletions src/components/AlbumItem.jsx
@@ -1,70 +1,54 @@
/* global cozy */
import styles from '../styles/albumsList'

import React, { Component } from 'react'
import { Link, withRouter } from 'react-router'
import { translate } from '../lib/I18n'

import { getPhotoLink } from '../actions/photos'
import { fetchAlbumCover } from '../ducks/albums'
import ImageLoader from './ImageLoader'

const isAlbumEmpty = album => !(album && album.photoCount)

const fetchMainPhoto = album =>
isAlbumEmpty(album)
? Promise.resolve(null)
: getPhotoLink(album.coverId)

export class AlbumItem extends Component {
constructor (props) {
super(props)

// Detect right now if there is a main photo to load, otherwise the
// Promise.resolve(null) in fetchMainPhoto will not be considered as it will
// trigger a setState directly in constructor.
const albumIsEmpty = isAlbumEmpty(props.album)

this.state = {
// Set loading state in function of album emptyness.
isLoading: !albumIsEmpty,
isImageLoading: !albumIsEmpty
isLoading: true,
coverPhoto: null
}

this.handleImageLoaded = this.handleImageLoaded.bind(this)
}

componentWillMount () {
fetchMainPhoto(this.props.album)
.then(link => {
this.setState({
url: link,
isLoading: false
})
}).catch(linkError => {
this.props.onServerError(linkError)
this.setState({
url: null,
isLoading: false
})
componentDidMount () {
const { album } = this.props
if (isAlbumEmpty(album) || !album.coverId) {
return this.setState({ isLoading: false })
}
fetchAlbumCover(album)
.then(photo => {
this.setState({ coverPhoto: photo, isLoading: false })
})
.catch(error => {
this.setState({ isLoading: false })
console.log(error)
})
}

handleImageLoaded () {
this.setState({ isImageLoading: false })
}

render () {
if (this.state.isLoading) {
return null
}
const { t, album, onClick } = this.props
const { url, isImageLoading } = this.state
const { coverPhoto } = this.state

const image = <img
className={styles['pho-album-photo-item']}
onLoad={this.handleImageLoaded}
style={isImageLoading ? 'display:none' : ''}
alt={`${album.name} album cover`}
src={url || ''}
/>
const image = !coverPhoto
? <img />
: <ImageLoader
className={styles['pho-album-photo-item']}
alt={`${album.name} album cover`}
photo={coverPhoto}
src={`${cozy.client._url}${coverPhoto.links.small}`}
/>
const desc = <h4 className={styles['pho-album-description']}>
{t('Albums.album_item_description',
{smart_count: album.photoCount})
Expand Down
2 changes: 1 addition & 1 deletion src/components/AppRoute.jsx
Expand Up @@ -5,7 +5,7 @@ import Layout from './Layout'
import Timeline from '../containers/Timeline'
import AlbumsView from '../containers/AlbumsView'
import AlbumPhotos from '../containers/AlbumPhotos'
import Viewer from '../containers/Viewer'
import Viewer from '../components/Viewer'

export const ComingSoon = () => (<p style='margin-left: 2em'>Coming soon!</p>)

Expand Down
54 changes: 54 additions & 0 deletions src/components/ImageLoader.jsx
@@ -0,0 +1,54 @@
/* global cozy */
import React, { Component } from 'react'

// Extreme fallback: returns a direct download link to the raw image
const getPhotoLink = async (photoId) => {
return await cozy.client.files.getDownloadLinkById(photoId)
.then(path => `${cozy.client._url}${path}`)
}

export default class ImageLoader extends Component {
state = {
loading: true,
fallback: null
}

onLoad = () => {
this.setState({ loading: false })
if (this.props.onLoad) this.props.onLoad()
}

onError = () => {
if (!this.img) return // if we already unmounted
if (this.state.fallback && this.img.src === this.state.fallback) return
// extreme fallback
getPhotoLink(this.props.photo._id)
.then(url => {
this.img.src = url
this.setState({ loading: false, fallback: url })
if (this.props.onLoad) this.props.onLoad()
})
}

componentWillUnmount () {
// this is needed because when opening 2 times in a row the same photo in the viewer,
// the second time the onLoad is not fired... This will fire the onError (see above)
this.img.src = ''
}

render () {
const { photo, src, alt, className, style = {} } = this.props
const { loading } = this.state
return (
<img
ref={img => { this.img = img }}
className={className}
onLoad={this.onLoad}
onError={this.onError}
style={Object.assign({}, style, loading === true ? { display: 'none' } : {})}
alt={alt || photo.name}
src={src}
/>
)
}
}
113 changes: 40 additions & 73 deletions src/components/Photo.jsx
@@ -1,11 +1,11 @@
/* global cozy */
import styles from '../styles/photoList'

import React, { Component } from 'react'
import React from 'react'
import classNames from 'classnames'
import { Link, withRouter } from 'react-router'

import { getPhotoLink } from '../actions/photos'
import ImageLoader from './ImageLoader'

const getStyleFromBox = box => {
let style = {}
Expand All @@ -20,78 +20,45 @@ const getStyleFromBox = box => {
return style
}

export class Photo extends Component {
constructor (props) {
super(props)
this.state = {
isImageLoading: true,
url: `${cozy.client._url}${props.photo.links.small}`,
fallback: null
}
this.handleImageLoaded = this.handleImageLoaded.bind(this)
this.handleImageError = this.handleImageError.bind(this)
}

handleImageLoaded () {
this.setState({ isImageLoading: false })
}

handleImageError () {
if (this.state.fallback && this.img.src === this.state.fallback) return
// extreme fallback
getPhotoLink(this.props.photo._id)
.then(url => {
this.img.src = url
this.setState({ fallback: url })
})
}

render () {
const { photo, box, selected = false, onToggle, router } = this.props
const { loading, url, isImageLoading } = this.state
const parentPath = router.location.pathname
return (
<div
className={classNames(
styles['pho-photo'],
{ [styles['pho-photo--selected']]: selected }
)}
style={getStyleFromBox(box)}
>
{ !loading &&
<div>
<span
className={styles['pho-photo-select']}
data-input='checkbox'
onClick={e => {
e.stopImmediatePropagation()
onToggle(photo._id, selected)
}}>
<input
type='checkbox'
checked={selected}
/>
<label />
</span>
<Link to={`${parentPath}/${photo._id}`}>
<img
ref={img => { this.img = img }}
className={styles['pho-photo-item']}
onLoad={this.handleImageLoaded}
onError={this.handleImageError}
style={Object.assign(
getStyleFromBox(box),
isImageLoading ? {display: 'none'} : {})
}
alt={photo.name}
src={url}
/>
</Link>
</div>
}
const Photo = props => {
const { photo, box, selected = false, onToggle, router } = props
const style = getStyleFromBox(box)
return (
<div
style={style}
className={classNames(
styles['pho-photo'],
{ [styles['pho-photo--selected']]: selected }
)}
>
<div>
<span
className={styles['pho-photo-select']}
data-input='checkbox'
onClick={e => {
e.stopImmediatePropagation()
onToggle(photo._id, selected)
}}>
<input
type='checkbox'
checked={selected}
/>
<label />
</span>
<Link to={{
pathname: `${router.location.pathname}/${photo._id}`,
query: router.location.query
}}>
<ImageLoader
photo={photo}
className={styles['pho-photo-item']}
style={style}
src={`${cozy.client._url}${photo.links.small}`}
/>
</Link>
</div>
)
}
</div>
)
}

export default withRouter(Photo)

0 comments on commit b465de8

Please sign in to comment.