(Partial) removal of WrapperProps (#4431)

This commit is contained in:
Dimitri Mitropoulos 2022-02-02 09:54:05 -06:00 committed by GitHub
parent aa2dc2e2de
commit 3556a20f85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 255 additions and 264 deletions

View File

@ -1,5 +1,5 @@
import { isDesign, Workspace } from '../models/workspace'; import { isDesign, Workspace } from '../models/workspace';
import { strings } from './strings'; import { strings } from './strings';
export const getWorkspaceLabel = (w: Workspace) => export const getWorkspaceLabel = (workspace: Workspace) =>
isDesign(w) ? strings.document : strings.collection; isDesign(workspace) ? strings.document : strings.collection;

View File

@ -1,17 +1,18 @@
import { MultiSwitch } from 'insomnia-components'; import { MultiSwitch } from 'insomnia-components';
import React, { FunctionComponent } from 'react'; import React, { FunctionComponent, useCallback } from 'react';
import { useSelector } from 'react-redux';
import type { GlobalActivity } from '../../common/constants'; import type { GlobalActivity } from '../../common/constants';
import { ACTIVITY_DEBUG, ACTIVITY_SPEC, ACTIVITY_UNIT_TEST } from '../../common/constants'; import { ACTIVITY_DEBUG, ACTIVITY_SPEC, ACTIVITY_UNIT_TEST } from '../../common/constants';
import type { Workspace } from '../../models/workspace'; import { isDesign } from '../../models/workspace';
import { selectActiveActivity, selectActiveWorkspace } from '../redux/selectors';
import { HandleActivityChange } from './wrapper';
interface Props { interface Props {
activity: GlobalActivity; handleActivityChange: HandleActivityChange;
handleActivityChange: (options: {workspaceId?: string; nextActivity: GlobalActivity}) => Promise<void>;
workspace: Workspace;
} }
export const ActivityToggle: FunctionComponent<Props> = ({ activity, handleActivityChange, workspace }) => { export const ActivityToggle: FunctionComponent<Props> = ({ handleActivityChange }) => {
const choices = [ const choices = [
{ {
label: 'Design', label: 'Design',
@ -27,12 +28,27 @@ export const ActivityToggle: FunctionComponent<Props> = ({ activity, handleActiv
}, },
]; ];
const activeActivity = useSelector(selectActiveActivity);
const activeWorkspace = useSelector(selectActiveWorkspace);
const onChange = useCallback((nextActivity: GlobalActivity) => {
handleActivityChange({ workspaceId: activeWorkspace?._id, nextActivity });
}, [handleActivityChange, activeWorkspace]);
if (!activeActivity) {
return null;
}
if (!activeWorkspace || !isDesign(activeWorkspace)) {
return null;
}
return ( return (
<MultiSwitch <MultiSwitch
name="activity-toggle" name="activity-toggle"
onChange={(nextActivity: GlobalActivity) => handleActivityChange({ workspaceId: workspace._id, nextActivity })} onChange={onChange}
choices={choices} choices={choices}
selectedValue={activity} selectedValue={activeActivity}
/> />
); );
}; };

View File

@ -1,7 +1,7 @@
import { autoBindMethodsForReact } from 'class-autobind-decorator'; import { autoBindMethodsForReact } from 'class-autobind-decorator';
import classnames from 'classnames'; import classnames from 'classnames';
import { HotKeyRegistry } from 'insomnia-common';
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import { AUTOBIND_CFG } from '../../../common/constants'; import { AUTOBIND_CFG } from '../../../common/constants';
import { database as db } from '../../../common/database'; import { database as db } from '../../../common/database';
@ -9,15 +9,15 @@ import { getWorkspaceLabel } from '../../../common/get-workspace-label';
import { hotKeyRefs } from '../../../common/hotkeys'; import { hotKeyRefs } from '../../../common/hotkeys';
import { executeHotKey } from '../../../common/hotkeys-listener'; import { executeHotKey } from '../../../common/hotkeys-listener';
import { RENDER_PURPOSE_NO_RENDER } from '../../../common/render'; import { RENDER_PURPOSE_NO_RENDER } from '../../../common/render';
import { ApiSpec } from '../../../models/api-spec';
import type { Environment } from '../../../models/environment';
import { Project } from '../../../models/project';
import { isRequest } from '../../../models/request'; import { isRequest } from '../../../models/request';
import { isRequestGroup } from '../../../models/request-group'; import { isRequestGroup } from '../../../models/request-group';
import { isDesign, Workspace } from '../../../models/workspace'; import { isDesign, Workspace } from '../../../models/workspace';
import type { WorkspaceAction } from '../../../plugins'; import type { WorkspaceAction } from '../../../plugins';
import { ConfigGenerator, getConfigGenerators, getWorkspaceActions } from '../../../plugins'; import { ConfigGenerator, getConfigGenerators, getWorkspaceActions } from '../../../plugins';
import * as pluginContexts from '../../../plugins/context'; import * as pluginContexts from '../../../plugins/context';
import { RootState } from '../../redux/modules';
import { selectIsLoading } from '../../redux/modules/global';
import { selectActiveApiSpec, selectActiveEnvironment, selectActiveProject, selectActiveWorkspace, selectActiveWorkspaceName, selectSettings } from '../../redux/selectors';
import { Dropdown } from '../base/dropdown/dropdown'; import { Dropdown } from '../base/dropdown/dropdown';
import { DropdownButton } from '../base/dropdown/dropdown-button'; import { DropdownButton } from '../base/dropdown/dropdown-button';
import { DropdownDivider } from '../base/dropdown/dropdown-divider'; import { DropdownDivider } from '../base/dropdown/dropdown-divider';
@ -29,13 +29,9 @@ import { showGenerateConfigModal } from '../modals/generate-config-modal';
import { SettingsModal, TAB_INDEX_EXPORT } from '../modals/settings-modal'; import { SettingsModal, TAB_INDEX_EXPORT } from '../modals/settings-modal';
import { WorkspaceSettingsModal } from '../modals/workspace-settings-modal'; import { WorkspaceSettingsModal } from '../modals/workspace-settings-modal';
interface Props { type ReduxProps = ReturnType<typeof mapStateToProps>;
activeEnvironment: Environment | null;
activeWorkspace: Workspace; interface Props extends ReduxProps {
activeWorkspaceName?: string;
activeApiSpec: ApiSpec;
activeProject: Project;
hotKeyRegistry: HotKeyRegistry;
isLoading: boolean; isLoading: boolean;
className?: string; className?: string;
} }
@ -47,7 +43,7 @@ interface State {
} }
@autoBindMethodsForReact(AUTOBIND_CFG) @autoBindMethodsForReact(AUTOBIND_CFG)
export class WorkspaceDropdown extends PureComponent<Props, State> { export class UnconnectedWorkspaceDropdown extends PureComponent<Props, State> {
_dropdown: Dropdown | null = null; _dropdown: Dropdown | null = null;
state: State = { state: State = {
actionPlugins: [], actionPlugins: [],
@ -55,31 +51,31 @@ export class WorkspaceDropdown extends PureComponent<Props, State> {
loadingActions: {}, loadingActions: {},
}; };
async _handlePluginClick(p: WorkspaceAction) { async _handlePluginClick({ action, plugin, label }: WorkspaceAction, workspace: Workspace) {
this.setState(state => ({ this.setState(state => ({
loadingActions: { ...state.loadingActions, [p.label]: true }, loadingActions: { ...state.loadingActions, [label]: true },
})); }));
const { activeEnvironment, activeWorkspace, activeProject } = this.props; const { activeEnvironment, activeProject } = this.props;
try { try {
const activeEnvironmentId = activeEnvironment ? activeEnvironment._id : null; const activeEnvironmentId = activeEnvironment ? activeEnvironment._id : null;
const context = { const context = {
...(pluginContexts.app.init(RENDER_PURPOSE_NO_RENDER) as Record<string, any>), ...(pluginContexts.app.init(RENDER_PURPOSE_NO_RENDER) as Record<string, any>),
...pluginContexts.data.init(activeProject._id), ...pluginContexts.data.init(activeProject._id),
...(pluginContexts.store.init(p.plugin) as Record<string, any>), ...(pluginContexts.store.init(plugin) as Record<string, any>),
...(pluginContexts.network.init(activeEnvironmentId) as Record<string, any>), ...(pluginContexts.network.init(activeEnvironmentId) as Record<string, any>),
}; };
const docs = await db.withDescendants(activeWorkspace); const docs = await db.withDescendants(workspace);
const requests = docs const requests = docs
.filter(isRequest) .filter(isRequest)
.filter(doc => ( .filter(doc => (
!doc.isPrivate !doc.isPrivate
)); ));
const requestGroups = docs.filter(isRequestGroup); const requestGroups = docs.filter(isRequestGroup);
await p.action(context, { await action(context, {
requestGroups, requestGroups,
requests, requests,
workspace: activeWorkspace, workspace,
}); });
} catch (err) { } catch (err) {
showError({ showError({
@ -89,7 +85,7 @@ export class WorkspaceDropdown extends PureComponent<Props, State> {
} }
this.setState(state => ({ this.setState(state => ({
loadingActions: { ...state.loadingActions, [p.label]: false }, loadingActions: { ...state.loadingActions, [label]: false },
})); }));
this._dropdown?.hide(); this._dropdown?.hide();
} }
@ -123,6 +119,9 @@ export class WorkspaceDropdown extends PureComponent<Props, State> {
async _handleGenerateConfig(label: string) { async _handleGenerateConfig(label: string) {
const { activeApiSpec } = this.props; const { activeApiSpec } = this.props;
if (!activeApiSpec) {
return;
}
showGenerateConfigModal({ showGenerateConfigModal({
apiSpec: activeApiSpec, apiSpec: activeApiSpec,
activeTabLabel: label, activeTabLabel: label,
@ -140,6 +139,11 @@ export class WorkspaceDropdown extends PureComponent<Props, State> {
} = this.props; } = this.props;
const classes = classnames(className, 'wide', 'workspace-dropdown'); const classes = classnames(className, 'wide', 'workspace-dropdown');
const { actionPlugins, loadingActions, configGeneratorPlugins } = this.state; const { actionPlugins, loadingActions, configGeneratorPlugins } = this.state;
if (!activeWorkspace) {
console.error('warning: tried to render WorkspaceDropdown without an activeWorkspace');
return null;
}
return ( return (
<KeydownBinder onKeydown={this._handleKeydown}> <KeydownBinder onKeydown={this._handleKeydown}>
<Dropdown <Dropdown
@ -177,7 +181,7 @@ export class WorkspaceDropdown extends PureComponent<Props, State> {
{actionPlugins.map((p: WorkspaceAction) => ( {actionPlugins.map((p: WorkspaceAction) => (
<DropdownItem <DropdownItem
key={p.label} key={p.label}
onClick={() => this._handlePluginClick(p)} onClick={() => this._handlePluginClick(p, activeWorkspace)}
stayOpenAfterClick stayOpenAfterClick
> >
{loadingActions[p.label] ? ( {loadingActions[p.label] ? (
@ -210,3 +214,15 @@ export class WorkspaceDropdown extends PureComponent<Props, State> {
); );
} }
} }
const mapStateToProps = (state: RootState) => ({
activeEnvironment: selectActiveEnvironment(state),
activeWorkspace: selectActiveWorkspace(state),
activeWorkspaceName: selectActiveWorkspaceName(state),
activeApiSpec: selectActiveApiSpec(state),
activeProject: selectActiveProject(state),
hotKeyRegistry: selectSettings(state).hotKeyRegistry,
isLoading: selectIsLoading(state),
});
export const WorkspaceDropdown = connect(mapStateToProps)(UnconnectedWorkspaceDropdown);

View File

@ -1,8 +1,9 @@
import { autoBindMethodsForReact } from 'class-autobind-decorator';
import classnames from 'classnames'; import classnames from 'classnames';
import React, { forwardRef, PureComponent, ReactNode } from 'react'; import React, { FC, forwardRef, ReactNode, useCallback } from 'react';
import { useSelector } from 'react-redux';
import { AUTOBIND_CFG } from '../../common/constants'; import { selectIsLoading } from '../redux/modules/global';
import { selectActiveEnvironment, selectActiveGitRepository, selectSettings, selectUnseenWorkspaces, selectWorkspacesForActiveProject } from '../redux/selectors';
import { ErrorBoundary } from './error-boundary'; import { ErrorBoundary } from './error-boundary';
import { Sidebar } from './sidebar/sidebar'; import { Sidebar } from './sidebar/sidebar';
import type { WrapperProps } from './wrapper'; import type { WrapperProps } from './wrapper';
@ -26,168 +27,164 @@ interface Props {
renderPaneTwo?: () => ReactNode; renderPaneTwo?: () => ReactNode;
} }
@autoBindMethodsForReact(AUTOBIND_CFG) export const PageLayout: FC<Props> = ({
export class PageLayout extends PureComponent<Props> { renderPaneOne,
// Special request updaters renderPaneTwo,
_handleStartDragSidebar(e: React.MouseEvent<HTMLDivElement>) { renderPageBody,
e.preventDefault(); renderPageHeader,
const { handleStartDragSidebar } = this.props.wrapperProps; renderPageSidebar,
handleStartDragSidebar(e); wrapperProps,
} }) => {
const activeEnvironment = useSelector(selectActiveEnvironment);
const activeGitRepository = useSelector(selectActiveGitRepository);
const isLoading = useSelector(selectIsLoading);
const settings = useSelector(selectSettings);
const unseenWorkspaces = useSelector(selectUnseenWorkspaces);
const workspaces = useSelector(selectWorkspacesForActiveProject);
render() { const {
const { gitVCS,
renderPaneOne, handleInitializeEntities,
renderPaneTwo, handleResetDragSidebar,
renderPageBody, handleStartDragSidebar,
renderPageHeader, handleSetActiveEnvironment,
renderPageSidebar, sidebarRef,
wrapperProps, requestPaneRef,
} = this.props; responsePaneRef,
const { handleStartDragPaneHorizontal,
activeEnvironment, handleResetDragPaneHorizontal,
activeGitRepository, handleStartDragPaneVertical,
gitVCS, handleResetDragPaneVertical,
handleInitializeEntities, paneHeight,
handleResetDragSidebar, paneWidth,
handleSetActiveEnvironment, sidebarHidden,
sidebarRef, sidebarWidth,
requestPaneRef, } = wrapperProps;
responsePaneRef,
handleStartDragPaneHorizontal, // Special request updaters
handleResetDragPaneHorizontal, const startDragSidebar = useCallback((event: React.MouseEvent<HTMLDivElement>) => {
handleStartDragPaneVertical, event.preventDefault();
handleResetDragPaneVertical, handleStartDragSidebar(event);
isLoading, }, [handleStartDragSidebar]);
paneHeight,
paneWidth, const realSidebarWidth = sidebarHidden ? 0 : sidebarWidth;
settings, const paneTwo = renderPaneTwo?.();
sidebarHidden, const gridRows = paneTwo
sidebarWidth, ? `auto minmax(0, ${paneHeight}fr) 0 minmax(0, ${1 - paneHeight}fr)`
unseenWorkspaces, : 'auto 1fr';
workspaces, const gridColumns =
} = wrapperProps;
const realSidebarWidth = sidebarHidden ? 0 : sidebarWidth;
const paneTwo = renderPaneTwo?.();
const gridRows = paneTwo
? `auto minmax(0, ${paneHeight}fr) 0 minmax(0, ${1 - paneHeight}fr)`
: 'auto 1fr';
const gridColumns =
`auto ${realSidebarWidth}rem 0 ` + `auto ${realSidebarWidth}rem 0 ` +
`${paneTwo ? `minmax(0, ${paneWidth}fr) 0 minmax(0, ${1 - paneWidth}fr)` : '1fr'}`; `${paneTwo ? `minmax(0, ${paneWidth}fr) 0 minmax(0, ${1 - paneWidth}fr)` : '1fr'}`;
return ( return (
<div <div
key="wrapper" key="wrapper"
id="wrapper" id="wrapper"
className={classnames('wrapper', { className={classnames('wrapper', {
'wrapper--vertical': settings.forceVerticalLayout, 'wrapper--vertical': settings.forceVerticalLayout,
})} })}
style={{ style={{
gridTemplateColumns: gridColumns, gridTemplateColumns: gridColumns,
gridTemplateRows: gridRows, gridTemplateRows: gridRows,
boxSizing: 'border-box', boxSizing: 'border-box',
borderTop: borderTop:
activeEnvironment && activeEnvironment &&
activeEnvironment.color && activeEnvironment.color &&
settings.environmentHighlightColorStyle === 'window-top' settings.environmentHighlightColorStyle === 'window-top'
? '5px solid ' + activeEnvironment.color ? '5px solid ' + activeEnvironment.color
: undefined, : undefined,
borderBottom: borderBottom:
activeEnvironment && activeEnvironment &&
activeEnvironment.color && activeEnvironment.color &&
settings.environmentHighlightColorStyle === 'window-bottom' settings.environmentHighlightColorStyle === 'window-bottom'
? '5px solid ' + activeEnvironment.color ? '5px solid ' + activeEnvironment.color
: undefined, : undefined,
borderLeft: borderLeft:
activeEnvironment && activeEnvironment &&
activeEnvironment.color && activeEnvironment.color &&
settings.environmentHighlightColorStyle === 'window-left' settings.environmentHighlightColorStyle === 'window-left'
? '5px solid ' + activeEnvironment.color ? '5px solid ' + activeEnvironment.color
: undefined, : undefined,
borderRight: borderRight:
activeEnvironment && activeEnvironment &&
activeEnvironment.color && activeEnvironment.color &&
settings.environmentHighlightColorStyle === 'window-right' settings.environmentHighlightColorStyle === 'window-right'
? '5px solid ' + activeEnvironment.color ? '5px solid ' + activeEnvironment.color
: undefined, : undefined,
}} }}
> >
{renderPageHeader && <ErrorBoundary showAlert>{renderPageHeader()}</ErrorBoundary>} {renderPageHeader && <ErrorBoundary showAlert>{renderPageHeader()}</ErrorBoundary>}
{renderPageSidebar && ( {renderPageSidebar && (
<ErrorBoundary showAlert> <ErrorBoundary showAlert>
<Sidebar <Sidebar
ref={sidebarRef} ref={sidebarRef}
activeEnvironment={activeEnvironment} activeEnvironment={activeEnvironment}
// @ts-expect-error -- TSCONVERSION // @ts-expect-error -- TSCONVERSION
activeGitRepository={activeGitRepository} activeGitRepository={activeGitRepository}
environmentHighlightColorStyle={settings.environmentHighlightColorStyle} environmentHighlightColorStyle={settings.environmentHighlightColorStyle}
handleInitializeEntities={handleInitializeEntities} handleInitializeEntities={handleInitializeEntities}
handleSetActiveEnvironment={handleSetActiveEnvironment} handleSetActiveEnvironment={handleSetActiveEnvironment}
hidden={sidebarHidden || false} hidden={sidebarHidden || false}
hotKeyRegistry={settings.hotKeyRegistry} hotKeyRegistry={settings.hotKeyRegistry}
isLoading={isLoading} isLoading={isLoading}
// @ts-expect-error -- TSCONVERSION appears to be a genuine error, or is it that it comes from Wrapper? unseenWorkspaces={unseenWorkspaces}
showEnvironmentsModal={this._handleShowEnvironmentsModal} gitVCS={gitVCS}
unseenWorkspaces={unseenWorkspaces} width={sidebarWidth}
gitVCS={gitVCS} workspaces={workspaces}
width={sidebarWidth} >
workspaces={workspaces} {renderPageSidebar()}
> </Sidebar>
{renderPageSidebar()}
</Sidebar> <div className="drag drag--sidebar">
<div
onDoubleClick={handleResetDragSidebar}
onMouseDown={startDragSidebar}
/>
</div>
</ErrorBoundary>
)}
{renderPageBody ? (
<ErrorBoundary showAlert>{renderPageBody()}</ErrorBoundary>
) : (
<>
{renderPaneOne && (
<ErrorBoundary showAlert>
<Pane
position="one"
ref={requestPaneRef}
>
{renderPaneOne()}
</Pane>
</ErrorBoundary>
)}
{paneTwo && (
<>
<div className="drag drag--pane-horizontal">
<div
onMouseDown={handleStartDragPaneHorizontal}
onDoubleClick={handleResetDragPaneHorizontal}
/>
</div>
<div className="drag drag--pane-vertical">
<div
onMouseDown={handleStartDragPaneVertical}
onDoubleClick={handleResetDragPaneVertical}
/>
</div>
<div className="drag drag--sidebar">
<div
onDoubleClick={handleResetDragSidebar}
onMouseDown={this._handleStartDragSidebar}
/>
</div>
</ErrorBoundary>
)}
{renderPageBody ? (
<ErrorBoundary showAlert>{renderPageBody()}</ErrorBoundary>
) : (
<>
{renderPaneOne && (
<ErrorBoundary showAlert> <ErrorBoundary showAlert>
<Pane <Pane
position="one" position="two"
ref={requestPaneRef} ref={responsePaneRef}
> >
{renderPaneOne()} {paneTwo}
</Pane> </Pane>
</ErrorBoundary> </ErrorBoundary>
)} </>
{paneTwo && ( )}
<> </>
<div className="drag drag--pane-horizontal"> )}
<div </div>
onMouseDown={handleStartDragPaneHorizontal} );
onDoubleClick={handleResetDragPaneHorizontal} };
/>
</div>
<div className="drag drag--pane-vertical">
<div
onMouseDown={handleStartDragPaneVertical}
onDoubleClick={handleResetDragPaneVertical}
/>
</div>
<ErrorBoundary showAlert>
<Pane
position="two"
ref={responsePaneRef}
>
{paneTwo}
</Pane>
</ErrorBoundary>
</>
)}
</>
)}
</div>
);
}
}

View File

@ -17,7 +17,6 @@ interface Props {
hidden: boolean; hidden: boolean;
hotKeyRegistry: HotKeyRegistry; hotKeyRegistry: HotKeyRegistry;
isLoading: boolean; isLoading: boolean;
showEnvironmentsModal: (...args: any[]) => any;
unseenWorkspaces: Workspace[]; unseenWorkspaces: Workspace[];
width: number; width: number;
workspaces: Workspace[]; workspaces: Workspace[];

View File

@ -1,74 +1,44 @@
import React, { FunctionComponent, ReactNode, useCallback } from 'react'; import React, { FunctionComponent, ReactNode, useCallback } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { ACTIVITY_HOME, GlobalActivity } from '../../common/constants'; import { ACTIVITY_HOME } from '../../common/constants';
import { isDesign } from '../../models/workspace'; import { selectActiveActivity, selectActiveApiSpec, selectActiveProjectName, selectActiveWorkspace } from '../redux/selectors';
import { selectActiveProjectName } from '../redux/selectors';
import { ActivityToggle } from './activity-toggle'; import { ActivityToggle } from './activity-toggle';
import { AppHeader } from './app-header'; import { AppHeader } from './app-header';
import { WorkspaceDropdown } from './dropdowns/workspace-dropdown'; import { WorkspaceDropdown } from './dropdowns/workspace-dropdown';
import type { WrapperProps } from './wrapper'; import { HandleActivityChange } from './wrapper';
interface Props { interface Props {
wrapperProps: WrapperProps; handleActivityChange: HandleActivityChange;
handleActivityChange: (options: {workspaceId?: string; nextActivity: GlobalActivity}) => Promise<void>;
gridRight: ReactNode; gridRight: ReactNode;
} }
export const WorkspacePageHeader: FunctionComponent<Props> = ({ export const WorkspacePageHeader: FunctionComponent<Props> = ({
gridRight, gridRight,
handleActivityChange, handleActivityChange,
wrapperProps: {
activeApiSpec,
activeWorkspace,
activeWorkspaceName,
activeProject,
activeEnvironment,
settings,
activity,
isLoading,
},
}) => { }) => {
const homeCallback = useCallback( const activeApiSpec = useSelector(selectActiveApiSpec);
() => handleActivityChange({ workspaceId: activeWorkspace?._id, nextActivity: ACTIVITY_HOME }), const activeWorkspace = useSelector(selectActiveWorkspace);
[activeWorkspace, handleActivityChange], const activity = useSelector(selectActiveActivity);
);
const homeCallback = useCallback(() => {
handleActivityChange({ workspaceId: activeWorkspace?._id, nextActivity: ACTIVITY_HOME });
}, [activeWorkspace, handleActivityChange]);
const activeProjectName = useSelector(selectActiveProjectName); const activeProjectName = useSelector(selectActiveProjectName);
if (!activeWorkspace || !activeApiSpec || !activity) { if (!activeWorkspace || !activeApiSpec || !activity) {
return null; return null;
} }
const workspace = (
<WorkspaceDropdown
key="workspace-dd"
activeEnvironment={activeEnvironment}
activeWorkspace={activeWorkspace}
activeWorkspaceName={activeWorkspaceName}
activeApiSpec={activeApiSpec}
activeProject={activeProject}
hotKeyRegistry={settings.hotKeyRegistry}
isLoading={isLoading}
/>
);
const crumbs = [ const crumbs = [
{ id: 'project', node: activeProjectName, onClick: homeCallback }, { id: 'project', node: activeProjectName, onClick: homeCallback },
{ id: 'workspace', node: workspace }, { id: 'workspace', node: <WorkspaceDropdown key="workspace-dd" /> },
]; ];
return ( return (
<AppHeader <AppHeader
breadcrumbProps={{ crumbs }} breadcrumbProps={{ crumbs }}
gridCenter={ gridCenter={<ActivityToggle handleActivityChange={handleActivityChange} />}
isDesign(activeWorkspace) && (
<ActivityToggle
activity={activity}
handleActivityChange={handleActivityChange}
workspace={activeWorkspace}
/>
)
}
gridRight={gridRight} gridRight={gridRight}
/> />
); );

View File

@ -1,7 +1,7 @@
import { autoBindMethodsForReact } from 'class-autobind-decorator'; import { autoBindMethodsForReact } from 'class-autobind-decorator';
import React, { Fragment, PureComponent, ReactNode } from 'react'; import React, { Fragment, PureComponent, ReactNode } from 'react';
import { AUTOBIND_CFG, GlobalActivity, SortOrder } from '../../common/constants'; import { AUTOBIND_CFG, SortOrder } from '../../common/constants';
import { isGrpcRequest } from '../../models/grpc-request'; import { isGrpcRequest } from '../../models/grpc-request';
import { isRemoteProject } from '../../models/project'; import { isRemoteProject } from '../../models/project';
import { Request, RequestAuthentication, RequestBody, RequestHeader, RequestParameter } from '../../models/request'; import { Request, RequestAuthentication, RequestBody, RequestHeader, RequestParameter } from '../../models/request';
@ -19,12 +19,12 @@ import { ResponsePane } from './panes/response-pane';
import { SidebarChildren } from './sidebar/sidebar-children'; import { SidebarChildren } from './sidebar/sidebar-children';
import { SidebarFilter } from './sidebar/sidebar-filter'; import { SidebarFilter } from './sidebar/sidebar-filter';
import { WorkspacePageHeader } from './workspace-page-header'; import { WorkspacePageHeader } from './workspace-page-header';
import type { WrapperProps } from './wrapper'; import type { HandleActivityChange, WrapperProps } from './wrapper';
interface Props { interface Props {
forceRefreshKey: number; forceRefreshKey: number;
gitSyncDropdown: ReactNode; gitSyncDropdown: ReactNode;
handleActivityChange: (options: {workspaceId?: string; nextActivity: GlobalActivity}) => Promise<void>; handleActivityChange: HandleActivityChange;
handleChangeEnvironment: Function; handleChangeEnvironment: Function;
handleDeleteResponse: Function; handleDeleteResponse: Function;
handleDeleteResponses: Function; handleDeleteResponses: Function;
@ -54,18 +54,22 @@ interface Props {
@autoBindMethodsForReact(AUTOBIND_CFG) @autoBindMethodsForReact(AUTOBIND_CFG)
export class WrapperDebug extends PureComponent<Props> { export class WrapperDebug extends PureComponent<Props> {
_renderPageHeader() { _renderPageHeader() {
const { wrapperProps, gitSyncDropdown, handleActivityChange } = this.props; const { gitSyncDropdown, handleActivityChange, wrapperProps: {
const { vcs, activeWorkspace, activeWorkspaceMeta, activeProject, syncItems, isLoggedIn } = this.props.wrapperProps; vcs,
activeWorkspace,
activeWorkspaceMeta,
activeProject,
syncItems,
isLoggedIn,
} } = this.props;
if (!activeWorkspace) { if (!activeWorkspace) {
return null; return null;
} }
const collection = isCollection(activeWorkspace); const collection = isCollection(activeWorkspace);
const design = isDesign(activeWorkspace);
let insomniaSync: ReactNode = null; let insomniaSync: ReactNode = null;
if (isLoggedIn && collection && isRemoteProject(activeProject) && vcs) { if (isLoggedIn && collection && isRemoteProject(activeProject) && vcs) {
insomniaSync = <SyncDropdown insomniaSync = <SyncDropdown
workspace={activeWorkspace} workspace={activeWorkspace}
@ -76,12 +80,11 @@ export class WrapperDebug extends PureComponent<Props> {
/>; />;
} }
const gitSync = design && gitSyncDropdown; const gitSync = isDesign(activeWorkspace) && gitSyncDropdown;
const sync = insomniaSync || gitSync; const sync = insomniaSync || gitSync;
return ( return (
<WorkspacePageHeader <WorkspacePageHeader
wrapperProps={wrapperProps}
handleActivityChange={handleActivityChange} handleActivityChange={handleActivityChange}
gridRight={sync} gridRight={sync}
/> />

View File

@ -1,23 +1,24 @@
import { IRuleResult } from '@stoplight/spectral'; import { IRuleResult } from '@stoplight/spectral';
import { Button, Notice, NoticeTable } from 'insomnia-components'; import { Button, Notice, NoticeTable } from 'insomnia-components';
import React, { createRef, FC, Fragment, ReactNode, RefObject, useCallback, useState } from 'react'; import React, { createRef, FC, Fragment, ReactNode, RefObject, useCallback, useState } from 'react';
import { useSelector } from 'react-redux';
import { useAsync, useDebounce } from 'react-use'; import { useAsync, useDebounce } from 'react-use';
import styled from 'styled-components'; import styled from 'styled-components';
import SwaggerUI from 'swagger-ui-react'; import SwaggerUI from 'swagger-ui-react';
import { parseApiSpec, ParsedApiSpec } from '../../common/api-specs'; import { parseApiSpec, ParsedApiSpec } from '../../common/api-specs';
import type { GlobalActivity } from '../../common/constants';
import { initializeSpectral, isLintError } from '../../common/spectral'; import { initializeSpectral, isLintError } from '../../common/spectral';
import * as models from '../../models/index'; import * as models from '../../models/index';
import { superFaint } from '../css/css-in-js'; import { superFaint } from '../css/css-in-js';
import previewIcon from '../images/icn-eye.svg'; import previewIcon from '../images/icn-eye.svg';
import { selectActiveApiSpec, selectActiveWorkspace, selectActiveWorkspaceMeta } from '../redux/selectors';
import { CodeEditor, UnconnectedCodeEditor } from './codemirror/code-editor'; import { CodeEditor, UnconnectedCodeEditor } from './codemirror/code-editor';
import { DesignEmptyState } from './design-empty-state'; import { DesignEmptyState } from './design-empty-state';
import { ErrorBoundary } from './error-boundary'; import { ErrorBoundary } from './error-boundary';
import { PageLayout } from './page-layout'; import { PageLayout } from './page-layout';
import { SpecEditorSidebar } from './spec-editor/spec-editor-sidebar'; import { SpecEditorSidebar } from './spec-editor/spec-editor-sidebar';
import { WorkspacePageHeader } from './workspace-page-header'; import { WorkspacePageHeader } from './workspace-page-header';
import type { WrapperProps } from './wrapper'; import type { HandleActivityChange, WrapperProps } from './wrapper';
const EmptySpaceHelper = styled.div({ const EmptySpaceHelper = styled.div({
...superFaint, ...superFaint,
@ -33,13 +34,12 @@ const spectral = initializeSpectral();
const RenderPageHeader: FC<Pick<Props, const RenderPageHeader: FC<Pick<Props,
| 'gitSyncDropdown' | 'gitSyncDropdown'
| 'handleActivityChange' | 'handleActivityChange'
| 'wrapperProps'
>> = ({ >> = ({
gitSyncDropdown, gitSyncDropdown,
handleActivityChange, handleActivityChange,
wrapperProps,
}) => { }) => {
const { activeWorkspace, activeWorkspaceMeta } = wrapperProps; const activeWorkspace = useSelector(selectActiveWorkspace);
const activeWorkspaceMeta = useSelector(selectActiveWorkspaceMeta);
const previewHidden = Boolean(activeWorkspaceMeta?.previewHidden); const previewHidden = Boolean(activeWorkspaceMeta?.previewHidden);
const handleTogglePreview = useCallback(async () => { const handleTogglePreview = useCallback(async () => {
@ -53,7 +53,6 @@ const RenderPageHeader: FC<Pick<Props,
return ( return (
<WorkspacePageHeader <WorkspacePageHeader
wrapperProps={wrapperProps}
handleActivityChange={handleActivityChange} handleActivityChange={handleActivityChange}
gridRight={ gridRight={
<Fragment> <Fragment>
@ -75,13 +74,8 @@ interface LintMessage {
range: IRuleResult['range']; range: IRuleResult['range'];
} }
const RenderEditor: FC<Pick<Props, 'wrapperProps'> & { const RenderEditor: FC<{ editor: RefObject<UnconnectedCodeEditor> }> = ({ editor }) => {
editor: RefObject<UnconnectedCodeEditor>; const activeApiSpec = useSelector(selectActiveApiSpec);
}> = ({
editor,
wrapperProps,
}) => {
const { activeApiSpec } = wrapperProps;
const [forceRefreshCounter, setForceRefreshCounter] = useState(0); const [forceRefreshCounter, setForceRefreshCounter] = useState(0);
const [lintMessages, setLintMessages] = useState<LintMessage[]>([]); const [lintMessages, setLintMessages] = useState<LintMessage[]>([]);
const contents = activeApiSpec?.contents ?? ''; const contents = activeApiSpec?.contents ?? '';
@ -160,8 +154,9 @@ const RenderEditor: FC<Pick<Props, 'wrapperProps'> & {
); );
}; };
const RenderPreview: FC<Pick<Props, 'wrapperProps'>> = ({ wrapperProps }) => { const RenderPreview: FC = () => {
const { activeApiSpec, activeWorkspaceMeta } = wrapperProps; const activeWorkspaceMeta = useSelector(selectActiveWorkspaceMeta);
const activeApiSpec = useSelector(selectActiveApiSpec);
if (!activeApiSpec || activeWorkspaceMeta?.previewHidden) { if (!activeApiSpec || activeWorkspaceMeta?.previewHidden) {
return null; return null;
@ -217,10 +212,8 @@ const RenderPreview: FC<Pick<Props, 'wrapperProps'>> = ({ wrapperProps }) => {
); );
}; };
const RenderPageSidebar: FC<Pick<Props, 'wrapperProps'> & { editor: RefObject<UnconnectedCodeEditor>}> = ({ const RenderPageSidebar: FC<{ editor: RefObject<UnconnectedCodeEditor>}> = ({ editor }) => {
editor, const activeApiSpec = useSelector(selectActiveApiSpec);
wrapperProps: { activeApiSpec },
}) => {
const handleScrollToSelection = useCallback((chStart: number, chEnd: number, lineStart: number, lineEnd: number) => { const handleScrollToSelection = useCallback((chStart: number, chEnd: number, lineStart: number, lineEnd: number) => {
if (!editor.current) { if (!editor.current) {
return; return;
@ -263,7 +256,7 @@ const RenderPageSidebar: FC<Pick<Props, 'wrapperProps'> & { editor: RefObject<Un
interface Props { interface Props {
gitSyncDropdown: ReactNode; gitSyncDropdown: ReactNode;
handleActivityChange: (options: {workspaceId?: string; nextActivity: GlobalActivity}) => Promise<void>; handleActivityChange: HandleActivityChange;
wrapperProps: WrapperProps; wrapperProps: WrapperProps;
} }
@ -278,29 +271,20 @@ export const WrapperDesign: FC<Props> = ({
<RenderPageHeader <RenderPageHeader
gitSyncDropdown={gitSyncDropdown} gitSyncDropdown={gitSyncDropdown}
handleActivityChange={handleActivityChange} handleActivityChange={handleActivityChange}
wrapperProps={wrapperProps}
/> />
), [gitSyncDropdown, handleActivityChange, wrapperProps]); ), [gitSyncDropdown, handleActivityChange]);
const renderEditor = useCallback(() => ( const renderEditor = useCallback(() => (
<RenderEditor <RenderEditor editor={editor} />
editor={editor} ), [editor]);
wrapperProps={wrapperProps}
/>
), [editor, wrapperProps]);
const renderPreview = useCallback(() => ( const renderPreview = useCallback(() => (
<RenderPreview <RenderPreview />
wrapperProps={wrapperProps} ), []);
/>
), [wrapperProps]);
const renderPageSidebar = useCallback(() => ( const renderPageSidebar = useCallback(() => (
<RenderPageSidebar <RenderPageSidebar editor={editor} />
editor={editor} ), [editor]);
wrapperProps={wrapperProps}
/>
), [editor, wrapperProps]);
return ( return (
<PageLayout <PageLayout

View File

@ -14,7 +14,6 @@ import { isEmpty } from 'ramda';
import React, { PureComponent, ReactNode } from 'react'; import React, { PureComponent, ReactNode } from 'react';
import { SegmentEvent, trackSegmentEvent } from '../../common/analytics'; import { SegmentEvent, trackSegmentEvent } from '../../common/analytics';
import type { GlobalActivity } from '../../common/constants';
import { AUTOBIND_CFG } from '../../common/constants'; import { AUTOBIND_CFG } from '../../common/constants';
import { documentationLinks } from '../../common/documentation'; import { documentationLinks } from '../../common/documentation';
import { getSendRequestCallback } from '../../common/send-request'; import { getSendRequestCallback } from '../../common/send-request';
@ -33,12 +32,12 @@ import { EmptyStatePane } from './panes/empty-state-pane';
import type { SidebarChildObjects } from './sidebar/sidebar-children'; import type { SidebarChildObjects } from './sidebar/sidebar-children';
import { UnitTestEditable } from './unit-test-editable'; import { UnitTestEditable } from './unit-test-editable';
import { WorkspacePageHeader } from './workspace-page-header'; import { WorkspacePageHeader } from './workspace-page-header';
import type { WrapperProps } from './wrapper'; import type { HandleActivityChange, WrapperProps } from './wrapper';
interface Props { interface Props {
children: SidebarChildObjects; children: SidebarChildObjects;
gitSyncDropdown: ReactNode; gitSyncDropdown: ReactNode;
handleActivityChange: (options: {workspaceId?: string; nextActivity: GlobalActivity}) => Promise<void>; handleActivityChange: HandleActivityChange;
wrapperProps: WrapperProps; wrapperProps: WrapperProps;
} }
@ -546,10 +545,9 @@ export class WrapperUnitTest extends PureComponent<Props, State> {
} }
_renderPageHeader() { _renderPageHeader() {
const { wrapperProps, gitSyncDropdown, handleActivityChange } = this.props; const { gitSyncDropdown, handleActivityChange } = this.props;
return ( return (
<WorkspacePageHeader <WorkspacePageHeader
wrapperProps={wrapperProps}
handleActivityChange={handleActivityChange} handleActivityChange={handleActivityChange}
gridRight={gitSyncDropdown} gridRight={gitSyncDropdown}
/> />

View File

@ -122,6 +122,11 @@ export type WrapperProps = AppProps & {
gitVCS: GitVCS | null; gitVCS: GitVCS | null;
}; };
export type HandleActivityChange = (options: {
workspaceId?: string;
nextActivity: GlobalActivity;
}) => Promise<void>;
interface State { interface State {
forceRefreshKey: number; forceRefreshKey: number;
activeGitBranch: string; activeGitBranch: string;
@ -213,7 +218,7 @@ export class Wrapper extends PureComponent<WrapperProps, State> {
return null; return null;
} }
async _handleWorkspaceActivityChange({ workspaceId, nextActivity }: {workspaceId?: string; nextActivity: GlobalActivity}) { async _handleWorkspaceActivityChange({ workspaceId, nextActivity }: Parameters<HandleActivityChange>[0]): ReturnType<HandleActivityChange> {
const { activity, activeApiSpec, handleSetActiveActivity } = this.props; const { activity, activeApiSpec, handleSetActiveActivity } = this.props;
// Remember last activity on workspace for later, but only if it isn't HOME // Remember last activity on workspace for later, but only if it isn't HOME

View File

@ -44,6 +44,7 @@ import {
TAB_INDEX_THEMES, TAB_INDEX_THEMES,
} from '../../components/modals/settings-modal'; } from '../../components/modals/settings-modal';
import { selectActiveProjectName, selectStats, selectWorkspacesForActiveProject } from '../selectors'; import { selectActiveProjectName, selectStats, selectWorkspacesForActiveProject } from '../selectors';
import { RootState } from '.';
import { importUri } from './import'; import { importUri } from './import';
import { activateWorkspace } from './workspace'; import { activateWorkspace } from './workspace';
@ -180,6 +181,8 @@ export const reducer = combineReducers<GlobalState>({
isLoggedIn: loginStateChangeReducer, isLoggedIn: loginStateChangeReducer,
}); });
export const selectIsLoading = (state: RootState) => state.global.isLoading;
// ~~~~~~~ // // ~~~~~~~ //
// ACTIONS // // ACTIONS //
// ~~~~~~~ // // ~~~~~~~ //