stremio-web/src/common/MetaPreview/MetaPreview.js
2019-12-16 14:59:18 +02:00

245 lines
10 KiB
JavaScript

const React = require('react');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const UrlUtils = require('url');
const Icon = require('stremio-icons/dom');
const Image = require('stremio/common/Image');
const ModalDialog = require('stremio/common/ModalDialog');
const SharePrompt = require('stremio/common/SharePrompt');
const routesRegexp = require('stremio/common/routesRegexp');
const useBinaryState = require('stremio/common/useBinaryState');
const ActionButton = require('./ActionButton');
const MetaLinks = require('./MetaLinks');
const MetaPreviewPlaceholder = require('./MetaPreviewPlaceholder');
const styles = require('./styles');
const IMDB_LINK_CATEGORY = 'imdb';
const SHARE_LINK_CATEGORY = 'share';
const ALLOWED_LINK_REDIRECTS = [
routesRegexp.search.regexp,
routesRegexp.discover.regexp,
routesRegexp.metadetails.regexp
];
const MetaPreview = ({ className, compact, name, logo, background, runtime, releaseInfo, released, description, links, trailer, inLibrary, toggleInLibrary }) => {
const [shareModalOpen, openShareModal, closeShareModal] = useBinaryState(false);
const linksGroups = React.useMemo(() => {
return Array.isArray(links) ?
links
.filter((link) => {
return link &&
typeof link.category === 'string' &&
typeof link.url === 'string';
})
.reduce((linksGroups, { category, name, url }) => {
if (category === IMDB_LINK_CATEGORY) {
linksGroups[category] = {
label: name,
href: `https://www.stremio.com/warning#${encodeURIComponent(`https://www.imdb.com/title/${url}`)}`
};
} else if (category === SHARE_LINK_CATEGORY) {
linksGroups[category] = {
label: name,
href: url
};
} else {
const { protocol, host, path, pathname } = UrlUtils.parse(url);
if (protocol === 'stremio:') {
if (ALLOWED_LINK_REDIRECTS.some((regexp) => pathname.match(regexp))) {
linksGroups[category] = linksGroups[category] || [];
linksGroups[category].push({
label: name,
href: `#${path}`
});
}
} else {
if (typeof host === 'string' && host.length > 0) {
linksGroups[category] = linksGroups[category] || [];
linksGroups[category].push({
label: name,
href: `https://www.stremio.com/warning#${encodeURIComponent(url)}`
});
}
}
}
return linksGroups;
}, {})
:
[];
}, [links]);
return (
<div className={classnames(className, styles['meta-preview-container'], { [styles['compact']]: compact })}>
{
typeof background === 'string' && background.length > 0 ?
<div className={styles['background-image-layer']}>
<img
key={background}
className={styles['background-image']}
src={background}
alt={' '}
/>
</div>
:
null
}
<div className={styles['meta-info-container']}>
{
typeof logo === 'string' && logo.length > 0 ?
<Image
key={logo}
className={styles['logo']}
src={logo}
alt={' '}
renderFallback={() => (
<Icon
className={styles['logo-placeholder-icon']}
icon={'ic_broken_link'}
/>
)}
/>
:
null
}
{
(typeof releaseInfo === 'string' && releaseInfo.length > 0) || (released instanceof Date && !isNaN(released.getTime())) || (typeof runtime === 'string' && runtime.length > 0) ?
<div className={styles['runtime-release-info-container']}>
{
typeof releaseInfo === 'string' && releaseInfo.length > 0 ?
<div className={styles['release-info-label']}>{releaseInfo}</div>
:
released instanceof Date && !isNaN(released.getTime()) ?
<div className={styles['release-info-label']}>{released.getFullYear()}</div>
:
null
}
{
typeof runtime === 'string' && runtime.length > 0 ?
<div className={styles['runtime-label']}>{runtime}</div>
:
null
}
</div>
:
null
}
{
typeof name === 'string' && name.length > 0 ?
<div className={styles['name-container']}>
{name}
</div>
:
null
}
{
typeof description === 'string' && description.length > 0 ?
<div className={styles['description-container']}>{description}</div>
:
null
}
{
Object.keys(linksGroups)
.filter((category) => {
return category !== IMDB_LINK_CATEGORY &&
category !== SHARE_LINK_CATEGORY;
})
.map((category, index) => (
<MetaLinks
key={index}
className={styles['meta-links']}
label={category}
links={linksGroups[category]}
/>
))
}
</div>
<div className={styles['action-buttons-container']}>
{
typeof toggleInLibrary === 'function' ?
<ActionButton
className={styles['action-button']}
icon={inLibrary ? 'ic_removelib' : 'ic_addlib'}
label={inLibrary ? 'Remove from Library' : 'Add to library'}
onClick={toggleInLibrary}
{...(compact ? { tabIndex: -1 } : null)}
/>
:
null
}
{
typeof trailer === 'object' && trailer !== null ?
<ActionButton
className={styles['action-button']}
icon={'ic_movies'}
label={'Trailer'}
href={`#/player?stream=${JSON.stringify(trailer)}`}
{...(compact ? { tabIndex: -1 } : null)}
/>
:
null
}
{
typeof linksGroups[IMDB_LINK_CATEGORY] === 'object' ?
<ActionButton
{...linksGroups[IMDB_LINK_CATEGORY]}
className={styles['action-button']}
icon={'ic_imdb'}
target={'_blank'}
{...(compact ? { tabIndex: -1 } : null)}
/>
:
null
}
{
!compact && typeof linksGroups[SHARE_LINK_CATEGORY] === 'object' ?
<React.Fragment>
<ActionButton
className={styles['action-button']}
icon={'ic_share'}
label={'Share'}
onClick={openShareModal}
{...(compact ? { tabIndex: -1 } : null)}
/>
{
shareModalOpen ?
<ModalDialog title={'Share'} onCloseRequest={closeShareModal}>
<SharePrompt
className={styles['share-prompt']}
url={linksGroups[SHARE_LINK_CATEGORY].href}
/>
</ModalDialog>
:
null
}
</React.Fragment>
:
null
}
</div>
</div>
);
};
MetaPreview.Placeholder = MetaPreviewPlaceholder;
MetaPreview.propTypes = {
className: PropTypes.string,
compact: PropTypes.bool,
name: PropTypes.string,
logo: PropTypes.string,
background: PropTypes.string,
runtime: PropTypes.string,
releaseInfo: PropTypes.string,
released: PropTypes.instanceOf(Date),
description: PropTypes.string,
links: PropTypes.arrayOf(PropTypes.shape({
category: PropTypes.string,
name: PropTypes.string,
url: PropTypes.string
})),
trailer: PropTypes.object,
inLibrary: PropTypes.bool,
toggleInLibrary: PropTypes.func
};
module.exports = MetaPreview;