refactor(PhotoInformation): simplify state management and improve rendering logic

refactor(Widgets): optimize state initialization and event handling
remove(Tabs): delete unused Tabs component
This commit is contained in:
alexsparkes
2024-12-12 15:24:44 +00:00
parent 2febdb4616
commit 1f62ea966c
4 changed files with 50 additions and 159 deletions

View File

@@ -1,86 +0,0 @@
import variables from 'config/variables';
import React, { useState, useEffect, useMemo } from 'react';
import { motion } from 'framer-motion';
import {
MdSettings,
MdOutlineShoppingBasket,
MdOutlineExtension,
MdRefresh,
MdClose,
} from 'react-icons/md';
import Tab from './Tab';
import { Button, Tooltip } from 'components/Elements';
import ErrorBoundary from '../../../../features/misc/modals/ErrorBoundary';
const Tabs = (props) => {
const [currentTab, setCurrentTab] = useState(props.children[0].props.label);
const [currentName, setCurrentName] = useState(props.children[0].props.name);
const onClick = (tab, name) => {
if (name !== currentName) {
variables.stats.postEvent('tab', ${name}, 'opened');
}
setCurrentTab(tab);
setCurrentName(name);
props.setSubTab(name);
};
const hideReminder = () => {
localStorage.setItem('showReminder', false);
document.querySelector('.reminder-info').style.display = 'none';
};
const reminderInfo = useMemo(
() => (
<div
className="reminder-info"
style={{ display: localStorage.getItem('showReminder') === 'true' ? 'flex' : 'none' }}
>
<div className="shareHeader">
<span className="title">{variables.getMessage('settings:reminder.title')}</span>
<span className="closeModal" onClick={hideReminder}>
<MdClose />
</span>
</div>
<span className="subtitle">{variables.getMessage('settings:reminder.message')}</span>
<button onClick={() => window.location.reload()}>
<MdRefresh />
{variables.getMessage('modals.main.error_boundary.refresh')}
</button>
</div>
),
[],
);
return (
<>
{props.current === 'settings' && (
<div className="modalSidebar">
{props.children.map((tab, index) => (
<Tab
currentTab={currentTab}
key={index}
label={tab.props.label}
onClick={(nextTab) => onClick(nextTab, tab.props.name)}
navbarTab={props.navbar || false}
/>
))}
{reminderInfo}
</div>
)}
<div className="w-full rounded h-[calc(78vh-80px)] bg-modal-content-light dark:bg-modal-content-dark p-10 flex flex-col overflow-scroll">
{props.children.map((tab, index) => {
if (tab.props.label !== currentTab) {
return null;
}
return (
<ErrorBoundary key={`error-boundary-${index}`}>{tab.props.children}</ErrorBoundary>
);
})}
</div>
</>
);
};
export default React.memo(Tabs);

View File

@@ -12,9 +12,6 @@ function PhotoInformation({ info, url, api }) {
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
const [usePhotoMap, setPhotoMap] = useState(false);
const [useMapIcon, setMapIcon] = useState(true);
const [showExtraInfo, setShowExtraInfo] = useState(false);
const [other, setOther] = useState(false);
const [shareModal, openShareModal] = useState(false);
const [excludeModal, openExcludeModal] = useState(false);
const [favouriteTooltipText, setFavouriteTooltipText] = useState(
@@ -52,16 +49,6 @@ function PhotoInformation({ info, url, api }) {
);
}, [info.latitude, info.longitude, usePhotoMap]);
useEffect(() => {
const photoInformationElement = document.getElementsByClassName('photoInformation')[0];
if (photoInformationElement) {
photoInformationElement.onmouseover = () => {
setPhotoMap(true);
setMapIcon(false);
};
}
}, []);
if (info.hidden === true || !info.credit) {
return null;
}
@@ -103,11 +90,7 @@ function PhotoInformation({ info, url, api }) {
const widgetStyle = localStorage.getItem('widgetStyle');
return (
<div
className="photoInformationHolder"
onMouseEnter={() => setOther(true)}
onMouseLeave={() => setOther(false)}
>
<div className="photoInformationHolder">
<Modal
closeTimeoutMS={300}
isOpen={shareModal}
@@ -136,15 +119,13 @@ function PhotoInformation({ info, url, api }) {
</span>
</div>
)}
{(widgetStyle !== 'legacy' || other) && (
{widgetStyle !== 'legacy' && (
<div
className="photoInformation orHover"
style={{ padding: widgetStyle === 'legacy' && '20px' }}
onMouseEnter={() => setShowExtraInfo(true)}
onMouseLeave={() => setShowExtraInfo(false)}
>
<div className={photoMapClassList}>
{useMapIcon || photoMap() === null ? <MdLocationOn /> : ''}
<MdLocationOn />
{photoMap()}
</div>
{photoMap() && (
@@ -179,16 +160,9 @@ function PhotoInformation({ info, url, api }) {
)}
<div className="photoInformation-content">
<div className="photoInformation-text">
<span
className="title"
title={
(showExtraInfo || other) && info.description ? info.description : info.location
}
>
{(showExtraInfo || other) && info.description
? info.description.length > 40
? `${info.description.substring(0, 40)}...`
: info.description
<span className="title" title={info.description || info.location}>
{info.description?.length > 40
? `${info.description.substring(0, 40)}...`
: info.location?.split(',').slice(-2).join(', ').trim()}
</span>
<span className="subtitle" id="credit">
@@ -197,21 +171,17 @@ function PhotoInformation({ info, url, api }) {
</div>
{info.views && info.downloads !== null && <UnsplashStats info={info} />}
</div>
{(showExtraInfo || other) && !excludeModal && (
<>
<span className="subtitle">
{variables.getMessage('widgets.background.information')}
</span>
<InformationItems info={info} width={width} height={height} api={api} />
<ActionButtons
info={info}
favouriteTooltipText={favouriteTooltipText}
setFavouriteTooltipText={setFavouriteTooltipText}
openShareModal={openShareModal}
openExcludeModal={openExcludeModal}
/>
</>
)}
<span className="subtitle information-title">
{variables.getMessage('widgets.background.information')}
</span>
<InformationItems info={info} width={width} height={height} api={api} />
<ActionButtons
info={info}
favouriteTooltipText={favouriteTooltipText}
setFavouriteTooltipText={setFavouriteTooltipText}
openShareModal={openShareModal}
openExcludeModal={openExcludeModal}
/>
</div>
)}
</div>

View File

@@ -244,12 +244,20 @@
}
}
.information-title {
display: none;
}
&:hover {
padding: 20px;
height: auto;
align-items: flex-start;
flex-flow: column;
.information-title {
display: block;
}
.buttons {
padding: 30px 0 0;
display: flex;

View File

@@ -1,5 +1,4 @@
import { Fragment, Suspense, lazy, useState, useEffect } from 'react';
import { Fragment, Suspense, lazy, useState, useEffect, useCallback, useMemo } from 'react';
import Clock from '../../time/Clock';
import Greeting from '../../greeting/Greeting';
import Quote from '../../quote/Quote';
@@ -8,37 +7,35 @@ import QuickLinks from '../../quicklinks/QuickLinks';
import Date from '../../time/Date';
import Message from '../../message/Message';
import { WidgetsLayout } from 'components/Layout';
import EventBus from 'utils/eventbus';
import defaults from 'config/default';
// weather is lazy loaded due to the size of the weather icons module
// since we're using react-icons this might not be accurate,
// however, when we used the original module https://bundlephobia.com/package/weather-icons-react@1.2.0
// as seen here it is ridiculously large
const Weather = lazy(() => import('../../weather/Weather'));
export function Widgets() {
const [order, setOrder] = useState(JSON.parse(localStorage.getItem('order')) || defaults.order);
const [welcome, setWelcome] = useState(localStorage.getItem('showWelcome'));
const Widgets = () => {
const [order, setOrder] = useState(
() => JSON.parse(localStorage.getItem('order')) || defaults.order,
);
const [welcome, setWelcome] = useState(() => localStorage.getItem('showWelcome'));
const online = useMemo(() => localStorage.getItem('offlineMode') === 'false', []);
const online = localStorage.getItem('offlineMode') === 'false';
const enabled = useCallback((key) => localStorage.getItem(key) !== 'false', []);
const widgets = {
time: enabled('time') && <Clock />,
greeting: enabled('greeting') && <Greeting />,
quote: enabled('quote') && <Quote />,
date: enabled('date') && <Date />,
quicklinks: enabled('quicklinksenabled') && online ? <QuickLinks /> : null,
message: enabled('message') && <Message />,
};
function enabled(key) {
return localStorage.getItem(key) !== 'false';
}
const widgets = useMemo(
() => ({
time: enabled('time') && <Clock />,
greeting: enabled('greeting') && <Greeting />,
quote: enabled('quote') && <Quote />,
date: enabled('date') && <Date />,
quicklinks: enabled('quicklinksenabled') && online ? <QuickLinks /> : null,
message: enabled('message') && <Message />,
}),
[enabled, online],
);
useEffect(() => {
EventBus.on('refresh', (data) => {
const handleRefresh = (data) => {
switch (data) {
case 'widgets':
setOrder(JSON.parse(localStorage.getItem('order')) || defaults.order);
@@ -56,8 +53,10 @@ export function Widgets() {
default:
break;
}
});
return () => EventBus.off('refresh');
};
EventBus.on('refresh', handleRefresh);
return () => EventBus.off('refresh', handleRefresh);
}, []);
return welcome !== 'false' ? (
@@ -73,6 +72,6 @@ export function Widgets() {
</Suspense>
</WidgetsLayout>
);
}
};
export default Widgets;