From 12232e7a402f0fb50ecc5bd0de7821781b44bfd9 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Tue, 17 Sep 2019 17:42:27 +0300 Subject: [PATCH 01/54] Settings Page WIP --- src/routes/Settings/Settings.js | 79 ++---- src/routes/Settings/index.js | 4 +- src/routes/Settings/styles.less | 464 ++++++++++++++++---------------- 3 files changed, 265 insertions(+), 282 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index abcfcf59e..20aee78c8 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -1,9 +1,9 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import Icon from 'stremio-icons/dom'; -import { Input, Popup, Checkbox } from 'stremio/common'; -import classnames from 'classnames'; -import styles from './styles'; +const React = require('react'); +const PropTypes = require('prop-types'); +const classnames = require('classnames'); +const { TextInput, Button, Dropdown, Checkbox, MainNavBar } = require('stremio/common'); +const styles = require('./styles'); +const Input = TextInput const SECTIONS_ORDER = { 'General': 1, @@ -11,7 +11,7 @@ const SECTIONS_ORDER = { 'Streaming': 3 }; -class Settings extends Component { +class Settings extends React.Component { constructor(props) { super(props); @@ -128,24 +128,6 @@ class Settings extends Component { }); } - activate = (id) => { - this.setState(({ inputs }) => ({ - inputs: inputs.map((input) => ({ - ...input, - active: id === input.id - })) - })); - } - - deactivate = (id) => { - this.setState(({ inputs }) => ({ - inputs: inputs.map((input) => ({ - ...input, - active: id === input.id ? false : input.active - })) - })); - } - toggleCheckbox = (id) => { this.setState(({ inputs }) => ({ inputs: inputs.map((input) => ({ @@ -162,41 +144,29 @@ class Settings extends Component { return { inputs: inputs.map((input) => ({ ...input, - value: data.id === input.id ? data.option : input.value, - active: false + value: data.name === input.id ? data.value : input.value, })) } }) } - renderPopup({ ref, activate, deactivate, active, value, id, options, onClick }) { + renderDropdown({ value, id, options, onClick}) { + options = options.map(o=>({value: o, label: o})) return ( - - - -
{value}
- - -
- -
- {options.map((option) => -
{option}
- )} -
-
-
+ ); } render() { return ( +
+
{this.state.sections.map((section) => - + )}
@@ -223,10 +193,7 @@ class Settings extends Component { return (
{input.header ?
{input.header}
: null} - {this.renderPopup({ - ref: input.ref, - activate: this.activate, - deactivate: this.deactivate, + {this.renderDropdown({ active: input.active, id: input.id, value: input.value, @@ -239,17 +206,18 @@ class Settings extends Component { return (
{input.header ?
{input.header}
: null} - {input.label} +
); } else if (input.type === 'button') { return (
{input.header ?
{input.header}
: null} - - {input.icon ? : null} +
); } else if (input.type === 'checkbox') { @@ -266,7 +234,7 @@ class Settings extends Component {
{input.header ?
{input.header}
: null}
- {input.icon ? : null} + {/* {input.icon ? : null} */}
{input.label}
@@ -284,6 +252,7 @@ class Settings extends Component { )}
+
); } } @@ -309,4 +278,4 @@ Settings.defaultProps = { settingsConfiguration: [] } -export default Settings; +module.exports = Settings; diff --git a/src/routes/Settings/index.js b/src/routes/Settings/index.js index 6a02e4e24..0e6819989 100644 --- a/src/routes/Settings/index.js +++ b/src/routes/Settings/index.js @@ -1,3 +1,3 @@ -import Settings from './Settings'; +const Settings = require('./Settings'); -export default Settings; +module.exports = Settings; diff --git a/src/routes/Settings/styles.less b/src/routes/Settings/styles.less index 9b7f6e72b..8e3d92883 100644 --- a/src/routes/Settings/styles.less +++ b/src/routes/Settings/styles.less @@ -4,280 +4,294 @@ font-size: 14px; } -.settings-container { +.settings-parent-container { + display: flex; + flex-direction: column; width: 100%; height: 100%; - display: flex; - flex-direction: row; + background-color: var(--color-background); - .side-menu { - padding: var(--spacing); - width: 17em; - display: flex; - flex-direction: column; - background-color: var(--color-backgroundlighter); - - .section-label { - padding: var(--spacing); - font-size: 1.1em; - border: calc(var(--focusable-border-size) * 0.5) solid transparent; - color: var(--color-surfacelight); - cursor: pointer; - - &.selected { - color: var(--color-surfacelighter); - background-color: var(--color-background); - - &:hover { - background-color: var(--color-background); - } - } - - &:focus { - border-color: var(--color-surfacelighter); - } - - &:hover { - color: var(--color-surfacelighter); - background-color: var(--color-surface20); - border-color: transparent; - } - } + .nav-bar { + flex: none; + align-self: stretch; } - .scroll-container { - padding: 0 calc(var(--spacing) * 2); - flex: 1; - overflow-y: auto; + .settings-container { + width: 100%; + height: 100%; + display: flex; + flex-direction: row; + background-color: var(--color-backgroundlight); - .section { - padding: calc(var(--spacing) * 4) calc(var(--spacing) * 2); + .side-menu { + padding: var(--spacing); + width: 17em; + display: flex; + flex-direction: column; + background-color: var(--color-backgroundlighter); - .section-header { - margin: 0 calc(var(--spacing) * 1.5) calc(var(--spacing) * 1.5) calc(var(--spacing) * 1.5); - font-size: 2em; - color: var(--color-surfacelighter); + .section-label { + padding: var(--spacing); + font-size: 1.1em; + border: calc(var(--focusable-border-size) * 0.5) solid transparent; + color: var(--color-surfacelight); + cursor: pointer; + + &.selected { + color: var(--color-surfacelighter); + background-color: var(--color-background); + + &:hover { + background-color: var(--color-background); + } + } + + &:focus { + border-color: var(--color-surfacelighter); + } + + &:hover { + color: var(--color-surfacelighter); + background-color: var(--color-surface20); + border-color: transparent; + } } + } - .input-container { - margin: calc(var(--spacing) * 1.5); - display: flex; - flex-direction: column; + .scroll-container { + padding: 0 calc(var(--spacing) * 2); + flex: 1; + overflow-y: auto; - .input-header { - margin-bottom: calc(var(--spacing) * 0.5); + .section { + padding: calc(var(--spacing) * 4) calc(var(--spacing) * 2); + + .section-header { + margin: 0 calc(var(--spacing) * 1.5) calc(var(--spacing) * 1.5) calc(var(--spacing) * 1.5); + font-size: 2em; color: var(--color-surfacelighter); } - &.user-container { - flex-direction: row; - align-items: center; + .input-container { + margin: calc(var(--spacing) * 1.5); + display: flex; + flex-direction: column; - .avatar { - margin: 0 var(--spacing); - width: 4.2em; - height: 4.2em; - border-radius: 50%; - border: var(--focusable-border-size) solid var(--color-primary); - background-size: cover; - background-position: center; - background-repeat: no-repeat; - } - - .email { + .input-header { + margin-bottom: calc(var(--spacing) * 0.5); color: var(--color-surfacelighter); } - } - &.select-container { - .bar-button { - padding: calc(var(--spacing) * 0.5); - width: var(--input-width); - display: flex; + &.user-container { flex-direction: row; align-items: center; - justify-content: space-between; - border: calc(var(--focusable-border-size) * 0.5) solid var(--color-primary); - cursor: pointer; - .value { - width: 100%; - color: var(--color-surface); + .avatar { + margin: 0 var(--spacing); + width: 4.2em; + height: 4.2em; + border-radius: 50%; + border: var(--focusable-border-size) solid var(--color-primary); + background-size: cover; + background-position: center; + background-repeat: no-repeat; } - .icon { - width: var(--spacing); - height: var(--spacing); - fill: var(--color-surface); + .email { + color: var(--color-surfacelighter); } + } - &:focus { - border-color: var(--color-surfacelighter); - } - - &:hover { - background-color: var(--color-backgroundlight); + &.select-container { + .bar-button { + padding: calc(var(--spacing) * 0.5); + width: var(--input-width); + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + border: calc(var(--focusable-border-size) * 0.5) solid var(--color-primary); + cursor: pointer; .value { - color: var(--color-surfacelighter); + width: 100%; + color: var(--color-surface); } .icon { + width: var(--spacing); + height: var(--spacing); + fill: var(--color-surface); + } + + &:focus { + border-color: var(--color-surfacelighter); + } + + &:hover { + background-color: var(--color-backgroundlight); + + .value { + color: var(--color-surfacelighter); + } + + .icon { + fill: var(--color-surfacelighter); + } + } + + &:global(.active) { + background-color: var(--color-surfacelighter); + + .value { + color: var(--color-backgrounddarker); + } + + .icon { + fill: var(--color-backgrounddarker); + } + } + } + } + + &.link-container { + margin: var(--spacing) calc(var(--spacing) * 1.5); + + .link { + display: block; + color: var(--color-secondarylight); + + &:focus { + color: var(--color-surface); + } + + &:hover { + color: var(--color-surfacelighter); + } + } + } + + &.button-container { + .button { + padding: calc(var(--spacing) * 0.7); + width: var(--input-width); + min-height: calc(var(--input-width) * 0.09); + display: flex; + align-items: center; + justify-content: center; + background-color: var(--color-primary); + border: calc(var(--focusable-border-size) * 0.5) solid transparent; + cursor: pointer; + + .icon { + width: 1.4em; + height: 100%; + margin-right: calc(var(--spacing) * 0.5); fill: var(--color-surfacelighter); } - } - - &:global(.active) { - background-color: var(--color-surfacelighter); - - .value { - color: var(--color-backgrounddarker); - } - - .icon { - fill: var(--color-backgrounddarker); - } - } - } - } - - &.link-container { - margin: var(--spacing) calc(var(--spacing) * 1.5); - - .link { - display: block; - color: var(--color-secondarylight); - - &:focus { - color: var(--color-surface); - } - - &:hover { - color: var(--color-surfacelighter); - } - } - } - - &.button-container { - .button { - padding: calc(var(--spacing) * 0.7); - width: var(--input-width); - min-height: calc(var(--input-width) * 0.09); - display: flex; - align-items: center; - justify-content: center; - background-color: var(--color-primary); - border: calc(var(--focusable-border-size) * 0.5) solid transparent; - cursor: pointer; - - .icon { - width: 1.4em; - height: 100%; - margin-right: calc(var(--spacing) * 0.5); - fill: var(--color-surfacelighter); - } - - .label { - max-width: 30em; - color: var(--color-surfacelighter); - } - - &:focus { - border-color: var(--color-surfacelighter); - } - - &:hover { - border-color: transparent; - background-color: var(--color-primarylight); - } - } - } - - &.checkbox-container { - .checkbox { - --icon-size: 1.2em; - --icon-color: var(--color-surface); - --icon-background-color: transparent; - - display: flex; - flex-direction: row; - align-items: center; - cursor: pointer; - - .label { - width: 100%; - margin-left: 0.5em; - color: var(--color-surfacelight); - } - - &:global(.checked) { - --icon-color: var(--color-surfacelight); - --icon-background-color: var(--color-primary); - } - - &:focus, &:hover { - --icon-color: var(--color-surfacelighter); .label { + max-width: 30em; + color: var(--color-surfacelighter); + } + + &:focus { + border-color: var(--color-surfacelighter); + } + + &:hover { + border-color: transparent; + background-color: var(--color-primarylight); + } + } + } + + &.checkbox-container { + .checkbox { + --icon-size: 1.2em; + --icon-color: var(--color-surface); + --icon-background-color: transparent; + + display: flex; + flex-direction: row; + align-items: center; + cursor: pointer; + + .label { + width: 100%; + margin-left: 0.5em; + color: var(--color-surfacelight); + } + + &:global(.checked) { + --icon-color: var(--color-surfacelight); + --icon-background-color: var(--color-primary); + } + + &:focus, &:hover { + --icon-color: var(--color-surfacelighter); + + .label { + color: var(--color-surfacelighter); + } + } + } + } + + &.text-container { + .text { + display: flex; + flex-direction: row; + align-items: center; + + .icon { + margin-right: calc(var(--spacing) * 0.5); + width: var(--spacing); + height: var(--spacing); + fill: var(--color-signal5); + } + + .x-icon { + margin-right: calc(var(--spacing) * 0.5); + width: var(--spacing); + height: var(--spacing); + fill: var(--color-signal2); + } + + .label { + width: 100%; color: var(--color-surfacelighter); } } } - } - &.text-container { - .text { - display: flex; - flex-direction: row; - align-items: center; + &.color-container { + .color-picker { + width: var(--input-width); + height: calc(var(--input-width) * 0.08); + border: calc(var(--focusable-border-size) * 0.5) solid transparent; + cursor: pointer; - .icon { - margin-right: calc(var(--spacing) * 0.5); - width: var(--spacing); - height: var(--spacing); - fill: var(--color-signal5); - } + &:focus { + border-color: var(--color-surfacelighter); + } - .x-icon { - margin-right: calc(var(--spacing) * 0.5); - width: var(--spacing); - height: var(--spacing); - fill: var(--color-signal2); - } - - .label { - width: 100%; - color: var(--color-surfacelighter); + &:hover { + border-color: transparent; + } } } } - &.color-container { - .color-picker { - width: var(--input-width); - height: calc(var(--input-width) * 0.08); - border: calc(var(--focusable-border-size) * 0.5) solid transparent; - cursor: pointer; - - &:focus { - border-color: var(--color-surfacelighter); - } - - &:hover { - border-color: transparent; - } - } + >:last-child { + margin-bottom: 0; } } - >:last-child { - margin-bottom: 0; + >:not(:last-child) { + border-bottom: calc(var(--focusable-border-size) * 0.5) solid var(--color-primary); } } - - >:not(:last-child) { - border-bottom: calc(var(--focusable-border-size) * 0.5) solid var(--color-primary); - } } } From 5a42a8514bff97e350e094c32c19e327fe4f284a Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Tue, 17 Sep 2019 17:49:02 +0300 Subject: [PATCH 02/54] Improved dropdown styles --- src/routes/Settings/styles.less | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/routes/Settings/styles.less b/src/routes/Settings/styles.less index 8e3d92883..402d329f0 100644 --- a/src/routes/Settings/styles.less +++ b/src/routes/Settings/styles.less @@ -72,6 +72,11 @@ color: var(--color-surfacelighter); } + .dropdown { + height: 3rem; + width: var(--input-width); + } + .input-container { margin: calc(var(--spacing) * 1.5); display: flex; From e5ab7b7d5dfe3831df327b8fb079dcb70c009331 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Wed, 18 Sep 2019 09:47:49 +0300 Subject: [PATCH 03/54] Replaced the MainNavBar with NavBar --- src/routes/Settings/Settings.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 20aee78c8..e1daa62a6 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -1,7 +1,7 @@ const React = require('react'); const PropTypes = require('prop-types'); const classnames = require('classnames'); -const { TextInput, Button, Dropdown, Checkbox, MainNavBar } = require('stremio/common'); +const { TextInput, Button, Dropdown, Checkbox, NavBar } = require('stremio/common'); const styles = require('./styles'); const Input = TextInput @@ -160,7 +160,12 @@ class Settings extends React.Component { render() { return (
- +
{this.state.sections.map((section) => From 8132a335638b47a20f6585d697d480333ee00c07 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Wed, 18 Sep 2019 15:38:14 +0300 Subject: [PATCH 04/54] Use rem in the stylesheet --- src/routes/Settings/styles.less | 53 ++++++++------------------------- 1 file changed, 12 insertions(+), 41 deletions(-) diff --git a/src/routes/Settings/styles.less b/src/routes/Settings/styles.less index 402d329f0..72bd55d78 100644 --- a/src/routes/Settings/styles.less +++ b/src/routes/Settings/styles.less @@ -1,9 +1,3 @@ -.settings-container, :global(.popup-container) { - --spacing: 16px; - --input-width: 500px; - font-size: 14px; -} - .settings-parent-container { display: flex; flex-direction: column; @@ -22,17 +16,19 @@ display: flex; flex-direction: row; background-color: var(--color-backgroundlight); - + --spacing: 1rem; + --input-width: 35rem; + .side-menu { padding: var(--spacing); - width: 17em; + width: 17rem; display: flex; flex-direction: column; background-color: var(--color-backgroundlighter); .section-label { padding: var(--spacing); - font-size: 1.1em; + font-size: 1.1rem; border: calc(var(--focusable-border-size) * 0.5) solid transparent; color: var(--color-surfacelight); cursor: pointer; @@ -68,7 +64,7 @@ .section-header { margin: 0 calc(var(--spacing) * 1.5) calc(var(--spacing) * 1.5) calc(var(--spacing) * 1.5); - font-size: 2em; + font-size: 2rem; color: var(--color-surfacelighter); } @@ -93,8 +89,8 @@ .avatar { margin: 0 var(--spacing); - width: 4.2em; - height: 4.2em; + width: 4.2rem; + height: 4.2rem; border-radius: 50%; border: var(--focusable-border-size) solid var(--color-primary); background-size: cover; @@ -189,14 +185,14 @@ cursor: pointer; .icon { - width: 1.4em; + width: 1.4rem; height: 100%; margin-right: calc(var(--spacing) * 0.5); fill: var(--color-surfacelighter); } .label { - max-width: 30em; + max-width: 30rem; color: var(--color-surfacelighter); } @@ -213,7 +209,7 @@ &.checkbox-container { .checkbox { - --icon-size: 1.2em; + --icon-size: 1.2rem; --icon-color: var(--color-surface); --icon-background-color: transparent; @@ -224,7 +220,7 @@ .label { width: 100%; - margin-left: 0.5em; + margin-left: 0.5rem; color: var(--color-surfacelight); } @@ -299,28 +295,3 @@ } } } - -:global(.popup-container) { - .popup-content { - width: var(--input-width); - border: calc(var(--focusable-border-size) * 0.5) solid var(--color-primary); - background-color: var(--color-surfacelighter); - - .option { - padding: calc(var(--spacing) * 0.5); - width: 100%; - color: var(--color-backgrounddarker); - cursor: pointer; - - &.selected { - color: var(--color-surfacelighter); - background-color: var(--color-primarydark); - } - - &:hover { - color: var(--color-surfacelighter); - background-color: var(--color-primary); - } - } - } -} \ No newline at end of file From 651f4d897caa10287f945a0da85084a1c3be8009 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Wed, 18 Sep 2019 15:38:50 +0300 Subject: [PATCH 05/54] Use arrow function instead of class --- src/routes/Settings/Settings.js | 265 +++++++++++++------------------- 1 file changed, 105 insertions(+), 160 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index e1daa62a6..47df86497 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -1,9 +1,34 @@ const React = require('react'); -const PropTypes = require('prop-types'); const classnames = require('classnames'); const { TextInput, Button, Dropdown, Checkbox, NavBar } = require('stremio/common'); const styles = require('./styles'); -const Input = TextInput + +const settingsData = [ + { section: 'General', label: 'Username', type: 'user', avatar: '', email: '' }, + { section: 'General', label: 'LOG OUT', type: 'button' }, + { section: 'General', label: 'Change password', type: 'link', href: '' }, + { section: 'General', label: 'Import options', type: 'static-text' }, + { section: 'General', label: 'Import from Facebook', type: 'link', href: '' }, + { section: 'General', label: 'Export user data', type: 'link', href: '' }, + { section: 'General', label: 'Subscribe to calendar', type: 'link', href: '' }, + { section: 'General', label: 'Contact support', type: 'link', href: 'https://stremio.zendesk.com/' }, + { section: 'General', label: 'Request account deletion', type: 'link', href: 'https://docs.google.com/forms/d/e/1FAIpQLScubrlTpDMIPUUBlhZ5lwcXl3HxzKfunIMCX5Jnp-cDyglWjQ/viewform?usp=sf_link' }, + { section: 'General', header: 'Trakt Scrobbling', label: 'AUTHENTICATE', type: 'button', icon: 'ic_trackt' }, + { section: 'General', header: 'UI Language', label: 'UI Language', type: 'select', options: ['Български език', 'English', 'Deutsch', 'Español', 'Italiano'], value: 'English' }, + { section: 'Player', label: 'ADD-ONS', type: 'button', icon: 'ic_addons' }, + { section: 'Player', header: 'Default Subtitles Language', label: 'Default Subtitles Language', type: 'select', options: ['English', 'Nederlands', 'Avesta', 'Български език', 'Deutsch', 'Español', 'Italiano'], value: 'English' }, + { section: 'Player', header: 'Default Subtitles Size', label: 'Default Subtitles Size', type: 'select', options: ['72%', '80%', '100%', '120%', '140%', '160%', '180%'], value: '100%' }, + { section: 'Player', header: 'Subtitles Background', label: 'Subtitles background', type: 'select', options: ['None', 'Solid', 'Transparent'], value: 'None' }, + { section: 'Player', header: 'Subtitles color', label: 'Subtitles color', type: 'color', color: '#FFFFFF' }, + { section: 'Player', header: 'Subtitles outline color', label: 'Subtitles outline color', type: 'color', color: '#000000' }, + { section: 'Player', label: 'Auto-play next episode', type: 'checkbox', value: true }, + { section: 'Player', label: 'Pause playback when minimized', type: 'checkbox', value: false }, + { section: 'Player', label: 'Hardware-accelerated decoding', type: 'checkbox', value: true }, + { section: 'Player', label: 'Launch player in a separate window (advanced)', type: 'checkbox', value: true }, + { section: 'Streaming', header: 'Caching', label: 'Caching', type: 'select', options: ['No Caching', '2GB', '5GB', '10GB'], value: '2GB' }, + { section: 'Streaming', header: 'Torrent Profile', label: 'Torrent Profile', type: 'select', options: ['Default', 'Soft', 'Fast'], value: 'Default' }, + { section: 'Streaming', header: 'Streaming server URL: http://127.0.0.1:11470', label: 'Streaming server is available.', type: 'static-text', icon: 'ic_check' } +]; const SECTIONS_ORDER = { 'General': 1, @@ -11,174 +36,116 @@ const SECTIONS_ORDER = { 'Streaming': 3 }; -class Settings extends React.Component { - constructor(props) { - super(props); +const Settings = () => { + const [selectedSectionId, setSelectedSectionId] = React.useState(null) + const [sections, setSections] = React.useState([]) + const [inputs, setInputs] = React.useState([]) + const scrollContainerRef = React.useRef(null) - this.scrollContainerRef = React.createRef(); + React.useEffect(() => { + const theSections = settingsData.map(({ section }) => section) + .filter((section, index, sections) => sections.indexOf(section) === index) + .sort(function(a, b) { + const valueA = SECTIONS_ORDER[a]; + const valueB = SECTIONS_ORDER[b]; + if (!isNaN(valueA) && !isNaN(valueB)) return valueA - valueB; + if (!isNaN(valueA)) return -1; + if (!isNaN(valueB)) return 1; + return a - b; + }) + .map((section) => ({ + id: section, + ref: React.createRef() + })); + setSections(theSections); - this.state = { - selectedSectionId: null, - sections: [], - inputs: [] - }; + if (theSections.length > 0 && (selectedSectionId === null || !theSections.find(({ id }) => id === selectedSectionId))) { + setSelectedSectionId(theSections[0].id) + } + + setInputs(settingsData.map((setting) => ({ + ...setting, + id: setting.label, + ref: React.createRef(), + active: !!(inputs.find(({ id }) => id === setting.label) || {}).active + }))) + }, []) + + ///////////////// + + const toggleCheckbox = (id) => { + setInputs(inputs.map((input) => ({ + ...input, + value: id === input.id ? !input.value : input.value + }))) } - componentDidMount() { - this.settingsOnUpdate([ - { section: 'General', label: 'Username', type: 'user', avatar: '', email: '' }, - { section: 'General', label: 'LOG OUT', type: 'button' }, - { section: 'General', label: 'Change password', type: 'link', href: '' }, - { section: 'General', label: 'Import options', type: 'static-text' }, - { section: 'General', label: 'Import from Facebook', type: 'link', href: '' }, - { section: 'General', label: 'Export user data', type: 'link', href: '' }, - { section: 'General', label: 'Subscribe to calendar', type: 'link', href: '' }, - { section: 'General', label: 'Contact support', type: 'link', href: 'https://stremio.zendesk.com/' }, - { section: 'General', label: 'Request account deletion', type: 'link', href: 'https://docs.google.com/forms/d/e/1FAIpQLScubrlTpDMIPUUBlhZ5lwcXl3HxzKfunIMCX5Jnp-cDyglWjQ/viewform?usp=sf_link' }, - { section: 'General', header: 'Trakt Scrobbling', label: 'AUTHENTICATE', type: 'button', icon: 'ic_trackt' }, - { section: 'General', header: 'UI Language', label: 'UI Language', type: 'select', options: ['Български език', 'English', 'Deutsch', 'Español', 'Italiano'], value: 'English' }, - { section: 'Player', label: 'ADD-ONS', type: 'button', icon: 'ic_addons' }, - { section: 'Player', header: 'Default Subtitles Language', label: 'Default Subtitles Language', type: 'select', options: ['English', 'Nederlands', 'Avesta', 'Български език', 'Deutsch', 'Español', 'Italiano'], value: 'English' }, - { section: 'Player', header: 'Default Subtitles Size', label: 'Default Subtitles Size', type: 'select', options: ['72%', '80%', '100%', '120%', '140%', '160%', '180%'], value: '100%' }, - { section: 'Player', header: 'Subtitles Background', label: 'Subtitles background', type: 'select', options: ['None', 'Solid', 'Transparent'], value: 'None' }, - { section: 'Player', header: 'Subtitles color', label: 'Subtitles color', type: 'color', color: '#FFFFFF' }, - { section: 'Player', header: 'Subtitles outline color', label: 'Subtitles outline color', type: 'color', color: '#000000' }, - { section: 'Player', label: 'Auto-play next episode', type: 'checkbox', value: true }, - { section: 'Player', label: 'Pause playback when minimized', type: 'checkbox', value: false }, - { section: 'Player', label: 'Hardware-accelerated decoding', type: 'checkbox', value: true }, - { section: 'Player', label: 'Launch player in a separate window (advanced)', type: 'checkbox', value: true }, - { section: 'Streaming', header: 'Caching', label: 'Caching', type: 'select', options: ['No Caching', '2GB', '5GB', '10GB'], value: '2GB' }, - { section: 'Streaming', header: 'Torrent Profile', label: 'Torrent Profile', type: 'select', options: ['Default', 'Soft', 'Fast'], value: 'Default' }, - { section: 'Streaming', header: 'Streaming server URL: http://127.0.0.1:11470', label: 'Streaming server is available.', type: 'static-text', icon: 'ic_check' } - ]); + const updateDropdown = (event) => { + var data = event.currentTarget.dataset; + setInputs(inputs.map((input) => ({ + ...input, + value: data.name === input.id ? data.value : input.value, + }))) } - settingsOnUpdate = (settings) => { - this.setState(({ selectedSectionId, inputs }) => { - const sections = settings.map(({ section }) => section) - .filter((section, index, sections) => sections.indexOf(section) === index) - .sort(function(a, b) { - const valueA = SECTIONS_ORDER[a]; - const valueB = SECTIONS_ORDER[b]; - if (!isNaN(valueA) && !isNaN(valueB)) return valueA - valueB; - if (!isNaN(valueA)) return -1; - if (!isNaN(valueB)) return 1; - return a - b; - }) - .map((section) => ({ - id: section, - ref: React.createRef() - })); - - var sectionId = null; - if (selectedSectionId !== null && sections.find(({ id }) => id === selectedSectionId)) { - sectionId = selectedSectionId - } else if (sections.length > 0) { - sectionId = sections[0].id - } - - return { - selectedSectionId: sectionId, - sections: sections, - inputs: settings.map((setting) => ({ - ...setting, - id: setting.label, - ref: React.createRef(), - active: !!(inputs.find(({ id }) => id === setting.label) || {}).active - })) - } + const changeSection = (event) => { + const currentSectionId = event.currentTarget.dataset.section + const section = sections.find((section) => section.id === currentSectionId); + setSelectedSectionId(currentSectionId) + scrollContainerRef.current.scrollTo({ + top: section.ref.current.offsetTop }); } - changeSection = (event) => { - this.setState({ selectedSectionId: event.currentTarget.dataset.section }, () => { - const section = this.state.sections.find((section) => section.id === this.state.selectedSectionId); - this.scrollContainerRef.current.scrollTo({ - top: section.ref.current.offsetTop - }); - }); - } - - onScroll = () => { - if (this.state.sections.length <= 0) { + const onScroll = (event) => { + const current = event.currentTarget + if (sections.length <= 0) { return; } - if (this.scrollContainerRef.current.scrollTop + this.scrollContainerRef.current.clientHeight === this.scrollContainerRef.current.scrollHeight) { - this.setState({ selectedSectionId: this.state.sections[this.state.sections.length - 1].id }); + if (current.scrollTop + current.clientHeight === current.scrollHeight) { + setSelectedSectionId(sections[sections.length - 1].id); } else { - for (let i = this.state.sections.length - 1; i >= 0; i--) { - if (this.state.sections[i].ref.current.offsetTop <= this.scrollContainerRef.current.scrollTop) { - this.setState({ selectedSectionId: this.state.sections[i].id }); + for (let i = sections.length - 1;i >= 0;i--) { + if (sections[i].ref.current.offsetTop <= current.scrollTop) { + setSelectedSectionId(sections[i].id); break; } } } } - shouldComponentUpdate(nextProps, nextState) { - return nextState.selectedSectionId !== this.state.selectedSectionId || - nextState.sections !== this.state.sections || - nextState.inputs !== this.state.inputs; - } + //////////////// - componentDidUpdate(prevProps, prevState) { - this.state.inputs.forEach((input) => { - input.ref.current && !input.active && input.ref.current.close && input.ref.current.close(); - }); - } - - toggleCheckbox = (id) => { - this.setState(({ inputs }) => ({ - inputs: inputs.map((input) => ({ - ...input, - value: id === input.id ? !input.value : input.value - })) - })); - } - - onChange = (event) => { - var data = event.currentTarget.dataset; - - this.setState(({ inputs }) => { - return { - inputs: inputs.map((input) => ({ - ...input, - value: data.name === input.id ? data.value : input.value, - })) - } - }) - } - - renderDropdown({ value, id, options, onClick}) { - options = options.map(o=>({value: o, label: o})) + const renderDropdown = ({ value, id, options, onClick }) => { + // TODO provide proper data instead of this mapping + options = options.map(o => ({ value: o, label: o })) return ( ); } - render() { - return ( -
+ return ( +
+ navMenu={true} />
- {this.state.sections.map((section) => - )}
-
- {this.state.sections.map((section) => +
+ {sections.map((section) =>
{section.id}
- {this.state.inputs + {inputs .filter((input) => input.section === section.id) .map((input) => { if (input.type === 'user') { @@ -198,12 +165,12 @@ class Settings extends React.Component { return (
{input.header ?
{input.header}
: null} - {this.renderDropdown({ + {renderDropdown({ active: input.active, id: input.id, value: input.value, options: input.options, - onClick: this.onChange + onClick: updateDropdown })}
); @@ -229,7 +196,7 @@ class Settings extends React.Component { return (
{input.header ?
{input.header}
: null} - +
{input.label}
@@ -248,7 +215,7 @@ class Settings extends React.Component { return (
{input.header ?
{input.header}
: null} - +
); } @@ -257,30 +224,8 @@ class Settings extends React.Component { )}
-
- ); - } -} - -Settings.propTypes = { - settingsConfiguration: PropTypes.arrayOf(PropTypes.shape({ - section: PropTypes.string.isRequired, - header: PropTypes.string, - label: PropTypes.string.isRequired, - type: PropTypes.string.isRequired, - avatar: PropTypes.string, - email: PropTypes.string, - href: PropTypes.string, - icon: PropTypes.string, - options: PropTypes.arrayOf(PropTypes.string), - value: PropTypes.oneOfType([ - PropTypes.bool, - PropTypes.string - ]) - })).isRequired -} -Settings.defaultProps = { - settingsConfiguration: [] +
+ ); } module.exports = Settings; From 8a0760156a3600ef18309b8b9a90c472f3a0e343 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Wed, 18 Sep 2019 16:32:19 +0300 Subject: [PATCH 06/54] Use icons --- src/routes/Settings/Settings.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 47df86497..c6fe79891 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -1,6 +1,7 @@ const React = require('react'); const classnames = require('classnames'); const { TextInput, Button, Dropdown, Checkbox, NavBar } = require('stremio/common'); +const Icon = require('stremio-icons/dom/Icon'); const styles = require('./styles'); const settingsData = [ @@ -188,6 +189,7 @@ const Settings = () => {
{input.header ?
{input.header}
: null}
@@ -206,7 +208,7 @@ const Settings = () => {
{input.header ?
{input.header}
: null}
- {/* {input.icon ? : null} */} + {input.icon ? : null}
{input.label}
From e5bad11b48502b87c5c2978daa1c40526d5df73d Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Wed, 18 Sep 2019 16:40:02 +0300 Subject: [PATCH 07/54] Smooth scrolling of sections --- src/routes/Settings/Settings.js | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index c6fe79891..0b7576e5c 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -45,19 +45,19 @@ const Settings = () => { React.useEffect(() => { const theSections = settingsData.map(({ section }) => section) - .filter((section, index, sections) => sections.indexOf(section) === index) - .sort(function(a, b) { - const valueA = SECTIONS_ORDER[a]; - const valueB = SECTIONS_ORDER[b]; - if (!isNaN(valueA) && !isNaN(valueB)) return valueA - valueB; - if (!isNaN(valueA)) return -1; - if (!isNaN(valueB)) return 1; - return a - b; - }) - .map((section) => ({ - id: section, - ref: React.createRef() - })); + .filter((section, index, sections) => sections.indexOf(section) === index) + .sort(function(a, b) { + const valueA = SECTIONS_ORDER[a]; + const valueB = SECTIONS_ORDER[b]; + if (!isNaN(valueA) && !isNaN(valueB)) return valueA - valueB; + if (!isNaN(valueA)) return -1; + if (!isNaN(valueB)) return 1; + return a - b; + }) + .map((section) => ({ + id: section, + ref: React.createRef() + })); setSections(theSections); if (theSections.length > 0 && (selectedSectionId === null || !theSections.find(({ id }) => id === selectedSectionId))) { @@ -90,11 +90,12 @@ const Settings = () => { } const changeSection = (event) => { - const currentSectionId = event.currentTarget.dataset.section + const currentSectionId = event.currentTarget.dataset.section; const section = sections.find((section) => section.id === currentSectionId); setSelectedSectionId(currentSectionId) scrollContainerRef.current.scrollTo({ - top: section.ref.current.offsetTop + top: section.ref.current.offsetTop, + behavior: 'smooth' }); } From 9fef56fca105b826e3bacc5e13b3b89ceb3170d1 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Wed, 18 Sep 2019 17:14:11 +0300 Subject: [PATCH 08/54] Formatting; Inline dropdown rendering --- src/routes/Settings/Settings.js | 54 ++++++++++++--------------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 0b7576e5c..f89040bd6 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -38,10 +38,10 @@ const SECTIONS_ORDER = { }; const Settings = () => { - const [selectedSectionId, setSelectedSectionId] = React.useState(null) - const [sections, setSections] = React.useState([]) - const [inputs, setInputs] = React.useState([]) - const scrollContainerRef = React.useRef(null) + const [selectedSectionId, setSelectedSectionId] = React.useState(null); + const [sections, setSections] = React.useState([]); + const [inputs, setInputs] = React.useState([]); + const scrollContainerRef = React.useRef(null); React.useEffect(() => { const theSections = settingsData.map(({ section }) => section) @@ -61,7 +61,7 @@ const Settings = () => { setSections(theSections); if (theSections.length > 0 && (selectedSectionId === null || !theSections.find(({ id }) => id === selectedSectionId))) { - setSelectedSectionId(theSections[0].id) + setSelectedSectionId(theSections[0].id); } setInputs(settingsData.map((setting) => ({ @@ -69,8 +69,8 @@ const Settings = () => { id: setting.label, ref: React.createRef(), active: !!(inputs.find(({ id }) => id === setting.label) || {}).active - }))) - }, []) + }))); + }, []); ///////////////// @@ -78,29 +78,29 @@ const Settings = () => { setInputs(inputs.map((input) => ({ ...input, value: id === input.id ? !input.value : input.value - }))) - } + }))); + }; const updateDropdown = (event) => { var data = event.currentTarget.dataset; setInputs(inputs.map((input) => ({ ...input, value: data.name === input.id ? data.value : input.value, - }))) - } + }))); + }; const changeSection = (event) => { const currentSectionId = event.currentTarget.dataset.section; const section = sections.find((section) => section.id === currentSectionId); - setSelectedSectionId(currentSectionId) + setSelectedSectionId(currentSectionId); scrollContainerRef.current.scrollTo({ top: section.ref.current.offsetTop, behavior: 'smooth' }); - } + }; const onScroll = (event) => { - const current = event.currentTarget + const current = event.currentTarget; if (sections.length <= 0) { return; } @@ -115,17 +115,7 @@ const Settings = () => { } } } - } - - //////////////// - - const renderDropdown = ({ value, id, options, onClick }) => { - // TODO provide proper data instead of this mapping - options = options.map(o => ({ value: o, label: o })) - return ( - - ); - } + }; return (
@@ -147,8 +137,7 @@ const Settings = () => { {sections.map((section) =>
{section.id}
- {inputs - .filter((input) => input.section === section.id) + {inputs.filter((input) => input.section === section.id) .map((input) => { if (input.type === 'user') { return ( @@ -167,13 +156,8 @@ const Settings = () => { return (
{input.header ?
{input.header}
: null} - {renderDropdown({ - active: input.active, - id: input.id, - value: input.value, - options: input.options, - onClick: updateDropdown - })} + {/* TODO: provide proper data instead of mapping the options */} + ({ value: o, label: o }))} selected={[input.value]} name={input.id} key={input.id} className={styles['dropdown']} onSelect={updateDropdown} />
); } else if (input.type === 'link') { @@ -229,6 +213,6 @@ const Settings = () => {
); -} +}; module.exports = Settings; From e36e585fcc64aaf84e088ffd24f18e4af62ff940 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Thu, 19 Sep 2019 17:58:43 +0300 Subject: [PATCH 09/54] New component for color input --- src/common/ColorInput/ColorInput.js | 45 +++++++++++++++++++++++++++ src/common/ColorInput/index.js | 4 +++ src/common/ColorInput/styles.less | 11 +++++++ src/common/ColorPicker/ColorPicker.js | 5 +-- src/common/index.js | 2 ++ 5 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 src/common/ColorInput/ColorInput.js create mode 100644 src/common/ColorInput/index.js create mode 100644 src/common/ColorInput/styles.less diff --git a/src/common/ColorInput/ColorInput.js b/src/common/ColorInput/ColorInput.js new file mode 100644 index 000000000..ae231de9e --- /dev/null +++ b/src/common/ColorInput/ColorInput.js @@ -0,0 +1,45 @@ +const React = require('react'); +const classnames = require('classnames'); +const PropTypes = require('prop-types'); +const Popup = require('stremio/common/Popup'); +const Button = require('stremio/common/Button'); +const ColorPicker = require('stremio/common/ColorPicker'); +const useBinaryState = require('stremio/common/useBinaryState'); +const styles = require('./styles'); + +const ColorInput = React.forwardRef((props, ref) => { + const [menuOpen, openMenu, closeMenu, toggleMenu] = useBinaryState(false); + const [color, setColor] = React.useState(props.defaultValue); + const [chosenColor, setChosenColor] = React.useState(props.defaultValue); + const updateolorsAndCloseMenu = () => { setChosenColor(color); props.onChange(color); closeMenu(); }; + return ( + ( + + )} + renderMenu={() => ( +
+ +
+ + +
+
+ )} + /> + ); +}); + + +ColorInput.displayName = 'ColorInput'; + +ColorInput.propTypes = { + className: PropTypes.string, + defaultValue: PropTypes.string, + onChange: PropTypes.func +}; + +module.exports = ColorInput; \ No newline at end of file diff --git a/src/common/ColorInput/index.js b/src/common/ColorInput/index.js new file mode 100644 index 000000000..ce4fa913a --- /dev/null +++ b/src/common/ColorInput/index.js @@ -0,0 +1,4 @@ +const ColorInput = require('./ColorInput'); + +module.exports = ColorInput; + diff --git a/src/common/ColorInput/styles.less b/src/common/ColorInput/styles.less new file mode 100644 index 000000000..0bea883d3 --- /dev/null +++ b/src/common/ColorInput/styles.less @@ -0,0 +1,11 @@ +.color-input-container { + display: flex; + padding: 10px; + background-color: #000; + flex-direction: row; + + .buttons-container { + padding-left: 1rem; + flex-grow: .5; + } +} \ No newline at end of file diff --git a/src/common/ColorPicker/ColorPicker.js b/src/common/ColorPicker/ColorPicker.js index 341bacb39..f414a349e 100644 --- a/src/common/ColorPicker/ColorPicker.js +++ b/src/common/ColorPicker/ColorPicker.js @@ -4,7 +4,7 @@ const AColorPicker = require('a-color-picker'); const COLOR_FORMAT = 'rgbacss'; -const ColorPicker = ({ className, value, onChange }) => { +const ColorPicker = ({ className, value, onChange, pickerOpts }) => { value = AColorPicker.parseColor(value, COLOR_FORMAT); const pickerRef = React.useRef(null); const pickerElementRef = React.useRef(null); @@ -14,7 +14,8 @@ const ColorPicker = ({ className, value, onChange }) => { showHSL: false, showHEX: false, showRGB: false, - showAlpha: true + showAlpha: true, + ...pickerOpts }); }, []); React.useEffect(() => { diff --git a/src/common/index.js b/src/common/index.js index b22b17376..28bad5f65 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -1,6 +1,7 @@ const Button = require('./Button'); const Checkbox = require('./Checkbox'); const ColorPicker = require('./ColorPicker'); +const ColorInput = require('./ColorInput'); const Dropdown = require('./Dropdown'); const MainNavBar = require('./MainNavBar'); const MetaItem = require('./MetaItem'); @@ -26,6 +27,7 @@ module.exports = { Button, Checkbox, ColorPicker, + ColorInput, Dropdown, MainNavBar, MetaItem, From 011bad157da8c00acbefe582725d69db38290799 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Thu, 19 Sep 2019 17:59:12 +0300 Subject: [PATCH 10/54] Make use of the Colorinput component --- src/routes/Settings/Settings.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index f89040bd6..d5dd806a7 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -1,6 +1,6 @@ const React = require('react'); const classnames = require('classnames'); -const { TextInput, Button, Dropdown, Checkbox, NavBar } = require('stremio/common'); +const { Button, Dropdown, Checkbox, NavBar, ColorInput } = require('stremio/common'); const Icon = require('stremio-icons/dom/Icon'); const styles = require('./styles'); @@ -81,6 +81,13 @@ const Settings = () => { }))); }; + const colorChanged = (id, color) => { + setInputs(inputs.map((input) => ({ + ...input, + value: id === input.id ? color : input.value + }))); + }; + const updateDropdown = (event) => { var data = event.currentTarget.dataset; setInputs(inputs.map((input) => ({ @@ -183,7 +190,7 @@ const Settings = () => { return (
{input.header ?
{input.header}
: null} - +
{input.label}
@@ -202,7 +209,8 @@ const Settings = () => { return (
{input.header ?
{input.header}
: null} - + + {/* */}
); } From 95d1078757915f3a68343589d14fb1c0ff26081f Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Fri, 20 Sep 2019 15:35:33 +0300 Subject: [PATCH 11/54] Remove the color picker's additional options --- src/common/ColorPicker/ColorPicker.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/common/ColorPicker/ColorPicker.js b/src/common/ColorPicker/ColorPicker.js index f414a349e..5c201f378 100644 --- a/src/common/ColorPicker/ColorPicker.js +++ b/src/common/ColorPicker/ColorPicker.js @@ -4,7 +4,7 @@ const AColorPicker = require('a-color-picker'); const COLOR_FORMAT = 'rgbacss'; -const ColorPicker = ({ className, value, onChange, pickerOpts }) => { +const ColorPicker = ({ className, value, onChange}) => { value = AColorPicker.parseColor(value, COLOR_FORMAT); const pickerRef = React.useRef(null); const pickerElementRef = React.useRef(null); @@ -15,7 +15,6 @@ const ColorPicker = ({ className, value, onChange, pickerOpts }) => { showHEX: false, showRGB: false, showAlpha: true, - ...pickerOpts }); }, []); React.useEffect(() => { From 15a6a8381ac604c5911b07360bb4fb4978ae1baa Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Fri, 20 Sep 2019 15:36:12 +0300 Subject: [PATCH 12/54] Better looking color picker --- src/common/ColorInput/ColorInput.js | 15 ++++++---- src/common/ColorInput/styles.less | 43 ++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/src/common/ColorInput/ColorInput.js b/src/common/ColorInput/ColorInput.js index ae231de9e..3d6f4df08 100644 --- a/src/common/ColorInput/ColorInput.js +++ b/src/common/ColorInput/ColorInput.js @@ -5,28 +5,31 @@ const Popup = require('stremio/common/Popup'); const Button = require('stremio/common/Button'); const ColorPicker = require('stremio/common/ColorPicker'); const useBinaryState = require('stremio/common/useBinaryState'); +const Icon = require('stremio-icons/dom/Icon'); const styles = require('./styles'); const ColorInput = React.forwardRef((props, ref) => { const [menuOpen, openMenu, closeMenu, toggleMenu] = useBinaryState(false); const [color, setColor] = React.useState(props.defaultValue); const [chosenColor, setChosenColor] = React.useState(props.defaultValue); - const updateolorsAndCloseMenu = () => { setChosenColor(color); props.onChange(color); closeMenu(); }; + const updateColorsAndCloseMenu = () => { setChosenColor(color); props.onChange(color); closeMenu(); }; return ( event.type === 'scroll' || closeMenu()} renderLabel={(ref) => ( )} renderMenu={() => (
- -
- - -
+ +

Choose a color:

+ +
)} /> diff --git a/src/common/ColorInput/styles.less b/src/common/ColorInput/styles.less index 0bea883d3..328bed6a0 100644 --- a/src/common/ColorInput/styles.less +++ b/src/common/ColorInput/styles.less @@ -1,11 +1,40 @@ .color-input-container { - display: flex; - padding: 10px; - background-color: #000; - flex-direction: row; + --spacing: 1rem; + padding: var(--spacing); + background-color: var(--color-surfacelighter); - .buttons-container { - padding-left: 1rem; - flex-grow: .5; + * { + overflow: visible; + } + + .x-icon { + position: absolute; + top: 1rem; + right: 1rem; + width: .8rem; + height: .8rem; + fill: var(--color-surfacedark); + } + + h1 { + font-size: 1.2rem; + } + + .color-input { + margin: var(--spacing) auto 0 auto; + :global(.a-color-picker-stack):not(:global(.a-color-picker-row-top)) canvas, :global(.a-color-picker-circle) { + border: solid 1px var(--color-surfacedark); + } + :global(.a-color-picker-circle) { + box-shadow: 0 0 .2rem var(--color-surfacedark); + } + } + + .button { + text-align: center; + color: var(--color-surfacelighter); + background-color: var(--color-signal5); + padding: 1rem; + margin-top: var(--spacing); } } \ No newline at end of file From e14378e03e5da728cafae801ff9303c9e55d2160 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Fri, 20 Sep 2019 15:41:26 +0300 Subject: [PATCH 13/54] Undo all changes in ColorPicker --- src/common/ColorPicker/ColorPicker.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/ColorPicker/ColorPicker.js b/src/common/ColorPicker/ColorPicker.js index 5c201f378..341bacb39 100644 --- a/src/common/ColorPicker/ColorPicker.js +++ b/src/common/ColorPicker/ColorPicker.js @@ -4,7 +4,7 @@ const AColorPicker = require('a-color-picker'); const COLOR_FORMAT = 'rgbacss'; -const ColorPicker = ({ className, value, onChange}) => { +const ColorPicker = ({ className, value, onChange }) => { value = AColorPicker.parseColor(value, COLOR_FORMAT); const pickerRef = React.useRef(null); const pickerElementRef = React.useRef(null); @@ -14,7 +14,7 @@ const ColorPicker = ({ className, value, onChange}) => { showHSL: false, showHEX: false, showRGB: false, - showAlpha: true, + showAlpha: true }); }, []); React.useEffect(() => { From 51b607e7b42c0a4f2aa0bb089cc103d4de41e674 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Fri, 20 Sep 2019 16:36:55 +0300 Subject: [PATCH 14/54] Stylesheet cleanup --- src/routes/Settings/styles.less | 57 ++------------------------------- 1 file changed, 2 insertions(+), 55 deletions(-) diff --git a/src/routes/Settings/styles.less b/src/routes/Settings/styles.less index 72bd55d78..e9d36c98e 100644 --- a/src/routes/Settings/styles.less +++ b/src/routes/Settings/styles.less @@ -68,11 +68,6 @@ color: var(--color-surfacelighter); } - .dropdown { - height: 3rem; - width: var(--input-width); - } - .input-container { margin: calc(var(--spacing) * 1.5); display: flex; @@ -104,54 +99,9 @@ } &.select-container { - .bar-button { - padding: calc(var(--spacing) * 0.5); + .dropdown { + height: 3rem; width: var(--input-width); - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; - border: calc(var(--focusable-border-size) * 0.5) solid var(--color-primary); - cursor: pointer; - - .value { - width: 100%; - color: var(--color-surface); - } - - .icon { - width: var(--spacing); - height: var(--spacing); - fill: var(--color-surface); - } - - &:focus { - border-color: var(--color-surfacelighter); - } - - &:hover { - background-color: var(--color-backgroundlight); - - .value { - color: var(--color-surfacelighter); - } - - .icon { - fill: var(--color-surfacelighter); - } - } - - &:global(.active) { - background-color: var(--color-surfacelighter); - - .value { - color: var(--color-backgrounddarker); - } - - .icon { - fill: var(--color-backgrounddarker); - } - } } } @@ -181,7 +131,6 @@ align-items: center; justify-content: center; background-color: var(--color-primary); - border: calc(var(--focusable-border-size) * 0.5) solid transparent; cursor: pointer; .icon { @@ -192,7 +141,6 @@ } .label { - max-width: 30rem; color: var(--color-surfacelighter); } @@ -270,7 +218,6 @@ .color-picker { width: var(--input-width); height: calc(var(--input-width) * 0.08); - border: calc(var(--focusable-border-size) * 0.5) solid transparent; cursor: pointer; &:focus { From d9c3743400b18ee3e2f5cd5ccca55eeea24693be Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Wed, 25 Sep 2019 15:51:43 +0300 Subject: [PATCH 15/54] Dropped forwardRef. Minor formatting changes --- src/common/ColorInput/ColorInput.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/common/ColorInput/ColorInput.js b/src/common/ColorInput/ColorInput.js index 3d6f4df08..28278d354 100644 --- a/src/common/ColorInput/ColorInput.js +++ b/src/common/ColorInput/ColorInput.js @@ -8,11 +8,15 @@ const useBinaryState = require('stremio/common/useBinaryState'); const Icon = require('stremio-icons/dom/Icon'); const styles = require('./styles'); -const ColorInput = React.forwardRef((props, ref) => { +const ColorInput = ({ className, defaultValue, onChange }) => { const [menuOpen, openMenu, closeMenu, toggleMenu] = useBinaryState(false); - const [color, setColor] = React.useState(props.defaultValue); - const [chosenColor, setChosenColor] = React.useState(props.defaultValue); - const updateColorsAndCloseMenu = () => { setChosenColor(color); props.onChange(color); closeMenu(); }; + const [color, setColor] = React.useState(defaultValue); + const [selectedColor, setSelectedColor] = React.useState(defaultValue); + const updateColorsAndCloseMenu = React.useCallback(() => { + setSelectedColor(color); + onChange(color); + closeMenu(); + }, [color, onChange]); return ( { onCloseRequest={closeMenu} // onCloseRequest={(event) => event.type === 'scroll' || closeMenu()} renderLabel={(ref) => ( - + )} renderMenu={() => ( -
+

Choose a color:

- - + +
)} /> ); -}); +}; -ColorInput.displayName = 'ColorInput'; - ColorInput.propTypes = { className: PropTypes.string, defaultValue: PropTypes.string, From 7a1f42d4fdf730949c0c565644ebf98995a52c31 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Wed, 25 Sep 2019 16:33:57 +0300 Subject: [PATCH 16/54] Separate description of settings --- src/routes/Settings/constants.js | 50 ++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/routes/Settings/constants.js diff --git a/src/routes/Settings/constants.js b/src/routes/Settings/constants.js new file mode 100644 index 000000000..7f89debee --- /dev/null +++ b/src/routes/Settings/constants.js @@ -0,0 +1,50 @@ +const settingsSections = { + "General": { + "inputs": [ + { "section": "General", "label": "Username", "type": "user", "id": "user" }, + { "section": "General", "label": "LOG OUT", "type": "button", "id": "log_out" }, + { "section": "General", "label": "Change password", "type": "link", "href": "", "id": "change_password" }, + { "section": "General", "label": "Import options", "type": "static-text", "id": "import_options" }, + { "section": "General", "label": "Import from Facebook", "type": "link", "href": "", "id": "import_from_facebook" }, + { "section": "General", "label": "Export user data", "type": "link", "href": "", "id": "export_user_data" }, + { "section": "General", "label": "Subscribe to calendar", "type": "link", "href": "", "id": "subscribe_to_calendar" }, + { "section": "General", "label": "Contact support", "type": "link", "id": "contact_support" }, + { "section": "General", "label": "Request account deletion", "type": "link", "id": "request_account_deletion" }, + { "section": "General", "header": "Trakt Scrobbling", "label": "AUTHENTICATE", "type": "button", "icon": "ic_trackt", "id": "authenticate" }, + { "section": "General", "header": "UI Language", "label": "UI Language", "type": "select", "options": [{ "label": "Български език", "value": "bul" }, { "label": "English", "value": "eng" }, { "label": "Deutsch", "value": "ger" }, { "label": "Español", "value": "esp" }, { "label": "Italiano", "value": "ita" }], "id": "ui_language" }, + ], + }, + "Player": { + "inputs": [ + { "section": "Player", "label": "ADD-ONS", "type": "button", "icon": "ic_addons", "id": "add-ons" }, + { "section": "Player", "header": "Default Subtitles Language", "label": "Default Subtitles Language", "type": "select", "options": [{ "label": "Български език", "value": "bul" }, { "label": "English", "value": "eng" }, { "label": "Deutsch", "value": "ger" }, { "label": "Español", "value": "esp" }, { "label": "Italiano", "value": "ita" }], "id": "default_subtitles_language" }, + { "section": "Player", "header": "Default Subtitles Size", "label": "Default Subtitles Size", "type": "select", "options": [{ "label": "72%", "value": "72%" }, { "label": "80%", "value": "80%" }, { "label": "100%", "value": "100%" }, { "label": "120%", "value": "120%" }, { "label": "140%", "value": "140%" }, { "label": "160%", "value": "160%" }, { "label": "180%", "value": "180%" }], "id": "default_subtitles_size" }, + { "section": "Player", "header": "Subtitles Background", "label": "Subtitles background", "type": "select", "options": [{ "label": "None", "value": "" }, { "label": "Solid", "value": "solid" }, { "label": "Transparent", "value": "transparent" }], "id": "subtitles_background" }, + { "section": "Player", "header": "Subtitles color", "label": "Subtitles color", "type": "color", "color": "#FFFFFF", "id": "subtitles_color" }, + { "section": "Player", "header": "Subtitles outline color", "label": "Subtitles outline color", "type": "color", "color": "#000000", "id": "subtitles_outline_color" }, + { "section": "Player", "label": "Auto-play next episode", "type": "checkbox", "id": "auto-play_next_episode" }, + { "section": "Player", "label": "Pause playback when minimized", "type": "checkbox", "id": "pause_playback_when_minimized" }, + { "section": "Player", "label": "Hardware-accelerated decoding", "type": "checkbox", "id": "hardware-accelerated_decoding" }, + { "section": "Player", "label": "Launch player in a separate window (advanced)", "type": "checkbox", "id": "launch_player_in_a_separate_window_(advanced)" }, + ] + }, + "Streaming": { + "inputs": [ + { "section": "Streaming", "header": "Caching", "label": "Caching", "type": "select", "options": [{ "label": "No Caching", "value": "" }, { "label": "2GB", "value": "2048" }, { "label": "5GB", "value": "5120" }, { "label": "10GB", "value": "10240" }], "id": "caching" }, + { "section": "Streaming", "header": "Torrent Profile", "label": "Torrent Profile", "type": "select", "options": [{ "label": "Default", "value": "profile-default" }, { "label": "Soft", "value": "profile-soft" }, { "label": "Fast", "value": "profile-fast" }], "id": "torrent_profile" }, + { "section": "Streaming", "header": "Streaming server URL: http://127.0.0.1:11470", "label": "Streaming server is available.", "type": "static-text", "icon": "ic_check", "id": "streaming_server_is_available." } + ] + } +}; + +const SECTIONS_ORDER = { + 'General': 1, + 'Player': 2, + 'Streaming': 3 +}; + +const useSettings = { + SECTIONS_ORDER, settingsSections +} + +module.exports = useSettings; \ No newline at end of file From 92a0522faabc385f39c75687b0553a9cc4b34f6a Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Wed, 25 Sep 2019 16:34:36 +0300 Subject: [PATCH 17/54] Split settings into several files --- .../Settings/SectionsList/SectionsList.js | 103 ++++++++ src/routes/Settings/SectionsList/index.js | 3 + .../SectionsSelector/SectionsSelector.js | 18 ++ src/routes/Settings/SectionsSelector/index.js | 3 + src/routes/Settings/Settings.js | 231 ++++-------------- 5 files changed, 175 insertions(+), 183 deletions(-) create mode 100644 src/routes/Settings/SectionsList/SectionsList.js create mode 100644 src/routes/Settings/SectionsList/index.js create mode 100644 src/routes/Settings/SectionsSelector/SectionsSelector.js create mode 100644 src/routes/Settings/SectionsSelector/index.js diff --git a/src/routes/Settings/SectionsList/SectionsList.js b/src/routes/Settings/SectionsList/SectionsList.js new file mode 100644 index 000000000..201ca4138 --- /dev/null +++ b/src/routes/Settings/SectionsList/SectionsList.js @@ -0,0 +1,103 @@ +const React = require('react'); +const { Button, Dropdown, Checkbox, ColorInput } = require('stremio/common'); +const Icon = require('stremio-icons/dom/Icon'); +const classnames = require('classnames'); +const styles = require('../styles'); + +const SectionsList = React.forwardRef(({className, sections, preferences, onPreferenceChanged, onScroll }, ref) => { + const scrollContainerRef = ref; + const toggleCheckbox = (id) => { + onPreferenceChanged(id, !preferences[id]); + }; + + const colorChanged = (id, color) => { + onPreferenceChanged(id, color); + }; + + const updateDropdown = (event) => { + var data = event.currentTarget.dataset; + onPreferenceChanged(data.name, data.value); + }; + + return ( +
+ {sections.map((section) => +
+
{section.id}
+ {(section.inputs || []) + .map((input) => { + if (input.type === 'user') { + return ( +
+ { + !preferences[input.id] + ? +
+ : +
+ } +
{!preferences[input.id] ? 'Anonymous user' : preferences[input.id].email}
+
+ ); + } else if (input.type === 'select') { + return ( +
+ {input.header ?
{input.header}
: null} + +
+ ); + } else if (input.type === 'link') { + return ( +
+ {input.header ?
{input.header}
: null} + +
+ ); + } else if (input.type === 'button') { + return ( +
+ {input.header ?
{input.header}
: null} + +
+ ); + } else if (input.type === 'checkbox') { + return ( +
+ {input.header ?
{input.header}
: null} + +
{input.label}
+
+
+ ); + } else if (input.type === 'static-text') { + return ( +
+ {input.header ?
{input.header}
: null} +
+ {input.icon ? : null} +
{input.label}
+
+
+ ); + } else if (input.type === 'color') { + return ( +
+ {input.header ?
{input.header}
: null} + + {/* */} +
+ ); + } + })} +
+ )} +
+ ); +}); + +module.exports = SectionsList; \ No newline at end of file diff --git a/src/routes/Settings/SectionsList/index.js b/src/routes/Settings/SectionsList/index.js new file mode 100644 index 000000000..ff7a9b4a0 --- /dev/null +++ b/src/routes/Settings/SectionsList/index.js @@ -0,0 +1,3 @@ +const SectionsList = require('./SectionsList'); + +module.exports = SectionsList; \ No newline at end of file diff --git a/src/routes/Settings/SectionsSelector/SectionsSelector.js b/src/routes/Settings/SectionsSelector/SectionsSelector.js new file mode 100644 index 000000000..2c1f44ced --- /dev/null +++ b/src/routes/Settings/SectionsSelector/SectionsSelector.js @@ -0,0 +1,18 @@ +const React = require('react'); +const { Button } = require('stremio/common'); +const classnames = require('classnames'); +const styles = require('../styles'); + +const SectionsSelector = ({ className, sections, selectedSectionId, onSelectedSection }) => { + return ( +
+ {sections.map((section) => + + )} +
+ ); +}; + +module.exports = SectionsSelector; \ No newline at end of file diff --git a/src/routes/Settings/SectionsSelector/index.js b/src/routes/Settings/SectionsSelector/index.js new file mode 100644 index 000000000..d873092a7 --- /dev/null +++ b/src/routes/Settings/SectionsSelector/index.js @@ -0,0 +1,3 @@ +const SectionsSelector = require('./SectionsSelector'); + +module.exports = SectionsSelector; diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index d5dd806a7..a892fac6b 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -1,128 +1,76 @@ const React = require('react'); -const classnames = require('classnames'); -const { Button, Dropdown, Checkbox, NavBar, ColorInput } = require('stremio/common'); -const Icon = require('stremio-icons/dom/Icon'); +const { NavBar } = require('stremio/common'); const styles = require('./styles'); +const SectionsSelector = require('./SectionsSelector'); +const SectionsList = require('./SectionsList'); +const { settingsSections, SECTIONS_ORDER } = require('./constants'); -const settingsData = [ - { section: 'General', label: 'Username', type: 'user', avatar: '', email: '' }, - { section: 'General', label: 'LOG OUT', type: 'button' }, - { section: 'General', label: 'Change password', type: 'link', href: '' }, - { section: 'General', label: 'Import options', type: 'static-text' }, - { section: 'General', label: 'Import from Facebook', type: 'link', href: '' }, - { section: 'General', label: 'Export user data', type: 'link', href: '' }, - { section: 'General', label: 'Subscribe to calendar', type: 'link', href: '' }, - { section: 'General', label: 'Contact support', type: 'link', href: 'https://stremio.zendesk.com/' }, - { section: 'General', label: 'Request account deletion', type: 'link', href: 'https://docs.google.com/forms/d/e/1FAIpQLScubrlTpDMIPUUBlhZ5lwcXl3HxzKfunIMCX5Jnp-cDyglWjQ/viewform?usp=sf_link' }, - { section: 'General', header: 'Trakt Scrobbling', label: 'AUTHENTICATE', type: 'button', icon: 'ic_trackt' }, - { section: 'General', header: 'UI Language', label: 'UI Language', type: 'select', options: ['Български език', 'English', 'Deutsch', 'Español', 'Italiano'], value: 'English' }, - { section: 'Player', label: 'ADD-ONS', type: 'button', icon: 'ic_addons' }, - { section: 'Player', header: 'Default Subtitles Language', label: 'Default Subtitles Language', type: 'select', options: ['English', 'Nederlands', 'Avesta', 'Български език', 'Deutsch', 'Español', 'Italiano'], value: 'English' }, - { section: 'Player', header: 'Default Subtitles Size', label: 'Default Subtitles Size', type: 'select', options: ['72%', '80%', '100%', '120%', '140%', '160%', '180%'], value: '100%' }, - { section: 'Player', header: 'Subtitles Background', label: 'Subtitles background', type: 'select', options: ['None', 'Solid', 'Transparent'], value: 'None' }, - { section: 'Player', header: 'Subtitles color', label: 'Subtitles color', type: 'color', color: '#FFFFFF' }, - { section: 'Player', header: 'Subtitles outline color', label: 'Subtitles outline color', type: 'color', color: '#000000' }, - { section: 'Player', label: 'Auto-play next episode', type: 'checkbox', value: true }, - { section: 'Player', label: 'Pause playback when minimized', type: 'checkbox', value: false }, - { section: 'Player', label: 'Hardware-accelerated decoding', type: 'checkbox', value: true }, - { section: 'Player', label: 'Launch player in a separate window (advanced)', type: 'checkbox', value: true }, - { section: 'Streaming', header: 'Caching', label: 'Caching', type: 'select', options: ['No Caching', '2GB', '5GB', '10GB'], value: '2GB' }, - { section: 'Streaming', header: 'Torrent Profile', label: 'Torrent Profile', type: 'select', options: ['Default', 'Soft', 'Fast'], value: 'Default' }, - { section: 'Streaming', header: 'Streaming server URL: http://127.0.0.1:11470', label: 'Streaming server is available.', type: 'static-text', icon: 'ic_check' } -]; - -const SECTIONS_ORDER = { - 'General': 1, - 'Player': 2, - 'Streaming': 3 +const settingsValues = { + "user": null, + "ui_language": "eng", + "default_subtitles_language": "", + "default_subtitles_size": "100%", + "subtitles_background": "", + "subtitles_color": "#ffffff", + "subtitles_outline_color": "#000", + "auto-play_next_episode": true, + "pause_playback_when_minimized": false, + "hardware-accelerated_decoding": true, + "launch_player_in_a_separate_window_(advanced)": true, + "caching": "2048", + "torrent_profile": "profile-default", + "streaming_server_is_available.": true, }; const Settings = () => { - const [selectedSectionId, setSelectedSectionId] = React.useState(null); - const [sections, setSections] = React.useState([]); - const [inputs, setInputs] = React.useState([]); + const sections = Object.keys(settingsSections) + .sort(function(a, b) { + const valueA = SECTIONS_ORDER[a]; + const valueB = SECTIONS_ORDER[b]; + if (!isNaN(valueA) && !isNaN(valueB)) return valueA - valueB; + if (!isNaN(valueA)) return -1; + if (!isNaN(valueB)) return 1; + return a - b; + }) + .map((section) => ({ + id: section, + inputs: settingsSections[section].inputs, + ref: React.createRef() + })); + const [selectedSectionId, setSelectedSectionId] = React.useState(sections[0].id); + const [preferences, setPreferences] = React.useState(settingsValues); const scrollContainerRef = React.useRef(null); - React.useEffect(() => { - const theSections = settingsData.map(({ section }) => section) - .filter((section, index, sections) => sections.indexOf(section) === index) - .sort(function(a, b) { - const valueA = SECTIONS_ORDER[a]; - const valueB = SECTIONS_ORDER[b]; - if (!isNaN(valueA) && !isNaN(valueB)) return valueA - valueB; - if (!isNaN(valueA)) return -1; - if (!isNaN(valueB)) return 1; - return a - b; - }) - .map((section) => ({ - id: section, - ref: React.createRef() - })); - setSections(theSections); - - if (theSections.length > 0 && (selectedSectionId === null || !theSections.find(({ id }) => id === selectedSectionId))) { - setSelectedSectionId(theSections[0].id); - } - - setInputs(settingsData.map((setting) => ({ - ...setting, - id: setting.label, - ref: React.createRef(), - active: !!(inputs.find(({ id }) => id === setting.label) || {}).active - }))); - }, []); - ///////////////// - const toggleCheckbox = (id) => { - setInputs(inputs.map((input) => ({ - ...input, - value: id === input.id ? !input.value : input.value - }))); - }; - - const colorChanged = (id, color) => { - setInputs(inputs.map((input) => ({ - ...input, - value: id === input.id ? color : input.value - }))); - }; - - const updateDropdown = (event) => { - var data = event.currentTarget.dataset; - setInputs(inputs.map((input) => ({ - ...input, - value: data.name === input.id ? data.value : input.value, - }))); - }; + const updatePreference = (option, value) => { + const newPrefs = { ...preferences }; + newPrefs[option] = value; + setPreferences(newPrefs); + } const changeSection = (event) => { const currentSectionId = event.currentTarget.dataset.section; const section = sections.find((section) => section.id === currentSectionId); - setSelectedSectionId(currentSectionId); + //setSelectedSectionId(currentSectionId); scrollContainerRef.current.scrollTo({ top: section.ref.current.offsetTop, behavior: 'smooth' }); }; - - const onScroll = (event) => { - const current = event.currentTarget; - if (sections.length <= 0) { - return; - } - - if (current.scrollTop + current.clientHeight === current.scrollHeight) { + const updateSection = React.useCallback((event) => { + const scrollContainer = event.currentTarget; + if (scrollContainer.scrollTop + scrollContainer.clientHeight === scrollContainer.scrollHeight) { setSelectedSectionId(sections[sections.length - 1].id); } else { for (let i = sections.length - 1;i >= 0;i--) { - if (sections[i].ref.current.offsetTop <= current.scrollTop) { + if (sections[i].ref.current.offsetTop <= scrollContainer.scrollTop) { setSelectedSectionId(sections[i].id); break; } } } - }; + }); return (
@@ -133,91 +81,8 @@ const Settings = () => { fullscreenButton={true} navMenu={true} />
-
- {sections.map((section) => - - )} -
-
- {sections.map((section) => -
-
{section.id}
- {inputs.filter((input) => input.section === section.id) - .map((input) => { - if (input.type === 'user') { - return ( -
- { - input.email.length === 0 - ? -
- : -
- } -
{input.email.length === 0 ? 'Anonymous user' : input.email}
-
- ); - } else if (input.type === 'select') { - return ( -
- {input.header ?
{input.header}
: null} - {/* TODO: provide proper data instead of mapping the options */} - ({ value: o, label: o }))} selected={[input.value]} name={input.id} key={input.id} className={styles['dropdown']} onSelect={updateDropdown} /> -
- ); - } else if (input.type === 'link') { - return ( -
- {input.header ?
{input.header}
: null} - -
- ); - } else if (input.type === 'button') { - return ( -
- {input.header ?
{input.header}
: null} - -
- ); - } else if (input.type === 'checkbox') { - return ( -
- {input.header ?
{input.header}
: null} - -
{input.label}
-
-
- ); - } else if (input.type === 'static-text') { - return ( -
- {input.header ?
{input.header}
: null} -
- {input.icon ? : null} -
{input.label}
-
-
- ); - } else if (input.type === 'color') { - return ( -
- {input.header ?
{input.header}
: null} - - {/* */} -
- ); - } - })} -
- )} -
+ +
); From 81b28263a428aa350488e8afc15830e0866b3eda Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Wed, 25 Sep 2019 16:59:37 +0300 Subject: [PATCH 18/54] Moved ColorInput component into Settings --- src/common/index.js | 1 - src/{common => routes/Settings}/ColorInput/ColorInput.js | 0 src/{common => routes/Settings}/ColorInput/index.js | 0 src/{common => routes/Settings}/ColorInput/styles.less | 0 src/routes/Settings/SectionsList/SectionsList.js | 3 ++- 5 files changed, 2 insertions(+), 2 deletions(-) rename src/{common => routes/Settings}/ColorInput/ColorInput.js (100%) rename src/{common => routes/Settings}/ColorInput/index.js (100%) rename src/{common => routes/Settings}/ColorInput/styles.less (100%) diff --git a/src/common/index.js b/src/common/index.js index 28bad5f65..28634aecf 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -27,7 +27,6 @@ module.exports = { Button, Checkbox, ColorPicker, - ColorInput, Dropdown, MainNavBar, MetaItem, diff --git a/src/common/ColorInput/ColorInput.js b/src/routes/Settings/ColorInput/ColorInput.js similarity index 100% rename from src/common/ColorInput/ColorInput.js rename to src/routes/Settings/ColorInput/ColorInput.js diff --git a/src/common/ColorInput/index.js b/src/routes/Settings/ColorInput/index.js similarity index 100% rename from src/common/ColorInput/index.js rename to src/routes/Settings/ColorInput/index.js diff --git a/src/common/ColorInput/styles.less b/src/routes/Settings/ColorInput/styles.less similarity index 100% rename from src/common/ColorInput/styles.less rename to src/routes/Settings/ColorInput/styles.less diff --git a/src/routes/Settings/SectionsList/SectionsList.js b/src/routes/Settings/SectionsList/SectionsList.js index 201ca4138..cd2f0a303 100644 --- a/src/routes/Settings/SectionsList/SectionsList.js +++ b/src/routes/Settings/SectionsList/SectionsList.js @@ -1,6 +1,7 @@ const React = require('react'); -const { Button, Dropdown, Checkbox, ColorInput } = require('stremio/common'); +const { Button, Dropdown, Checkbox } = require('stremio/common'); const Icon = require('stremio-icons/dom/Icon'); +const ColorInput = require('../ColorInput'); const classnames = require('classnames'); const styles = require('../styles'); From 04e2859d2c177de0785c29afecd0e6b9ad773127 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Wed, 25 Sep 2019 18:06:34 +0300 Subject: [PATCH 19/54] Removet ColorInput from common components list --- src/common/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/index.js b/src/common/index.js index 28634aecf..b22b17376 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -1,7 +1,6 @@ const Button = require('./Button'); const Checkbox = require('./Checkbox'); const ColorPicker = require('./ColorPicker'); -const ColorInput = require('./ColorInput'); const Dropdown = require('./Dropdown'); const MainNavBar = require('./MainNavBar'); const MetaItem = require('./MetaItem'); From 581cf02b9634347db7bff67f44f3a1fe7892063d Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Wed, 25 Sep 2019 18:06:46 +0300 Subject: [PATCH 20/54] Color input using modal --- src/routes/Settings/ColorInput/ColorInput.js | 18 ++--- src/routes/Settings/ColorInput/styles.less | 74 +++++++++++--------- 2 files changed, 48 insertions(+), 44 deletions(-) diff --git a/src/routes/Settings/ColorInput/ColorInput.js b/src/routes/Settings/ColorInput/ColorInput.js index 28278d354..2899610c6 100644 --- a/src/routes/Settings/ColorInput/ColorInput.js +++ b/src/routes/Settings/ColorInput/ColorInput.js @@ -1,7 +1,7 @@ const React = require('react'); const classnames = require('classnames'); const PropTypes = require('prop-types'); -const Popup = require('stremio/common/Popup'); +const { Modal } = require('stremio-router'); const Button = require('stremio/common/Button'); const ColorPicker = require('stremio/common/ColorPicker'); const useBinaryState = require('stremio/common/useBinaryState'); @@ -18,15 +18,9 @@ const ColorInput = ({ className, defaultValue, onChange }) => { closeMenu(); }, [color, onChange]); return ( - event.type === 'scroll' || closeMenu()} - renderLabel={(ref) => ( - - )} - renderMenu={() => ( + + + {menuOpen ?
- )} - /> +
: null} +
); }; diff --git a/src/routes/Settings/ColorInput/styles.less b/src/routes/Settings/ColorInput/styles.less index 328bed6a0..a0ea87d8d 100644 --- a/src/routes/Settings/ColorInput/styles.less +++ b/src/routes/Settings/ColorInput/styles.less @@ -1,40 +1,50 @@ -.color-input-container { - --spacing: 1rem; - padding: var(--spacing); - background-color: var(--color-surfacelighter); +.color-input-modal { + background-color: var(--color-backgrounddarker40); + display: flex; + flex-direction: column; - * { - overflow: visible; - } - .x-icon { - position: absolute; - top: 1rem; - right: 1rem; - width: .8rem; - height: .8rem; - fill: var(--color-surfacedark); - } + .color-input-container { + position: relative; + --spacing: 1rem; + padding: var(--spacing); + background-color: var(--color-surfacelighter); - h1 { - font-size: 1.2rem; - } + margin: auto; - .color-input { - margin: var(--spacing) auto 0 auto; - :global(.a-color-picker-stack):not(:global(.a-color-picker-row-top)) canvas, :global(.a-color-picker-circle) { - border: solid 1px var(--color-surfacedark); + * { + overflow: visible; } - :global(.a-color-picker-circle) { - box-shadow: 0 0 .2rem var(--color-surfacedark); - } - } - .button { - text-align: center; - color: var(--color-surfacelighter); - background-color: var(--color-signal5); - padding: 1rem; - margin-top: var(--spacing); + .x-icon { + position: absolute; + top: 1rem; + right: 1rem; + width: .8rem; + height: .8rem; + fill: var(--color-surfacedark); + } + + h1 { + font-size: 1.2rem; + } + + .color-input { + margin: var(--spacing) auto 0 auto; + :global(.a-color-picker-stack):not(:global(.a-color-picker-row-top)) canvas, :global(.a-color-picker-circle) { + border: solid 1px var(--color-surfacedark); + } + :global(.a-color-picker-circle) { + box-shadow: 0 0 .2rem var(--color-surfacedark); + } + } + + .button { + text-align: center; + color: var(--color-surfacelighter); + background-color: var(--color-signal5); + padding: 1rem; + margin-top: var(--spacing); + } } } \ No newline at end of file From c9d5a4e42ff414cdafbe7353b46dfc3de02ec7e3 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Thu, 26 Sep 2019 09:31:17 +0300 Subject: [PATCH 21/54] Checkbox styles --- src/routes/Settings/styles.less | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/routes/Settings/styles.less b/src/routes/Settings/styles.less index e9d36c98e..20141676d 100644 --- a/src/routes/Settings/styles.less +++ b/src/routes/Settings/styles.less @@ -1,3 +1,7 @@ +:import('~stremio/common/Checkbox/styles.less') { + checkbox-icon: icon; +} + .settings-parent-container { display: flex; flex-direction: column; @@ -78,6 +82,13 @@ color: var(--color-surfacelighter); } + .checkbox-icon { + flex: none; + width: 1.2rem; + height: 1.2rem; + fill: var(--color-surfacelight); + } + &.user-container { flex-direction: row; align-items: center; From 0d03bfe9dbaa1ff2f2e06eed0b1ac1a0835fd0f0 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Thu, 26 Sep 2019 16:17:16 +0300 Subject: [PATCH 22/54] Hardcoded user settings. Determine if link is internal --- .../Settings/SectionsList/SectionsList.js | 210 +++++++++++------- src/routes/Settings/Settings.js | 10 +- src/routes/Settings/constants.js | 13 +- 3 files changed, 144 insertions(+), 89 deletions(-) diff --git a/src/routes/Settings/SectionsList/SectionsList.js b/src/routes/Settings/SectionsList/SectionsList.js index cd2f0a303..865331933 100644 --- a/src/routes/Settings/SectionsList/SectionsList.js +++ b/src/routes/Settings/SectionsList/SectionsList.js @@ -5,7 +5,7 @@ const ColorInput = require('../ColorInput'); const classnames = require('classnames'); const styles = require('../styles'); -const SectionsList = React.forwardRef(({className, sections, preferences, onPreferenceChanged, onScroll }, ref) => { +const SectionsList = React.forwardRef(({ className, sections, preferences, onPreferenceChanged, onScroll }, ref) => { const scrollContainerRef = ref; const toggleCheckbox = (id) => { onPreferenceChanged(id, !preferences[id]); @@ -20,83 +20,141 @@ const SectionsList = React.forwardRef(({className, sections, preferences, onPref onPreferenceChanged(data.name, data.value); }; + // Determines whether the link should be opened in new window or in the current one. + const getTargetFor = url => ['//', 'http://', 'https://', 'file://', 'ftp://', 'mailto:', 'magnet:'].some(scheme => url.startsWith(scheme)) ? + '_blank' : '_self' + + const changePasswordUrl = preferences.user && 'https://www.strem.io/reset-password/' + preferences.user.email; + const webCalUrl = preferences.user && 'webcal://www.strem.io/calendar/' + preferences.user._id + '.ics'; + + const sectionsElements = sections.map((section) => +
+
{section.id}
+ {(section.inputs || []) + .map((input) => { + if (input.type === 'user') { + return ( + +
+ { + !preferences.user + ? +
+ : +
+ } +
{!preferences.user ? 'Anonymous user' : preferences.user.email}
+
+
+ +
+
+ +
+
+
+
{'Import options'}
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
{'Trakt Scrobbling'}
+ +
+ + ); + } else if (input.type === 'select') { + return ( +
+ {input.header ?
{input.header}
: null} + +
+ ); + } else if (input.type === 'link') { + return ( +
+ {input.header ?
{input.header}
: null} + +
+ ); + } else if (input.type === 'button') { + return ( +
+ {input.header ?
{input.header}
: null} + +
+ ); + } else if (input.type === 'checkbox') { + return ( +
+ {input.header ?
{input.header}
: null} + +
{input.label}
+
+
+ ); + } else if (input.type === 'static-text') { + return ( +
+ {input.header ?
{input.header}
: null} +
+ {input.icon ? : null} +
{input.label}
+
+
+ ); + } else if (input.type === 'color') { + return ( +
+ {input.header ?
{input.header}
: null} + + {/* */} +
+ ); + } + })} +
+ ); + return (
- {sections.map((section) => -
-
{section.id}
- {(section.inputs || []) - .map((input) => { - if (input.type === 'user') { - return ( -
- { - !preferences[input.id] - ? -
- : -
- } -
{!preferences[input.id] ? 'Anonymous user' : preferences[input.id].email}
-
- ); - } else if (input.type === 'select') { - return ( -
- {input.header ?
{input.header}
: null} - -
- ); - } else if (input.type === 'link') { - return ( -
- {input.header ?
{input.header}
: null} - -
- ); - } else if (input.type === 'button') { - return ( -
- {input.header ?
{input.header}
: null} - -
- ); - } else if (input.type === 'checkbox') { - return ( -
- {input.header ?
{input.header}
: null} - -
{input.label}
-
-
- ); - } else if (input.type === 'static-text') { - return ( -
- {input.header ?
{input.header}
: null} -
- {input.icon ? : null} -
{input.label}
-
-
- ); - } else if (input.type === 'color') { - return ( -
- {input.header ?
{input.header}
: null} - - {/* */} -
- ); - } - })} -
- )} + {sectionsElements}
); }); diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index a892fac6b..bda1fbfbc 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -5,10 +5,16 @@ const SectionsSelector = require('./SectionsSelector'); const SectionsList = require('./SectionsList'); const { settingsSections, SECTIONS_ORDER } = require('./constants'); +const devTestWithUser = true; + const settingsValues = { - "user": null, + "user": devTestWithUser ? { + "_id": "neo", + "email": "neo@example.com", + "avatar": "https://www.thenational.ae/image/policy:1.891803:1566372420/AC17-Matrix-20-04.jpg?f=16x9&w=1200&$p$f$w=5867e40", + } : null, "ui_language": "eng", - "default_subtitles_language": "", + "default_subtitles_language": "bul", "default_subtitles_size": "100%", "subtitles_background": "", "subtitles_color": "#ffffff", diff --git a/src/routes/Settings/constants.js b/src/routes/Settings/constants.js index 7f89debee..796f20807 100644 --- a/src/routes/Settings/constants.js +++ b/src/routes/Settings/constants.js @@ -1,22 +1,13 @@ const settingsSections = { "General": { "inputs": [ - { "section": "General", "label": "Username", "type": "user", "id": "user" }, - { "section": "General", "label": "LOG OUT", "type": "button", "id": "log_out" }, - { "section": "General", "label": "Change password", "type": "link", "href": "", "id": "change_password" }, - { "section": "General", "label": "Import options", "type": "static-text", "id": "import_options" }, - { "section": "General", "label": "Import from Facebook", "type": "link", "href": "", "id": "import_from_facebook" }, - { "section": "General", "label": "Export user data", "type": "link", "href": "", "id": "export_user_data" }, - { "section": "General", "label": "Subscribe to calendar", "type": "link", "href": "", "id": "subscribe_to_calendar" }, - { "section": "General", "label": "Contact support", "type": "link", "id": "contact_support" }, - { "section": "General", "label": "Request account deletion", "type": "link", "id": "request_account_deletion" }, - { "section": "General", "header": "Trakt Scrobbling", "label": "AUTHENTICATE", "type": "button", "icon": "ic_trackt", "id": "authenticate" }, + { "section": "General", "type": "user" }, { "section": "General", "header": "UI Language", "label": "UI Language", "type": "select", "options": [{ "label": "Български език", "value": "bul" }, { "label": "English", "value": "eng" }, { "label": "Deutsch", "value": "ger" }, { "label": "Español", "value": "esp" }, { "label": "Italiano", "value": "ita" }], "id": "ui_language" }, ], }, "Player": { "inputs": [ - { "section": "Player", "label": "ADD-ONS", "type": "button", "icon": "ic_addons", "id": "add-ons" }, + { "section": "Player", "label": "ADD-ONS", "type": "button", "icon": "ic_addons", "id": "add-ons", "href": "#/addons" }, { "section": "Player", "header": "Default Subtitles Language", "label": "Default Subtitles Language", "type": "select", "options": [{ "label": "Български език", "value": "bul" }, { "label": "English", "value": "eng" }, { "label": "Deutsch", "value": "ger" }, { "label": "Español", "value": "esp" }, { "label": "Italiano", "value": "ita" }], "id": "default_subtitles_language" }, { "section": "Player", "header": "Default Subtitles Size", "label": "Default Subtitles Size", "type": "select", "options": [{ "label": "72%", "value": "72%" }, { "label": "80%", "value": "80%" }, { "label": "100%", "value": "100%" }, { "label": "120%", "value": "120%" }, { "label": "140%", "value": "140%" }, { "label": "160%", "value": "160%" }, { "label": "180%", "value": "180%" }], "id": "default_subtitles_size" }, { "section": "Player", "header": "Subtitles Background", "label": "Subtitles background", "type": "select", "options": [{ "label": "None", "value": "" }, { "label": "Solid", "value": "solid" }, { "label": "Transparent", "value": "transparent" }], "id": "subtitles_background" }, From 9c4715a052a2c92b6627f4d3b798fbfbe2d9630b Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Fri, 27 Sep 2019 11:38:54 +0300 Subject: [PATCH 23/54] Reset ColorInput when closed; Close by clicking outside the dialog --- src/routes/Settings/ColorInput/ColorInput.js | 42 +++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/routes/Settings/ColorInput/ColorInput.js b/src/routes/Settings/ColorInput/ColorInput.js index 2899610c6..50bbfcc85 100644 --- a/src/routes/Settings/ColorInput/ColorInput.js +++ b/src/routes/Settings/ColorInput/ColorInput.js @@ -9,27 +9,41 @@ const Icon = require('stremio-icons/dom/Icon'); const styles = require('./styles'); const ColorInput = ({ className, defaultValue, onChange }) => { - const [menuOpen, openMenu, closeMenu, toggleMenu] = useBinaryState(false); + const colorInputRef = React.useRef(null); + const [colorInputVisible, showColorInput, closeColorInput] = useBinaryState(false); const [color, setColor] = React.useState(defaultValue); const [selectedColor, setSelectedColor] = React.useState(defaultValue); - const updateColorsAndCloseMenu = React.useCallback(() => { + const discardColorInput = React.useCallback((event) => { + if (event.type === "mousedown" && colorInputRef.current.contains(event.target)) { + return; + } + setColor(selectedColor); + closeColorInput(); + }); + const confirmColorInput = React.useCallback(() => { setSelectedColor(color); onChange(color); - closeMenu(); + closeColorInput(); }, [color, onChange]); return ( - - {menuOpen ? -
- -

Choose a color:

- - -
-
: null} + + { + colorInputVisible + ? + +
+ +

Choose a color:

+ + +
+
+ : + null + }
); }; From 335e2e857bfcfa973c08734835927db3e034aa0e Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Fri, 27 Sep 2019 16:52:16 +0300 Subject: [PATCH 24/54] Log a message when anonymous user tries to use some features --- .../Settings/SectionsList/SectionsList.js | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/routes/Settings/SectionsList/SectionsList.js b/src/routes/Settings/SectionsList/SectionsList.js index 865331933..54145810f 100644 --- a/src/routes/Settings/SectionsList/SectionsList.js +++ b/src/routes/Settings/SectionsList/SectionsList.js @@ -20,10 +20,19 @@ const SectionsList = React.forwardRef(({ className, sections, preferences, onPre onPreferenceChanged(data.name, data.value); }; - // Determines whether the link should be opened in new window or in the current one. - const getTargetFor = url => ['//', 'http://', 'https://', 'file://', 'ftp://', 'mailto:', 'magnet:'].some(scheme => url.startsWith(scheme)) ? - '_blank' : '_self' + const checkUser = (event) => { + if(! preferences.user) { + // Here in Stremio 4 we show a toast with a message, asking the anonymous user to log in/register + console.log('No user found'); + event.preventDefault(); + } + } + // Determines whether the link should be opened in new window or in the current one. + const getTargetFor = url => ['//', 'http://', 'https://', 'file://', 'ftp://', 'mailto:', 'magnet:'] + .some(scheme => url.startsWith(scheme)) ? '_blank' : '_self' + + // TODO: If we get the user data after initialization, these should be wrapped in React.useState and set by React.useEffect const changePasswordUrl = preferences.user && 'https://www.strem.io/reset-password/' + preferences.user.email; const webCalUrl = preferences.user && 'webcal://www.strem.io/calendar/' + preferences.user._id + '.ics'; @@ -51,7 +60,7 @@ const SectionsList = React.forwardRef(({ className, sections, preferences, onPre
-
@@ -61,17 +70,17 @@ const SectionsList = React.forwardRef(({ className, sections, preferences, onPre
-
-
-
From 4d29fa7ec6d70af9d0ba9f0d88178039396cc40f Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Fri, 27 Sep 2019 16:53:37 +0300 Subject: [PATCH 25/54] Make Sign in/Log out button work --- src/routes/Settings/SectionsList/SectionsList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Settings/SectionsList/SectionsList.js b/src/routes/Settings/SectionsList/SectionsList.js index 54145810f..813729b0b 100644 --- a/src/routes/Settings/SectionsList/SectionsList.js +++ b/src/routes/Settings/SectionsList/SectionsList.js @@ -55,7 +55,7 @@ const SectionsList = React.forwardRef(({ className, sections, preferences, onPre
{!preferences.user ? 'Anonymous user' : preferences.user.email}
-
From 1adb6b6815417cec7757f8e0cea71f5ee59f646a Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Fri, 27 Sep 2019 17:53:57 +0300 Subject: [PATCH 26/54] Remove spacing var in ColorInput --- src/routes/Settings/ColorInput/styles.less | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/routes/Settings/ColorInput/styles.less b/src/routes/Settings/ColorInput/styles.less index a0ea87d8d..a230323b1 100644 --- a/src/routes/Settings/ColorInput/styles.less +++ b/src/routes/Settings/ColorInput/styles.less @@ -6,8 +6,7 @@ .color-input-container { position: relative; - --spacing: 1rem; - padding: var(--spacing); + padding: 1rem; background-color: var(--color-surfacelighter); margin: auto; @@ -30,7 +29,7 @@ } .color-input { - margin: var(--spacing) auto 0 auto; + margin: 1rem auto 0 auto; :global(.a-color-picker-stack):not(:global(.a-color-picker-row-top)) canvas, :global(.a-color-picker-circle) { border: solid 1px var(--color-surfacedark); } @@ -44,7 +43,7 @@ color: var(--color-surfacelighter); background-color: var(--color-signal5); padding: 1rem; - margin-top: var(--spacing); + margin-top: 1rem; } } } \ No newline at end of file From 2c61153294f7eeb0bd8822c10d890cbff4f24003 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Fri, 27 Sep 2019 17:59:22 +0300 Subject: [PATCH 27/54] Remove the spacing var from Settings styles --- src/routes/Settings/styles.less | 35 ++++++++++++++++----------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/routes/Settings/styles.less b/src/routes/Settings/styles.less index 20141676d..9c90f4b6c 100644 --- a/src/routes/Settings/styles.less +++ b/src/routes/Settings/styles.less @@ -20,18 +20,17 @@ display: flex; flex-direction: row; background-color: var(--color-backgroundlight); - --spacing: 1rem; --input-width: 35rem; .side-menu { - padding: var(--spacing); + padding: 1rem; width: 17rem; display: flex; flex-direction: column; background-color: var(--color-backgroundlighter); .section-label { - padding: var(--spacing); + padding: 1rem; font-size: 1.1rem; border: calc(var(--focusable-border-size) * 0.5) solid transparent; color: var(--color-surfacelight); @@ -59,26 +58,26 @@ } .scroll-container { - padding: 0 calc(var(--spacing) * 2); + padding: 0 2rem; flex: 1; overflow-y: auto; .section { - padding: calc(var(--spacing) * 4) calc(var(--spacing) * 2); + padding: 4rem 2rem; .section-header { - margin: 0 calc(var(--spacing) * 1.5) calc(var(--spacing) * 1.5) calc(var(--spacing) * 1.5); + margin: 0 1.5rem 1.5rem 1.5rem; font-size: 2rem; color: var(--color-surfacelighter); } .input-container { - margin: calc(var(--spacing) * 1.5); + margin: 1.5rem; display: flex; flex-direction: column; .input-header { - margin-bottom: calc(var(--spacing) * 0.5); + margin-bottom: 0.5rem; color: var(--color-surfacelighter); } @@ -94,7 +93,7 @@ align-items: center; .avatar { - margin: 0 var(--spacing); + margin: 0 1rem; width: 4.2rem; height: 4.2rem; border-radius: 50%; @@ -117,7 +116,7 @@ } &.link-container { - margin: var(--spacing) calc(var(--spacing) * 1.5); + margin: 1rem 1.5rem; .link { display: block; @@ -135,7 +134,7 @@ &.button-container { .button { - padding: calc(var(--spacing) * 0.7); + padding: 0.7rem; width: var(--input-width); min-height: calc(var(--input-width) * 0.09); display: flex; @@ -147,7 +146,7 @@ .icon { width: 1.4rem; height: 100%; - margin-right: calc(var(--spacing) * 0.5); + margin-right: 0.5rem; fill: var(--color-surfacelighter); } @@ -205,16 +204,16 @@ align-items: center; .icon { - margin-right: calc(var(--spacing) * 0.5); - width: var(--spacing); - height: var(--spacing); + margin-right: 0.5rem; + width: 1rem; + height: 1rem; fill: var(--color-signal5); } .x-icon { - margin-right: calc(var(--spacing) * 0.5); - width: var(--spacing); - height: var(--spacing); + margin-right: 0.5rem; + width: 1rem; + height: 1rem; fill: var(--color-signal2); } From 56fd4e1a782508c5e05b9aa19b7085caa4592fc2 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 10:52:16 +0300 Subject: [PATCH 28/54] Dropped the sections sorting --- src/routes/Settings/Settings.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index bda1fbfbc..6c44a3d53 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -30,14 +30,6 @@ const settingsValues = { const Settings = () => { const sections = Object.keys(settingsSections) - .sort(function(a, b) { - const valueA = SECTIONS_ORDER[a]; - const valueB = SECTIONS_ORDER[b]; - if (!isNaN(valueA) && !isNaN(valueB)) return valueA - valueB; - if (!isNaN(valueA)) return -1; - if (!isNaN(valueB)) return 1; - return a - b; - }) .map((section) => ({ id: section, inputs: settingsSections[section].inputs, From 30305da6231903e05da7aacf999d28ffcb65e640 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 10:53:11 +0300 Subject: [PATCH 29/54] PropTypes definitions --- .../Settings/SectionsSelector/SectionsSelector.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/routes/Settings/SectionsSelector/SectionsSelector.js b/src/routes/Settings/SectionsSelector/SectionsSelector.js index 2c1f44ced..0cfcce343 100644 --- a/src/routes/Settings/SectionsSelector/SectionsSelector.js +++ b/src/routes/Settings/SectionsSelector/SectionsSelector.js @@ -1,4 +1,5 @@ const React = require('react'); +const PropTypes = require('prop-types'); const { Button } = require('stremio/common'); const classnames = require('classnames'); const styles = require('../styles'); @@ -15,4 +16,13 @@ const SectionsSelector = ({ className, sections, selectedSectionId, onSelectedSe ); }; -module.exports = SectionsSelector; \ No newline at end of file +SectionsSelector.propTypes = { + className: PropTypes.string, + sections: PropTypes.arrayOf(PropTypes.shape({ + id: PropTypes.string.isRequired, + })), + selectedSectionId: PropTypes.string.isRequired, + onSelectedSection: PropTypes.func.isRequired, +}; + +module.exports = SectionsSelector; From c89f80c397bac736302368c1b0c55f080b7f593a Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 10:56:14 +0300 Subject: [PATCH 30/54] Removed the type parameter of the buttons --- .../Settings/SectionsList/SectionsList.js | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/routes/Settings/SectionsList/SectionsList.js b/src/routes/Settings/SectionsList/SectionsList.js index 813729b0b..a1a0895ce 100644 --- a/src/routes/Settings/SectionsList/SectionsList.js +++ b/src/routes/Settings/SectionsList/SectionsList.js @@ -55,12 +55,12 @@ const SectionsList = React.forwardRef(({ className, sections, preferences, onPre
{!preferences.user ? 'Anonymous user' : preferences.user.email}
-
-
@@ -70,33 +70,33 @@ const SectionsList = React.forwardRef(({ className, sections, preferences, onPre
-
-
-
-
-
{'Trakt Scrobbling'}
- @@ -114,7 +114,7 @@ const SectionsList = React.forwardRef(({ className, sections, preferences, onPre return (
{input.header ?
{input.header}
: null} -
@@ -123,7 +123,7 @@ const SectionsList = React.forwardRef(({ className, sections, preferences, onPre return (
{input.header ?
{input.header}
: null} - @@ -153,7 +153,7 @@ const SectionsList = React.forwardRef(({ className, sections, preferences, onPre
{input.header ?
{input.header}
: null} - {/* */} + {/* */}
); } From f824a967a706fc955eff8eaccd863a793daf45a1 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 11:01:31 +0300 Subject: [PATCH 31/54] useRef instead of createRef for performance --- src/routes/Settings/Settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 6c44a3d53..af544843a 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -33,7 +33,7 @@ const Settings = () => { .map((section) => ({ id: section, inputs: settingsSections[section].inputs, - ref: React.createRef() + ref: React.useRef(null) })); const [selectedSectionId, setSelectedSectionId] = React.useState(sections[0].id); const [preferences, setPreferences] = React.useState(settingsValues); From 007ac3d5061f6da9977bba4158f4fefdcb702d17 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 11:22:58 +0300 Subject: [PATCH 32/54] Flatten constants. Use single quotes --- src/routes/Settings/Settings.js | 4 +-- src/routes/Settings/constants.js | 56 +++++++++++++------------------- 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index af544843a..0dc87d3bd 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -3,7 +3,7 @@ const { NavBar } = require('stremio/common'); const styles = require('./styles'); const SectionsSelector = require('./SectionsSelector'); const SectionsList = require('./SectionsList'); -const { settingsSections, SECTIONS_ORDER } = require('./constants'); +const { settingsSections } = require('./constants'); const devTestWithUser = true; @@ -32,7 +32,7 @@ const Settings = () => { const sections = Object.keys(settingsSections) .map((section) => ({ id: section, - inputs: settingsSections[section].inputs, + inputs: settingsSections[section], ref: React.useRef(null) })); const [selectedSectionId, setSelectedSectionId] = React.useState(sections[0].id); diff --git a/src/routes/Settings/constants.js b/src/routes/Settings/constants.js index 796f20807..65c3cf6b6 100644 --- a/src/routes/Settings/constants.js +++ b/src/routes/Settings/constants.js @@ -1,41 +1,29 @@ const settingsSections = { - "General": { - "inputs": [ - { "section": "General", "type": "user" }, - { "section": "General", "header": "UI Language", "label": "UI Language", "type": "select", "options": [{ "label": "Български език", "value": "bul" }, { "label": "English", "value": "eng" }, { "label": "Deutsch", "value": "ger" }, { "label": "Español", "value": "esp" }, { "label": "Italiano", "value": "ita" }], "id": "ui_language" }, - ], - }, - "Player": { - "inputs": [ - { "section": "Player", "label": "ADD-ONS", "type": "button", "icon": "ic_addons", "id": "add-ons", "href": "#/addons" }, - { "section": "Player", "header": "Default Subtitles Language", "label": "Default Subtitles Language", "type": "select", "options": [{ "label": "Български език", "value": "bul" }, { "label": "English", "value": "eng" }, { "label": "Deutsch", "value": "ger" }, { "label": "Español", "value": "esp" }, { "label": "Italiano", "value": "ita" }], "id": "default_subtitles_language" }, - { "section": "Player", "header": "Default Subtitles Size", "label": "Default Subtitles Size", "type": "select", "options": [{ "label": "72%", "value": "72%" }, { "label": "80%", "value": "80%" }, { "label": "100%", "value": "100%" }, { "label": "120%", "value": "120%" }, { "label": "140%", "value": "140%" }, { "label": "160%", "value": "160%" }, { "label": "180%", "value": "180%" }], "id": "default_subtitles_size" }, - { "section": "Player", "header": "Subtitles Background", "label": "Subtitles background", "type": "select", "options": [{ "label": "None", "value": "" }, { "label": "Solid", "value": "solid" }, { "label": "Transparent", "value": "transparent" }], "id": "subtitles_background" }, - { "section": "Player", "header": "Subtitles color", "label": "Subtitles color", "type": "color", "color": "#FFFFFF", "id": "subtitles_color" }, - { "section": "Player", "header": "Subtitles outline color", "label": "Subtitles outline color", "type": "color", "color": "#000000", "id": "subtitles_outline_color" }, - { "section": "Player", "label": "Auto-play next episode", "type": "checkbox", "id": "auto-play_next_episode" }, - { "section": "Player", "label": "Pause playback when minimized", "type": "checkbox", "id": "pause_playback_when_minimized" }, - { "section": "Player", "label": "Hardware-accelerated decoding", "type": "checkbox", "id": "hardware-accelerated_decoding" }, - { "section": "Player", "label": "Launch player in a separate window (advanced)", "type": "checkbox", "id": "launch_player_in_a_separate_window_(advanced)" }, - ] - }, - "Streaming": { - "inputs": [ - { "section": "Streaming", "header": "Caching", "label": "Caching", "type": "select", "options": [{ "label": "No Caching", "value": "" }, { "label": "2GB", "value": "2048" }, { "label": "5GB", "value": "5120" }, { "label": "10GB", "value": "10240" }], "id": "caching" }, - { "section": "Streaming", "header": "Torrent Profile", "label": "Torrent Profile", "type": "select", "options": [{ "label": "Default", "value": "profile-default" }, { "label": "Soft", "value": "profile-soft" }, { "label": "Fast", "value": "profile-fast" }], "id": "torrent_profile" }, - { "section": "Streaming", "header": "Streaming server URL: http://127.0.0.1:11470", "label": "Streaming server is available.", "type": "static-text", "icon": "ic_check", "id": "streaming_server_is_available." } - ] - } -}; - -const SECTIONS_ORDER = { - 'General': 1, - 'Player': 2, - 'Streaming': 3 + 'General': [ + { 'section': 'General', 'type': 'user' }, + { 'section': 'General', 'header': 'UI Language', 'label': 'UI Language', 'type': 'select', 'options': [{ 'label': 'Български език', 'value': 'bul' }, { 'label': 'English', 'value': 'eng' }, { 'label': 'Deutsch', 'value': 'ger' }, { 'label': 'Español', 'value': 'esp' }, { 'label': 'Italiano', 'value': 'ita' }], 'id': 'ui_language' }, + ], + 'Player': [ + { 'section': 'Player', 'label': 'ADD-ONS', 'type': 'button', 'icon': 'ic_addons', 'id': 'add-ons', 'href': '#/addons' }, + { 'section': 'Player', 'header': 'Default Subtitles Language', 'label': 'Default Subtitles Language', 'type': 'select', 'options': [{ 'label': 'Български език', 'value': 'bul' }, { 'label': 'English', 'value': 'eng' }, { 'label': 'Deutsch', 'value': 'ger' }, { 'label': 'Español', 'value': 'esp' }, { 'label': 'Italiano', 'value': 'ita' }], 'id': 'default_subtitles_language' }, + { 'section': 'Player', 'header': 'Default Subtitles Size', 'label': 'Default Subtitles Size', 'type': 'select', 'options': [{ 'label': '72%', 'value': '72%' }, { 'label': '80%', 'value': '80%' }, { 'label': '100%', 'value': '100%' }, { 'label': '120%', 'value': '120%' }, { 'label': '140%', 'value': '140%' }, { 'label': '160%', 'value': '160%' }, { 'label': '180%', 'value': '180%' }], 'id': 'default_subtitles_size' }, + { 'section': 'Player', 'header': 'Subtitles Background', 'label': 'Subtitles background', 'type': 'select', 'options': [{ 'label': 'None', 'value': '' }, { 'label': 'Solid', 'value': 'solid' }, { 'label': 'Transparent', 'value': 'transparent' }], 'id': 'subtitles_background' }, + { 'section': 'Player', 'header': 'Subtitles color', 'label': 'Subtitles color', 'type': 'color', 'color': '#FFFFFF', 'id': 'subtitles_color' }, + { 'section': 'Player', 'header': 'Subtitles outline color', 'label': 'Subtitles outline color', 'type': 'color', 'color': '#000000', 'id': 'subtitles_outline_color' }, + { 'section': 'Player', 'label': 'Auto-play next episode', 'type': 'checkbox', 'id': 'auto-play_next_episode' }, + { 'section': 'Player', 'label': 'Pause playback when minimized', 'type': 'checkbox', 'id': 'pause_playback_when_minimized' }, + { 'section': 'Player', 'label': 'Hardware-accelerated decoding', 'type': 'checkbox', 'id': 'hardware-accelerated_decoding' }, + { 'section': 'Player', 'label': 'Launch player in a separate window (advanced)', 'type': 'checkbox', 'id': 'launch_player_in_a_separate_window_(advanced)' }, + ], + 'Streaming': [ + { 'section': 'Streaming', 'header': 'Caching', 'label': 'Caching', 'type': 'select', 'options': [{ 'label': 'No Caching', 'value': '' }, { 'label': '2GB', 'value': '2048' }, { 'label': '5GB', 'value': '5120' }, { 'label': '10GB', 'value': '10240' }], 'id': 'caching' }, + { 'section': 'Streaming', 'header': 'Torrent Profile', 'label': 'Torrent Profile', 'type': 'select', 'options': [{ 'label': 'Default', 'value': 'profile-default' }, { 'label': 'Soft', 'value': 'profile-soft' }, { 'label': 'Fast', 'value': 'profile-fast' }], 'id': 'torrent_profile' }, + { 'section': 'Streaming', 'header': 'Streaming server URL: http://127.0.0.1:11470', 'label': 'Streaming server is available.', 'type': 'static-text', 'icon': 'ic_check', 'id': 'streaming_server_is_available.' } + ] }; const useSettings = { - SECTIONS_ORDER, settingsSections + settingsSections } module.exports = useSettings; \ No newline at end of file From 59bdd5b416050b340b47c587db9e37d12afcd7b9 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 11:27:09 +0300 Subject: [PATCH 33/54] Removed unnecessary properties --- src/routes/Settings/constants.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/routes/Settings/constants.js b/src/routes/Settings/constants.js index 65c3cf6b6..21f2843a4 100644 --- a/src/routes/Settings/constants.js +++ b/src/routes/Settings/constants.js @@ -1,24 +1,24 @@ const settingsSections = { 'General': [ - { 'section': 'General', 'type': 'user' }, - { 'section': 'General', 'header': 'UI Language', 'label': 'UI Language', 'type': 'select', 'options': [{ 'label': 'Български език', 'value': 'bul' }, { 'label': 'English', 'value': 'eng' }, { 'label': 'Deutsch', 'value': 'ger' }, { 'label': 'Español', 'value': 'esp' }, { 'label': 'Italiano', 'value': 'ita' }], 'id': 'ui_language' }, + { 'type': 'user' }, + { 'header': 'UI Language', 'label': 'UI Language', 'type': 'select', 'options': [{ 'label': 'Български език', 'value': 'bul' }, { 'label': 'English', 'value': 'eng' }, { 'label': 'Deutsch', 'value': 'ger' }, { 'label': 'Español', 'value': 'esp' }, { 'label': 'Italiano', 'value': 'ita' }], 'id': 'ui_language' }, ], 'Player': [ - { 'section': 'Player', 'label': 'ADD-ONS', 'type': 'button', 'icon': 'ic_addons', 'id': 'add-ons', 'href': '#/addons' }, - { 'section': 'Player', 'header': 'Default Subtitles Language', 'label': 'Default Subtitles Language', 'type': 'select', 'options': [{ 'label': 'Български език', 'value': 'bul' }, { 'label': 'English', 'value': 'eng' }, { 'label': 'Deutsch', 'value': 'ger' }, { 'label': 'Español', 'value': 'esp' }, { 'label': 'Italiano', 'value': 'ita' }], 'id': 'default_subtitles_language' }, - { 'section': 'Player', 'header': 'Default Subtitles Size', 'label': 'Default Subtitles Size', 'type': 'select', 'options': [{ 'label': '72%', 'value': '72%' }, { 'label': '80%', 'value': '80%' }, { 'label': '100%', 'value': '100%' }, { 'label': '120%', 'value': '120%' }, { 'label': '140%', 'value': '140%' }, { 'label': '160%', 'value': '160%' }, { 'label': '180%', 'value': '180%' }], 'id': 'default_subtitles_size' }, - { 'section': 'Player', 'header': 'Subtitles Background', 'label': 'Subtitles background', 'type': 'select', 'options': [{ 'label': 'None', 'value': '' }, { 'label': 'Solid', 'value': 'solid' }, { 'label': 'Transparent', 'value': 'transparent' }], 'id': 'subtitles_background' }, - { 'section': 'Player', 'header': 'Subtitles color', 'label': 'Subtitles color', 'type': 'color', 'color': '#FFFFFF', 'id': 'subtitles_color' }, - { 'section': 'Player', 'header': 'Subtitles outline color', 'label': 'Subtitles outline color', 'type': 'color', 'color': '#000000', 'id': 'subtitles_outline_color' }, - { 'section': 'Player', 'label': 'Auto-play next episode', 'type': 'checkbox', 'id': 'auto-play_next_episode' }, - { 'section': 'Player', 'label': 'Pause playback when minimized', 'type': 'checkbox', 'id': 'pause_playback_when_minimized' }, - { 'section': 'Player', 'label': 'Hardware-accelerated decoding', 'type': 'checkbox', 'id': 'hardware-accelerated_decoding' }, - { 'section': 'Player', 'label': 'Launch player in a separate window (advanced)', 'type': 'checkbox', 'id': 'launch_player_in_a_separate_window_(advanced)' }, + { 'label': 'ADD-ONS', 'type': 'button', 'icon': 'ic_addons', 'id': 'add-ons', 'href': '#/addons' }, + { 'header': 'Default Subtitles Language', 'label': 'Default Subtitles Language', 'type': 'select', 'options': [{ 'label': 'Български език', 'value': 'bul' }, { 'label': 'English', 'value': 'eng' }, { 'label': 'Deutsch', 'value': 'ger' }, { 'label': 'Español', 'value': 'esp' }, { 'label': 'Italiano', 'value': 'ita' }], 'id': 'default_subtitles_language' }, + { 'header': 'Default Subtitles Size', 'label': 'Default Subtitles Size', 'type': 'select', 'options': [{ 'label': '72%', 'value': '72%' }, { 'label': '80%', 'value': '80%' }, { 'label': '100%', 'value': '100%' }, { 'label': '120%', 'value': '120%' }, { 'label': '140%', 'value': '140%' }, { 'label': '160%', 'value': '160%' }, { 'label': '180%', 'value': '180%' }], 'id': 'default_subtitles_size' }, + { 'header': 'Subtitles Background', 'label': 'Subtitles background', 'type': 'select', 'options': [{ 'label': 'None', 'value': '' }, { 'label': 'Solid', 'value': 'solid' }, { 'label': 'Transparent', 'value': 'transparent' }], 'id': 'subtitles_background' }, + { 'header': 'Subtitles color', 'label': 'Subtitles color', 'type': 'color', 'color': '#FFFFFF', 'id': 'subtitles_color' }, + { 'header': 'Subtitles outline color', 'label': 'Subtitles outline color', 'type': 'color', 'color': '#000000', 'id': 'subtitles_outline_color' }, + { 'label': 'Auto-play next episode', 'type': 'checkbox', 'id': 'auto-play_next_episode' }, + { 'label': 'Pause playback when minimized', 'type': 'checkbox', 'id': 'pause_playback_when_minimized' }, + { 'label': 'Hardware-accelerated decoding', 'type': 'checkbox', 'id': 'hardware-accelerated_decoding' }, + { 'label': 'Launch player in a separate window (advanced)', 'type': 'checkbox', 'id': 'launch_player_in_a_separate_window_(advanced)' }, ], 'Streaming': [ - { 'section': 'Streaming', 'header': 'Caching', 'label': 'Caching', 'type': 'select', 'options': [{ 'label': 'No Caching', 'value': '' }, { 'label': '2GB', 'value': '2048' }, { 'label': '5GB', 'value': '5120' }, { 'label': '10GB', 'value': '10240' }], 'id': 'caching' }, - { 'section': 'Streaming', 'header': 'Torrent Profile', 'label': 'Torrent Profile', 'type': 'select', 'options': [{ 'label': 'Default', 'value': 'profile-default' }, { 'label': 'Soft', 'value': 'profile-soft' }, { 'label': 'Fast', 'value': 'profile-fast' }], 'id': 'torrent_profile' }, - { 'section': 'Streaming', 'header': 'Streaming server URL: http://127.0.0.1:11470', 'label': 'Streaming server is available.', 'type': 'static-text', 'icon': 'ic_check', 'id': 'streaming_server_is_available.' } + { 'header': 'Caching', 'label': 'Caching', 'type': 'select', 'options': [{ 'label': 'No Caching', 'value': '' }, { 'label': '2GB', 'value': '2048' }, { 'label': '5GB', 'value': '5120' }, { 'label': '10GB', 'value': '10240' }], 'id': 'caching' }, + { 'header': 'Torrent Profile', 'label': 'Torrent Profile', 'type': 'select', 'options': [{ 'label': 'Default', 'value': 'profile-default' }, { 'label': 'Soft', 'value': 'profile-soft' }, { 'label': 'Fast', 'value': 'profile-fast' }], 'id': 'torrent_profile' }, + { 'header': 'Streaming server URL: http://127.0.0.1:11470', 'label': 'Streaming server is available.', 'type': 'static-text', 'icon': 'ic_check', 'id': 'streaming_server_is_available.' } ] }; From d7e221b5859692837f4d748d9f87f14fc811c131 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 12:04:03 +0300 Subject: [PATCH 34/54] Better checkbox styles --- src/routes/Settings/styles.less | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/routes/Settings/styles.less b/src/routes/Settings/styles.less index 9c90f4b6c..7cde2ab69 100644 --- a/src/routes/Settings/styles.less +++ b/src/routes/Settings/styles.less @@ -167,10 +167,6 @@ &.checkbox-container { .checkbox { - --icon-size: 1.2rem; - --icon-color: var(--color-surface); - --icon-background-color: transparent; - display: flex; flex-direction: row; align-items: center; @@ -182,13 +178,10 @@ color: var(--color-surfacelight); } - &:global(.checked) { - --icon-color: var(--color-surfacelight); - --icon-background-color: var(--color-primary); - } - &:focus, &:hover { - --icon-color: var(--color-surfacelighter); + .checkbox-icon { + fill: var(--color-surfacelighter); + } .label { color: var(--color-surfacelighter); From a1f77f031b03268baef25d40da7d5092ede0e9f1 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 12:04:44 +0300 Subject: [PATCH 35/54] Removed useless variable declaration --- src/routes/Settings/constants.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/routes/Settings/constants.js b/src/routes/Settings/constants.js index 21f2843a4..2c1a25ea7 100644 --- a/src/routes/Settings/constants.js +++ b/src/routes/Settings/constants.js @@ -22,8 +22,6 @@ const settingsSections = { ] }; -const useSettings = { - settingsSections -} - -module.exports = useSettings; \ No newline at end of file +module.exports = { + settingsSections, +}; From ad5acaed8c42b740604ece5809914762fd1829a4 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 12:24:07 +0300 Subject: [PATCH 36/54] Improved ColorInput --- src/routes/Settings/ColorInput/ColorInput.js | 49 ++++++++++--------- .../Settings/SectionsList/SectionsList.js | 3 +- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/routes/Settings/ColorInput/ColorInput.js b/src/routes/Settings/ColorInput/ColorInput.js index 50bbfcc85..a7d5f4b29 100644 --- a/src/routes/Settings/ColorInput/ColorInput.js +++ b/src/routes/Settings/ColorInput/ColorInput.js @@ -8,36 +8,42 @@ const useBinaryState = require('stremio/common/useBinaryState'); const Icon = require('stremio-icons/dom/Icon'); const styles = require('./styles'); -const ColorInput = ({ className, defaultValue, onChange }) => { - const colorInputRef = React.useRef(null); +const ColorInput = ({ className, value, onChange }) => { const [colorInputVisible, showColorInput, closeColorInput] = useBinaryState(false); - const [color, setColor] = React.useState(defaultValue); - const [selectedColor, setSelectedColor] = React.useState(defaultValue); - const discardColorInput = React.useCallback((event) => { - if (event.type === "mousedown" && colorInputRef.current.contains(event.target)) { - return; - } - setColor(selectedColor); - closeColorInput(); - }); + const [selectedColor, setSelectedColor] = React.useState(value); + const confirmColorInput = React.useCallback(() => { - setSelectedColor(color); - onChange(color); + onChange(selectedColor); closeColorInput(); - }, [color, onChange]); + }, [selectedColor, onChange]); + + React.useEffect(() => { + setSelectedColor(value); + }, [value, colorInputVisible]); + + const modalBackgroundOnClick = React.useCallback((event) => { + if (!event.nativeEvent.preventClose) { + closeColorInput(); + } + }, []); + + const modalContentOnClick = React.useCallback((event) => { + event.nativeEvent.preventClose = true; + }, []); + return ( - + { colorInputVisible ? - -
-

Choose a color:

- +
@@ -48,11 +54,10 @@ const ColorInput = ({ className, defaultValue, onChange }) => { ); }; - ColorInput.propTypes = { className: PropTypes.string, - defaultValue: PropTypes.string, + value: PropTypes.string, onChange: PropTypes.func }; -module.exports = ColorInput; \ No newline at end of file +module.exports = ColorInput; diff --git a/src/routes/Settings/SectionsList/SectionsList.js b/src/routes/Settings/SectionsList/SectionsList.js index a1a0895ce..cdb507e5a 100644 --- a/src/routes/Settings/SectionsList/SectionsList.js +++ b/src/routes/Settings/SectionsList/SectionsList.js @@ -152,8 +152,7 @@ const SectionsList = React.forwardRef(({ className, sections, preferences, onPre return (
{input.header ?
{input.header}
: null} - - {/* */} +
); } From a62690159544739328e19d7f14242ea8d792f450 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 12:27:29 +0300 Subject: [PATCH 37/54] ColorInput moved to common --- src/{routes/Settings => common}/ColorInput/ColorInput.js | 0 src/{routes/Settings => common}/ColorInput/index.js | 0 src/{routes/Settings => common}/ColorInput/styles.less | 0 src/common/index.js | 2 ++ src/routes/Settings/SectionsList/SectionsList.js | 3 +-- 5 files changed, 3 insertions(+), 2 deletions(-) rename src/{routes/Settings => common}/ColorInput/ColorInput.js (100%) rename src/{routes/Settings => common}/ColorInput/index.js (100%) rename src/{routes/Settings => common}/ColorInput/styles.less (100%) diff --git a/src/routes/Settings/ColorInput/ColorInput.js b/src/common/ColorInput/ColorInput.js similarity index 100% rename from src/routes/Settings/ColorInput/ColorInput.js rename to src/common/ColorInput/ColorInput.js diff --git a/src/routes/Settings/ColorInput/index.js b/src/common/ColorInput/index.js similarity index 100% rename from src/routes/Settings/ColorInput/index.js rename to src/common/ColorInput/index.js diff --git a/src/routes/Settings/ColorInput/styles.less b/src/common/ColorInput/styles.less similarity index 100% rename from src/routes/Settings/ColorInput/styles.less rename to src/common/ColorInput/styles.less diff --git a/src/common/index.js b/src/common/index.js index 580b6db4a..5771413e0 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -1,6 +1,7 @@ const Button = require('./Button'); const Checkbox = require('./Checkbox'); const ColorPicker = require('./ColorPicker'); +const ColorInput = require('./ColorInput'); const Dropdown = require('./Dropdown'); const MainNavBar = require('./MainNavBar'); const MetaItem = require('./MetaItem'); @@ -25,6 +26,7 @@ module.exports = { Button, Checkbox, ColorPicker, + ColorInput, Dropdown, MainNavBar, MetaItem, diff --git a/src/routes/Settings/SectionsList/SectionsList.js b/src/routes/Settings/SectionsList/SectionsList.js index cdb507e5a..48b940f40 100644 --- a/src/routes/Settings/SectionsList/SectionsList.js +++ b/src/routes/Settings/SectionsList/SectionsList.js @@ -1,7 +1,6 @@ const React = require('react'); -const { Button, Dropdown, Checkbox } = require('stremio/common'); +const { Button, Dropdown, Checkbox, ColorInput } = require('stremio/common'); const Icon = require('stremio-icons/dom/Icon'); -const ColorInput = require('../ColorInput'); const classnames = require('classnames'); const styles = require('../styles'); From 683acc77efc74f91d6b121079fcfa122ddf37809 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 14:41:21 +0300 Subject: [PATCH 38/54] Wole number X icon size --- src/common/ColorInput/styles.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/ColorInput/styles.less b/src/common/ColorInput/styles.less index a230323b1..c52eaaba4 100644 --- a/src/common/ColorInput/styles.less +++ b/src/common/ColorInput/styles.less @@ -19,8 +19,8 @@ position: absolute; top: 1rem; right: 1rem; - width: .8rem; - height: .8rem; + width: 1rem; + height: 1rem; fill: var(--color-surfacedark); } From 0c90e2d490deed32afb8f30cc7a3e91a668ba8b5 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 14:41:34 +0300 Subject: [PATCH 39/54] Better handling of the close function --- src/common/ColorInput/ColorInput.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/common/ColorInput/ColorInput.js b/src/common/ColorInput/ColorInput.js index a7d5f4b29..d6a3726a0 100644 --- a/src/common/ColorInput/ColorInput.js +++ b/src/common/ColorInput/ColorInput.js @@ -22,23 +22,19 @@ const ColorInput = ({ className, value, onChange }) => { }, [value, colorInputVisible]); const modalBackgroundOnClick = React.useCallback((event) => { - if (!event.nativeEvent.preventClose) { + if(event.target === event.currentTarget) { closeColorInput(); } }, []); - const modalContentOnClick = React.useCallback((event) => { - event.nativeEvent.preventClose = true; - }, []); - return ( { colorInputVisible ? - -
+ +
From 523dda63a738fdc4670f8710aad0acbdb706fb22 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 15:12:45 +0300 Subject: [PATCH 40/54] Check if onChange is function --- src/common/ColorInput/ColorInput.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/common/ColorInput/ColorInput.js b/src/common/ColorInput/ColorInput.js index d6a3726a0..190f44b7f 100644 --- a/src/common/ColorInput/ColorInput.js +++ b/src/common/ColorInput/ColorInput.js @@ -13,7 +13,9 @@ const ColorInput = ({ className, value, onChange }) => { const [selectedColor, setSelectedColor] = React.useState(value); const confirmColorInput = React.useCallback(() => { - onChange(selectedColor); + if(typeof onChange === "function") { + onChange(selectedColor); + } closeColorInput(); }, [selectedColor, onChange]); From c87d1fb8aeb723da860b9ce001795e011d5a4672 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 15:13:26 +0300 Subject: [PATCH 41/54] Split styles per component --- .../Settings/SectionsList/SectionsList.js | 2 +- src/routes/Settings/SectionsList/styles.less | 181 +++++++++++++++ .../SectionsSelector/SectionsSelector.js | 2 +- .../Settings/SectionsSelector/styles.less | 26 +++ src/routes/Settings/styles.less | 206 ------------------ 5 files changed, 209 insertions(+), 208 deletions(-) create mode 100644 src/routes/Settings/SectionsList/styles.less create mode 100644 src/routes/Settings/SectionsSelector/styles.less diff --git a/src/routes/Settings/SectionsList/SectionsList.js b/src/routes/Settings/SectionsList/SectionsList.js index 48b940f40..8611e53c3 100644 --- a/src/routes/Settings/SectionsList/SectionsList.js +++ b/src/routes/Settings/SectionsList/SectionsList.js @@ -2,7 +2,7 @@ const React = require('react'); const { Button, Dropdown, Checkbox, ColorInput } = require('stremio/common'); const Icon = require('stremio-icons/dom/Icon'); const classnames = require('classnames'); -const styles = require('../styles'); +const styles = require('./styles'); const SectionsList = React.forwardRef(({ className, sections, preferences, onPreferenceChanged, onScroll }, ref) => { const scrollContainerRef = ref; diff --git a/src/routes/Settings/SectionsList/styles.less b/src/routes/Settings/SectionsList/styles.less new file mode 100644 index 000000000..d7b647535 --- /dev/null +++ b/src/routes/Settings/SectionsList/styles.less @@ -0,0 +1,181 @@ +:import('~stremio/common/Checkbox/styles.less') { + checkbox-icon: icon; +} + +.section { + padding: 4rem 2rem; + + .section-header { + margin: 0 1.5rem 1.5rem 1.5rem; + font-size: 2rem; + color: var(--color-surfacelighter); + } + + .input-container { + margin: 1.5rem; + display: flex; + flex-direction: column; + + .input-header { + margin-bottom: 0.5rem; + color: var(--color-surfacelighter); + } + + .checkbox-icon { + flex: none; + width: 1.2rem; + height: 1.2rem; + fill: var(--color-surfacelight); + } + + &.user-container { + flex-direction: row; + align-items: center; + + .avatar { + margin: 0 1rem; + width: 4.2rem; + height: 4.2rem; + border-radius: 50%; + border: var(--focusable-border-size) solid var(--color-primary); + background-size: cover; + background-position: center; + background-repeat: no-repeat; + } + + .email { + color: var(--color-surfacelighter); + } + } + + &.select-container { + .dropdown { + height: 3rem; + width: var(--input-width); + } + } + + &.link-container { + margin: 1rem 1.5rem; + + .link { + display: block; + color: var(--color-secondarylight); + + &:focus { + color: var(--color-surface); + } + + &:hover { + color: var(--color-surfacelighter); + } + } + } + + &.button-container { + .button { + padding: 0.7rem; + width: var(--input-width); + min-height: calc(var(--input-width) * 0.09); + display: flex; + align-items: center; + justify-content: center; + background-color: var(--color-primary); + cursor: pointer; + + .icon { + width: 1.4rem; + height: 100%; + margin-right: 0.5rem; + fill: var(--color-surfacelighter); + } + + .label { + color: var(--color-surfacelighter); + } + + &:focus { + border-color: var(--color-surfacelighter); + } + + &:hover { + border-color: transparent; + background-color: var(--color-primarylight); + } + } + } + + &.checkbox-container { + .checkbox { + display: flex; + flex-direction: row; + align-items: center; + cursor: pointer; + + .label { + width: 100%; + margin-left: 0.5rem; + color: var(--color-surfacelight); + } + + &:focus, + &:hover { + .checkbox-icon { + fill: var(--color-surfacelighter); + } + + .label { + color: var(--color-surfacelighter); + } + } + } + } + + &.text-container { + .text { + display: flex; + flex-direction: row; + align-items: center; + + .icon { + margin-right: 0.5rem; + width: 1rem; + height: 1rem; + fill: var(--color-signal5); + } + + .x-icon { + margin-right: 0.5rem; + width: 1rem; + height: 1rem; + fill: var(--color-signal2); + } + + .label { + width: 100%; + color: var(--color-surfacelighter); + } + } + } + + &.color-container { + .color-picker { + width: var(--input-width); + height: calc(var(--input-width) * 0.08); + cursor: pointer; + + &:focus { + border-color: var(--color-surfacelighter); + } + + &:hover { + border-color: transparent; + } + } + } + } + + >:last-child { + margin-bottom: 0; + } +} \ No newline at end of file diff --git a/src/routes/Settings/SectionsSelector/SectionsSelector.js b/src/routes/Settings/SectionsSelector/SectionsSelector.js index 0cfcce343..a1f08dd12 100644 --- a/src/routes/Settings/SectionsSelector/SectionsSelector.js +++ b/src/routes/Settings/SectionsSelector/SectionsSelector.js @@ -2,7 +2,7 @@ const React = require('react'); const PropTypes = require('prop-types'); const { Button } = require('stremio/common'); const classnames = require('classnames'); -const styles = require('../styles'); +const styles = require('./styles'); const SectionsSelector = ({ className, sections, selectedSectionId, onSelectedSection }) => { return ( diff --git a/src/routes/Settings/SectionsSelector/styles.less b/src/routes/Settings/SectionsSelector/styles.less new file mode 100644 index 000000000..f4a15c2a8 --- /dev/null +++ b/src/routes/Settings/SectionsSelector/styles.less @@ -0,0 +1,26 @@ +.section-label { + padding: 1rem; + font-size: 1.1rem; + border: calc(var(--focusable-border-size) * 0.5) solid transparent; + color: var(--color-surfacelight); + cursor: pointer; + + &.selected { + color: var(--color-surfacelighter); + background-color: var(--color-background); + + &:hover { + background-color: var(--color-background); + } + } + + &:focus { + border-color: var(--color-surfacelighter); + } + + &:hover { + color: var(--color-surfacelighter); + background-color: var(--color-surface20); + border-color: transparent; + } +} \ No newline at end of file diff --git a/src/routes/Settings/styles.less b/src/routes/Settings/styles.less index 7cde2ab69..df5281628 100644 --- a/src/routes/Settings/styles.less +++ b/src/routes/Settings/styles.less @@ -1,7 +1,3 @@ -:import('~stremio/common/Checkbox/styles.less') { - checkbox-icon: icon; -} - .settings-parent-container { display: flex; flex-direction: column; @@ -29,32 +25,7 @@ flex-direction: column; background-color: var(--color-backgroundlighter); - .section-label { - padding: 1rem; - font-size: 1.1rem; - border: calc(var(--focusable-border-size) * 0.5) solid transparent; - color: var(--color-surfacelight); - cursor: pointer; - &.selected { - color: var(--color-surfacelighter); - background-color: var(--color-background); - - &:hover { - background-color: var(--color-background); - } - } - - &:focus { - border-color: var(--color-surfacelighter); - } - - &:hover { - color: var(--color-surfacelighter); - background-color: var(--color-surface20); - border-color: transparent; - } - } } .scroll-container { @@ -62,183 +33,6 @@ flex: 1; overflow-y: auto; - .section { - padding: 4rem 2rem; - - .section-header { - margin: 0 1.5rem 1.5rem 1.5rem; - font-size: 2rem; - color: var(--color-surfacelighter); - } - - .input-container { - margin: 1.5rem; - display: flex; - flex-direction: column; - - .input-header { - margin-bottom: 0.5rem; - color: var(--color-surfacelighter); - } - - .checkbox-icon { - flex: none; - width: 1.2rem; - height: 1.2rem; - fill: var(--color-surfacelight); - } - - &.user-container { - flex-direction: row; - align-items: center; - - .avatar { - margin: 0 1rem; - width: 4.2rem; - height: 4.2rem; - border-radius: 50%; - border: var(--focusable-border-size) solid var(--color-primary); - background-size: cover; - background-position: center; - background-repeat: no-repeat; - } - - .email { - color: var(--color-surfacelighter); - } - } - - &.select-container { - .dropdown { - height: 3rem; - width: var(--input-width); - } - } - - &.link-container { - margin: 1rem 1.5rem; - - .link { - display: block; - color: var(--color-secondarylight); - - &:focus { - color: var(--color-surface); - } - - &:hover { - color: var(--color-surfacelighter); - } - } - } - - &.button-container { - .button { - padding: 0.7rem; - width: var(--input-width); - min-height: calc(var(--input-width) * 0.09); - display: flex; - align-items: center; - justify-content: center; - background-color: var(--color-primary); - cursor: pointer; - - .icon { - width: 1.4rem; - height: 100%; - margin-right: 0.5rem; - fill: var(--color-surfacelighter); - } - - .label { - color: var(--color-surfacelighter); - } - - &:focus { - border-color: var(--color-surfacelighter); - } - - &:hover { - border-color: transparent; - background-color: var(--color-primarylight); - } - } - } - - &.checkbox-container { - .checkbox { - display: flex; - flex-direction: row; - align-items: center; - cursor: pointer; - - .label { - width: 100%; - margin-left: 0.5rem; - color: var(--color-surfacelight); - } - - &:focus, &:hover { - .checkbox-icon { - fill: var(--color-surfacelighter); - } - - .label { - color: var(--color-surfacelighter); - } - } - } - } - - &.text-container { - .text { - display: flex; - flex-direction: row; - align-items: center; - - .icon { - margin-right: 0.5rem; - width: 1rem; - height: 1rem; - fill: var(--color-signal5); - } - - .x-icon { - margin-right: 0.5rem; - width: 1rem; - height: 1rem; - fill: var(--color-signal2); - } - - .label { - width: 100%; - color: var(--color-surfacelighter); - } - } - } - - &.color-container { - .color-picker { - width: var(--input-width); - height: calc(var(--input-width) * 0.08); - cursor: pointer; - - &:focus { - border-color: var(--color-surfacelighter); - } - - &:hover { - border-color: transparent; - } - } - } - } - - >:last-child { - margin-bottom: 0; - } - } - >:not(:last-child) { border-bottom: calc(var(--focusable-border-size) * 0.5) solid var(--color-primary); } From 15d325e571e67325d075a8234404c7ede8849cce Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 15:15:09 +0300 Subject: [PATCH 42/54] Dropped a local alias for ref --- src/routes/Settings/SectionsList/SectionsList.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/routes/Settings/SectionsList/SectionsList.js b/src/routes/Settings/SectionsList/SectionsList.js index 8611e53c3..e09981711 100644 --- a/src/routes/Settings/SectionsList/SectionsList.js +++ b/src/routes/Settings/SectionsList/SectionsList.js @@ -5,7 +5,6 @@ const classnames = require('classnames'); const styles = require('./styles'); const SectionsList = React.forwardRef(({ className, sections, preferences, onPreferenceChanged, onScroll }, ref) => { - const scrollContainerRef = ref; const toggleCheckbox = (id) => { onPreferenceChanged(id, !preferences[id]); }; @@ -160,7 +159,7 @@ const SectionsList = React.forwardRef(({ className, sections, preferences, onPre ); return ( -
+
{sectionsElements}
); From 6c07ebe9923fc03ef11974a48633bf97daff394e Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 15:18:11 +0300 Subject: [PATCH 43/54] Cache callback functions --- src/routes/Settings/SectionsList/SectionsList.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/routes/Settings/SectionsList/SectionsList.js b/src/routes/Settings/SectionsList/SectionsList.js index e09981711..3ffd6a3d8 100644 --- a/src/routes/Settings/SectionsList/SectionsList.js +++ b/src/routes/Settings/SectionsList/SectionsList.js @@ -13,18 +13,18 @@ const SectionsList = React.forwardRef(({ className, sections, preferences, onPre onPreferenceChanged(id, color); }; - const updateDropdown = (event) => { + const updateDropdown = React.useCallback((event) => { var data = event.currentTarget.dataset; onPreferenceChanged(data.name, data.value); - }; + }); - const checkUser = (event) => { + const checkUser = React.useCallback((event) => { if(! preferences.user) { // Here in Stremio 4 we show a toast with a message, asking the anonymous user to log in/register console.log('No user found'); event.preventDefault(); } - } + }); // Determines whether the link should be opened in new window or in the current one. const getTargetFor = url => ['//', 'http://', 'https://', 'file://', 'ftp://', 'mailto:', 'magnet:'] From 1ff6c298fe5705533f697a3df3d18a76a8352a51 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 15:22:45 +0300 Subject: [PATCH 44/54] PropTypes --- src/routes/Settings/SectionsList/SectionsList.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/routes/Settings/SectionsList/SectionsList.js b/src/routes/Settings/SectionsList/SectionsList.js index 3ffd6a3d8..5732f34f7 100644 --- a/src/routes/Settings/SectionsList/SectionsList.js +++ b/src/routes/Settings/SectionsList/SectionsList.js @@ -1,4 +1,5 @@ const React = require('react'); +const PropTypes = require('prop-types'); const { Button, Dropdown, Checkbox, ColorInput } = require('stremio/common'); const Icon = require('stremio-icons/dom/Icon'); const classnames = require('classnames'); @@ -165,4 +166,14 @@ const SectionsList = React.forwardRef(({ className, sections, preferences, onPre ); }); +SectionsList.propTypes = { + className: PropTypes.string, + sections: PropTypes.arrayOf(PropTypes.shape({ + id: PropTypes.string.isRequired, + })), + preferences: PropTypes.object, + onPreferenceChanged: PropTypes.func.isRequired, + onScroll: PropTypes.func.isRequired, +}; + module.exports = SectionsList; \ No newline at end of file From 892c6310ed87446efa261fe86a4f5d5cecb211b0 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 15:41:12 +0300 Subject: [PATCH 45/54] SectionsList PropTypes --- src/routes/Settings/SectionsList/SectionsList.js | 15 +++++++++++++++ src/routes/Settings/constants.js | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/routes/Settings/SectionsList/SectionsList.js b/src/routes/Settings/SectionsList/SectionsList.js index 5732f34f7..bfc6e411c 100644 --- a/src/routes/Settings/SectionsList/SectionsList.js +++ b/src/routes/Settings/SectionsList/SectionsList.js @@ -170,6 +170,21 @@ SectionsList.propTypes = { className: PropTypes.string, sections: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.string.isRequired, + ref: PropTypes.shape({ + current: PropTypes.object, + }).isRequired, + inputs: PropTypes.arrayOf(PropTypes.shape({ + type: PropTypes.string.isRequired, + id: PropTypes.string, + header: PropTypes.string, + label: PropTypes.string, + icon: PropTypes.string, + href: PropTypes.string, + options: PropTypes.arrayOf(PropTypes.shape({ + label: PropTypes.string.isRequired, + value: PropTypes.string.isRequired, + })), + })), })), preferences: PropTypes.object, onPreferenceChanged: PropTypes.func.isRequired, diff --git a/src/routes/Settings/constants.js b/src/routes/Settings/constants.js index 2c1a25ea7..563bbd8c2 100644 --- a/src/routes/Settings/constants.js +++ b/src/routes/Settings/constants.js @@ -8,8 +8,8 @@ const settingsSections = { { 'header': 'Default Subtitles Language', 'label': 'Default Subtitles Language', 'type': 'select', 'options': [{ 'label': 'Български език', 'value': 'bul' }, { 'label': 'English', 'value': 'eng' }, { 'label': 'Deutsch', 'value': 'ger' }, { 'label': 'Español', 'value': 'esp' }, { 'label': 'Italiano', 'value': 'ita' }], 'id': 'default_subtitles_language' }, { 'header': 'Default Subtitles Size', 'label': 'Default Subtitles Size', 'type': 'select', 'options': [{ 'label': '72%', 'value': '72%' }, { 'label': '80%', 'value': '80%' }, { 'label': '100%', 'value': '100%' }, { 'label': '120%', 'value': '120%' }, { 'label': '140%', 'value': '140%' }, { 'label': '160%', 'value': '160%' }, { 'label': '180%', 'value': '180%' }], 'id': 'default_subtitles_size' }, { 'header': 'Subtitles Background', 'label': 'Subtitles background', 'type': 'select', 'options': [{ 'label': 'None', 'value': '' }, { 'label': 'Solid', 'value': 'solid' }, { 'label': 'Transparent', 'value': 'transparent' }], 'id': 'subtitles_background' }, - { 'header': 'Subtitles color', 'label': 'Subtitles color', 'type': 'color', 'color': '#FFFFFF', 'id': 'subtitles_color' }, - { 'header': 'Subtitles outline color', 'label': 'Subtitles outline color', 'type': 'color', 'color': '#000000', 'id': 'subtitles_outline_color' }, + { 'header': 'Subtitles color', 'label': 'Subtitles color', 'type': 'color', 'id': 'subtitles_color' }, + { 'header': 'Subtitles outline color', 'label': 'Subtitles outline color', 'type': 'color', 'id': 'subtitles_outline_color' }, { 'label': 'Auto-play next episode', 'type': 'checkbox', 'id': 'auto-play_next_episode' }, { 'label': 'Pause playback when minimized', 'type': 'checkbox', 'id': 'pause_playback_when_minimized' }, { 'label': 'Hardware-accelerated decoding', 'type': 'checkbox', 'id': 'hardware-accelerated_decoding' }, From 27b242423aebdebb8ec417b9a642175cf489dd58 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 15:46:08 +0300 Subject: [PATCH 46/54] Removed classnames --- src/common/ColorInput/ColorInput.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/common/ColorInput/ColorInput.js b/src/common/ColorInput/ColorInput.js index 190f44b7f..0cd33f33a 100644 --- a/src/common/ColorInput/ColorInput.js +++ b/src/common/ColorInput/ColorInput.js @@ -1,5 +1,4 @@ const React = require('react'); -const classnames = require('classnames'); const PropTypes = require('prop-types'); const { Modal } = require('stremio-router'); const Button = require('stremio/common/Button'); @@ -38,11 +37,11 @@ const ColorInput = ({ className, value, onChange }) => {

Choose a color:

- - + +
: From b252ca0b134b7a6bbf9150fd92f03e052084148f Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 15:48:23 +0300 Subject: [PATCH 47/54] Cache callback function --- src/routes/Settings/Settings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 0dc87d3bd..48968b876 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -47,7 +47,7 @@ const Settings = () => { setPreferences(newPrefs); } - const changeSection = (event) => { + const changeSection = React.useCallback((event) => { const currentSectionId = event.currentTarget.dataset.section; const section = sections.find((section) => section.id === currentSectionId); //setSelectedSectionId(currentSectionId); @@ -55,7 +55,7 @@ const Settings = () => { top: section.ref.current.offsetTop, behavior: 'smooth' }); - }; + }); const updateSection = React.useCallback((event) => { const scrollContainer = event.currentTarget; if (scrollContainer.scrollTop + scrollContainer.clientHeight === scrollContainer.scrollHeight) { From f5aa635028300b6cba851d1e8b668f9c79fd304f Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 15:51:31 +0300 Subject: [PATCH 48/54] Renamed the updateSection function to sectionListOnScorll --- src/routes/Settings/Settings.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 48968b876..87959c6f4 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -56,7 +56,7 @@ const Settings = () => { behavior: 'smooth' }); }); - const updateSection = React.useCallback((event) => { + const sectionListOnScorll = React.useCallback((event) => { const scrollContainer = event.currentTarget; if (scrollContainer.scrollTop + scrollContainer.clientHeight === scrollContainer.scrollHeight) { setSelectedSectionId(sections[sections.length - 1].id); @@ -80,7 +80,7 @@ const Settings = () => { navMenu={true} />
- +
); From c9f2b735f3ad5d55d80697f4c27654c1f48d565e Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 16:19:57 +0300 Subject: [PATCH 49/54] Do not use bond function --- src/common/ColorInput/ColorInput.js | 12 +++++++----- src/routes/Settings/SectionsList/SectionsList.js | 12 +++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/common/ColorInput/ColorInput.js b/src/common/ColorInput/ColorInput.js index 0cd33f33a..ee5a37a10 100644 --- a/src/common/ColorInput/ColorInput.js +++ b/src/common/ColorInput/ColorInput.js @@ -4,16 +4,17 @@ const { Modal } = require('stremio-router'); const Button = require('stremio/common/Button'); const ColorPicker = require('stremio/common/ColorPicker'); const useBinaryState = require('stremio/common/useBinaryState'); -const Icon = require('stremio-icons/dom/Icon'); +const Icon = require('stremio-icons/dom'); const styles = require('./styles'); -const ColorInput = ({ className, value, onChange }) => { +const ColorInput = ({ className, id, value, onChange }) => { const [colorInputVisible, showColorInput, closeColorInput] = useBinaryState(false); const [selectedColor, setSelectedColor] = React.useState(value); - const confirmColorInput = React.useCallback(() => { + const confirmColorInput = React.useCallback((event) => { if(typeof onChange === "function") { - onChange(selectedColor); + event.nativeEvent.value = selectedColor; + onChange(event); } closeColorInput(); }, [selectedColor, onChange]); @@ -41,7 +42,7 @@ const ColorInput = ({ className, value, onChange }) => {

Choose a color:

- +
: @@ -53,6 +54,7 @@ const ColorInput = ({ className, value, onChange }) => { ColorInput.propTypes = { className: PropTypes.string, + id: PropTypes.string.isRequired, value: PropTypes.string, onChange: PropTypes.func }; diff --git a/src/routes/Settings/SectionsList/SectionsList.js b/src/routes/Settings/SectionsList/SectionsList.js index bfc6e411c..f6fff49f8 100644 --- a/src/routes/Settings/SectionsList/SectionsList.js +++ b/src/routes/Settings/SectionsList/SectionsList.js @@ -10,14 +10,16 @@ const SectionsList = React.forwardRef(({ className, sections, preferences, onPre onPreferenceChanged(id, !preferences[id]); }; - const colorChanged = (id, color) => { + const colorChanged = React.useCallback((event) => { + const id = event.currentTarget.dataset.id; + const color = event.nativeEvent.value; onPreferenceChanged(id, color); - }; + }, [onPreferenceChanged]); const updateDropdown = React.useCallback((event) => { var data = event.currentTarget.dataset; onPreferenceChanged(data.name, data.value); - }); + }, [onPreferenceChanged]); const checkUser = React.useCallback((event) => { if(! preferences.user) { @@ -25,7 +27,7 @@ const SectionsList = React.forwardRef(({ className, sections, preferences, onPre console.log('No user found'); event.preventDefault(); } - }); + }, []); // Determines whether the link should be opened in new window or in the current one. const getTargetFor = url => ['//', 'http://', 'https://', 'file://', 'ftp://', 'mailto:', 'magnet:'] @@ -151,7 +153,7 @@ const SectionsList = React.forwardRef(({ className, sections, preferences, onPre return (
{input.header ?
{input.header}
: null} - +
); } From f4845eb61929a6ab3e3c81c6c19572388f51e9ee Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 16:20:14 +0300 Subject: [PATCH 50/54] Callback dependencies --- src/routes/Settings/Settings.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 87959c6f4..d970bb180 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -55,7 +55,8 @@ const Settings = () => { top: section.ref.current.offsetTop, behavior: 'smooth' }); - }); + }, [sections]); + const sectionListOnScorll = React.useCallback((event) => { const scrollContainer = event.currentTarget; if (scrollContainer.scrollTop + scrollContainer.clientHeight === scrollContainer.scrollHeight) { @@ -68,7 +69,7 @@ const Settings = () => { } } } - }); + }, [sections]); return (
From 90568c9e858cd6819920eff653b029ebecb978f8 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 16:23:02 +0300 Subject: [PATCH 51/54] Icon require --- src/routes/Settings/SectionsList/SectionsList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/Settings/SectionsList/SectionsList.js b/src/routes/Settings/SectionsList/SectionsList.js index f6fff49f8..c835738b2 100644 --- a/src/routes/Settings/SectionsList/SectionsList.js +++ b/src/routes/Settings/SectionsList/SectionsList.js @@ -1,7 +1,7 @@ const React = require('react'); const PropTypes = require('prop-types'); const { Button, Dropdown, Checkbox, ColorInput } = require('stremio/common'); -const Icon = require('stremio-icons/dom/Icon'); +const Icon = require('stremio-icons/dom'); const classnames = require('classnames'); const styles = require('./styles'); From 3dac8f96f3732b5dbfc7467891e03cc59b4e0d4e Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 16:37:38 +0300 Subject: [PATCH 52/54] useSettings hook --- src/routes/Settings/Settings.js | 28 +++------------------------- src/routes/Settings/useSettings.js | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 25 deletions(-) create mode 100644 src/routes/Settings/useSettings.js diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index d970bb180..854e6a8ec 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -4,30 +4,10 @@ const styles = require('./styles'); const SectionsSelector = require('./SectionsSelector'); const SectionsList = require('./SectionsList'); const { settingsSections } = require('./constants'); +const settingsValues = require('./useSettings'); const devTestWithUser = true; -const settingsValues = { - "user": devTestWithUser ? { - "_id": "neo", - "email": "neo@example.com", - "avatar": "https://www.thenational.ae/image/policy:1.891803:1566372420/AC17-Matrix-20-04.jpg?f=16x9&w=1200&$p$f$w=5867e40", - } : null, - "ui_language": "eng", - "default_subtitles_language": "bul", - "default_subtitles_size": "100%", - "subtitles_background": "", - "subtitles_color": "#ffffff", - "subtitles_outline_color": "#000", - "auto-play_next_episode": true, - "pause_playback_when_minimized": false, - "hardware-accelerated_decoding": true, - "launch_player_in_a_separate_window_(advanced)": true, - "caching": "2048", - "torrent_profile": "profile-default", - "streaming_server_is_available.": true, -}; - const Settings = () => { const sections = Object.keys(settingsSections) .map((section) => ({ @@ -36,15 +16,13 @@ const Settings = () => { ref: React.useRef(null) })); const [selectedSectionId, setSelectedSectionId] = React.useState(sections[0].id); - const [preferences, setPreferences] = React.useState(settingsValues); + const [preferences, setPreferences] = settingsValues(devTestWithUser); const scrollContainerRef = React.useRef(null); ///////////////// const updatePreference = (option, value) => { - const newPrefs = { ...preferences }; - newPrefs[option] = value; - setPreferences(newPrefs); + setPreferences({ ...preferences, [option]: value }); } const changeSection = React.useCallback((event) => { diff --git a/src/routes/Settings/useSettings.js b/src/routes/Settings/useSettings.js new file mode 100644 index 000000000..bca76cb46 --- /dev/null +++ b/src/routes/Settings/useSettings.js @@ -0,0 +1,22 @@ +const React = require('react'); + +module.exports = (devTestWithUser) => React.useState({ + "user": devTestWithUser ? { + "_id": "neo", + "email": "neo@example.com", + "avatar": "https://www.thenational.ae/image/policy:1.891803:1566372420/AC17-Matrix-20-04.jpg?f=16x9&w=1200&$p$f$w=5867e40", + } : null, + "ui_language": "eng", + "default_subtitles_language": "bul", + "default_subtitles_size": "100%", + "subtitles_background": "", + "subtitles_color": "#ffffff", + "subtitles_outline_color": "#000", + "auto-play_next_episode": true, + "pause_playback_when_minimized": false, + "hardware-accelerated_decoding": true, + "launch_player_in_a_separate_window_(advanced)": true, + "caching": "2048", + "torrent_profile": "profile-default", + "streaming_server_is_available.": true, +}); \ No newline at end of file From 4a910893d074306c7465a6aef54251d44dc07c3d Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 16:56:55 +0300 Subject: [PATCH 53/54] Use memo --- src/routes/Settings/Settings.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 854e6a8ec..9f3476600 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -9,12 +9,12 @@ const settingsValues = require('./useSettings'); const devTestWithUser = true; const Settings = () => { - const sections = Object.keys(settingsSections) + const sections = React.useMemo(() => Object.keys(settingsSections) .map((section) => ({ id: section, inputs: settingsSections[section], - ref: React.useRef(null) - })); + ref: React.createRef() + })), []); const [selectedSectionId, setSelectedSectionId] = React.useState(sections[0].id); const [preferences, setPreferences] = settingsValues(devTestWithUser); const scrollContainerRef = React.useRef(null); From 04ec77543780722476aa80260ba9052b9712ec9d Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Mon, 30 Sep 2019 16:59:57 +0300 Subject: [PATCH 54/54] More meaningsull variable names --- src/routes/Settings/Settings.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index 9f3476600..4884722dd 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -4,19 +4,19 @@ const styles = require('./styles'); const SectionsSelector = require('./SectionsSelector'); const SectionsList = require('./SectionsList'); const { settingsSections } = require('./constants'); -const settingsValues = require('./useSettings'); +const useSettings = require('./useSettings'); const devTestWithUser = true; const Settings = () => { - const sections = React.useMemo(() => Object.keys(settingsSections) + const [preferences, setPreferences] = useSettings(devTestWithUser); + const sections = React.useMemo(()=>Object.keys(settingsSections) .map((section) => ({ id: section, inputs: settingsSections[section], ref: React.createRef() })), []); const [selectedSectionId, setSelectedSectionId] = React.useState(sections[0].id); - const [preferences, setPreferences] = settingsValues(devTestWithUser); const scrollContainerRef = React.useRef(null); /////////////////