From a4a275bd73422b706d743cc97df8ff52c2bc8a2a Mon Sep 17 00:00:00 2001 From: Devin Binnie <52460000+devinbinnie@users.noreply.github.com> Date: Thu, 25 Nov 2021 09:47:20 -0500 Subject: [PATCH] [MM-39491] Force Add Server modal to stay until server has been added (#1869) * [MM-39491] Force Add Server modal to stay until server has been added * Make the parameter optional * Actually do the logic, add a test for the logic * Add remove event listener --- e2e/specs/startup/app.test.js | 13 +++++++ src/common/communication.ts | 3 ++ src/main/main.ts | 2 +- src/main/preload/modalPreload.js | 33 ++++++++++++++--- src/main/views/modalManager.ts | 22 +++++++++-- src/main/views/modalView.ts | 4 +- src/renderer/components/NewTeamModal.tsx | 34 ++++++++++------- src/renderer/modals/newServer/newServer.tsx | 41 ++++++++++++++++++--- 8 files changed, 122 insertions(+), 30 deletions(-) diff --git a/e2e/specs/startup/app.test.js b/e2e/specs/startup/app.test.js index 4d6a1ab1..38646744 100644 --- a/e2e/specs/startup/app.test.js +++ b/e2e/specs/startup/app.test.js @@ -4,6 +4,8 @@ 'use strict'; +const robot = require('robotjs'); + const env = require('../../modules/environment'); describe('startup/app', function desc() { @@ -27,6 +29,17 @@ describe('startup/app', function desc() { modalTitle.should.equal('Add Server'); }); + it('MM-T4419 should not allow the user to close the new server modal when no servers exist', async () => { + const newServerModal = this.app.windows().find((window) => window.url().includes('newServer')); + + const existing = await newServerModal.isVisible('#cancelNewServerModal'); + existing.should.be.false; + + robot.keyTap('escape'); + const existingModal = this.app.windows().find((window) => window.url().includes('newServer')); + existingModal.should.not.be.null; + }); + it('MM-T4399_2 should show no servers configured in dropdown when no servers exist', async () => { const mainWindow = this.app.windows().find((window) => window.url().includes('index')); const dropdownButtonText = await mainWindow.innerText('.TeamDropdownButton'); diff --git a/src/common/communication.ts b/src/common/communication.ts index 0ca3e7ec..a990298d 100644 --- a/src/common/communication.ts +++ b/src/common/communication.ts @@ -104,3 +104,6 @@ export const GET_AVAILABLE_SPELL_CHECKER_LANGUAGES = 'get-available-spell-checke export const GET_VIEW_NAME = 'get-view-name'; export const GET_VIEW_WEBCONTENTS_ID = 'get-view-webcontents-id'; + +export const GET_MODAL_UNCLOSEABLE = 'get-modal-uncloseable'; +export const MODAL_UNCLOSEABLE = 'modal-uncloseable'; diff --git a/src/main/main.ts b/src/main/main.ts index 2437317e..07ecfd52 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -554,7 +554,7 @@ function handleNewServerModal() { if (!mainWindow) { return; } - const modalPromise = addModal('newServer', html, modalPreload, {}, mainWindow); + const modalPromise = addModal('newServer', html, modalPreload, {}, mainWindow, config.teams.length === 0); if (modalPromise) { modalPromise.then((data) => { const teams = config.teams; diff --git a/src/main/preload/modalPreload.js b/src/main/preload/modalPreload.js index d68ffafe..3303e6fd 100644 --- a/src/main/preload/modalPreload.js +++ b/src/main/preload/modalPreload.js @@ -6,10 +6,29 @@ import {ipcRenderer} from 'electron'; -import {MODAL_CANCEL, MODAL_RESULT, MODAL_INFO, RETRIEVE_MODAL_INFO, MODAL_SEND_IPC_MESSAGE, GET_DARK_MODE, DARK_MODE_CHANGE} from 'common/communication'; +import { + MODAL_CANCEL, + MODAL_RESULT, + MODAL_INFO, + RETRIEVE_MODAL_INFO, + MODAL_SEND_IPC_MESSAGE, + GET_DARK_MODE, + DARK_MODE_CHANGE, + GET_MODAL_UNCLOSEABLE, + MODAL_UNCLOSEABLE, +} from 'common/communication'; console.log('preloaded for the modal!'); +let uncloseable = false; +const createKeyDownListener = () => { + window.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && !uncloseable) { + ipcRenderer.send(MODAL_CANCEL); + } + }); +}; + window.addEventListener('message', async (event) => { switch (event.data.type) { case MODAL_CANCEL: { @@ -26,6 +45,12 @@ window.addEventListener('message', async (event) => { console.log('getting modal data'); window.postMessage({type: MODAL_INFO, data: await ipcRenderer.invoke(RETRIEVE_MODAL_INFO)}, window.location.href); break; + case GET_MODAL_UNCLOSEABLE: + console.log('get modal uncloseable'); + uncloseable = await ipcRenderer.invoke(GET_MODAL_UNCLOSEABLE); + createKeyDownListener(); + window.postMessage({type: MODAL_UNCLOSEABLE, data: uncloseable}, window.location.href); + break; case MODAL_SEND_IPC_MESSAGE: console.log('sending custom ipc message'); ipcRenderer.send(event.data.data.type, ...event.data.data.args); @@ -40,11 +65,7 @@ window.addEventListener('message', async (event) => { } }); -window.addEventListener('keydown', (e) => { - if (e.key === 'Escape') { - ipcRenderer.send(MODAL_CANCEL); - } -}); +createKeyDownListener(); ipcRenderer.on(DARK_MODE_CHANGE, (event, darkMode) => { window.postMessage({type: DARK_MODE_CHANGE, data: darkMode}, window.location.href); diff --git a/src/main/views/modalManager.ts b/src/main/views/modalManager.ts index 976cb781..34d8c2c3 100644 --- a/src/main/views/modalManager.ts +++ b/src/main/views/modalManager.ts @@ -6,7 +6,16 @@ import {IpcMainEvent, IpcMainInvokeEvent} from 'electron/main'; import {CombinedConfig} from 'types/config'; -import {RETRIEVE_MODAL_INFO, MODAL_CANCEL, MODAL_RESULT, MODAL_OPEN, MODAL_CLOSE, EMIT_CONFIGURATION, DARK_MODE_CHANGE} from 'common/communication'; +import { + RETRIEVE_MODAL_INFO, + MODAL_CANCEL, + MODAL_RESULT, + MODAL_OPEN, + MODAL_CLOSE, + EMIT_CONFIGURATION, + DARK_MODE_CHANGE, + GET_MODAL_UNCLOSEABLE, +} from 'common/communication'; import * as WindowManager from '../windows/windowManager'; @@ -16,11 +25,11 @@ let modalQueue: Array> = []; const modalPromises: Map> = new Map(); // TODO: add a queue/add differentiation, in case we need to put a modal first in line -export function addModal(key: string, html: string, preload: string, data: T, win: BrowserWindow) { +export function addModal(key: string, html: string, preload: string, data: T, win: BrowserWindow, uncloseable = false) { const foundModal = modalQueue.find((modal) => modal.key === key); if (!foundModal) { const modalPromise = new Promise((resolve: (value: T2) => void, reject) => { - const mv = new ModalView(key, html, preload, data, resolve, reject, win); + const mv = new ModalView(key, html, preload, data, resolve, reject, win, uncloseable); modalQueue.push(mv); }); @@ -34,6 +43,7 @@ export function addModal(key: string, html: string, preload: string, data return modalPromises.get(key) as Promise; } +ipcMain.handle(GET_MODAL_UNCLOSEABLE, handleGetModalUncloseable); ipcMain.handle(RETRIEVE_MODAL_INFO, handleInfoRequest); ipcMain.on(MODAL_RESULT, handleModalResult); ipcMain.on(MODAL_CANCEL, handleModalCancel); @@ -118,3 +128,9 @@ ipcMain.on(EMIT_CONFIGURATION, (event: IpcMainEvent, config: CombinedConfig) => modal.view.webContents.send(DARK_MODE_CHANGE, config.darkMode); }); }); + +function handleGetModalUncloseable(event: IpcMainInvokeEvent) { + const modalView = modalQueue.find((modal) => modal.view.webContents.id === event.sender.id); + return modalView?.uncloseable; +} + diff --git a/src/main/views/modalView.ts b/src/main/views/modalView.ts index ad64ff75..02648dac 100644 --- a/src/main/views/modalView.ts +++ b/src/main/views/modalView.ts @@ -24,8 +24,9 @@ export class ModalView { windowAttached?: BrowserWindow; status: Status; contextMenu: ContextMenu; + uncloseable: boolean; - constructor(key: string, html: string, preload: string, data: T, onResolve: (value: T2) => void, onReject: (value: T2) => void, currentWindow: BrowserWindow) { + constructor(key: string, html: string, preload: string, data: T, onResolve: (value: T2) => void, onReject: (value: T2) => void, currentWindow: BrowserWindow, uncloseable: boolean) { this.key = key; this.html = html; this.data = data; @@ -42,6 +43,7 @@ export class ModalView { this.onReject = onReject; this.onResolve = onResolve; this.window = currentWindow; + this.uncloseable = uncloseable; this.status = Status.ACTIVE; try { diff --git a/src/renderer/components/NewTeamModal.tsx b/src/renderer/components/NewTeamModal.tsx index 4ae32d5f..00f70a2d 100644 --- a/src/renderer/components/NewTeamModal.tsx +++ b/src/renderer/components/NewTeamModal.tsx @@ -10,7 +10,7 @@ import {TeamWithIndex} from 'types/config'; import urlUtils from 'common/utils/url'; type Props = { - onClose: () => void; + onClose?: () => void; onSave?: (team: TeamWithIndex) => void; team?: TeamWithIndex; editMode?: boolean; @@ -175,7 +175,7 @@ export default class NewTeamModal extends React.PureComponent { e.stopPropagation(); break; case 'Escape': - this.props.onClose(); + this.props.onClose?.(); break; } }} @@ -237,17 +237,25 @@ export default class NewTeamModal extends React.PureComponent { {this.getError()} - - + {this.props.onClose && + + } + {this.props.onSave && + + } diff --git a/src/renderer/modals/newServer/newServer.tsx b/src/renderer/modals/newServer/newServer.tsx index 5c7d83b6..bb7c46bc 100644 --- a/src/renderer/modals/newServer/newServer.tsx +++ b/src/renderer/modals/newServer/newServer.tsx @@ -4,12 +4,13 @@ import 'bootstrap/dist/css/bootstrap.min.css'; import 'renderer/css/modals.css'; -import React from 'react'; +import React, {useEffect, useState} from 'react'; import ReactDOM from 'react-dom'; import {TeamWithIndex} from 'types/config'; +import {ModalMessage} from 'types/modals'; -import {MODAL_CANCEL, MODAL_RESULT} from 'common/communication'; +import {GET_MODAL_UNCLOSEABLE, MODAL_CANCEL, MODAL_RESULT, MODAL_UNCLOSEABLE} from 'common/communication'; import NewTeamModal from '../../components/NewTeamModal'; //'./addServer.jsx'; @@ -25,14 +26,42 @@ const onSave = (data: TeamWithIndex) => { window.postMessage({type: MODAL_RESULT, data}, window.location.href); }; -const start = async () => { - ReactDOM.render( +const NewServerModalWrapper: React.FC = () => { + const [unremoveable, setUnremovable] = useState(); + + const handleNewServerMessage = (event: {data: ModalMessage}) => { + switch (event.data.type) { + case MODAL_UNCLOSEABLE: { + setUnremovable(event.data.data); + break; + } + default: + break; + } + }; + + useEffect(() => { + window.addEventListener('message', handleNewServerMessage); + window.postMessage({type: GET_MODAL_UNCLOSEABLE}, window.location.href); + + return () => { + window.removeEventListener('message', handleNewServerMessage); + }; + }, []); + + return ( , + /> + ); +}; + +const start = async () => { + ReactDOM.render( + , document.getElementById('app'), ); };