move windows to subfolder

This commit is contained in:
HF 2021-04-27 21:30:19 +02:00
parent 7d550d5a20
commit 6d1ad02b26
22 changed files with 483 additions and 320 deletions

View File

@ -1,5 +1,7 @@
/* @flow */ /* @flow */
import { t } from 'ttag';
import type { import type {
Action, Action,
ThunkAction, ThunkAction,
@ -557,42 +559,62 @@ export function initTimer(): ThunkAction {
}; };
} }
export function showModal(modalType: string): Action { export function showModal(modalType: string, title: string): Action {
return { return openWindow(
type: 'SHOW_MODAL',
modalType, modalType,
}; title,
true,
false,
{},
);
} }
export function showSettingsModal(): Action { export function showSettingsModal(): Action {
return showModal('SETTINGS'); return showModal(
'SETTINGS',
t`Settings`,
);
} }
export function showUserAreaModal(): Action { export function showUserAreaModal(): Action {
return showModal('USERAREA'); return showModal(
} 'USERAREA',
t`User Area`,
export function showMinecraftModal(): Action { );
return showModal('MINECRAFT');
} }
export function showRegisterModal(): Action { export function showRegisterModal(): Action {
return showModal('REGISTER'); return showModal(
'REGISTER',
t`Register New Account`,
);
} }
export function showForgotPasswordModal(): Action { export function showForgotPasswordModal(): Action {
return showModal('FORGOT_PASSWORD'); return showModal(
'FORGOT_PASSWORD',
t`Restore my Password`,
);
} }
export function showHelpModal(): Action { export function showHelpModal(): Action {
return showModal('HELP'); return showModal(
'HELP',
t`Welcome to PixelPlanet.fun`,
);
} }
export function showArchiveModal(): Action { export function showArchiveModal(): Action {
return showModal('ARCHIVE'); return showModal(
'ARCHIVE',
t`Canvas Archive`,
);
} }
export function showCanvasSelectionModal(): Action { export function showCanvasSelectionModal(): Action {
return showModal('CANVAS_SELECTION'); return showModal(
'CANVAS_SELECTION',
t`Canvas Selection`,
);
} }
export function showContextMenu( export function showContextMenu(
@ -610,6 +632,7 @@ export function showContextMenu(
}; };
} }
// TODO CHAT MODAL
export function showChatModal(forceModal: boolean = false): Action { export function showChatModal(forceModal: boolean = false): Action {
if (window.innerWidth > 604 && !forceModal) { return toggleChatBox(); } if (window.innerWidth > 604 && !forceModal) { return toggleChatBox(); }
return showModal('CHAT'); return showModal('CHAT');
@ -704,6 +727,53 @@ export function addToChatInputMessage(windowId: number, msg: string): Action {
}; };
} }
/*
* fullscreen means to open as modal
*/
export function openWindow(
windowType: string,
title: string,
fullscreen: boolean,
cloneable: boolean,
args: Object,
): Action {
return {
type: 'OPEN_WINDOW',
windowType,
title,
fullscreen,
cloneable,
args,
};
}
export function closeWindow(windowId): Action {
return {
type: 'CLOSE_WINDOW',
windowId,
};
}
export function cloneWindow(windowId): Action {
return {
type: 'CLONE_WINDOW',
windowId,
};
}
export function maximizeWindow(windowId): Action {
return {
type: 'MAXIMIZE_WINDOW',
windowId,
};
}
export function restoreWindow(): Action {
return {
type: 'RESTORE_WINDOW',
};
}
export function moveWindow(windowId, xDiff, yDiff): Action { export function moveWindow(windowId, xDiff, yDiff): Action {
return { return {
type: 'MOVE_WINDOW', type: 'MOVE_WINDOW',
@ -713,22 +783,20 @@ export function moveWindow(windowId, xDiff, yDiff): Action {
}; };
} }
export function openChatWindow(): Action { export function resizeWindow(windowId, xDiff, yDiff): Action {
return { return {
type: 'OPEN_WINDOW', type: 'RESIZE_WINDOW',
windowType: 'CHAT', windowId,
title: 'chat', xDiff,
width: 700, yDiff,
height: 300,
xPos: 100,
yPos: 100,
args: {
chatChannel: 1,
inputMessage: '',
},
}; };
} }
export function openChatWindow(): Action {
return openWindow('CHAT', t`Chat`, false, true,
{ chatChannel: 1, inputMessage: '' });
}
/* /*
* query: Object with either userId: number or userName: string * query: Object with either userId: number or userName: string
*/ */
@ -823,12 +891,6 @@ export function setLeaveChannel(
}; };
} }
export function hideModal(): Action {
return {
type: 'HIDE_MODAL',
};
}
export function hideContextMenu(): Action { export function hideContextMenu(): Action {
return { return {
type: 'HIDE_CONTEXT_MENU', type: 'HIDE_CONTEXT_MENU',

View File

@ -75,7 +75,19 @@ export type Action =
| { type: 'SET_CHAT_INPUT_MSG', windowId: number, msg: string } | { type: 'SET_CHAT_INPUT_MSG', windowId: number, msg: string }
| { type: 'ADD_CHAT_INPUT_MSG', windowId: number, msg: string } | { type: 'ADD_CHAT_INPUT_MSG', windowId: number, msg: string }
| { type: 'SET_CHAT_FETCHING', fetching: boolean } | { 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: 'CLONE_WINDOW', windowId: number }
| { type: 'MAXIMIZE_WINDOW', windowId: number }
| { type: 'RESTORE_WINDOW' }
| { type: 'MOVE_WINDOW', windowId: number, xDiff: number, yDiff: number } | { type: 'MOVE_WINDOW', windowId: number, xDiff: number, yDiff: number }
| { type: 'RESIZE_WINDOW', windowId: number, xDiff: number, yDiff: number }
| { type: 'BLOCK_USER', userId: number, userName: string } | { type: 'BLOCK_USER', userId: number, userName: string }
| { type: 'UNBLOCK_USER', userId: number, userName: string } | { type: 'UNBLOCK_USER', userId: number, userName: string }
| { type: 'SET_BLOCKING_DM', blockDm: boolean } | { type: 'SET_BLOCKING_DM', blockDm: boolean }
@ -117,14 +129,12 @@ export type Action =
| { type: 'SET_MINECRAFT_NAME', minecraftname: string } | { type: 'SET_MINECRAFT_NAME', minecraftname: string }
| { type: 'SET_MAILREG', mailreg: boolean } | { type: 'SET_MAILREG', mailreg: boolean }
| { type: 'REM_FROM_MESSAGES', message: string } | { type: 'REM_FROM_MESSAGES', message: string }
| { type: 'SHOW_MODAL', modalType: string }
| { type: 'SHOW_CONTEXT_MENU', | { type: 'SHOW_CONTEXT_MENU',
menuType: string, menuType: string,
xPos: number, xPos: number,
yPos: number, yPos: number,
args: Object, args: Object,
} }
| { type: 'HIDE_MODAL' }
| { type: 'HIDE_CONTEXT_MENU' } | { type: 'HIDE_CONTEXT_MENU' }
| { type: 'RELOAD_URL' } | { type: 'RELOAD_URL' }
| { type: 'SET_HISTORICAL_TIME', date: string, time: string } | { type: 'SET_HISTORICAL_TIME', date: string, time: string }

View File

@ -19,7 +19,7 @@ import Menu from './Menu';
import UI from './UI'; import UI from './UI';
import ExpandMenuButton from './ExpandMenuButton'; import ExpandMenuButton from './ExpandMenuButton';
import ModalRoot from './ModalRoot'; import ModalRoot from './ModalRoot';
import WindowsRoot from './WindowsRoot'; import WindowManager from './WindowManager';
const App = () => ( const App = () => (
<div> <div>
@ -34,7 +34,7 @@ const App = () => (
<ExpandMenuButton /> <ExpandMenuButton />
<UI /> <UI />
<ModalRoot /> <ModalRoot />
<WindowsRoot /> <WindowManager />
</IconContext.Provider> </IconContext.Provider>
</div> </div>
); );

View File

@ -1,49 +0,0 @@
/**
*
* @flow
*/
import React, { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import useWindowSize from '../utils/reactHookResize';
import { showChatModal } from '../actions';
import Chat from './Chat';
const ChatBox = () => {
const [render, setRender] = useState(false);
const chatOpen = useSelector((state) => state.modal.chatOpen);
const dispatch = useDispatch();
useEffect(() => {
window.setTimeout(() => {
if (chatOpen) setRender(true);
}, 10);
}, [chatOpen]);
const onTransitionEnd = () => {
if (!chatOpen) setRender(false);
};
const { width } = useWindowSize();
if (width < 604 && chatOpen) {
dispatch(showChatModal(true));
}
return (
(render || chatOpen) && (
<div
className={(chatOpen && render) ? 'chatbox show' : 'chatbox'}
onTransitionEnd={onTransitionEnd}
>
<Chat showExpand />
</div>
)
);
};
export default React.memo(ChatBox);

View File

@ -14,7 +14,6 @@ import { showChatModal, openChatWindow } from '../actions';
const ChatButton = ({ const ChatButton = ({
modalOpen,
chatNotify, chatNotify,
channels, channels,
unread, unread,
@ -24,6 +23,7 @@ const ChatButton = ({
const [unreadAny, setUnreadAny] = useState(false); const [unreadAny, setUnreadAny] = useState(false);
// TODO what do here? // TODO what do here?
const chatOpen = false; const chatOpen = false;
const modalOpen = false;
/* /*
* almost the same as in ChannelDropDown * almost the same as in ChannelDropDown
@ -88,9 +88,6 @@ function mapDispatchToProps(dispatch) {
} }
function mapStateToProps(state) { function mapStateToProps(state) {
const {
modalOpen,
} = state.windows;
const { const {
chatNotify, chatNotify,
} = state.audio; } = state.audio;
@ -102,8 +99,6 @@ function mapStateToProps(state) {
mute, mute,
} = state.chatRead; } = state.chatRead;
return { return {
modalOpen,
chatNotify,
channels, channels,
unread, unread,
mute, mute,

View File

@ -1,38 +0,0 @@
/**
*
* @flow
*/
import React from 'react';
import { t } from 'ttag';
import Chat from './Chat';
const ChatModal = () => (
<div style={{
position: 'fixed',
top: 80,
padding: 10,
bottom: 10,
left: 10,
right: 10,
}}
>
<div
className="inarea"
style={{
height: '95%',
}}
>
<Chat />
</div>
</div>
);
const data = {
content: ChatModal,
title: t`Chat`,
};
export default data;

View File

@ -1,32 +0,0 @@
/**
*
* @flow
*/
import React from 'react';
import { connect } from 'react-redux';
import Creeper from './Creeper.svg';
import { showMinecraftModal } from '../actions';
const MinecraftButton = ({ open }) => (
<div
id="minecraftbutton"
className="actionbuttons"
onClick={open}
role="button"
tabIndex={-1}
>
<Creeper />
</div>
);
function mapDispatchToProps(dispatch) {
return {
open() {
dispatch(showMinecraftModal());
},
};
}
export default connect(null, mapDispatchToProps)(MinecraftButton);

View File

@ -5,88 +5,76 @@
* @flow * @flow
*/ */
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux';
import { MdClose } from 'react-icons/md'; import { MdClose } from 'react-icons/md';
import { t } from 'ttag'; import { t } from 'ttag';
import { import {
hideModal, closeWindow,
restoreWindow,
} from '../actions'; } from '../actions';
import COMPONENTS from './windows';
import HelpModal from './HelpModal';
import SettingsModal from './SettingsModal';
import UserAreaModal from './UserAreaModal';
import RegisterModal from './RegisterModal';
import CanvasSelectModal from './CanvasSelectModal';
import ArchiveModal from './ArchiveModal';
import ChatModal from './ChatModal';
import ForgotPasswordModal from './ForgotPasswordModal';
const MODAL_COMPONENTS = {
NONE: { content: <div />, title: '' },
HELP: HelpModal,
SETTINGS: SettingsModal,
USERAREA: UserAreaModal,
REGISTER: RegisterModal,
FORGOT_PASSWORD: ForgotPasswordModal,
CHAT: ChatModal,
CANVAS_SELECTION: CanvasSelectModal,
ARCHIVE: ArchiveModal,
/* other modals */
};
const ModalRoot = () => { const ModalRoot = () => {
const [render, setRender] = useState(false); const [render, setRender] = useState(false);
const modalType = useSelector((state) => state.windows.modalType); const { windowType, open, title } = useSelector(
const modalOpen = useSelector((state) => state.windows.modalOpen); (state) => state.windows.modal,
);
const {
title,
content: SpecificModal,
} = MODAL_COMPONENTS[modalType || 'NONE'];
const dispatch = useDispatch(); const dispatch = useDispatch();
const close = useCallback(() => {
dispatch(hideModal());
}, [dispatch]);
const onTransitionEnd = () => { const onTransitionEnd = () => {
if (!modalOpen) setRender(false); if (!open) setRender(false);
}; };
useEffect(() => { useEffect(() => {
window.setTimeout(() => { window.setTimeout(() => {
if (modalOpen) setRender(true); if (open) setRender(true);
}, 10); }, 10);
}, [modalOpen]); }, [open]);
if (!windowType) {
return null;
}
const Content = COMPONENTS[windowType || 'NONE'];
return ( return (
(render || modalOpen) (render || open)
&& [ && [
<div <div
className={(modalOpen && render) className={(open && render)
? 'OverlayModal show' ? 'OverlayModal show'
: 'OverlayModal'} : 'OverlayModal'}
onTransitionEnd={onTransitionEnd} onTransitionEnd={onTransitionEnd}
tabIndex={-1} tabIndex={-1}
onClick={close} onClick={() => dispatch(closeWindow(0))}
/>, />,
<div <div
className={(modalOpen && render) ? 'Modal show' : 'Modal'} className={(open && render) ? 'Modal show' : 'Modal'}
> >
<h2 style={{ paddingLeft: '5%' }}>{title}</h2> <h2 style={{ paddingLeft: '5%' }}>{title}</h2>
<div <div
onClick={close} onClick={() => dispatch(closeWindow(0))}
className="ModalClose" className="ModalClose"
role="button" role="button"
label="close" label="close"
title={t`Close`} title={t`Close`}
tabIndex={-1} tabIndex={-1}
><MdClose /></div> ><MdClose /></div>
<SpecificModal windowId={0} /> <div
onClick={() => dispatch(restoreWindow())}
className="ModalRestore"
role="button"
label="restore"
title={t`Restore`}
tabIndex={-1}
></div>
<div className="win-content">
<Content windowId={0} />
</div>
</div>, </div>,
] ]
); );

View File

@ -6,18 +6,17 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { useSelector, useDispatch } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux';
import Chat from './Chat';
import { import {
moveWindow, moveWindow,
resizeWindow,
closeWindow,
maximizeWindow,
cloneWindow,
} from '../actions'; } from '../actions';
import COMPONENTS from './windows';
const selectWindowById = (state, windowId) => state.windows.windows.find((win) => win.windowId === windowId); const selectWindowById = (state, windowId) => state.windows.windows.find((win) => win.windowId === windowId);
const WINDOW_COMPONENTS = {
NONE: <div />,
CHAT: Chat,
};
const Window = ({ id }) => { const Window = ({ id }) => {
const win = useSelector((state) => selectWindowById(state, id)); const win = useSelector((state) => selectWindowById(state, id));
@ -46,6 +45,33 @@ const Window = ({ id }) => {
document.addEventListener('mouseleave', stopMove, { once: true }); document.addEventListener('mouseleave', stopMove, { once: true });
}, []); }, []);
const startResize = useCallback((event) => {
event.preventDefault();
let {
clientX: startX,
clientY: startY,
} = event;
const resize = (evt) => {
const {
clientX: curX,
clientY: curY,
} = evt;
dispatch(resizeWindow(id, curX - startX, curY - startY));
startX = curX;
startY = curY;
};
document.addEventListener('mousemove', resize);
const stopResize = () => {
document.removeEventListener('mousemove', resize);
};
document.addEventListener('mouseup', stopResize, { once: true });
document.addEventListener('mouseleave', stopResize, { once: true });
}, []);
if (!win) {
return null;
}
const { const {
width, height, width, height,
xPos, yPos, xPos, yPos,
@ -53,7 +79,7 @@ const Window = ({ id }) => {
title, title,
} = win; } = win;
const Content = WINDOW_COMPONENTS[windowType]; const Content = COMPONENTS[windowType];
console.log(`render window ${id}`); console.log(`render window ${id}`);
@ -71,7 +97,8 @@ const Window = ({ id }) => {
className="win-topbar" className="win-topbar"
> >
<span <span
className="win-topbtnn" className="win-topbtn"
onClick={() => dispatch(cloneWindow(id))}
> >
+ +
</span> </span>
@ -79,20 +106,30 @@ const Window = ({ id }) => {
className="win-title" className="win-title"
onMouseDown={startMove} onMouseDown={startMove}
> >
Move Here {title}
</span> </span>
<span <span
className="win-topbtnn" className="win-topbtn"
onClick={() => dispatch(maximizeWindow(id))}
> >
</span> </span>
<span <span
className="win-topbtnn" className="win-topbtn"
onClick={() => dispatch(closeWindow(id))}
> >
X X
</span> </span>
</div> </div>
<Content windowId={id} /> <div className="win-content">
<Content windowId={id} />
</div>
<div
onMouseDown={startResize}
className="win-resize"
>
R
</div>
</div> </div>
); );
}; };

View File

@ -14,7 +14,7 @@ const WindowsRoot = () => {
const windowIds = useSelector(selectWindowIds, shallowEqual); const windowIds = useSelector(selectWindowIds, shallowEqual);
return windowIds.map((id) => ( return windowIds.map((id) => (
<Window id={id} /> <Window key={id} id={id} />
)); ));
}; };

View File

@ -14,7 +14,7 @@ const imageStyle = {
verticalAlign: 'middle', verticalAlign: 'middle',
}; };
const ArchiveModal = () => ( const Archive = () => (
<p style={{ textAlign: 'center', paddingLeft: '5%', paddingRight: '5%' }}> <p style={{ textAlign: 'center', paddingLeft: '5%', paddingRight: '5%' }}>
<p className="modaltext"> <p className="modaltext">
{t`While we tend to not delete canvases, some canvases are started for fun or as a request by users who currently like a meme. \ {t`While we tend to not delete canvases, some canvases are started for fun or as a request by users who currently like a meme. \
@ -45,9 +45,4 @@ Those canvases can get boring after a while and after weeks of no major change a
</p> </p>
); );
const data = { export default Archive;
content: ArchiveModal,
title: t`Canvas Archive`,
};
export default data;

View File

@ -7,13 +7,13 @@ import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { t } from 'ttag'; import { t } from 'ttag';
import CanvasItem from './CanvasItem'; import CanvasItem from '../CanvasItem';
import { showArchiveModal } from '../actions'; import { showArchiveModal } from '../../actions';
import type { State } from '../reducers'; import type { State } from '../../reducers';
const CanvasSelectModal = ({ const CanvasSelect = ({
canvases, canvases,
showHiddenCanvases, showHiddenCanvases,
showArchive, showArchive,
@ -62,9 +62,4 @@ function mapStateToProps(state: State) {
return { canvases, showHiddenCanvases }; return { canvases, showHiddenCanvases };
} }
const data = { export default connect(mapStateToProps, mapDispatchToProps)(CanvasSelect);
content: connect(mapStateToProps, mapDispatchToProps)(CanvasSelectModal),
title: t`Canvas Selection`,
};
export default data;

View File

@ -10,9 +10,8 @@ import useStayScrolled from 'react-stay-scrolled';
import { useSelector, useDispatch } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux';
import { t } from 'ttag'; import { t } from 'ttag';
import type { State } from '../reducers'; import ChatMessage from '../ChatMessage';
import ChatMessage from './ChatMessage'; import ChannelDropDown from '../ChannelDropDown';
import ChannelDropDown from './ChannelDropDown';
import { import {
showUserAreaModal, showUserAreaModal,
@ -21,9 +20,9 @@ import {
setChatInputMessage, setChatInputMessage,
fetchChatMessages, fetchChatMessages,
showContextMenu, showContextMenu,
} from '../actions'; } from '../../actions';
import ProtocolClient from '../socket/ProtocolClient'; import ProtocolClient from '../../socket/ProtocolClient';
import splitChatMessage from '../core/chatMessageFilter'; import splitChatMessage from '../../core/chatMessageFilter';
function escapeRegExp(string) { function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
@ -115,13 +114,7 @@ const Chat = ({
return ( return (
<div <div
ref={targetRef} ref={targetRef}
style={{ className="chat-container"
display: 'flex',
position: 'relative',
width: '100%',
height: '100%',
flexDirection: 'column',
}}
> >
<div <div
className="chatlink" className="chatlink"

View File

@ -7,10 +7,10 @@ import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { t } from 'ttag'; import { t } from 'ttag';
import { showUserAreaModal } from '../actions'; import { showUserAreaModal } from '../../actions';
import NewPasswordForm from './NewPasswordForm'; import NewPasswordForm from '../NewPasswordForm';
const ForgotPasswordModal = ({ login }) => ( const ForgotPassword = ({ login }) => (
<p style={{ paddingLeft: '5%', paddingRight: '5%' }}> <p style={{ paddingLeft: '5%', paddingRight: '5%' }}>
<p className="modaltext"> <p className="modaltext">
{t`Enter your mail address and we will send you a new password:`} {t`Enter your mail address and we will send you a new password:`}
@ -32,9 +32,4 @@ function mapDispatchToProps(dispatch) {
}; };
} }
const data = { export default connect(null, mapDispatchToProps)(ForgotPassword);
content: connect(null, mapDispatchToProps)(ForgotPasswordModal),
title: t`Restore my Password`,
};
export default data;

View File

@ -11,7 +11,7 @@ import { MdTouchApp } from 'react-icons/md';
/* eslint-disable max-len */ /* eslint-disable max-len */
const HelpModal = () => { const Help = () => {
const bindG = <kbd>{c('keybinds').t`G`}</kbd>; const bindG = <kbd>{c('keybinds').t`G`}</kbd>;
const bindX = <kbd>{c('keybinds').t`X`}</kbd>; const bindX = <kbd>{c('keybinds').t`X`}</kbd>;
const bindH = <kbd>{c('keybinds').t`H`}</kbd>; const bindH = <kbd>{c('keybinds').t`H`}</kbd>;
@ -89,9 +89,4 @@ can be downloaded from mega.nz here: `}<a href="https://mega.nz/#!JpkBwAbJ!EnSLl
); );
}; };
const data = { export default Help;
content: HelpModal,
title: t`Welcome to PixelPlanet.fun`,
};
export default data;

View File

@ -7,13 +7,13 @@ import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { t } from 'ttag'; import { t } from 'ttag';
import { showUserAreaModal } from '../actions'; import { showUserAreaModal } from '../../actions';
// import { send_registration } from '../ui/register'; // import { send_registration } from '../ui/register';
import SignUpForm from './SignUpForm'; import SignUpForm from '../SignUpForm';
const RegisterModal = ({ login }) => ( const Register = ({ login }) => (
<p style={{ paddingLeft: '5%', paddingRight: '5%' }}> <p style={{ paddingLeft: '5%', paddingRight: '5%' }}>
<p className="modaltext">{t`Register new account here`}</p><br /> <p className="modaltext">{t`Register new account here`}</p><br />
<p style={{ textAlign: 'center' }}> <p style={{ textAlign: 'center' }}>
@ -33,9 +33,4 @@ function mapDispatchToProps(dispatch) {
}; };
} }
const data = { export default connect(null, mapDispatchToProps)(Register);
content: connect(null, mapDispatchToProps)(RegisterModal),
title: t`Register New Account`,
};
export default data;

View File

@ -7,8 +7,8 @@ import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { c, t } from 'ttag'; import { c, t } from 'ttag';
import LanguageSelect from './LanguageSelect'; import LanguageSelect from '../LanguageSelect';
import MdToggleButtonHover from './MdToggleButtonHover'; import MdToggleButtonHover from '../MdToggleButtonHover';
import { import {
toggleGrid, toggleGrid,
togglePixelNotify, togglePixelNotify,
@ -20,9 +20,9 @@ import {
toggleLightGrid, toggleLightGrid,
toggleHistoricalView, toggleHistoricalView,
selectStyle, selectStyle,
} from '../actions'; } from '../../actions';
import type { State } from '../reducers'; import type { State } from '../../reducers';
const flexy = { const flexy = {
@ -97,7 +97,7 @@ const SettingsItem = ({
</div> </div>
); );
function SettingsModal({ function Settings({
isMuted, isMuted,
isGridShown, isGridShown,
isPixelNotifyShown, isPixelNotifyShown,
@ -273,9 +273,4 @@ function mapDispatchToProps(dispatch) {
}; };
} }
const data = { export default connect(mapStateToProps, mapDispatchToProps)(Settings);
content: connect(mapStateToProps, mapDispatchToProps)(SettingsModal),
title: t`Settings`,
};
export default data;

View File

@ -7,21 +7,21 @@ import React, { Suspense } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { t } from 'ttag'; import { t } from 'ttag';
import type { State } from '../reducers'; import type { State } from '../../reducers';
import { import {
showRegisterModal, showForgotPasswordModal, setName, setMailreg, showRegisterModal, showForgotPasswordModal, setName, setMailreg,
} from '../actions'; } from '../../actions';
import LogInForm from './LogInForm'; import LogInForm from '../LogInForm';
import Tabs from './Tabs'; import Tabs from '../Tabs';
import UserArea from './UserArea'; import UserAreaContent from '../UserArea';
import Rankings from './Rankings'; import Rankings from '../Rankings';
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
const Converter = React.lazy(() => import(/* webpackChunkName: "converter" */ './Converter')); const Converter = React.lazy(() => import(/* webpackChunkName: "converter" */ '../Converter'));
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
const Admintools = React.lazy(() => import(/* webpackChunkName: "admintools" */ './Admintools')); const Admintools = React.lazy(() => import(/* webpackChunkName: "admintools" */ '../Admintools'));
const logoStyle = { const logoStyle = {
marginRight: 5, marginRight: 5,
@ -86,7 +86,7 @@ const LogInArea = ({ register, forgotPassword, me }) => (
</p> </p>
); );
const UserAreaModal = ({ const UserArea = ({
name, name,
register, register,
forgotPassword, forgotPassword,
@ -105,7 +105,7 @@ const UserAreaModal = ({
: ( : (
<Tabs> <Tabs>
<div label={t`Profile`}> <div label={t`Profile`}>
<UserArea <UserAreaContent
setName={setUserName} setName={setUserName}
setMailreg={setUserMailreg} setMailreg={setUserMailreg}
/> />
@ -155,9 +155,4 @@ function mapStateToProps(state: State) {
return { name, userlvl }; return { name, userlvl };
} }
const data = { export default connect(mapStateToProps, mapDispatchToProps)(UserArea);
content: connect(mapStateToProps, mapDispatchToProps)(UserAreaModal),
title: t`User Area`,
};
export default data;

View File

@ -0,0 +1,25 @@
/*
* @flow
*/
import Help from './Help';
import Settings from './Settings';
import UserArea from './UserArea';
import Register from './Register';
import CanvasSelect from './CanvasSelect';
import Archive from './Archive';
import Chat from './Chat';
import ForgotPassword from './ForgotPassword';
export default {
NONE: <div />,
HELP: Help,
SETTINGS: Settings,
USERAREA: UserArea,
REGISTER: Register,
FORGOT_PASSWORD: ForgotPassword,
CHAT: Chat,
CANVAS_SELECTION: CanvasSelect,
ARCHIVE: Archive,
/* other modals */
};

View File

@ -6,20 +6,33 @@
import type { Action } from '../actions/types'; import type { Action } from '../actions/types';
function generateWindowId(state) {
let windowId = Math.floor(Math.random() * 99999) + 1;
while (state.args[windowId]) {
windowId += 1;
}
return windowId;
}
export type WindowsState = { export type WindowsState = {
// modal is considerd as "fullscreen window" // modal is considerd as "fullscreen window"
// its windowId is considered 0 and args are under args[0] // its windowId is considered 0 and args are under args[0]
modalOpen: boolean, modal: {
modalType: ?string, windowType: ?string,
title: ?string,
open: boolean,
},
// [ // [
// { // {
// windowId: number, // windowId: number,
// windowOpen: boolean,
// windowType: string, // windowType: string,
// title: string, // title: string,
// width: number, // width: number,
// height: number, // height: number,
// xPos: percentage, // xPos: percentage,
// yPos: percentage, // yPos: percentage,
// cloneable: boolean,
// }, // },
// ] // ]
windows: Array, windows: Array,
@ -32,8 +45,11 @@ export type WindowsState = {
} }
const initialState: WindowsState = { const initialState: WindowsState = {
modalOpen: false, modal: {
modalType: null, windowType: null,
title: null,
open: false,
},
windows: [], windows: [],
args: {}, args: {},
}; };
@ -47,16 +63,27 @@ export default function windows(
const { const {
windowType, windowType,
title, title,
width, fullscreen,
height, cloneable,
xPos,
yPos,
args, args,
} = action; } = action;
let windowId = Math.floor(Math.random() * 99999) + 1; if (fullscreen) {
while (state.args[windowId]) { return {
windowId += 1; ...state,
modal: {
windowType,
title,
open: true,
},
args: {
...state.args,
0: {
...args,
},
},
};
} }
const windowId = generateWindowId(state);
return { return {
...state, ...state,
windows: [ windows: [
@ -64,12 +91,13 @@ export default function windows(
{ {
windowId, windowId,
windowType, windowType,
windowOpen: true,
title, title,
width, width: 600,
height, height: 300,
xPos, xPos: 200,
yPos, yPos: 200,
args, cloneable,
}, },
], ],
args: { args: {
@ -79,10 +107,57 @@ export default function windows(
}; };
} }
case 'REMOVE_WINDOW': {
const {
windowId,
} = action;
const args = { ...state.args };
delete args[windowId];
if (windowId === 0) {
return {
...state,
modal: {
windowType: null,
title: null,
open: false,
},
args,
};
}
return {
...state,
windows: state.windows.filter((win) => win.windowId !== windowId),
args,
};
}
case 'CLOSE_WINDOW': { case 'CLOSE_WINDOW': {
const { const {
windowId, windowId,
} = action; } = action;
if (windowId === 0) {
return {
...state,
modal: {
...state.modal,
open: false,
},
};
}
/*
const newWindows = state.windows.map((win) => {
if (win.windowId !== windowId) return win;
return {
...win,
windowOpen: false,
}
});
return {
...state,
windows: newWindows,
};
*/
const args = { ...state.args }; const args = { ...state.args };
delete args[windowId]; delete args[windowId];
return { return {
@ -92,6 +167,87 @@ export default function windows(
}; };
} }
case 'CLONE_WINDOW': {
const {
windowId,
} = action;
const win = state.windows.find((w) => w.windowId === windowId);
const newWindowId = generateWindowId(state);
return {
...state,
windows: [
...state.windows,
{
...win,
windowId: newWindowId,
xPos: win.xPos + 15,
yPos: win.yPos + 15,
},
],
args: {
...state.args,
[newWindowId]: {
...state.args[windowId],
},
},
};
}
case 'MAXIMIZE_WINDOW': {
const {
windowId,
} = action;
const args = {
...state.args,
0: state.args[windowId],
};
const { windowType, title } = state.windows.find((w) => w.windowId === windowId);
delete args[windowId];
return {
...state,
modal: {
windowType,
title,
open: true,
},
windows: state.windows.filter((w) => w.windowId !== windowId),
args,
};
}
case 'RESTORE_WINDOW': {
const windowId = generateWindowId(state);
const { windowType, title } = state.modal;
const cloneable = true;
return {
...state,
modal: {
...state.modal,
open: false,
},
windows: [
...state.windows,
{
windowType,
windowId,
windowOpen: true,
title,
width: 600,
height: 300,
xPos: 200,
yPos: 200,
cloneable,
},
],
args: {
...state.args,
[windowId]: {
...state.args[0],
},
},
};
}
case 'MOVE_WINDOW': { case 'MOVE_WINDOW': {
const { const {
windowId, windowId,
@ -112,6 +268,26 @@ export default function windows(
}; };
} }
case 'RESIZE_WINDOW': {
const {
windowId,
xDiff,
yDiff,
} = action;
const newWindows = state.windows.map((win) => {
if (win.windowId !== windowId) return win;
return {
...win,
width: win.width + xDiff,
height: win.height + yDiff,
};
});
return {
...state,
windows: newWindows,
};
}
case 'CLOSE_ALL_WINDOWS': { case 'CLOSE_ALL_WINDOWS': {
return initialState; return initialState;
} }

View File

@ -144,6 +144,7 @@ tr:nth-child(even) {
.win-topbar { .win-topbar {
display: flex; display: flex;
height: 16px;
} }
.win-title { .win-title {
@ -155,6 +156,29 @@ tr:nth-child(even) {
border: solid black; border: solid black;
border-width: thin; border-width: thin;
background-color: blue; background-color: blue;
cursor: pointer;
}
.win-resize {
position: absolute;
bottom: 0;
right: 0;
cursor: se-resize;
}
.chat-container {
position: relative;
display: flex;
flex-direction: column;
width: 100%;
height: calc(100% - 4px);
}
.win-content {
position: relative;
width: 100%;
height: calc(100% - 16px);
overflow-y: auto;
} }
.contextmenu { .contextmenu {
@ -444,7 +468,7 @@ tr:nth-child(even) {
width: 75%; width: 75%;
} }
.ModalClose { .ModalClose, .ModalRestore {
position: fixed; position: fixed;
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -459,11 +483,18 @@ tr:nth-child(even) {
background-color: #f6f6f7; background-color: #f6f6f7;
border-color: #dcddde; border-color: #dcddde;
top: 30px; top: 30px;
right: 40px;
z-index: 5; z-index: 5;
} }
.ModalClose:hover { .ModalClose {
right: 40px;
}
.ModalRestore {
right: 90px;
}
.ModalClose:hover, .ModalRestore:hover {
background-color: #e3e3e4; background-color: #e3e3e4;
} }