diff --git a/src/common/ColorInput/ColorInput.js b/src/common/ColorInput/ColorInput.js
new file mode 100644
index 000000000..ee5a37a10
--- /dev/null
+++ b/src/common/ColorInput/ColorInput.js
@@ -0,0 +1,62 @@
+const React = require('react');
+const PropTypes = require('prop-types');
+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');
+const styles = require('./styles');
+
+const ColorInput = ({ className, id, value, onChange }) => {
+ const [colorInputVisible, showColorInput, closeColorInput] = useBinaryState(false);
+ const [selectedColor, setSelectedColor] = React.useState(value);
+
+ const confirmColorInput = React.useCallback((event) => {
+ if(typeof onChange === "function") {
+ event.nativeEvent.value = selectedColor;
+ onChange(event);
+ }
+ closeColorInput();
+ }, [selectedColor, onChange]);
+
+ React.useEffect(() => {
+ setSelectedColor(value);
+ }, [value, colorInputVisible]);
+
+ const modalBackgroundOnClick = React.useCallback((event) => {
+ if(event.target === event.currentTarget) {
+ closeColorInput();
+ }
+ }, []);
+
+ return (
+
+
+ {
+ colorInputVisible
+ ?
+
+
+
+
Choose a color:
+
+
+
+
+ :
+ null
+ }
+
+ );
+};
+
+ColorInput.propTypes = {
+ className: PropTypes.string,
+ id: PropTypes.string.isRequired,
+ value: PropTypes.string,
+ onChange: PropTypes.func
+};
+
+module.exports = ColorInput;
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..c52eaaba4
--- /dev/null
+++ b/src/common/ColorInput/styles.less
@@ -0,0 +1,49 @@
+.color-input-modal {
+ background-color: var(--color-backgrounddarker40);
+ display: flex;
+ flex-direction: column;
+
+
+ .color-input-container {
+ position: relative;
+ padding: 1rem;
+ background-color: var(--color-surfacelighter);
+
+ margin: auto;
+
+ * {
+ overflow: visible;
+ }
+
+ .x-icon {
+ position: absolute;
+ top: 1rem;
+ right: 1rem;
+ width: 1rem;
+ height: 1rem;
+ fill: var(--color-surfacedark);
+ }
+
+ h1 {
+ font-size: 1.2rem;
+ }
+
+ .color-input {
+ 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);
+ }
+ :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: 1rem;
+ }
+ }
+}
\ No newline at end of file
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
new file mode 100644
index 000000000..c835738b2
--- /dev/null
+++ b/src/routes/Settings/SectionsList/SectionsList.js
@@ -0,0 +1,196 @@
+const React = require('react');
+const PropTypes = require('prop-types');
+const { Button, Dropdown, Checkbox, ColorInput } = require('stremio/common');
+const Icon = require('stremio-icons/dom');
+const classnames = require('classnames');
+const styles = require('./styles');
+
+const SectionsList = React.forwardRef(({ className, sections, preferences, onPreferenceChanged, onScroll }, ref) => {
+ const toggleCheckbox = (id) => {
+ onPreferenceChanged(id, !preferences[id]);
+ };
+
+ 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) {
+ // 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';
+
+ 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}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{'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 (
+
+ {sectionsElements}
+
+ );
+});
+
+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,
+ onScroll: PropTypes.func.isRequired,
+};
+
+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/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
new file mode 100644
index 000000000..a1f08dd12
--- /dev/null
+++ b/src/routes/Settings/SectionsSelector/SectionsSelector.js
@@ -0,0 +1,28 @@
+const React = require('react');
+const PropTypes = require('prop-types');
+const { Button } = require('stremio/common');
+const classnames = require('classnames');
+const styles = require('./styles');
+
+const SectionsSelector = ({ className, sections, selectedSectionId, onSelectedSection }) => {
+ return (
+
+ {sections.map((section) =>
+
+ )}
+
+ );
+};
+
+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;
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/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/Settings.js b/src/routes/Settings/Settings.js
index abcfcf59e..4884722dd 100644
--- a/src/routes/Settings/Settings.js
+++ b/src/routes/Settings/Settings.js
@@ -1,312 +1,68 @@
-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 { NavBar } = require('stremio/common');
+const styles = require('./styles');
+const SectionsSelector = require('./SectionsSelector');
+const SectionsList = require('./SectionsList');
+const { settingsSections } = require('./constants');
+const useSettings = require('./useSettings');
-const SECTIONS_ORDER = {
- 'General': 1,
- 'Player': 2,
- 'Streaming': 3
-};
+const devTestWithUser = true;
-class Settings extends Component {
- constructor(props) {
- super(props);
+const Settings = () => {
+ 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 scrollContainerRef = React.useRef(null);
- this.scrollContainerRef = React.createRef();
+ /////////////////
- this.state = {
- selectedSectionId: null,
- sections: [],
- inputs: []
- };
+ const updatePreference = (option, value) => {
+ setPreferences({ ...preferences, [option]: 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' }
- ]);
- }
-
- 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 = React.useCallback((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,
+ behavior: 'smooth'
});
- }
+ }, [sections]);
- 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) {
- 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 });
+ const sectionListOnScorll = React.useCallback((event) => {
+ const scrollContainer = event.currentTarget;
+ if (scrollContainer.scrollTop + scrollContainer.clientHeight === scrollContainer.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 <= scrollContainer.scrollTop) {
+ setSelectedSectionId(sections[i].id);
break;
}
}
}
- }
+ }, [sections]);
- 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();
- });
- }
-
- 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) => ({
- ...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.id === input.id ? data.option : input.value,
- active: false
- }))
- }
- })
- }
-
- renderPopup({ ref, activate, deactivate, active, value, id, options, onClick }) {
- return (
-
-
-
- {value}
-
-
-
-
-
- {options.map((option) =>
-
{option}
- )}
-
-
-
- );
- }
-
- render() {
- return (
+ return (
+
+
-
- {this.state.sections.map((section) =>
-
- {section.id}
-
- )}
-
-
- {this.state.sections.map((section) =>
-
-
{section.id}
- {this.state.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}
- {this.renderPopup({
- ref: input.ref,
- activate: this.activate,
- deactivate: this.deactivate,
- active: input.active,
- id: input.id,
- value: input.value,
- options: input.options,
- onClick: this.onChange
- })}
-
- );
- } else if (input.type === 'link') {
- return (
-
- {input.header ?
{input.header}
: null}
-
{input.label}
-
- );
- } else if (input.type === 'button') {
- return (
-
- {input.header ?
{input.header}
: null}
-
- {input.icon ?
: null}
-
{input.label}
-
-
- );
- } 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}
-
-
- );
- }
- })}
-
- )}
-
+
+
- );
- }
-}
+
+ );
+};
-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: []
-}
-
-export default Settings;
+module.exports = Settings;
diff --git a/src/routes/Settings/constants.js b/src/routes/Settings/constants.js
new file mode 100644
index 000000000..563bbd8c2
--- /dev/null
+++ b/src/routes/Settings/constants.js
@@ -0,0 +1,27 @@
+const settingsSections = {
+ 'General': [
+ { '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': [
+ { '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', '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' },
+ { 'label': 'Launch player in a separate window (advanced)', 'type': 'checkbox', 'id': 'launch_player_in_a_separate_window_(advanced)' },
+ ],
+ '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' },
+ { '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.' }
+ ]
+};
+
+module.exports = {
+ settingsSections,
+};
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..df5281628 100644
--- a/src/routes/Settings/styles.less
+++ b/src/routes/Settings/styles.less
@@ -1,307 +1,41 @@
-.settings-container, :global(.popup-container) {
- --spacing: 16px;
- --input-width: 500px;
- 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);
+ --input-width: 35rem;
+
+ .side-menu {
+ padding: 1rem;
+ width: 17rem;
+ display: flex;
+ flex-direction: column;
+ background-color: var(--color-backgroundlighter);
- .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);
- }
-
- .input-container {
- margin: calc(var(--spacing) * 1.5);
- display: flex;
- flex-direction: column;
-
- .input-header {
- margin-bottom: calc(var(--spacing) * 0.5);
- color: var(--color-surfacelighter);
- }
-
- &.user-container {
- flex-direction: row;
- align-items: center;
-
- .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 {
- color: var(--color-surfacelighter);
- }
- }
-
- &.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 {
- 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);
- }
-
- .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);
- }
- }
- }
-
- &.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;
- }
}
- >:not(:last-child) {
- border-bottom: calc(var(--focusable-border-size) * 0.5) solid var(--color-primary);
+ .scroll-container {
+ padding: 0 2rem;
+ flex: 1;
+ overflow-y: auto;
+
+ >:not(:last-child) {
+ border-bottom: calc(var(--focusable-border-size) * 0.5) solid var(--color-primary);
+ }
}
}
}
-
-: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
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