7.x Structural Changes (#637)

* refactor(files): Initial commit on experimental file structure

* refactor(structure): New components system

* refactor(structure): Tidy settings' components

* Refactor(structure): Component exports and imports

* refactor(settings): Use new component imports

* feat: unified background.js script

* fix(build): Partially, distributions still not ready

* feat: critical error on noscript, light theme support for it

* fix(background): Critical issue of code making every background #000

* refactor(welcome): Partition into different files + shared components
- This took too long and destroyed my sanity

---------

Co-authored-by: alexsparkes <turbomarshmello@gmail.com>
Co-authored-by: alexsparkes <alexsparkes@gmail.com>
This commit is contained in:
David Ralph
2024-02-18 23:05:15 +00:00
committed by GitHub
parent 8fc6b1bf1b
commit 10f12b20c5
233 changed files with 979 additions and 426 deletions

View File

@@ -0,0 +1,61 @@
import variables from 'config/variables';
import { PureComponent } from 'react';
import { Checkbox as CheckboxUI, FormControlLabel } from '@mui/material';
import EventBus from 'modules/helpers/eventbus';
class Checkbox extends PureComponent {
constructor(props) {
super(props);
this.state = {
checked: localStorage.getItem(this.props.name) === 'true',
};
}
handleChange = () => {
const value = this.state.checked !== true;
localStorage.setItem(this.props.name, value);
this.setState({
checked: value,
});
if (this.props.onChange) {
this.props.onChange(value);
}
variables.stats.postEvent(
'setting',
`${this.props.name} ${this.state.checked === true ? 'enabled' : 'disabled'}`,
);
if (this.props.element) {
if (!document.querySelector(this.props.element)) {
document.querySelector('.reminder-info').style.display = 'flex';
return localStorage.setItem('showReminder', true);
}
}
EventBus.emit('refresh', this.props.category);
};
render() {
return (
<FormControlLabel
control={
<CheckboxUI
name={this.props.name}
color="primary"
className="checkbox"
checked={this.state.checked}
onChange={this.handleChange}
disabled={this.props.disabled || false}
/>
}
label={this.props.text}
/>
);
}
}
export { Checkbox as default, Checkbox };

View File

@@ -0,0 +1 @@
export * from './Checkbox';

View File

@@ -0,0 +1,58 @@
import { useState, memo } from 'react';
import Box from '@mui/material/Box';
import OutlinedInput from '@mui/material/OutlinedInput';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';
import Chip from '@mui/material/Chip';
function ChipSelect({ label, options }) {
let start = (localStorage.getItem('apiCategories') || '').split(',');
if (start[0] === '') {
start = [];
}
const [optionsSelected, setoptionsSelected] = useState(start);
const handleChange = (event) => {
const {
target: { value },
} = event;
setoptionsSelected(typeof value === 'string' ? value.split(',') : value);
localStorage.setItem('apiCategories', value);
};
return (
<div>
<FormControl>
<InputLabel id="chipSelect-label">{label}</InputLabel>
<Select
labelId="chipSelect-label"
id="chipSelect"
multiple
value={optionsSelected}
onChange={handleChange}
input={<OutlinedInput id="select-multiple-chip" label={label} />}
renderValue={(optionsSelected) => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{optionsSelected.map((value) => (
<Chip key={value} label={value} />
))}
</Box>
)}
>
{options.map((option) => (
<MenuItem key={option.name} value={option.name}>
{option.name.charAt(0).toUpperCase() + option.name.slice(1)}{' '}
{option.count && `(${option.count})`}
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}
export default memo(ChipSelect);

View File

@@ -0,0 +1 @@
export * from './ChipSelect';

View File

@@ -0,0 +1,76 @@
import variables from 'config/variables';
import { PureComponent, createRef } from 'react';
import { InputLabel, MenuItem, FormControl, Select } from '@mui/material';
import EventBus from 'modules/helpers/eventbus';
class Dropdown extends PureComponent {
constructor(props) {
super(props);
this.state = {
value: localStorage.getItem(this.props.name) || this.props.items[0].value,
title: '',
};
this.dropdown = createRef();
}
onChange = (e) => {
const { value } = e.target;
if (value === variables.getMessage('modals.main.loading')) {
return;
}
variables.stats.postEvent('setting', `${this.props.name} from ${this.state.value} to ${value}`);
this.setState({
value,
});
if (!this.props.noSetting) {
localStorage.setItem(this.props.name, value);
localStorage.setItem(this.props.name2, this.props.value2);
}
if (this.props.onChange) {
this.props.onChange(value);
}
if (this.props.element) {
if (!document.querySelector(this.props.element)) {
document.querySelector('.reminder-info').style.display = 'flex';
return localStorage.setItem('showReminder', true);
}
}
EventBus.emit('refresh', this.props.category);
};
render() {
const id = 'dropdown' + this.props.name;
const label = this.props.label || '';
return (
<FormControl fullWidth className={id}>
<InputLabel id={id}>{label}</InputLabel>
<Select
labelId={id}
id={this.props.name}
value={this.state.value}
label={label}
onChange={this.onChange}
ref={this.dropdown}
key={id}
>
{this.props.items.map(({ value, text }) => (
<MenuItem key={id + value} value={value}>
{text}
</MenuItem>
))}
</Select>
</FormControl>
);
}
}
export { Dropdown as default, Dropdown };

View File

@@ -0,0 +1 @@
export * from './Dropdown';

View File

@@ -0,0 +1,65 @@
import variables from 'config/variables';
import { PureComponent } from 'react';
import { toast } from 'react-toastify';
import { compressAccurately, filetoDataURL } from 'image-conversion';
import { videoCheck } from 'modules/helpers/background/widget';
class FileUpload extends 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');
reader.onload = (e) => {
return this.props.loadFunction(e.target.result);
};
} else {
// background upload
const settings = {};
Object.keys(localStorage).forEach((key) => {
settings[key] = localStorage.getItem(key);
});
const settingsSize = new TextEncoder().encode(JSON.stringify(settings)).length;
if (videoCheck(file.type) === true) {
if (settingsSize + file.size > 4850000) {
return toast(variables.getMessage('toasts.no_storage'));
}
return this.props.loadFunction(file);
}
compressAccurately(file, {
size: 450,
accuracy: 0.9,
}).then(async (res) => {
if (settingsSize + res.size > 4850000) {
return toast(variables.getMessage('toasts.no_storage'));
}
this.props.loadFunction({
target: {
result: await filetoDataURL(res),
},
});
});
}
};
}
render() {
return (
<input
id={this.props.id}
type="file"
style={{ display: 'none' }}
accept={this.props.accept}
/>
);
}
}
export { FileUpload as default, FileUpload };

View File

@@ -0,0 +1 @@
export * from './FileUpload';

View File

@@ -0,0 +1,87 @@
import variables from 'config/variables';
import { PureComponent } from 'react';
import {
Radio as RadioUI,
RadioGroup,
FormControlLabel,
FormControl,
FormLabel,
} from '@mui/material';
import EventBus from 'modules/helpers/eventbus';
import { translations } from 'modules/translations';
class Radio extends PureComponent {
constructor(props) {
super(props);
this.state = {
value: localStorage.getItem(this.props.name),
};
}
handleChange = async (e) => {
const { value } = e.target;
if (value === 'loading') {
return;
}
if (this.props.name === 'language') {
// old tab name
if (localStorage.getItem('tabName') === variables.getMessage('tabname')) {
localStorage.setItem('tabName', translations[value.replace('-', '_')].tabname);
}
}
localStorage.setItem(this.props.name, value);
this.setState({
value,
});
if (this.props.onChange) {
this.props.onChange(value);
}
variables.stats.postEvent('setting', `${this.props.name} from ${this.state.value} to ${value}`);
if (this.props.element) {
if (!document.querySelector(this.props.element)) {
document.querySelector('.reminder-info').style.display = 'flex';
return localStorage.setItem('showReminder', true);
}
}
EventBus.emit('refresh', this.props.category);
};
render() {
return (
<FormControl component="fieldset">
<FormLabel
className={this.props.smallTitle ? 'radio-title-small' : 'radio-title'}
component="legend"
>
{this.props.title}
</FormLabel>
<RadioGroup
aria-label={this.props.name}
name={this.props.name}
onChange={this.handleChange}
value={this.state.value}
>
{this.props.options.map((option) => (
<FormControlLabel
value={option.value}
control={<RadioUI />}
label={option.name}
key={option.name}
/>
))}
</RadioGroup>
</FormControl>
);
}
}
export { Radio as default, Radio };

View File

@@ -0,0 +1 @@
export * from './Radio';

View File

@@ -0,0 +1,88 @@
import variables from 'config/variables';
import { PureComponent } from 'react';
import { toast } from 'react-toastify';
import { Slider } from '@mui/material';
import { MdRefresh } from 'react-icons/md';
import EventBus from 'modules/helpers/eventbus';
class SliderComponent extends PureComponent {
constructor(props) {
super(props);
this.state = {
value: localStorage.getItem(this.props.name) || this.props.default,
};
}
handleChange = (e, text) => {
let { value } = e.target;
value = Number(value);
if (text) {
if (value === '') {
return this.setState({
value: 0,
});
}
if (value > this.props.max) {
value = this.props.max;
}
if (value < this.props.min) {
value = this.props.min;
}
}
localStorage.setItem(this.props.name, value);
this.setState({
value,
});
if (this.props.element) {
if (!document.querySelector(this.props.element)) {
document.querySelector('.reminder-info').style.display = 'flex';
return localStorage.setItem('showReminder', true);
}
}
EventBus.emit('refresh', this.props.category);
};
resetItem = () => {
this.handleChange({
target: {
value: this.props.default || '',
},
});
toast(variables.getMessage('toasts.reset'));
};
render() {
return (
<>
<span className={'sliderTitle'}>
{this.props.title}
<span>{Number(this.state.value)}</span>
<span className="link" onClick={this.resetItem}>
<MdRefresh />
{variables.getMessage('modals.main.settings.buttons.reset')}
</span>
</span>
<Slider
value={Number(this.state.value)}
onChange={this.handleChange}
valueLabelDisplay="auto"
default={Number(this.props.default)}
min={Number(this.props.min)}
max={Number(this.props.max)}
step={Number(this.props.step) || 1}
getAriaValueText={(value) => `${value}`}
marks={this.props.marks || []}
/>
</>
);
}
}
export { SliderComponent as default, SliderComponent as Slider };

View File

@@ -0,0 +1 @@
export * from './Slider';

View File

@@ -0,0 +1,56 @@
import variables from 'config/variables';
import { PureComponent } from 'react';
import { Switch as SwitchUI, FormControlLabel } from '@mui/material';
import EventBus from 'modules/helpers/eventbus';
class Switch extends PureComponent {
constructor(props) {
super(props);
this.state = {
checked: localStorage.getItem(this.props.name) === 'true',
};
}
handleChange = () => {
const value = this.state.checked !== true;
localStorage.setItem(this.props.name, value);
this.setState({
checked: value,
});
variables.stats.postEvent(
'setting',
`${this.props.name} ${this.state.checked === true ? 'enabled' : 'disabled'}`,
);
if (this.props.element) {
if (!document.querySelector(this.props.element)) {
document.querySelector('.reminder-info').style.display = 'flex';
return localStorage.setItem('showReminder', true);
}
}
EventBus.emit('refresh', this.props.category);
};
render() {
return (
<FormControlLabel
control={
<SwitchUI
name={this.props.name}
color="primary"
checked={this.state.checked}
onChange={this.handleChange}
/>
}
label={this.props.header ? '' : this.props.text}
labelPlacement="start"
/>
);
}
}
export { Switch as default, Switch };

View File

@@ -0,0 +1 @@
export * from './Switch';

View File

@@ -0,0 +1,84 @@
import variables from 'config/variables';
import { PureComponent } from 'react';
import { toast } from 'react-toastify';
import { TextField } from '@mui/material';
import { MdRefresh } from 'react-icons/md';
import EventBus from 'modules/helpers/eventbus';
class Text extends PureComponent {
constructor(props) {
super(props);
this.state = {
value: localStorage.getItem(this.props.name) || '',
};
}
handleChange = (e) => {
let { value } = e.target;
// Alex wanted font to work with montserrat and Montserrat, so I made it work
if (this.props.upperCaseFirst === true) {
value = value.charAt(0).toUpperCase() + value.slice(1);
}
localStorage.setItem(this.props.name, value);
this.setState({
value,
});
if (this.props.element) {
if (!document.querySelector(this.props.element)) {
document.querySelector('.reminder-info').style.display = 'flex';
return localStorage.setItem('showReminder', true);
}
}
EventBus.emit('refresh', this.props.category);
};
resetItem = () => {
this.handleChange({
target: {
value: this.props.default || '',
},
});
toast(variables.getMessage('toasts.reset'));
};
render() {
return (
<>
{this.props.textarea === true ? (
<TextField
label={this.props.title}
value={this.state.value}
onChange={this.handleChange}
varient="outlined"
className={this.props.customcss ? 'customcss' : ''}
multiline
spellCheck={false}
minRows={4}
maxRows={10}
InputLabelProps={{ shrink: true }}
/>
) : (
<TextField
label={this.props.title}
value={this.state.value}
onChange={this.handleChange}
varient="outlined"
InputLabelProps={{ shrink: true }}
placeholder={this.props.placeholder || ''}
/>
)}
<span className="link" onClick={this.resetItem}>
<MdRefresh />
{variables.getMessage('modals.main.settings.buttons.reset')}
</span>
</>
);
}
}
export { Text as default, Text };

View File

@@ -0,0 +1 @@
export * from './Text';

View File

@@ -0,0 +1,8 @@
export * from './Checkbox';
export * from './ChipSelect';
export * from './Dropdown';
export * from './FileUpload';
export * from './Radio';
export * from './Slider';
export * from './Switch';
export * from './Text';