diff --git a/packages/insomnia-app/app/ui/components/sidebar/sidebar-children.js b/packages/insomnia-app/app/ui/components/sidebar/sidebar-children.js index 1abd9e35b7..d87d54b80f 100644 --- a/packages/insomnia-app/app/ui/components/sidebar/sidebar-children.js +++ b/packages/insomnia-app/app/ui/components/sidebar/sidebar-children.js @@ -14,7 +14,7 @@ import { Dropdown } from '../base/dropdown'; import SidebarCreateDropdown from './sidebar-create-dropdown'; type Child = { - doc: Request | RequestGroup, + doc: Request | GrpcRequest | RequestGroup, children: Array, collapsed: boolean, hidden: boolean, @@ -103,7 +103,7 @@ class SidebarChildren extends React.PureComponent { return null; } - if (child.doc.type === models.request.type) { + if (child.doc.type === models.request.type || child.doc.type === models.grpcRequest.type) { return ( ); } else { + const methodTag = + request.type === models.grpcRequest.type ? ( + + ) : ( + + ); + node = (
  • - + {methodTag} ( +
    +
    + gRPC +
    +
    +); + +export default GrpcTag; diff --git a/packages/insomnia-app/app/ui/containers/app.js b/packages/insomnia-app/app/ui/containers/app.js index 98b94dce01..40b0ad4060 100644 --- a/packages/insomnia-app/app/ui/containers/app.js +++ b/packages/insomnia-app/app/ui/containers/app.js @@ -52,11 +52,11 @@ import { selectActiveWorkspaceClientCertificates, selectActiveWorkspaceMeta, selectEntitiesLists, - selectSidebarChildren, selectSyncItems, selectUnseenWorkspaces, selectWorkspaceRequestsAndRequestGroups, } from '../redux/selectors'; +import { selectSidebarChildren } from '../redux/sidebar-selectors'; import RequestCreateModal from '../components/modals/request-create-modal'; import GenerateCodeModal from '../components/modals/generate-code-modal'; import WorkspaceSettingsModal from '../components/modals/workspace-settings-modal'; diff --git a/packages/insomnia-app/app/ui/css/constants/colors.less b/packages/insomnia-app/app/ui/css/constants/colors.less index a66207fa32..96338e044d 100644 --- a/packages/insomnia-app/app/ui/css/constants/colors.less +++ b/packages/insomnia-app/app/ui/css/constants/colors.less @@ -27,11 +27,14 @@ } .notice, -.http-method-PATCH, -.method-grpc { +.http-method-PATCH { color: var(--color-notice) !important; } +.method-grpc { + color: #ffbf2f !important; +} + .warning, .http-method-PUT { color: var(--color-warning) !important; diff --git a/packages/insomnia-app/app/ui/redux/__tests__/sidebar-selectors.test.js b/packages/insomnia-app/app/ui/redux/__tests__/sidebar-selectors.test.js new file mode 100644 index 0000000000..f022b53d76 --- /dev/null +++ b/packages/insomnia-app/app/ui/redux/__tests__/sidebar-selectors.test.js @@ -0,0 +1,47 @@ +import * as models from '../../../models'; +import { + shouldIgnoreChildrenOf, + shouldShowInSidebar, + sortByMetaKeyOrId, +} from '../sidebar-selectors'; +import { difference } from 'lodash'; + +describe('shouldShowInSidebar', () => { + const allTypes = models.types(); + const supported = [models.request.type, models.requestGroup.type, models.grpcRequest.type]; + const unsupported = difference(allTypes, supported); + + it.each(supported)('should show %s in sidebar', type => { + expect(shouldShowInSidebar({ type })).toBe(true); + }); + + it.each(unsupported)('should not show %s in sidebar', type => { + expect(shouldShowInSidebar({ type })).toBe(false); + }); +}); + +describe('shouldIgnoreChildrenOf', () => { + it.each([models.request.type, models.grpcRequest.type])('should ignore children of %s', type => { + expect(shouldIgnoreChildrenOf({ type })).toBe(true); + }); + + it.each([models.requestGroup.type])('should not ignore children of', type => { + expect(shouldIgnoreChildrenOf({ type })).toBe(false); + }); +}); + +describe('sortByMetaKeyOrId', () => { + it('sort by _id if meta keys are identical', () => { + const a = { _id: 'a', metaSortKey: '1' }; + const b = { _id: 'b', metaSortKey: '1' }; + expect(sortByMetaKeyOrId(a, b)).toBe(1); + expect(sortByMetaKeyOrId(b, a)).toBe(-1); + }); + + it('sort by meta keys', () => { + const a = { metaSortKey: '1' }; + const b = { metaSortKey: '2' }; + expect(sortByMetaKeyOrId(a, b)).toBe(-1); + expect(sortByMetaKeyOrId(b, a)).toBe(1); + }); +}); diff --git a/packages/insomnia-app/app/ui/redux/selectors.js b/packages/insomnia-app/app/ui/redux/selectors.js index b96611976c..1cd05dc2c4 100644 --- a/packages/insomnia-app/app/ui/redux/selectors.js +++ b/packages/insomnia-app/app/ui/redux/selectors.js @@ -1,5 +1,4 @@ import { createSelector } from 'reselect'; -import { fuzzyMatchAll } from '../../common/misc'; import * as models from '../../models'; // ~~~~~~~~~ // @@ -139,98 +138,22 @@ export const selectActiveWorkspaceEntities = createSelector( export const selectPinnedRequests = createSelector(selectEntitiesLists, entities => { const pinned = {}; + const requests = [...entities.requests, ...entities.grpcRequests]; + const requestMetas = [...entities.requestMetas, ...entities.grpcRequestMetas]; + // Default all to unpinned - for (const request of entities.requests) { + for (const request of requests) { pinned[request._id] = false; } // Update those that have metadata (not all do) - for (const meta of entities.requestMetas) { + for (const meta of requestMetas) { pinned[meta.parentId] = meta.pinned; } return pinned; }); -export const selectSidebarChildren = createSelector( - selectCollapsedRequestGroups, - selectPinnedRequests, - selectActiveWorkspace, - selectActiveWorkspaceMeta, - selectEntitiesChildrenMap, - (collapsed, pinned, activeWorkspace, activeWorkspaceMeta, childrenMap) => { - const sidebarFilter = activeWorkspaceMeta ? activeWorkspaceMeta.sidebarFilter : ''; - - function next(parentId, pinnedChildren) { - const children = (childrenMap[parentId] || []) - .filter(doc => { - return doc.type === models.request.type || doc.type === models.requestGroup.type; - }) - .sort((a, b) => { - if (a.metaSortKey === b.metaSortKey) { - return a._id > b._id ? -1 : 1; - } else { - return a.metaSortKey < b.metaSortKey ? -1 : 1; - } - }); - - if (children.length > 0) { - return children.map(c => { - const child = { - doc: c, - hidden: false, - collapsed: !!collapsed[c._id], - pinned: !!pinned[c._id], - }; - - if (child.pinned) { - pinnedChildren.push(child); - } - - // Don't add children of requests - child.children = c.type === models.request.type ? [] : next(c._id, pinnedChildren); - - return child; - }); - } else { - return children; - } - } - - function matchChildren(children, parentNames = []) { - // Bail early if no filter defined - if (!sidebarFilter) { - return children; - } - - for (const child of children) { - // Gather all parents so we can match them too - matchChildren(child.children, [...parentNames, child.doc.name]); - - const hasMatchedChildren = child.children.find(c => c.hidden === false); - - // Try to match request attributes - const { name, method } = child.doc; - const match = fuzzyMatchAll(sidebarFilter, [name, method, ...parentNames], { - splitSpace: true, - }); - - // Update hidden state depending on whether it matched - const matched = hasMatchedChildren || match; - child.hidden = !matched; - } - - return children; - } - - const pinnedChildren = []; - const childrenTree = next(activeWorkspace._id, pinnedChildren); - const matchedChildren = matchChildren(childrenTree); - - return { pinned: pinnedChildren, all: matchedChildren }; - }, -); - export const selectWorkspaceRequestsAndRequestGroups = createSelector( selectActiveWorkspaceEntities, entities => { diff --git a/packages/insomnia-app/app/ui/redux/sidebar-selectors.js b/packages/insomnia-app/app/ui/redux/sidebar-selectors.js new file mode 100644 index 0000000000..d49f06023b --- /dev/null +++ b/packages/insomnia-app/app/ui/redux/sidebar-selectors.js @@ -0,0 +1,104 @@ +// @flow +import * as models from '../../models'; +import type { BaseModel } from '../../models'; +import type { Request } from '../../models/request'; +import type { GrpcRequest } from '../../models/grpc-request'; +import type { RequestGroup } from '../../models/request-group'; +import { createSelector } from 'reselect'; +import { fuzzyMatchAll } from '../../common/misc'; +import { + selectActiveWorkspace, + selectActiveWorkspaceMeta, + selectCollapsedRequestGroups, + selectEntitiesChildrenMap, + selectPinnedRequests, +} from './selectors'; + +type SidebarModels = Request | GrpcRequest | RequestGroup; + +export const shouldShowInSidebar = ({ type }: BaseModel): boolean => + type === models.request.type || + type === models.grpcRequest.type || + type === models.requestGroup.type; + +export const shouldIgnoreChildrenOf = ({ type }: SidebarModels): boolean => + type === models.request.type || type === models.grpcRequest.type; + +export const sortByMetaKeyOrId = (a: SidebarModels, b: SidebarModels): number => { + if (a.metaSortKey === b.metaSortKey) { + return a._id > b._id ? -1 : 1; // ascending + } else { + return a.metaSortKey < b.metaSortKey ? -1 : 1; // descending + } +}; + +export const selectSidebarChildren = createSelector( + selectCollapsedRequestGroups, + selectPinnedRequests, + selectActiveWorkspace, + selectActiveWorkspaceMeta, + selectEntitiesChildrenMap, + (collapsed, pinned, activeWorkspace, activeWorkspaceMeta, childrenMap) => { + const sidebarFilter = activeWorkspaceMeta ? activeWorkspaceMeta.sidebarFilter : ''; + + function next(parentId, pinnedChildren) { + const children: Array = (childrenMap[parentId] || []) + .filter(shouldShowInSidebar) + .sort(sortByMetaKeyOrId); + + if (children.length > 0) { + return children.map(c => { + const child = { + doc: c, + hidden: false, + collapsed: !!collapsed[c._id], + pinned: !!pinned[c._id], + }; + + if (child.pinned) { + pinnedChildren.push(child); + } + + // Don't add children of requests + child.children = shouldIgnoreChildrenOf(c) ? [] : next(c._id, pinnedChildren); + + return child; + }); + } else { + return children; + } + } + + function matchChildren(children, parentNames = []) { + // Bail early if no filter defined + if (!sidebarFilter) { + return children; + } + + for (const child of children) { + // Gather all parents so we can match them too + matchChildren(child.children, [...parentNames, child.doc.name]); + + const hasMatchedChildren = child.children.find(c => c.hidden === false); + + // Try to match request attributes + const { name, method } = child.doc; + const match = fuzzyMatchAll(sidebarFilter, [name, method, ...parentNames], { + splitSpace: true, + }); + + // Update hidden state depending on whether it matched + const matched = hasMatchedChildren || match; + child.hidden = !matched; + } + + return children; + } + + const pinnedChildren = []; + const childrenTree = next(activeWorkspace._id, pinnedChildren); + const matchedChildren = matchChildren(childrenTree); + + return { pinned: pinnedChildren, all: matchedChildren }; + }, +);