diff --git a/package-lock.json b/package-lock.json index c36bde9..e4e4945 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,6 @@ "express-session": "^1.17.2", "image-q": "^4.0.0", "js-file-download": "^0.4.12", - "localforage": "^1.10.0", "morgan": "^1.10.0", "multer": "^1.4.4", "mysql2": "^2.3.3", @@ -42,6 +41,7 @@ "redux-logger": "^3.0.6", "redux-persist": "^6.0.0", "redux-thunk": "^2.4.1", + "reselect": "^4.1.6", "sequelize": "^6.21.3", "sharp": "^0.30.7", "startaudiocontext": "^1.2.1", @@ -6301,11 +6301,6 @@ "@types/node": "16.9.1" } }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" - }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -7097,14 +7092,6 @@ "node": ">= 0.8.0" } }, - "node_modules/lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", - "dependencies": { - "immediate": "~3.0.5" - } - }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -7134,14 +7121,6 @@ "node": ">=8.9.0" } }, - "node_modules/localforage": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", - "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", - "dependencies": { - "lie": "3.1.1" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -9177,6 +9156,11 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, + "node_modules/reselect": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.6.tgz", + "integrity": "sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ==" + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -15913,11 +15897,6 @@ "@types/node": "16.9.1" } }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" - }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -16506,14 +16485,6 @@ "type-check": "~0.4.0" } }, - "lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", - "requires": { - "immediate": "~3.0.5" - } - }, "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -16537,14 +16508,6 @@ "json5": "^2.1.2" } }, - "localforage": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", - "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", - "requires": { - "lie": "3.1.1" - } - }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -18047,6 +18010,11 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, + "reselect": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.6.tgz", + "integrity": "sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ==" + }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", diff --git a/package.json b/package.json index 540fc79..a410a9f 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,6 @@ "express-session": "^1.17.2", "image-q": "^4.0.0", "js-file-download": "^0.4.12", - "localforage": "^1.10.0", "morgan": "^1.10.0", "multer": "^1.4.4", "mysql2": "^2.3.3", @@ -56,6 +55,7 @@ "redux-logger": "^3.0.6", "redux-persist": "^6.0.0", "redux-thunk": "^2.4.1", + "reselect": "^4.1.6", "sequelize": "^6.21.3", "sharp": "^0.30.7", "startaudiocontext": "^1.2.1", diff --git a/src/client.js b/src/client.js index a899946..8f851ba 100644 --- a/src/client.js +++ b/src/client.js @@ -4,7 +4,6 @@ import createKeyPressHandler from './controls/keypress'; import { - fetchMe, initTimer, urlChange, receiveOnline, @@ -15,6 +14,9 @@ import { setMobile, windowResize, } from './store/actions'; +import { + fetchMe, +} from './store/actions/thunks'; import { receivePixelUpdate, receivePixelReturn, @@ -36,12 +38,12 @@ function init() { pixels.forEach((pxl) => { const [offset, color] = pxl; // remove protection - receivePixelUpdate(store, i, j, offset, color & 0x7F); + receivePixelUpdate(getRenderer(), i, j, offset, color & 0x7F); }); }); - SocketClient.on('pixelReturn', - (args) => receivePixelReturn(store, ...args), - ); + SocketClient.on('pixelReturn', (args) => { + receivePixelReturn(store, getRenderer(), args); + }); SocketClient.on('cooldownPacket', (coolDown) => { store.dispatch(receiveCoolDown(coolDown)); }); diff --git a/src/components/BanInfo.jsx b/src/components/BanInfo.jsx index d42a6ab..472be5f 100644 --- a/src/components/BanInfo.jsx +++ b/src/components/BanInfo.jsx @@ -7,7 +7,7 @@ import { useDispatch } from 'react-redux'; import { t } from 'ttag'; import useInterval from './hooks/interval'; -import { showHelpModal } from '../store/actions'; +import { showHelpModal } from '../store/actions/windows'; import { largeDurationToString, } from '../core/utils'; diff --git a/src/components/CoordinatesBox.jsx b/src/components/CoordinatesBox.jsx index f208a63..61d4b96 100644 --- a/src/components/CoordinatesBox.jsx +++ b/src/components/CoordinatesBox.jsx @@ -7,7 +7,7 @@ import { useSelector, useDispatch } from 'react-redux'; import { t } from 'ttag'; import copy from '../utils/clipboard'; -import { notify } from '../store/actions'; +import { notify } from '../store/actions/thunks'; function renderCoordinates(cell) { diff --git a/src/components/GetIID.jsx b/src/components/GetIID.jsx index b3f3b46..2b05fb5 100644 --- a/src/components/GetIID.jsx +++ b/src/components/GetIID.jsx @@ -6,7 +6,7 @@ import React, { useState } from 'react'; import { useDispatch } from 'react-redux'; import { t } from 'ttag'; -import { notify } from '../store/actions'; +import { notify } from '../store/actions/thunks'; import copyTextToClipboard from '../utils/clipboard'; import { requestIID, diff --git a/src/components/LogInArea.jsx b/src/components/LogInArea.jsx index 46fc63b..818bcf4 100644 --- a/src/components/LogInArea.jsx +++ b/src/components/LogInArea.jsx @@ -5,7 +5,7 @@ import { useDispatch } from 'react-redux'; import { t } from 'ttag'; import LogInForm from './LogInForm'; -import { changeWindowType } from '../store/actions'; +import { changeWindowType } from '../store/actions/windows'; const logoStyle = { marginRight: 5, diff --git a/src/components/SocialSettings.jsx b/src/components/SocialSettings.jsx index 860a82e..8acfaca 100644 --- a/src/components/SocialSettings.jsx +++ b/src/components/SocialSettings.jsx @@ -9,7 +9,7 @@ import { t } from 'ttag'; import { setBlockingDm, setUserBlock, -} from '../store/actions'; +} from '../store/actions/thunks'; import MdToggleButton from './MdToggleButton'; const SocialSettings = ({ done }) => { diff --git a/src/components/Window.jsx b/src/components/Window.jsx index a9caadb..127f7b9 100644 --- a/src/components/Window.jsx +++ b/src/components/Window.jsx @@ -3,7 +3,7 @@ */ import React, { - useState, useCallback, useRef, useEffect, + useState, useCallback, useRef, useEffect, useMemo, } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { t } from 'ttag'; @@ -16,21 +16,26 @@ import { toggleMaximizeWindow, cloneWindow, focusWindow, -} from '../store/actions'; +} from '../store/actions/windows'; +import { + makeSelectWindowById, + makeSelectWindowPosById, + selectShowWindows, +} from '../store/selectors/windows'; import useDrag from './hooks/drag'; import COMPONENTS from './windows'; -// eslint-disable-next-line max-len -const selectWindowById = (state, windowId) => state.windows.windows.find((win) => win.windowId === windowId); - const Window = ({ id }) => { const [render, setRender] = useState(false); const titleBarRef = useRef(null); const resizeRef = useRef(null); - const win = useSelector((state) => selectWindowById(state, id)); - const showWindows = useSelector((state) => state.windows.showWindows); + const selectWindowById = useMemo(() => makeSelectWindowById(id), []); + const selectWIndowPosById = useMemo(() => makeSelectWindowPosById(id), []); + const win = useSelector(selectWindowById); + const position = useSelector(selectWIndowPosById); + const showWindows = useSelector(selectShowWindows); const dispatch = useDispatch(); @@ -101,12 +106,14 @@ const Window = ({ id }) => { } const { - width, height, - xPos, yPos, windowType, - z, title, } = win; + const { + xPos, yPos, + width, height, + z, + } = position; const [Content, name] = COMPONENTS[windowType]; @@ -155,10 +162,6 @@ const Window = ({ id }) => { ); } - if (!showWindows) { - return null; - } - return (
state.windows.windows.map((win) => win.windowId); -// eslint-disable-next-line max-len -const selectMeta = (state) => { - console.log('check'); - return [ - state.windows.showWindows, - state.windows.someFullscreen, - state.windows.windows.some((win) => win.fullscreen && win.open && !win.hidden), -]}; +} from '../store/actions/windows'; +import { + selectIfFullscreen, + selectActiveWindowIds, +} from '../store/selectors/windows'; const WindowManager = () => { - const windowIds = useSelector(selectWindowIds, shallowEqual); + const windowIds = useSelector(selectActiveWindowIds, shallowEqual); const [ - showWindows, - someFullscreen, + fullscreenExistOrShowWindows, someOpenFullscreen, - ] = useSelector(selectMeta, shallowEqual); + ] = useSelector(selectIfFullscreen, shallowEqual); const dispatch = useDispatch(); - if ((!showWindows && !someFullscreen) || !windowIds.length) { + if (!fullscreenExistOrShowWindows || !windowIds.length) { return null; } diff --git a/src/components/buttons/CanvasSwitchButton.jsx b/src/components/buttons/CanvasSwitchButton.jsx index ed58df6..5e4c21b 100644 --- a/src/components/buttons/CanvasSwitchButton.jsx +++ b/src/components/buttons/CanvasSwitchButton.jsx @@ -7,7 +7,7 @@ import { useDispatch } from 'react-redux'; import { FaFlipboard } from 'react-icons/fa'; import { t } from 'ttag'; -import { showCanvasSelectionModal } from '../../store/actions'; +import { showCanvasSelectionModal } from '../../store/actions/windows'; const CanvasSwitchButton = () => { diff --git a/src/components/buttons/ChatButton.jsx b/src/components/buttons/ChatButton.jsx index 5238a18..23f30f3 100644 --- a/src/components/buttons/ChatButton.jsx +++ b/src/components/buttons/ChatButton.jsx @@ -12,7 +12,7 @@ import { t } from 'ttag'; import { hideAllWindowTypes, openChatWindow, -} from '../../store/actions'; +} from '../../store/actions/windows'; /* * return [ chatOpen, chatHiden ] diff --git a/src/components/buttons/HelpButton.jsx b/src/components/buttons/HelpButton.jsx index 5cb2570..d63f268 100644 --- a/src/components/buttons/HelpButton.jsx +++ b/src/components/buttons/HelpButton.jsx @@ -7,7 +7,7 @@ import { useDispatch } from 'react-redux'; import { FaQuestion } from 'react-icons/fa'; import { t } from 'ttag'; -import { showHelpModal } from '../../store/actions'; +import { showHelpModal } from '../../store/actions/windows'; const HelpButton = () => { diff --git a/src/components/buttons/LogInButton.jsx b/src/components/buttons/LogInButton.jsx index 74edf65..35a0185 100644 --- a/src/components/buttons/LogInButton.jsx +++ b/src/components/buttons/LogInButton.jsx @@ -7,7 +7,7 @@ import { useDispatch } from 'react-redux'; import { MdPerson } from 'react-icons/md'; import { t } from 'ttag'; -import { showUserAreaModal } from '../../store/actions'; +import { showUserAreaModal } from '../../store/actions/windows'; const LogInButton = () => { diff --git a/src/components/buttons/SettingsButton.jsx b/src/components/buttons/SettingsButton.jsx index 425cdca..ae29a6c 100644 --- a/src/components/buttons/SettingsButton.jsx +++ b/src/components/buttons/SettingsButton.jsx @@ -7,7 +7,7 @@ import { useDispatch } from 'react-redux'; import { FaCog } from 'react-icons/fa'; import { t } from 'ttag'; -import { showSettingsModal } from '../../store/actions'; +import { showSettingsModal } from '../../store/actions/windows'; const SettingsButton = () => { diff --git a/src/components/contextmenus/ChannelContextMenu.jsx b/src/components/contextmenus/ChannelContextMenu.jsx index 2c39503..0eff8fa 100644 --- a/src/components/contextmenus/ChannelContextMenu.jsx +++ b/src/components/contextmenus/ChannelContextMenu.jsx @@ -11,10 +11,12 @@ import { } from '../hooks/clickOutside'; import { hideContextMenu, - setLeaveChannel, muteChatChannel, unmuteChatChannel, } from '../../store/actions'; +import { + setLeaveChannel, +} from '../../store/actions/thunks'; const ChannelContextMenu = () => { const wrapperRef = useRef(null); diff --git a/src/components/contextmenus/UserContextMenu.jsx b/src/components/contextmenus/UserContextMenu.jsx index a93cb14..0a57f3a 100644 --- a/src/components/contextmenus/UserContextMenu.jsx +++ b/src/components/contextmenus/UserContextMenu.jsx @@ -11,11 +11,15 @@ import { } from '../hooks/clickOutside'; import { hideContextMenu, - addToChatInputMessage, +} from '../../store/actions'; +import { startDm, setUserBlock, +} from '../../store/actions/thunks'; +import { + addToChatInputMessage, setWindowArgs, -} from '../../store/actions'; +} from '../../store/actions/windows'; import { escapeMd } from '../../core/utils'; const UserContextMenu = () => { diff --git a/src/components/windows/CanvasSelect.jsx b/src/components/windows/CanvasSelect.jsx index 563bde1..61fc162 100644 --- a/src/components/windows/CanvasSelect.jsx +++ b/src/components/windows/CanvasSelect.jsx @@ -7,7 +7,8 @@ import { useDispatch, useSelector, shallowEqual } from 'react-redux'; import { t } from 'ttag'; import CanvasItem from '../CanvasItem'; -import { changeWindowType, selectCanvas } from '../../store/actions'; +import { selectCanvas } from '../../store/actions'; +import { changeWindowType } from '../../store/actions/windows'; const CanvasSelect = ({ windowId }) => { diff --git a/src/components/windows/Chat.jsx b/src/components/windows/Chat.jsx index f0dfb29..8cec06a 100644 --- a/src/components/windows/Chat.jsx +++ b/src/components/windows/Chat.jsx @@ -13,13 +13,17 @@ import ChatMessage from '../ChatMessage'; import ChannelDropDown from '../contextmenus/ChannelDropDown'; import { - showUserAreaModal, - fetchChatMessages, showContextMenu, - setWindowTitle, - setWindowArgs, markChannelAsRead, } from '../../store/actions'; +import { + showUserAreaModal, + setWindowTitle, + setWindowArgs, +} from '../../store/actions/windows'; +import { + fetchChatMessages, +} from '../../store/actions/thunks'; import SocketClient from '../../socket/SocketClient'; diff --git a/src/components/windows/ForgotPassword.jsx b/src/components/windows/ForgotPassword.jsx index 336cc8d..23e74cd 100644 --- a/src/components/windows/ForgotPassword.jsx +++ b/src/components/windows/ForgotPassword.jsx @@ -5,7 +5,7 @@ import React, { useState } from 'react'; import { useDispatch } from 'react-redux'; import { t } from 'ttag'; -import { changeWindowType } from '../../store/actions'; +import { changeWindowType } from '../../store/actions/windows'; import { validateEMail } from '../../utils/validation'; import { requestNewPassword } from '../../store/actions/fetch'; diff --git a/src/components/windows/Register.jsx b/src/components/windows/Register.jsx index fe84eb5..544b005 100644 --- a/src/components/windows/Register.jsx +++ b/src/components/windows/Register.jsx @@ -11,7 +11,8 @@ import { } from '../../utils/validation'; import { requestRegistration } from '../../store/actions/fetch'; -import { changeWindowType, loginUser } from '../../store/actions'; +import { loginUser } from '../../store/actions'; +import { changeWindowType } from '../../store/actions/windows'; function validate(name, email, password, confirmPassword) { diff --git a/src/components/windows/UserArea.jsx b/src/components/windows/UserArea.jsx index f3125c6..c9fcbcf 100644 --- a/src/components/windows/UserArea.jsx +++ b/src/components/windows/UserArea.jsx @@ -8,8 +8,10 @@ import { t } from 'ttag'; import { fetchStats, +} from '../../store/actions/thunks'; +import { setWindowArgs, -} from '../../store/actions'; +} from '../../store/actions/windows'; import useInterval from '../hooks/interval'; import LogInArea from '../LogInArea'; import Tabs from '../Tabs'; diff --git a/src/controls/PixelPainterControls.js b/src/controls/PixelPainterControls.js index e9d0e53..b199874 100644 --- a/src/controls/PixelPainterControls.js +++ b/src/controls/PixelPainterControls.js @@ -243,6 +243,7 @@ class PixelPlainterControls { const [i, j] = getChunkOfPixel(canvasSize, x, y); const offset = getOffsetOfPixel(canvasSize, x, y); tryPlacePixel( + renderer, store, i, j, offset, selectedColor, diff --git a/src/controls/keypress.js b/src/controls/keypress.js index 00ee36c..86f967d 100644 --- a/src/controls/keypress.js +++ b/src/controls/keypress.js @@ -9,9 +9,11 @@ import { toggleHiddenCanvases, togglePixelNotify, toggleMute, - notify, selectCanvas, } from '../store/actions'; +import { + notify, +} from '../store/actions/thunks'; const usedKeys = ['g', 'h', 'x', 'm', 'r', 'p']; diff --git a/src/socket/SocketClient.js b/src/socket/SocketClient.js index e0dc455..7b22ad8 100644 --- a/src/socket/SocketClient.js +++ b/src/socket/SocketClient.js @@ -248,8 +248,7 @@ class SocketClient extends EventEmitter { `Socket is closed. Reconnect will be attempted in ${timeout} ms.`, e.reason, ); - - setTimeout(() => this.connect(), 5000); + setTimeout(() => this.connect(), timeout); } reconnect() { diff --git a/src/socket/packets/PixelReturn.js b/src/socket/packets/PixelReturn.js index 28465d2..8ba9285 100644 --- a/src/socket/packets/PixelReturn.js +++ b/src/socket/packets/PixelReturn.js @@ -9,13 +9,13 @@ export default { const coolDownSeconds = data.getInt16(6); const pxlCnt = data.getUint8(8); const rankedPxlCnt = data.getUint8(9); - return [ + return { retCode, wait, coolDownSeconds, pxlCnt, rankedPxlCnt, - ]; + }; }, dehydrate( retCode, diff --git a/src/store/actions/README.md b/src/store/actions/README.md deleted file mode 100644 index a128d25..0000000 --- a/src/store/actions/README.md +++ /dev/null @@ -1,148 +0,0 @@ -# Actions - -List of redux actions for reference: - -```js -export type Action = - { type: 'LOGGED_OUT' } - | { type: 'ALERT', - title: string, - message: string, - alertType: string, - btn: string, - } - | { type: 'CLOSE_ALERT' } - | { type: 'TOGGLE_GRID' } - | { type: 'TOGGLE_PIXEL_NOTIFY' } - | { type: 'TOGGLE_AUTO_ZOOM_IN' } - | { type: 'TOGGLE_ONLINE_CANVAS' } - | { type: 'TOGGLE_MUTE' } - | { type: 'TOGGLE_OPEN_PALETTE' } - | { type: 'TOGGLE_COMPACT_PALETTE' } - | { type: 'TOGGLE_CHAT_NOTIFY' } - | { type: 'TOGGLE_POTATO_MODE' } - | { type: 'TOGGLE_LIGHT_GRID' } - | { type: 'TOGGLE_OPEN_MENU' } - | { type: 'TOGGLE_HISTORICAL_VIEW' } - | { type: 'SELECT_STYLE', style: string } - | { type: 'SET_NOTIFICATION', notification: string } - | { type: 'UNSET_NOTIFICATION' } - | { type: 'SET_REQUESTING_PIXEL', requestingPixel: boolean } - | { type: 'SET_HOVER', hover: Array } - | { type: 'UNSET_HOVER' } - | { type: 'SET_WAIT', wait: ?number } - | { type: 'RECEIVE_COOLDOWN', wait: number } - | { type: 'SET_MOBILE', mobile: boolean } - | { type: 'WINDOW_RESIZE'} - | { type: 'COOLDOWN_END' } - | { type: 'COOLDOWN_SET', coolDown: number } - | { type: 'COOLDOWN_DELTA', delta: number } - | { type: 'SELECT_COLOR', color: ColorIndex } - | { type: 'SELECT_CANVAS', canvasId: number } - | { type: 'PLACED_PIXELS', amount: number } - | { type: 'PIXEL_WAIT' } - | { type: 'SET_VIEW_COORDINATES', view: Array } - | { type: 'SET_SCALE', scale: number, zoompoint: Array } - | { type: 'REQUEST_BIG_CHUNK', center: Array } - | { type: 'PRE_LOADED_BIG_CHUNK', center: Array } - | { type: 'RECEIVE_BIG_CHUNK', center: Array, chunk: Uint8Array } - | { type: 'RECEIVE_BIG_CHUNK_FAILURE', center: Array, error: Error } - | { type: 'UPDATE_PIXEL', - i: number, - j: number, - offset: number, - color: ColorIndex, - notify: boolean, - } - | { type: 'RECEIVE_ONLINE', online: number } - | { type: 'RECEIVE_CHAT_MESSAGE', - name: string, - text: string, - country: string, - channel: number, - user: number, - isPing: boolean, - isRead: boolean, - } - | { type: 'RECEIVE_CHAT_HISTORY', cid: number, history: Array } - | { type: 'OPEN_CHAT_CHANNEL', cid: number } - | { type: 'CLOSE_CHAT_CHANNEL', cid: number } - | { type: 'ADD_CHAT_CHANNEL', channel: Object } - | { type: 'REMOVE_CHAT_CHANNEL', cid: number } - | { type: 'MUTE_CHAT_CHANNEL', cid: number } - | { type: 'UNMUTE_CHAT_CHANNEL', cid: number } - | { type: 'MARK_CHANNEL_AS_READ', cid: number } - | { type: 'SET_CHAT_FETCHING', fetching: boolean } - | { type: 'OPEN_WINDOW', - windowType: string, - title: string, - fullscreen: boolean, - cloneable: boolean, - args: Object, - } - | { type: 'CLOSE_WINDOW', windowId: number } - | { type: 'REMOVE_WINDOW', windowId: number } - | { type: 'HIDE_ALL_WINDOW_TYPE', windowType: string, hide: boolean } - | { type: 'CLOSE_ALL_WINDOW_TYPE', windowType: string} - | { type: 'FOCUS_WINDOW', windowId: number } - | { type: 'CLONE_WINDOW', windowId: number } - | { type: 'TOGGLE_MAXIMIZE_WINDOW', windowId: number } - | { type: 'CLOSE_FULLSCREEN_WINDOWS' } - | { type: 'MOVE_WINDOW', windowId: number, xDiff: number, yDiff: number } - | { type: 'RESIZE_WINDOW', windowId: number, xDiff: number, yDiff: number } - | { type: 'CHANGE_WINDOW_TYPE', - windowId: number, - windowType: number, - args: Object, - } - | { type: 'SET_WINDOW_TITLE', windowId: number, title: string } - | { type: 'BLOCK_USER', userId: number, userName: string } - | { type: 'UNBLOCK_USER', userId: number, userName: string } - | { type: 'SET_BLOCKING_DM', blockDm: boolean } - | { type: 'RECEIVE_ME', - name: string, - waitSeconds: number, - messages: Array, - mailreg: boolean, - totalPixels: number, - dailyTotalPixels: number, - ranking: number, - dailyRanking: number, - blockDm: boolean, - canvases: Object, - channels: Object, - blocked: Array, - userlvl: number, - } - | { type: 'LOGIN', - name: string, - waitSeconds: number, - messages: Array, - mailreg: boolean, - totalPixels: number, - dailyTotalPixels: number, - ranking: number, - dailyRanking: number, - blockDm: boolean, - canvases: Object, - channels: Object, - blocked: Array, - userlvl: number, - } - | { type: 'LOGOUT' } - | { type: 'RECEIVE_STATS', totalRanking: Object, totalDailyRanking: Object } - | { type: 'SET_NAME', name: string } - | { type: 'SET_MAILREG', mailreg: boolean } - | { type: 'REM_FROM_MESSAGES', message: string } - | { type: 'SHOW_CONTEXT_MENU', - menuType: string, - xPos: number, - yPos: number, - args: Object, - } - | { type: 'HIDE_CONTEXT_MENU' } - | { type: 'RELOAD_URL' } - | { type: 'SET_HISTORICAL_TIME', date: string, time: string } - | { type: 'ON_VIEW_FINISH_CHANGE' }; - -``` diff --git a/src/store/actions/index.js b/src/store/actions/index.js index 60e1ec6..7ca376e 100644 --- a/src/store/actions/index.js +++ b/src/store/actions/index.js @@ -1,14 +1,5 @@ import { t } from 'ttag'; -import { - requestStartDm, - requestBlock, - requestBlockDm, - requestLeaveChan, - requestRankings, - requestMe, -} from './fetch'; - export function pAlert( title, message, @@ -115,27 +106,10 @@ export function toggleOpenMenu() { }; } -/* - * requestingPixel is inveted, it has the meaning of - * "can i request a pixel" - */ -export function setRequestingPixel(requestingPixel) { +export function setAllowSettingPixel(allowSettingPixel) { return { - type: 'SET_REQUESTING_PIXEL', - requestingPixel, - }; -} - -export function setNotification(notification) { - return { - type: 'SET_NOTIFICATION', - notification, - }; -} - -export function unsetNotification() { - return { - type: 'UNSET_NOTIFICATION', + type: 'ALLOW_SETTING_PIXEL', + allowSettingPixel, }; } @@ -152,13 +126,6 @@ export function unsetHover() { }; } -export function setWait(wait) { - return { - type: 'SET_WAIT', - wait, - }; -} - export function setMobile(mobile) { return { type: 'SET_MOBILE', @@ -186,61 +153,6 @@ export function selectCanvas(canvasId) { }; } -export function placedPixels(amount) { - return { - type: 'PLACED_PIXELS', - amount, - }; -} - -export function pixelWait() { - return { - type: 'PIXEL_WAIT', - }; -} - -export function receiveOnline(online) { - return { - type: 'RECEIVE_ONLINE', - online, - }; -} - -export function receiveChatMessage( - name, - text, - country, - channel, - user, - isPing, - isRead, -) { - return { - type: 'RECEIVE_CHAT_MESSAGE', - name, - text, - country, - channel, - user, - isPing, - isRead, - }; -} - -let lastNotify = null; -export function notify(notification) { - return async (dispatch) => { - dispatch(setNotification(notification)); - if (lastNotify) { - clearTimeout(lastNotify); - lastNotify = null; - } - lastNotify = setTimeout(() => { - dispatch(unsetNotification()); - }, 1500); - }; -} - export function setViewCoordinates(view) { return { type: 'SET_VIEW_COORDINATES', @@ -358,38 +270,6 @@ export function receiveCoolDown( }; } -/* - * draw pixel on canvas - * @param i, j, offset Chunk and offset in chunk - * @param color integer Color Index - * @param notifyPxl Bool if pixel notification appears (false when my own pixel) - */ -export function updatePixel( - i, - j, - offset, - color, - notifyPxl = true, -) { - return { - type: 'UPDATE_PIXEL', - i, - j, - offset, - color, - notify: notifyPxl, - }; -} - -export function loginUser( - me, -) { - return { - type: 'LOGIN', - ...me, - }; -} - export function receiveMe( me, ) { @@ -399,13 +279,6 @@ export function receiveMe( }; } -export function logoutUser( -) { - return { - type: 'LOGOUT', - }; -} - export function receiveStats( rankings, ) { @@ -417,6 +290,58 @@ export function receiveStats( }; } +export function receiveOnline(online) { + return { + type: 'RECEIVE_ONLINE', + online, + }; +} + +export function receiveChatMessage( + name, + text, + country, + channel, + user, + isPing, + isRead, +) { + return { + type: 'RECEIVE_CHAT_MESSAGE', + name, + text, + country, + channel, + user, + isPing, + isRead, + }; +} + +/* + * check socket/packets/PixelReturn.js for args + */ +export function storeReceivePixelReturn(args) { + args.type = 'RECEIVE_PIXEL_RETURN'; + return args; +} + +export function logoutUser( +) { + return { + type: 'LOGOUT', + }; +} + +export function loginUser( + me, +) { + return { + type: 'LOGIN', + ...me, + }; +} + export function setName( name, ) { @@ -444,35 +369,6 @@ export function remFromMessages( }; } -export function fetchStats() { - return async (dispatch) => { - const rankings = await requestRankings(); - if (!rankings.errors) { - dispatch(receiveStats(rankings)); - } - }; -} - -export function fetchMe() { - return async (dispatch) => { - const me = await requestMe(); - if (!me.errors) { - dispatch(receiveMe(me)); - } - }; -} - -function receiveChatHistory( - cid, - history, -) { - return { - type: 'RECEIVE_CHAT_HISTORY', - cid, - history, - }; -} - export function markChannelAsRead( cid, ) { @@ -482,42 +378,6 @@ export function markChannelAsRead( }; } -function setChatFetching(fetching) { - return { - type: 'SET_CHAT_FETCHING', - fetching, - }; -} - -function setApiFetching(fetching) { - return { - type: 'SET_API_FETCHING', - fetching, - }; -} - -export function fetchChatMessages( - cid, -) { - return async (dispatch) => { - dispatch(setChatFetching(true)); - const response = await fetch(`api/chathistory?cid=${cid}&limit=50`, { - credentials: 'include', - }); - - /* - * timeout in order to not spam api requests and get rate limited - */ - if (response.ok) { - setTimeout(() => { dispatch(setChatFetching(false)); }, 500); - const { history } = await response.json(); - dispatch(receiveChatHistory(cid, history)); - } else { - setTimeout(() => { dispatch(setChatFetching(false)); }, 5000); - } - }; -} - function setCoolDown(coolDown) { return { type: 'COOLDOWN_SET', @@ -560,132 +420,6 @@ export function initTimer() { }; } -/* - * fullscreen means to open as modal - * Look into store/reducers/windows.js to know what it means - */ -export function openWindow( - windowType, - title = '', - args = null, - fullscreen = false, - cloneable = true, - xPos = null, - yPos = null, - width = null, - height = null, -) { - return { - type: 'OPEN_WINDOW', - windowType, - title, - args, - fullscreen, - cloneable, - xPos, - yPos, - width, - height, - }; -} - -export function setWindowArgs( - windowId, - args, -) { - return { - type: 'SET_WINDOW_ARGS', - windowId, - args, - }; -} - -function showFullscreenWindow(modalType, title) { - return openWindow( - modalType, - title, - null, - true, - ); -} - -export function closeFullscreenWindows() { - return { - type: 'CLOSE_FULLSCREEN_WINDOWS', - }; -} - -export function showSettingsModal() { - return showFullscreenWindow( - 'SETTINGS', - '', - ); -} - -export function showUserAreaModal() { - return showFullscreenWindow( - 'USERAREA', - '', - ); -} - -export function changeWindowType( - windowId, - windowType, - title = '', - args = null, -) { - return { - type: 'CHANGE_WINDOW_TYPE', - windowId, - windowType, - title, - args, - }; -} - -export function setWindowTitle(windowId, title) { - return { - type: 'SET_WINDOW_TITLE', - windowId, - title, - }; -} - -export function showRegisterModal() { - return showFullscreenWindow( - 'REGISTER', - t`Register New Account`, - ); -} - -export function showForgotPasswordModal() { - return showFullscreenWindow( - 'FORGOT_PASSWORD', - t`Restore my Password`, - ); -} - -export function showHelpModal() { - return showFullscreenWindow( - 'HELP', - t`Welcome to PixelPlanet.fun`, - ); -} -export function showArchiveModal() { - return showFullscreenWindow( - 'ARCHIVE', - t`Look at past Canvases`, - ); -} - -export function showCanvasSelectionModal() { - return showFullscreenWindow( - 'CANVAS_SELECTION', - '', - ); -} - export function showContextMenu( menuType, xPos, @@ -766,213 +500,6 @@ export function unmuteChatChannel(cid) { }; } -export function addToChatInputMessage(windowId, msg, focus = true) { - return (dispatch, getState) => { - const args = getState().windows.args[windowId]; - let inputMessage = args && args.inputMessage; - if (!inputMessage) { - inputMessage = ''; - } else if (inputMessage.slice(-1) !== ' ') { - inputMessage += ' '; - } - inputMessage += msg; - - dispatch(setWindowArgs(windowId, { - inputMessage, - })); - - if (focus) { - const inputElem = document.getElementById(`chtipt-${windowId}`); - if (inputElem) { - inputElem.focus(); - } - } - }; -} - -export function closeWindow(windowId) { - return { - type: 'CLOSE_WINDOW', - windowId, - }; -} - -export function removeWindow(windowId) { - return { - type: 'REMOVE_WINDOW', - windowId, - }; -} - -export function focusWindow(windowId) { - return { - type: 'FOCUS_WINDOW', - windowId, - }; -} - -export function cloneWindow(windowId) { - return { - type: 'CLONE_WINDOW', - windowId, - }; -} - -export function toggleMaximizeWindow(windowId) { - return { - type: 'TOGGLE_MAXIMIZE_WINDOW', - windowId, - }; -} - -export function moveWindow(windowId, xDiff, yDiff) { - return { - type: 'MOVE_WINDOW', - windowId, - xDiff, - yDiff, - }; -} - -export function resizeWindow(windowId, xDiff, yDiff) { - return { - type: 'RESIZE_WINDOW', - windowId, - xDiff, - yDiff, - }; -} - -export function closeAllWindowTypes(windowType) { - return { - type: 'CLOSE_ALL_WINDOW_TYPE', - windowType, - }; -} - -export function hideAllWindowTypes( - windowType, - hide, -) { - return { - type: 'HIDE_ALL_WINDOW_TYPE', - windowType, - hide, - }; -} - -export function openChatWindow() { - const width = 350; - const height = 350; - return openWindow( - 'CHAT', - '', - null, - false, - true, - window.innerWidth - width - 62, - window.innerHeight - height - 64, - width, - height, - ); -} - -/* - * query with either userId or userName - */ -export function startDm(windowId, query) { - return async (dispatch) => { - dispatch(setApiFetching(true)); - const res = await requestStartDm(query); - if (typeof res === 'string') { - dispatch(pAlert( - 'Direct Message Error', - res, - 'error', - 'OK', - )); - } else { - const cid = Number(Object.keys(res)[0]); - dispatch(addChatChannel(res)); - dispatch(setWindowArgs(windowId, { - chatChannel: cid, - })); - } - dispatch(setApiFetching(false)); - }; -} - -export function gotCoolDownDelta(delta) { - return { - type: 'COOLDOWN_DELTA', - delta, - }; -} - -export function setUserBlock( - userId, - userName, - block, -) { - return async (dispatch) => { - dispatch(setApiFetching(true)); - const res = await requestBlock(userId, block); - if (res) { - dispatch(pAlert( - 'User Block Error', - res, - 'error', - 'OK', - )); - } else if (block) { - dispatch(blockUser(userId, userName)); - } else { - dispatch(unblockUser(userId, userName)); - } - dispatch(setApiFetching(false)); - }; -} - -export function setBlockingDm( - block, -) { - return async (dispatch) => { - dispatch(setApiFetching(true)); - const res = await requestBlockDm(block); - if (res) { - dispatch(pAlert( - 'Blocking DMs Error', - res, - 'error', - 'OK', - )); - } else { - dispatch(blockingDm(block)); - } - dispatch(setApiFetching(false)); - }; -} - -export function setLeaveChannel( - cid, -) { - return async (dispatch) => { - dispatch(setApiFetching(true)); - const res = await requestLeaveChan(cid); - if (res) { - dispatch(pAlert( - 'Leaving Channel Error', - res, - 'error', - 'OK', - )); - } else { - dispatch(removeChatChannel(cid)); - } - dispatch(setApiFetching(false)); - }; -} - export function hideContextMenu() { return { type: 'HIDE_CONTEXT_MENU', @@ -1004,3 +531,4 @@ export function urlChange() { dispatch(reloadUrl()); }; } + diff --git a/src/store/actions/thunks.js b/src/store/actions/thunks.js new file mode 100644 index 0000000..3816452 --- /dev/null +++ b/src/store/actions/thunks.js @@ -0,0 +1,206 @@ +/* + * thunk actions + */ +import { + requestStartDm, + requestBlock, + requestBlockDm, + requestLeaveChan, + requestRankings, + requestMe, +} from './fetch'; + +import { + setWindowArgs, +} from './windows'; +import { + addChatChannel, + pAlert, + receiveStats, + receiveMe, + blockUser, + unblockUser, + blockingDm, + removeChatChannel, +} from './index'; + +function setApiFetching(fetching) { + return { + type: 'SET_API_FETCHING', + fetching, + }; +} + +function setChatFetching(fetching) { + return { + type: 'SET_CHAT_FETCHING', + fetching, + }; +} + +function receiveChatHistory( + cid, + history, +) { + return { + type: 'RECEIVE_CHAT_HISTORY', + cid, + history, + }; +} + +/* + * query with either userId or userName + */ +export function startDm(windowId, query) { + return async (dispatch) => { + dispatch(setApiFetching(true)); + const res = await requestStartDm(query); + if (typeof res === 'string') { + dispatch(pAlert( + 'Direct Message Error', + res, + 'error', + 'OK', + )); + } else { + const cid = Number(Object.keys(res)[0]); + dispatch(addChatChannel(res)); + dispatch(setWindowArgs(windowId, { + chatChannel: cid, + })); + } + dispatch(setApiFetching(false)); + }; +} + +export function fetchStats() { + return async (dispatch) => { + const rankings = await requestRankings(); + if (!rankings.errors) { + dispatch(receiveStats(rankings)); + } + }; +} + +export function fetchMe() { + return async (dispatch) => { + const me = await requestMe(); + if (!me.errors) { + dispatch(receiveMe(me)); + } + }; +} + +export function fetchChatMessages( + cid, +) { + return async (dispatch) => { + dispatch(setChatFetching(true)); + const response = await fetch(`api/chathistory?cid=${cid}&limit=50`, { + credentials: 'include', + }); + + /* + * timeout in order to not spam api requests and get rate limited + */ + if (response.ok) { + setTimeout(() => { dispatch(setChatFetching(false)); }, 500); + const { history } = await response.json(); + dispatch(receiveChatHistory(cid, history)); + } else { + setTimeout(() => { dispatch(setChatFetching(false)); }, 5000); + } + }; +} + +export function setUserBlock( + userId, + userName, + block, +) { + return async (dispatch) => { + dispatch(setApiFetching(true)); + const res = await requestBlock(userId, block); + if (res) { + dispatch(pAlert( + 'User Block Error', + res, + 'error', + 'OK', + )); + } else if (block) { + dispatch(blockUser(userId, userName)); + } else { + dispatch(unblockUser(userId, userName)); + } + dispatch(setApiFetching(false)); + }; +} + +export function setBlockingDm( + block, +) { + return async (dispatch) => { + dispatch(setApiFetching(true)); + const res = await requestBlockDm(block); + if (res) { + dispatch(pAlert( + 'Blocking DMs Error', + res, + 'error', + 'OK', + )); + } else { + dispatch(blockingDm(block)); + } + dispatch(setApiFetching(false)); + }; +} + +export function setLeaveChannel( + cid, +) { + return async (dispatch) => { + dispatch(setApiFetching(true)); + const res = await requestLeaveChan(cid); + if (res) { + dispatch(pAlert( + 'Leaving Channel Error', + res, + 'error', + 'OK', + )); + } else { + dispatch(removeChatChannel(cid)); + } + dispatch(setApiFetching(false)); + }; +} + +function setNotification(notification) { + return { + type: 'SET_NOTIFICATION', + notification, + }; +} + +function unsetNotification() { + return { + type: 'UNSET_NOTIFICATION', + }; +} + +let lastNotify = null; +export function notify(notification) { + return (dispatch) => { + dispatch(setNotification(notification)); + if (lastNotify) { + clearTimeout(lastNotify); + lastNotify = null; + } + lastNotify = setTimeout(() => { + dispatch(unsetNotification()); + }, 1500); + }; +} diff --git a/src/store/actions/windows.js b/src/store/actions/windows.js new file mode 100644 index 0000000..dbb902a --- /dev/null +++ b/src/store/actions/windows.js @@ -0,0 +1,238 @@ +/* + * Actions that are exclusively used by windows + */ + +import { t } from 'ttag'; + +export function openWindow( + windowType, + title = '', + args = null, + fullscreen = false, + cloneable = true, + xPos = null, + yPos = null, + width = null, + height = null, +) { + return { + type: 'OPEN_WINDOW', + windowType, + title, + args, + fullscreen, + cloneable, + xPos, + yPos, + width, + height, + }; +} + +export function setWindowArgs( + windowId, + args, +) { + return { + type: 'SET_WINDOW_ARGS', + windowId, + args, + }; +} + +function showFullscreenWindow(modalType, title) { + return openWindow( + modalType, + title, + null, + true, + ); +} + +export function closeFullscreenWindows() { + return { + type: 'CLOSE_FULLSCREEN_WINDOWS', + }; +} + +export function showSettingsModal() { + return showFullscreenWindow( + 'SETTINGS', + '', + ); +} + +export function showUserAreaModal() { + return showFullscreenWindow( + 'USERAREA', + '', + ); +} + +export function changeWindowType( + windowId, + windowType, + title = '', + args = null, +) { + return { + type: 'CHANGE_WINDOW_TYPE', + windowId, + windowType, + title, + args, + }; +} + +export function setWindowTitle(windowId, title) { + return { + type: 'SET_WINDOW_TITLE', + windowId, + title, + }; +} + +export function showRegisterModal() { + return showFullscreenWindow( + 'REGISTER', + t`Register New Account`, + ); +} + +export function showForgotPasswordModal() { + return showFullscreenWindow( + 'FORGOT_PASSWORD', + t`Restore my Password`, + ); +} + +export function showHelpModal() { + return showFullscreenWindow( + 'HELP', + t`Welcome to PixelPlanet.fun`, + ); +} +export function showArchiveModal() { + return showFullscreenWindow( + 'ARCHIVE', + t`Look at past Canvases`, + ); +} + +export function showCanvasSelectionModal() { + return showFullscreenWindow( + 'CANVAS_SELECTION', + '', + ); +} + +export function addToChatInputMessage(windowId, msg, focus = true) { + return (dispatch, getState) => { + const args = getState().windows.args[windowId]; + let inputMessage = args && args.inputMessage; + if (!inputMessage) { + inputMessage = ''; + } else if (inputMessage.slice(-1) !== ' ') { + inputMessage += ' '; + } + inputMessage += msg; + + dispatch(setWindowArgs(windowId, { + inputMessage, + })); + + if (focus) { + const inputElem = document.getElementById(`chtipt-${windowId}`); + if (inputElem) { + inputElem.focus(); + } + } + }; +} + +export function closeWindow(windowId) { + return { + type: 'CLOSE_WINDOW', + windowId, + }; +} + +export function removeWindow(windowId) { + return { + type: 'REMOVE_WINDOW', + windowId, + }; +} + +export function focusWindow(windowId) { + return { + type: 'FOCUS_WINDOW', + windowId, + }; +} + +export function cloneWindow(windowId) { + return { + type: 'CLONE_WINDOW', + windowId, + }; +} + +export function toggleMaximizeWindow(windowId) { + return { + type: 'TOGGLE_MAXIMIZE_WINDOW', + windowId, + }; +} + +export function moveWindow(windowId, xDiff, yDiff) { + return { + type: 'MOVE_WINDOW', + windowId, + xDiff, + yDiff, + }; +} + +export function resizeWindow(windowId, xDiff, yDiff) { + return { + type: 'RESIZE_WINDOW', + windowId, + xDiff, + yDiff, + }; +} + +export function closeAllWindowTypes(windowType) { + return { + type: 'CLOSE_ALL_WINDOW_TYPE', + windowType, + }; +} + +export function hideAllWindowTypes( + windowType, + hide, +) { + return { + type: 'HIDE_ALL_WINDOW_TYPE', + windowType, + hide, + }; +} + +export function openChatWindow() { + const width = 350; + const height = 350; + return openWindow( + 'CHAT', + '', + null, + false, + true, + window.innerWidth - width - 62, + window.innerHeight - height - 64, + width, + height, + ); +} diff --git a/src/store/configureStore.js b/src/store/configureStore.js index f76740e..e82599e 100644 --- a/src/store/configureStore.js +++ b/src/store/configureStore.js @@ -1,7 +1,15 @@ -import { applyMiddleware, createStore, compose } from 'redux'; +/* + * redux store + */ + +/* eslint-disable no-console */ + +import { + applyMiddleware, createStore, compose, combineReducers, +} from 'redux'; import thunk from 'redux-thunk'; -import { persistStore, persistCombineReducers } from 'redux-persist'; -import localForage from 'localforage'; +import { persistStore, persistReducer } from 'redux-persist'; +import storage from 'redux-persist/lib/storage'; /* * reducers @@ -29,12 +37,23 @@ import array from './middleware/array'; import promise from './middleware/promise'; import notifications from './middleware/notifications'; import title from './middleware/title'; -import placePixelControl from './middleware/placePixelControl'; +// import placePixelControl from './middleware/placePixelControl'; import extensions from './middleware/extensions'; -const reducers = persistCombineReducers({ +const CURRENT_VERSION = 3; + +const reducers = persistReducer({ key: 'primary', - storage: localForage, + storage, + version: CURRENT_VERSION, + migrate: (state, version) => { + if (version !== CURRENT_VERSION) { + console.log('Newer version run, resetting store.'); + return Promise.resolve({}); + } + console.log(`Store version: ${version}`); + return Promise.resolve(state); + }, blacklist: [ 'user', 'canvas', @@ -43,7 +62,7 @@ const reducers = persistCombineReducers({ 'contextMenu', 'fetching', ], -}, { +}, combineReducers({ audio, canvas, gui, @@ -55,7 +74,7 @@ const reducers = persistCombineReducers({ contextMenu, chatRead, fetching, -}); +})); const store = createStore( reducers, @@ -70,7 +89,7 @@ const store = createStore( title, socketClientHook, rendererHook, - placePixelControl, + // placePixelControl, extensions, ), ), diff --git a/src/store/middleware/audio.js b/src/store/middleware/audio.js index 1d82bee..19004ae 100644 --- a/src/store/middleware/audio.js +++ b/src/store/middleware/audio.js @@ -68,30 +68,6 @@ export default (store) => (next) => (action) => { break; } - case 'PIXEL_WAIT': { - /* - * TODO refactor other sounds also like this one - * i.e. gain sould ramp to 0, do oscillator first - */ - const oscillatorNode = context.createOscillator(); - const gainNode = context.createGain(); - const { currentTime } = context; - - oscillatorNode.type = 'sine'; - oscillatorNode.start(currentTime); - oscillatorNode.frequency.setValueAtTime(1479.98, currentTime); - oscillatorNode.frequency.exponentialRampToValueAtTime( - 493.88, - currentTime + 0.01, - ); - oscillatorNode.stop(currentTime + 0.1); - gainNode.gain.setValueAtTime(0.5, currentTime); - gainNode.gain.setTargetAtTime(0, currentTime, 0.1); - oscillatorNode.connect(gainNode); - gainNode.connect(context.destination); - break; - } - case 'ALERT': { const oscillatorNode = context.createOscillator(); const gainNode = context.createGain(); @@ -143,40 +119,62 @@ export default (store) => (next) => (action) => { break; } - case 'PLACED_PIXELS': { - const { palette, selectedColor: color } = state.canvas; - const colorsAmount = palette.colors.length; + case 'RECEIVE_PIXEL_RETURN': { + switch (action.retCode) { + case 0: { + // successfully placed pixel + const { palette, selectedColor: color } = state.canvas; + const colorsAmount = palette.colors.length; - const clrFreq = 100 + Math.log(color / colorsAmount + 1) * 300; - const oscillatorNode = context.createOscillator(); - const gainNode = context.createGain(); - const { currentTime } = context; + const clrFreq = 100 + Math.log(color / colorsAmount + 1) * 300; + const oscillatorNode = context.createOscillator(); + const gainNode = context.createGain(); + const { currentTime } = context; - oscillatorNode.type = 'sine'; - oscillatorNode.frequency.setValueAtTime(clrFreq, currentTime); - oscillatorNode.frequency.exponentialRampToValueAtTime( - 1400, - currentTime + 0.2, - ); + oscillatorNode.type = 'sine'; + oscillatorNode.start(currentTime); + oscillatorNode.frequency.setValueAtTime(clrFreq, currentTime); + oscillatorNode.frequency.exponentialRampToValueAtTime( + 1400, + currentTime + 0.2, + ); + oscillatorNode.stop(currentTime + 0.1); + gainNode.gain.setValueAtTime(0.5, currentTime); + gainNode.gain.setTargetAtTime(0, currentTime, 0.1); + oscillatorNode.connect(gainNode); + gainNode.connect(context.destination); + break; + } + case 9: { + // pixelstack used up + const oscillatorNode = context.createOscillator(); + const gainNode = context.createGain(); + const { currentTime } = context; - gainNode.gain.setValueAtTime(0.5, currentTime); - gainNode.gain.exponentialRampToValueAtTime( - 0.2, - currentTime + 0.1, - ); - - oscillatorNode.connect(gainNode); - gainNode.connect(context.destination); - - oscillatorNode.start(); - oscillatorNode.stop(currentTime + 0.1); + oscillatorNode.type = 'sine'; + oscillatorNode.start(currentTime); + oscillatorNode.frequency.setValueAtTime(1479.98, currentTime); + oscillatorNode.frequency.exponentialRampToValueAtTime( + 493.88, + currentTime + 0.01, + ); + oscillatorNode.stop(currentTime + 0.1); + gainNode.gain.setValueAtTime(0.5, currentTime); + gainNode.gain.setTargetAtTime(0, currentTime, 0.1); + oscillatorNode.connect(gainNode); + gainNode.connect(context.destination); + break; + } + default: + // nothing + } break; } case 'COOLDOWN_END': { // do not play sound if last cooldown end was <5s ago const { lastCoolDownEnd } = state.user; - if (lastCoolDownEnd && lastCoolDownEnd.getTime() + 5000 > Date.now()) { + if (lastCoolDownEnd && lastCoolDownEnd + 5000 > Date.now()) { break; } diff --git a/src/store/middleware/notifications.js b/src/store/middleware/notifications.js index e76899e..dc6220a 100644 --- a/src/store/middleware/notifications.js +++ b/src/store/middleware/notifications.js @@ -2,14 +2,13 @@ * Notifications * */ - +import { t } from 'ttag'; export default (store) => (next) => (action) => { try { if (!document.hasFocus()) { switch (action.type) { - case 'RECEIVE_ME': - case 'PLACED_PIXELS': { + case 'RECEIVE_ME': { if (window.Notification && Notification.permission !== 'granted' && Notification.permission !== 'denied' @@ -25,17 +24,17 @@ export default (store) => (next) => (action) => { // do not notify if last cooldown end was <15s ago const { lastCoolDownEnd } = state.user; if (lastCoolDownEnd - && lastCoolDownEnd.getTime() + 15000 > Date.now()) { + && lastCoolDownEnd + 15000 > Date.now()) { break; } if (window.Notification && Notification.permission === 'granted') { // eslint-disable-next-line no-new - new Notification('Your next pixels are ready', { + new Notification(t`Your next pixels are ready`, { icon: '/tile.png', silent: false, vibrate: [200, 100], - body: 'You can now place more on pixelplanet.fun :)', + body: t`You can now place more on pixelplanet.fun :)`, }); } break; @@ -52,11 +51,11 @@ export default (store) => (next) => (action) => { if (window.Notification && Notification.permission === 'granted') { // eslint-disable-next-line no-new - new Notification(`${name} mentioned you`, { + new Notification(`${name} ${t`mentioned you`}`, { icon: '/tile.png', silent: false, vibrate: [200, 100], - body: 'You have new messages in chat', + body: t`You have new messages in chat`, }); } break; diff --git a/src/store/middleware/placePixelControl.js b/src/store/middleware/placePixelControl.js index a4ed170..2a52cd6 100644 --- a/src/store/middleware/placePixelControl.js +++ b/src/store/middleware/placePixelControl.js @@ -3,17 +3,36 @@ * */ -import { requestFromQueue } from '../../ui/placePixel'; +import { getRenderer } from '../../ui/renderer'; +import { receivePixelReturn } from '../../ui/placePixel'; export default (store) => (next) => (action) => { + const ret = next(action); + switch (action.type) { - case 'CLOSE_ALERT': { - requestFromQueue(store); + case 'RECEIVE_PIXEL_RETURN': { + const renderer = getRenderer(); + const { + retCode, + wait, + coolDownSeconds, + pxlCnt, + rankedPxlCnt, + } = action; + receivePixelReturn( + store, + renderer, + retCode, + wait, + coolDownSeconds, + pxlCnt, + rankedPxlCnt, + ); break; } default: // nothing } - return next(action); + return ret; }; diff --git a/src/store/middleware/rendererHook.js b/src/store/middleware/rendererHook.js index 6195a82..0f915aa 100644 --- a/src/store/middleware/rendererHook.js +++ b/src/store/middleware/rendererHook.js @@ -95,7 +95,7 @@ export default (store) => (next) => (action) => { } case 'TOGGLE_GRID': - case 'SET_REQUESTING_PIXEL': { + case 'ALLOW_SETTING_PIXEL': { const renderer = getRenderer(); renderer.forceNextSubrender = true; break; @@ -108,30 +108,21 @@ export default (store) => (next) => (action) => { break; } - case 'UPDATE_PIXEL': { - const { - i, - j, - offset, - color, - notify, - } = action; - const renderer = getRenderer(); - renderer.renderPixel(i, j, offset, color, notify); - break; - } - case 'SET_VIEW_COORDINATES': { const renderer = getRenderer(); renderer.updateView(state); break; } - case 'COOLDOWN_DELTA': { - const { delta } = action; + case 'RECEIVE_PIXEL_RETURN': { const renderer = getRenderer(); - if (renderer && renderer.controls && renderer.controls.gotCoolDownDelta) { - renderer.controls.gotCoolDownDelta(delta * -1); + renderer.forceNextSubrender = true; + const { coolDownSeconds } = action; + if (coolDownSeconds < 0 + && renderer && renderer.controls + && renderer.controls.gotCoolDownDelta + ) { + renderer.controls.gotCoolDownDelta(coolDownSeconds * -1); } break; } diff --git a/src/store/reducers/ranks.js b/src/store/reducers/ranks.js index acfb79a..b9d5df2 100644 --- a/src/store/reducers/ranks.js +++ b/src/store/reducers/ranks.js @@ -24,11 +24,16 @@ export default function ranks( action, ) { switch (action.type) { - case 'PLACED_PIXELS': { + case 'RECEIVE_PIXEL_RETURN': { + const { + rankedPxlCnt, + } = action; + if (!rankedPxlCnt) { + return state; + } let { totalPixels, dailyTotalPixels } = state; - const { amount } = action; - totalPixels += amount; - dailyTotalPixels += amount; + totalPixels += rankedPxlCnt; + dailyTotalPixels += rankedPxlCnt; return { ...state, totalPixels, diff --git a/src/store/reducers/user.js b/src/store/reducers/user.js index b1d77f9..0cb21a7 100644 --- a/src/store/reducers/user.js +++ b/src/store/reducers/user.js @@ -4,7 +4,7 @@ const initialState = { wait: null, coolDown: null, // ms lastCoolDownEnd: null, - requestingPixel: true, + allowSettingPixel: true, // messages are sent by api/me, like not_verified status messages: [], mailreg: false, @@ -35,34 +35,34 @@ export default function user( return { ...state, coolDown: null, - lastCoolDownEnd: new Date(), + lastCoolDownEnd: Date().now(), wait: null, }; } - case 'SET_REQUESTING_PIXEL': { - const { requestingPixel } = action; + case 'ALLOW_SETTING_PIXEL': { + const { allowSettingPixel } = action; return { ...state, - requestingPixel, + allowSettingPixel, }; } - case 'SET_WAIT': { - const { wait: duration } = action; - const wait = duration - ? new Date(Date.now() + duration) - : null; + case 'RECEIVE_PIXEL_RETURN': { + const { + wait: duration, + } = action; return { ...state, - wait, + wait: (duration) ? Date.now() + duration : state.wait, + allowSettingPixel: true, }; } case 'RECEIVE_COOLDOWN': { const { wait: duration } = action; const wait = duration - ? new Date(Date.now() + duration) + ? Date.now() + duration : null; return { ...state, diff --git a/src/store/reducers/windows.js b/src/store/reducers/windows.js index 19f80c1..84ed18e 100644 --- a/src/store/reducers/windows.js +++ b/src/store/reducers/windows.js @@ -73,13 +73,20 @@ function clampPos(prefXPos, prefYPos, width, height) { */ function sortWindows(newState, force = false) { if (newState.zMax >= MAX_AMOUNT_WINDOWS * 0.5 || force) { - const orderedZ = newState.windows.map((win) => win.z) + const positions = { ...newState.positions }; + const ids = Object.keys(positions); + const orderedZ = ids + .map((id) => positions[id].z) .sort((a, b) => !b || (a && a >= b)); - newState.windows = newState.windows.map((win) => ({ - ...win, - z: orderedZ.indexOf(win.z), - })); + for (let i = 0; i < ids.length; i += 1) { + const id = ids[i]; + positions[id] = { + ...positions[id], + z: orderedZ.indexOf(positions[id].z), + }; + } newState.zMax = orderedZ.length - 1; + newState.positions = positions; } return newState; } @@ -87,8 +94,6 @@ function sortWindows(newState, force = false) { const initialState = { // if windows get shown, false on small screens showWindows: window.innerWidth > SCREEN_WIDTH_THRESHOLD, - // if at least one window is in fullscreen - someFullscreen: false, // highest zIndex of window zMax: 0, // [ @@ -97,20 +102,25 @@ const initialState = { // open: boolean, // hidden: boolean, // fullscreen: boolean, - // z: number, // windowType: string, // title: string, // title that is additionally shown to the window-type-title - // width: number, - // height: number, - // xPos: percentage, - // yPos: percentage, // cloneable: boolean, // }, // ] windows: [], // { // windowId: { + // width: number, + // height: number, + // xPos: percentage, + // yPos: percentage, + // z: number, + // } + // } + positions: {}, + // { + // windowId: { // ... // } // } @@ -152,39 +162,38 @@ export default function windows( } const windowId = generateWindowId(state); const newZMax = state.zMax + 1; - const newWindows = [ - ...state.windows, - { - windowId, - windowType, - open: true, - hidden: false, - fullscreen, - z: newZMax, - title, - width, - height, - xPos, - yPos, - cloneable, - }, - ]; - - const someFullscreen = newWindows.some( - (win) => win.fullscreen && !win.hidden, - ); return sortWindows({ ...state, - someFullscreen, zMax: newZMax, - windows: newWindows, + windows: [ + ...state.windows, + { + windowId, + windowType, + open: true, + hidden: false, + fullscreen, + title, + cloneable, + }, + ], args: { ...state.args, [windowId]: { ...args, }, }, + positions: { + ...state.positions, + [windowId]: { + width, + height, + xPos, + yPos, + z: newZMax, + }, + }, }); } @@ -193,12 +202,15 @@ export default function windows( windowId, } = action; const args = { ...state.args }; + const positions = { ...state.positions }; delete args[windowId]; + delete positions[windowId]; return { ...state, windows: state.windows.filter((win) => win.windowId !== windowId), args, + positions, }; } @@ -215,13 +227,8 @@ export default function windows( }; }); - const someFullscreen = newWindows.some( - (win) => win.fullscreen && !win.hidden, - ); - return { ...state, - someFullscreen, windows: newWindows, }; } @@ -238,13 +245,8 @@ export default function windows( }; }); - const someFullscreen = newWindows.some( - (win) => win.fullscreen && !win.hidden, - ); - return { ...state, - someFullscreen, windows: newWindows, }; } @@ -272,6 +274,7 @@ export default function windows( windowId, } = action; const win = state.windows.find((w) => w.windowId === windowId); + const position = state.positions[windowId]; const newWindowId = generateWindowId(state); const newZMax = state.zMax + 1; const { @@ -286,9 +289,6 @@ export default function windows( { ...win, windowId: newWindowId, - xPos: Math.min(win.xPos + 15, width - SCREEN_MARGIN_EW), - yPos: Math.min(win.yPos + 15, height - SCREEN_MARGIN_S), - z: newZMax, }, ], args: { @@ -297,6 +297,15 @@ export default function windows( ...state.args[windowId], }, }, + positions: { + ...state.positions, + [newWindowId]: { + ...position, + xPos: Math.min(position.xPos + 15, width - SCREEN_MARGIN_EW), + yPos: Math.min(position.yPos + 15, height - SCREEN_MARGIN_S), + z: newZMax, + }, + }, }); } @@ -321,13 +330,8 @@ export default function windows( }; }); - const someFullscreen = newWindows.some( - (win) => win.fullscreen && !win.hidden, - ); - return { ...state, - someFullscreen, args, windows: newWindows, }; @@ -338,29 +342,22 @@ export default function windows( windowId, } = action; const { - windows: oldWindows, zMax, + zMax, } = state; - - const newWindows = []; - - for (let i = 0; i < oldWindows.length; i += 1) { - const win = oldWindows[i]; - if (win.windowId !== windowId) { - newWindows.push(win); - } else { - if (win.z === zMax) { - return state; - } - newWindows.push({ - ...win, - z: zMax + 1, - }); - } + const { z } = state.positions[windowId]; + if (z === zMax) { + return state; } return sortWindows({ ...state, zMax: zMax + 1, - windows: newWindows, + positions: { + ...state.positions, + [windowId]: { + ...state.positions[windowId], + z: zMax + 1, + }, + }, }); } @@ -379,13 +376,8 @@ export default function windows( }; }); - const someFullscreen = newWindows.some( - (win) => win.fullscreen && !win.hidden, - ); - return { ...state, - someFullscreen, windows: newWindows, }; } @@ -403,7 +395,6 @@ export default function windows( return { ...state, - someFullscreen: false, windows: newWindows, }; } @@ -414,23 +405,23 @@ export default function windows( xDiff, yDiff, } = action; - const newWindows = state.windows.map((win) => { - if (win.windowId !== windowId) return win; - const [xPos, yPos] = clampPos( - win.xPos + xDiff, - win.yPos + yDiff, - win.width, - win.height, - ); - return { - ...win, - xPos, - yPos, - }; - }); + let { + xPos, yPos, + } = state.positions[windowId]; + const { + width, height, + } = state.positions[windowId]; + [xPos, yPos] = clampPos(xPos + xDiff, yPos + yDiff, width, height); return { ...state, - windows: newWindows, + positions: { + ...state.positions, + [windowId]: { + ...state.positions[windowId], + xPos, + yPos, + }, + }, }; } @@ -440,22 +431,18 @@ export default function windows( xDiff, yDiff, } = action; - const newWindows = state.windows.map((win) => { - if (win.windowId !== windowId) return win; - const [width, height] = clampSize( - win.width + xDiff, - win.height + yDiff, - false, - ); - return { - ...win, - width: Math.max(width, SCREEN_MARGIN_EW - win.xPos), - height, - }; - }); + let { width, height } = state.positions[windowId]; + [width, height] = clampSize(width + xDiff, height + yDiff, false); return { ...state, - windows: newWindows, + positions: { + ...state.positions, + [windowId]: { + ...state.positions[windowId], + width, + height, + }, + }, }; } @@ -466,16 +453,17 @@ export default function windows( innerHeight: height, } = window; - let { windows: newWindows, args, someFullscreen } = state; + let { windows: newWindows, args, positions } = state; const showWindows = width > SCREEN_WIDTH_THRESHOLD; if (action.type === 'RECEIVE_ME') { - if (state.modal) { - // reset if out of date + if (!showWindows) { + // reset on phones on every refresh return initialState; } - args = { ...state.args }; + args = { ...args }; + positions = { ...positions }; newWindows = newWindows.filter((win) => { if (win.open && (win.fullscreen || showWindows)) { @@ -486,12 +474,9 @@ export default function windows( `Cleaning up window from previous session: ${win.windowId}`, ); delete args[win.windowId]; + delete positions[win.windowId]; return false; }); - - someFullscreen = newWindows.some( - (win) => win.fullscreen && !win.hidden, - ); } if (!showWindows) { @@ -499,45 +484,47 @@ export default function windows( ...state, windows: newWindows, showWindows, - someFullscreen, args, + positions, }; } const xMax = width - SCREEN_MARGIN_EW; const yMax = height - SCREEN_MARGIN_S; let modified = false; - const fixWindows = []; + const newPositions = {}; for (let i = 0; i < newWindows.length; i += 1) { - const win = newWindows[i]; + const id = newWindows[i].windowId; const { xPos, yPos, width: winWidth, height: winHeight, - } = win; + } = positions[id]; if (xPos > xMax || yPos > yMax || width > winWidth || height > winHeight) { modified = true; - fixWindows.push({ - ...win, + newPositions[id] = { xPos: Math.min(xMax, xPos), yPos: Math.min(yMax, yPos), width: Math.min(winWidth, width - SCREEN_MARGIN_S), height: Math.min(winHeight, height - SCREEN_MARGIN_S), - }); + }; } else { - fixWindows.push(win); + newPositions[id] = positions[id]; } } + if (modified) { + positions = newPositions; + } return { ...state, - windows: (modified) ? fixWindows : newWindows, + windows: newWindows, showWindows: true, - someFullscreen, args, + positions, }; } diff --git a/src/store/selectors/windows.js b/src/store/selectors/windows.js new file mode 100644 index 0000000..6fff03b --- /dev/null +++ b/src/store/selectors/windows.js @@ -0,0 +1,46 @@ +/* + * selectors for window manager + * + * Memoize heavy selectors or else they recalculate on any store update + * see https://redux.js.org/usage/deriving-data-selectors + */ +import { createSelector } from 'reselect'; + +const selectWindows = (state) => state.windows.windows; +export const selectShowWindows = (state) => state.windows.showWindows; + +export const selectIfFullscreen = createSelector( + selectWindows, + selectShowWindows, + (windows, showWindows) => [ + windows.some((win) => win.fullscreen && !win.hidden) || showWindows, + windows.some((win) => win.fullscreen && win.open && !win.hidden), + ], +); + +export const selectActiveWindowIds = createSelector( + selectWindows, + selectShowWindows, + (windows, showWindows) => { + if (!showWindows) { + windows = windows.filter((win) => win.fullscreen); + } + return windows.map((win) => win.windowId); + }, +); + +/* + * function factory returning a selector for given windowId + * use useMemo to cache result + */ +export const makeSelectWindowById = (windowId) => createSelector( + selectWindows, + (windows) => windows.find((win) => win.windowId === windowId), +); + +/* + * function factory for non-memorized selector + * use useMemo to cache result + */ +// eslint-disable-next-line max-len +export const makeSelectWindowPosById = (windowId) => (state) => state.windows.positions[windowId]; diff --git a/src/ui/Renderer2D.js b/src/ui/Renderer2D.js index 3a4a1b9..70ec66f 100644 --- a/src/ui/Renderer2D.js +++ b/src/ui/Renderer2D.js @@ -424,7 +424,7 @@ class Renderer { isLightGrid, } = state.gui; const { - requestingPixel, + allowSettingPixel, } = state.user; const { view, @@ -444,13 +444,13 @@ class Renderer { // if we have to render placeholder const doRenderPlaceholder = ( viewscale >= 3 - && requestingPixel + && allowSettingPixel && (hover || this.hover) && !isPotato ); const doRenderPotatoPlaceholder = ( viewscale >= 3 - && requestingPixel + && allowSettingPixel && (hover !== this.hover || this.forceNextRender || this.forceNextSubrender diff --git a/src/ui/Renderer3D.js b/src/ui/Renderer3D.js index 7b07895..c1451c7 100644 --- a/src/ui/Renderer3D.js +++ b/src/ui/Renderer3D.js @@ -398,7 +398,7 @@ class Renderer { store, } = this; const { - requestingPixel, + allowSettingPixel, } = store.getState().user; mouse.set( @@ -414,7 +414,7 @@ class Renderer { .add(intersect.face.normal.multiplyScalar(0.5)) .floor() .addScalar(0.5); - if (!requestingPixel + if (!allowSettingPixel || target.clone().sub(camera.position).length() > 120) { rollOverMesh.position.y = -10; } else { @@ -442,7 +442,7 @@ class Renderer { store, } = this; const { - requestingPixel, + allowSettingPixel, } = store.getState().user; mouse.set(0, 0); @@ -454,9 +454,9 @@ class Renderer { .add(intersect.face.normal.multiplyScalar(0.5)) .floor() .addScalar(0.5); - // TODO make rollOverMesh in a different color while requestingPixel false + // TODO make rollOverMesh in a different color while allowSettingPixel false // instead of hiding it.... we can now queue Voxels - if (!requestingPixel + if (!allowSettingPixel || target.clone().sub(camera.position).length() > 50) { rollOverMesh.position.y = -10; } else { @@ -482,6 +482,7 @@ class Renderer { const [i, j] = getChunkOfPixel(canvasSize, x, y, z); const offset = getOffsetOfPixel(canvasSize, x, y, z); tryPlacePixel( + this, store, i, j, offset, @@ -578,10 +579,10 @@ class Renderer { const state = this.store.getState(); const { - requestingPixel, + allowSettingPixel, isOnMobile, } = state.user; - if (!requestingPixel || isOnMobile) { + if (!allowSettingPixel || isOnMobile) { return; } diff --git a/src/ui/placePixel.js b/src/ui/placePixel.js index 6b193a9..ca61fa9 100644 --- a/src/ui/placePixel.js +++ b/src/ui/placePixel.js @@ -3,18 +3,19 @@ * Always just one pixelrequest, queue additional requests to send later * Pixels get predicted on the client and reset if server refused * + * TODO move stuff out of here and to actions / middleware + * clientPredictions could be in rendererHook + * * */ import { t } from 'ttag'; import { - notify, - setRequestingPixel, + setAllowSettingPixel, pAlert, - gotCoolDownDelta, - setWait, - placedPixels, - pixelWait, - updatePixel, + storeReceivePixelReturn, } from '../store/actions'; +import { + notify, +} from '../store/actions/thunks'; import SocketClient from '../socket/SocketClient'; let pixelTimeout = null; @@ -35,8 +36,10 @@ let clientPredictions = []; */ let lastRequestValues = {}; - -export function requestFromQueue(store) { +/* + * request pixel placement from queue + */ +function requestFromQueue(store) { if (!pixelQueue.length) { pixelTimeout = null; return; @@ -46,7 +49,7 @@ export function requestFromQueue(store) { pixelTimeout = setTimeout(() => { pixelQueue = []; pixelTimeout = null; - store.dispatch(setRequestingPixel(true)); + store.dispatch(setAllowSettingPixel(true)); store.dispatch(pAlert( t`Error :(`, t`Didn't get an answer from pixelplanet. Maybe try to refresh?`, @@ -57,11 +60,14 @@ export function requestFromQueue(store) { lastRequestValues = pixelQueue.shift(); const { i, j, pixels } = lastRequestValues; SocketClient.requestPlacePixels(i, j, pixels); - store.dispatch(setRequestingPixel(false)); + store.dispatch(setAllowSettingPixel(false)); } +/* + * got pixel update from websocket + */ export function receivePixelUpdate( - store, + renderer, i, j, offset, @@ -79,7 +85,7 @@ export function receivePixelUpdate( return; } } - store.dispatch(updatePixel(i, j, offset, color)); + renderer.renderPixel(i, j, offset, color, true); } /* @@ -87,7 +93,7 @@ export function receivePixelUpdate( * @param i, j, offset data of the first pixel that got rejected */ function revertPredictionsAt( - store, + renderer, sI, sJ, sOffset, @@ -111,14 +117,18 @@ function revertPredictionsAt( while (p < clientPredictions.length) { const [i, j, offset, color] = clientPredictions[p]; - store.dispatch(updatePixel(i, j, offset, color, false)); + renderer.renderPixel(i, j, offset, color, false); p += 1; } clientPredictions = []; } +/* + * try to place a pixel + */ export function tryPlacePixel( + renderer, store, i, j, @@ -126,7 +136,7 @@ export function tryPlacePixel( color, curColor, ) { - store.dispatch(updatePixel(i, j, offset, color, false)); + renderer.renderPixel(i, j, offset, color, false); clientPredictions.push([i, j, offset, curColor, color]); if (pixelQueue.length) { @@ -150,28 +160,26 @@ export function tryPlacePixel( } } +/* + * got return from pixel request + */ export function receivePixelReturn( store, - retCode, - wait, - coolDownSeconds, - pxlCnt, - rankedPxlCnt, + renderer, + args, ) { clearTimeout(pixelTimeout); - /* - * the terms coolDown is used in a different meaning here - * coolDown is the delta seconds of the placed pixel - */ - if (wait) { - store.dispatch(setWait(wait)); - } + store.dispatch(storeReceivePixelReturn(args)); + + const { + retCode, + coolDownSeconds, + pxlCnt, + } = args; + if (coolDownSeconds) { store.dispatch(notify(coolDownSeconds)); - if (coolDownSeconds < 0) { - store.dispatch(gotCoolDownDelta(coolDownSeconds)); - } } if (retCode) { @@ -181,7 +189,7 @@ export function receivePixelReturn( */ const { i, j, pixels } = lastRequestValues; const [offset] = pixels[pxlCnt]; - revertPredictionsAt(store, i, j, offset); + revertPredictionsAt(renderer, i, j, offset); pixelQueue = []; } @@ -190,7 +198,6 @@ export function receivePixelReturn( let type = 'error'; switch (retCode) { case 0: - store.dispatch(placedPixels(rankedPxlCnt)); break; case 1: errorTitle = t`Invalid Canvas`; @@ -226,7 +233,6 @@ export function receivePixelReturn( break; case 9: // pixestack used up - store.dispatch(pixelWait()); break; case 10: errorTitle = 'Captcha'; @@ -267,11 +273,6 @@ export function receivePixelReturn( )); } - store.dispatch(setRequestingPixel(true)); - - if (!msg) { - /* start next request if queue isn't empty */ - requestFromQueue(store); - } + requestFromQueue(store); }