diff --git a/packages/insomnia/src/ui/components/modals/proto-files-modal.tsx b/packages/insomnia/src/ui/components/modals/proto-files-modal.tsx index af5dd1827d..f7b2dad010 100644 --- a/packages/insomnia/src/ui/components/modals/proto-files-modal.tsx +++ b/packages/insomnia/src/ui/components/modals/proto-files-modal.tsx @@ -1,195 +1,119 @@ -import { autoBindMethodsForReact } from 'class-autobind-decorator'; import { AsyncButton } from 'insomnia-components'; -import React, { PureComponent } from 'react'; -import { connect } from 'react-redux'; +import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react'; +import { useSelector } from 'react-redux'; -import { AUTOBIND_CFG } from '../../../common/constants'; import { GrpcRequestEventEnum } from '../../../common/grpc-events'; -import type { ProtoDirectory } from '../../../models/proto-directory'; import type { ProtoFile } from '../../../models/proto-file'; import * as protoManager from '../../../network/grpc/proto-manager'; import type { GrpcDispatch } from '../../context/grpc'; import { grpcActions, sendGrpcIpcMultiple } from '../../context/grpc'; -import { RootState } from '../../redux/modules'; import { selectExpandedActiveProtoDirectories } from '../../redux/proto-selectors'; import { selectActiveWorkspace } from '../../redux/selectors'; -import { type ModalHandle, Modal } from '../base/modal'; +import { type ModalHandle, Modal, ModalProps } from '../base/modal'; import { ModalBody } from '../base/modal-body'; import { ModalFooter } from '../base/modal-footer'; import { ModalHeader } from '../base/modal-header'; import { ProtoFileList } from '../proto-file/proto-file-list'; -type ReduxProps = ReturnType; - -interface Props extends ReduxProps { - grpcDispatch: GrpcDispatch; +export interface ProtoFilesModalOptions { + selectedId?: string; + onSave?: (arg0: string) => Promise; } - -interface State { - selectedProtoFileId: string; +export interface ProtoFilesModalHandle { + show: (options: ProtoFilesModalOptions) => void; + hide: () => void; } +type Props = ModalProps & { grpcDispatch: GrpcDispatch }; +export const ProtoFilesModal = forwardRef(({ grpcDispatch }, ref) => { + const modalRef = useRef(null); + const [state, setState] = useState({ + selectedId: '', + }); -interface ProtoFilesModalOptions { - preselectProtoFileId?: string; - onSave: (arg0: string) => Promise; -} + useImperativeHandle(ref, () => ({ + hide: () => { + modalRef.current?.hide(); + }, + show: ({ onSave, selectedId }) => { + setState({ onSave, selectedId }); + modalRef.current?.show(); + }, + }), []); -const INITIAL_STATE: State = { - selectedProtoFileId: '', -}; + const workspace = useSelector(selectActiveWorkspace); + const protoDirectories = useSelector(selectExpandedActiveProtoDirectories); -const spinner = ; + const selectFile = (selectedId: string) => setState({ selectedId, onSave: state.onSave }); + const clearSelection = () => setState({ selectedId: '', onSave: state.onSave }); -@autoBindMethodsForReact(AUTOBIND_CFG) -class ProtoFilesModal extends PureComponent { - modal: ModalHandle | null = null; - onSave: ((arg0: string) => Promise) | null; + const handleUpdate = async (protoFile: ProtoFile) => protoManager.updateFile(protoFile, async updatedId => { + const action = await grpcActions.invalidateMany(updatedId); + grpcDispatch(action); + sendGrpcIpcMultiple(GrpcRequestEventEnum.cancelMultiple, action?.requestIds); + }); - constructor(props: Props) { - super(props); - this.state = INITIAL_STATE; - this.onSave = null; + if (!workspace) { + return null; } - - _setModalRef(ref: ModalHandle) { - this.modal = ref; - } - - async show(options: ProtoFilesModalOptions) { - this.onSave = options.onSave; - this.setState({ - selectedProtoFileId: options.preselectProtoFileId || '', - }); - this.modal?.show(); - } - - async _handleSave(event: React.SyntheticEvent) { - event.preventDefault(); - this.hide(); - - if (typeof this.onSave === 'function' && this.state.selectedProtoFileId) { - await this.onSave(this.state.selectedProtoFileId); - } - } - - hide() { - this.modal?.hide(); - } - - _handleSelect(id: string) { - this.setState({ - selectedProtoFileId: id, - }); - } - - _handleDeleteFile(protoFile: ProtoFile) { - return protoManager.deleteFile(protoFile, deletedId => { - // if the deleted protoFile was previously selected, clear the selection - if (this.state.selectedProtoFileId === deletedId) { - this.setState({ - selectedProtoFileId: '', - }); - } - }); - } - - _handleDeleteDirectory(protoDirectory: ProtoDirectory) { - return protoManager.deleteDirectory(protoDirectory, deletedIds => { - // if previously selected protoFile has been deleted, clear the selection - if (deletedIds.includes(this.state.selectedProtoFileId)) { - this.setState({ - selectedProtoFileId: '', - }); - } - }); - } - - _handleAdd() { - const { workspace } = this.props; - - if (!workspace) { - return; - } - - return protoManager.addFile(workspace._id, createdId => { - this.setState({ - selectedProtoFileId: createdId, - }); - }); - } - - _handleUpload(protoFile: ProtoFile) { - const { grpcDispatch } = this.props; - return protoManager.updateFile(protoFile, async updatedId => { - const action = await grpcActions.invalidateMany(updatedId); - grpcDispatch(action); - sendGrpcIpcMultiple(GrpcRequestEventEnum.cancelMultiple, action?.requestIds); - }); - } - - _handleAddDirectory() { - const { workspace } = this.props; - - if (!workspace) { - return; - } - - return protoManager.addDirectory(workspace._id); - } - - _handleRename(protoFile: ProtoFile, name?: string) { - return protoManager.renameFile(protoFile, name); - } - - render() { - const { protoDirectories } = this.props; - const { selectedProtoFileId } = this.state; - return ( - - Select Proto File - -
- Files - - - Add Directory - - - Add Proto File - - -
- -
- -
- -
-
-
- ); - } -} - -const mapStateToProps = (state: RootState) => ({ - protoDirectories: selectExpandedActiveProtoDirectories(state), - workspace: selectActiveWorkspace(state), + return ( + + Select Proto File + +
+ Files + + protoManager.addDirectory(workspace._id)} + loadingNode={} + > + Add Directory + + protoManager.addFile(workspace._id, selectFile)} + loadingNode={} + > + Add Proto File + + +
+ protoManager.renameFile(protoFile, name)} + handleDelete={protoFile => protoManager.deleteFile(protoFile, deletedId => { + if (state.selectedId === deletedId) { + clearSelection(); + } + }) + } + handleDeleteDirectory={protoDirectory => protoManager.deleteDirectory(protoDirectory, deletedIds => { + if (state.selectedId && deletedIds.includes(state.selectedId)) { + clearSelection(); + } + })} + /> +
+ +
+ +
+
+
+ ); }); - -export default connect(mapStateToProps, null, null, { - forwardRef: true, -})(ProtoFilesModal); +ProtoFilesModal.displayName = 'ProtoFilesModal'; diff --git a/packages/insomnia/src/ui/components/panes/grpc-request-pane/use-change-handlers.ts b/packages/insomnia/src/ui/components/panes/grpc-request-pane/use-change-handlers.ts index 0f3063b46d..f565242114 100644 --- a/packages/insomnia/src/ui/components/panes/grpc-request-pane/use-change-handlers.ts +++ b/packages/insomnia/src/ui/components/panes/grpc-request-pane/use-change-handlers.ts @@ -5,7 +5,7 @@ import type { GrpcRequest, GrpcRequestHeader } from '../../../../models/grpc-req import type { GrpcDispatch } from '../../../context/grpc'; import { grpcActions } from '../../../context/grpc'; import { showModal } from '../../modals'; -import ProtoFilesModal from '../../modals/proto-files-modal'; +import { ProtoFilesModal } from '../../modals/proto-files-modal'; interface ChangeHandlers { url: (arg0: string) => Promise; @@ -45,7 +45,7 @@ const useChangeHandlers = (request: GrpcRequest, dispatch: GrpcDispatch): Change const protoFile = async () => { showModal(ProtoFilesModal, { - preselectProtoFileId: request.protoFileId, + selected: request.protoFileId, onSave: async (protoFileId: string) => { if (request.protoFileId !== protoFileId) { const initial = models.grpcRequest.init(); diff --git a/packages/insomnia/src/ui/containers/app.tsx b/packages/insomnia/src/ui/containers/app.tsx index b038963c07..40a5a946c6 100644 --- a/packages/insomnia/src/ui/containers/app.tsx +++ b/packages/insomnia/src/ui/containers/app.tsx @@ -33,7 +33,7 @@ import { LoginModal } from '../components/modals/login-modal'; import { NunjucksModal } from '../components/modals/nunjucks-modal'; import ProjectSettingsModal from '../components/modals/project-settings-modal'; import { PromptModal } from '../components/modals/prompt-modal'; -import ProtoFilesModal from '../components/modals/proto-files-modal'; +import { ProtoFilesModal } from '../components/modals/proto-files-modal'; import { RequestGroupSettingsModal } from '../components/modals/request-group-settings-modal'; import { RequestRenderErrorModal } from '../components/modals/request-render-error-modal'; import { RequestSettingsModal } from '../components/modals/request-settings-modal'; @@ -302,7 +302,7 @@ const App = () => { {dispatch => ( registerModal(instance, 'ProtoFilesModal')} grpcDispatch={dispatch} /> )} diff --git a/packages/insomnia/src/ui/hooks/create-request.ts b/packages/insomnia/src/ui/hooks/create-request.ts index 9a9245811b..68b4538285 100644 --- a/packages/insomnia/src/ui/hooks/create-request.ts +++ b/packages/insomnia/src/ui/hooks/create-request.ts @@ -13,7 +13,7 @@ import { GrpcRequestMeta } from '../../models/grpc-request-meta'; import { RequestMeta } from '../../models/request-meta'; import { WorkspaceMeta } from '../../models/workspace-meta'; import { showModal } from '../components/modals'; -import ProtoFilesModal from '../components/modals/proto-files-modal'; +import { ProtoFilesModal } from '../components/modals/proto-files-modal'; export const updateActiveWorkspaceMeta = async ( patch: Partial,