From 6d1ad02b26b386760be255616ab4c75e0979882d Mon Sep 17 00:00:00 2001 From: HF Date: Tue, 27 Apr 2021 21:30:19 +0200 Subject: [PATCH] move windows to subfolder --- src/actions/index.js | 128 ++++++++--- src/actions/types.js | 14 +- src/components/App.jsx | 4 +- src/components/ChatBox.jsx | 49 ----- src/components/ChatButton.jsx | 7 +- src/components/ChatModal.jsx | 38 ---- src/components/MinecraftButton.jsx | 32 --- src/components/ModalRoot.jsx | 76 +++---- src/components/Window.jsx | 61 ++++- .../{WindowsRoot.jsx => WindowManager.jsx} | 2 +- .../{ArchiveModal.jsx => windows/Archive.jsx} | 9 +- .../CanvasSelect.jsx} | 15 +- src/components/{ => windows}/Chat.jsx | 19 +- .../ForgotPassword.jsx} | 13 +- .../{HelpModal.jsx => windows/Help.jsx} | 9 +- .../Minecraft.jsx} | 0 .../Register.jsx} | 13 +- .../Settings.jsx} | 17 +- .../UserArea.jsx} | 27 +-- src/components/windows/index.jsx | 25 +++ src/reducers/windows.js | 208 ++++++++++++++++-- src/styles/default.css | 37 +++- 22 files changed, 483 insertions(+), 320 deletions(-) delete mode 100644 src/components/ChatBox.jsx delete mode 100644 src/components/ChatModal.jsx delete mode 100644 src/components/MinecraftButton.jsx rename src/components/{WindowsRoot.jsx => WindowManager.jsx} (92%) rename src/components/{ArchiveModal.jsx => windows/Archive.jsx} (93%) rename src/components/{CanvasSelectModal.jsx => windows/CanvasSelect.jsx} (79%) rename src/components/{ => windows}/Chat.jsx (93%) rename src/components/{ForgotPasswordModal.jsx => windows/ForgotPassword.jsx} (69%) rename src/components/{HelpModal.jsx => windows/Help.jsx} (97%) rename src/components/{MinecraftModal.jsx => windows/Minecraft.jsx} (100%) rename src/components/{RegisterModal.jsx => windows/Register.jsx} (71%) rename src/components/{SettingsModal.jsx => windows/Settings.jsx} (95%) rename src/components/{UserAreaModal.jsx => windows/UserArea.jsx} (88%) create mode 100644 src/components/windows/index.jsx diff --git a/src/actions/index.js b/src/actions/index.js index 1a98662..99a60c4 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -1,5 +1,7 @@ /* @flow */ +import { t } from 'ttag'; + import type { Action, ThunkAction, @@ -557,42 +559,62 @@ export function initTimer(): ThunkAction { }; } -export function showModal(modalType: string): Action { - return { - type: 'SHOW_MODAL', +export function showModal(modalType: string, title: string): Action { + return openWindow( modalType, - }; + title, + true, + false, + {}, + ); } export function showSettingsModal(): Action { - return showModal('SETTINGS'); + return showModal( + 'SETTINGS', + t`Settings`, + ); } export function showUserAreaModal(): Action { - return showModal('USERAREA'); -} - -export function showMinecraftModal(): Action { - return showModal('MINECRAFT'); + return showModal( + 'USERAREA', + t`User Area`, + ); } export function showRegisterModal(): Action { - return showModal('REGISTER'); + return showModal( + 'REGISTER', + t`Register New Account`, + ); } export function showForgotPasswordModal(): Action { - return showModal('FORGOT_PASSWORD'); + return showModal( + 'FORGOT_PASSWORD', + t`Restore my Password`, + ); } export function showHelpModal(): Action { - return showModal('HELP'); + return showModal( + 'HELP', + t`Welcome to PixelPlanet.fun`, + ); } export function showArchiveModal(): Action { - return showModal('ARCHIVE'); + return showModal( + 'ARCHIVE', + t`Canvas Archive`, + ); } export function showCanvasSelectionModal(): Action { - return showModal('CANVAS_SELECTION'); + return showModal( + 'CANVAS_SELECTION', + t`Canvas Selection`, + ); } export function showContextMenu( @@ -610,6 +632,7 @@ export function showContextMenu( }; } +// TODO CHAT MODAL export function showChatModal(forceModal: boolean = false): Action { if (window.innerWidth > 604 && !forceModal) { return toggleChatBox(); } 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 { return { 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 { - type: 'OPEN_WINDOW', - windowType: 'CHAT', - title: 'chat', - width: 700, - height: 300, - xPos: 100, - yPos: 100, - args: { - chatChannel: 1, - inputMessage: '', - }, + type: 'RESIZE_WINDOW', + windowId, + xDiff, + yDiff, }; } +export function openChatWindow(): Action { + return openWindow('CHAT', t`Chat`, false, true, + { chatChannel: 1, inputMessage: '' }); +} + /* * 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 { return { type: 'HIDE_CONTEXT_MENU', diff --git a/src/actions/types.js b/src/actions/types.js index 5801255..69b0431 100644 --- a/src/actions/types.js +++ b/src/actions/types.js @@ -75,7 +75,19 @@ export type Action = | { type: 'SET_CHAT_INPUT_MSG', windowId: number, msg: string } | { type: 'ADD_CHAT_INPUT_MSG', windowId: number, msg: string } | { 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: 'RESIZE_WINDOW', windowId: number, xDiff: number, yDiff: number } | { type: 'BLOCK_USER', userId: number, userName: string } | { type: 'UNBLOCK_USER', userId: number, userName: string } | { type: 'SET_BLOCKING_DM', blockDm: boolean } @@ -117,14 +129,12 @@ export type Action = | { type: 'SET_MINECRAFT_NAME', minecraftname: string } | { type: 'SET_MAILREG', mailreg: boolean } | { type: 'REM_FROM_MESSAGES', message: string } - | { type: 'SHOW_MODAL', modalType: string } | { type: 'SHOW_CONTEXT_MENU', menuType: string, xPos: number, yPos: number, args: Object, } - | { type: 'HIDE_MODAL' } | { type: 'HIDE_CONTEXT_MENU' } | { type: 'RELOAD_URL' } | { type: 'SET_HISTORICAL_TIME', date: string, time: string } diff --git a/src/components/App.jsx b/src/components/App.jsx index 14cfdc7..a1c5b38 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -19,7 +19,7 @@ import Menu from './Menu'; import UI from './UI'; import ExpandMenuButton from './ExpandMenuButton'; import ModalRoot from './ModalRoot'; -import WindowsRoot from './WindowsRoot'; +import WindowManager from './WindowManager'; const App = () => (
@@ -34,7 +34,7 @@ const App = () => ( - +
); diff --git a/src/components/ChatBox.jsx b/src/components/ChatBox.jsx deleted file mode 100644 index df0b9ee..0000000 --- a/src/components/ChatBox.jsx +++ /dev/null @@ -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) && ( -
- -
- ) - ); -}; - -export default React.memo(ChatBox); diff --git a/src/components/ChatButton.jsx b/src/components/ChatButton.jsx index 459d93d..92e8c5a 100644 --- a/src/components/ChatButton.jsx +++ b/src/components/ChatButton.jsx @@ -14,7 +14,6 @@ import { showChatModal, openChatWindow } from '../actions'; const ChatButton = ({ - modalOpen, chatNotify, channels, unread, @@ -24,6 +23,7 @@ const ChatButton = ({ const [unreadAny, setUnreadAny] = useState(false); // TODO what do here? const chatOpen = false; + const modalOpen = false; /* * almost the same as in ChannelDropDown @@ -88,9 +88,6 @@ function mapDispatchToProps(dispatch) { } function mapStateToProps(state) { - const { - modalOpen, - } = state.windows; const { chatNotify, } = state.audio; @@ -102,8 +99,6 @@ function mapStateToProps(state) { mute, } = state.chatRead; return { - modalOpen, - chatNotify, channels, unread, mute, diff --git a/src/components/ChatModal.jsx b/src/components/ChatModal.jsx deleted file mode 100644 index 763b4d9..0000000 --- a/src/components/ChatModal.jsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * - * @flow - */ - -import React from 'react'; -import { t } from 'ttag'; - -import Chat from './Chat'; - - -const ChatModal = () => ( -
-
- -
-
-); - -const data = { - content: ChatModal, - title: t`Chat`, -}; - -export default data; diff --git a/src/components/MinecraftButton.jsx b/src/components/MinecraftButton.jsx deleted file mode 100644 index 448a906..0000000 --- a/src/components/MinecraftButton.jsx +++ /dev/null @@ -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 }) => ( -
- -
-); - -function mapDispatchToProps(dispatch) { - return { - open() { - dispatch(showMinecraftModal()); - }, - }; -} - -export default connect(null, mapDispatchToProps)(MinecraftButton); diff --git a/src/components/ModalRoot.jsx b/src/components/ModalRoot.jsx index ca977c0..f11d919 100644 --- a/src/components/ModalRoot.jsx +++ b/src/components/ModalRoot.jsx @@ -5,88 +5,76 @@ * @flow */ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { MdClose } from 'react-icons/md'; import { t } from 'ttag'; import { - hideModal, + closeWindow, + restoreWindow, } from '../actions'; - -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:
, title: '' }, - HELP: HelpModal, - SETTINGS: SettingsModal, - USERAREA: UserAreaModal, - REGISTER: RegisterModal, - FORGOT_PASSWORD: ForgotPasswordModal, - CHAT: ChatModal, - CANVAS_SELECTION: CanvasSelectModal, - ARCHIVE: ArchiveModal, - /* other modals */ -}; +import COMPONENTS from './windows'; const ModalRoot = () => { const [render, setRender] = useState(false); - const modalType = useSelector((state) => state.windows.modalType); - const modalOpen = useSelector((state) => state.windows.modalOpen); - - const { - title, - content: SpecificModal, - } = MODAL_COMPONENTS[modalType || 'NONE']; + const { windowType, open, title } = useSelector( + (state) => state.windows.modal, + ); const dispatch = useDispatch(); - const close = useCallback(() => { - dispatch(hideModal()); - }, [dispatch]); const onTransitionEnd = () => { - if (!modalOpen) setRender(false); + if (!open) setRender(false); }; useEffect(() => { window.setTimeout(() => { - if (modalOpen) setRender(true); + if (open) setRender(true); }, 10); - }, [modalOpen]); + }, [open]); + + if (!windowType) { + return null; + } + + const Content = COMPONENTS[windowType || 'NONE']; return ( - (render || modalOpen) + (render || open) && [
dispatch(closeWindow(0))} />,

{title}

dispatch(closeWindow(0))} className="ModalClose" role="button" label="close" title={t`Close`} tabIndex={-1} >
- +
dispatch(restoreWindow())} + className="ModalRestore" + role="button" + label="restore" + title={t`Restore`} + tabIndex={-1} + >♥
+
+ +
, ] ); diff --git a/src/components/Window.jsx b/src/components/Window.jsx index b171660..6208a2b 100644 --- a/src/components/Window.jsx +++ b/src/components/Window.jsx @@ -6,18 +6,17 @@ import React, { useCallback } from 'react'; import { useSelector, useDispatch } from 'react-redux'; -import Chat from './Chat'; import { moveWindow, + resizeWindow, + closeWindow, + maximizeWindow, + cloneWindow, } from '../actions'; +import COMPONENTS from './windows'; const selectWindowById = (state, windowId) => state.windows.windows.find((win) => win.windowId === windowId); -const WINDOW_COMPONENTS = { - NONE:
, - CHAT: Chat, -}; - const Window = ({ id }) => { const win = useSelector((state) => selectWindowById(state, id)); @@ -46,6 +45,33 @@ const Window = ({ id }) => { 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 { width, height, xPos, yPos, @@ -53,7 +79,7 @@ const Window = ({ id }) => { title, } = win; - const Content = WINDOW_COMPONENTS[windowType]; + const Content = COMPONENTS[windowType]; console.log(`render window ${id}`); @@ -71,7 +97,8 @@ const Window = ({ id }) => { className="win-topbar" > dispatch(cloneWindow(id))} > + @@ -79,20 +106,30 @@ const Window = ({ id }) => { className="win-title" onMouseDown={startMove} > - Move Here + {title} dispatch(maximizeWindow(id))} > ↑ dispatch(closeWindow(id))} > X
- +
+ +
+
+ R +
); }; diff --git a/src/components/WindowsRoot.jsx b/src/components/WindowManager.jsx similarity index 92% rename from src/components/WindowsRoot.jsx rename to src/components/WindowManager.jsx index 486d491..510bc6f 100644 --- a/src/components/WindowsRoot.jsx +++ b/src/components/WindowManager.jsx @@ -14,7 +14,7 @@ const WindowsRoot = () => { const windowIds = useSelector(selectWindowIds, shallowEqual); return windowIds.map((id) => ( - + )); }; diff --git a/src/components/ArchiveModal.jsx b/src/components/windows/Archive.jsx similarity index 93% rename from src/components/ArchiveModal.jsx rename to src/components/windows/Archive.jsx index 15113ea..3593eff 100644 --- a/src/components/ArchiveModal.jsx +++ b/src/components/windows/Archive.jsx @@ -14,7 +14,7 @@ const imageStyle = { verticalAlign: 'middle', }; -const ArchiveModal = () => ( +const Archive = () => (

{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

); -const data = { - content: ArchiveModal, - title: t`Canvas Archive`, -}; - -export default data; +export default Archive; diff --git a/src/components/CanvasSelectModal.jsx b/src/components/windows/CanvasSelect.jsx similarity index 79% rename from src/components/CanvasSelectModal.jsx rename to src/components/windows/CanvasSelect.jsx index b2d23bb..94f3f13 100644 --- a/src/components/CanvasSelectModal.jsx +++ b/src/components/windows/CanvasSelect.jsx @@ -7,13 +7,13 @@ import React from 'react'; import { connect } from 'react-redux'; import { t } from 'ttag'; -import CanvasItem from './CanvasItem'; -import { showArchiveModal } from '../actions'; +import CanvasItem from '../CanvasItem'; +import { showArchiveModal } from '../../actions'; -import type { State } from '../reducers'; +import type { State } from '../../reducers'; -const CanvasSelectModal = ({ +const CanvasSelect = ({ canvases, showHiddenCanvases, showArchive, @@ -62,9 +62,4 @@ function mapStateToProps(state: State) { return { canvases, showHiddenCanvases }; } -const data = { - content: connect(mapStateToProps, mapDispatchToProps)(CanvasSelectModal), - title: t`Canvas Selection`, -}; - -export default data; +export default connect(mapStateToProps, mapDispatchToProps)(CanvasSelect); diff --git a/src/components/Chat.jsx b/src/components/windows/Chat.jsx similarity index 93% rename from src/components/Chat.jsx rename to src/components/windows/Chat.jsx index bdb37e7..19c6d96 100644 --- a/src/components/Chat.jsx +++ b/src/components/windows/Chat.jsx @@ -10,9 +10,8 @@ import useStayScrolled from 'react-stay-scrolled'; import { useSelector, useDispatch } from 'react-redux'; import { t } from 'ttag'; -import type { State } from '../reducers'; -import ChatMessage from './ChatMessage'; -import ChannelDropDown from './ChannelDropDown'; +import ChatMessage from '../ChatMessage'; +import ChannelDropDown from '../ChannelDropDown'; import { showUserAreaModal, @@ -21,9 +20,9 @@ import { setChatInputMessage, fetchChatMessages, showContextMenu, -} from '../actions'; -import ProtocolClient from '../socket/ProtocolClient'; -import splitChatMessage from '../core/chatMessageFilter'; +} from '../../actions'; +import ProtocolClient from '../../socket/ProtocolClient'; +import splitChatMessage from '../../core/chatMessageFilter'; function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string @@ -115,13 +114,7 @@ const Chat = ({ return (
( +const ForgotPassword = ({ login }) => (

{t`Enter your mail address and we will send you a new password:`} @@ -32,9 +32,4 @@ function mapDispatchToProps(dispatch) { }; } -const data = { - content: connect(null, mapDispatchToProps)(ForgotPasswordModal), - title: t`Restore my Password`, -}; - -export default data; +export default connect(null, mapDispatchToProps)(ForgotPassword); diff --git a/src/components/HelpModal.jsx b/src/components/windows/Help.jsx similarity index 97% rename from src/components/HelpModal.jsx rename to src/components/windows/Help.jsx index c6d4abe..6f20805 100644 --- a/src/components/HelpModal.jsx +++ b/src/components/windows/Help.jsx @@ -11,7 +11,7 @@ import { MdTouchApp } from 'react-icons/md'; /* eslint-disable max-len */ -const HelpModal = () => { +const Help = () => { const bindG = {c('keybinds').t`G`}; const bindX = {c('keybinds').t`X`}; const bindH = {c('keybinds').t`H`}; @@ -89,9 +89,4 @@ can be downloaded from mega.nz here: `}{t`Register new account here`}


@@ -33,9 +33,4 @@ function mapDispatchToProps(dispatch) { }; } -const data = { - content: connect(null, mapDispatchToProps)(RegisterModal), - title: t`Register New Account`, -}; - -export default data; +export default connect(null, mapDispatchToProps)(Register); diff --git a/src/components/SettingsModal.jsx b/src/components/windows/Settings.jsx similarity index 95% rename from src/components/SettingsModal.jsx rename to src/components/windows/Settings.jsx index 8a27e22..59580c4 100644 --- a/src/components/SettingsModal.jsx +++ b/src/components/windows/Settings.jsx @@ -7,8 +7,8 @@ import React from 'react'; import { connect } from 'react-redux'; import { c, t } from 'ttag'; -import LanguageSelect from './LanguageSelect'; -import MdToggleButtonHover from './MdToggleButtonHover'; +import LanguageSelect from '../LanguageSelect'; +import MdToggleButtonHover from '../MdToggleButtonHover'; import { toggleGrid, togglePixelNotify, @@ -20,9 +20,9 @@ import { toggleLightGrid, toggleHistoricalView, selectStyle, -} from '../actions'; +} from '../../actions'; -import type { State } from '../reducers'; +import type { State } from '../../reducers'; const flexy = { @@ -97,7 +97,7 @@ const SettingsItem = ({

); -function SettingsModal({ +function Settings({ isMuted, isGridShown, isPixelNotifyShown, @@ -273,9 +273,4 @@ function mapDispatchToProps(dispatch) { }; } -const data = { - content: connect(mapStateToProps, mapDispatchToProps)(SettingsModal), - title: t`Settings`, -}; - -export default data; +export default connect(mapStateToProps, mapDispatchToProps)(Settings); diff --git a/src/components/UserAreaModal.jsx b/src/components/windows/UserArea.jsx similarity index 88% rename from src/components/UserAreaModal.jsx rename to src/components/windows/UserArea.jsx index 390782b..15c81d1 100644 --- a/src/components/UserAreaModal.jsx +++ b/src/components/windows/UserArea.jsx @@ -7,21 +7,21 @@ import React, { Suspense } from 'react'; import { connect } from 'react-redux'; import { t } from 'ttag'; -import type { State } from '../reducers'; +import type { State } from '../../reducers'; import { showRegisterModal, showForgotPasswordModal, setName, setMailreg, -} from '../actions'; -import LogInForm from './LogInForm'; -import Tabs from './Tabs'; -import UserArea from './UserArea'; -import Rankings from './Rankings'; +} from '../../actions'; +import LogInForm from '../LogInForm'; +import Tabs from '../Tabs'; +import UserAreaContent from '../UserArea'; +import Rankings from '../Rankings'; // 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 -const Admintools = React.lazy(() => import(/* webpackChunkName: "admintools" */ './Admintools')); +const Admintools = React.lazy(() => import(/* webpackChunkName: "admintools" */ '../Admintools')); const logoStyle = { marginRight: 5, @@ -86,7 +86,7 @@ const LogInArea = ({ register, forgotPassword, me }) => (

); -const UserAreaModal = ({ +const UserArea = ({ name, register, forgotPassword, @@ -105,7 +105,7 @@ const UserAreaModal = ({ : (
- @@ -155,9 +155,4 @@ function mapStateToProps(state: State) { return { name, userlvl }; } -const data = { - content: connect(mapStateToProps, mapDispatchToProps)(UserAreaModal), - title: t`User Area`, -}; - -export default data; +export default connect(mapStateToProps, mapDispatchToProps)(UserArea); diff --git a/src/components/windows/index.jsx b/src/components/windows/index.jsx new file mode 100644 index 0000000..977c6b1 --- /dev/null +++ b/src/components/windows/index.jsx @@ -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:
, + HELP: Help, + SETTINGS: Settings, + USERAREA: UserArea, + REGISTER: Register, + FORGOT_PASSWORD: ForgotPassword, + CHAT: Chat, + CANVAS_SELECTION: CanvasSelect, + ARCHIVE: Archive, + /* other modals */ +}; diff --git a/src/reducers/windows.js b/src/reducers/windows.js index 5ee83ea..e106ca9 100644 --- a/src/reducers/windows.js +++ b/src/reducers/windows.js @@ -6,20 +6,33 @@ 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 = { // modal is considerd as "fullscreen window" // its windowId is considered 0 and args are under args[0] - modalOpen: boolean, - modalType: ?string, + modal: { + windowType: ?string, + title: ?string, + open: boolean, + }, // [ // { // windowId: number, + // windowOpen: boolean, // windowType: string, // title: string, // width: number, // height: number, // xPos: percentage, // yPos: percentage, + // cloneable: boolean, // }, // ] windows: Array, @@ -32,8 +45,11 @@ export type WindowsState = { } const initialState: WindowsState = { - modalOpen: false, - modalType: null, + modal: { + windowType: null, + title: null, + open: false, + }, windows: [], args: {}, }; @@ -47,16 +63,27 @@ export default function windows( const { windowType, title, - width, - height, - xPos, - yPos, + fullscreen, + cloneable, args, } = action; - let windowId = Math.floor(Math.random() * 99999) + 1; - while (state.args[windowId]) { - windowId += 1; + if (fullscreen) { + return { + ...state, + modal: { + windowType, + title, + open: true, + }, + args: { + ...state.args, + 0: { + ...args, + }, + }, + }; } + const windowId = generateWindowId(state); return { ...state, windows: [ @@ -64,12 +91,13 @@ export default function windows( { windowId, windowType, + windowOpen: true, title, - width, - height, - xPos, - yPos, - args, + width: 600, + height: 300, + xPos: 200, + yPos: 200, + cloneable, }, ], 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': { const { windowId, } = 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 }; delete args[windowId]; 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': { const { 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': { return initialState; } diff --git a/src/styles/default.css b/src/styles/default.css index c171fa4..4c97ab4 100644 --- a/src/styles/default.css +++ b/src/styles/default.css @@ -144,6 +144,7 @@ tr:nth-child(even) { .win-topbar { display: flex; + height: 16px; } .win-title { @@ -155,6 +156,29 @@ tr:nth-child(even) { border: solid black; border-width: thin; 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 { @@ -444,7 +468,7 @@ tr:nth-child(even) { width: 75%; } -.ModalClose { +.ModalClose, .ModalRestore { position: fixed; display: flex; justify-content: center; @@ -459,11 +483,18 @@ tr:nth-child(even) { background-color: #f6f6f7; border-color: #dcddde; top: 30px; - right: 40px; z-index: 5; } -.ModalClose:hover { +.ModalClose { + right: 40px; +} + +.ModalRestore { + right: 90px; +} + +.ModalClose:hover, .ModalRestore:hover { background-color: #e3e3e4; }