addons ui implemented

This commit is contained in:
NikolaBorislavovHristov 2019-09-18 17:11:53 +03:00
parent a449a8f00c
commit c3adb07bc4
7 changed files with 259 additions and 251 deletions

View file

@ -1,117 +1,76 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import Icon from 'stremio-icons/dom';
import styles from './styles';
const MAX_DESCRIPTION_SYMBOLS = 500;
const renderName = (name) => {
if (name.length === 0) {
return null;
}
const React = require('react');
const PropTypes = require('prop-types');
const classnames = require('classnames');
const Icon = require('stremio-icons/dom');
const { Button, useTabIndex } = require('stremio/common');
const styles = require('./styles');
const Addon = ({ className, id, name, logo, description, types, version, transportUrl, installed, toggle }) => {
const tabIndex = useTabIndex();
return (
<div className={styles['name']}>{name}</div>
);
}
const renderVersion = (version) => {
if (version.length === 0) {
return null;
}
return (
<div className={styles['version']}>{'v. ' + version}</div>
);
}
const renderType = (types) => {
if (types.length === 0) {
return null;
}
return (
<div className={styles['types-container']}>
<div className={styles['type']}>
{types.length <= 1 ? types.join('') : types.slice(0, -1).join(', ') + ' & ' + types[types.length - 1]}
</div>
</div>
);
}
const renderHostname = (hostname) => {
if (hostname.length === 0) {
return null;
}
return (
<div className={styles['hostname-container']}>
<div className={styles['hostname']}>{hostname}</div>
</div>
)
}
const renderDescription = (description) => {
if (description.length === 0) {
return null;
}
return (
<div className={styles['description']}>{description.length > MAX_DESCRIPTION_SYMBOLS ? description.slice(0, MAX_DESCRIPTION_SYMBOLS) + '...' : description}</div>
);
}
const Addon = (props) => {
return (
<div className={classnames(styles['addon'], props.className)}>
<div className={classnames(styles['addon-container'], className)} tabIndex={tabIndex} data-id={id}>
<div className={styles['logo-container']}>
<Icon className={styles['logo']} icon={props.logo.length === 0 ? 'ic_addons' : props.logo} />
{
typeof logo === 'string' && logo.length > 0 ?
<img className={styles['logo']} src={logo} alt={' '} />
:
<Icon className={styles['icon']} icon={'ic_addons'} />
}
</div>
<div className={styles['header-container']}>
<div className={styles['header']}>
{renderName(props.name)}
{renderVersion(props.version)}
<div className={styles['info-container']}>
<div className={styles['name-container']} title={typeof name === 'string' && name.length > 0 ? name : id}>
{typeof name === 'string' && name.length > 0 ? name : id}
</div>
{
typeof version === 'string' && version.length > 0 ?
<div className={styles['version-container']} title={`v.${version}`}>v.{version}</div>
:
null
}
{
Array.isArray(types) ?
<div className={styles['types-container']}>
{
types.length <= 1 ?
types.join('')
:
types.slice(0, -1).join(', ') + ' & ' + types[types.length - 1]
}
</div>
:
null
}
{
typeof description === 'string' && description.length > 0 ?
<div className={styles['description-container']} title={description}>{description}</div>
:
null
}
</div>
{renderType(props.types)}
{renderHostname(props.hostname)}
{renderDescription(props.description)}
<div className={styles['buttons']}>
<div className={classnames(styles['button'], props.isInstalled ? styles['uninstall-button'] : styles['install-button'])} onClick={props.onToggleClicked}>
<span className={styles['label']}>{props.isInstalled ? 'Uninstall' : 'Install'}</span>
</div>
<div className={classnames(styles['button'], styles['share-button'])} onClick={props.shareAddon}>
<div className={styles['buttons-container']}>
<Button className={installed ? styles['uninstall-button-container'] : styles['install-button-container']} title={installed ? 'Uninstall' : 'Install'} tabIndex={-1} data-id={id} onClick={toggle}>
<div className={styles['label']}>{installed ? 'Uninstall' : 'Install'}</div>
</Button>
<Button className={styles['share-button-container']} title={'Share addon'} tabIndex={-1}>
<Icon className={styles['icon']} icon={'ic_share'} />
<span className={styles['label']}>SHARE ADD-ON</span>
</div>
<div className={styles['label']}>Share addon</div>
</Button>
</div>
</div>
);
}
};
Addon.propTypes = {
className: PropTypes.string,
logo: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
version: PropTypes.string.isRequired,
types: PropTypes.arrayOf(PropTypes.string).isRequired,
hostname: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
isOfficial: PropTypes.bool.isRequired,
isInstalled: PropTypes.bool.isRequired,
shareAddon: PropTypes.func.isRequired,
onToggleClicked: PropTypes.func.isRequired
};
Addon.defaultProps = {
logo: '',
name: '',
version: '',
types: [],
hostname: '',
description: '',
isOfficial: false,
isInstalled: false
id: PropTypes.string,
name: PropTypes.string,
logo: PropTypes.string,
description: PropTypes.string,
types: PropTypes.arrayOf(PropTypes.string),
version: PropTypes.string,
transportUrl: PropTypes.string,
installed: PropTypes.bool,
toggle: PropTypes.func
};
export default Addon;
module.exports = Addon;

View file

@ -1,3 +1,3 @@
import Addon from './Addon';
const Addon = require('./Addon');
export default Addon;
module.exports = Addon;

View file

@ -1,179 +1,185 @@
.addon {
display: inline-grid;
width: var(--addon-width);
padding: 1.6em;
.addon-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: flex-start;
padding: 1rem;
background-color: var(--color-backgroundlighter);
grid-template-columns: 1fr 5fr 4fr;
grid-template-rows: calc(0.03 * var(--addon-width)) calc(0.03 * var(--addon-width)) calc(0.03 * var(--addon-width)) auto;
grid-template-areas:
"logo header buttons"
"logo type buttons"
"logo hostname buttons"
"logo description buttons";
.logo-container {
grid-area: logo;
flex: none;
width: 8rem;
height: 8rem;
background-color: var(--color-backgrounddarker);
.logo {
width: calc(0.12 * var(--addon-width));
height: calc(0.12 * var(--addon-width));
padding: 20%;
fill: var(--color-surfacelighter);
background-color: var(--color-backgrounddarker);
}
}
.header-container {
grid-area: header;
display: flex;
align-items: center;
overflow: hidden;
padding-left: 4%;
.header {
display: block;
width: 100%;
display: flex;
align-items: baseline;
height: 100%;
object-fit: contain;
object-position: center;
}
.name {
max-width: 80%;
margin-right: 3%;
font-size: 1.8em;
color: var(--color-surfacelighter);
overflow: hidden;
white-space: pre;
text-overflow: ellipsis;
}
.version {
flex: 1;
color: var(--color-surface);
overflow: hidden;
white-space: pre;
text-overflow: ellipsis;
}
.icon {
display: block;
width: 100%;
height: 100%;
padding: 1rem;
fill: var(--color-surfacelighter);
}
}
.types-container {
grid-area: type;
.info-container {
flex-grow: 1000;
flex-shrink: 1;
flex-basis: 0;
display: flex;
align-items: center;
padding-left: 4%;
overflow: hidden;
color: var(--color-secondarylighter);
flex-direction: row;
flex-wrap: wrap;
align-items: baseline;
min-width: 40rem;
padding: 0 0.5rem;
.type {
overflow: hidden;
white-space: pre;
text-overflow: ellipsis;
.name-container {
flex-grow: 0;
flex-shrink: 1;
flex-basis: auto;
padding: 0 0.5rem;
max-height: 3.6em;
font-size: 1.5rem;
color: var(--color-surfacelighter);
}
.version-container {
flex-grow: 1;
flex-shrink: 1;
flex-basis: auto;
padding: 0 0.5rem;
max-height: 2.4em;
color: var(--color-surfacelight);
}
.types-container {
flex-grow: 0;
flex-shrink: 0;
flex-basis: 100%;
margin-top: 0.5rem;
padding: 0 0.5rem;
max-height: 2.4em;
color: var(--color-surfacelight);
text-transform: capitalize;
}
.description-container {
flex-grow: 0;
flex-shrink: 0;
flex-basis: 100%;
margin-top: 0.5rem;
padding: 0 0.5rem;
max-height: 4.8em;
color: var(--color-surfacelight);
}
}
.hostname-container {
grid-area: hostname;
.buttons-container {
flex-grow: 1;
flex-shrink: 0;
flex-basis: 0;
display: flex;
align-items: center;
padding-left: 4%;
overflow: hidden;
overflow-wrap: break-word;
color: var(--color-surfacelighter);
.hostname {
overflow: hidden;
white-space: pre;
text-overflow: ellipsis;
}
}
.description {
grid-area: description;
padding-left: 4%;
color: var(--color-surfacelighter);
overflow: hidden;
overflow-wrap: break-word;
}
.buttons {
grid-area: buttons;
height: calc(0.12 * var(--addon-width));
display: flex;
flex-direction: column;
flex-direction: row;
flex-wrap: wrap;
align-items: flex-end;
justify-content: space-between;
min-width: 17rem;
.button {
width: 65%;
height: calc(0.05 * var(--addon-width));
cursor: pointer;
.install-button-container, .uninstall-button-container, .share-button-container {
flex: none;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
border-width: var(--button-border-width);
border-style: solid;
width: 17rem;
height: 3.5rem;
padding: 0 1rem;
&:not(:first-child) {
margin-top: 1rem;
}
&:not(:last-child) {
margin-right: 1rem;
}
.icon {
width: 8%;
margin-right: 3%;
flex: none;
display: block;
width: 1.5rem;
height: 1.5rem;
margin-right: 1rem;
}
.label {
font-size: 1.1em;
color: var(--color-surfacelighter);
flex-grow: 0;
flex-shrink: 1;
flex-basis: auto;
max-height: 2.4em;
font-size: 1.2rem;
font-weight: 500;
text-align: center;
}
}
&.share-button {
border-color: var(--color-secondarylighter);
.icon {
fill: var(--color-secondarylighter);
}
.label {
color: var(--color-secondarylighter);
}
&:hover {
border-color: var(--color-secondarylight);
background-color: var(--color-secondarylight);
.icon {
fill: var(--color-surfacelighter);
}
.label {
color: var(--color-surfacelighter);
}
}
}
&.install-button {
border-color: var(--color-signal5);
background-color: var(--color-signal5);
&:hover {
background-color: var(--color-signal560);
}
}
&.uninstall-button {
border-color: var(--color-surfacedark);
.label {
color: var(--color-surfacedark);
}
&:hover {
background-color: var(--color-surfacedark);
.label {
color: var(--color-surfacelighter);
}
}
}
.install-button-container {
background-color: var(--color-signal5);
&:hover {
border: none;
filter: brightness(1.1);
}
.label {
color: var(--color-surfacelighter);
}
}
.uninstall-button-container {
outline-color: var(--color-surfacedark);
outline-style: solid;
&:hover {
outline-color: var(--color-surfacelight);
.label {
color: var(--color-surfacelight);
}
}
.label {
color: var(--color-surfacedark);
}
}
.share-button-container {
outline-color: var(--color-secondarylighter);
outline-style: solid;
&:hover {
outline: none;
background: var(--color-secondarylight);
.icon {
fill: var(--color-surfacelighter);
}
.label {
color: var(--color-surfacelighter);
}
}
.icon {
fill: var(--color-secondarylighter);
}
.label {
color: var(--color-secondarylighter);
}
}
}

View file

@ -1,6 +1,7 @@
const React = require('react');
const Icon = require('stremio-icons/dom');
const { Button, Dropdown, NavBar } = require('stremio/common');
const Addon = require('./Addon');
const useAddons = require('./useAddons');
const styles = require('./styles');
@ -19,6 +20,11 @@ const Addons = ({ urlParams }) => {
<Dropdown {...dropdown} key={dropdown.name} className={styles['dropdown']} />
))}
</div>
<div className={styles['addons-list-container']} >
{addons.map((addon) => (
<Addon {...addon} key={addon.id} className={styles['addon']} />
))}
</div>
</div>
</div>
);

View file

@ -62,5 +62,16 @@
}
}
}
.addons-list-container {
max-height: calc(100% - 7rem);
padding: 0 2rem;
overflow-y: auto;
.addon {
width: 100%;
margin-bottom: 2rem;
}
}
}
}

View file

@ -7,6 +7,31 @@ const DEFAULT_TYPE = 'all';
const useAddons = (category, type) => {
category = CATEGORIES.includes(category) ? category : DEFAULT_CATEGORY;
type = typeof type === 'string' && type.length > 0 ? type : DEFAULT_TYPE;
const addons = React.useMemo(() => {
return [
{
id: 'com.linvo.cinemeta',
name: 'Cinemeta',
description: 'The official add-on for movie and series catalogs',
types: ['movie', 'series'],
version: '2.12.1',
transportUrl: 'https://v3-cinemeta.strem.io/manifest.json',
installed: true,
official: true
},
{
id: 'com.linvo.cinemeta2',
name: 'Cinemeta2',
logo: '/images/intro_background.jpg',
description: 'The official add-on for movie and series catalogs',
types: ['movie', 'series'],
version: '2.12.2',
transportUrl: 'https://v2-cinemeta.strem.io/manifest.json',
installed: false,
official: false
}
];
}, []);
const onSelect = React.useCallback((event) => {
const { name, value } = event.currentTarget.dataset;
if (name === 'category') {
@ -41,7 +66,7 @@ const useAddons = (category, type) => {
onSelect
};
}, [type, onSelect]);
return [[], [categoryDropdown, typeDropdown]];
return [addons, [categoryDropdown, typeDropdown]];
};
module.exports = useAddons;

View file

@ -43,6 +43,7 @@
font-size: 1.1rem;
font-weight: 600;
color: var(--color-surfacelighter);
text-align: center;
}
}