diff --git a/src/components/Admintools.jsx b/src/components/Admintools.jsx index c056278..feeae24 100644 --- a/src/components/Admintools.jsx +++ b/src/components/Admintools.jsx @@ -4,10 +4,10 @@ */ import React, { useState, useEffect } from 'react'; -import { connect } from 'react-redux'; +import { useSelector, shallowEqual } from 'react-redux'; import { t } from 'ttag'; -import type { State } from '../reducers'; +import { getToday } from '../core/utils'; const keptState = { coords: null, @@ -146,19 +146,10 @@ async function submitMakeMod( } -function Admintools({ - canvasId, - canvases, - userlvl, -}) { - const curDate = new Date(); - let day = curDate.getDate(); - let month = curDate.getMonth() + 1; - if (month < 10) month = `0${month}`; - if (day < 10) day = `0${day}`; - const maxDate = `${curDate.getFullYear()}-${month}-${day}`; +function Admintools() { + const maxDate = getToday(); - const [selectedCanvas, selectCanvas] = useState(canvasId); + const [selectedCanvas, selectCanvas] = useState(0); const [imageAction, selectImageAction] = useState('build'); const [iPAction, selectIPAction] = useState('ban'); const [protAction, selectProtAction] = useState('protect'); @@ -173,6 +164,20 @@ function Admintools({ const [modlist, setModList] = useState([]); const [submitting, setSubmitting] = useState(false); + const [ + canvasId, + canvases, + userlvl, + ] = useSelector((state) => [ + state.canvas.canvasId, + state.canvas.canvases, + state.user.userlvl, + ], shallowEqual); + + useEffect(() => { + selectCanvas(canvasId); + }, [canvasId]); + let descAction; switch (imageAction) { case 'build': @@ -597,10 +602,4 @@ function Admintools({ ); } -function mapStateToProps(state: State) { - const { canvasId, canvases } = state.canvas; - const { userlvl } = state.user; - return { canvasId, canvases, userlvl }; -} - -export default connect(mapStateToProps)(Admintools); +export default React.memo(Admintools); diff --git a/src/components/Converter.jsx b/src/components/Converter.jsx index b0607d5..b886df5 100644 --- a/src/components/Converter.jsx +++ b/src/components/Converter.jsx @@ -4,12 +4,11 @@ */ import React, { useState, useEffect } from 'react'; -import { connect } from 'react-redux'; +import { useSelector, shallowEqual } from 'react-redux'; import fileDownload from 'js-file-download'; import { utils, applyPalette } from 'image-q'; import { jt, t } from 'ttag'; -import type { State } from '../reducers'; import printGIMPPalette from '../core/exportGPL'; import { copyCanvasToClipboard } from '../utils/clipboard'; @@ -208,12 +207,8 @@ async function renderOutputImage(opts) { } -function Converter({ - canvasId, - canvases, - showHiddenCanvases, -}) { - const [selectedCanvas, selectCanvas] = useState(canvasId); +function Converter() { + const [selectedCanvas, selectCanvas] = useState(0); const [selectedFile, selectFile] = useState(null); const [selectedStrategy, selectStrategy] = useState('nearest'); const [selectedColorDist, selectColorDist] = useState('euclidean'); @@ -230,6 +225,21 @@ function Converter({ offsetX: 0, offsetY: 0, }); + + const [ + canvasId, + canvases, + showHiddenCanvases, + ] = useSelector((state) => [ + state.canvas.canvasId, + state.canvas.canvases, + state.canvas.showHiddenCanvases, + ], shallowEqual); + + useEffect(() => { + selectCanvas(canvasId); + }, []); + const input = document.createElement('canvas'); useEffect(() => { @@ -615,13 +625,4 @@ function Converter({ ); } -function mapStateToProps(state: State) { - const { - canvasId, - canvases, - showHiddenCanvases, - } = state.canvas; - return { canvasId, canvases, showHiddenCanvases }; -} - -export default connect(mapStateToProps)(Converter); +export default React.memo(Converter); diff --git a/src/components/CoolDownBox.jsx b/src/components/CoolDownBox.jsx index 9e8dfd2..e14c1e0 100644 --- a/src/components/CoolDownBox.jsx +++ b/src/components/CoolDownBox.jsx @@ -4,26 +4,24 @@ */ import React from 'react'; -import { connect } from 'react-redux'; +import { useSelector } from 'react-redux'; import { durationToString, } from '../core/utils'; -import type { State } from '../reducers'; -const CoolDownBox = ({ coolDown }) => ( -
= 300) - ? 'cooldownbox show' : 'cooldownbox'} - > - {coolDown && durationToString(coolDown, true)} -
-); +const CoolDownBox = () => { + const coolDown = useSelector((state) => state.user.coolDown); -function mapStateToProps(state: State) { - const { coolDown } = state.user; - return { coolDown }; -} + return ( +
= 300) + ? 'cooldownbox show' : 'cooldownbox'} + > + {coolDown && durationToString(coolDown, true)} +
+ ); +}; -export default connect(mapStateToProps)(CoolDownBox); +export default React.memo(CoolDownBox); diff --git a/src/components/CoordinatesBox.jsx b/src/components/CoordinatesBox.jsx index 88f3e32..bf47b97 100644 --- a/src/components/CoordinatesBox.jsx +++ b/src/components/CoordinatesBox.jsx @@ -4,45 +4,38 @@ */ import React from 'react'; -import { connect } from 'react-redux'; +import { useSelector, useDispatch } from 'react-redux'; import { t } from 'ttag'; import copy from '../utils/clipboard'; import { notify } from '../actions'; -import type { State } from '../reducers'; - function renderCoordinates(cell): string { return `(${cell.join(', ')})`; } -const CoordinatesBox = ({ view, hover, notifyCopy }) => ( -
{ copy(window.location.hash); notifyCopy(); }} - role="button" - title={t`Copy to Clipboard`} - tabIndex="0" - >{ - renderCoordinates(hover - || view.map(Math.round)) - }
-); +const CoordinatesBox = () => { + const view = useSelector((state) => state.canvas.view); + const hover = useSelector((state) => state.gui.hover); + const dispatch = useDispatch(); -function mapDispatchToProps(dispatch) { - return { - notifyCopy() { - dispatch(notify(t`Copied!`)); - }, - }; -} + return ( +
{ + copy(window.location.hash); + dispatch(notify(t`Copied!`)); + }} + role="button" + title={t`Copy to Clipboard`} + tabIndex="0" + >{ + renderCoordinates(hover + || view.map(Math.round)) + }
+ ); +}; -function mapStateToProps(state: State) { - const { view } = state.canvas; - const { hover } = state.gui; - return { view, hover }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(CoordinatesBox); +export default React.memo(CoordinatesBox); diff --git a/src/components/HistorySelect.jsx b/src/components/HistorySelect.jsx index be3826c..21962d3 100644 --- a/src/components/HistorySelect.jsx +++ b/src/components/HistorySelect.jsx @@ -8,7 +8,7 @@ import React, { import { useSelector, shallowEqual, useDispatch } from 'react-redux'; import { t } from 'ttag'; -import { dateToString } from '../core/utils'; +import { dateToString, getToday } from '../core/utils'; import { selectHistoricalTime } from '../actions'; import { requestHistoricalTimes } from '../actions/fetch'; @@ -25,15 +25,6 @@ function stringToTime(timeString) { return `${timeString.substr(0, 2)}:${timeString.substr(2, 2)}`; } -function getToday() { - const date = new Date(); - let day = date.getDate(); - let month = date.getMonth() + 1; - if (month < 10) month = `0${month}`; - if (day < 10) day = `0${day}`; - return `${date.getFullYear()}-${month}-${day}`; -} - const HistorySelect = () => { const dateSelect = useRef(null); diff --git a/src/components/OnlineBox.jsx b/src/components/OnlineBox.jsx index 182ca51..d861cc3 100644 --- a/src/components/OnlineBox.jsx +++ b/src/components/OnlineBox.jsx @@ -4,37 +4,40 @@ */ import React from 'react'; -import { connect } from 'react-redux'; +import { useSelector, shallowEqual } from 'react-redux'; import { FaUser, FaPaintBrush } from 'react-icons/fa'; import { t } from 'ttag'; import { numberToString } from '../core/utils'; -import type { State } from '../reducers'; +const OnlineBox = () => { + const [ + online, + totalPixels, + name, + ] = useSelector((state) => [ + state.ranks.online, + state.ranks.totalPixels, + state.user.name, + ], shallowEqual); + return ( +
+ {(online || name) + ? ( +
+ {(online) + && {online}  } + {(name != null) + && ( + + {numberToString(totalPixels)} + + )} +
+ ) : null} +
+ ); +}; -const OnlineBox = ({ online, totalPixels, name }) => ( -
- {(online || name) - ? ( -
- {(online) - && {online}  } - {(name != null) - && ( - - {numberToString(totalPixels)} - - )} -
- ) : null} -
-); - -function mapStateToProps(state: State) { - const { online, totalPixels } = state.ranks; - const { name } = state.user; - return { online, totalPixels, name }; -} - -export default connect(mapStateToProps)(OnlineBox); +export default React.memo(OnlineBox); diff --git a/src/components/Palette.jsx b/src/components/Palette.jsx index 1e9f2e0..b71abab 100644 --- a/src/components/Palette.jsx +++ b/src/components/Palette.jsx @@ -4,10 +4,9 @@ */ import React, { useState, useEffect } from 'react'; -import { connect } from 'react-redux'; +import { useDispatch, useSelector, shallowEqual } from 'react-redux'; import { selectColor } from '../actions'; -import type { State } from '../reducers'; import useWindowSize from './hooks/resize'; @@ -90,15 +89,22 @@ function getStylesByWindowSize( }]; } -function Palette({ - colors, - selectedColor, - paletteOpen, - compactPalette, - select, - clrIgnore, -}) { +const Palette = () => { const [render, setRender] = useState(false); + const [ + paletteOpen, + compactPalette, + colors, + clrIgnore, + selectedColor, + ] = useSelector((state) => [ + state.gui.paletteOpen, + state.gui.compactPalette, + state.canvas.palette.colors, + state.canvas.clrIgnore, + state.canvas.selectedColor, + ], shallowEqual); + const dispatch = useDispatch(); useEffect(() => { window.setTimeout(() => { @@ -139,32 +145,12 @@ function Palette({ ? 'selected' : 'unselected'} color={color} - onClick={() => select(index + clrIgnore)} + onClick={() => dispatch(selectColor(index + clrIgnore))} /> ))} ) ); -} +}; -function mapStateToProps(state: State) { - const { paletteOpen, compactPalette } = state.gui; - const { palette, clrIgnore, selectedColor } = state.canvas; - return { - colors: palette.colors, - selectedColor, - paletteOpen, - compactPalette, - clrIgnore, - }; -} - -function mapDispatchToProps(dispatch) { - return { - select(color) { - dispatch(selectColor(color)); - }, - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(Palette); +export default React.memo(Palette); diff --git a/src/components/Style.jsx b/src/components/Style.jsx index 893ccab..144520e 100644 --- a/src/components/Style.jsx +++ b/src/components/Style.jsx @@ -4,21 +4,13 @@ */ import React from 'react'; -import { connect } from 'react-redux'; +import { useSelector } from 'react-redux'; -import type { State } from '../reducers'; - -function Style({ style }) { +function Style() { + const style = useSelector((state) => state.gui.style); const cssUri = window.ssv.availableStyles[style]; return (style === 'default') ? null : (); } -function mapStateToProps(state: State) { - const { - style, - } = state.gui; - return { style }; -} - -export default connect(mapStateToProps)(Style); +export default React.memo(Style); diff --git a/src/components/UI.jsx b/src/components/UI.jsx index 1fedeee..37cbebc 100644 --- a/src/components/UI.jsx +++ b/src/components/UI.jsx @@ -4,9 +4,8 @@ */ import React from 'react'; -import { connect } from 'react-redux'; +import { useSelector, shallowEqual } from 'react-redux'; -import type { State } from '../reducers'; import CoolDownBox from './CoolDownBox'; import NotifyBox from './NotifyBox'; import GlobeButton from './buttons/GlobeButton'; @@ -25,13 +24,21 @@ const CONTEXT_MENUS = { /* other context menus */ }; -const UI = ({ - isHistoricalView, - is3D, - isOnMobile, - menuOpen, - menuType, -}) => { +const UI = () => { + const [ + isHistoricalView, + is3D, + isOnMobile, + menuOpen, + menuType, + ] = useSelector((state) => [ + state.canvas.isHistoricalView, + state.canvas.is3D, + state.user.isOnMobile, + state.contextMenu.menuOpen, + state.contextMenu.menuType, + ], shallowEqual); + const contextMenu = (menuOpen && menuType) ? CONTEXT_MENUS[menuType] : null; if (isHistoricalView) { @@ -52,25 +59,4 @@ const UI = ({ ]; }; -function mapStateToProps(state: State) { - const { - isHistoricalView, - is3D, - } = state.canvas; - const { - isOnMobile, - } = state.user; - const { - menuOpen, - menuType, - } = state.contextMenu; - return { - isHistoricalView, - is3D, - isOnMobile, - menuOpen, - menuType, - }; -} - -export default connect(mapStateToProps)(UI); +export default React.memo(UI); diff --git a/src/components/windows/Settings.jsx b/src/components/windows/Settings.jsx index e3f43db..fb7d42f 100644 --- a/src/components/windows/Settings.jsx +++ b/src/components/windows/Settings.jsx @@ -4,7 +4,7 @@ */ import React from 'react'; -import { connect } from 'react-redux'; +import { useSelector, useDispatch, shallowEqual } from 'react-redux'; import { c, t } from 'ttag'; import LanguageSelect from '../LanguageSelect'; @@ -22,8 +22,6 @@ import { selectStyle, } from '../../actions'; -import type { State } from '../../reducers'; - const flexy = { display: 'flex', @@ -97,28 +95,32 @@ const SettingsItem = ({ ); -function Settings({ - isMuted, - isGridShown, - isPixelNotifyShown, - isPotato, - isLightGrid, - isHistoricalView, - onMute, - autoZoomIn, - compactPalette, - selectedStyle, - onToggleGrid, - onTogglePixelNotify, - onToggleAutoZoomIn, - onToggleCompactPalette, - onToggleChatNotify, - onTogglePotatoMode, - onToggleLightGrid, - onToggleHistoricalView, - onSelectStyle, - chatNotify, -}) { +function Settings() { + const [ + isGridShown, + isPixelNotifyShown, + autoZoomIn, + compactPalette, + isPotato, + isLightGrid, + selectedStyle, + isMuted, + chatNotify, + isHistoricalView, + ] = useSelector((state) => [ + state.gui.showGrid, + state.gui.showPixelNotify, + state.gui.autoZoomIn, + state.gui.compactPalette, + state.gui.isPotato, + state.gui.isLightGrid, + state.gui.style, + state.audio.mute, + state.audio.chatNotify, + state.canvas.isHistoricalView, + ], shallowEqual); + const dispatch = useDispatch(); + return (
dispatch(toggleGrid())} /> dispatch(togglePixelNotify())} /> dispatch(toggleMute())} /> dispatch(toggleChatNotify())} /> dispatch(toggleAutoZoomIn())} /> dispatch(toggleCompactPalette())} /> dispatch(togglePotatoMode())} /> dispatch(toggleLightGrid())} /> {(window.ssv && window.ssv.backupurl) && ( dispatch(toggleHistoricalView())} /> )} {(window.ssv && window.ssv.availableStyles) && ( @@ -190,7 +192,7 @@ function Settings({ description={t`How pixelplanet should look like.`} values={Object.keys(window.ssv.availableStyles)} selected={selectedStyle} - onSelect={onSelectStyle} + onSelect={(style) => dispatch(selectStyle(style))} /> )} {(window.ssv && navigator.cookieEnabled && window.ssv.langs) && ( @@ -207,70 +209,4 @@ function Settings({ ); } -function mapStateToProps(state: State) { - const { mute, chatNotify } = state.audio; - const { - showGrid, - showPixelNotify, - autoZoomIn, - compactPalette, - isPotato, - isLightGrid, - style: selectedStyle, - } = state.gui; - const isMuted = mute; - const { - isHistoricalView, - } = state.canvas; - const isGridShown = showGrid; - const isPixelNotifyShown = showPixelNotify; - return { - isMuted, - isGridShown, - isPixelNotifyShown, - autoZoomIn, - compactPalette, - chatNotify, - isPotato, - isLightGrid, - isHistoricalView, - selectedStyle, - }; -} - -function mapDispatchToProps(dispatch) { - return { - onMute() { - dispatch(toggleMute()); - }, - onToggleGrid() { - dispatch(toggleGrid()); - }, - onTogglePixelNotify() { - dispatch(togglePixelNotify()); - }, - onToggleAutoZoomIn() { - dispatch(toggleAutoZoomIn()); - }, - onToggleCompactPalette() { - dispatch(toggleCompactPalette()); - }, - onToggleChatNotify() { - dispatch(toggleChatNotify()); - }, - onTogglePotatoMode() { - dispatch(togglePotatoMode()); - }, - onToggleLightGrid() { - dispatch(toggleLightGrid()); - }, - onToggleHistoricalView() { - dispatch(toggleHistoricalView()); - }, - onSelectStyle(style) { - dispatch(selectStyle(style)); - }, - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(Settings); +export default React.memo(Settings); diff --git a/src/core/utils.js b/src/core/utils.js index 44322bc..f762a6a 100644 --- a/src/core/utils.js +++ b/src/core/utils.js @@ -46,6 +46,18 @@ export function dateToString(date: string) { return date.substr(0, 4) + date.substr(5, 2) + date.substr(8, 2); } +/* + * get current date in YYYY-MM-DD + */ +export function getToday() { + const date = new Date(); + let day = date.getDate(); + let month = date.getMonth() + 1; + if (month < 10) month = `0${month}`; + if (day < 10) day = `0${day}`; + return `${date.getFullYear()}-${month}-${day}`; +} + // z is assumed to be height here // in ui and rendeer, y is height export function getChunkOfPixel(