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;