Refactor state updates for consistency and readability across components

- Simplified state updates in Background, Favourite, PhotoInformation, GreetingOptions, Added, Browse, About, Overview, Navbar, Apps, Notes, QuickLinksOptions, Quote, and QuoteOptions components by consolidating multiple lines into single line updates.
- Improved readability by reducing unnecessary line breaks and maintaining consistent formatting in JSX elements.
- Enhanced maintainability by ensuring uniformity in how state is set and how JSX is structured.
This commit is contained in:
alexsparkes
2025-10-27 23:27:37 +00:00
parent 6f05e3bf03
commit 467adcdd85
18 changed files with 296 additions and 480 deletions

View File

@@ -24,11 +24,7 @@ export default [
languageOptions: { languageOptions: {
ecmaVersion: 2024, ecmaVersion: 2024,
sourceType: 'module', sourceType: 'module',
parserOptions: { parserOptions: { ecmaFeatures: { jsx: true } },
ecmaFeatures: {
jsx: true,
},
},
globals: { globals: {
// Browser globals // Browser globals
window: 'readonly', window: 'readonly',
@@ -60,16 +56,8 @@ export default [
require: 'readonly', require: 'readonly',
}, },
}, },
plugins: { plugins: { react, 'react-hooks': reactHooks, 'jsx-a11y': jsxA11y },
react, settings: { react: { version: 'detect' } },
'react-hooks': reactHooks,
'jsx-a11y': jsxA11y,
},
settings: {
react: {
version: 'detect',
},
},
rules: { rules: {
...js.configs.recommended.rules, ...js.configs.recommended.rules,
...react.configs.recommended.rules, ...react.configs.recommended.rules,
@@ -84,7 +72,7 @@ export default [
// General rules // General rules
'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], 'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
'no-console': ['warn', { allow: ['warn', 'error'] }], 'no-console': ['warn', { allow: ['warn', 'error'] }],
// Modern JS // Modern JS
'prefer-const': 'warn', 'prefer-const': 'warn',
'no-var': 'error', 'no-var': 'error',

View File

@@ -1,4 +1,3 @@
// todo: rewrite this mess // todo: rewrite this mess
import variables from 'config/variables'; import variables from 'config/variables';
import { PureComponent } from 'react'; import { PureComponent } from 'react';
@@ -24,12 +23,7 @@ export default class Background extends PureComponent {
url: '', url: '',
currentAPI: '', currentAPI: '',
firstTime: false, firstTime: false,
photoInfo: { photoInfo: { hidden: false, offline: false, photographerURL: '', photoURL: '' },
hidden: false,
offline: false,
photographerURL: '',
photoURL: '',
},
}; };
} }
@@ -164,22 +158,9 @@ export default class Background extends PureComponent {
const setFavourited = ({ type, url, credit, location, camera, pun, offline }) => { const setFavourited = ({ type, url, credit, location, camera, pun, offline }) => {
if (type === 'random_colour' || type === 'random_gradient') { if (type === 'random_colour' || type === 'random_gradient') {
return this.setState({ return this.setState({ type: 'colour', style: `background:${url}` });
type: 'colour',
style: `background:${url}`,
});
} }
this.setState({ this.setState({ url, photoInfo: { credit, location, camera, pun, offline, url } });
url,
photoInfo: {
credit,
location,
camera,
pun,
offline,
url,
},
});
}; };
const favourited = JSON.parse(localStorage.getItem('favourite')); const favourited = JSON.parse(localStorage.getItem('favourite'));
@@ -195,7 +176,8 @@ export default class Background extends PureComponent {
} }
// API background // API background
const data = JSON.parse(localStorage.getItem('nextImage')) || (await this.getAPIImageData()); const data =
JSON.parse(localStorage.getItem('nextImage')) || (await this.getAPIImageData());
localStorage.setItem('nextImage', null); localStorage.setItem('nextImage', null);
if (data) { if (data) {
this.setState(data); this.setState(data);
@@ -263,9 +245,7 @@ export default class Background extends PureComponent {
url: customBackground, url: customBackground,
type: 'custom', type: 'custom',
video: videoCheck(customBackground), video: videoCheck(customBackground),
photoInfo: { photoInfo: { hidden: true },
hidden: true,
},
}; };
this.setState(object); this.setState(object);
@@ -363,15 +343,7 @@ export default class Background extends PureComponent {
// this resets it so the fade in and getting background all works properly // this resets it so the fade in and getting background all works properly
const refresh = () => { const refresh = () => {
element.classList.remove('fade-in'); element.classList.remove('fade-in');
this.setState({ this.setState({ url: '', style: '', type: '', video: false, photoInfo: { hidden: true } });
url: '',
style: '',
type: '',
video: false,
photoInfo: {
hidden: true,
},
});
this.getBackground(); this.getBackground();
}; };

View File

@@ -20,9 +20,7 @@ class Favourite extends PureComponent {
async favourite() { async favourite() {
if (localStorage.getItem('favourite')) { if (localStorage.getItem('favourite')) {
localStorage.removeItem('favourite'); localStorage.removeItem('favourite');
this.setState({ this.setState({ favourited: this.buttons.unfavourited });
favourited: this.buttons.unfavourited,
});
this.props.tooltipText(variables.getMessage('widgets.quote.favourite')); this.props.tooltipText(variables.getMessage('widgets.quote.favourite'));
variables.stats.postEvent('feature', 'Background favourite'); variables.stats.postEvent('feature', 'Background favourite');
} else { } else {
@@ -62,13 +60,7 @@ class Favourite extends PureComponent {
} }
if (type === 'custom') { if (type === 'custom') {
localStorage.setItem( localStorage.setItem('favourite', JSON.stringify({ type, url }));
'favourite',
JSON.stringify({
type,
url,
}),
);
} else { } else {
// photo information now hides information if it isn't sent, unless if photoinformation hover is hidden // photo information now hides information if it isn't sent, unless if photoinformation hover is hidden
const location = document.getElementById('infoLocation'); const location = document.getElementById('infoLocation');
@@ -92,9 +84,7 @@ class Favourite extends PureComponent {
} }
} }
this.setState({ this.setState({ favourited: this.buttons.favourited });
favourited: this.buttons.favourited,
});
this.props.tooltipText(variables.getMessage('widgets.quote.unfavourite')); this.props.tooltipText(variables.getMessage('widgets.quote.unfavourite'));
variables.stats.postEvent('feature', 'Background unfavourite'); variables.stats.postEvent('feature', 'Background unfavourite');
} }

View File

@@ -170,12 +170,7 @@ function PhotoInformation({ info, url, api }) {
<Source /> <Source />
<span id="infoSource"> <span id="infoSource">
{info.photoURL ? ( {info.photoURL ? (
<a <a href={info.photoURL} target="_blank" rel="noopener noreferrer" className="link">
href={info.photoURL}
target="_blank"
rel="noopener noreferrer"
className="link"
>
{api.charAt(0).toUpperCase() + api.slice(1)} {api.charAt(0).toUpperCase() + api.slice(1)}
</a> </a>
) : ( ) : (

View File

@@ -50,12 +50,7 @@ const GreetingOptions = () => {
const customEvents = JSON.parse(localStorage.getItem('customEvents')) || []; const customEvents = JSON.parse(localStorage.getItem('customEvents')) || [];
// Create a new event // Create a new event
const newEvent = { const newEvent = { id: 'widgets.greeting.halloween', name: '', month: 1, date: 1 };
id: 'widgets.greeting.halloween',
name: '',
month: 1,
date: 1,
};
// Add the new event to the array // Add the new event to the array
const updatedEvents = [...customEvents, newEvent]; const updatedEvents = [...customEvents, newEvent];

View File

@@ -62,18 +62,13 @@ export default class Added extends PureComponent {
} }
if (failedReason !== '' && this.state.showFailed === false) { if (failedReason !== '' && this.state.showFailed === false) {
return this.setState({ return this.setState({ failedReason, showFailed: true });
failedReason,
showFailed: true,
});
} }
install(input.type, input, true, false); install(input.type, input, true, false);
toast(variables.getMessage('toasts.installed')); toast(variables.getMessage('toasts.installed'));
variables.stats.postEvent('marketplace', 'Sideload'); variables.stats.postEvent('marketplace', 'Sideload');
this.setState({ this.setState({ installed: JSON.parse(localStorage.getItem('installed')) });
installed: JSON.parse(localStorage.getItem('installed')),
});
} }
getSideloadButton() { getSideloadButton() {
@@ -91,9 +86,7 @@ export default class Added extends PureComponent {
toggle(type, data) { toggle(type, data) {
if (type === 'item') { if (type === 'item') {
const installed = JSON.parse(localStorage.getItem('installed')); const installed = JSON.parse(localStorage.getItem('installed'));
const info = { const info = { data: installed.find((i) => i.name === data.name) };
data: installed.find((i) => i.name === data.name),
};
this.setState({ this.setState({
item: { item: {
@@ -110,9 +103,7 @@ export default class Added extends PureComponent {
}); });
variables.stats.postEvent('marketplace', 'ItemPage viewed'); variables.stats.postEvent('marketplace', 'ItemPage viewed');
} else { } else {
this.setState({ this.setState({ item: {} });
item: {},
});
} }
} }
@@ -157,9 +148,7 @@ export default class Added extends PureComponent {
break; break;
} }
this.setState({ this.setState({ installed });
installed,
});
if (sendEvent) { if (sendEvent) {
variables.stats.postEvent('marketplace', 'Sort'); variables.stats.postEvent('marketplace', 'Sort');
@@ -178,11 +167,7 @@ export default class Added extends PureComponent {
}); });
if (updates > 0) { if (updates > 0) {
toast( toast(variables.getMessage('modals.main.addons.updates_available', { amount: updates }));
variables.getMessage('modals.main.addons.updates_available', {
amount: updates,
}),
);
} else { } else {
toast(variables.getMessage('modals.main.addons.no_updates')); toast(variables.getMessage('modals.main.addons.no_updates'));
} }
@@ -201,9 +186,7 @@ export default class Added extends PureComponent {
toast(variables.getMessage('toasts.uninstalled_all')); toast(variables.getMessage('toasts.uninstalled_all'));
this.setState({ this.setState({ installed: [] });
installed: [],
});
this.forceUpdate(); this.forceUpdate();
} }
@@ -301,22 +284,10 @@ export default class Added extends PureComponent {
name="sortAddons" name="sortAddons"
onChange={(value) => this.sortAddons(value)} onChange={(value) => this.sortAddons(value)}
items={[ items={[
{ { value: 'newest', text: variables.getMessage('modals.main.addons.sort.newest') },
value: 'newest', { value: 'oldest', text: variables.getMessage('modals.main.addons.sort.oldest') },
text: variables.getMessage('modals.main.addons.sort.newest'), { value: 'a-z', text: variables.getMessage('modals.main.addons.sort.a_z') },
}, { value: 'z-a', text: variables.getMessage('modals.main.addons.sort.z_a') },
{
value: 'oldest',
text: variables.getMessage('modals.main.addons.sort.oldest'),
},
{
value: 'a-z',
text: variables.getMessage('modals.main.addons.sort.a_z'),
},
{
value: 'z-a',
text: variables.getMessage('modals.main.addons.sort.z_a'),
},
]} ]}
/> />
<Items <Items

View File

@@ -21,14 +21,7 @@ import { install, urlParser, uninstall } from 'utils/marketplace';
class Marketplace extends PureComponent { class Marketplace extends PureComponent {
constructor() { constructor() {
super(); super();
this.state = { this.state = { items: [], button: '', done: false, item: {}, collection: false, filter: '' };
items: [],
button: '',
done: false,
item: {},
collection: false,
filter: '',
};
this.buttons = { this.buttons = {
uninstall: ( uninstall: (
<Button <Button
@@ -112,10 +105,7 @@ class Marketplace extends PureComponent {
document.querySelector('#modal').scrollTop = 0; document.querySelector('#modal').scrollTop = 0;
variables.stats.postEvent('marketplace-item', `${this.state.item.display_name} viewed`); variables.stats.postEvent('marketplace-item', `${this.state.item.display_name} viewed`);
} else if (pageType === 'collection') { } else if (pageType === 'collection') {
this.setState({ this.setState({ done: false, item: {} });
done: false,
item: {},
});
const collection = await ( const collection = await (
await fetch(`${variables.constants.API_URL}/marketplace/collection/${data}`, { await fetch(`${variables.constants.API_URL}/marketplace/collection/${data}`, {
signal: this.controller.signal, signal: this.controller.signal,
@@ -130,25 +120,17 @@ class Marketplace extends PureComponent {
done: true, done: true,
}); });
} else { } else {
this.setState({ this.setState({ item: {} });
item: {},
});
} }
} }
async getItems() { async getItems() {
this.setState({ this.setState({ done: false });
done: false,
});
const dataURL = const dataURL =
this.props.type === 'collections' this.props.type === 'collections'
? variables.constants.API_URL + '/marketplace/collections' ? variables.constants.API_URL + '/marketplace/collections'
: variables.constants.API_URL + '/marketplace/items/' + this.props.type; : variables.constants.API_URL + '/marketplace/items/' + this.props.type;
const { data } = await ( const { data } = await (await fetch(dataURL, { signal: this.controller.signal })).json();
await fetch(dataURL, {
signal: this.controller.signal,
})
).json();
const collections = await ( const collections = await (
await fetch(variables.constants.API_URL + '/marketplace/collections', { await fetch(variables.constants.API_URL + '/marketplace/collections', {
signal: this.controller.signal, signal: this.controller.signal,
@@ -180,9 +162,7 @@ class Marketplace extends PureComponent {
} }
toast(variables.getMessage('toasts.' + type + 'ed')); toast(variables.getMessage('toasts.' + type + 'ed'));
this.setState({ this.setState({ button: type === 'install' ? this.buttons.uninstall : this.buttons.install });
button: type === 'install' ? this.buttons.uninstall : this.buttons.install,
});
variables.stats.postEvent( variables.stats.postEvent(
'marketplace-item', 'marketplace-item',
@@ -250,10 +230,7 @@ class Marketplace extends PureComponent {
variables.stats.postEvent('marketplace', 'Sort'); variables.stats.postEvent('marketplace', 'Sort');
} }
return { return { items: items, sortType: value };
items: items,
sortType: value,
};
} }
changeSort(value) { changeSort(value) {
@@ -262,10 +239,7 @@ class Marketplace extends PureComponent {
} }
returnToMain() { returnToMain() {
this.setState({ this.setState({ items: this.state.oldItems, collection: false });
items: this.state.oldItems,
collection: false,
});
} }
componentDidMount() { componentDidMount() {
@@ -411,14 +385,8 @@ class Marketplace extends PureComponent {
name="sortMarketplace" name="sortMarketplace"
onChange={(value) => this.changeSort(value)} onChange={(value) => this.changeSort(value)}
items={[ items={[
{ { value: 'a-z', text: variables.getMessage('modals.main.addons.sort.a_z') },
value: 'a-z', { value: 'z-a', text: variables.getMessage('modals.main.addons.sort.z_a') },
text: variables.getMessage('modals.main.addons.sort.a_z'),
},
{
value: 'z-a',
text: variables.getMessage('modals.main.addons.sort.z_a'),
},
]} ]}
/> />
</div> </div>

View File

@@ -345,11 +345,7 @@ class About extends PureComponent {
</div> </div>
<div <div
className="settingsRow" className="settingsRow"
style={{ style={{ flexFlow: 'column', alignItems: 'flex-start', minHeight: '10px' }}
flexFlow: 'column',
alignItems: 'flex-start',
minHeight: '10px',
}}
> >
<span className="title"> <span className="title">
{variables.getMessage('modals.main.settings.sections.about.photographers')} {variables.getMessage('modals.main.settings.sections.about.photographers')}

View File

@@ -40,10 +40,7 @@ const widget_name = {
const SortableItem = ({ id }) => { const SortableItem = ({ id }) => {
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id }); const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id });
const style = { const style = { transform: CSS.Transform.toString(transform), transition };
transform: CSS.Transform.toString(transform),
transition,
};
return ( return (
<li ref={setNodeRef} style={style} {...attributes} {...listeners} className="sortableItem"> <li ref={setNodeRef} style={style} {...attributes} {...listeners} className="sortableItem">
@@ -77,9 +74,7 @@ const Overview = () => {
const sensors = useSensors( const sensors = useSensors(
useSensor(PointerSensor), useSensor(PointerSensor),
useSensor(KeyboardSensor, { useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),
coordinateGetter: sortableKeyboardCoordinates,
}),
); );
const handleDragEnd = (event) => { const handleDragEnd = (event) => {
@@ -135,11 +130,7 @@ const Overview = () => {
const data = await response.json(); const data = await response.json();
data.date = new window.Date(data.date).toLocaleDateString( data.date = new window.Date(data.date).toLocaleDateString(
variables.languagecode.replace('_', '-'), variables.languagecode.replace('_', '-'),
{ { year: 'numeric', month: 'long', day: 'numeric' },
year: 'numeric',
month: 'long',
day: 'numeric',
},
); );
setNews(data); setNews(data);
setNewsDone(true); setNewsDone(true);
@@ -187,7 +178,11 @@ const Overview = () => {
<span className="title"> <span className="title">
{variables.getMessage('modals.main.settings.sections.order.title')} {variables.getMessage('modals.main.settings.sections.order.title')}
</span> </span>
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}> <DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
>
<SortableContext items={items} strategy={verticalListSortingStrategy}> <SortableContext items={items} strategy={verticalListSortingStrategy}>
<ul className="sortableContainer"> <ul className="sortableContainer">
{items.map((value) => { {items.map((value) => {

View File

@@ -51,9 +51,7 @@ class Navbar extends PureComponent {
break; break;
} }
this.setState({ this.setState({ refreshText });
refreshText,
});
} }
componentDidMount() { componentDidMount() {

View File

@@ -45,15 +45,11 @@ class Apps extends PureComponent {
} }
showApps() { showApps() {
this.setState({ this.setState({ showApps: true });
showApps: true,
});
} }
hideApps() { hideApps() {
this.setState({ this.setState({ showApps: localStorage.getItem('AppsPinned') === 'true' });
showApps: localStorage.getItem('AppsPinned') === 'true',
});
} }
render() { render() {
@@ -137,9 +133,7 @@ function AppsWrapper() {
const { x, y, refs, strategy } = useFloating({ const { x, y, refs, strategy } = useFloating({
placement: 'bottom', placement: 'bottom',
middleware: [shift()], middleware: [shift()],
elements: { elements: { reference },
reference,
},
}); });
return ( return (

View File

@@ -47,30 +47,22 @@ class Notes extends PureComponent {
setNotes = (e) => { setNotes = (e) => {
localStorage.setItem('notes', e.target.value); localStorage.setItem('notes', e.target.value);
this.setState({ this.setState({ notes: e.target.value });
notes: e.target.value,
});
}; };
showNotes() { showNotes() {
this.setState({ this.setState({ showNotes: true });
showNotes: true,
});
} }
hideNotes() { hideNotes() {
this.setState({ this.setState({ showNotes: localStorage.getItem('notesPinned') === 'true' });
showNotes: localStorage.getItem('notesPinned') === 'true',
});
} }
pin() { pin() {
variables.stats.postEvent('feature', 'Notes pin'); variables.stats.postEvent('feature', 'Notes pin');
const notesPinned = localStorage.getItem('notesPinned') === 'true'; const notesPinned = localStorage.getItem('notesPinned') === 'true';
localStorage.setItem('notesPinned', !notesPinned); localStorage.setItem('notesPinned', !notesPinned);
this.setState({ this.setState({ showNotes: !notesPinned });
showNotes: !notesPinned,
});
} }
copy() { copy() {
@@ -156,9 +148,7 @@ function NotesWrapper() {
const { x, y, refs, strategy } = useFloating({ const { x, y, refs, strategy } = useFloating({
placement: 'bottom', placement: 'bottom',
middleware: [shift()], middleware: [shift()],
elements: { elements: { reference },
reference,
},
}); });
return ( return (

View File

@@ -59,7 +59,10 @@ const SortableItem = ({ value, enabled, startEditLink, deleteLink }) => {
}; };
const getIconUrl = (item) => { const getIconUrl = (item) => {
return item.icon || 'https://icon.horse/icon/' + item.url.replace('https://', '').replace('http://', ''); return (
item.icon ||
'https://icon.horse/icon/' + item.url.replace('https://', '').replace('http://', '')
);
}; };
return ( return (
@@ -115,14 +118,8 @@ const SortableItem = ({ value, enabled, startEditLink, deleteLink }) => {
const SortableList = ({ items, enabled, onDragEnd, startEditLink, deleteLink }) => { const SortableList = ({ items, enabled, onDragEnd, startEditLink, deleteLink }) => {
const sensors = useSensors( const sensors = useSensors(
useSensor(PointerSensor, { useSensor(PointerSensor, { activationConstraint: { distance: 6 } }),
activationConstraint: { useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),
distance: 6,
},
}),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
}),
); );
return ( return (
@@ -162,85 +159,88 @@ class QuickLinksOptions extends PureComponent {
} }
setContainerDisplay(enabled) { setContainerDisplay(enabled) {
if (!this.quicklinksContainer || !this.quicklinksContainer.current) return; if (!this.quicklinksContainer || !this.quicklinksContainer.current) return;
const el = this.quicklinksContainer.current; const el = this.quicklinksContainer.current;
el.classList.toggle('disabled', !enabled); el.classList.toggle('disabled', !enabled);
if (!enabled) { if (!enabled) {
el.setAttribute('aria-hidden', 'true'); el.setAttribute('aria-hidden', 'true');
} else { } else {
el.removeAttribute('aria-hidden'); el.removeAttribute('aria-hidden');
}
} }
}
deleteLink(key, event) { deleteLink(key, event) {
event.preventDefault(); event.preventDefault();
const stored = readQuicklinks();
const data = stored.filter((i) => i.key !== key);
this.silenceEvent = true;
localStorage.setItem('quicklinks', JSON.stringify(data));
this.setState({ items: data }, () => {
variables.stats.postEvent('feature', 'Quicklink delete');
EventBus.emit('refresh', 'quicklinks');
setTimeout(() => { this.silenceEvent = false; }, 0);
});
}
const stored = readQuicklinks();
const data = stored.filter((i) => i.key !== key);
this.silenceEvent = true;
localStorage.setItem('quicklinks', JSON.stringify(data));
this.setState({ items: data }, () => {
variables.stats.postEvent('feature', 'Quicklink delete');
EventBus.emit('refresh', 'quicklinks');
setTimeout(() => {
this.silenceEvent = false;
}, 0);
});
}
async addLink(name, url, icon) { async addLink(name, url, icon) {
const data = readQuicklinks(); const data = readQuicklinks();
if (!url.startsWith('http://') && !url.startsWith('https://')) { if (!url.startsWith('http://') && !url.startsWith('https://')) {
url = 'https://' + url; url = 'https://' + url;
}
if (url.length <= 0 || isValidUrl(url) === false) {
return this.setState({ urlError: variables.getMessage('widgets.quicklinks.url_error') });
}
if (icon.length > 0 && isValidUrl(icon) === false) {
return this.setState({ iconError: variables.getMessage('widgets.quicklinks.url_error') });
}
const newItem = {
name: name || (await getTitleFromUrl(url)),
url,
icon: icon || '',
key: Date.now().toString() + Math.random().toString(36).substring(2),
};
data.push(newItem);
this.silenceEvent = true;
localStorage.setItem('quicklinks', JSON.stringify(data));
this.setState({ items: data, showAddModal: false, urlError: '', iconError: '' }, () => {
variables.stats.postEvent('feature', 'Quicklink add');
EventBus.emit('refresh', 'quicklinks');
setTimeout(() => {
this.silenceEvent = false;
}, 0);
});
} }
if (url.length <= 0 || isValidUrl(url) === false) {
return this.setState({ urlError: variables.getMessage('widgets.quicklinks.url_error') });
}
if (icon.length > 0 && isValidUrl(icon) === false) {
return this.setState({ iconError: variables.getMessage('widgets.quicklinks.url_error') });
}
const newItem = {
name: name || (await getTitleFromUrl(url)),
url,
icon: icon || '',
key: Date.now().toString() + Math.random().toString(36).substring(2),
};
data.push(newItem);
this.silenceEvent = true;
localStorage.setItem('quicklinks', JSON.stringify(data));
this.setState({ items: data, showAddModal: false, urlError: '', iconError: '' }, () => {
variables.stats.postEvent('feature', 'Quicklink add');
EventBus.emit('refresh', 'quicklinks');
setTimeout(() => { this.silenceEvent = false; }, 0);
});
}
startEditLink(data) { startEditLink(data) {
this.setState({ edit: true, editData: data, showAddModal: true }); this.setState({ edit: true, editData: data, showAddModal: true });
} }
async editLink(og, name, url, icon) { async editLink(og, name, url, icon) {
const data = readQuicklinks(); const data = readQuicklinks();
const dataobj = data.find((i) => i.key === og.key); const dataobj = data.find((i) => i.key === og.key);
if (!dataobj) return; if (!dataobj) return;
dataobj.name = name || (await getTitleFromUrl(url)); dataobj.name = name || (await getTitleFromUrl(url));
dataobj.url = url; dataobj.url = url;
dataobj.icon = icon || ''; dataobj.icon = icon || '';
this.silenceEvent = true;
localStorage.setItem('quicklinks', JSON.stringify(data));
this.setState({ items: data, showAddModal: false, edit: false }, () => {
EventBus.emit('refresh', 'quicklinks');
setTimeout(() => { this.silenceEvent = false; }, 0);
});
}
this.silenceEvent = true;
localStorage.setItem('quicklinks', JSON.stringify(data));
this.setState({ items: data, showAddModal: false, edit: false }, () => {
EventBus.emit('refresh', 'quicklinks');
setTimeout(() => {
this.silenceEvent = false;
}, 0);
});
}
arrayMove = (array, oldIndex, newIndex) => { arrayMove = (array, oldIndex, newIndex) => {
const result = Array.from(array); const result = Array.from(array);
@@ -251,7 +251,7 @@ class QuickLinksOptions extends PureComponent {
handleDragEnd = (event) => { handleDragEnd = (event) => {
const { active, over } = event; const { active, over } = event;
if (!over || !this.state.enabled) return; if (!over || !this.state.enabled) return;
if (active.id === over.id) return; if (active.id === over.id) return;
@@ -272,51 +272,48 @@ class QuickLinksOptions extends PureComponent {
}); });
}; };
componentDidMount() { componentDidMount() {
this.setContainerDisplay(this.state.enabled); this.setContainerDisplay(this.state.enabled);
this.handleRefresh = (data) => { this.handleRefresh = (data) => {
if (data !== 'quicklinks') return; if (data !== 'quicklinks') return;
if (this.silenceEvent) return; if (this.silenceEvent) return;
const enabled = localStorage.getItem('quicklinksenabled') !== 'false'; const enabled = localStorage.getItem('quicklinksenabled') !== 'false';
const newItems = readQuicklinks(); const newItems = readQuicklinks();
const oldItems = this.state.items || []; const oldItems = this.state.items || [];
const oldKeys = new Set(oldItems.map(i => i.key)); const oldKeys = new Set(oldItems.map((i) => i.key));
const newKeys = new Set(newItems.map(i => i.key)); const newKeys = new Set(newItems.map((i) => i.key));
const keysEqual = const keysEqual =
oldItems.length === newItems.length && oldItems.length === newItems.length && oldItems.every((i) => newKeys.has(i.key));
oldItems.every(i => newKeys.has(i.key));
if (enabled !== this.state.enabled || !keysEqual) { if (enabled !== this.state.enabled || !keysEqual) {
this.setContainerDisplay(enabled); this.setContainerDisplay(enabled);
this.setState({ items: newItems, enabled }); this.setState({ items: newItems, enabled });
} }
}; };
EventBus.on('refresh', this.handleRefresh);
}
EventBus.on('refresh', this.handleRefresh);
}
componentDidUpdate(prevProps, prevState) { componentDidUpdate(prevProps, prevState) {
if (prevState.enabled !== this.state.enabled) { if (prevState.enabled !== this.state.enabled) {
this.setContainerDisplay(this.state.enabled); this.setContainerDisplay(this.state.enabled);
} }
} }
componentWillUnmount() { componentWillUnmount() {
if (this.handleRefresh) { if (this.handleRefresh) {
EventBus.off('refresh', this.handleRefresh); EventBus.off('refresh', this.handleRefresh);
} else { } else {
try { try {
EventBus.off('refresh'); EventBus.off('refresh');
} catch { } catch {
// Ignore errors // Ignore errors
}
} }
} }
}
render() { render() {
const QUICKLINKS_SECTION = 'modals.main.settings.sections.quicklinks'; const QUICKLINKS_SECTION = 'modals.main.settings.sections.quicklinks';
const { enabled } = this.state; const { enabled } = this.state;
@@ -328,8 +325,16 @@ class QuickLinksOptions extends PureComponent {
subtitle={variables.getMessage(`${QUICKLINKS_SECTION}.additional`)} subtitle={variables.getMessage(`${QUICKLINKS_SECTION}.additional`)}
/> />
<Action> <Action>
<Checkbox name="quicklinksnewtab" text={variables.getMessage(`${QUICKLINKS_SECTION}.open_new`)} category="quicklinks" /> <Checkbox
<Checkbox name="quicklinkstooltip" text={variables.getMessage(`${QUICKLINKS_SECTION}.tooltip`)} category="quicklinks" /> name="quicklinksnewtab"
text={variables.getMessage(`${QUICKLINKS_SECTION}.open_new`)}
category="quicklinks"
/>
<Checkbox
name="quicklinkstooltip"
text={variables.getMessage(`${QUICKLINKS_SECTION}.tooltip`)}
category="quicklinks"
/>
</Action> </Action>
</Row> </Row>
); );
@@ -338,7 +343,9 @@ class QuickLinksOptions extends PureComponent {
<Row> <Row>
<Content <Content
title={variables.getMessage(`${QUICKLINKS_SECTION}.styling`)} title={variables.getMessage(`${QUICKLINKS_SECTION}.styling`)}
subtitle={variables.getMessage('modals.main.settings.sections.quicklinks.styling_description')} subtitle={variables.getMessage(
'modals.main.settings.sections.quicklinks.styling_description',
)}
/> />
<Action> <Action>
<Dropdown <Dropdown
@@ -347,7 +354,10 @@ class QuickLinksOptions extends PureComponent {
category="quicklinks" category="quicklinks"
items={[ items={[
{ value: 'icon', text: variables.getMessage(`${QUICKLINKS_SECTION}.options.icon`) }, { value: 'icon', text: variables.getMessage(`${QUICKLINKS_SECTION}.options.icon`) },
{ value: 'text_only', text: variables.getMessage(`${QUICKLINKS_SECTION}.options.text_only`) }, {
value: 'text_only',
text: variables.getMessage(`${QUICKLINKS_SECTION}.options.text_only`),
},
{ value: 'metro', text: variables.getMessage(`${QUICKLINKS_SECTION}.options.metro`) }, { value: 'metro', text: variables.getMessage(`${QUICKLINKS_SECTION}.options.metro`) },
]} ]}
/> />
@@ -395,9 +405,18 @@ class QuickLinksOptions extends PureComponent {
<div className="photosEmpty"> <div className="photosEmpty">
<div className="emptyNewMessage"> <div className="emptyNewMessage">
<MdLinkOff /> <MdLinkOff />
<span className="title">{variables.getMessage(`${QUICKLINKS_SECTION}.no_quicklinks`)}</span> <span className="title">
<span className="subtitle">{variables.getMessage('modals.main.settings.sections.message.add_some')}</span> {variables.getMessage(`${QUICKLINKS_SECTION}.no_quicklinks`)}
<Button type="settings" onClick={() => this.setState({ showAddModal: true })} icon={<MdAddLink />} label={variables.getMessage(`${QUICKLINKS_SECTION}.add_link`)} /> </span>
<span className="subtitle">
{variables.getMessage('modals.main.settings.sections.message.add_some')}
</span>
<Button
type="settings"
onClick={() => this.setState({ showAddModal: true })}
icon={<MdAddLink />}
label={variables.getMessage(`${QUICKLINKS_SECTION}.add_link`)}
/>
</div> </div>
</div> </div>
)} )}
@@ -432,7 +451,9 @@ class QuickLinksOptions extends PureComponent {
editLink={(og, name, url, icon) => this.editLink(og, name, url, icon)} editLink={(og, name, url, icon) => this.editLink(og, name, url, icon)}
edit={this.state.edit} edit={this.state.edit}
editData={this.state.editData} editData={this.state.editData}
closeModal={() => this.setState({ showAddModal: false, urlError: '', iconError: '', edit: false })} closeModal={() =>
this.setState({ showAddModal: false, urlError: '', iconError: '', edit: false })
}
/> />
</Modal> </Modal>
</> </>
@@ -440,4 +461,4 @@ class QuickLinksOptions extends PureComponent {
} }
} }
export { QuickLinksOptions as default, QuickLinksOptions }; export { QuickLinksOptions as default, QuickLinksOptions };

View File

@@ -122,10 +122,7 @@ class Quote extends PureComponent {
async getAuthorImg(author) { async getAuthorImg(author) {
if (localStorage.getItem('authorImg') === 'false') { if (localStorage.getItem('authorImg') === 'false') {
return { return { authorimg: null, authorimglicense: null };
authorimg: null,
authorimglicense: null,
};
} }
const authorimgdata = await ( const authorimgdata = await (
@@ -185,10 +182,7 @@ class Quote extends PureComponent {
authorimglicense = null; authorimglicense = null;
} }
return { return { authorimg, authorimglicense };
authorimg,
authorimglicense,
};
} }
async getQuote() { async getQuote() {
@@ -237,9 +231,7 @@ class Quote extends PureComponent {
noQuote: false, noQuote: false,
}); });
} else { } else {
this.setState({ this.setState({ noQuote: true });
noQuote: true,
});
} }
break; break;
} }
@@ -330,14 +322,10 @@ class Quote extends PureComponent {
favourite() { favourite() {
if (localStorage.getItem('favouriteQuote')) { if (localStorage.getItem('favouriteQuote')) {
localStorage.removeItem('favouriteQuote'); localStorage.removeItem('favouriteQuote');
this.setState({ this.setState({ favourited: this.buttons.unfavourited });
favourited: this.buttons.unfavourited,
});
} else { } else {
localStorage.setItem('favouriteQuote', this.state.quote + ' - ' + this.state.author); localStorage.setItem('favouriteQuote', this.state.quote + ' - ' + this.state.author);
this.setState({ this.setState({ favourited: this.buttons.favourited });
favourited: this.buttons.favourited,
});
} }
variables.stats.postEvent('feature', 'Quote favourite'); variables.stats.postEvent('feature', 'Quote favourite');
@@ -438,85 +426,91 @@ class Quote extends PureComponent {
{localStorage.getItem('widgetStyle') === 'legacy' ? ( {localStorage.getItem('widgetStyle') === 'legacy' ? (
<> <>
{ this.authorDetails && ( {this.authorDetails && (
<> <>
<div> <div>
<h1 className="quoteauthor" ref={this.quoteauthor}> <h1 className="quoteauthor" ref={this.quoteauthor}>
<a <a
href={this.state.authorlink} href={this.state.authorlink}
className="quoteAuthorLink" className="quoteAuthorLink"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
aria-label="Learn about the author of the quote." aria-label="Learn about the author of the quote."
> >
{this.state.author} {this.state.author}
</a> </a>
</h1> </h1>
</div> </div>
<div style={{ display: 'flex', justifyContent: 'center', gap: '20px' }}> <div style={{ display: 'flex', justifyContent: 'center', gap: '20px' }}>
{this.state.copy} {this.state.share} {this.state.favourited} {this.state.copy} {this.state.share} {this.state.favourited}
</div> </div>
</> </>
)} )}
</> </>
) : ( ) : (
<> <>
{ this.authorDetails && ( {this.authorDetails && (
<> <>
<div className="author-holder"> <div className="author-holder">
<div className="author"> <div className="author">
{localStorage.getItem('authorImg') !== 'false' ? ( {localStorage.getItem('authorImg') !== 'false' ? (
<div <div
className="author-img" className="author-img"
style={{ backgroundImage: `url(${this.state.authorimg})` }} style={{ backgroundImage: `url(${this.state.authorimg})` }}
>
{this.state.authorimg === undefined || this.state.authorimg ? '' : <MdPerson />}
</div>
) : null}
{this.state.author !== null ? (
<div className="author-content" ref={this.quoteauthor}>
<span className="title">{this.state.author}</span>
{this.state.authorOccupation !== 'Unknown' && (
<span className="subtitle">{this.state.authorOccupation}</span>
)}
<span className="author-license" title={this.state.authorimglicense}>
{this.state.authorimglicense &&
this.state.authorimglicense.substring(0, 40) +
(this.state.authorimglicense.length > 40 ? '…' : '')}
</span>
</div>
) : (
<div className="author-content whileLoading" ref={this.quoteauthor}>
{/* these are placeholders for skeleton and as such don't need translating */}
<span className="title pulse">loading</span>
<span className="subtitle pulse">loading</span>
</div>
)}
{(this.state.authorOccupation !== 'Unknown' && this.state.authorlink !== null) ||
this.state.copy ||
this.state.share ||
this.state.favourited ? (
<div className="quote-buttons">
{this.state.authorOccupation !== 'Unknown' && this.state.authorlink !== null ? (
<Tooltip title={variables.getMessage('widgets.quote.link_tooltip')}>
<a
href={this.state.authorlink}
className="quoteAuthorLink"
target="_blank"
rel="noopener noreferrer"
aria-label="Learn about the author of the quote."
> >
<MdOpenInNew /> {this.state.authorimg === undefined || this.state.authorimg ? (
</a>{' '} ''
</Tooltip> ) : (
) : null} <MdPerson />
{this.state.copy} {this.state.share} {this.state.favourited} )}
</div>
) : null}
{this.state.author !== null ? (
<div className="author-content" ref={this.quoteauthor}>
<span className="title">{this.state.author}</span>
{this.state.authorOccupation !== 'Unknown' && (
<span className="subtitle">{this.state.authorOccupation}</span>
)}
<span className="author-license" title={this.state.authorimglicense}>
{this.state.authorimglicense &&
this.state.authorimglicense.substring(0, 40) +
(this.state.authorimglicense.length > 40 ? '…' : '')}
</span>
</div>
) : (
<div className="author-content whileLoading" ref={this.quoteauthor}>
{/* these are placeholders for skeleton and as such don't need translating */}
<span className="title pulse">loading</span>
<span className="subtitle pulse">loading</span>
</div>
)}
{(this.state.authorOccupation !== 'Unknown' &&
this.state.authorlink !== null) ||
this.state.copy ||
this.state.share ||
this.state.favourited ? (
<div className="quote-buttons">
{this.state.authorOccupation !== 'Unknown' &&
this.state.authorlink !== null ? (
<Tooltip title={variables.getMessage('widgets.quote.link_tooltip')}>
<a
href={this.state.authorlink}
className="quoteAuthorLink"
target="_blank"
rel="noopener noreferrer"
aria-label="Learn about the author of the quote."
>
<MdOpenInNew />
</a>{' '}
</Tooltip>
) : null}
{this.state.copy} {this.state.share} {this.state.favourited}
</div>
) : null}
</div>
</div> </div>
) : null} </>
</div> )}
</div>
</>
)}
</> </>
)} )}
</div> </div>

View File

@@ -29,14 +29,7 @@ class QuoteOptions extends PureComponent {
resetCustom = () => { resetCustom = () => {
localStorage.setItem('customQuote', '[{"quote": "", "author": ""}]'); localStorage.setItem('customQuote', '[{"quote": "", "author": ""}]');
this.setState({ this.setState({ customQuote: [{ quote: '', author: '' }] });
customQuote: [
{
quote: '',
author: '',
},
],
});
toast(variables.getMessage('toasts.reset')); toast(variables.getMessage('toasts.reset'));
EventBus.emit('refresh', 'background'); EventBus.emit('refresh', 'background');
}; };
@@ -46,9 +39,7 @@ class QuoteOptions extends PureComponent {
const customQuote = this.state.customQuote; const customQuote = this.state.customQuote;
customQuote[index][type] = result; customQuote[index][type] = result;
this.setState({ this.setState({ customQuote });
customQuote,
});
this.forceUpdate(); this.forceUpdate();
localStorage.setItem('customQuote', JSON.stringify(customQuote)); localStorage.setItem('customQuote', JSON.stringify(customQuote));
@@ -59,17 +50,12 @@ class QuoteOptions extends PureComponent {
modifyCustomQuote(type, index) { modifyCustomQuote(type, index) {
const customQuote = this.state.customQuote; const customQuote = this.state.customQuote;
if (type === 'add') { if (type === 'add') {
customQuote.push({ customQuote.push({ quote: '', author: '' });
quote: '',
author: '',
});
} else { } else {
customQuote.splice(index, 1); customQuote.splice(index, 1);
} }
this.setState({ this.setState({ customQuote });
customQuote,
});
this.forceUpdate(); this.forceUpdate();
localStorage.setItem('customQuote', JSON.stringify(customQuote)); localStorage.setItem('customQuote', JSON.stringify(customQuote));
@@ -153,7 +139,7 @@ class QuoteOptions extends PureComponent {
name="authorLink" name="authorLink"
text={variables.getMessage(`${QUOTE_SECTION}.author_link`)} text={variables.getMessage(`${QUOTE_SECTION}.author_link`)}
element=".other" element=".other"
disabled={localStorage.getItem('authorDetails' )=== 'false'} disabled={localStorage.getItem('authorDetails') === 'false'}
/> />
<Checkbox <Checkbox
name="authorImg" name="authorImg"

View File

@@ -46,9 +46,7 @@ export default class Clock extends PureComponent {
// load analog clock css // load analog clock css
import('react-clock/dist/Clock.css'); import('react-clock/dist/Clock.css');
this.setState({ this.setState({ time: now });
time: now,
});
break; break;
default: { default: {
// Default clock // Default clock
@@ -78,10 +76,7 @@ export default class Clock extends PureComponent {
}); });
} }
this.setState({ this.setState({ time, ampm: '' });
time,
ampm: '',
});
} else { } else {
// 12 hour // 12 hour
let hours = now.getHours(); let hours = now.getHours();
@@ -106,10 +101,7 @@ export default class Clock extends PureComponent {
}); });
} }
this.setState({ this.setState({ time, ampm: now.getHours() > 11 ? 'PM' : 'AM' });
time,
ampm: now.getHours() > 11 ? 'PM' : 'AM',
});
} }
break; break;
} }

View File

@@ -17,10 +17,7 @@ export function install(type, input, sideload, collection) {
const oldSettings = []; const oldSettings = [];
Object.keys(localStorage).forEach((key) => { Object.keys(localStorage).forEach((key) => {
oldSettings.push({ oldSettings.push({ name: key, value: localStorage.getItem(key) });
name: key,
value: localStorage.getItem(key),
});
}); });
localStorage.setItem('backup_settings', JSON.stringify(oldSettings)); localStorage.setItem('backup_settings', JSON.stringify(oldSettings));

View File

@@ -29,9 +29,7 @@ const prepareBuilds = () => ({
fs.cpSync( fs.cpSync(
path.resolve(__dirname, './manifest/_locales'), path.resolve(__dirname, './manifest/_locales'),
path.resolve(__dirname, './build/chrome/_locales'), path.resolve(__dirname, './build/chrome/_locales'),
{ { recursive: true },
recursive: true,
},
); );
fs.cpSync(path.resolve(__dirname, './dist'), path.resolve(__dirname, './build/chrome/'), { fs.cpSync(path.resolve(__dirname, './dist'), path.resolve(__dirname, './build/chrome/'), {
recursive: true, recursive: true,
@@ -39,17 +37,13 @@ const prepareBuilds = () => ({
fs.cpSync( fs.cpSync(
path.resolve(__dirname, './src/assets/icons'), path.resolve(__dirname, './src/assets/icons'),
path.resolve(__dirname, './build/chrome/icons'), path.resolve(__dirname, './build/chrome/icons'),
{ { recursive: true },
recursive: true,
},
); );
fs.mkdirSync(path.resolve(__dirname, './build/chrome/src/assets'), { recursive: true }); fs.mkdirSync(path.resolve(__dirname, './build/chrome/src/assets'), { recursive: true });
fs.cpSync( fs.cpSync(
path.resolve(__dirname, './src/assets'), path.resolve(__dirname, './src/assets'),
path.resolve(__dirname, './build/chrome/src/assets'), path.resolve(__dirname, './build/chrome/src/assets'),
{ { recursive: true },
recursive: true,
},
); );
// firefox // firefox
@@ -68,16 +62,12 @@ const prepareBuilds = () => ({
fs.cpSync( fs.cpSync(
path.resolve(__dirname, './src/assets/icons'), path.resolve(__dirname, './src/assets/icons'),
path.resolve(__dirname, './build/firefox/icons'), path.resolve(__dirname, './build/firefox/icons'),
{ { recursive: true },
recursive: true,
},
); );
fs.cpSync( fs.cpSync(
path.resolve(__dirname, './src/assets'), path.resolve(__dirname, './src/assets'),
path.resolve(__dirname, './build/firefox/src/assets'), path.resolve(__dirname, './build/firefox/src/assets'),
{ { recursive: true },
recursive: true,
},
); );
// create zip // create zip
@@ -94,9 +84,7 @@ const prepareBuilds = () => ({
fs.cpSync( fs.cpSync(
path.resolve(__dirname, './src/assets'), path.resolve(__dirname, './src/assets'),
path.resolve(__dirname, './dist/src/assets'), path.resolve(__dirname, './dist/src/assets'),
{ { recursive: true },
recursive: true,
},
); );
} }
}, },
@@ -105,17 +93,9 @@ const prepareBuilds = () => ({
export default defineConfig(({ command, mode }) => { export default defineConfig(({ command, mode }) => {
const env = loadEnv(mode, process.cwd(), ''); const env = loadEnv(mode, process.cwd(), '');
return { return {
define: { define: { __APP_ENV__: JSON.stringify(env.APP_ENV) },
__APP_ENV__: JSON.stringify(env.APP_ENV),
},
plugins: [react(), prepareBuilds(), progress()], plugins: [react(), prepareBuilds(), progress()],
server: { server: { open: true, hmr: { protocol: 'ws', host: 'localhost' } },
open: true,
hmr: {
protocol: 'ws',
host: 'localhost',
},
},
build: { build: {
target: 'esnext', // Use modern JavaScript features target: 'esnext', // Use modern JavaScript features
minify: isProd ? 'esbuild' : false, minify: isProd ? 'esbuild' : false,
@@ -151,12 +131,6 @@ export default defineConfig(({ command, mode }) => {
utils: path.resolve(__dirname, './src/utils'), utils: path.resolve(__dirname, './src/utils'),
}, },
}, },
css: { css: { preprocessorOptions: { scss: { api: 'modern-compiler' } } },
preprocessorOptions: {
scss: {
api: 'modern-compiler',
},
},
},
}; };
}); });