From ce2ddb6a6a05850962b4c793d8b7fff8382a9fe2 Mon Sep 17 00:00:00 2001 From: Devin Binnie <52460000+devinbinnie@users.noreply.github.com> Date: Tue, 19 Apr 2022 09:31:04 -0400 Subject: [PATCH] [MM-31547] Stop users from being able to enter the same server name or URL twice (#2049) Co-authored-by: Mattermod --- .../add_server_modal.test.js | 16 ++++++++++++++++ .../edit_server_modal.test.js | 12 ++++++++++++ src/main/app/intercom.ts | 14 +++++++++++--- src/renderer/components/NewTeamModal.tsx | 19 +++++++++++++++++++ src/renderer/modals/editServer/editServer.tsx | 7 +++++-- src/renderer/modals/newServer/newServer.tsx | 12 +++++++++--- 6 files changed, 72 insertions(+), 8 deletions(-) diff --git a/e2e/specs/server_management/add_server_modal.test.js b/e2e/specs/server_management/add_server_modal.test.js index a2edafa5..85eaead3 100644 --- a/e2e/specs/server_management/add_server_modal.test.js +++ b/e2e/specs/server_management/add_server_modal.test.js @@ -64,6 +64,22 @@ describe('Add Server Modal', function desc() { existing.should.be.true; }); + it('should not be valid if a server with the same name exists', async () => { + await newServerView.type('#teamNameInput', config.teams[0].name); + await newServerView.type('#teamUrlInput', 'http://example.org'); + await newServerView.click('#saveNewServerModal'); + const existing = await newServerView.isVisible('#teamNameInput.is-invalid'); + existing.should.be.true; + }); + + it('should not be valid if a server with the same URL exists', async () => { + await newServerView.type('#teamNameInput', 'some-new-server'); + await newServerView.type('#teamUrlInput', config.teams[0].url); + await newServerView.click('#saveNewServerModal'); + const existing = await newServerView.isVisible('#teamUrlInput.is-invalid'); + existing.should.be.true; + }); + describe('Valid server name', async () => { beforeEach(async () => { await newServerView.type('#teamNameInput', 'TestTeam'); diff --git a/e2e/specs/server_management/edit_server_modal.test.js b/e2e/specs/server_management/edit_server_modal.test.js index 9459294a..02685ff8 100644 --- a/e2e/specs/server_management/edit_server_modal.test.js +++ b/e2e/specs/server_management/edit_server_modal.test.js @@ -109,6 +109,18 @@ describe('EditServerModal', function desc() { existing.should.be.true; }); + it('should not edit team if another server with the same name or URL exists', async () => { + await editServerView.fill('#teamNameInput', config.teams[1].name); + await editServerView.click('#saveNewServerModal'); + let existing = await editServerView.isVisible('#teamNameInput.is-invalid'); + existing.should.be.true; + + await editServerView.fill('#teamNameInput', 'NewTestTeam'); + await editServerView.fill('#teamUrlInput', config.teams[1].url); + existing = await editServerView.isVisible('#teamUrlInput.is-invalid'); + existing.should.be.true; + }); + it('MM-T4391_2 should edit team when Save is pressed and name edited', async () => { await editServerView.fill('#teamNameInput', 'NewTestTeam'); await editServerView.click('#saveNewServerModal'); diff --git a/src/main/app/intercom.ts b/src/main/app/intercom.ts index 2c9991a2..f6d23e8d 100644 --- a/src/main/app/intercom.ts +++ b/src/main/app/intercom.ts @@ -4,7 +4,7 @@ import {app, dialog, IpcMainEvent, IpcMainInvokeEvent, Menu} from 'electron'; import log from 'electron-log'; -import {Team} from 'types/config'; +import {Team, TeamWithIndex} from 'types/config'; import {MentionData} from 'types/notification'; import Config from 'common/config'; @@ -109,7 +109,7 @@ export function handleNewServerModal() { if (!mainWindow) { return; } - const modalPromise = ModalManager.addModal('newServer', html, modalPreload, {}, mainWindow, Config.teams.length === 0); + const modalPromise = ModalManager.addModal('newServer', html, modalPreload, Config.teams.map((team, index) => ({...team, index})), mainWindow, Config.teams.length === 0); if (modalPromise) { modalPromise.then((data) => { const teams = Config.teams; @@ -145,7 +145,15 @@ export function handleEditServerModal(e: IpcMainEvent, name: string) { if (serverIndex < 0) { return; } - const modalPromise = ModalManager.addModal('editServer', html, modalPreload, Config.teams[serverIndex], mainWindow); + const modalPromise = ModalManager.addModal<{currentTeams: TeamWithIndex[]; team: TeamWithIndex}, Team>( + 'editServer', + html, + modalPreload, + { + currentTeams: Config.teams.map((team, index) => ({...team, index})), + team: {...Config.teams[serverIndex], index: serverIndex}, + }, + mainWindow); if (modalPromise) { modalPromise.then((data) => { const teams = Config.teams; diff --git a/src/renderer/components/NewTeamModal.tsx b/src/renderer/components/NewTeamModal.tsx index 00f70a2d..e1b1e13c 100644 --- a/src/renderer/components/NewTeamModal.tsx +++ b/src/renderer/components/NewTeamModal.tsx @@ -13,6 +13,7 @@ type Props = { onClose?: () => void; onSave?: (team: TeamWithIndex) => void; team?: TeamWithIndex; + currentTeams?: TeamWithIndex[]; editMode?: boolean; show?: boolean; restoreFocus?: boolean; @@ -62,6 +63,15 @@ export default class NewTeamModal extends React.PureComponent { if (!this.state.saveStarted) { return null; } + if (this.props.currentTeams) { + const currentTeams = [...this.props.currentTeams]; + if (this.props.editMode && this.props.team) { + currentTeams.splice(this.props.team.index, 1); + } + if (currentTeams.find((team) => team.name === this.state.teamName)) { + return 'A server with the same name already exists.'; + } + } return this.state.teamName.length > 0 ? null : 'Name is required.'; } @@ -79,6 +89,15 @@ export default class NewTeamModal extends React.PureComponent { if (!this.state.saveStarted) { return null; } + if (this.props.currentTeams) { + const currentTeams = [...this.props.currentTeams]; + if (this.props.editMode && this.props.team) { + currentTeams.splice(this.props.team.index, 1); + } + if (currentTeams.find((team) => team.url === this.state.teamUrl)) { + return 'A server with the same URL already exists.'; + } + } if (this.state.teamUrl.length === 0) { return 'URL is required.'; } diff --git a/src/renderer/modals/editServer/editServer.tsx b/src/renderer/modals/editServer/editServer.tsx index 21c0f9ad..363c6cd7 100644 --- a/src/renderer/modals/editServer/editServer.tsx +++ b/src/renderer/modals/editServer/editServer.tsx @@ -28,11 +28,13 @@ const onSave = (data: TeamWithIndex) => { const EditServerModalWrapper: React.FC = () => { const [server, setServer] = useState(); + const [currentTeams, setCurrentTeams] = useState(); - const handleEditServerMessage = (event: {data: ModalMessage}) => { + const handleEditServerMessage = (event: {data: ModalMessage<{currentTeams: TeamWithIndex[]; team: TeamWithIndex}>}) => { switch (event.data.type) { case MODAL_INFO: { - setServer(event.data.data); + setServer(event.data.data.team); + setCurrentTeams(event.data.data.currentTeams); break; } default: @@ -52,6 +54,7 @@ const EditServerModalWrapper: React.FC = () => { editMode={true} show={Boolean(server)} team={server} + currentTeams={currentTeams} /> ); }; diff --git a/src/renderer/modals/newServer/newServer.tsx b/src/renderer/modals/newServer/newServer.tsx index bb7c46bc..a0987993 100644 --- a/src/renderer/modals/newServer/newServer.tsx +++ b/src/renderer/modals/newServer/newServer.tsx @@ -10,7 +10,7 @@ import ReactDOM from 'react-dom'; import {TeamWithIndex} from 'types/config'; import {ModalMessage} from 'types/modals'; -import {GET_MODAL_UNCLOSEABLE, MODAL_CANCEL, MODAL_RESULT, MODAL_UNCLOSEABLE} from 'common/communication'; +import {GET_MODAL_UNCLOSEABLE, MODAL_CANCEL, MODAL_INFO, MODAL_RESULT, MODAL_UNCLOSEABLE, RETRIEVE_MODAL_INFO} from 'common/communication'; import NewTeamModal from '../../components/NewTeamModal'; //'./addServer.jsx'; @@ -28,13 +28,17 @@ const onSave = (data: TeamWithIndex) => { const NewServerModalWrapper: React.FC = () => { const [unremoveable, setUnremovable] = useState(); + const [currentTeams, setCurrentTeams] = useState(); - const handleNewServerMessage = (event: {data: ModalMessage}) => { + const handleNewServerMessage = (event: {data: ModalMessage}) => { switch (event.data.type) { case MODAL_UNCLOSEABLE: { - setUnremovable(event.data.data); + setUnremovable(event.data.data as boolean); break; } + case MODAL_INFO: + setCurrentTeams(event.data.data as TeamWithIndex[]); + break; default: break; } @@ -43,6 +47,7 @@ const NewServerModalWrapper: React.FC = () => { useEffect(() => { window.addEventListener('message', handleNewServerMessage); window.postMessage({type: GET_MODAL_UNCLOSEABLE}, window.location.href); + window.postMessage({type: RETRIEVE_MODAL_INFO}, window.location.href); return () => { window.removeEventListener('message', handleNewServerMessage); @@ -55,6 +60,7 @@ const NewServerModalWrapper: React.FC = () => { onSave={onSave} editMode={false} show={true} + currentTeams={currentTeams} /> ); };