From 5b54b9b35ec257ba821dc299a08732d1f5e5b172 Mon Sep 17 00:00:00 2001 From: NickHackman Date: Sun, 2 May 2021 18:47:10 -0500 Subject: [PATCH] feat: request group descriptions Adds the ability to modify the description for a Request Group in a modal shown by hitting settings in a dropdown. Identical implementation to RequestSettings. In addition allows renaming the Request Group as well as moving/copying it to a different workspace. Ref: https://github.com/Kong/insomnia/issues/3306 --- .../request-group-actions-dropdown.js | 22 +- .../modals/request-group-settings-modal.js | 338 ++++++++++++++++++ .../sidebar/sidebar-request-group-row.js | 7 + .../insomnia-app/app/ui/components/wrapper.js | 14 + 4 files changed, 365 insertions(+), 16 deletions(-) create mode 100644 packages/insomnia-app/app/ui/components/modals/request-group-settings-modal.js diff --git a/packages/insomnia-app/app/ui/components/dropdowns/request-group-actions-dropdown.js b/packages/insomnia-app/app/ui/components/dropdowns/request-group-actions-dropdown.js index 143f0d2e3ee..2235410cc23 100644 --- a/packages/insomnia-app/app/ui/components/dropdowns/request-group-actions-dropdown.js +++ b/packages/insomnia-app/app/ui/components/dropdowns/request-group-actions-dropdown.js @@ -13,7 +13,7 @@ import { } from '../base/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 @@ type Props = { hotKeyRegistry: HotKeyRegistry, activeEnvironment: Environment | null, handleCreateRequest: (id: string) => any, + handleShowSettings: Function, handleDuplicateRequestGroup: (rg: RequestGroup) => any, handleMoveRequestGroup: (rg: RequestGroup) => any, handleCreateRequestGroup: (id: string) => any, @@ -53,18 +54,6 @@ class RequestGroupActionsDropdown extends React.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); } @@ -155,9 +144,6 @@ class RequestGroupActionsDropdown extends React.PureComponent { Duplicate - - Rename - Environment @@ -178,6 +164,10 @@ class RequestGroupActionsDropdown extends React.PureComponent { {p.label} ))} + + + Settings + ); } diff --git a/packages/insomnia-app/app/ui/components/modals/request-group-settings-modal.js b/packages/insomnia-app/app/ui/components/modals/request-group-settings-modal.js new file mode 100644 index 00000000000..5612861097f --- /dev/null +++ b/packages/insomnia-app/app/ui/components/modals/request-group-settings-modal.js @@ -0,0 +1,338 @@ +// @flow +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 * as db from '../../../common/database'; +import type { Workspace } from '../../../models/workspace'; +import type { RequestGroup } from '../../../models/request-group'; + +type Props = { + editorFontSize: number, + editorIndentSize: number, + editorKeyMap: string, + editorLineWrapping: boolean, + nunjucksPowerUserMode: boolean, + isVariableUncovered: boolean, + handleRender: Function, + handleGetRenderContext: Function, + workspaces: Array, +}; + +type State = { + requestGroupGroup: RequestGroup | null, + showDescription: boolean, + defaultPreviewMode: boolean, + activeWorkspaceIdToCopyTo: string | null, + workspace: Workspace | null, + justCopied: boolean, + justMoved: boolean, +}; + +type RequestGroupSettingsModalOptions = { + requestGroup: RequestGroup, + forceEditMode: boolean, +}; + +@autoBindMethodsForReact(AUTOBIND_CFG) +class RequestGroupSettingsModal extends React.PureComponent { + modal: ?Modal; + _editor: ?MarkdownEditor; + + constructor(props: Props) { + super(props); + this.state = { + requestGroup: null, + showDescription: false, + defaultPreviewMode: false, + activeWorkspaceIdToCopyTo: null, + workspace: null, + 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 = 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: 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; + } + + const patch = { + metaSortKey: -1e9, // Move to top of sort order + parentId: activeWorkspaceIdToCopyTo, + }; + + await models.requestGroup.update(requestGroup, patch); + + 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(): React.Node { + const { + editorLineWrapping, + editorFontSize, + editorIndentSize, + editorKeyMap, + handleRender, + handleGetRenderContext, + nunjucksPowerUserMode, + isVariableUncovered, + } = this.props; + + const { showDescription, defaultPreviewMode, requestGroup } = this.state; + + if (!requestGroup) { + return null; + } + + return showDescription ? ( + + ) : ( + + ); + } + + _renderMoveCopy(): React.Node { + const { workspaces } = this.props; + + const { + activeWorkspaceIdToCopyTo, + justMoved, + justCopied, + workspace, + requestGroup, + } = this.state; + + if (!requestGroup) { + return null; + } + + return ( +
+
+ +
+
+ +
+
+ +
+
+ ); + } + + renderModalBody(): React.Node { + const { requestGroup } = this.state; + + if (!requestGroup) { + return null; + } + + return ( +
+
+ +
+ {this._renderDescription()} + {this._renderMoveCopy()} +
+ ); + } + + render() { + const { requestGroup } = this.state; + return ( + + + Request Group Settings{' '} + + {requestGroup ? requestGroup._id : ''} + + + {this.renderModalBody()} + + ); + } +} + +export default RequestGroupSettingsModal; diff --git a/packages/insomnia-app/app/ui/components/sidebar/sidebar-request-group-row.js b/packages/insomnia-app/app/ui/components/sidebar/sidebar-request-group-row.js index 7057fa68be1..86375030cfd 100644 --- a/packages/insomnia-app/app/ui/components/sidebar/sidebar-request-group-row.js +++ b/packages/insomnia-app/app/ui/components/sidebar/sidebar-request-group-row.js @@ -9,6 +9,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'; @autoBindMethodsForReact(AUTOBIND_CFG) class SidebarRequestGroupRow extends PureComponent { @@ -47,6 +49,10 @@ class SidebarRequestGroupRow extends PureComponent { } } + _handleShowRequestSettings() { + showModal(RequestGroupSettingsModal, { requestGroup: this.props.requestGroup }); + } + render() { const { connectDragSource, @@ -123,6 +129,7 @@ class SidebarRequestGroupRow extends PureComponent { handleCreateRequest={handleCreateRequest} handleCreateRequestGroup={handleCreateRequestGroup} handleDuplicateRequestGroup={handleDuplicateRequestGroup} + handleShowSettings={this._handleShowRequestSettings} handleMoveRequestGroup={handleMoveRequestGroup} workspace={workspace} requestGroup={requestGroup} diff --git a/packages/insomnia-app/app/ui/components/wrapper.js b/packages/insomnia-app/app/ui/components/wrapper.js index ca5dc3c80d5..b213c302053 100644 --- a/packages/insomnia-app/app/ui/components/wrapper.js +++ b/packages/insomnia-app/app/ui/components/wrapper.js @@ -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'; @@ -624,6 +625,19 @@ class Wrapper extends React.PureComponent { isVariableUncovered={isVariableUncovered} /> + + {/* TODO: Figure out why cookieJar is sometimes null */} {activeCookieJar ? (