change types and notify popups about big window unloading

This commit is contained in:
HF 2022-08-19 21:42:58 +02:00
parent cd9177cc1b
commit 37b8b834b6
24 changed files with 198 additions and 147 deletions

View File

@ -108,9 +108,6 @@ function init() {
window.addEventListener('resize', onWindowResize); window.addEventListener('resize', onWindowResize);
onWindowResize(); onWindowResize();
// listen ofr messages from popups
window.addEventListener('message', store.dispatch);
store.dispatch(initTimer()); store.dispatch(initTimer());
store.dispatch(fetchMe()); store.dispatch(fetchMe());

View File

@ -8,7 +8,7 @@ import React, {
import { useSelector, useDispatch } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux';
import { t } from 'ttag'; import { t } from 'ttag';
import popUps from '../core/popUps'; import { openPopUp } from '../core/popUps';
import { import {
moveWindow, moveWindow,
removeWindow, removeWindow,
@ -82,7 +82,7 @@ const Window = ({ id }) => {
} = position; } = position;
const popUp = useCallback((evt) => { const popUp = useCallback((evt) => {
popUps.open(xPos, yPos, width, height); openPopUp(xPos, yPos, width, height);
close(evt); close(evt);
}, [xPos, yPos, width, height]); }, [xPos, yPos, width, height]);

View File

@ -1,43 +1,31 @@
/* /*
* keeping track of open popups * keeping track of open popups
*/ */
import { unload } from '../store/actions';
class PopUps { class PopUps {
constructor() { constructor() {
this.wins = []; this.wins = [];
this.origin = window.location.origin; this.origin = window.location.origin;
this.closeAll = this.closeAll.bind(this); window.addEventListener('beforeunload', () => {
window.addEventListener('beforeunload', this.closeAll); this.dispatch(unload());
});
} }
open(xPos, yPos, width, height) { add(win) {
let left; const pos = this.wins.indexOf(win);
let top; if (pos === -1) {
try { this.wins.push(win);
left = Math.round(window.top.screenX + xPos);
top = Math.round(window.top.screenY + yPos);
if (Number.isNaN(left) || Number.isNaN(top)) {
throw new Error('NaN');
}
} catch {
left = 0;
top = 0;
}
try {
const newWindow = window.open(
'./win',
'lol',
`popup=yes,width=${width},height=${height},left=${left},top=${top},toolbar=no,status=no,directories=no,menubar=no`,
);
this.wins.push(newWindow);
} catch {
// nothing, just don't bubble up
} }
} }
remove(win) {
const pos = this.wins.indexOf(win);
if (~pos) this.wins.splice(pos, 1);
}
dispatch(msg) { dispatch(msg) {
const { wins } = this; const { wins } = this;
console.log('sending', msg);
try { try {
for (let i = 0; i < wins.length; i += 1) { for (let i = 0; i < wins.length; i += 1) {
const win = wins[i]; const win = wins[i];
@ -64,4 +52,35 @@ class PopUps {
const popUps = new PopUps(); const popUps = new PopUps();
export function openPopUp(xPos, yPos, width, height) {
let left;
let top;
try {
if (window.innerWidth <= 604) {
width = window.innerWidth;
height = window.innerHeight;
left = window.top.screenX;
top = window.top.screenY;
} else {
left = Math.round(window.top.screenX + xPos);
top = Math.round(window.top.screenY + yPos);
}
if (Number.isNaN(left) || Number.isNaN(top)) {
throw new Error('NaN');
}
} catch {
left = 0;
top = 0;
}
try {
return window.open(
'./win',
'lol',
`popup=yes,width=${width},height=${height},left=${left},top=${top},toolbar=no,status=no,directories=no,menubar=no`,
);
} catch {
return null;
}
}
export default popUps; export default popUps;

2
src/fix.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
grep -rli "$1" | xargs -i@ sed -i "s/${1}/s\/${1}/g" @

View File

@ -2,4 +2,5 @@
We use redux as a state manager of our application: We use redux as a state manager of our application:
https://redux.js.org/ https://redux.js.org/
All actions that have a p/ prefix are shared between popups with the parent / popUp middlewares All actions that have a s/ prefix are shared between popups with the parent / popUp middlewares
Actions that are one-way signals, like notifications for window open / closed are prefixed with t/

View File

@ -23,73 +23,73 @@ export function closeAlert() {
export function toggleHistoricalView() { export function toggleHistoricalView() {
return { return {
type: 'TOGGLE_HISTORICAL_VIEW', type: 's/TGL_HISTORICAL_VIEW',
}; };
} }
export function toggleHiddenCanvases() { export function toggleHiddenCanvases() {
return { return {
type: 'TOGGLE_HIDDEN_CANVASES', type: 's/TGL_HIDDEN_CANVASES',
}; };
} }
export function toggleGrid() { export function toggleGrid() {
return { return {
type: 'TOGGLE_GRID', type: 's/TGL_GRID',
}; };
} }
export function togglePixelNotify() { export function togglePixelNotify() {
return { return {
type: 'TOGGLE_PXL_NOTIFY', type: 's/TGL_PXL_NOTIFY',
}; };
} }
export function toggleAutoZoomIn() { export function toggleAutoZoomIn() {
return { return {
type: 'TOGGLE_AUTO_ZOOM_IN', type: 's/TGL_AUTO_ZOOM_IN',
}; };
} }
export function toggleOnlineCanvas() { export function toggleOnlineCanvas() {
return { return {
type: 'TOGGLE_ONLINE_CANVAS', type: 's/TGL_ONLINE_CANVAS',
}; };
} }
export function toggleMute() { export function toggleMute() {
return { return {
type: 'TOGGLE_MUTE', type: 's/TGL_MUTE',
}; };
} }
export function toggleCompactPalette() { export function toggleCompactPalette() {
return { return {
type: 'TOGGLE_COMPACT_PALETTE', type: 's/TGL_COMPACT_PALETTE',
}; };
} }
export function toggleChatNotify() { export function toggleChatNotify() {
return { return {
type: 'TOGGLE_CHAT_NOTIFY', type: 's/TGL_CHAT_NOTIFY',
}; };
} }
export function togglePotatoMode() { export function togglePotatoMode() {
return { return {
type: 'TOGGLE_POTATO_MODE', type: 's/TGL_POTATO_MODE',
}; };
} }
export function toggleLightGrid() { export function toggleLightGrid() {
return { return {
type: 'TOGGLE_LIGHT_GRID', type: 's/TGL_LIGHT_GRID',
}; };
} }
export function toggleOpenPalette() { export function toggleOpenPalette() {
return { return {
type: 'TOGGLE_OPEN_PALETTE', type: 's/TGL_OPEN_PALETTE',
}; };
} }
@ -102,7 +102,7 @@ export function selectStyle(style) {
export function toggleOpenMenu() { export function toggleOpenMenu() {
return { return {
type: 'TOGGLE_OPEN_MENU', type: 's/TGL_OPEN_MENU',
}; };
} }
@ -296,7 +296,7 @@ export function receiveChatMessage(
isRead, isRead,
) { ) {
return { return {
type: 'REC_CHAT_MESSAGE', type: 's/REC_CHAT_MESSAGE',
name, name,
text, text,
country, country,
@ -417,23 +417,9 @@ export function initTimer() {
}; };
} }
export function openChatChannel(cid) {
return {
type: 'OPEN_CHAT_CHANNEL',
cid,
};
}
export function closeChatChannel(cid) {
return {
type: 'CLOSE_CHAT_CHANNEL',
cid,
};
}
export function addChatChannel(channel) { export function addChatChannel(channel) {
return { return {
type: 'ADD_CHAT_CHANNEL', type: 's/ADD_CHAT_CHANNEL',
channel, channel,
}; };
} }
@ -463,21 +449,21 @@ export function blockingDm(blockDm) {
export function removeChatChannel(cid) { export function removeChatChannel(cid) {
return { return {
type: 'REMOVE_CHAT_CHANNEL', type: 's/REMOVE_CHAT_CHANNEL',
cid, cid,
}; };
} }
export function muteChatChannel(cid) { export function muteChatChannel(cid) {
return { return {
type: 'MUTE_CHAT_CHANNEL', type: 's/MUTE_CHAT_CHANNEL',
cid, cid,
}; };
} }
export function unmuteChatChannel(cid) { export function unmuteChatChannel(cid) {
return { return {
type: 'UNMUTE_CHAT_CHANNEL', type: 'UNs/MUTE_CHAT_CHANNEL',
cid, cid,
}; };
} }
@ -503,8 +489,53 @@ export function selectHistoricalTime(date, time) {
} }
export function urlChange() { export function urlChange() {
return (dispatch) => { return reloadUrl();
dispatch(reloadUrl()); }
export function unload() {
return {
type: 't/UNLOAD',
}; };
} }
export function load() {
return {
type: 't/LOAD',
};
}
export function propagateMe(state) {
const {
id,
name,
mailreg,
blockDm,
userlvl,
} = state.user;
const { canvases } = state.canvas;
const {
blocked,
channels,
} = state.chat;
const {
ranking,
dailyRanking,
totalPixels,
dailyTotalPixels,
} = state.ranks;
return {
type: 'REC_ME',
blockDm,
blocked,
canvases,
channels,
dailyRanking,
dailyTotalPixels,
id,
mailreg,
name,
ranking,
totalPixels,
userlvl,
};
}

View File

@ -30,7 +30,7 @@ function setApiFetching(fetching) {
function setChatFetching(fetching) { function setChatFetching(fetching) {
return { return {
type: 'SET_CHAT_FETCHING', type: 's/SET_CHAT_FETCHING',
fetching, fetching,
}; };
} }
@ -40,7 +40,7 @@ function receiveChatHistory(
history, history,
) { ) {
return { return {
type: 'REC_CHAT_HISTORY', type: 's/REC_CHAT_HISTORY',
cid, cid,
history, history,
}; };

View File

@ -156,7 +156,7 @@ export function cloneWindow(windowId) {
export function toggleMaximizeWindow(windowId) { export function toggleMaximizeWindow(windowId) {
return { return {
type: 'TOGGLE_MAXIMIZE_WIN', type: 's/TGL_MAXIMIZE_WIN',
windowId, windowId,
}; };
} }

View File

@ -207,7 +207,7 @@ export default (store) => (next) => (action) => {
break; break;
} }
case 'REC_CHAT_MESSAGE': { case 's/REC_CHAT_MESSAGE': {
if (chatNotify) break; if (chatNotify) break;
const { isPing, channel } = action; const { isPing, channel } = action;

View File

@ -40,7 +40,7 @@ export default (store) => (next) => (action) => {
break; break;
} }
case 'REC_CHAT_MESSAGE': { case 's/REC_CHAT_MESSAGE': {
const state = store.getState(); const state = store.getState();
const { chatNotify } = state.gui; const { chatNotify } = state.gui;
if (!chatNotify) break; if (!chatNotify) break;

View File

@ -1,31 +1,41 @@
/* /*
* send and receive actions from parent window * send and receive actions from parent window
*/ */
import { load, unload } from '../actions';
const BLACKLIST = [
'SET_HOVER',
'UNSET_HOVER',
'SET_SCALE',
'SET_VIEW_COORDINATES',
];
const { origin } = window.location; const { origin } = window.location;
window.addEventListener('beforeunload', () => {
if (window.opener && !window.closed) {
window.opener.postMessage(unload(), origin);
}
});
export default () => (next) => (action) => { export default () => (next) => (action) => {
if (action instanceof MessageEvent) { if (action instanceof MessageEvent) {
if (action.origin !== origin) { if (action.origin !== origin) {
return null; return null;
} }
if (action.data.type === 't/UNLOAD') {
return null;
}
console.log('GOT', action.data);
return next(action.data); return next(action.data);
} }
if (window.opener if (window.opener
&& !window.opener.closed && !window.opener.closed
&& !BLACKLIST.includes(action.type) && action.type
) { ) {
try { if (action.type === 'HYDRATED') {
window.opener.postMessage(action, origin); window.opener.postMessage(load(), origin);
} catch { } else if (action.type.startsWith('s/')) {
// nothing try {
window.opener.postMessage(action, origin);
} catch {
// nothing
}
} }
} }

View File

@ -1,26 +1,32 @@
/* /*
* send and receive actions from popups * send and receive actions from popups
*/ */
import { propagateMe } from '../actions';
import popUps from '../../core/popUps'; import popUps from '../../core/popUps';
const BLACKLIST = [ export default (store) => (next) => (action) => {
'SET_HOVER',
'UNSET_HOVER',
'SET_SCALE',
'SET_VIEW_COORDINATES',
];
export default () => (next) => (action) => {
if (action instanceof MessageEvent) { if (action instanceof MessageEvent) {
if (action.origin !== window.location.origin) { if (action.origin !== window.location.origin) {
return null; return null;
} }
if (action.data.type === 't/UNLOAD') {
console.log('popup closed');
popUps.remove(action.source);
} else if (action.data.type === 't/LOAD') {
const state = store.getState();
action.source.postMessage(
propagateMe(state),
window.location.origin,
);
popUps.add(action.source);
console.log('popup added');
}
return next(action.data); return next(action.data);
} }
if (popUps.wins.length if (popUps.wins.length
&& !BLACKLIST.includes(action.type) && action.type
&& action.type.indexOf('WIN') === -1 && action.type.startsWith('s/')
) { ) {
popUps.dispatch(action); popUps.dispatch(action);
} }

View File

@ -1,13 +0,0 @@
/*
* consume async function as action
*/
function warn(error) {
// eslint-disable-next-line no-console
console.warn(error.message || error);
throw error; // To let the caller handle the rejection
}
export default () => (next) => (action) => (typeof action.then === 'function'
? Promise.resolve(action).then(next, warn)
: next(action));

View File

@ -61,7 +61,7 @@ export default (store) => (next) => (action) => {
break; break;
} }
case 'TOGGLE_HIDDEN_CANVASES': { case 's/TGL_HIDDEN_CANVASES': {
const renderer = getRenderer(); const renderer = getRenderer();
const { is3D } = state.canvas; const { is3D } = state.canvas;
if (is3D) { if (is3D) {
@ -94,14 +94,14 @@ export default (store) => (next) => (action) => {
break; break;
} }
case 'TOGGLE_GRID': case 's/TGL_GRID':
case 'ALLOW_SETTING_PXL': { case 'ALLOW_SETTING_PXL': {
const renderer = getRenderer(); const renderer = getRenderer();
renderer.forceNextSubrender = true; renderer.forceNextSubrender = true;
break; break;
} }
case 'TOGGLE_HISTORICAL_VIEW': case 's/TGL_HISTORICAL_VIEW':
case 'SET_SCALE': { case 'SET_SCALE': {
const renderer = getRenderer(); const renderer = getRenderer();
renderer.updateScale(state, prevScale); renderer.updateScale(state, prevScale);

View File

@ -213,7 +213,7 @@ export default function canvasReducer(
}; };
} }
case 'TOGGLE_HISTORICAL_VIEW': { case 's/TGL_HISTORICAL_VIEW': {
const { const {
scale, scale,
viewscale, viewscale,
@ -226,7 +226,7 @@ export default function canvasReducer(
}; };
} }
case 'TOGGLE_HIDDEN_CANVASES': { case 's/TGL_HIDDEN_CANVASES': {
return { return {
...state, ...state,
showHiddenCanvases: !state.showHiddenCanvases, showHiddenCanvases: !state.showHiddenCanvases,

View File

@ -105,7 +105,7 @@ export default function chat(
}; };
} }
case 'ADD_CHAT_CHANNEL': { case 's/ADD_CHAT_CHANNEL': {
const { channel } = action; const { channel } = action;
const cid = Number(Object.keys(channel)[0]); const cid = Number(Object.keys(channel)[0]);
if (state.channels[cid]) { if (state.channels[cid]) {
@ -120,7 +120,7 @@ export default function chat(
}; };
} }
case 'REMOVE_CHAT_CHANNEL': { case 's/REMOVE_CHAT_CHANNEL': {
const { cid } = action; const { cid } = action;
if (!state.channels[cid]) { if (!state.channels[cid]) {
return state; return state;
@ -136,7 +136,7 @@ export default function chat(
}; };
} }
case 'REC_CHAT_MESSAGE': { case 's/REC_CHAT_MESSAGE': {
const { const {
name, text, country, channel, user, name, text, country, channel, user,
} = action; } = action;
@ -172,7 +172,7 @@ export default function chat(
}; };
} }
case 'REC_CHAT_HISTORY': { case 's/REC_CHAT_HISTORY': {
const { cid, history } = action; const { cid, history } = action;
for (let i = 0; i < history.length; i += 1) { for (let i = 0; i < history.length; i += 1) {
msgId += 1; msgId += 1;

View File

@ -45,7 +45,7 @@ export default function chatRead(
}; };
} }
case 'ADD_CHAT_CHANNEL': { case 's/ADD_CHAT_CHANNEL': {
const [cid] = Object.keys(action.channel); const [cid] = Object.keys(action.channel);
return { return {
...state, ...state,
@ -60,7 +60,7 @@ export default function chatRead(
}; };
} }
case 'REMOVE_CHAT_CHANNEL': { case 's/REMOVE_CHAT_CHANNEL': {
const { cid } = action; const { cid } = action;
if (!state.readTs[cid]) { if (!state.readTs[cid]) {
return state; return state;
@ -76,7 +76,7 @@ export default function chatRead(
}; };
} }
case 'REC_CHAT_MESSAGE': { case 's/REC_CHAT_MESSAGE': {
const { channel: cid, isRead } = action; const { channel: cid, isRead } = action;
const readTs = isRead const readTs = isRead
? { ? {
@ -131,7 +131,7 @@ export default function chatRead(
}; };
} }
case 'MUTE_CHAT_CHANNEL': { case 's/MUTE_CHAT_CHANNEL': {
const { cid } = action; const { cid } = action;
return { return {
...state, ...state,
@ -142,7 +142,7 @@ export default function chatRead(
}; };
} }
case 'UNMUTE_CHAT_CHANNEL': { case 'UNs/MUTE_CHAT_CHANNEL': {
const { cid } = action; const { cid } = action;
const mute = state.mute.filter((id) => (id !== cid)); const mute = state.mute.filter((id) => (id !== cid));
return { return {

View File

@ -14,7 +14,7 @@ export default function fetching(
action, action,
) { ) {
switch (action.type) { switch (action.type) {
case 'SET_CHAT_FETCHING': { case 's/SET_CHAT_FETCHING': {
const { fetching: fetchingChat } = action; const { fetching: fetchingChat } = action;
return { return {
...state, ...state,

View File

@ -22,63 +22,63 @@ export default function gui(
action, action,
) { ) {
switch (action.type) { switch (action.type) {
case 'TOGGLE_GRID': { case 's/TGL_GRID': {
return { return {
...state, ...state,
showGrid: !state.showGrid, showGrid: !state.showGrid,
}; };
} }
case 'TOGGLE_PXL_NOTIFY': { case 's/TGL_PXL_NOTIFY': {
return { return {
...state, ...state,
showPixelNotify: !state.showPixelNotify, showPixelNotify: !state.showPixelNotify,
}; };
} }
case 'TOGGLE_AUTO_ZOOM_IN': { case 's/TGL_AUTO_ZOOM_IN': {
return { return {
...state, ...state,
autoZoomIn: !state.autoZoomIn, autoZoomIn: !state.autoZoomIn,
}; };
} }
case 'TOGGLE_ONLINE_CANVAS': { case 's/TGL_ONLINE_CANVAS': {
return { return {
...state, ...state,
onlineCanvas: !state.onlineCanvas, onlineCanvas: !state.onlineCanvas,
}; };
} }
case 'TOGGLE_POTATO_MODE': { case 's/TGL_POTATO_MODE': {
return { return {
...state, ...state,
isPotato: !state.isPotato, isPotato: !state.isPotato,
}; };
} }
case 'TOGGLE_LIGHT_GRID': { case 's/TGL_LIGHT_GRID': {
return { return {
...state, ...state,
isLightGrid: !state.isLightGrid, isLightGrid: !state.isLightGrid,
}; };
} }
case 'TOGGLE_COMPACT_PALETTE': { case 's/TGL_COMPACT_PALETTE': {
return { return {
...state, ...state,
compactPalette: !state.compactPalette, compactPalette: !state.compactPalette,
}; };
} }
case 'TOGGLE_OPEN_PALETTE': { case 's/TGL_OPEN_PALETTE': {
return { return {
...state, ...state,
paletteOpen: !state.paletteOpen, paletteOpen: !state.paletteOpen,
}; };
} }
case 'TOGGLE_OPEN_MENU': { case 's/TGL_OPEN_MENU': {
return { return {
...state, ...state,
menuOpen: !state.menuOpen, menuOpen: !state.menuOpen,
@ -109,13 +109,13 @@ export default function gui(
}; };
} }
case 'TOGGLE_MUTE': case 's/TGL_MUTE':
return { return {
...state, ...state,
mute: !state.mute, mute: !state.mute,
}; };
case 'TOGGLE_CHAT_NOTIFY': case 's/TGL_CHAT_NOTIFY':
return { return {
...state, ...state,
chatNotify: !state.chatNotify, chatNotify: !state.chatNotify,

View File

@ -183,9 +183,6 @@ export default function windows(
state = initialState, state = initialState,
action, action,
) { ) {
if (action.type.startsWith('persist')) {
console.log(action);
}
switch (action.type) { switch (action.type) {
case 'OPEN_WIN': { case 'OPEN_WIN': {
/* /*
@ -412,7 +409,7 @@ export default function windows(
}); });
} }
case 'TOGGLE_MAXIMIZE_WIN': { case 's/TGL_MAXIMIZE_WIN': {
const { const {
windowId, windowId,
} = action; } = action;
@ -504,7 +501,6 @@ export default function windows(
return state; return state;
} }
console.log(action);
const loadedState = { const loadedState = {
...state, ...state,
...action.payload, ...action.payload,

View File

@ -14,7 +14,7 @@ import chatRead from './reducers/chatRead';
export const CURRENT_VERSION = 11; export const CURRENT_VERSION = 11;
export const migrate = (state, version) => { export const migrate = (state, version) => {
console.log('migrate', state, version); // eslint-disable-next-line no-underscore-dangle
if (!state || !state._persist || state._persist.version !== version) { if (!state || !state._persist || state._persist.version !== version) {
console.log('Newer version run, resetting store.'); console.log('Newer version run, resetting store.');
return Promise.resolve({}); return Promise.resolve({});

View File

@ -31,7 +31,6 @@ import audio from './middleware/audio';
import socketClientHook from './middleware/socketClientHook'; import socketClientHook from './middleware/socketClientHook';
import rendererHook from './middleware/rendererHook'; import rendererHook from './middleware/rendererHook';
import array from './middleware/array'; import array from './middleware/array';
import promise from './middleware/promise';
import notifications from './middleware/notifications'; import notifications from './middleware/notifications';
import title from './middleware/title'; import title from './middleware/title';
import popUps from './middleware/popUps'; import popUps from './middleware/popUps';
@ -58,7 +57,6 @@ const store = createStore(
reducers, reducers,
applyMiddleware( applyMiddleware(
thunk, thunk,
promise,
array, array,
popUps, popUps,
audio, audio,
@ -70,9 +68,9 @@ const store = createStore(
), ),
); );
export const persistor = persistStore(store, {}, () => {
persistStore(store); window.addEventListener('message', store.dispatch);
store.dispatch({ type: 'HYDRATED' });
window.lol = store; });
export default store; export default store;

View File

@ -20,7 +20,6 @@ import win from './reducers/win';
/* /*
* middleware * middleware
*/ */
import promise from './middleware/promise';
import parent from './middleware/parent'; import parent from './middleware/parent';
const reducers = combineReducers({ const reducers = combineReducers({
@ -33,12 +32,13 @@ const store = createStore(
reducers, reducers,
applyMiddleware( applyMiddleware(
thunk, thunk,
promise,
parent, parent,
), ),
); );
export const persistor = persistStore(store, {}, () => {
export const persistor = persistStore(store); window.addEventListener('message', store.dispatch);
store.dispatch({ type: 'HYDRATED' });
});
export default store; export default store;

View File

@ -6,9 +6,13 @@ import store from './store/storeWin';
import renderAppWin from './components/AppWin'; import renderAppWin from './components/AppWin';
function init() {
// window.addEventListener('message', store.dispatch);
}
init();
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('hello'); console.log('hello');
renderAppWin(document.getElementById('app'), store); renderAppWin(document.getElementById('app'), store);
window.addEventListener('message', store.dispatch);
}); });