mirror of
https://github.com/Kong/insomnia
synced 2024-11-08 06:39:48 +00:00
app tsx fc (#5235)
* fc rebase * extract render listeners from app * organise global shortcuts * show logo in index.html * import listeners * add loading indicator and split shortcuts * Simplify GitVCS/VCS instance creation Co-authored-by: gatzjames <jamesgatzos@gmail.com>
This commit is contained in:
parent
a94c488410
commit
9be7e05000
@ -29,7 +29,7 @@ import { selectSettings } from '../../redux/selectors';
|
||||
import { Dropdown } from '../base/dropdown/dropdown';
|
||||
import { DropdownButton } from '../base/dropdown/dropdown-button';
|
||||
import { DropdownItem } from '../base/dropdown/dropdown-item';
|
||||
import { useGlobalKeyboardShortcuts } from '../keydown-binder';
|
||||
import { useDocBodyKeyboardShortcuts } from '../keydown-binder';
|
||||
import { FilterHelpModal } from '../modals/filter-help-modal';
|
||||
import { showModal } from '../modals/index';
|
||||
import { isKeyCombinationInRegistry } from '../settings/shortcuts';
|
||||
@ -188,7 +188,7 @@ const CodeEditorFCWithRef: ForwardRefRenderFunction<UnconnectedCodeEditor, RawPr
|
||||
ref
|
||||
) => {
|
||||
const editorRef = useRef<UnconnectedCodeEditor | null>(null);
|
||||
useGlobalKeyboardShortcuts({
|
||||
useDocBodyKeyboardShortcuts({
|
||||
beautifyRequestBody: () => editorRef.current?._prettify(),
|
||||
});
|
||||
|
||||
|
@ -10,7 +10,7 @@ import { DropdownButton } from '../base/dropdown/dropdown-button';
|
||||
import { DropdownDivider } from '../base/dropdown/dropdown-divider';
|
||||
import { DropdownHint } from '../base/dropdown/dropdown-hint';
|
||||
import { DropdownItem } from '../base/dropdown/dropdown-item';
|
||||
import { useGlobalKeyboardShortcuts } from '../keydown-binder';
|
||||
import { useDocBodyKeyboardShortcuts } from '../keydown-binder';
|
||||
import { showModal } from '../modals/index';
|
||||
import { WorkspaceEnvironmentsEditModal } from '../modals/workspace-environments-edit-modal';
|
||||
import { Tooltip } from '../tooltip';
|
||||
@ -39,7 +39,7 @@ export const EnvironmentsDropdown: FC<Props> = ({
|
||||
dropdownRef.current?.toggle(true);
|
||||
}, []);
|
||||
|
||||
useGlobalKeyboardShortcuts({
|
||||
useDocBodyKeyboardShortcuts({
|
||||
environment_showSwitchMenu: toggleSwitchMenu,
|
||||
});
|
||||
|
||||
|
@ -13,7 +13,7 @@ import { DropdownButton } from '../base/dropdown/dropdown-button';
|
||||
import { DropdownDivider } from '../base/dropdown/dropdown-divider';
|
||||
import { DropdownItem } from '../base/dropdown/dropdown-item';
|
||||
import { PromptButton } from '../base/prompt-button';
|
||||
import { useGlobalKeyboardShortcuts } from '../keydown-binder';
|
||||
import { useDocBodyKeyboardShortcuts } from '../keydown-binder';
|
||||
import { SizeTag } from '../tags/size-tag';
|
||||
import { StatusTag } from '../tags/status-tag';
|
||||
import { TimeTag } from '../tags/time-tag';
|
||||
@ -163,7 +163,7 @@ export const ResponseHistoryDropdown = <GenericResponse extends Response | WebSo
|
||||
);
|
||||
};
|
||||
|
||||
useGlobalKeyboardShortcuts({
|
||||
useDocBodyKeyboardShortcuts({
|
||||
request_toggleHistory: () => dropdownRef.current?.toggle(true),
|
||||
});
|
||||
|
||||
|
@ -32,7 +32,7 @@ import { CodeEditor } from '../../codemirror/code-editor';
|
||||
import { GraphQLExplorer } from '../../graph-ql-explorer/graph-ql-explorer';
|
||||
import { ActiveReference } from '../../graph-ql-explorer/graph-ql-types';
|
||||
import { HelpTooltip } from '../../help-tooltip';
|
||||
import { useGlobalKeyboardShortcuts } from '../../keydown-binder';
|
||||
import { useDocBodyKeyboardShortcuts } from '../../keydown-binder';
|
||||
import { TimeFromNow } from '../../time-from-now';
|
||||
const explorerContainer = document.querySelector('#graphql-explorer-container');
|
||||
|
||||
@ -317,7 +317,7 @@ export const GraphQLEditor: FC<Props> = ({
|
||||
}
|
||||
};
|
||||
|
||||
useGlobalKeyboardShortcuts({
|
||||
useDocBodyKeyboardShortcuts({
|
||||
beautifyRequestBody: _handlePrettify,
|
||||
});
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { GraphQLEnumType, GraphQLField, GraphQLNamedType, GraphQLSchema, GraphQL
|
||||
import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { DebouncedInput } from '../base/debounced-input';
|
||||
import { useGlobalKeyboardShortcuts } from '../keydown-binder';
|
||||
import { useDocBodyKeyboardShortcuts } from '../keydown-binder';
|
||||
import { GraphQLExplorerEnum } from './graph-ql-explorer-enum';
|
||||
import { GraphQLExplorerField } from './graph-ql-explorer-field';
|
||||
import { GraphQLExplorerSchema } from './graph-ql-explorer-schema';
|
||||
@ -131,7 +131,7 @@ export const GraphQLExplorer: FC<Props> = ({ schema, handleClose, visible, refer
|
||||
});
|
||||
};
|
||||
|
||||
useGlobalKeyboardShortcuts({
|
||||
useDocBodyKeyboardShortcuts({
|
||||
graphql_explorer_focus_filter: () => {
|
||||
setState(state => ({
|
||||
...state,
|
||||
|
@ -36,7 +36,7 @@ export function useKeyboardShortcuts(getTarget: () => HTMLElement, listeners: {
|
||||
}, [hotKeyRegistry, listeners, getTarget]);
|
||||
}
|
||||
|
||||
export function useGlobalKeyboardShortcuts(listeners: { [key in KeyboardShortcut]?: (event: KeyboardEvent) => any }) {
|
||||
export function useDocBodyKeyboardShortcuts(listeners: { [key in KeyboardShortcut]?: (event: KeyboardEvent) => any }) {
|
||||
useKeyboardShortcuts(() => document.body, listeners);
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ import { Highlight } from '../base/highlight';
|
||||
import { Modal, ModalHandle, ModalProps } from '../base/modal';
|
||||
import { ModalBody } from '../base/modal-body';
|
||||
import { ModalHeader } from '../base/modal-header';
|
||||
import { createKeybindingsHandler, useGlobalKeyboardShortcuts } from '../keydown-binder';
|
||||
import { createKeybindingsHandler, useDocBodyKeyboardShortcuts } from '../keydown-binder';
|
||||
import { GrpcTag } from '../tags/grpc-tag';
|
||||
import { MethodTag } from '../tags/method-tag';
|
||||
import { WebSocketTag } from '../tags/websocket-tag';
|
||||
@ -294,7 +294,7 @@ export const RequestSwitcherModal = forwardRef<RequestSwitcherModalHandle, Modal
|
||||
},
|
||||
});
|
||||
|
||||
useGlobalKeyboardShortcuts({
|
||||
useDocBodyKeyboardShortcuts({
|
||||
request_showRecent: () => {
|
||||
if (state.isModalVisible) {
|
||||
setState(state => ({
|
||||
|
@ -16,7 +16,7 @@ import { OneLineEditor } from '../../codemirror/one-line-editor';
|
||||
import { GrpcMethodDropdown } from '../../dropdowns/grpc-method-dropdown/grpc-method-dropdown';
|
||||
import { ErrorBoundary } from '../../error-boundary';
|
||||
import { KeyValueEditor } from '../../key-value-editor/key-value-editor';
|
||||
import { useGlobalKeyboardShortcuts } from '../../keydown-binder';
|
||||
import { useDocBodyKeyboardShortcuts } from '../../keydown-binder';
|
||||
import { GrpcTabbedMessages } from '../../viewers/grpc-tabbed-messages';
|
||||
import { EmptyStatePane } from '../empty-state-pane';
|
||||
import { Pane, PaneBody, PaneHeader } from '../pane';
|
||||
@ -78,7 +78,7 @@ export const GrpcRequestPane: FunctionComponent<Props> = ({
|
||||
}
|
||||
}, [method, running, start]);
|
||||
|
||||
useGlobalKeyboardShortcuts({
|
||||
useDocBodyKeyboardShortcuts({
|
||||
request_send: handleRequestSend,
|
||||
});
|
||||
|
||||
|
@ -24,7 +24,7 @@ import { DropdownItem } from './base/dropdown/dropdown-item';
|
||||
import { PromptButton } from './base/prompt-button';
|
||||
import { OneLineEditor } from './codemirror/one-line-editor';
|
||||
import { MethodDropdown } from './dropdowns/method-dropdown';
|
||||
import { createKeybindingsHandler, useGlobalKeyboardShortcuts } from './keydown-binder';
|
||||
import { createKeybindingsHandler, useDocBodyKeyboardShortcuts } from './keydown-binder';
|
||||
import { GenerateCodeModal } from './modals/generate-code-modal';
|
||||
import { showAlert, showModal, showPrompt } from './modals/index';
|
||||
import { RequestRenderErrorModal } from './modals/request-render-error-modal';
|
||||
@ -275,7 +275,7 @@ export const RequestUrlBar = forwardRef<RequestUrlBarHandle, Props>(({
|
||||
}, [request._id]);
|
||||
const handleClearDownloadLocation = () => updateRequestMetaByParentId(request._id, { downloadPath: null });
|
||||
|
||||
useGlobalKeyboardShortcuts({
|
||||
useDocBodyKeyboardShortcuts({
|
||||
request_focusUrl: () => {
|
||||
inputRef.current?.focus();
|
||||
inputRef.current?.selectAll();
|
||||
|
@ -7,7 +7,7 @@ import { sortMethodMap } from '../../../common/sorting';
|
||||
import * as models from '../../../models';
|
||||
import { isRequestGroup } from '../../../models/request-group';
|
||||
import { selectActiveWorkspace, selectActiveWorkspaceMeta } from '../../redux/selectors';
|
||||
import { useGlobalKeyboardShortcuts } from '../keydown-binder';
|
||||
import { useDocBodyKeyboardShortcuts } from '../keydown-binder';
|
||||
import { SidebarCreateDropdown } from './sidebar-create-dropdown';
|
||||
import { SidebarSortDropdown } from './sidebar-sort-dropdown';
|
||||
|
||||
@ -36,7 +36,7 @@ export const SidebarFilter: FC<Props> = ({ filter }) => {
|
||||
}
|
||||
}, [activeWorkspaceMeta]);
|
||||
|
||||
useGlobalKeyboardShortcuts({
|
||||
useDocBodyKeyboardShortcuts({
|
||||
sidebar_focusFilter: () => {
|
||||
inputRef.current?.focus();
|
||||
},
|
||||
|
@ -16,7 +16,7 @@ import {
|
||||
import { clickLink } from '../../../common/electron-helpers';
|
||||
import { xmlDecode } from '../../../common/misc';
|
||||
import { CodeEditor, UnconnectedCodeEditor } from '../codemirror/code-editor';
|
||||
import { useGlobalKeyboardShortcuts } from '../keydown-binder';
|
||||
import { useDocBodyKeyboardShortcuts } from '../keydown-binder';
|
||||
import { ResponseCSVViewer } from './response-csv-viewer';
|
||||
import { ResponseErrorViewer } from './response-error-viewer';
|
||||
import { ResponseMultipartViewer } from './response-multipart-viewer';
|
||||
@ -122,7 +122,7 @@ export const ResponseViewer = ({
|
||||
);
|
||||
};
|
||||
|
||||
useGlobalKeyboardShortcuts({
|
||||
useDocBodyKeyboardShortcuts({
|
||||
response_focus: () => {
|
||||
if (!_isViewSelectable()) {
|
||||
return;
|
||||
|
@ -7,7 +7,7 @@ import * as models from '../../../models';
|
||||
import { WebSocketRequest } from '../../../models/websocket-request';
|
||||
import { ReadyState } from '../../context/websocket-client/use-ws-ready-state';
|
||||
import { OneLineEditor } from '../codemirror/one-line-editor';
|
||||
import { useGlobalKeyboardShortcuts } from '../keydown-binder';
|
||||
import { useDocBodyKeyboardShortcuts } from '../keydown-binder';
|
||||
import { showAlert, showModal } from '../modals';
|
||||
import { RequestRenderErrorModal } from '../modals/request-render-error-modal';
|
||||
|
||||
@ -118,7 +118,7 @@ export const WebSocketActionBar: FC<ActionBarProps> = ({ request, workspaceId, e
|
||||
}
|
||||
}, [environmentId, isOpen, request, workspaceId]);
|
||||
|
||||
useGlobalKeyboardShortcuts({
|
||||
useDocBodyKeyboardShortcuts({
|
||||
request_send: () => handleSubmit(),
|
||||
request_focusUrl: () => {
|
||||
editorRef.current?.focus();
|
||||
|
@ -1,16 +1,24 @@
|
||||
import React, { FC, Fragment, ReactNode, useEffect } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { SegmentEvent, trackSegmentEvent } from '../../common/analytics';
|
||||
import * as models from '../../models';
|
||||
import { isGrpcRequest } from '../../models/grpc-request';
|
||||
import { getByParentId as getGrpcRequestMetaByParentId } from '../../models/grpc-request-meta';
|
||||
import * as requestOperations from '../../models/helpers/request-operations';
|
||||
import { isRemoteProject } from '../../models/project';
|
||||
import { getByParentId as getRequestMetaByParentId } from '../../models/request-meta';
|
||||
import { isWebSocketRequest } from '../../models/websocket-request';
|
||||
import { isCollection, isDesign } from '../../models/workspace';
|
||||
import { VCS } from '../../sync/vcs/vcs';
|
||||
import { updateRequestMetaByParentId } from '../hooks/create-request';
|
||||
import { createRequestGroup } from '../hooks/create-request-group';
|
||||
import {
|
||||
selectActiveEnvironment,
|
||||
selectActiveProject,
|
||||
selectActiveRequest,
|
||||
selectActiveWorkspace,
|
||||
selectActiveWorkspaceMeta,
|
||||
selectIsLoggedIn,
|
||||
selectSettings,
|
||||
} from '../redux/selectors';
|
||||
@ -18,7 +26,15 @@ import { selectSidebarFilter } from '../redux/sidebar-selectors';
|
||||
import { EnvironmentsDropdown } from './dropdowns/environments-dropdown';
|
||||
import { SyncDropdown } from './dropdowns/sync-dropdown';
|
||||
import { ErrorBoundary } from './error-boundary';
|
||||
import { showCookiesModal } from './modals/cookies-modal';
|
||||
import { useDocBodyKeyboardShortcuts } from './keydown-binder';
|
||||
import { showModal } from './modals';
|
||||
import { AskModal } from './modals/ask-modal';
|
||||
import { CookiesModal, showCookiesModal } from './modals/cookies-modal';
|
||||
import { GenerateCodeModal } from './modals/generate-code-modal';
|
||||
import { PromptModal } from './modals/prompt-modal';
|
||||
import { RequestSettingsModal } from './modals/request-settings-modal';
|
||||
import { RequestSwitcherModal } from './modals/request-switcher-modal';
|
||||
import { WorkspaceEnvironmentsEditModal } from './modals/workspace-environments-edit-modal';
|
||||
import { PageLayout } from './page-layout';
|
||||
import { GrpcRequestPane } from './panes/grpc-request-pane';
|
||||
import { GrpcResponsePane } from './panes/grpc-response-pane';
|
||||
@ -37,7 +53,6 @@ interface Props {
|
||||
handleActivityChange: HandleActivityChange;
|
||||
handleSetActiveEnvironment: (id: string | null) => void;
|
||||
handleImport: Function;
|
||||
handleSetResponseFilter: (filter: string) => void;
|
||||
vcs: VCS | null;
|
||||
}
|
||||
export const WrapperDebug: FC<Props> = ({
|
||||
@ -45,7 +60,6 @@ export const WrapperDebug: FC<Props> = ({
|
||||
handleActivityChange,
|
||||
handleSetActiveEnvironment,
|
||||
handleImport,
|
||||
handleSetResponseFilter,
|
||||
vcs,
|
||||
}) => {
|
||||
const activeProject = useSelector(selectActiveProject);
|
||||
@ -56,15 +70,142 @@ export const WrapperDebug: FC<Props> = ({
|
||||
const activeWorkspace = useSelector(selectActiveWorkspace);
|
||||
const settings = useSelector(selectSettings);
|
||||
const sidebarFilter = useSelector(selectSidebarFilter);
|
||||
const activeWorkspaceMeta = useSelector(selectActiveWorkspaceMeta);
|
||||
|
||||
const isTeamSync = isLoggedIn && activeWorkspace && isCollection(activeWorkspace) && isRemoteProject(activeProject) && vcs;
|
||||
|
||||
useDocBodyKeyboardShortcuts({
|
||||
request_togglePin:
|
||||
async () => {
|
||||
if (activeRequest) {
|
||||
const meta = isGrpcRequest(activeRequest) ? await getGrpcRequestMetaByParentId(activeRequest._id) : await getRequestMetaByParentId(activeRequest._id);
|
||||
updateRequestMetaByParentId(activeRequest._id, { pinned: !meta?.pinned });
|
||||
}
|
||||
},
|
||||
request_showSettings:
|
||||
() => {
|
||||
if (activeRequest) {
|
||||
showModal(RequestSettingsModal, { request: activeRequest });
|
||||
}
|
||||
},
|
||||
request_showDelete:
|
||||
() => {
|
||||
if (activeRequest) {
|
||||
showModal(AskModal, {
|
||||
title: 'Delete Request?',
|
||||
message: `Really delete ${activeRequest.name}?`,
|
||||
onDone: async (confirmed: boolean) => {
|
||||
if (confirmed) {
|
||||
await requestOperations.remove(activeRequest);
|
||||
models.stats.incrementDeletedRequests();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
request_showDuplicate:
|
||||
() => {
|
||||
if (activeRequest) {
|
||||
showModal(PromptModal, {
|
||||
title: 'Duplicate Request',
|
||||
defaultValue: activeRequest.name,
|
||||
submitName: 'Create',
|
||||
label: 'New Name',
|
||||
selectText: true,
|
||||
onComplete: async (name: string) => {
|
||||
const newRequest = await requestOperations.duplicate(activeRequest, {
|
||||
name,
|
||||
});
|
||||
if (activeWorkspaceMeta) {
|
||||
await models.workspaceMeta.update(activeWorkspaceMeta, { activeRequestId: newRequest._id });
|
||||
}
|
||||
await updateRequestMetaByParentId(newRequest._id, {
|
||||
lastActive: Date.now(),
|
||||
});
|
||||
models.stats.incrementCreatedRequests();
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
request_createHTTP:
|
||||
async () => {
|
||||
if (activeWorkspace) {
|
||||
const parentId = activeRequest ? activeRequest.parentId : activeWorkspace._id;
|
||||
const request = await models.request.create({
|
||||
parentId,
|
||||
name: 'New Request',
|
||||
});
|
||||
if (activeWorkspaceMeta) {
|
||||
await models.workspaceMeta.update(activeWorkspaceMeta, { activeRequestId: request._id });
|
||||
}
|
||||
await updateRequestMetaByParentId(request._id, {
|
||||
lastActive: Date.now(),
|
||||
});
|
||||
models.stats.incrementCreatedRequests();
|
||||
trackSegmentEvent(SegmentEvent.requestCreate, { requestType: 'HTTP' });
|
||||
}
|
||||
},
|
||||
request_showCreateFolder:
|
||||
() => {
|
||||
if (activeWorkspace) {
|
||||
createRequestGroup(activeRequest ? activeRequest.parentId : activeWorkspace._id);
|
||||
}
|
||||
},
|
||||
request_showRecent:
|
||||
() => showModal(RequestSwitcherModal, {
|
||||
disableInput: true,
|
||||
maxRequests: 10,
|
||||
maxWorkspaces: 0,
|
||||
selectOnKeyup: true,
|
||||
title: 'Recent Requests',
|
||||
hideNeverActiveRequests: true,
|
||||
// Add an open delay so the dialog won't show for quick presses
|
||||
openDelay: 150,
|
||||
}),
|
||||
request_quickSwitch:
|
||||
() => showModal(RequestSwitcherModal),
|
||||
environment_showEditor:
|
||||
() => showModal(WorkspaceEnvironmentsEditModal, activeWorkspace),
|
||||
showCookiesEditor:
|
||||
() => showModal(CookiesModal),
|
||||
request_showGenerateCodeEditor:
|
||||
() => showModal(GenerateCodeModal, activeRequest),
|
||||
});
|
||||
// Close all websocket connections when the active environment changes
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
window.main.webSocket.closeAll();
|
||||
};
|
||||
}, [activeEnvironment?._id]);
|
||||
|
||||
async function handleSetResponseFilter(responseFilter: string) {
|
||||
if (!activeRequest) {
|
||||
return;
|
||||
}
|
||||
const requestId = activeRequest._id;
|
||||
await updateRequestMetaByParentId(requestId, { responseFilter });
|
||||
|
||||
const meta = await models.requestMeta.getByParentId(requestId);
|
||||
if (!meta) {
|
||||
return;
|
||||
}
|
||||
const responseFilterHistory = meta.responseFilterHistory.slice(0, 10);
|
||||
|
||||
// Already in history?
|
||||
if (responseFilterHistory.includes(responseFilter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Blank?
|
||||
if (!responseFilter) {
|
||||
return;
|
||||
}
|
||||
|
||||
responseFilterHistory.unshift(responseFilter);
|
||||
await updateRequestMetaByParentId(requestId, {
|
||||
responseFilterHistory,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<PageLayout
|
||||
renderPageHeader={activeWorkspace ?
|
||||
|
@ -36,7 +36,7 @@ import { AppHeader } from './app-header';
|
||||
import { DashboardSortDropdown } from './dropdowns/dashboard-sort-dropdown';
|
||||
import { ProjectDropdown } from './dropdowns/project-dropdown';
|
||||
import { RemoteWorkspacesDropdown } from './dropdowns/remote-workspaces-dropdown';
|
||||
import { useGlobalKeyboardShortcuts } from './keydown-binder';
|
||||
import { useDocBodyKeyboardShortcuts } from './keydown-binder';
|
||||
import { showPrompt } from './modals';
|
||||
import { PageLayout } from './page-layout';
|
||||
import { WrapperHomeEmptyStatePane } from './panes/wrapper-home-empty-state-pane';
|
||||
@ -245,7 +245,7 @@ const WrapperHome: FC<Props> = (({ vcs }) => {
|
||||
setFilter(event.currentTarget.value);
|
||||
}, []);
|
||||
|
||||
useGlobalKeyboardShortcuts({
|
||||
useDocBodyKeyboardShortcuts({
|
||||
documents_filter: () => inputRef.current?.focus(),
|
||||
});
|
||||
|
||||
|
@ -131,7 +131,6 @@ const ActivityRouter = () => {
|
||||
const spectral = initializeSpectral();
|
||||
|
||||
export type Props = ReturnType<typeof mapStateToProps> & ReturnType<typeof mapDispatchToProps> & {
|
||||
handleSetResponseFilter: Function;
|
||||
vcs: VCS | null;
|
||||
gitVCS: GitVCS | null;
|
||||
};
|
||||
@ -244,12 +243,6 @@ export class WrapperClass extends PureComponent<Props, State> {
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
_handleSetResponseFilter(filter: string) {
|
||||
const activeRequest = this.props.activeRequest;
|
||||
const activeRequestId = activeRequest ? activeRequest._id : 'n/a';
|
||||
this.props.handleSetResponseFilter(activeRequestId, filter);
|
||||
}
|
||||
|
||||
_handleGitBranchChanged(branch: string) {
|
||||
this.setState({
|
||||
activeGitBranch: branch || 'no-vcs',
|
||||
@ -343,7 +336,7 @@ export class WrapperClass extends PureComponent<Props, State> {
|
||||
<SettingsModal ref={instance => registerModal(instance, 'SettingsModal')} />
|
||||
<ResponseDebugModal ref={instance => registerModal(instance, 'ResponseDebugModal')} />
|
||||
|
||||
<RequestSwitcherModal ref={instance => registerModal(instance, 'RequestSwitcherModal')}/>
|
||||
<RequestSwitcherModal ref={instance => registerModal(instance, 'RequestSwitcherModal')} />
|
||||
|
||||
<EnvironmentEditModal
|
||||
ref={registerModal}
|
||||
@ -439,7 +432,6 @@ export class WrapperClass extends PureComponent<Props, State> {
|
||||
handleActivityChange={this._handleWorkspaceActivityChange}
|
||||
handleSetActiveEnvironment={this._handleSetActiveEnvironment}
|
||||
handleImport={this._handleImport}
|
||||
handleSetResponseFilter={this._handleSetResponseFilter}
|
||||
vcs={vcs}
|
||||
/>
|
||||
</Suspense>
|
||||
|
@ -1,151 +1,12 @@
|
||||
import { FC } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { SegmentEvent, trackSegmentEvent } from '../../common/analytics';
|
||||
import * as models from '../../models';
|
||||
import { isGrpcRequest } from '../../models/grpc-request';
|
||||
import { getByParentId as getGrpcRequestMetaByParentId } from '../../models/grpc-request-meta';
|
||||
import * as requestOperations from '../../models/helpers/request-operations';
|
||||
import { getByParentId as getRequestMetaByParentId } from '../../models/request-meta';
|
||||
import * as plugins from '../../plugins';
|
||||
import { useGlobalKeyboardShortcuts } from '../components/keydown-binder';
|
||||
import { showModal } from '../components/modals';
|
||||
import { AskModal } from '../components/modals/ask-modal';
|
||||
import { CookiesModal } from '../components/modals/cookies-modal';
|
||||
import { GenerateCodeModal } from '../components/modals/generate-code-modal';
|
||||
import { PromptModal } from '../components/modals/prompt-modal';
|
||||
import { RequestSettingsModal } from '../components/modals/request-settings-modal';
|
||||
import { RequestSwitcherModal } from '../components/modals/request-switcher-modal';
|
||||
import { SettingsModal, TAB_INDEX_SHORTCUTS } from '../components/modals/settings-modal';
|
||||
import { WorkspaceEnvironmentsEditModal } from '../components/modals/workspace-environments-edit-modal';
|
||||
import { WorkspaceSettingsModal } from '../components/modals/workspace-settings-modal';
|
||||
import { updateRequestMetaByParentId } from '../hooks/create-request';
|
||||
import { createRequestGroup } from '../hooks/create-request-group';
|
||||
import { useGlobalKeyboardShortcuts } from '../hooks/use-global-keyboard-shortcuts';
|
||||
import { useSettingsSideEffects } from '../hooks/use-settings-side-effects';
|
||||
import { useSyncMigration } from '../hooks/use-sync-migration';
|
||||
import { selectActiveRequest, selectActiveWorkspace, selectActiveWorkspaceMeta, selectSettings } from '../redux/selectors';
|
||||
|
||||
export const AppHooks: FC = () => {
|
||||
useSyncMigration();
|
||||
useSettingsSideEffects();
|
||||
const activeRequest = useSelector(selectActiveRequest);
|
||||
const activeWorkspace = useSelector(selectActiveWorkspace);
|
||||
const activeWorkspaceMeta = useSelector(selectActiveWorkspaceMeta);
|
||||
const settings = useSelector(selectSettings);
|
||||
|
||||
useGlobalKeyboardShortcuts({
|
||||
preferences_showGeneral:
|
||||
() => showModal(SettingsModal),
|
||||
preferences_showKeyboardShortcuts:
|
||||
() => showModal(SettingsModal, TAB_INDEX_SHORTCUTS),
|
||||
request_showRecent:
|
||||
() => showModal(RequestSwitcherModal, {
|
||||
disableInput: true,
|
||||
maxRequests: 10,
|
||||
maxWorkspaces: 0,
|
||||
selectOnKeyup: true,
|
||||
title: 'Recent Requests',
|
||||
hideNeverActiveRequests: true,
|
||||
// Add an open delay so the dialog won't show for quick presses
|
||||
openDelay: 150,
|
||||
}),
|
||||
workspace_showSettings:
|
||||
() => showModal(WorkspaceSettingsModal, activeWorkspace),
|
||||
request_showSettings:
|
||||
() => {
|
||||
if (activeRequest) {
|
||||
showModal(RequestSettingsModal, { request: activeRequest });
|
||||
}
|
||||
},
|
||||
request_quickSwitch:
|
||||
() => showModal(RequestSwitcherModal),
|
||||
environment_showEditor:
|
||||
() => showModal(WorkspaceEnvironmentsEditModal, activeWorkspace),
|
||||
showCookiesEditor:
|
||||
() => showModal(CookiesModal),
|
||||
request_createHTTP:
|
||||
async () => {
|
||||
if (activeWorkspace) {
|
||||
const parentId = activeRequest ? activeRequest.parentId : activeWorkspace._id;
|
||||
const request = await models.request.create({
|
||||
parentId,
|
||||
name: 'New Request',
|
||||
});
|
||||
if (activeWorkspaceMeta) {
|
||||
await models.workspaceMeta.update(activeWorkspaceMeta, { activeRequestId: request._id });
|
||||
}
|
||||
await updateRequestMetaByParentId(request._id, {
|
||||
lastActive: Date.now(),
|
||||
});
|
||||
models.stats.incrementCreatedRequests();
|
||||
trackSegmentEvent(SegmentEvent.requestCreate, { requestType: 'HTTP' });
|
||||
}
|
||||
},
|
||||
request_showDelete:
|
||||
() => {
|
||||
if (activeRequest) {
|
||||
showModal(AskModal, {
|
||||
title: 'Delete Request?',
|
||||
message: `Really delete ${activeRequest.name}?`,
|
||||
onDone: async (confirmed: boolean) => {
|
||||
if (confirmed) {
|
||||
await requestOperations.remove(activeRequest);
|
||||
models.stats.incrementDeletedRequests();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
request_showCreateFolder:
|
||||
() => {
|
||||
if (activeWorkspace) {
|
||||
createRequestGroup(activeRequest ? activeRequest.parentId : activeWorkspace._id);
|
||||
}
|
||||
},
|
||||
request_showGenerateCodeEditor:
|
||||
() => showModal(GenerateCodeModal, activeRequest),
|
||||
request_showDuplicate:
|
||||
() => {
|
||||
if (activeRequest) {
|
||||
showModal(PromptModal, {
|
||||
title: 'Duplicate Request',
|
||||
defaultValue: activeRequest.name,
|
||||
submitName: 'Create',
|
||||
label: 'New Name',
|
||||
selectText: true,
|
||||
onComplete: async (name: string) => {
|
||||
const newRequest = await requestOperations.duplicate(activeRequest, {
|
||||
name,
|
||||
});
|
||||
if (activeWorkspaceMeta) {
|
||||
await models.workspaceMeta.update(activeWorkspaceMeta, { activeRequestId: newRequest._id });
|
||||
}
|
||||
await updateRequestMetaByParentId(newRequest._id, {
|
||||
lastActive: Date.now(),
|
||||
});
|
||||
models.stats.incrementCreatedRequests();
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
request_togglePin:
|
||||
async () => {
|
||||
if (activeRequest) {
|
||||
const meta = isGrpcRequest(activeRequest) ? await getGrpcRequestMetaByParentId(activeRequest._id) : await getRequestMetaByParentId(activeRequest._id);
|
||||
updateRequestMetaByParentId(activeRequest._id, { pinned: !meta?.pinned });
|
||||
}
|
||||
},
|
||||
plugin_reload:
|
||||
() => plugins.reloadPlugins(),
|
||||
environment_showVariableSourceAndValue:
|
||||
() => models.settings.update(settings, { showVariableSourceAndValue: !settings.showVariableSourceAndValue }),
|
||||
sidebar_toggle:
|
||||
() => {
|
||||
if (activeWorkspaceMeta) {
|
||||
models.workspaceMeta.update(activeWorkspaceMeta, { sidebarHidden: !activeWorkspaceMeta.sidebarHidden });
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
useGlobalKeyboardShortcuts();
|
||||
return null;
|
||||
};
|
||||
|
@ -1,27 +1,16 @@
|
||||
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import * as path from 'path';
|
||||
import React, { PureComponent } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Action, bindActionCreators, Dispatch } from 'redux';
|
||||
import path from 'path';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { parse as urlParse } from 'url';
|
||||
|
||||
import {
|
||||
ACTIVITY_HOME,
|
||||
AUTOBIND_CFG,
|
||||
getProductName,
|
||||
isDevelopment,
|
||||
} from '../../common/constants';
|
||||
import { type ChangeBufferEvent, database as db } from '../../common/database';
|
||||
import { database as db } from '../../common/database';
|
||||
import { getDataDirectory } from '../../common/electron-helpers';
|
||||
import {
|
||||
generateId,
|
||||
} from '../../common/misc';
|
||||
import * as models from '../../models';
|
||||
import { isNotDefaultProject } from '../../models/project';
|
||||
import { type RequestGroupMeta } from '../../models/request-group-meta';
|
||||
import { isWorkspace } from '../../models/workspace';
|
||||
import * as plugins from '../../plugins';
|
||||
import { GitRepository } from '../../models/git-repository';
|
||||
import * as themes from '../../plugins/misc';
|
||||
import { fsClient } from '../../sync/git/fs-client';
|
||||
import { GIT_CLONE_DIR, GIT_INSOMNIA_DIR, GIT_INTERNAL_DIR, GitVCS } from '../../sync/git/git-vcs';
|
||||
@ -30,35 +19,26 @@ import { routableFSClient } from '../../sync/git/routable-fs-client';
|
||||
import FileSystemDriver from '../../sync/store/drivers/file-system-driver';
|
||||
import { type MergeConflict } from '../../sync/types';
|
||||
import { VCS } from '../../sync/vcs/vcs';
|
||||
import * as templating from '../../templating/index';
|
||||
import { ErrorBoundary } from '../components/error-boundary';
|
||||
import { AskModal } from '../components/modals/ask-modal';
|
||||
import { showAlert, showModal } from '../components/modals/index';
|
||||
import { showSelectModal } from '../components/modals/select-modal';
|
||||
import { SettingsModal, TAB_INDEX_SHORTCUTS } from '../components/modals/settings-modal';
|
||||
import { AlertModal } from '../components/modals/alert-modal';
|
||||
import { showModal } from '../components/modals/index';
|
||||
import { SyncMergeModal } from '../components/modals/sync-merge-modal';
|
||||
import { Toast } from '../components/toast';
|
||||
import { Wrapper } from '../components/wrapper';
|
||||
import { type WrapperClass, Wrapper } from '../components/wrapper';
|
||||
import withDragDropContext from '../context/app/drag-drop-context';
|
||||
import { GrpcProvider } from '../context/grpc';
|
||||
import { NunjucksEnabledProvider } from '../context/nunjucks/nunjucks-enabled-context';
|
||||
import { updateRequestMetaByParentId } from '../hooks/create-request';
|
||||
import { RootState } from '../redux/modules';
|
||||
import {
|
||||
newCommand,
|
||||
} from '../redux/modules/global';
|
||||
import { importUri } from '../redux/modules/import';
|
||||
import {
|
||||
selectActiveActivity,
|
||||
selectActiveApiSpec,
|
||||
selectActiveCookieJar,
|
||||
selectActiveEnvironment,
|
||||
selectActiveGitRepository,
|
||||
selectActiveProject,
|
||||
selectActiveRequest,
|
||||
selectActiveWorkspace,
|
||||
selectActiveWorkspaceMeta,
|
||||
selectActiveWorkspaceName,
|
||||
selectEnvironments,
|
||||
selectIsFinishedBooting,
|
||||
selectIsLoggedIn,
|
||||
@ -66,231 +46,31 @@ import {
|
||||
} from '../redux/selectors';
|
||||
import { AppHooks } from './app-hooks';
|
||||
|
||||
export type AppProps = ReturnType<typeof mapStateToProps> & ReturnType<typeof mapDispatchToProps>;
|
||||
|
||||
interface State {
|
||||
vcs: VCS | null;
|
||||
gitVCS: GitVCS | null;
|
||||
isMigratingChildren: boolean;
|
||||
}
|
||||
|
||||
@autoBindMethodsForReact(AUTOBIND_CFG)
|
||||
class App extends PureComponent<AppProps, State> {
|
||||
private _updateVCSLock: any;
|
||||
private _responseFilterHistorySaveTimeout: NodeJS.Timeout | null = null;
|
||||
|
||||
constructor(props: AppProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
vcs: null,
|
||||
gitVCS: null,
|
||||
isMigratingChildren: false,
|
||||
};
|
||||
|
||||
this._updateVCSLock = null;
|
||||
}
|
||||
|
||||
static async _updateRequestGroupMetaByParentId(requestGroupId: string, patch: Partial<RequestGroupMeta>) {
|
||||
const requestGroupMeta = await models.requestGroupMeta.getByParentId(requestGroupId);
|
||||
|
||||
if (requestGroupMeta) {
|
||||
await models.requestGroupMeta.update(requestGroupMeta, patch);
|
||||
} else {
|
||||
const newPatch = Object.assign(
|
||||
{
|
||||
parentId: requestGroupId,
|
||||
},
|
||||
patch,
|
||||
);
|
||||
await models.requestGroupMeta.create(newPatch);
|
||||
}
|
||||
}
|
||||
|
||||
async _handleSetResponseFilter(requestId: string, responseFilter: string) {
|
||||
await updateRequestMetaByParentId(requestId, {
|
||||
responseFilter,
|
||||
});
|
||||
|
||||
if (this._responseFilterHistorySaveTimeout !== null) {
|
||||
clearTimeout(this._responseFilterHistorySaveTimeout);
|
||||
}
|
||||
this._responseFilterHistorySaveTimeout = setTimeout(async () => {
|
||||
const meta = await models.requestMeta.getByParentId(requestId);
|
||||
// @ts-expect-error -- TSCONVERSION meta can be null
|
||||
const responseFilterHistory = meta.responseFilterHistory.slice(0, 10);
|
||||
|
||||
// Already in history?
|
||||
if (responseFilterHistory.includes(responseFilter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Blank?
|
||||
if (!responseFilter) {
|
||||
return;
|
||||
}
|
||||
|
||||
responseFilterHistory.unshift(responseFilter);
|
||||
await updateRequestMetaByParentId(requestId, {
|
||||
responseFilterHistory,
|
||||
});
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
static _handleShowSettingsModal(tabIndex?: number) {
|
||||
showModal(SettingsModal, tabIndex);
|
||||
}
|
||||
|
||||
async _handleReloadPlugins() {
|
||||
const { settings } = this.props;
|
||||
await plugins.reloadPlugins();
|
||||
await themes.applyColorScheme(settings);
|
||||
templating.reload();
|
||||
console.log('[plugins] reloaded');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update document.title to be "Project - Workspace (Environment) – Request" when not home
|
||||
* @private
|
||||
*/
|
||||
_updateDocumentTitle() {
|
||||
const {
|
||||
activeWorkspace,
|
||||
activeProject,
|
||||
activeWorkspaceName,
|
||||
activeEnvironment,
|
||||
activeRequest,
|
||||
activeActivity,
|
||||
} = this.props;
|
||||
let title;
|
||||
|
||||
if (activeActivity === ACTIVITY_HOME) {
|
||||
title = getProductName();
|
||||
} else if (activeWorkspace && activeWorkspaceName) {
|
||||
title = activeProject.name;
|
||||
title += ` - ${activeWorkspaceName}`;
|
||||
|
||||
if (activeEnvironment) {
|
||||
title += ` (${activeEnvironment.name})`;
|
||||
}
|
||||
|
||||
if (activeRequest) {
|
||||
title += ` – ${activeRequest.name}`;
|
||||
}
|
||||
}
|
||||
|
||||
document.title = title || getProductName();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: AppProps) {
|
||||
this._updateDocumentTitle();
|
||||
|
||||
this._ensureWorkspaceChildren();
|
||||
|
||||
// Check on VCS things
|
||||
const { activeWorkspace, activeProject, activeGitRepository } = this.props;
|
||||
const changingWorkspace = prevProps.activeWorkspace?._id !== activeWorkspace?._id;
|
||||
|
||||
// Update VCS if needed
|
||||
if (changingWorkspace) {
|
||||
this._updateVCS();
|
||||
}
|
||||
|
||||
// Update Git VCS if needed
|
||||
const changingProject = prevProps.activeProject?._id !== activeProject?._id;
|
||||
const changingGit = prevProps.activeGitRepository?._id !== activeGitRepository?._id;
|
||||
|
||||
if (changingWorkspace || changingProject || changingGit) {
|
||||
this._updateGitVCS();
|
||||
}
|
||||
}
|
||||
|
||||
async _updateGitVCS() {
|
||||
const { activeGitRepository, activeWorkspace, activeProject } = this.props;
|
||||
|
||||
// Get the vcs and set it to null in the state while we update it
|
||||
let gitVCS = this.state.gitVCS;
|
||||
this.setState({
|
||||
gitVCS: null,
|
||||
});
|
||||
|
||||
if (!gitVCS) {
|
||||
gitVCS = new GitVCS();
|
||||
}
|
||||
|
||||
if (activeWorkspace && activeGitRepository) {
|
||||
// Create FS client
|
||||
const baseDir = path.join(
|
||||
getDataDirectory(),
|
||||
`version-control/git/${activeGitRepository._id}`,
|
||||
);
|
||||
|
||||
/** All app data is stored within a namespaced GIT_INSOMNIA_DIR directory at the root of the repository and is read/written from the local NeDB database */
|
||||
const neDbClient = NeDBClient.createClient(activeWorkspace._id, activeProject._id);
|
||||
|
||||
/** All git metadata in the GIT_INTERNAL_DIR directory is stored in a git/ directory on the filesystem */
|
||||
const gitDataClient = fsClient(baseDir);
|
||||
|
||||
/** All data outside the directories listed below will be stored in an 'other' directory. This is so we can support files that exist outside the ones the app is specifically in charge of. */
|
||||
const otherDatClient = fsClient(path.join(baseDir, 'other'));
|
||||
|
||||
/** The routable FS client directs isomorphic-git to read/write from the database or from the correct directory on the file system while performing git operations. */
|
||||
const routableFS = routableFSClient(otherDatClient, {
|
||||
[GIT_INSOMNIA_DIR]: neDbClient,
|
||||
[GIT_INTERNAL_DIR]: gitDataClient,
|
||||
});
|
||||
// Init VCS
|
||||
const { credentials, uri } = activeGitRepository;
|
||||
|
||||
if (activeGitRepository.needsFullClone) {
|
||||
await models.gitRepository.update(activeGitRepository, {
|
||||
needsFullClone: false,
|
||||
});
|
||||
await gitVCS.initFromClone({
|
||||
url: uri,
|
||||
gitCredentials: credentials,
|
||||
directory: GIT_CLONE_DIR,
|
||||
fs: routableFS,
|
||||
gitDirectory: GIT_INTERNAL_DIR,
|
||||
});
|
||||
} else {
|
||||
await gitVCS.init({
|
||||
directory: GIT_CLONE_DIR,
|
||||
fs: routableFS,
|
||||
gitDirectory: GIT_INTERNAL_DIR,
|
||||
});
|
||||
}
|
||||
|
||||
// Configure basic info
|
||||
const { author, uri: gitUri } = activeGitRepository;
|
||||
await gitVCS.setAuthor(author.name, author.email);
|
||||
await gitVCS.addRemote(gitUri);
|
||||
} else {
|
||||
// Create new one to un-initialize it
|
||||
gitVCS = new GitVCS();
|
||||
}
|
||||
|
||||
this.setState({
|
||||
gitVCS,
|
||||
});
|
||||
}
|
||||
|
||||
async _updateVCS() {
|
||||
const { activeWorkspace } = this.props;
|
||||
function useVCS({
|
||||
workspaceId,
|
||||
}: {
|
||||
workspaceId?: string;
|
||||
}) {
|
||||
const vcsInstanceRef = useRef<VCS | null>(null);
|
||||
const [vcs, setVCS] = useState<VCS | null>(null);
|
||||
const updateVCSLock = useRef<boolean | string>(false);
|
||||
|
||||
// Update VCS when the active workspace changes
|
||||
useEffect(() => {
|
||||
const lock = generateId();
|
||||
this._updateVCSLock = lock;
|
||||
updateVCSLock.current = lock;
|
||||
|
||||
// Get the vcs and set it to null in the state while we update it
|
||||
let vcs = this.state.vcs;
|
||||
this.setState({
|
||||
vcs: null,
|
||||
});
|
||||
// Set vcs to null while we update it
|
||||
setVCS(null);
|
||||
|
||||
if (!vcs) {
|
||||
if (!vcsInstanceRef.current) {
|
||||
const driver = FileSystemDriver.create(getDataDirectory());
|
||||
|
||||
vcs = new VCS(driver, async conflicts => {
|
||||
vcsInstanceRef.current = new VCS(driver, async conflicts => {
|
||||
return new Promise(resolve => {
|
||||
showModal(SyncMergeModal, {
|
||||
conflicts,
|
||||
@ -300,170 +80,142 @@ class App extends PureComponent<AppProps, State> {
|
||||
});
|
||||
}
|
||||
|
||||
if (activeWorkspace) {
|
||||
await vcs.switchProject(activeWorkspace._id);
|
||||
if (workspaceId) {
|
||||
vcsInstanceRef.current.switchProject(workspaceId);
|
||||
} else {
|
||||
vcs.clearBackendProject();
|
||||
vcsInstanceRef.current.clearBackendProject();
|
||||
}
|
||||
|
||||
// Prevent a potential race-condition when _updateVCS() gets called for different workspaces in rapid succession
|
||||
if (this._updateVCSLock === lock) {
|
||||
this.setState({
|
||||
vcs,
|
||||
});
|
||||
if (updateVCSLock.current === lock) {
|
||||
setVCS(vcsInstanceRef.current);
|
||||
}
|
||||
}
|
||||
}, [workspaceId]);
|
||||
|
||||
async listenforWorkspaceDelete(changes: ChangeBufferEvent[]) {
|
||||
for (const change of changes) {
|
||||
const [type, doc] = change;
|
||||
const { vcs } = this.state;
|
||||
return vcs;
|
||||
}
|
||||
|
||||
// Delete VCS project if workspace deleted
|
||||
if (vcs && isWorkspace(doc) && type === db.CHANGE_REMOVE) {
|
||||
await vcs.removeBackendProjectsForRoot(doc._id);
|
||||
function useGitVCS({
|
||||
workspaceId,
|
||||
projectId,
|
||||
gitRepository,
|
||||
}: {
|
||||
workspaceId?: string;
|
||||
projectId: string;
|
||||
gitRepository?: GitRepository | null;
|
||||
}) {
|
||||
const gitVCSInstanceRef = useRef<GitVCS | null>(null);
|
||||
const [gitVCS, setGitVCS] = useState<GitVCS | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
|
||||
// Set the instance to null in the state while we update it
|
||||
if (gitVCSInstanceRef.current) {
|
||||
setGitVCS(null);
|
||||
}
|
||||
|
||||
if (!gitVCSInstanceRef.current) {
|
||||
gitVCSInstanceRef.current = new GitVCS();
|
||||
}
|
||||
|
||||
async function update() {
|
||||
if (workspaceId && gitRepository && gitVCSInstanceRef.current) {
|
||||
// Create FS client
|
||||
const baseDir = path.join(
|
||||
getDataDirectory(),
|
||||
`version-control/git/${gitRepository._id}`,
|
||||
);
|
||||
|
||||
/** All app data is stored within a namespaced GIT_INSOMNIA_DIR directory at the root of the repository and is read/written from the local NeDB database */
|
||||
const neDbClient = NeDBClient.createClient(workspaceId, projectId);
|
||||
|
||||
/** All git metadata in the GIT_INTERNAL_DIR directory is stored in a git/ directory on the filesystem */
|
||||
const gitDataClient = fsClient(baseDir);
|
||||
|
||||
/** All data outside the directories listed below will be stored in an 'other' directory. This is so we can support files that exist outside the ones the app is specifically in charge of. */
|
||||
const otherDatClient = fsClient(path.join(baseDir, 'other'));
|
||||
|
||||
/** The routable FS client directs isomorphic-git to read/write from the database or from the correct directory on the file system while performing git operations. */
|
||||
const routableFS = routableFSClient(otherDatClient, {
|
||||
[GIT_INSOMNIA_DIR]: neDbClient,
|
||||
[GIT_INTERNAL_DIR]: gitDataClient,
|
||||
});
|
||||
// Init VCS
|
||||
const { credentials, uri } = gitRepository;
|
||||
if (gitRepository.needsFullClone) {
|
||||
await models.gitRepository.update(gitRepository, {
|
||||
needsFullClone: false,
|
||||
});
|
||||
await gitVCSInstanceRef.current.initFromClone({
|
||||
url: uri,
|
||||
gitCredentials: credentials,
|
||||
directory: GIT_CLONE_DIR,
|
||||
fs: routableFS,
|
||||
gitDirectory: GIT_INTERNAL_DIR,
|
||||
});
|
||||
} else {
|
||||
await gitVCSInstanceRef.current.init({
|
||||
directory: GIT_CLONE_DIR,
|
||||
fs: routableFS,
|
||||
gitDirectory: GIT_INTERNAL_DIR,
|
||||
});
|
||||
}
|
||||
|
||||
// Configure basic info
|
||||
const { author, uri: gitUri } = gitRepository;
|
||||
await gitVCSInstanceRef.current.setAuthor(author.name, author.email);
|
||||
await gitVCSInstanceRef.current.addRemote(gitUri);
|
||||
} else {
|
||||
// Create new one to un-initialize it
|
||||
gitVCSInstanceRef.current = new GitVCS();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
// Update title
|
||||
this._updateDocumentTitle();
|
||||
|
||||
// Update VCS
|
||||
await this._updateVCS();
|
||||
await this._updateGitVCS();
|
||||
db.onChange(this.listenforWorkspaceDelete);
|
||||
ipcRenderer.on('toggle-preferences', () => {
|
||||
App._handleShowSettingsModal();
|
||||
});
|
||||
|
||||
if (isDevelopment()) {
|
||||
ipcRenderer.on('clear-model', () => {
|
||||
const options = models
|
||||
.types()
|
||||
.filter(t => t !== models.settings.type) // don't clear settings
|
||||
.map(t => ({ name: t, value: t }));
|
||||
|
||||
showSelectModal({
|
||||
title: 'Clear a model',
|
||||
message: 'Select a model to clear; this operation cannot be undone.',
|
||||
value: options[0].value,
|
||||
options,
|
||||
onDone: async type => {
|
||||
if (type) {
|
||||
const bufferId = await db.bufferChanges();
|
||||
console.log(`[developer] clearing all "${type}" entities`);
|
||||
const allEntities = await db.all(type);
|
||||
const filteredEntites = allEntities
|
||||
.filter(isNotDefaultProject); // don't clear the default project
|
||||
await db.batchModifyDocs({ remove: filteredEntites });
|
||||
db.flushChanges(bufferId);
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
ipcRenderer.on('clear-all-models', () => {
|
||||
showModal(AskModal, {
|
||||
title: 'Clear all models',
|
||||
message: 'Are you sure you want to clear all models? This operation cannot be undone.',
|
||||
yesText: 'Yes',
|
||||
noText: 'No',
|
||||
onDone: async (yes: boolean) => {
|
||||
if (yes) {
|
||||
const bufferId = await db.bufferChanges();
|
||||
const promises = models
|
||||
.types()
|
||||
.filter(t => t !== models.settings.type) // don't clear settings
|
||||
.reverse().map(async type => {
|
||||
console.log(`[developer] clearing all "${type}" entities`);
|
||||
const allEntities = await db.all(type);
|
||||
const filteredEntites = allEntities
|
||||
.filter(isNotDefaultProject); // don't clear the default project
|
||||
await db.batchModifyDocs({ remove: filteredEntites });
|
||||
});
|
||||
await Promise.all(promises);
|
||||
db.flushChanges(bufferId);
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
isMounted && setGitVCS(gitVCSInstanceRef.current);
|
||||
}
|
||||
|
||||
ipcRenderer.on('reload-plugins', this._handleReloadPlugins);
|
||||
ipcRenderer.on('toggle-preferences-shortcuts', () => {
|
||||
App._handleShowSettingsModal(TAB_INDEX_SHORTCUTS);
|
||||
});
|
||||
ipcRenderer.on('run-command', (_, commandUri) => {
|
||||
const parsed = urlParse(commandUri, true);
|
||||
const command = `${parsed.hostname}${parsed.pathname}`;
|
||||
const args = JSON.parse(JSON.stringify(parsed.query));
|
||||
args.workspaceId = args.workspaceId || this.props.activeWorkspace?._id;
|
||||
this.props.handleCommand(command, args);
|
||||
});
|
||||
// NOTE: This is required for "drop" event to trigger.
|
||||
document.addEventListener(
|
||||
'dragover',
|
||||
event => {
|
||||
event.preventDefault();
|
||||
},
|
||||
false,
|
||||
);
|
||||
document.addEventListener(
|
||||
'drop',
|
||||
async event => {
|
||||
event.preventDefault();
|
||||
const { activeWorkspace, handleImportUri } = this.props;
|
||||
update();
|
||||
|
||||
if (!activeWorkspace) {
|
||||
return;
|
||||
}
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, [workspaceId, projectId, gitRepository]);
|
||||
|
||||
// @ts-expect-error -- TSCONVERSION
|
||||
if (event.dataTransfer.files.length === 0) {
|
||||
console.log('[drag] Ignored drop event because no files present');
|
||||
return;
|
||||
}
|
||||
return gitVCS;
|
||||
}
|
||||
|
||||
// @ts-expect-error -- TSCONVERSION
|
||||
const file = event.dataTransfer.files[0];
|
||||
const { path } = file;
|
||||
const uri = `file://${path}`;
|
||||
await showAlert({
|
||||
title: 'Confirm Data Import',
|
||||
message: (
|
||||
<span>
|
||||
Import <code>{path}</code>?
|
||||
</span>
|
||||
),
|
||||
addCancel: true,
|
||||
});
|
||||
handleImportUri(uri, { workspaceId: activeWorkspace?._id });
|
||||
},
|
||||
false,
|
||||
);
|
||||
const App = () => {
|
||||
const [state, setState] = useState<State>({
|
||||
isMigratingChildren: false,
|
||||
});
|
||||
|
||||
// Give it a bit before letting the backend know it's ready
|
||||
setTimeout(() => ipcRenderer.send('window-ready'), 500);
|
||||
window
|
||||
.matchMedia('(prefers-color-scheme: dark)')
|
||||
.addListener(async () => themes.applyColorScheme(this.props.settings));
|
||||
}
|
||||
const activeProject = useSelector(selectActiveProject);
|
||||
const activeCookieJar = useSelector(selectActiveCookieJar);
|
||||
const activeApiSpec = useSelector(selectActiveApiSpec);
|
||||
const activeWorkspace = useSelector(selectActiveWorkspace);
|
||||
const activeGitRepository = useSelector(selectActiveGitRepository);
|
||||
const activeWorkspaceMeta = useSelector(selectActiveWorkspaceMeta);
|
||||
const environments = useSelector(selectEnvironments);
|
||||
const isLoggedIn = useSelector(selectIsLoggedIn);
|
||||
const isFinishedBooting = useSelector(selectIsFinishedBooting);
|
||||
const settings = useSelector(selectSettings);
|
||||
const dispatch = useDispatch();
|
||||
const handleCommand = dispatch(newCommand);
|
||||
const handleImportUri = dispatch(importUri);
|
||||
const vcs = useVCS({
|
||||
workspaceId: activeWorkspace?._id,
|
||||
});
|
||||
|
||||
componentWillUnmount() {
|
||||
db.offChange(this.listenforWorkspaceDelete);
|
||||
}
|
||||
const gitVCS = useGitVCS({
|
||||
workspaceId: activeWorkspace?._id,
|
||||
projectId: activeProject?._id,
|
||||
gitRepository: activeGitRepository,
|
||||
});
|
||||
|
||||
async _ensureWorkspaceChildren() {
|
||||
const {
|
||||
activeWorkspace,
|
||||
activeWorkspaceMeta,
|
||||
activeCookieJar,
|
||||
environments,
|
||||
activeApiSpec,
|
||||
} = this.props;
|
||||
const wrapperRef = useRef<WrapperClass | null>(null);
|
||||
|
||||
// Ensure Children: Make sure cookies, env, and meta models are created under this workspace
|
||||
useEffect(() => {
|
||||
if (!activeWorkspace) {
|
||||
return;
|
||||
}
|
||||
@ -476,106 +228,120 @@ class App extends PureComponent<AppProps, State> {
|
||||
}
|
||||
|
||||
// We already started migrating. Let it finish.
|
||||
if (this.state.isMigratingChildren) {
|
||||
if (state.isMigratingChildren) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent rendering of everything
|
||||
this.setState(
|
||||
{
|
||||
isMigratingChildren: true,
|
||||
},
|
||||
async () => {
|
||||
// Prevent rendering of everything until we check the workspace has cookies, env, and meta
|
||||
setState(state => ({ ...state, isMigratingChildren: true }));
|
||||
async function update() {
|
||||
if (activeWorkspace) {
|
||||
const flushId = await db.bufferChanges();
|
||||
await models.workspace.ensureChildren(activeWorkspace);
|
||||
await db.flushChanges(flushId);
|
||||
this.setState({
|
||||
isMigratingChildren: false,
|
||||
});
|
||||
setState(state => ({ ...state, isMigratingChildren: false }));
|
||||
}
|
||||
}
|
||||
update();
|
||||
}, [activeApiSpec, activeCookieJar, activeWorkspace, activeWorkspaceMeta, environments, state.isMigratingChildren]);
|
||||
|
||||
// Give it a bit before letting the backend know it's ready
|
||||
useEffect(() => {
|
||||
setTimeout(() => ipcRenderer.send('window-ready'), 500);
|
||||
}, []);
|
||||
|
||||
// Handle Application Commands
|
||||
useEffect(() => {
|
||||
ipcRenderer.on('run-command', (_, commandUri) => {
|
||||
const parsed = urlParse(commandUri, true);
|
||||
const command = `${parsed.hostname}${parsed.pathname}`;
|
||||
const args = JSON.parse(JSON.stringify(parsed.query));
|
||||
args.workspaceId = args.workspaceId || activeWorkspace?._id;
|
||||
handleCommand(command, args);
|
||||
});
|
||||
}, [activeWorkspace?._id, handleCommand]);
|
||||
|
||||
// Handle System Theme change
|
||||
useEffect(() => {
|
||||
const matches = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
matches.addEventListener('change', () => themes.applyColorScheme(settings));
|
||||
return () => {
|
||||
matches.removeEventListener('change', () => themes.applyColorScheme(settings));
|
||||
};
|
||||
});
|
||||
|
||||
// Global Drag and Drop for importing files
|
||||
useEffect(() => {
|
||||
// NOTE: This is required for "drop" event to trigger.
|
||||
document.addEventListener(
|
||||
'dragover',
|
||||
event => {
|
||||
event.preventDefault();
|
||||
},
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
this._ensureWorkspaceChildren();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
UNSAFE_componentWillMount() {
|
||||
this._ensureWorkspaceChildren();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.isMigratingChildren) {
|
||||
console.log('[app] Waiting for migration to complete');
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!this.props.isFinishedBooting) {
|
||||
console.log('[app] Waiting to finish booting');
|
||||
return null;
|
||||
}
|
||||
|
||||
const { activeWorkspace, isLoggedIn } = this.props;
|
||||
const {
|
||||
gitVCS,
|
||||
vcs,
|
||||
} = this.state;
|
||||
const uniquenessKey = `${isLoggedIn}::${activeWorkspace?._id || 'n/a'}`;
|
||||
return (
|
||||
<GrpcProvider>
|
||||
<NunjucksEnabledProvider>
|
||||
<AppHooks />
|
||||
|
||||
<div className="app" key={uniquenessKey}>
|
||||
<ErrorBoundary showAlert>
|
||||
<Wrapper
|
||||
handleSetResponseFilter={this._handleSetResponseFilter}
|
||||
vcs={vcs}
|
||||
gitVCS={gitVCS}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
|
||||
<ErrorBoundary showAlert>
|
||||
<Toast />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
</NunjucksEnabledProvider>
|
||||
</GrpcProvider>
|
||||
document.addEventListener(
|
||||
'drop',
|
||||
async event => {
|
||||
event.preventDefault();
|
||||
if (!activeWorkspace) {
|
||||
return;
|
||||
}
|
||||
const files = event.dataTransfer?.files || [];
|
||||
if (files.length === 0) {
|
||||
console.log('[drag] Ignored drop event because no files present');
|
||||
return;
|
||||
}
|
||||
const file = files[0];
|
||||
if (!file?.path) {
|
||||
return;
|
||||
}
|
||||
await showModal(AlertModal, {
|
||||
title: 'Confirm Data Import',
|
||||
message: (
|
||||
<span>
|
||||
Import <code>{file.path}</code>?
|
||||
</span>
|
||||
),
|
||||
addCancel: true,
|
||||
});
|
||||
handleImportUri(`file://${file.path}`, { workspaceId: activeWorkspace?._id });
|
||||
},
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
if (state.isMigratingChildren) {
|
||||
console.log('[app] Waiting for migration to complete');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: RootState) => ({
|
||||
activeActivity: selectActiveActivity(state),
|
||||
activeProject: selectActiveProject(state),
|
||||
activeApiSpec: selectActiveApiSpec(state),
|
||||
activeWorkspaceName: selectActiveWorkspaceName(state),
|
||||
activeCookieJar: selectActiveCookieJar(state),
|
||||
activeEnvironment: selectActiveEnvironment(state),
|
||||
activeGitRepository: selectActiveGitRepository(state),
|
||||
activeRequest: selectActiveRequest(state),
|
||||
activeWorkspace: selectActiveWorkspace(state),
|
||||
activeWorkspaceMeta: selectActiveWorkspaceMeta(state),
|
||||
environments: selectEnvironments(state),
|
||||
isLoggedIn: selectIsLoggedIn(state),
|
||||
isFinishedBooting: selectIsFinishedBooting(state),
|
||||
settings: selectSettings(state),
|
||||
});
|
||||
if (!isFinishedBooting) {
|
||||
console.log('[app] Waiting to finish booting');
|
||||
return null;
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch: Dispatch<Action<any>>) => {
|
||||
const {
|
||||
importUri: handleImportUri,
|
||||
newCommand: handleCommand,
|
||||
} = bindActionCreators({
|
||||
importUri,
|
||||
newCommand,
|
||||
}, dispatch);
|
||||
return {
|
||||
handleCommand,
|
||||
handleImportUri,
|
||||
};
|
||||
const uniquenessKey = `${isLoggedIn}::${activeWorkspace?._id || 'n/a'}`;
|
||||
return (
|
||||
<GrpcProvider>
|
||||
<NunjucksEnabledProvider>
|
||||
<AppHooks />
|
||||
<div className="app" key={uniquenessKey}>
|
||||
<ErrorBoundary showAlert>
|
||||
<Wrapper
|
||||
ref={wrapperRef}
|
||||
vcs={vcs}
|
||||
gitVCS={gitVCS}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
|
||||
<ErrorBoundary showAlert>
|
||||
<Toast />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
</NunjucksEnabledProvider>
|
||||
</GrpcProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(withDragDropContext(App));
|
||||
export default withDragDropContext(App);
|
||||
|
33
packages/insomnia/src/ui/hooks/use-document-title.ts
Normal file
33
packages/insomnia/src/ui/hooks/use-document-title.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { ACTIVITY_HOME, getProductName } from '../../common/constants';
|
||||
import { selectActiveActivity, selectActiveEnvironment, selectActiveProject, selectActiveRequest, selectActiveWorkspace, selectActiveWorkspaceName } from '../redux/selectors';
|
||||
|
||||
export const useDocumentTitle = () => {
|
||||
const activeActivity = useSelector(selectActiveActivity);
|
||||
const activeProject = useSelector(selectActiveProject);
|
||||
const activeWorkspaceName = useSelector(selectActiveWorkspaceName);
|
||||
const activeWorkspace = useSelector(selectActiveWorkspace);
|
||||
|
||||
const activeEnvironment = useSelector(selectActiveEnvironment);
|
||||
const activeRequest = useSelector(selectActiveRequest);
|
||||
// Update document title
|
||||
useEffect(() => {
|
||||
let title;
|
||||
if (activeActivity === ACTIVITY_HOME) {
|
||||
title = getProductName();
|
||||
} else if (activeWorkspace && activeWorkspaceName) {
|
||||
title = activeProject.name;
|
||||
title += ` - ${activeWorkspaceName}`;
|
||||
if (activeEnvironment) {
|
||||
title += ` (${activeEnvironment.name})`;
|
||||
}
|
||||
if (activeRequest) {
|
||||
title += ` – ${activeRequest.name}`;
|
||||
}
|
||||
}
|
||||
document.title = title || getProductName();
|
||||
}, [activeActivity, activeEnvironment, activeProject.name, activeRequest, activeWorkspace, activeWorkspaceName]);
|
||||
|
||||
};
|
@ -0,0 +1,34 @@
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import * as models from '../../models';
|
||||
import * as plugins from '../../plugins';
|
||||
import { useDocBodyKeyboardShortcuts } from '../components/keydown-binder';
|
||||
import { showModal } from '../components/modals';
|
||||
import { SettingsModal, TAB_INDEX_SHORTCUTS } from '../components/modals/settings-modal';
|
||||
import { WorkspaceSettingsModal } from '../components/modals/workspace-settings-modal';
|
||||
import { selectActiveWorkspace, selectActiveWorkspaceMeta, selectSettings } from '../redux/selectors';
|
||||
|
||||
export const useGlobalKeyboardShortcuts = () => {
|
||||
const activeWorkspace = useSelector(selectActiveWorkspace);
|
||||
const activeWorkspaceMeta = useSelector(selectActiveWorkspaceMeta);
|
||||
const settings = useSelector(selectSettings);
|
||||
|
||||
useDocBodyKeyboardShortcuts({
|
||||
workspace_showSettings:
|
||||
() => showModal(WorkspaceSettingsModal, activeWorkspace),
|
||||
plugin_reload:
|
||||
() => plugins.reloadPlugins(),
|
||||
environment_showVariableSourceAndValue:
|
||||
() => models.settings.update(settings, { showVariableSourceAndValue: !settings.showVariableSourceAndValue }),
|
||||
preferences_showGeneral:
|
||||
() => showModal(SettingsModal),
|
||||
preferences_showKeyboardShortcuts:
|
||||
() => showModal(SettingsModal, TAB_INDEX_SHORTCUTS),
|
||||
sidebar_toggle:
|
||||
() => {
|
||||
if (activeWorkspaceMeta) {
|
||||
models.workspaceMeta.update(activeWorkspaceMeta, { sidebarHidden: !activeWorkspaceMeta.sidebarHidden });
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
@ -1,11 +1,10 @@
|
||||
// eslint-disable-next-line simple-import-sort/imports
|
||||
import { ipcRenderer } from 'electron';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import './rendererListeners';
|
||||
import { getProductName, isDevelopment } from '../common/constants';
|
||||
import { database as db } from '../common/database';
|
||||
import { database } from '../common/database';
|
||||
import { initializeLogging } from '../common/log';
|
||||
import * as models from '../models';
|
||||
import { initNewOAuthSession } from '../network/o-auth-2/misc';
|
||||
@ -25,7 +24,7 @@ document.body.setAttribute('data-platform', process.platform);
|
||||
document.title = getProductName();
|
||||
|
||||
(async function() {
|
||||
await db.initClient();
|
||||
await database.initClient();
|
||||
|
||||
await initPlugins();
|
||||
|
||||
@ -59,22 +58,5 @@ if (isDevelopment()) {
|
||||
// @ts-expect-error -- TSCONVERSION needs window augmentation
|
||||
window.models = models;
|
||||
// @ts-expect-error -- TSCONVERSION needs window augmentation
|
||||
window.db = db;
|
||||
window.db = database;
|
||||
}
|
||||
|
||||
function showUpdateNotification() {
|
||||
console.log('[app] Update Available');
|
||||
// eslint-disable-next-line no-new
|
||||
new window.Notification('Insomnia Update Ready', {
|
||||
body: 'Relaunch the app for it to take effect',
|
||||
silent: true,
|
||||
// @ts-expect-error -- TSCONVERSION
|
||||
sticky: true,
|
||||
});
|
||||
}
|
||||
|
||||
ipcRenderer.on('update-available', () => {
|
||||
// Give it a few seconds before showing this. Sometimes, when
|
||||
// you relaunch too soon it doesn't work the first time.
|
||||
setTimeout(showUpdateNotification, 1000 * 10);
|
||||
});
|
||||
|
98
packages/insomnia/src/ui/rendererListeners.ts
Normal file
98
packages/insomnia/src/ui/rendererListeners.ts
Normal file
@ -0,0 +1,98 @@
|
||||
|
||||
import { ipcRenderer } from 'electron';
|
||||
|
||||
import { isDevelopment } from '../common/constants';
|
||||
import { database } from '../common/database';
|
||||
import * as models from '../models';
|
||||
import { isNotDefaultProject } from '../models/project';
|
||||
import * as plugins from '../plugins';
|
||||
import * as themes from '../plugins/misc';
|
||||
import * as templating from '../templating';
|
||||
import { showModal } from './components/modals';
|
||||
import { AskModal } from './components/modals/ask-modal';
|
||||
import { SelectModal } from './components/modals/select-modal';
|
||||
import { SettingsModal, TAB_INDEX_SHORTCUTS } from './components/modals/settings-modal';
|
||||
|
||||
ipcRenderer.on('update-available', () => {
|
||||
// Give it a few seconds before showing this. Sometimes, when
|
||||
// you relaunch too soon it doesn't work the first time.
|
||||
setTimeout(() => {
|
||||
console.log('[app] Update Available');
|
||||
// eslint-disable-next-line no-new
|
||||
new window.Notification('Insomnia Update Ready', {
|
||||
body: 'Relaunch the app for it to take effect',
|
||||
silent: true,
|
||||
// @ts-expect-error -- TSCONVERSION
|
||||
sticky: true,
|
||||
});
|
||||
}, 1000 * 10);
|
||||
});
|
||||
|
||||
ipcRenderer.on('toggle-preferences', () => {
|
||||
showModal(SettingsModal);
|
||||
});
|
||||
|
||||
if (isDevelopment()) {
|
||||
ipcRenderer.on('clear-model', () => {
|
||||
const options = models
|
||||
.types()
|
||||
.filter(t => t !== models.settings.type) // don't clear settings
|
||||
.map(t => ({ name: t, value: t }));
|
||||
|
||||
showModal(SelectModal, {
|
||||
title: 'Clear a model',
|
||||
message: 'Select a model to clear; this operation cannot be undone.',
|
||||
value: options[0].value,
|
||||
options,
|
||||
onDone: async (type: string | null) => {
|
||||
if (type) {
|
||||
const bufferId = await database.bufferChanges();
|
||||
console.log(`[developer] clearing all "${type}" entities`);
|
||||
const allEntities = await database.all(type);
|
||||
const filteredEntites = allEntities
|
||||
.filter(isNotDefaultProject); // don't clear the default project
|
||||
await database.batchModifyDocs({ remove: filteredEntites });
|
||||
database.flushChanges(bufferId);
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
ipcRenderer.on('clear-all-models', () => {
|
||||
showModal(AskModal, {
|
||||
title: 'Clear all models',
|
||||
message: 'Are you sure you want to clear all models? This operation cannot be undone.',
|
||||
yesText: 'Yes',
|
||||
noText: 'No',
|
||||
onDone: async (yes: boolean) => {
|
||||
if (yes) {
|
||||
const bufferId = await database.bufferChanges();
|
||||
const promises = models
|
||||
.types()
|
||||
.filter(t => t !== models.settings.type) // don't clear settings
|
||||
.reverse().map(async type => {
|
||||
console.log(`[developer] clearing all "${type}" entities`);
|
||||
const allEntities = await database.all(type);
|
||||
const filteredEntites = allEntities
|
||||
.filter(isNotDefaultProject); // don't clear the default project
|
||||
await database.batchModifyDocs({ remove: filteredEntites });
|
||||
});
|
||||
await Promise.all(promises);
|
||||
database.flushChanges(bufferId);
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ipcRenderer.on('reload-plugins', async () => {
|
||||
const settings = await models.settings.getOrCreate();
|
||||
await plugins.reloadPlugins();
|
||||
await themes.applyColorScheme(settings);
|
||||
templating.reload();
|
||||
console.log('[plugins] reloaded');
|
||||
});
|
||||
|
||||
ipcRenderer.on('toggle-preferences-shortcuts', () => {
|
||||
showModal(SettingsModal, TAB_INDEX_SHORTCUTS);
|
||||
});
|
Loading…
Reference in New Issue
Block a user