From a03c09897491c969c3f1c3173abcf793b7dfa40a Mon Sep 17 00:00:00 2001 From: Nick Hackman Date: Fri, 25 Jun 2021 10:21:07 -0400 Subject: [PATCH] feat: introduce request group settings (name, description, and move/copy) (#3350) --- .../request-group-actions-dropdown.tsx | 30 +- .../modals/move-request-group-modal.tsx | 116 ------ .../modals/request-group-settings-modal.tsx | 354 ++++++++++++++++++ .../components/sidebar/sidebar-children.tsx | 3 - .../sidebar/sidebar-request-group-row.tsx | 12 +- .../app/ui/components/wrapper-debug.tsx | 2 - .../app/ui/components/wrapper.tsx | 22 +- .../insomnia-app/app/ui/containers/app.tsx | 8 - 8 files changed, 383 insertions(+), 164 deletions(-) delete mode 100644 packages/insomnia-app/app/ui/components/modals/move-request-group-modal.tsx create mode 100644 packages/insomnia-app/app/ui/components/modals/request-group-settings-modal.tsx diff --git a/packages/insomnia-app/app/ui/components/dropdowns/request-group-actions-dropdown.tsx b/packages/insomnia-app/app/ui/components/dropdowns/request-group-actions-dropdown.tsx index e98017dc027..d9b8a4e5d5d 100644 --- a/packages/insomnia-app/app/ui/components/dropdowns/request-group-actions-dropdown.tsx +++ b/packages/insomnia-app/app/ui/components/dropdowns/request-group-actions-dropdown.tsx @@ -12,7 +12,7 @@ import { import Dropdown from '../base/dropdown/dropdown'; import EnvironmentEditModal from '../modals/environment-edit-modal'; import * as models from '../../../models'; -import { showError, showModal, showPrompt } from '../modals'; +import { showError, showModal } from '../modals'; import type { HotKeyRegistry } from '../../../common/hotkeys'; import { hotKeyRefs } from '../../../common/hotkeys'; import type { RequestGroupAction } from '../../../plugins'; @@ -30,6 +30,7 @@ interface Props { activeEnvironment?: Environment | null; handleCreateRequest: (id: string) => any; handleDuplicateRequestGroup: (requestGroup: RequestGroup) => any; + handleShowSettings: (requestGroup: RequestGroup) => any, handleMoveRequestGroup: (requestGroup: RequestGroup) => any; handleCreateRequestGroup: (requestGroup: string) => any; } @@ -51,19 +52,6 @@ class RequestGroupActionsDropdown extends PureComponent { this._dropdown = n; } - _handleRename() { - const { requestGroup } = this.props; - showPrompt({ - title: 'Rename Folder', - defaultValue: requestGroup.name, - onComplete: name => { - models.requestGroup.update(requestGroup, { - name, - }); - }, - }); - } - async _handleRequestCreate() { this.props.handleCreateRequest(this.props.requestGroup._id); } @@ -72,10 +60,6 @@ class RequestGroupActionsDropdown extends PureComponent { this.props.handleDuplicateRequestGroup(this.props.requestGroup); } - _handleRequestGroupMove() { - this.props.handleMoveRequestGroup(this.props.requestGroup); - } - async _handleRequestGroupCreate() { this.props.handleCreateRequestGroup(this.props.requestGroup._id); } @@ -160,15 +144,9 @@ class RequestGroupActionsDropdown extends PureComponent { Duplicate - - Rename - Environment - - Move - Delete @@ -183,6 +161,10 @@ class RequestGroupActionsDropdown extends PureComponent { {p.label} ))} + + + Settings + ); } diff --git a/packages/insomnia-app/app/ui/components/modals/move-request-group-modal.tsx b/packages/insomnia-app/app/ui/components/modals/move-request-group-modal.tsx deleted file mode 100644 index b4989300de9..00000000000 --- a/packages/insomnia-app/app/ui/components/modals/move-request-group-modal.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import React, { PureComponent } from 'react'; -import { autoBindMethodsForReact } from 'class-autobind-decorator'; -import { AUTOBIND_CFG } from '../../../common/constants'; -import Modal, { ModalProps } from '../base/modal'; -import ModalBody from '../base/modal-body'; -import ModalHeader from '../base/modal-header'; -import ModalFooter from '../base/modal-footer'; -import type { RequestGroup } from '../../../models/request-group'; -import type { Workspace } from '../../../models/workspace'; -import * as models from '../../../models'; -import HelpTooltip from '../help-tooltip'; - -interface Props extends ModalProps { - workspaces: Workspace[]; - activeWorkspace: Workspace; -} - -interface State { - requestGroup: RequestGroup | null; - selectedWorkspaceId?: string; -} - -@autoBindMethodsForReact(AUTOBIND_CFG) -class MoveRequestGroupModal extends PureComponent { - modal: Modal | null = null; - - state: State = { - requestGroup: null, - selectedWorkspaceId: undefined, - } - - _setModalRef(n: Modal) { - this.modal = n; - } - - async _handleSubmit(e: React.SyntheticEvent) { - e.preventDefault(); - const { requestGroup, selectedWorkspaceId } = this.state; - - if (!requestGroup || !selectedWorkspaceId) { - return; - } - - const workspace = await models.workspace.getById(selectedWorkspaceId); - - if (!workspace) { - return; - } - - // TODO: if there are gRPC requests in a request group - // we should also copy the protofiles to the destination workspace - INS-267 - await models.requestGroup.duplicate(requestGroup, { - metaSortKey: -1e9, - parentId: selectedWorkspaceId, - name: requestGroup.name, // Because duplicating will add (Copy) suffix - }); - await models.requestGroup.remove(requestGroup); - this.hide(); - } - - _handleChangeSelectedWorkspace(e: React.SyntheticEvent) { - const selectedWorkspaceId = e.currentTarget.value; - this.setState({ - selectedWorkspaceId, - }); - } - - show(options: { requestGroup: RequestGroup }) { - const { requestGroup } = options; - this.setState({ - requestGroup, - }); - this.modal && this.modal.show(); - } - - hide() { - this.modal && this.modal.hide(); - } - - render() { - const { workspaces, activeWorkspace } = this.props; - const { selectedWorkspaceId } = this.state; - return ( -
- - Move Folder to Workspace - -
- -
-
- - - -
-
- ); - } -} - -export default MoveRequestGroupModal; diff --git a/packages/insomnia-app/app/ui/components/modals/request-group-settings-modal.tsx b/packages/insomnia-app/app/ui/components/modals/request-group-settings-modal.tsx new file mode 100644 index 00000000000..525f825cacf --- /dev/null +++ b/packages/insomnia-app/app/ui/components/modals/request-group-settings-modal.tsx @@ -0,0 +1,354 @@ +import * as React from 'react'; +import { autoBindMethodsForReact } from 'class-autobind-decorator'; +import { AUTOBIND_CFG } from '../../../common/constants'; +import Modal from '../base/modal'; +import ModalBody from '../base/modal-body'; +import ModalHeader from '../base/modal-header'; +import HelpTooltip from '../help-tooltip'; +import * as models from '../../../models'; +import DebouncedInput from '../base/debounced-input'; +import MarkdownEditor from '../markdown-editor'; +import { database as db } from '../../../common/database'; +import type { Workspace } from '../../../models/workspace'; +import type { RequestGroup } from '../../../models/request-group'; +import { HandleGetRenderContext, HandleRender } from '../../../common/render'; + +interface Props { + editorFontSize: number; + editorIndentSize: number; + editorKeyMap: string; + editorLineWrapping: boolean; + nunjucksPowerUserMode: boolean; + isVariableUncovered: boolean; + handleRender: HandleRender; + handleGetRenderContext: HandleGetRenderContext; + workspaces: Workspace[]; +} + +interface State { + requestGroup: RequestGroup | null; + showDescription: boolean; + defaultPreviewMode: boolean; + activeWorkspaceIdToCopyTo: string | null; + workspace?: Workspace; + workspaces: Workspace[]; + justCopied: boolean; + justMoved: boolean; +} + +interface RequestGroupSettingsModalOptions { + requestGroup: RequestGroup; + forceEditMode: boolean; +} + +@autoBindMethodsForReact(AUTOBIND_CFG) +class RequestGroupSettingsModal extends React.PureComponent { + modal: Modal | null = null; + _editor: MarkdownEditor | null = null; + + state: State = { + requestGroup: null, + showDescription: false, + defaultPreviewMode: false, + activeWorkspaceIdToCopyTo: null, + workspace: undefined, + workspaces: [], + justCopied: false, + justMoved: false, + }; + + _setModalRef(n: Modal) { + this.modal = n; + } + + _setEditorRef(n: MarkdownEditor) { + this._editor = n; + } + + async _handleNameChange(name: string) { + const { requestGroup: originalRequestGroup } = this.state; + + if (!originalRequestGroup) { + return; + } + const patch = { name }; + + const updatedRequestGroup = await models.requestGroup.update( + originalRequestGroup, + patch, + ); + this.setState({ requestGroup: updatedRequestGroup }); + } + + async _handleDescriptionChange(description: string) { + if (!this.state.requestGroup) { + return; + } + const requestGroup = await models.requestGroup.update( + this.state.requestGroup, + { + description, + }, + ); + this.setState({ requestGroup, defaultPreviewMode: false }); + } + + _handleAddDescription() { + this.setState({ showDescription: true }); + } + + _handleUpdateMoveCopyWorkspace(e: React.SyntheticEvent) { + const { value } = e.currentTarget; + const workspaceId = value === '__NULL__' ? null : value; + this.setState({ activeWorkspaceIdToCopyTo: workspaceId }); + } + + async _handleMoveToWorkspace() { + const { activeWorkspaceIdToCopyTo, requestGroup } = this.state; + if (!requestGroup || !activeWorkspaceIdToCopyTo) { + return; + } + + const workspace = await models.workspace.getById(activeWorkspaceIdToCopyTo); + if (!workspace) { + return; + } + + // TODO: if there are gRPC requests in a request group + // we should also copy the protofiles to the destination workspace - INS-267 + + await models.requestGroup.duplicate(requestGroup, { + metaSortKey: -1e9, + parentId: activeWorkspaceIdToCopyTo, + name: requestGroup.name, // Because duplicating will add (Copy) suffix + }); + + await models.requestGroup.remove(requestGroup); + + this.setState({ justMoved: true }); + setTimeout(() => { + this.setState({ justMoved: false }); + }, 2000); + } + + async _handleCopyToWorkspace() { + const { activeWorkspaceIdToCopyTo, requestGroup } = this.state; + if (!requestGroup || !activeWorkspaceIdToCopyTo) { + return; + } + + const workspace = await models.workspace.getById(activeWorkspaceIdToCopyTo); + if (!workspace) { + return; + } + + const patch = { + metaSortKey: -1e9, // Move to top of sort order + name: requestGroup.name, // Because duplicate will add (Copy) suffix if name is not provided in patch + parentId: activeWorkspaceIdToCopyTo, + }; + + await models.requestGroup.duplicate(requestGroup, patch); + + this.setState({ justCopied: true }); + setTimeout(() => { + this.setState({ justCopied: false }); + }, 2000); + + models.stats.incrementCreatedRequests(); + } + + async show({ + requestGroup, + forceEditMode, + }: RequestGroupSettingsModalOptions) { + const { workspaces } = this.props; + + const hasDescription = !!requestGroup.description; + + // Find workspaces for use with moving workspace + const ancestors = await db.withAncestors(requestGroup); + const doc = ancestors.find((doc) => doc.type === models.workspace.type); + const workspaceId = doc ? doc._id : 'should-never-happen'; + const workspace = workspaces.find((w) => w._id === workspaceId); + + this.setState( + { + requestGroup, + workspace: workspace, + activeWorkspaceIdToCopyTo: null, + showDescription: forceEditMode || hasDescription, + defaultPreviewMode: hasDescription && !forceEditMode, + }, + () => { + this.modal && this.modal.show(); + + if (forceEditMode) { + setTimeout(() => { + this._editor && this._editor.focus(); + }, 400); + } + }, + ); + } + + hide() { + this.modal && this.modal.hide(); + } + + _renderDescription() { + const { + editorLineWrapping, + editorFontSize, + editorIndentSize, + editorKeyMap, + handleRender, + handleGetRenderContext, + nunjucksPowerUserMode, + isVariableUncovered, + } = this.props; + + const { showDescription, defaultPreviewMode, requestGroup } = this.state; + + if (!requestGroup) { + return null; + } + + return showDescription ? ( + + ) : ( + + ); + } + + _renderMoveCopy() { + const { workspaces } = this.props; + + const { + activeWorkspaceIdToCopyTo, + justMoved, + justCopied, + workspace, + requestGroup, + } = this.state; + + if (!requestGroup) { + return null; + } + + return ( +
+
+ +
+
+ +
+
+ +
+
+ ); + } + + renderModalBody() { + const { requestGroup } = this.state; + + if (!requestGroup) { + return null; + } + + return ( +
+
+ +
+ {this._renderDescription()} +
+ {this._renderMoveCopy()} +
+ ); + } + + render() { + const { requestGroup } = this.state; + return ( + + + Folder Settings{' '} + + {requestGroup ? requestGroup._id : ''} + + + {this.renderModalBody()} + + ); + } +} + +export default RequestGroupSettingsModal; diff --git a/packages/insomnia-app/app/ui/components/sidebar/sidebar-children.tsx b/packages/insomnia-app/app/ui/components/sidebar/sidebar-children.tsx index 19f57f1301c..4b4b51ae0c5 100644 --- a/packages/insomnia-app/app/ui/components/sidebar/sidebar-children.tsx +++ b/packages/insomnia-app/app/ui/components/sidebar/sidebar-children.tsx @@ -32,7 +32,6 @@ interface Props { handleSetRequestGroupCollapsed: Function; handleDuplicateRequest: Function; handleDuplicateRequestGroup: (requestGroup: RequestGroup) => any; - handleMoveRequestGroup: (requestGroup: RequestGroup) => Promise; handleGenerateCode: Function; handleCopyAsCurl: Function; handleRender: HandleRender; @@ -82,7 +81,6 @@ class SidebarChildren extends PureComponent { handleSetRequestGroupCollapsed, handleDuplicateRequest, handleDuplicateRequestGroup, - handleMoveRequestGroup, handleGenerateCode, handleCopyAsCurl, handleRender, @@ -151,7 +149,6 @@ class SidebarChildren extends PureComponent { handleActivateRequest={handleActivateRequest} handleSetRequestGroupCollapsed={handleSetRequestGroupCollapsed} handleDuplicateRequestGroup={handleDuplicateRequestGroup} - handleMoveRequestGroup={handleMoveRequestGroup} handleRender={handleRender} isCollapsed={child.collapsed} handleCreateRequest={handleCreateRequest} diff --git a/packages/insomnia-app/app/ui/components/sidebar/sidebar-request-group-row.tsx b/packages/insomnia-app/app/ui/components/sidebar/sidebar-request-group-row.tsx index a63c73f418e..42c6aab23be 100644 --- a/packages/insomnia-app/app/ui/components/sidebar/sidebar-request-group-row.tsx +++ b/packages/insomnia-app/app/ui/components/sidebar/sidebar-request-group-row.tsx @@ -8,6 +8,8 @@ import Highlight from '../base/highlight'; import RequestGroupActionsDropdown from '../dropdowns/request-group-actions-dropdown'; import SidebarRequestRow from './sidebar-request-row'; import * as misc from '../../../common/misc'; +import { showModal } from '../modals'; +import RequestGroupSettingsModal from '../modals/request-group-settings-modal'; import { RequestGroup } from '../../../models/request-group'; import { Workspace } from '../../../models/workspace'; import { Environment } from '../../../models/environment'; @@ -17,7 +19,6 @@ import { HandleRender } from '../../../common/render'; interface Props { handleSetRequestGroupCollapsed: Function; handleDuplicateRequestGroup: (requestGroup: RequestGroup) => any; - handleMoveRequestGroup: (requestGroup: RequestGroup) => any; moveDoc: Function; handleActivateRequest: Function; handleCreateRequest: (id: string) => any; @@ -80,6 +81,12 @@ class SidebarRequestGroupRow extends PureComponent { } } + _handleShowRequestGroupSettings() { + showModal(RequestGroupSettingsModal, { + requestGroup: this.props.requestGroup, + }); + } + render() { const { connectDragSource, @@ -93,7 +100,6 @@ class SidebarRequestGroupRow extends PureComponent { handleCreateRequest, handleCreateRequestGroup, handleDuplicateRequestGroup, - handleMoveRequestGroup, handleRender, isDragging, isDraggingOver, @@ -152,7 +158,7 @@ class SidebarRequestGroupRow extends PureComponent { handleCreateRequest={handleCreateRequest} handleCreateRequestGroup={handleCreateRequestGroup} handleDuplicateRequestGroup={handleDuplicateRequestGroup} - handleMoveRequestGroup={handleMoveRequestGroup} + handleShowSettings={this._handleShowRequestGroupSettings} workspace={workspace} requestGroup={requestGroup} hotKeyRegistry={hotKeyRegistry} diff --git a/packages/insomnia-app/app/ui/components/wrapper-debug.tsx b/packages/insomnia-app/app/ui/components/wrapper-debug.tsx index e12508f015a..62bc598ae76 100644 --- a/packages/insomnia-app/app/ui/components/wrapper-debug.tsx +++ b/packages/insomnia-app/app/ui/components/wrapper-debug.tsx @@ -116,7 +116,6 @@ class WrapperDebug extends PureComponent { handleDuplicateRequestGroup, handleGenerateCode, handleMoveDoc, - handleMoveRequestGroup, handleRender, handleSetRequestGroupCollapsed, handleSetRequestPinned, @@ -165,7 +164,6 @@ class WrapperDebug extends PureComponent { handleSetRequestPinned={handleSetRequestPinned} handleDuplicateRequest={handleDuplicateRequest} handleDuplicateRequestGroup={handleDuplicateRequestGroup} - handleMoveRequestGroup={handleMoveRequestGroup} handleGenerateCode={handleGenerateCode} handleCopyAsCurl={handleCopyAsCurl} handleRender={handleRender} diff --git a/packages/insomnia-app/app/ui/components/wrapper.tsx b/packages/insomnia-app/app/ui/components/wrapper.tsx index 4ccea344c09..60839f0acea 100644 --- a/packages/insomnia-app/app/ui/components/wrapper.tsx +++ b/packages/insomnia-app/app/ui/components/wrapper.tsx @@ -47,6 +47,7 @@ import RequestSwitcherModal from './modals/request-switcher-modal'; import SettingsModal from './modals/settings-modal'; import FilterHelpModal from './modals/filter-help-modal'; import RequestSettingsModal from './modals/request-settings-modal'; +import RequestGroupSettingsModal from './modals/request-group-settings-modal'; import SyncStagingModal from './modals/sync-staging-modal'; import GitRepositorySettingsModal from './modals/git-repository-settings-modal'; import GitStagingModal from './modals/git-staging-modal'; @@ -68,7 +69,6 @@ import type { Cookie, CookieJar } from '../../models/cookie-jar'; import type { Environment } from '../../models/environment'; import ErrorBoundary from './error-boundary'; import type { ClientCertificate } from '../../models/client-certificate'; -import MoveRequestGroupModal from './modals/move-request-group-modal'; import AddKeyCombinationModal from './modals/add-key-combination-modal'; import ExportRequestsModal from './modals/export-requests-modal'; import { VCS } from '../../sync/vcs/vcs'; @@ -120,7 +120,6 @@ export interface WrapperProps { handleCreateRequest: (id: string) => any; handleDuplicateRequest: Function; handleDuplicateRequestGroup: (requestGroup: RequestGroup) => void; - handleMoveRequestGroup: (requestGroup: RequestGroup) => Promise; handleDuplicateWorkspace: Function; handleCreateRequestGroup: (parentId: string) => void; handleGenerateCodeForActiveRequest: Function; @@ -620,6 +619,19 @@ class Wrapper extends PureComponent { isVariableUncovered={isVariableUncovered} /> + + {/* TODO: Figure out why cookieJar is sometimes null */} {activeCookieJar ? ( { workspace={activeWorkspace} /> - - { }); } - static async _requestGroupMove(requestGroup: RequestGroup) { - showModal(MoveRequestGroupModal, { - requestGroup, - }); - } - _requestDuplicate(request?: Request | GrpcRequest) { if (!request) { return; @@ -1530,7 +1523,6 @@ class App extends PureComponent { handleGetRenderContext={this._handleGetRenderContext} handleDuplicateRequest={this._requestDuplicate} handleDuplicateRequestGroup={App._requestGroupDuplicate} - handleMoveRequestGroup={App._requestGroupMove} handleDuplicateWorkspace={this._workspaceDuplicate} handleCreateRequestGroup={this._requestGroupCreate} handleGenerateCode={App._handleGenerateCode}