Split settings into several files

This commit is contained in:
Vladimir Borisov 2019-09-25 16:34:36 +03:00
parent 7a1f42d4fd
commit 92a0522faa
No known key found for this signature in database
GPG key ID: F9A584BE4FCB6603
5 changed files with 175 additions and 183 deletions

View file

@ -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 (
<div ref={scrollContainerRef} className={className} onScroll={onScroll}>
{sections.map((section) =>
<div key={section.id} ref={section.ref} className={styles['section']} data-section={section.id}>
<div className={styles['section-header']}>{section.id}</div>
{(section.inputs || [])
.map((input) => {
if (input.type === 'user') {
return (
<div key={input.id} className={classnames(styles['input-container'], styles['user-container'])}>
{
!preferences[input.id]
?
<div style={{ backgroundImage: `url('/images/anonymous.png')` }} className={styles['avatar']} />
:
<div style={{ backgroundImage: `url('${this.props.avatar}'), url('/images/default_avatar.png')` }} className={styles['avatar']} />
}
<div className={styles['email']}>{!preferences[input.id] ? 'Anonymous user' : preferences[input.id].email}</div>
</div>
);
} else if (input.type === 'select') {
return (
<div key={input.id} className={classnames(styles['input-container'], styles['select-container'])}>
{input.header ? <div className={styles['input-header']}>{input.header}</div> : null}
<Dropdown options={input.options} selected={[preferences[input.id]]} name={input.id} key={input.id} className={styles['dropdown']} onSelect={updateDropdown} />
</div>
);
} else if (input.type === 'link') {
return (
<div key={input.id} className={classnames(styles['input-container'], styles['link-container'])}>
{input.header ? <div className={styles['input-header']}>{input.header}</div> : null}
<Button ref={input.ref} className={styles['link']} type={input.type} href={input.href} target={'_blank'}>
<div className={styles['label']}>{input.label}</div>
</Button>
</div>
);
} else if (input.type === 'button') {
return (
<div key={input.id} className={classnames(styles['input-container'], styles['button-container'])}>
{input.header ? <div className={styles['input-header']}>{input.header}</div> : null}
<Button ref={input.ref} className={styles['button']} type={input.type}>
{input.icon ? <Icon className={styles['icon']} icon={input.icon} /> : null}
<div className={styles['label']}>{input.label}</div>
</Button>
</div>
);
} else if (input.type === 'checkbox') {
return (
<div key={input.id} className={classnames(styles['input-container'], styles['checkbox-container'])}>
{input.header ? <div className={styles['input-header']}>{input.header}</div> : null}
<Checkbox ref={input.ref} className={styles['checkbox']} checked={preferences[input.id]} onClick={toggleCheckbox.bind(null, input.id)}>
<div className={styles['label']}>{input.label}</div>
</Checkbox>
</div>
);
} else if (input.type === 'static-text') {
return (
<div key={input.id} className={classnames(styles['input-container'], styles['text-container'])}>
{input.header ? <div className={styles['input-header']}>{input.header}</div> : null}
<div className={styles['text']}>
{input.icon ? <Icon className={styles[input.icon === 'ic_x' ? 'x-icon' : 'icon']} icon={input.icon} /> : null}
<div className={styles['label']}>{input.label}</div>
</div>
</div>
);
} else if (input.type === 'color') {
return (
<div key={input.id} className={classnames(styles['input-container'], styles['color-container'])}>
{input.header ? <div className={styles['input-header']}>{input.header}</div> : null}
<ColorInput className={styles['color-picker']} defaultValue={preferences[input.id]} tabIndex={'-1'} onChange={colorChanged.bind(null, input.id)} />
{/* <TextInput className={styles['color-picker']} type={input.type} defaultValue={input.color} tabIndex={'-1'} /> */}
</div>
);
}
})}
</div>
)}
</div>
);
});
module.exports = SectionsList;

View file

@ -0,0 +1,3 @@
const SectionsList = require('./SectionsList');
module.exports = SectionsList;

View file

@ -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 (
<div className={className}>
{sections.map((section) =>
<Button key={section.id} className={classnames(styles['section-label'], { [styles['selected']]: selectedSectionId === section.id })} type={'button'} data-section={section.id} onClick={onSelectedSection}>
{section.id}
</Button>
)}
</div>
);
};
module.exports = SectionsSelector;

View file

@ -0,0 +1,3 @@
const SectionsSelector = require('./SectionsSelector');
module.exports = SectionsSelector;

View file

@ -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 (
<div className={styles['settings-parent-container']}>
@ -133,91 +81,8 @@ const Settings = () => {
fullscreenButton={true}
navMenu={true} />
<div className={styles['settings-container']}>
<div className={styles['side-menu']}>
{sections.map((section) =>
<Button key={section.id} className={classnames(styles['section-label'], { [styles['selected']]: selectedSectionId === section.id })} type={'button'} data-section={section.id} onClick={changeSection}>
{section.id}
</Button>
)}
</div>
<div ref={scrollContainerRef} className={styles['scroll-container']} onScroll={onScroll}>
{sections.map((section) =>
<div key={section.id} ref={section.ref} className={styles['section']} data-section={section.id}>
<div className={styles['section-header']}>{section.id}</div>
{inputs.filter((input) => input.section === section.id)
.map((input) => {
if (input.type === 'user') {
return (
<div key={input.id} className={classnames(styles['input-container'], styles['user-container'])}>
{
input.email.length === 0
?
<div style={{ backgroundImage: `url('/images/anonymous.png')` }} className={styles['avatar']} />
:
<div style={{ backgroundImage: `url('${this.props.avatar}'), url('/images/default_avatar.png')` }} className={styles['avatar']} />
}
<div className={styles['email']}>{input.email.length === 0 ? 'Anonymous user' : input.email}</div>
</div>
);
} else if (input.type === 'select') {
return (
<div key={input.id} className={classnames(styles['input-container'], styles['select-container'])}>
{input.header ? <div className={styles['input-header']}>{input.header}</div> : null}
{/* TODO: provide proper data instead of mapping the options */}
<Dropdown options={input.options.map(o => ({ value: o, label: o }))} selected={[input.value]} name={input.id} key={input.id} className={styles['dropdown']} onSelect={updateDropdown} />
</div>
);
} else if (input.type === 'link') {
return (
<div key={input.id} className={classnames(styles['input-container'], styles['link-container'])}>
{input.header ? <div className={styles['input-header']}>{input.header}</div> : null}
<Button ref={input.ref} className={styles['link']} type={input.type} href={input.href} target={'_blank'}>
<div className={styles['label']}>{input.label}</div>
</Button>
</div>
);
} else if (input.type === 'button') {
return (
<div key={input.id} className={classnames(styles['input-container'], styles['button-container'])}>
{input.header ? <div className={styles['input-header']}>{input.header}</div> : null}
<Button ref={input.ref} className={styles['button']} type={input.type}>
{input.icon ? <Icon className={styles['icon']} icon={input.icon} /> : null}
<div className={styles['label']}>{input.label}</div>
</Button>
</div>
);
} else if (input.type === 'checkbox') {
return (
<div key={input.id} className={classnames(styles['input-container'], styles['checkbox-container'])}>
{input.header ? <div className={styles['input-header']}>{input.header}</div> : null}
<Checkbox ref={input.ref} className={styles['checkbox']} checked={input.value} onClick={toggleCheckbox.bind(null, input.id)}>
<div className={styles['label']}>{input.label}</div>
</Checkbox>
</div>
);
} else if (input.type === 'static-text') {
return (
<div key={input.id} className={classnames(styles['input-container'], styles['text-container'])}>
{input.header ? <div className={styles['input-header']}>{input.header}</div> : null}
<div className={styles['text']}>
{input.icon ? <Icon className={styles[input.icon === 'ic_x' ? 'x-icon' : 'icon']} icon={input.icon} /> : null}
<div className={styles['label']}>{input.label}</div>
</div>
</div>
);
} else if (input.type === 'color') {
return (
<div key={input.id} className={classnames(styles['input-container'], styles['color-container'])}>
{input.header ? <div className={styles['input-header']}>{input.header}</div> : null}
<ColorInput className={styles['color-picker']} defaultValue={input.color} tabIndex={'-1'} onChange={colorChanged.bind(null, input.id)} />
{/* <TextInput className={styles['color-picker']} type={input.type} defaultValue={input.color} tabIndex={'-1'} /> */}
</div>
);
}
})}
</div>
)}
</div>
<SectionsSelector className={styles['side-menu']} sections={sections} selectedSectionId={selectedSectionId} onSelectedSection={changeSection} />
<SectionsList ref={scrollContainerRef} className={styles['scroll-container']} sections={sections} preferences={preferences} onPreferenceChanged={updatePreference} onScroll={updateSection} />
</div>
</div>
);