From c73092e2947d3eaf059473086b178fa2fe117df0 Mon Sep 17 00:00:00 2001 From: Opender Singh Date: Fri, 23 Oct 2020 08:14:46 +1300 Subject: [PATCH] Modal prompt to select protofile (#2756) --- .../insomnia-app/app/models/proto-file.js | 4 +- .../ui/components/modals/proto-files-modal.js | 121 ++++++++++++++++++ .../components/modals/request-create-modal.js | 17 ++- .../proto-file/proto-file-list-item.js | 54 ++++++++ .../components/proto-file/proto-file-list.js | 32 +++++ .../insomnia-app/app/ui/components/wrapper.js | 3 + 6 files changed, 222 insertions(+), 9 deletions(-) create mode 100644 packages/insomnia-app/app/ui/components/modals/proto-files-modal.js create mode 100644 packages/insomnia-app/app/ui/components/proto-file/proto-file-list-item.js create mode 100644 packages/insomnia-app/app/ui/components/proto-file/proto-file-list.js diff --git a/packages/insomnia-app/app/models/proto-file.js b/packages/insomnia-app/app/models/proto-file.js index e8e6dcf528..d7a22accb9 100644 --- a/packages/insomnia-app/app/models/proto-file.js +++ b/packages/insomnia-app/app/models/proto-file.js @@ -46,8 +46,8 @@ export function getById(_id: string): Promise { return db.getWhere(type, { _id }); } -export function getByParentId(parentId: string): Promise { - return db.getWhere(type, { parentId }); +export function findByParentId(parentId: string): Promise> { + return db.find(type, { parentId }); } export function all(): Promise> { diff --git a/packages/insomnia-app/app/ui/components/modals/proto-files-modal.js b/packages/insomnia-app/app/ui/components/modals/proto-files-modal.js new file mode 100644 index 0000000000..1a386cb4b3 --- /dev/null +++ b/packages/insomnia-app/app/ui/components/modals/proto-files-modal.js @@ -0,0 +1,121 @@ +// @flow +import * as React from 'react'; +import * as models from '../../../models'; +import type { ProtoFile } from '../../../models/proto-file'; +import ModalHeader from '../base/modal-header'; +import ModalBody from '../base/modal-body'; +import ModalFooter from '../base/modal-footer'; +import autobind from 'autobind-decorator'; +import type { Workspace } from '../../../models/workspace'; +import Modal from '../base/modal'; +import ProtoFileList from '../proto-file/proto-file-list'; + +type Props = {| + workspace: Workspace, +|}; + +type State = {| + protoFiles: Array, + selectedProtoFileId: string, +|}; + +type ProtoFilesModalOptions = {| + preselectProtoFileId?: string, + onSave: string => Promise, +|}; + +const INITIAL_STATE: State = { + protoFiles: [], + selectedProtoFileId: '', +}; + +@autobind +class ProtoFilesModal extends React.PureComponent { + modal: Modal | null; + onSave: (string => Promise) | null; + + constructor(props: Props) { + super(props); + + this.state = INITIAL_STATE; + this.onSave = null; + } + + _setModalRef(ref: ?Modal) { + this.modal = ref; + } + + async show(options: ProtoFilesModalOptions) { + this.onSave = options.onSave; + this.setState({ ...INITIAL_STATE }); + + this.modal && this.modal.show(); + await this._refresh(options.preselectProtoFileId); + } + + async _refresh(preselectProtoFileId?: string) { + const { workspace } = this.props; + const protoFilesForWorkspace = await models.protoFile.findByParentId(workspace._id); + + protoFilesForWorkspace.push( + { _id: 'pf_123', name: 'File 1' }, + { _id: 'pf_456', name: 'File 2' }, + { _id: 'pf_789', name: 'File 3' }, + ); + + this.setState({ + protoFiles: protoFilesForWorkspace, + selectedProtoFileId: preselectProtoFileId, + }); + } + + async _handleSave(e: SyntheticEvent) { + e.preventDefault(); + this.hide(); + + if (typeof this.onSave === 'function') { + await this.onSave(this.state.selectedProtoFileId); + } + } + + hide() { + this.modal && this.modal.hide(); + } + + _handleSelect(id: string) { + this.setState({ selectedProtoFileId: id }); + } + + _handleDelete(protoFile: ProtoFile) { + // TODO: to be built in INS-209 + console.log(`delete ${protoFile._id}`); + } + + render() { + const { protoFiles, selectedProtoFileId } = this.state; + + return ( + + Select Protofile + + Files + + + +
+ +
+
+
+ ); + } +} + +export default ProtoFilesModal; diff --git a/packages/insomnia-app/app/ui/components/modals/request-create-modal.js b/packages/insomnia-app/app/ui/components/modals/request-create-modal.js index 51b34eb7bb..0eedbe9eeb 100644 --- a/packages/insomnia-app/app/ui/components/modals/request-create-modal.js +++ b/packages/insomnia-app/app/ui/components/modals/request-create-modal.js @@ -17,7 +17,8 @@ import { } from '../../../common/constants'; import * as models from '../../../models/index'; import { trackEvent } from '../../../common/analytics'; -import { showAlert } from './index'; +import { showModal } from './index'; +import ProtoFilesModal from './proto-files-modal'; type RequestCreateModalOptions = { parentId: string, @@ -54,13 +55,15 @@ class RequestCreateModal extends PureComponent { const { parentId, selectedContentType, selectedMethod } = this.state; const requestName = this._input.value; if (selectedMethod === METHOD_GRPC) { - // TODO: Create new grpc request with the name - INS-198 + showModal(ProtoFilesModal, { + onSave: async protofileId => { + // TODO: Create new grpc request with the name and selected proto file - INS-198 + console.log(`Create request name: [${requestName}], and protofileId: [${protofileId}]`); - const id = 'gr_123'; - this._onComplete(id); - - // TODO: Show protofile selection modal - INS-188 - showAlert({ title: 'Select protofile', message: 'To be built', okLabel: 'ok' }); + const createdRequestId = 'gr_123'; + this._onComplete(createdRequestId); + }, + }); } else { const request = await models.initModel(models.request.type, { parentId, diff --git a/packages/insomnia-app/app/ui/components/proto-file/proto-file-list-item.js b/packages/insomnia-app/app/ui/components/proto-file/proto-file-list-item.js new file mode 100644 index 0000000000..efcc451004 --- /dev/null +++ b/packages/insomnia-app/app/ui/components/proto-file/proto-file-list-item.js @@ -0,0 +1,54 @@ +// @flow +import React from 'react'; +import styled from 'styled-components'; +import type { ProtoFile } from '../../../models/proto-file'; +import PromptButton from '../base/prompt-button'; +import type { DeleteProtoFileHandler, SelectProtoFileHandler } from './proto-file-list'; +import { ListGroupItem } from '../../../../../insomnia-components'; + +type Props = { + protoFile: ProtoFile, + isSelected?: boolean, + handleSelect: SelectProtoFileHandler, + handleDelete: DeleteProtoFileHandler, +}; + +const SelectableListItem: React.PureComponent<{ isSelected?: boolean }> = styled(ListGroupItem)` + &:hover { + background-color: var(--hl-sm) !important; + } + + background-color: ${({ isSelected }) => isSelected && 'var(--hl-sm) !important'}; +`; + +const ProtoFileListItem = ({ protoFile, isSelected, handleSelect, handleDelete }: Props) => { + const { name, _id } = protoFile; + + // Don't re-instantiate the callbacks if the dependencies have not changed + const handleSelectCallback = React.useCallback(() => handleSelect(_id), [handleSelect, _id]); + const handleDeleteCallback = React.useCallback( + async (e: SyntheticEvent) => { + e.stopPropagation(); + await handleDelete(protoFile); + }, + [handleDelete, protoFile], + ); + + return ( + +
+ {name} + + + +
+
+ ); +}; + +export default ProtoFileListItem; diff --git a/packages/insomnia-app/app/ui/components/proto-file/proto-file-list.js b/packages/insomnia-app/app/ui/components/proto-file/proto-file-list.js new file mode 100644 index 0000000000..fccc31b56c --- /dev/null +++ b/packages/insomnia-app/app/ui/components/proto-file/proto-file-list.js @@ -0,0 +1,32 @@ +// @flow +import React from 'react'; +import type { ProtoFile } from '../../../models/proto-file'; +import { ListGroup, ListGroupItem } from 'insomnia-components'; +import ProtoFileListItem from './proto-file-list-item'; + +export type SelectProtoFileHandler = (id: string) => void; +export type DeleteProtoFileHandler = (protofile: ProtoFile) => Promise; + +type Props = { + protoFiles: Array, + selectedId?: string, + handleSelect: SelectProtoFileHandler, + handleDelete: DeleteProtoFileHandler, +}; + +const ProtoFileList = ({ protoFiles, selectedId, handleSelect, handleDelete }: Props) => ( + + {!protoFiles.length && No proto files exist for this workspace} + {protoFiles.map(p => ( + + ))} + +); + +export default ProtoFileList; diff --git a/packages/insomnia-app/app/ui/components/wrapper.js b/packages/insomnia-app/app/ui/components/wrapper.js index ea317428ee..72d7f1f640 100644 --- a/packages/insomnia-app/app/ui/components/wrapper.js +++ b/packages/insomnia-app/app/ui/components/wrapper.js @@ -90,6 +90,7 @@ import { getAppName, } from '../../common/constants'; import { Spectral } from '@stoplight/spectral'; +import ProtoFilesModal from './modals/proto-files-modal'; const spectral = new Spectral(); @@ -770,6 +771,8 @@ class Wrapper extends React.PureComponent { childObjects={sidebarChildren.all} handleExportRequestsToFile={handleExportRequestsToFile} /> + +