refactor: make modals like widgets

This commit is contained in:
David Ralph
2021-03-20 12:55:20 +00:00
parent 0c71f0ebef
commit ab7681f3d0
44 changed files with 362 additions and 117 deletions

View File

@@ -0,0 +1,43 @@
import React from 'react';
import SettingsFunctions from '../../../../modules/helpers/settings';
import CheckboxUI from '@material-ui/core/Checkbox';
import FormControlLabel from '@material-ui/core/FormControlLabel';
export default class Checkbox extends React.PureComponent {
constructor(...args) {
super(...args);
this.state = {
checked: (localStorage.getItem(this.props.name) === 'true')
};
}
handleChange(name) {
SettingsFunctions.setItem(name);
this.setState({
checked: (this.state.checked === true) ? false : true
});
}
render() {
let text = this.props.text;
if (this.props.newFeature) {
text = <span>{this.props.text} <span className='newFeature'> NEW</span></span>;
} else if (this.props.betaFeature) {
text = <span>{this.props.text} <span className='newFeature'> BETA</span></span>;
}
return (
<React.Fragment>
<FormControlLabel
control={<CheckboxUI name='checkedB' color='primary' checked={this.state.checked} onChange={() => this.handleChange(this.props.name)} />}
label={text}
/>
<br/>
</React.Fragment>
);
}
}

View File

@@ -0,0 +1,31 @@
import React from 'react';
export default class Dropdown extends React.PureComponent {
getLabel() {
return this.props.label ? <label htmlFor={this.props.name}>{this.props.label}</label> : null;
}
componentDidMount() {
document.getElementById(this.props.name).value = localStorage.getItem(this.props.name);
}
onChange = () => {
localStorage.setItem(this.props.name, document.getElementById(this.props.name).value);
if (this.props.onChange) {
this.props.onChange();
}
}
render() {
return (
<React.Fragment>
{this.getLabel()}
<div className='dropdown' style={{ display: 'inline' }}>
<select name={this.props.name} id={this.props.name} onChange={this.onChange}>
{this.props.children}
</select>
</div>
</React.Fragment>
);
}
}

View File

@@ -0,0 +1,29 @@
import React from 'react';
import { toast } from 'react-toastify';
export default class FileUpload extends React.PureComponent {
componentDidMount() {
document.getElementById(this.props.id).onchange = (e) => {
const reader = new FileReader();
const file = e.target.files[0];
if (this.props.type === 'settings') {
reader.readAsText(file, 'UTF-8');
} else {
// background upload
if (file.size > 2000000) {
return toast('File is over 2MB');
}
reader.readAsDataURL(file);
}
reader.addEventListener('load', (e) => this.props.loadFunction(e));
};
}
render() {
return <input id={this.props.id} type='file' className='hidden' accept={this.props.accept} />;
}
}

View File

@@ -0,0 +1,14 @@
import React from 'react';
export default function ResetModal(props) {
return (
<div className='welcomeContent'>
<span className='closeModal' onClick={props.modalClose}>&times;</span>
<div className='welcomeModalText'>
reset text
<button className='close' onClick={props.modalClose}>yes</button>
<button className='close' onClick={props.modalClose}>no</button>
</div>
</div>
);
}

View File

@@ -0,0 +1,83 @@
import React from 'react';
import Tooltip from '@material-ui/core/Tooltip';
const other_contributors = require('../../../../../modules/other_contributors.json');
const { version } = require('../../../../../../package.json');
export default class About extends React.PureComponent {
constructor(...args) {
super(...args);
this.state = {
contributors: [],
sponsors: [],
other_contributors: [],
update: window.language.modals.main.settings.sections.about.version.checking_update
}
this.language = window.language.modals.main.settings.sections.about;
}
async getGitHubData() {
const contributors = await (await fetch('https://api.github.com/repos/mue/mue/contributors')).json();
const { sponsors } = await (await fetch(window.constants.SPONSORS_URL + '/list')).json();
const versionData = await (await fetch('https://api.github.com/repos/mue/mue/releases')).json();
const newVersion = versionData[0].tag_name;
let updateMsg = this.language.version.no_update;
if (version < newVersion) {
updateMsg = `${this.language.version.update_available}: ${newVersion}`;
}
this.setState({
contributors: contributors.filter((contributor) => !contributor.login.includes('bot')),
sponsors: sponsors,
update: updateMsg,
other_contributors: other_contributors
});
}
componentDidMount() {
if (localStorage.getItem('offlineMode') === 'true') {
this.setState({
update: this.language.version.offline_mode
});
return;
}
this.getGitHubData();
}
render() {
return (
<div>
<h2>{this.language.title}</h2>
<img draggable='false' style={{'height': '100px', 'width': 'auto'}} src='https://raw.githubusercontent.com/mue/branding/master/logo/logo_horizontal.png' alt='Mue logo'></img>
<p>{this.language.copyright} 2018-{new Date().getFullYear()} Mue Tab (BSD-3 License)</p>
<p>{this.language.version.title} {version} ({this.state.update})</p>
<h3>{this.language.resources_used.title}</h3>
<p>Pexels ({this.language.resources_used.bg_images})</p>
<p>Google ({this.language.resources_used.pin_icon})</p>
<p>Undraw ({this.language.resources_used.welcome_img})</p>
<h3>{this.language.contributors}</h3>
{this.state.contributors.map((item) =>
<Tooltip title={item.login} placement='top' key={item.login}>
<a href={'https://github.com/' + item.login} target='_blank' rel='noopener noreferrer'><img draggable='false' className='abouticon' src={item.avatar_url + '&size=256'} alt={item.login}></img></a>
</Tooltip>
)}
{ // for those who contributed without opening a pull request
this.state.other_contributors.map((item) =>
<Tooltip title={item.login} placement='top' key={item.login}>
<a href={'https://github.com/' + item.login} target='_blank' rel='noopener noreferrer'><img draggable='false' className='abouticon' src={item.avatar_url + '&size=256'} alt={item.login}></img></a>
</Tooltip>
)}
<h3>{this.language.supporters}</h3>
{this.state.sponsors.map((item) =>
<Tooltip title={item.handle} placement='top' key={item.handle}>
<a href={item.profile} target='_blank' rel='noopener noreferrer'><img draggable='false' className='abouticon' src={item.avatar + '&size=256'} alt={item.handle}></img></a>
</Tooltip>
)}
</div>
);
}
}

View File

@@ -0,0 +1,80 @@
import React from 'react';
import Checkbox from '../Checkbox';
import FileUpload from '../FileUpload';
import ResetModal from '../ResetModal';
import SettingsFunctions from '../../../../../modules/helpers/settings';
import { toast } from 'react-toastify';
import Modal from 'react-modal';
export default class AdvancedSettings extends React.PureComponent {
constructor(...args) {
super(...args);
this.state = {
resetModal: false
};
this.language = window.language.modals.main.settings;
}
resetItem(type) {
document.getElementById(type).value = '';
toast(this.language.toasts.reset);
}
settingsImport(e) {
const content = JSON.parse(e.target.result);
for (const key of Object.keys(content)) {
localStorage.setItem(key, content[key]);
}
toast(this.language.toasts.imported);
}
componentDidMount() {
document.getElementById('customcss').value = localStorage.getItem('customcss');
document.getElementById('customjs').value = localStorage.getItem('customjs');
}
componentDidUpdate() {
localStorage.setItem('customcss', document.getElementById('customcss').value);
localStorage.setItem('customjs', document.getElementById('customjs').value);
}
render() {
const { advanced } = this.language.sections;
return (
<div>
<h2>{advanced.title}</h2>
<Checkbox name='offlineMode' text={advanced.offline_mode} />
<h3>{advanced.data}</h3>
<button className='reset' onClick={() => this.setState({ resetModal: true })}>{this.language.buttons.reset}</button>
<button className='export' onClick={() => SettingsFunctions.exportSettings()}>{this.language.buttons.export}</button>
<button className='import' onClick={() => document.getElementById('file-input').click()}>{this.language.buttons.import}</button>
<FileUpload id='file-input' accept='application/json' type='settings' loadFunction={(e) => this.settingsImport(e)} />
<h3>{advanced.customisation}</h3>
<ul>
<p>{advanced.custom_css} <span className='modalLink' onClick={() => this.resetItem('customcss')}>{this.language.buttons.reset}</span></p>
<textarea id='customcss'></textarea>
</ul>
<ul>
<p>{advanced.custom_js} <span className='modalLink' onClick={() => this.resetItem('customjs')}>{this.language.buttons.reset}</span></p>
<textarea id='customjs'></textarea>
</ul>
<h3>{this.language.sections.experimental.title}</h3>
<p>{advanced.experimental_warning}</p>
<Checkbox name='experimental' text={this.language.enabled} />
<Modal onRequestClose={() => this.setState({ resetModal: false })} isOpen={this.state.resetModal} className={'modal'} overlayClassName={'Overlay'} ariaHideApp={false}>
<ResetModal modalClose={() => this.setState({ resetModal: false })} />
</Modal>
</div>
);
}
}

View File

@@ -0,0 +1,115 @@
import React from 'react';
import Checkbox from '../Checkbox';
import Dropdown from '../Dropdown';
import { toast } from 'react-toastify';
export default class AppearanceSettings extends React.PureComponent {
constructor(...args) {
super(...args);
this.state = {
zoom: localStorage.getItem('zoom'),
toast_duration: localStorage.getItem('toastDisplayTime'),
font: localStorage.getItem('font') || ''
};
this.language = window.language.modals.main.settings;
}
resetItem(key) {
switch (key) {
case 'zoom':
localStorage.setItem('zoom', 100);
this.setState({
zoom: 100
});
break;
case 'toast_duration':
localStorage.setItem('toastDisplayTime', 2500);
this.setState({
toast_duration: 2500
});
break;
case 'font':
localStorage.setItem('font', '');
this.setState({
font: ''
});
break;
default:
toast('resetItem requires a key!');
}
toast(this.language.toasts.reset);
}
componentDidUpdate() {
localStorage.setItem('zoom', this.state.zoom);
localStorage.setItem('toastDisplayTime', this.state.toast_duration);
localStorage.setItem('font', this.state.font.charAt(0).toUpperCase() + this.state.font.slice(1));
}
render() {
const { appearance } = this.language.sections;
return (
<div>
<h2>{appearance.title}</h2>
<Checkbox name='darkTheme' text={appearance.dark_theme} />
<Checkbox name='brightnessTime' text={appearance.night_mode} />
<h3>{appearance.navbar.title}</h3>
<Checkbox name='notesEnabled' text={appearance.navbar.notes} />
<Checkbox name='refresh' text={appearance.navbar.refresh} />
<h3>{appearance.font.title}</h3>
<ul>
<p>{appearance.font.custom} <span className='modalLink' onClick={() => this.resetItem('font')}>{this.language.buttons.reset}</span></p>
<input type='text' value={this.state.font} onChange={(e) => this.setState({ font: e.target.value })}></input>
</ul>
<br />
<Dropdown
label='Font Weight'
name='fontweight'
id='fontWeight'
onChange={() => localStorage.setItem('fontWeight', document.getElementById('fontWeight').value)}>
{/* names are taken from https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight */}
<option className='choices' value='100'>Thin</option>
<option className='choices' value='200'>Extra-Light</option>
<option className='choices' value='300'>Light</option>
<option className='choices' value='400'>Normal</option>
<option className='choices' value='500'>Medium</option>
<option className='choices' value='600'>Semi-Bold</option>
<option className='choices' value='700'>Bold</option>
<option className='choices' value='800'>Extra-Bold</option>
</Dropdown>
<br /><br />
<Dropdown
label='Font Style'
name='fontstyle'
id='fontStyle'
onChange={() => localStorage.setItem('fontStyle', document.getElementById('fontStyle').value)}>
<option className='choices' value='normal'>Normal</option>
<option className='choices' value='italic'>Italic</option>
<option className='choices' value='oblique'>Oblique</option>
</Dropdown>
<br /><br />
<Checkbox name='fontGoogle' text={appearance.font.google} />
<h3>{appearance.accessibility.title}</h3>
<Checkbox name='animations' text={appearance.animations} betaFeature={true} />
<ul>
<p>{appearance.accessibility.zoom} ({this.state.zoom}%) <span className='modalLink' onClick={() => this.resetItem('zoom')}>{this.language.buttons.reset}</span></p>
<input className='range' type='range' min='50' max='200' value={this.state.zoom} onChange={(event) => this.setState({ zoom: event.target.value })} />
</ul>
<ul>
<p>{appearance.accessibility.toast_duration} ({this.state.toast_duration} {appearance.accessibility.milliseconds}) <span className='modalLink' onClick={() => this.resetItem('toast_duration')}>{this.language.buttons.reset}</span></p>
<input className='range' type='range' min='500' max='5000' value={this.state.toast_duration} onChange={(event) => this.setState({ toast_duration: event.target.value })} />
</ul>
</div>
);
}
}

View File

@@ -0,0 +1,265 @@
import React from 'react';
import Checkbox from '../Checkbox';
import Dropdown from '../Dropdown';
import FileUpload from '../FileUpload';
import { ColorPicker } from 'react-color-gradient-picker';
import hexToRgb from '../../../../../modules/helpers/background/hexToRgb';
import rgbToHex from '../../../../../modules/helpers/background/rgbToHex';
import { toast } from 'react-toastify';
import 'react-color-gradient-picker/dist/index.css';
import '../../../../../scss/react-color-picker-gradient-picker-custom-styles.scss';
export default class BackgroundSettings extends React.PureComponent {
DefaultGradientSettings = { 'angle': '180', 'gradient': [{ 'colour': window.language.modals.main.settings.sections.background.source.disabled, 'stop': 0 }], 'type': 'linear' };
GradientPickerInitalState = undefined;
constructor(...args) {
super(...args);
this.state = {
blur: localStorage.getItem('blur'),
brightness: localStorage.getItem('brightness'),
customBackground: localStorage.getItem('customBackground') || '',
gradientSettings: this.DefaultGradientSettings
};
this.language = window.language.modals.main.settings;
}
resetItem(key) {
switch (key) {
case 'customBackgroundColour':
localStorage.setItem('customBackgroundColour', '');
this.setState({
gradientSettings: this.DefaultGradientSettings
});
break;
case 'customBackground':
localStorage.setItem('customBackground', '');
this.setState({
customBackground: ''
});
break;
case 'blur':
localStorage.setItem('blur', 0);
this.setState({
blur: 0
});
break;
case 'brightness':
localStorage.setItem('brightness', 100);
this.setState({
brightness: 100
});
break;
default:
toast('resetItem requires a key!');
}
toast(this.language.toasts.reset);
}
InitializeColorPickerState(gradientSettings) {
this.GradientPickerInitalState = {
points: gradientSettings.gradient.map((g) => {
const rgb = hexToRgb(g.colour);
return {
left: +g.stop,
red: rgb.red,
green: rgb.green,
blue: rgb.blue,
alpha: 1
};
}),
degree: + gradientSettings.angle,
type: gradientSettings.type
};
}
componentDidMount() {
const hex = localStorage.getItem('customBackgroundColour');
let gradientSettings = undefined;
if (hex !== '') {
try {
gradientSettings = JSON.parse(hex);
} catch (e) {
// Disregard exception.
}
}
if (gradientSettings === undefined) {
gradientSettings = this.DefaultGradientSettings;
}
this.setState({
gradientSettings
});
}
onGradientChange = (event, index) => {
const newValue = event.target.value;
const name = event.target.name;
this.setState((s) => {
const newState = {
gradientSettings: {
...s.gradientSettings,
gradient: s.gradientSettings.gradient.map((g, i) => i === index ? { ...g, [name]: newValue } : g)
}
};
return newState;
});
}
addColour = () => {
this.setState((s) => {
const [lastGradient, ...initGradients] = s.gradientSettings.gradient.reverse();
const newState = {
gradientSettings: {
...s.gradientSettings,
gradient: [...initGradients, lastGradient, { 'colour': localStorage.getItem('darkTheme') === 'true' ? '#000000' : '#ffffff', stop: 100 }].sort((a, b) => (a.stop > b.stop) ? 1 : -1)
}
};
return newState;
});
}
currentGradientSettings = () => {
if (typeof this.state.gradientSettings === 'object' && this.state.gradientSettings.gradient.every(g => g.colour !== this.language.sections.background.source.disabled)) {
const clampNumber = (num, a, b) => Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b));
return JSON.stringify({
...this.state.gradientSettings,
gradient: [...this.state.gradientSettings.gradient.map(g => { return { ...g, stop: clampNumber(+g.stop, 0, 100) } })].sort((a, b) => (a.stop > b.stop) ? 1 : -1)
});
}
return this.language.sections.background.source.disabled;
}
onColorPickerChange = (attrs, name) => {
if (process.env.NODE_ENV === 'development') {
console.log(attrs, name);
}
this.setState({
gradientSettings: {
'angle': attrs.degree,
'gradient': attrs.points.map((p) => {
return {
'colour': '#' + rgbToHex(p.red, p.green, p.blue),
'stop': p.left
}}),
'type': attrs.type
}
});
};
fileUpload(e) {
localStorage.setItem('customBackground', e.target.result);
this.setState({
customBackground: e.target.result
});
}
componentDidUpdate() {
localStorage.setItem('blur', this.state.blur);
localStorage.setItem('brightness', this.state.brightness);
localStorage.setItem('customBackground', this.state.customBackground);
if (document.getElementById('customBackgroundHex').value !== this.language.sections.background.source.disabled) {
localStorage.setItem('customBackgroundColour', document.getElementById('customBackgroundHex').value);
}
}
render() {
const { background } = this.language.sections;
let colourSettings = null;
if (typeof this.state.gradientSettings === 'object') {
const gradientHasMoreThanOneColour = this.state.gradientSettings.gradient.length > 1;
let gradientInputs;
if (gradientHasMoreThanOneColour) {
if (this.GradientPickerInitalState === undefined) {
this.InitializeColorPickerState(this.state.gradientSettings);
}
gradientInputs = (
<ColorPicker
onStartChange={(color) => this.onColorPickerChange(color, 'start')}
onChange={(color) => this.onColorPickerChange(color, 'change')}
onEndChange={(color) => this.onColorPickerChange(color, 'end')}
gradient={this.GradientPickerInitalState}
isGradient />
);
} else {
gradientInputs = this.state.gradientSettings.gradient.map((g, i) => {
return (
<div key={i}>
<input id={'colour_' + i} type='color' name='colour' className='colour' onChange={(event) => this.onGradientChange(event, i)} value={g.colour}></input>
<label htmlFor={'colour_' + i} className='customBackgroundHex'>{g.colour}</label>
</div>
);
});
}
colourSettings = (
<div>
{gradientInputs}
{this.state.gradientSettings.gradient[0].colour !== background.source.disabled &&
!gradientHasMoreThanOneColour ? (<button type='button' className='add' onClick={this.addColour}>{background.source.add_colour}</button>) : null
}
</div>
);
}
return (
<div>
<h2>{background.title}</h2>
<Checkbox name='background' text='Enabled' />
<h3>{background.buttons.title}</h3>
<ul>
<Checkbox name='view' text={background.buttons.view} />
<Checkbox name='favouriteEnabled' text={background.buttons.favourite} />
</ul>
<h3>{background.effects.title}</h3>
<ul>
<p>{background.effects.blur} ({this.state.blur}%) <span className='modalLink' onClick={() => this.resetItem('blur')}>{this.language.buttons.reset}</span></p>
<input className='range' type='range' min='0' max='100' value={this.state.blur} onChange={(event) => this.setState({ blur: event.target.value })} />
</ul>
<ul>
<p>{background.effects.brightness} ({this.state.brightness}%) <span className='modalLink' onClick={() => this.resetItem('brightness')}>{this.language.buttons.reset}</span></p>
<input className='range' type='range' min='0' max='100' value={this.state.brightness} onChange={(event) => this.setState({ brightness: event.target.value })} />
</ul>
<h3>{background.source.title}</h3>
<ul>
<Dropdown label={background.source.api} name='backgroundAPI'>
<option className='choices' value='mue'>Mue</option>
<option className='choices' value='unsplash'>Unsplash</option>
</Dropdown>
</ul>
<ul>
<p>{background.source.custom_url} <span className='modalLink' onClick={() => this.resetItem('customBackground')}>{this.language.buttons.reset}</span></p>
<input type='text' value={this.state.customBackground} onChange={(e) => this.setState({ customBackground: e.target.value })}></input>
</ul>
<ul>
<p>{background.source.custom_background} <span className='modalLink' onClick={() => this.resetItem('customBackground')}>{this.language.buttons.reset}</span></p>
<button className='uploadbg' onClick={() => document.getElementById('bg-input').click()}>{background.source.upload}</button>
<FileUpload id='bg-input' accept='image/jpeg, image/png, image/webp, image/webm, image/gif' loadFunction={(e) => this.fileUpload(e)} />
</ul>
<ul>
<p>{background.source.custom_colour} <span className='modalLink' onClick={() => this.resetItem('customBackgroundColour')}>{this.language.buttons.reset}</span></p>
<input id='customBackgroundHex' type='hidden' value={this.currentGradientSettings()} />
{colourSettings}
</ul>
</div>
);
}
}

View File

@@ -0,0 +1,56 @@
import React from 'react';
export default class Changelog extends React.PureComponent {
constructor(...args) {
super(...args);
this.state = {
title: this.props.language.title,
date: null,
content: this.props.language.title,
html: this.props.language.loading,
image: null
};
}
async getUpdate() {
const data = await (await fetch(window.constants.API_URL + '/getUpdate')).json();
if (data.statusCode === 500 || data.title === null) {
const supportText = `<br/><p>${this.props.language.contact_support}: <a target='_blank' class='modalLink' href='https://muetab.com/contact'>https://muetab.com/contact</a></p>`;
return this.setState({
title: this.props.language.error.title,
html: this.props.language.error.description + supportText
});
}
this.setState({
title: data.title,
date: data.published,
image: data.image || null,
author: data.author,
html: data.content
});
}
componentDidMount() {
if (localStorage.getItem('offlineMode') === 'true') {
return this.setState({
title: this.props.language.offline.title,
html: this.props.language.offline.description
});
}
this.getUpdate();
}
render() {
return (
<div>
<h1 style={{ 'marginBottom': '-10px' }}>{this.state.title}</h1>
<h5 style={{ 'lineHeight': '0px' }}>{this.state.date}</h5>
{this.state.image ? <img draggable='false' src={this.state.image} alt='Update'></img> : null}
<p dangerouslySetInnerHTML={{ __html: this.state.html }}></p>
</div>
);
}
}

View File

@@ -0,0 +1,9 @@
import React from 'react';
import Checkbox from '../Checkbox';
export default function ExperimentalSettings (props) {
return (
<div>
</div>
);
}

View File

@@ -0,0 +1,66 @@
import React from 'react';
import Checkbox from '../Checkbox';
import DatePicker from 'react-date-picker';
import { toast } from 'react-toastify';
export default class GreetingSettings extends React.PureComponent {
constructor(...args) {
super(...args);
this.state = {
birthday: new Date(localStorage.getItem('birthday')) || new Date(),
greetingName: localStorage.getItem('greetingName') || ''
};
this.language = window.language.modals.main.settings;
}
resetItem() {
this.setState({
greetingName: ''
});
toast(this.language.toasts.reset);
}
changeDate(data) {
//soon
if (data === 'reset') {
return;
}
localStorage.setItem('birthday', data);
this.setState({
birthday: data
});
}
componentDidUpdate() {
localStorage.setItem('greetingName', this.state.greetingName);
}
render() {
const { greeting } = this.language.sections;
return (
<div>
<h2>{greeting.title}</h2>
<Checkbox name='greeting' text={this.language.enabled} />
<Checkbox name='events' text={greeting.events} />
<Checkbox name='defaultGreetingMessage' text={greeting.default} />
<ul>
<p>{greeting.name} <span className='modalLink' onClick={() => this.resetItem()}>{this.language.buttons.reset}</span></p>
<input type='text' value={this.state.greetingName} onChange={(e) => this.setState({ greetingName: e.target.value })}></input>
</ul>
<h3>{greeting.birthday}</h3>
<Checkbox name='birthdayenabled' text={this.language.enabled} />
<ul>
<p>{greeting.birthday_date}</p>
<DatePicker onChange={(data) => this.changeDate(data)} value={this.state.birthday}/>
</ul>
</div>
);
}
}

View File

@@ -0,0 +1,25 @@
import React from 'react';
import Dropdown from '../Dropdown';
const languages = require('../../../../../modules/languages.json');
export default function LanguageSettings () {
const language = window.language.modals.main.settings.sections.language;
return (
<div>
<h2>{language.title}</h2>
<Dropdown label={language.title} name='language' id='language' onChange={() => localStorage.setItem('language', document.getElementById('language').value)}>
{languages.map(language =>
<option className='choices' value={language.code} key={language.code}>{language.text}</option>
)}
</Dropdown>
<br/>
<Dropdown label={language.quote} name='quotelanguage' id='quotelanguage' onChange={() => localStorage.setItem('quotelanguage', document.getElementById('quotelanguage').value)}>
<option className='choices' value='English'>English</option>
<option className='choices' value='French'>Français</option>
</Dropdown>
</div>
);
}

View File

@@ -0,0 +1,69 @@
import React from 'react';
import Checkbox from '../Checkbox';
import { toast } from 'react-toastify';
export default class QuoteSettings extends React.PureComponent {
constructor(...args) {
super(...args);
this.state = {
customQuote: localStorage.getItem('customQuote') || '',
customQuoteAuthor: localStorage.getItem('customQuoteAuthor') || 'Unknown'
};
this.language = window.language.modals.main.settings;
}
resetItem(key) {
switch (key) {
case 'customQuote':
localStorage.setItem('customQuote', '');
this.setState({
customQuote: ''
});
break;
case 'customQuoteAuthor':
localStorage.setItem('customQuoteAuthor', '');
this.setState({
customQuoteAuthor: 'Unknown'
});
break;
default:
toast('resetItem requires a key!');
}
toast(this.language.toasts.reset);
}
componentDidUpdate() {
localStorage.setItem('customQuote', this.state.customQuote);
localStorage.setItem('customQuoteAuthor', this.state.customQuoteAuthor);
}
render() {
const { quote } = this.language.sections;
return (
<div>
<h2>{quote.title}</h2>
<Checkbox name='quote' text={this.language.enabled}/>
<Checkbox name='authorLink' text={quote.author_link}/>
<ul>
<p>{quote.custom} <span className='modalLink' onClick={() => this.resetItem('customQuote')}>{this.language.buttons.reset}</span></p>
<input type='text' value={this.state.customQuote} onChange={(e) => this.setState({ customQuote: e.target.value })}></input>
</ul>
<ul>
<p>{quote.custom_author} <span className='modalLink' onClick={() => this.resetItem('customQuoteAuthor')}>{this.language.buttons.reset}</span></p>
<input type='text' value={this.state.customQuoteAuthor} onChange={(e) => this.setState({ customQuoteAuthor: e.target.value })}></input>
</ul>
<h3>{quote.buttons.title}</h3>
<Checkbox name='copyButton' text={quote.buttons.copy}/>
<Checkbox name='tweetButton' text={quote.buttons.tweet}/>
<Checkbox name='favouriteQuoteEnabled' text={quote.buttons.favourite}/>
</div>
);
}
}

View File

@@ -0,0 +1,84 @@
import React from 'react';
import { toast } from 'react-toastify';
import Dropdown from '../Dropdown';
import Checkbox from '../Checkbox';
const searchEngines = require('../../../../widgets/search/search_engines.json');
export default class SearchSettings extends React.PureComponent {
resetSearch() {
localStorage.removeItem('customSearchEngine');
document.getElementById('customSearchEngine').value = '';
toast(this.props.language.toasts.reset);
}
componentDidMount() {
const searchEngine = localStorage.getItem('searchEngine');
if (searchEngine === 'custom') {
const input = document.getElementById('searchEngineInput');
input.style.display = 'block';
input.enabled = 'true';
document.getElementById('customSearchEngine').value = localStorage.getItem('customSearchEngine');
} else {
localStorage.removeItem('customSearchEngine');
}
document.getElementById('searchEngine').value = searchEngine;
}
componentDidUpdate() {
if (document.getElementById('searchEngineInput').enabled === 'true') {
const input = document.getElementById('customSearchEngine').value;
if (input) {
localStorage.setItem('searchEngine', 'custom');
localStorage.setItem('customSearchEngine', input);
}
}
}
setSearchEngine(input) {
const searchEngineInput = document.getElementById('searchEngineInput');
if (input === 'custom') {
searchEngineInput.enabled = 'true';
searchEngineInput.style.display = 'block';
} else {
searchEngineInput.style.display = 'none';
searchEngineInput.enabled = 'false';
localStorage.setItem('searchEngine', input);
}
}
render() {
const language = window.language.modals.main.settings;
const { search } = language.sections;
return (
<div className='section'>
<h2>{search.title}</h2>
<Checkbox name='searchBar' text={language.enabled} />
<Checkbox name='voiceSearch' text={search.voice_search} />
<ul>
<Dropdown label={search.search_engine}
name='searchEngine'
id='searchEngine'
onChange={() => this.setSearchEngine(document.getElementById('searchEngine').value)} >
{searchEngines.map((engine) =>
<option key={engine.name} className='choices' value={engine.settingsName}>{engine.name}</option>
)}
<option className='choices' value='custom'>{search.custom.split(' ')[0]}</option>
</Dropdown>
</ul>
<ul id='searchEngineInput' style={{ display: 'none' }}>
<p style={{ 'marginTop': '0px' }}>{search.custom} <span className='modalLink' onClick={() => this.resetSearch()}>{language.reset}</span></p>
<input type='text' id='customSearchEngine'></input>
</ul>
</div>
);
}
}

View File

@@ -0,0 +1,89 @@
import React from 'react';
import Checkbox from '../Checkbox';
import Dropdown from '../Dropdown';
export default class TimeSettings extends React.PureComponent {
constructor(...args) {
super(...args);
this.state = {
timeType: localStorage.getItem('timeType') || 'digital'
};
this.language = window.language.modals.main.settings;
}
changeType() {
const value = document.getElementById('timeType').value;
localStorage.setItem('timeType', value);
this.setState({
timeType: value
});
}
render() {
const { time } = this.language.sections;
let timeSettings;
const digitalSettings = (
<React.Fragment>
<h3>{time.digital.title}</h3>
<Checkbox name='seconds' text={time.digital.seconds} />
<Checkbox name='24hour' text={time.digital.twentyfourhour} />
<Checkbox name='ampm' text={time.digital.ampm} />
<Checkbox name='zero' text={time.digital.zero} />
</React.Fragment>
);
const analogSettings = (
<React.Fragment>
<h3>{time.analogue.title}</h3>
<Checkbox name='secondHand' text={time.analogue.second_hand} />
<Checkbox name='minuteHand' text={time.analogue.minute_hand} />
<Checkbox name='hourHand' text={time.analogue.hour_hand} />
<Checkbox name='hourMarks' text={time.analogue.hour_marks} />
<Checkbox name='minuteMarks' text={time.analogue.minute_marks} />
</React.Fragment>
);
switch (this.state.timeType) {
case 'digital': timeSettings = digitalSettings; break;
case 'analogue': timeSettings = analogSettings; break;
default: timeSettings = null; break;
}
return (
<div>
<h2>{time.title}</h2>
<Checkbox name='time' text={this.language.enabled} />
<Dropdown label='Type' name='timeType' onChange={() => this.changeType()}>
<option className='choices' value='digital'>{time.digital.title}</option>
<option className='choices' value='analogue'>{time.analogue.title}</option>
<option className='choices' value='percentageComplete'>{time.percentage_complete}</option>
</Dropdown>
{timeSettings}
<h3>{time.date.title}</h3>
<Checkbox name='date' text={this.language.enabled} />
<Checkbox name='dayofweek' text={time.date.day_of_week} />
<Checkbox name='weeknumber' text={time.date.week_number} />
<Checkbox name='datenth' text={time.date.datenth} />
<Checkbox name='short' text={time.date.short_date} betaFeature={true} />
<Dropdown label={time.date.short_format} name='dateFormat'>
<option className='choices' value='DMY'>DMY</option>
<option className='choices' value='MDY'>MDY</option>
<option className='choices' value='YMD'>YMD</option>
</Dropdown>
<br/>
<br/>
<Dropdown label={time.date.short_separator.title} name='shortFormat'>
<option className='choices' value='dots'>{time.date.short_separator.dots}</option>
<option className='choices' value='dash'>{time.date.short_separator.dash}</option>
<option className='choices' value='gaps'>{time.date.short_separator.gaps}</option>
<option className='choices' value='slashes'>{time.date.short_separator.slashes}</option>
</Dropdown>
</div>
);
}
}