site/pages/about-us/social-slice/index.js
// @flow
/* eslint-disable react/no-did-mount-set-state */
import * as React from 'react';
import SwipeableViews from 'react-swipeable-views';
import styles from './styles.css';
import IntroMobileTile from './intro-mobile-tile';
import IntroDesktopTile from './intro-desktop-tile';
import TwitterTile from './twitter-tile';
import type { Tweet } from '../../../types';
import ClientOnly from '../../../components/clientOnly';
type SocialSliceProps = {
tweets: Array<Tweet>,
};
type SocialSliceState = {
tile: number,
totalTiles: number,
viewWidth: number,
};
class SocialSlice extends React.Component<SocialSliceProps, SocialSliceState> {
constructor(props: SocialSliceProps) {
super(props);
this.state = {
tile: 0,
totalTiles: props.tweets.length - 1,
viewWidth: 0,
};
}
componentDidMount() {
this.setState({ viewWidth: window.innerWidth });
window.addEventListener('resize', this.updateViewWidth);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateViewWidth);
}
setTile = (tile: number) => this.setState({ tile });
/**
* Called on every browser resize this updates the width and determines
* the swipableTileTotal tile total
*/
updateViewWidth = () => {
const newWidth = window.innerWidth;
this.setState({ viewWidth: newWidth });
};
/**
* Calulate the number of total tiles that can be swiped
* If we have a view width larger thatn the intro tile + 1 we would get
* black space at the end. By returning a lower number than total tiles this is prevented
*/
calculateSwipableTotal = () => {
const { viewWidth, totalTiles } = this.state;
if (viewWidth > this.tileSize * 2) {
// Adjust for intro slide + 1 slide;
const newTileSize = this.tileSize * 2;
const adjustedWidth = viewWidth - newTileSize;
// Calculate the number of tiles that are in view and then remove that from the total tiles
const tilesToRemove = Math.floor(adjustedWidth / this.tileSize);
return totalTiles - tilesToRemove;
}
return totalTiles;
};
prevTile = () => {
this.setState(({ tile }) => {
const prevTile = tile - 1;
if (prevTile < 0) {
return {};
}
return { tile: prevTile };
});
};
nextTile = () => {
this.setState(({ tile }) => {
const nextTile = tile + 1;
const swipableTotal = this.calculateSwipableTotal();
if (nextTile > swipableTotal) {
return;
}
return { tile: nextTile };
});
};
props: SocialSliceProps;
/** This is the tile width used for calculations - also the value in the CSS styling */
tileSize = 350;
/**
* Calculate the padding that should be added onto the swipe container
* to ensure that the correct distance is swiped. This is dynamic and
* responds to a browser resize. The calculation is based on
* the intro tile + one tile + padding offset of 60
*/
calculateSwipePadding() {
const { viewWidth } = this.state;
const newTileSize = this.tileSize * 2;
return viewWidth - newTileSize;
}
/** As above but for small screen */
calculateMobileSwipePadding() {
const { viewWidth } = this.state;
return viewWidth - this.tileSize;
}
renderTiles = (): React.Node => {
const { tweets } = this.props;
const data = [...tweets.map(tweet => ({ type: 'tweet', tweet, created: tweet.created }))];
if (data.length === 0) {
return null;
}
data.sort((a, b) => new Date(b.created) - new Date(a.created));
const socialComp = (row, index) => {
return <TwitterTile key={row.created} tweet={row.tweet} index={index} />;
};
return data.map(socialComp);
};
render() {
const swipePadding = this.calculateSwipePadding();
const mobileSwipePadding = this.calculateMobileSwipePadding();
return (
<section className={styles.socialSlice}>
<ClientOnly>
{/** Desktop View */}
<div className={styles.desktopView}>
<IntroDesktopTile
nextCard={this.nextTile}
prevCard={this.prevTile}
currentTile={this.state.tile}
totalSwipableTiles={this.calculateSwipableTotal()}
/>
<SwipeableViews
index={this.state.tile}
onChangeIndex={this.setTile}
style={{ paddingRight: swipePadding }}
slideStyle={{ width: 350, height: 525 }}
>
{this.renderTiles()}
</SwipeableViews>
</div>
{/** Mobile View */}
<div className={styles.mobileView}>
<SwipeableViews
index={this.state.tile}
onChangeIndex={this.setTile}
style={{ paddingRight: mobileSwipePadding }}
slideStyle={{ maxWidth: 415 }}
>
<IntroMobileTile />
{this.renderTiles()}
</SwipeableViews>
</div>
</ClientOnly>
{/** No Script View */}
<noscript>
<div className={styles.noscript}>
<IntroDesktopTile
nextCard={this.nextTile}
prevCard={this.prevTile}
currentTile={this.state.tile}
totalSwipableTiles={this.calculateSwipableTotal()}
/>
{this.renderTiles()}
</div>
</noscript>
</section>
);
}
}
export default SocialSlice;