167 lines
4.4 KiB
JavaScript
167 lines
4.4 KiB
JavaScript
import React from 'react';
|
|
import {
|
|
HashRouter as Router,
|
|
Switch,
|
|
Route,
|
|
useParams
|
|
} from "react-router-dom";
|
|
|
|
import './App.css';
|
|
|
|
const IMAGE_CHUNK = 256;
|
|
const roundup = (v, mod) => {
|
|
let r = v % mod;
|
|
if (r === 0) {
|
|
return v;
|
|
} else {
|
|
return v + (mod - r);
|
|
}
|
|
};
|
|
|
|
class Album extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
error: null,
|
|
mediaItems: null,
|
|
idx: 0,
|
|
};
|
|
}
|
|
componentDidMount() {
|
|
let {album} = this.props;
|
|
fetch(process.env.PUBLIC_URL + `/api/album/${album}`)
|
|
.then(res => res.json())
|
|
.then(
|
|
(result) => this.setState({mediaItems: result}),
|
|
(error) => this.setState({error}),
|
|
);
|
|
}
|
|
render() {
|
|
// TODO(wathiede): fade transition.
|
|
// TODO(wathiede): pair-up portrait orientation images.
|
|
// TODO(wathiede): fetch an image that maintains the originals aspect ratio
|
|
let w = window.innerWidth * window.devicePixelRatio;
|
|
let h = window.innerHeight * window.devicePixelRatio;
|
|
let ratio = w/h;
|
|
if (ratio > 1) {
|
|
// Landscape image
|
|
w = roundup(w, IMAGE_CHUNK);
|
|
h = (w/ratio).toFixed();
|
|
} else {
|
|
// Portrait image
|
|
h = roundup(h, IMAGE_CHUNK);
|
|
w = (h/ratio).toFixed();
|
|
}
|
|
|
|
//let w = roundup(window.innerWidth*window.devicePixelRatio, IMAGE_CHUNK);
|
|
//let h = roundup(window.innerHeight*window.devicePixelRatio, IMAGE_CHUNK);
|
|
let {idx, error, mediaItems} = this.state;
|
|
if (error !== null) {
|
|
return <h2>Error: {JSON.stringify(error)}</h2>;
|
|
} else if (mediaItems !== null) {
|
|
let numImages = mediaItems.length;
|
|
let nextIdx = (idx+1)%numImages;
|
|
let prevIdx = (numImages+idx-1)%numImages;
|
|
let image = mediaItems[idx];
|
|
let nextImage = mediaItems[nextIdx];
|
|
let style = {
|
|
height: '100%',
|
|
width: '100%',
|
|
backgroundColor: 'black',
|
|
backgroundImage: `url(/api/image/${image.id}?w=${w}&h=${h})`,
|
|
backgroundRepeat: 'no-repeat',
|
|
backgroundPosition: 'center center',
|
|
backgroundSize: 'cover',
|
|
};
|
|
let prefetchStyle = {
|
|
// display: 'none'
|
|
position: 'absolute',
|
|
right: 0,
|
|
bottom: 0,
|
|
width: '25%',
|
|
height: '25%',
|
|
};
|
|
console.log(`window.devicePixelRatio ${window.devicePixelRatio}`);
|
|
return <div style={style} onClick={(e)=>{
|
|
e.stopPropagation();
|
|
this.setState({idx: nextIdx})
|
|
}}>
|
|
<img
|
|
style={prefetchStyle}
|
|
onClick={(e)=>{
|
|
e.stopPropagation();
|
|
this.setState({idx: prevIdx})
|
|
}}
|
|
src={`/api/image/${nextImage.id}?w=${w}&h=${h}`} alt="prefetch next" />
|
|
</div>;
|
|
} else {
|
|
return <h2>Loading...</h2>;
|
|
}
|
|
}
|
|
}
|
|
|
|
class AlbumIndex extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
error: null,
|
|
albums: null,
|
|
};
|
|
}
|
|
componentDidMount() {
|
|
fetch(process.env.PUBLIC_URL + "/api/albums")
|
|
.then(res => res.json())
|
|
.then(
|
|
(result) => this.setState({albums: result}),
|
|
(error) => this.setState({error}),
|
|
);
|
|
}
|
|
render() {
|
|
let {error, albums} = this.state;
|
|
if (error !== null) {
|
|
return <h2>Error: {JSON.stringify(error)}</h2>;
|
|
} else if (albums !== null) {
|
|
return albums.map((a) => {
|
|
let img = <img src="https://via.placeholder.com/256x128" className="mr-3" alt="unset"/>;
|
|
if (a.coverPhotoMediaItemId !== undefined) {
|
|
img = <img src={ `/api/image/${a.coverPhotoMediaItemId}?w=256&h=256` } className="mr-3" alt={ a.title }/>
|
|
}
|
|
|
|
let figure = <figure key={ a.id } className="figure">
|
|
{img}
|
|
<figcaption className="figure-caption">{ a.title || "No title" } - { a.mediaItemsCount || 0 } photos </figcaption>
|
|
</figure>;
|
|
return <a key={ a.id } href={ '#' + a.id }>
|
|
{ figure }
|
|
</a>
|
|
});
|
|
} else {
|
|
return <h2>Loading...</h2>;
|
|
}
|
|
}
|
|
}
|
|
|
|
const AlbumRoute = () => {
|
|
// We can use the `useParams` hook here to access
|
|
// the dynamic pieces of the URL.
|
|
let { albumId } = useParams();
|
|
return <Album album={albumId} />;
|
|
}
|
|
|
|
const App = () => {
|
|
return <Router>
|
|
<Switch>
|
|
<Route exact path="/">
|
|
<div className="container">
|
|
<AlbumIndex />
|
|
</div>
|
|
</Route>
|
|
<Route exact path="/:albumId">
|
|
<AlbumRoute />
|
|
</Route>
|
|
</Switch>
|
|
</Router>
|
|
}
|
|
|
|
export default App;
|