From 320eded1a3d3c025e8c0ab29806680f7db634d11 Mon Sep 17 00:00:00 2001 From: Pedro Abreu Date: Sun, 21 Apr 2019 23:42:03 +0100 Subject: [PATCH] Tidy/Item component (#573) * CSS text-ellipsis of item summary * No need to wrap event props * Fix summary test * CSS solution for torrent health * Use uncontrolled tooltip * Use uncontrolled dropdown * Read playback information from state directly * description component * Remove unused state vars * use data-e2e attribute * Capitalize react component filename --- app/components/item/Description.js | 110 ++++++++++++ app/components/item/Item.js | 267 ++++++----------------------- app/styles/components/Item.scss | 12 ++ app/styles/components/Movie.scss | 23 ++- app/styles/variables.scss | 4 - test/e2e/HomePage.e2e.js | 2 +- 6 files changed, 190 insertions(+), 228 deletions(-) create mode 100644 app/components/item/Description.js diff --git a/app/components/item/Description.js b/app/components/item/Description.js new file mode 100644 index 00000000..88e5b39a --- /dev/null +++ b/app/components/item/Description.js @@ -0,0 +1,110 @@ +import React from 'react'; +import classNames from 'classnames'; +import { Row, Col, UncontrolledTooltip } from 'reactstrap'; + +import Rating from '../card/Rating'; + +type Props = { + title: string, + runtime: Object, + genres: Array, + summary: string, + rating: number, + onTrailerClick: Function, + year: number, + torrentHealth: string, + certification: string, + nrSeeders: number, + trailer: string +}; + +export default function Description({ + title, + runtime, + genres, + summary, + rating = 'n/a', + onTrailerClick, + year, + torrentHealth, + certification, + nrSeeders = 0, + trailer +}: Props) { + const torrentHealthClassName = classNames([ + 'torrent__health', + { [`torrent__health--${torrentHealth}`]: torrentHealth } + ]); + + return ( + +

+ {title} +

+ + {(runtime.hours || runtime.minutes) && ( + +
+ {runtime.hours ? `${runtime.hours} hrs ` : ''} + {runtime.minutes ? `${runtime.minutes} min` : ''} +
+
+ )} + + {genres &&
{genres.join(', ')}
} +
+
+
+ {summary} +
+ + {rating && typeof rating === 'number' && ( + + + + )} + + {year} + + + {certification && certification !== 'n/a' && ( + +
{certification}
+ + )} + + + +
+ + {nrSeeders} + {' Seeders'} + + + + {process.env.NODE_ENV === 'test' && trailer !== 'n/a' && ( + + + + Trailer + + + )} + + + ); +} diff --git a/app/components/item/Item.js b/app/components/item/Item.js index 40b7515a..d038f306 100644 --- a/app/components/item/Item.js +++ b/app/components/item/Item.js @@ -4,8 +4,7 @@ */ import React, { Component } from 'react'; import { - Tooltip, - Dropdown, + UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem, @@ -21,7 +20,7 @@ import Plyr from '@amilajack/react-plyr'; import yifysubtitles from '@amilajack/yifysubtitles'; import CardList from '../card/CardList'; import SaveItem from '../metadata/SaveItem'; -import Rating from '../card/Rating'; +import Description from './Description'; import Show from '../show/Show'; import ChromecastPlayerProvider from '../../api/players/ChromecastPlayerProvider'; import { getIdealTorrent } from '../../api/torrents/BaseTorrentProvider'; @@ -67,19 +66,15 @@ type State = { selectedEpisode: number, seasons: [], season: [], - episode: {}, episodes: [], castingDevices: Array, currentPlayer: playerType, playbackInProgress: boolean, fetchingTorrents: boolean, - dropdownOpen: boolean, idealTorrent: torrentType, - magnetPopoverOpen: boolean, torrent: torrentSelectionType, servingUrl: string, similarLoading: boolean, - metadataLoading: boolean, torrentInProgress: boolean, torrentProgress: number, isFinished: boolean, @@ -103,8 +98,6 @@ export default class Item extends Component { checkCastingDevicesInterval: number; - SUMMARY_CHAR_LIMIT = 300; - defaultTorrent: torrentSelectionType = { default: { quality: undefined, @@ -158,7 +151,6 @@ export default class Item extends Component { } }, servingUrl: undefined, - dropdownOpen: false, isFinished: false, selectedSeason: 1, selectedEpisode: 1, @@ -167,7 +159,6 @@ export default class Item extends Component { episode: {}, castingDevices: [], currentPlayer: 'default', - magnetPopoverOpen: false, playbackInProgress: false, fetchingTorrents: false, idealTorrent: this.defaultTorrent, @@ -218,12 +209,6 @@ export default class Item extends Component { this.setState({ currentPlayer: player }); } - toggle() { - this.setState(prevState => ({ - dropdownOpen: !prevState.dropdownOpen - })); - } - async componentDidMount() { const { itemId } = this.props; window.scrollTo(0, 0); @@ -238,7 +223,6 @@ export default class Item extends Component { this.setState({ ...this.initialState, - dropdownOpen: false, currentPlayer: 'default', favorites: await this.butter.favorites('get'), watchList: await this.butter.watchList('get') @@ -286,40 +270,22 @@ export default class Item extends Component { ]); } - async getShowData( - type: string, - imdbId: string, - season?: number, - episode?: number - ) { + async getShowData(type: string, imdbId: string, season?: number) { switch (type) { case 'seasons': - this.setState({ seasons: [], episodes: [], episode: {} }); + this.setState({ seasons: [], episodes: [] }); this.setState({ seasons: await this.butter.getSeasons(imdbId), - episodes: await this.butter.getSeason(imdbId, 1), - episode: await this.butter.getEpisode(imdbId, 1, 1) + episodes: await this.butter.getSeason(imdbId, 1) }); break; case 'episodes': if (!season) { throw new Error('"season" not provided to getShowData()'); } - this.setState({ episodes: [], episode: {} }); - this.setState({ - episodes: await this.butter.getSeason(imdbId, season), - episode: await this.butter.getEpisode(imdbId, season, 1) - }); - break; - case 'episode': - if (!season || !episode) { - throw new Error( - '"season" or "episode" not provided to getShowData()' - ); - } - this.setState({ episode: {} }); + this.setState({ episodes: [] }); this.setState({ - episode: await this.butter.getEpisode(imdbId, season, episode) + episodes: await this.butter.getSeason(imdbId, season) }); break; default: @@ -332,7 +298,6 @@ export default class Item extends Component { */ async getItem(imdbId: string) { const { activeMode } = this.props; - this.setState({ metadataLoading: true }); const item = await (() => { switch (activeMode) { @@ -345,7 +310,7 @@ export default class Item extends Component { } })(); - this.setState({ item, metadataLoading: false }); + this.setState({ item }); return item; } @@ -494,7 +459,7 @@ export default class Item extends Component { return []; } - stopPlayback() { + stopPlayback = () => { const { torrentInProgress, playbackInProgress, currentPlayer } = this.state; if (!torrentInProgress && !playbackInProgress) { return; @@ -511,7 +476,7 @@ export default class Item extends Component { this.player.destroy(); this.torrent.destroy(); this.setState({ torrentInProgress: false }); - } + }; selectShow = ( type: string, @@ -578,7 +543,7 @@ export default class Item extends Component { return captions; } - closeVideo() { + closeVideo = () => { const { playbackInProgress } = this.state; if (!playbackInProgress) { return; @@ -588,7 +553,7 @@ export default class Item extends Component { this.setState({ currentPlayer: 'default' }); - } + }; toggleActive() { this.setState(prevState => ({ @@ -596,25 +561,24 @@ export default class Item extends Component { })); } - toggleStateProperty(property: string) { - this.setState(prevState => ({ - [property]: !prevState[property] - })); - } - - async startPlayback( - magnet?: string, - activeMode?: string, - currentPlayer: playerType - ) { + startPlayback = async ({ + target: { name: playbackQuality } + }: Event) => { const { + currentPlayer, torrentInProgress, selectedEpisode, selectedSeason, item, - captions + captions, + torrent, + idealTorrent } = this.state; + const { magnet, method: activeMode } = playbackQuality + ? torrent[playbackQuality] + : idealTorrent; + if (torrentInProgress) { this.stopPlayback(); } @@ -646,7 +610,7 @@ export default class Item extends Component { servingUrl: string, file: { name: string }, files: string, - torrent: string + torrentHash: string ) => { console.log(`Serving torrent at: ${servingUrl}`); @@ -701,13 +665,13 @@ export default class Item extends Component { servingUrl }); - return torrent; + return torrentHash; }, downloaded => { console.log('DOWNLOADING', downloaded); } ); - } + }; render() { const { @@ -717,7 +681,6 @@ export default class Item extends Component { servingUrl, torrentInProgress, fetchingTorrents, - dropdownOpen, currentPlayer, seasons, selectedSeason, @@ -729,27 +692,12 @@ export default class Item extends Component { playbackInProgress, favorites, watchList, - magnetPopoverOpen, - trailerPopoverOpen, castingDevices, captions } = this.state; const { activeMode } = this.props; - const statusColorStyle = { - backgroundColor: (() => { - switch (idealTorrent && idealTorrent.health) { - case 'healthy': - return 'green'; - case 'decent': - return 'yellow'; - default: - return 'red'; - } - })() - }; - const itemBackgroundUrl = { backgroundImage: `url(${item.images.fanart.full})` }; @@ -764,7 +712,7 @@ export default class Item extends Component { role="presentation" className="pct-btn pct-btn-tran pct-btn-outline pct-btn-round" data-e2e="item-button-back" - onClick={() => this.stopPlayback()} + onClick={this.stopPlayback} > Back @@ -793,7 +741,7 @@ export default class Item extends Component { data-e2e="close-player" role="presentation" id="close-button" - onClick={() => this.closeVideo()} + onClick={this.closeVideo} > @@ -805,26 +753,14 @@ export default class Item extends Component {
- this.startPlayback( - idealTorrent.magnet, - idealTorrent.method, - currentPlayer - ) - } + onClick={this.startPlayback} > {idealTorrent.magnet && ( - this.startPlayback( - idealTorrent.magnet, - idealTorrent.method, - currentPlayer - ) - } + onClick={this.startPlayback} /> )}
@@ -849,112 +785,26 @@ export default class Item extends Component { watchList={watchList} /> - - -

- {item.title} -

- - {item.runtime && item.runtime.hours && item.runtime.minutes && ( - -
- {item.runtime.hours ? `${item.runtime.hours} hrs ` : ''} - {item.runtime.minutes - ? `${item.runtime.minutes} min` - : ''} -
-
- )} - - {item.genres &&
{item.genres.join(', ')}
} -
-
- {/* HACK: Prefer a CSS solution to this, using text-overflow: ellipse */} -
- {item.summary - ? item.summary.length > this.SUMMARY_CHAR_LIMIT - ? `${item.summary.slice(0, this.SUMMARY_CHAR_LIMIT)}...` - : item.summary - : ''} -
- - {item.rating && typeof item.rating === 'number' && ( - - - - )} - - {item.year} - - - {item && item.certification && item.certification !== 'n/a' && ( - -
{item.certification}
- - )} - - - -
- this.toggleStateProperty('magnetPopoverOpen')} - > - {idealTorrent && idealTorrent.seeders - ? idealTorrent.seeders - : 0}{' '} - Seeders - - - - {process.env.NODE_ENV === 'test' && - item.trailer && - item.trailer !== 'n/a' && ( - - this.setPlayer('youtube')} - role="presentation" - /> - - this.toggleStateProperty('trailerPopoverOpen') - } - > - Trailer - - - )} - - - + this.setPlayer('youtube')} + rating={item.rating} + runtime={item.runtime} + summary={item.summary} + title={item.title} + torrentHeath={idealTorrent.health} + trailer={item.trailer} + year={item.year} + />
- this.toggle()} - > + {currentPlayer || 'default'} @@ -987,33 +837,23 @@ export default class Item extends Component { ))} - + {process.env.FLAG_MANUAL_TORRENT_SELECTION === 'true' && (