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(