mirror of
https://github.com/Kong/insomnia
synced 2024-11-08 06:39:48 +00:00
Card order by date modified (#3170)
Co-authored-by: Dimitri Mitropoulos <dimitrimitropoulos@gmail.com> Co-authored-by: Opender Singh <opender.singh@konghq.com>
This commit is contained in:
parent
f1539e2b40
commit
84b9b84fdf
@ -1,6 +1,6 @@
|
|||||||
import * as misc from '../misc';
|
import * as misc from '../misc';
|
||||||
import { globalBeforeEach } from '../../__jest__/before-each';
|
import { globalBeforeEach } from '../../__jest__/before-each';
|
||||||
import { diffPatchObj, pluralize, snapNumberToLimits } from '../misc';
|
import { diffPatchObj, isNotNullOrUndefined, pluralize, snapNumberToLimits } from '../misc';
|
||||||
|
|
||||||
describe('hasAuthHeader()', () => {
|
describe('hasAuthHeader()', () => {
|
||||||
beforeEach(globalBeforeEach);
|
beforeEach(globalBeforeEach);
|
||||||
@ -271,3 +271,14 @@ describe('snapNumberToLimits()', () => {
|
|||||||
expect(snapNumberToLimits(5, NaN, 3)).toBe(3);
|
expect(snapNumberToLimits(5, NaN, 3)).toBe(3);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('isNotNullOrUndefined', () => {
|
||||||
|
it('should return correctly', () => {
|
||||||
|
expect(isNotNullOrUndefined(0)).toBe(true);
|
||||||
|
expect(isNotNullOrUndefined('')).toBe(true);
|
||||||
|
expect(isNotNullOrUndefined(false)).toBe(true);
|
||||||
|
|
||||||
|
expect(isNotNullOrUndefined(null)).toBe(false);
|
||||||
|
expect(isNotNullOrUndefined(undefined)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
import { metaSortKeySort, sortMethodMap } from '../sorting';
|
import {
|
||||||
|
ascendingNumberSort,
|
||||||
|
descendingNumberSort,
|
||||||
|
metaSortKeySort,
|
||||||
|
sortMethodMap,
|
||||||
|
} from '../sorting';
|
||||||
import { request, requestGroup, grpcRequest } from '../../models';
|
import { request, requestGroup, grpcRequest } from '../../models';
|
||||||
import {
|
import {
|
||||||
METHOD_DELETE,
|
METHOD_DELETE,
|
||||||
@ -340,4 +345,16 @@ describe('Sorting methods', () => {
|
|||||||
expect(metaSortKeySort({ metaSortKey: 1, _id: 2 }, { metaSortKey: 1, _id: 1 })).toBe(-1);
|
expect(metaSortKeySort({ metaSortKey: 1, _id: 2 }, { metaSortKey: 1, _id: 1 })).toBe(-1);
|
||||||
expect(metaSortKeySort({ metaSortKey: 1, _id: 1 }, { metaSortKey: 1, _id: 2 })).toBe(1);
|
expect(metaSortKeySort({ metaSortKey: 1, _id: 1 }, { metaSortKey: 1, _id: 2 })).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sorts by number', () => {
|
||||||
|
expect(ascendingNumberSort(1, 2)).toBe(-1);
|
||||||
|
expect(ascendingNumberSort(-2, 1)).toBe(-1);
|
||||||
|
expect(ascendingNumberSort(2, 1)).toBe(1);
|
||||||
|
expect(ascendingNumberSort(1, -2)).toBe(1);
|
||||||
|
|
||||||
|
expect(descendingNumberSort(1, 2)).toBe(1);
|
||||||
|
expect(descendingNumberSort(-2, 1)).toBe(1);
|
||||||
|
expect(descendingNumberSort(2, 1)).toBe(-1);
|
||||||
|
expect(descendingNumberSort(1, -2)).toBe(-1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -453,3 +453,7 @@ export function snapNumberToLimits(value: number, min?: number, max?: number): n
|
|||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isNotNullOrUndefined(obj: any): boolean {
|
||||||
|
return obj !== null && obj !== undefined;
|
||||||
|
}
|
||||||
|
@ -97,6 +97,14 @@ export const metaSortKeySort: SortFunction = (a, b) => {
|
|||||||
return a.metaSortKey < b.metaSortKey ? -1 : 1;
|
return a.metaSortKey < b.metaSortKey ? -1 : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ascendingNumberSort = (a: number, b: number): number => {
|
||||||
|
return a < b ? -1 : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const descendingNumberSort = (a: number, b: number): number => {
|
||||||
|
return ascendingNumberSort(b, a);
|
||||||
|
};
|
||||||
|
|
||||||
export const sortMethodMap: { [SortOrder]: SortFunction } = {
|
export const sortMethodMap: { [SortOrder]: SortFunction } = {
|
||||||
[SORT_NAME_ASC]: ascendingNameSort,
|
[SORT_NAME_ASC]: ascendingNameSort,
|
||||||
[SORT_NAME_DESC]: descendingNameSort,
|
[SORT_NAME_DESC]: descendingNameSort,
|
||||||
|
@ -19,6 +19,7 @@ import HelpTooltip from '../help-tooltip';
|
|||||||
import Link from '../base/link';
|
import Link from '../base/link';
|
||||||
import { trackEvent } from '../../../common/analytics';
|
import { trackEvent } from '../../../common/analytics';
|
||||||
import { docsGitSync } from '../../../common/documentation';
|
import { docsGitSync } from '../../../common/documentation';
|
||||||
|
import { isNotNullOrUndefined } from '../../../common/misc';
|
||||||
|
|
||||||
type Props = {|
|
type Props = {|
|
||||||
handleInitializeEntities: () => Promise<void>,
|
handleInitializeEntities: () => Promise<void>,
|
||||||
@ -68,11 +69,20 @@ class GitSyncDropdown extends React.PureComponent<Props, State> {
|
|||||||
|
|
||||||
// Clear cached items and return if no state
|
// Clear cached items and return if no state
|
||||||
if (!vcs.isInitialized() || !workspaceMeta.gitRepositoryId) {
|
if (!vcs.isInitialized() || !workspaceMeta.gitRepositoryId) {
|
||||||
await models.workspaceMeta.updateByParentId(workspace._id, {
|
// Don't update unnecessarily
|
||||||
cachedGitRepositoryBranch: null,
|
const needsUpdate = [
|
||||||
cachedGitLastAuthor: null,
|
workspaceMeta.cachedGitRepositoryBranch,
|
||||||
cachedGitLastCommitTime: null,
|
workspaceMeta.cachedGitLastAuthor,
|
||||||
});
|
workspaceMeta.cachedGitLastCommitTime,
|
||||||
|
].some(isNotNullOrUndefined);
|
||||||
|
|
||||||
|
if (needsUpdate) {
|
||||||
|
await models.workspaceMeta.updateByParentId(workspace._id, {
|
||||||
|
cachedGitRepositoryBranch: null,
|
||||||
|
cachedGitLastAuthor: null,
|
||||||
|
cachedGitLastCommitTime: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ import TimeFromNow from './time-from-now';
|
|||||||
import Highlight from './base/highlight';
|
import Highlight from './base/highlight';
|
||||||
import type { GlobalActivity } from '../../common/constants';
|
import type { GlobalActivity } from '../../common/constants';
|
||||||
|
|
||||||
import { fuzzyMatchAll, pluralize } from '../../common/misc';
|
import { fuzzyMatchAll, isNotNullOrUndefined, pluralize } from '../../common/misc';
|
||||||
import type {
|
import type {
|
||||||
HandleImportClipboardCallback,
|
HandleImportClipboardCallback,
|
||||||
HandleImportFileCallback,
|
HandleImportFileCallback,
|
||||||
@ -60,6 +60,8 @@ import RemoteWorkspacesDropdown from './dropdowns/remote-workspaces-dropdown';
|
|||||||
import SettingsButton from './buttons/settings-button';
|
import SettingsButton from './buttons/settings-button';
|
||||||
import AccountDropdown from './dropdowns/account-dropdown';
|
import AccountDropdown from './dropdowns/account-dropdown';
|
||||||
import { strings } from '../../common/strings';
|
import { strings } from '../../common/strings';
|
||||||
|
import { WorkspaceScopeKeys } from '../../models/workspace';
|
||||||
|
import { descendingNumberSort } from '../../common/sorting';
|
||||||
|
|
||||||
type Props = {|
|
type Props = {|
|
||||||
wrapperProps: WrapperProps,
|
wrapperProps: WrapperProps,
|
||||||
@ -316,7 +318,6 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
|||||||
const { activeActivity } = await models.workspaceMeta.getOrCreateByParentId(id);
|
const { activeActivity } = await models.workspaceMeta.getOrCreateByParentId(id);
|
||||||
|
|
||||||
if (!activeActivity || !isWorkspaceActivity(activeActivity)) {
|
if (!activeActivity || !isWorkspaceActivity(activeActivity)) {
|
||||||
// or migration or onboarding
|
|
||||||
handleSetActiveActivity(defaultActivity);
|
handleSetActiveActivity(defaultActivity);
|
||||||
} else {
|
} else {
|
||||||
handleSetActiveActivity(activeActivity);
|
handleSetActiveActivity(activeActivity);
|
||||||
@ -324,7 +325,7 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
|||||||
handleSetActiveWorkspace(id);
|
handleSetActiveWorkspace(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCard(w: Workspace) {
|
renderCard(workspace: Workspace): { card: React.Node, lastModifiedTimestamp: number } {
|
||||||
const {
|
const {
|
||||||
apiSpecs,
|
apiSpecs,
|
||||||
handleSetActiveWorkspace,
|
handleSetActiveWorkspace,
|
||||||
@ -334,7 +335,7 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
|||||||
|
|
||||||
const { filter } = this.state;
|
const { filter } = this.state;
|
||||||
|
|
||||||
const apiSpec = apiSpecs.find(s => s.parentId === w._id);
|
const apiSpec = apiSpecs.find(s => s.parentId === workspace._id);
|
||||||
|
|
||||||
let spec = null;
|
let spec = null;
|
||||||
let specFormat = null;
|
let specFormat = null;
|
||||||
@ -350,18 +351,25 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get cached branch from WorkspaceMeta
|
// Get cached branch from WorkspaceMeta
|
||||||
const workspaceMeta = workspaceMetas.find(wm => wm.parentId === w._id);
|
const workspaceMeta = workspaceMetas.find(wm => wm.parentId === workspace._id);
|
||||||
const lastActiveBranch = workspaceMeta ? workspaceMeta.cachedGitRepositoryBranch : null;
|
const lastActiveBranch = workspaceMeta ? workspaceMeta.cachedGitRepositoryBranch : null;
|
||||||
const lastCommitAuthor = workspaceMeta ? workspaceMeta.cachedGitLastAuthor : null;
|
const lastCommitAuthor = workspaceMeta ? workspaceMeta.cachedGitLastAuthor : null;
|
||||||
const lastCommitTime = workspaceMeta ? workspaceMeta.cachedGitLastCommitTime : null;
|
const lastCommitTime = workspaceMeta ? workspaceMeta.cachedGitLastCommitTime : null;
|
||||||
|
|
||||||
// WorkspaceMeta is a good proxy for last modified time
|
// WorkspaceMeta is a good proxy for last modified time
|
||||||
const workspaceModified = workspaceMeta ? workspaceMeta.modified : w.modified;
|
const workspaceModified = workspaceMeta ? workspaceMeta.modified : workspace.modified;
|
||||||
const modifiedLocally = apiSpec ? apiSpec.modified : workspaceModified;
|
const modifiedLocally =
|
||||||
|
apiSpec && workspace.scope === WorkspaceScopeKeys.designer
|
||||||
|
? apiSpec.modified
|
||||||
|
: workspaceModified;
|
||||||
|
|
||||||
let log = <TimeFromNow timestamp={modifiedLocally} />;
|
let log = <TimeFromNow timestamp={modifiedLocally} />;
|
||||||
let branch = lastActiveBranch;
|
let branch = lastActiveBranch;
|
||||||
if (w.scope === 'designer' && lastCommitTime && apiSpec?.modified > lastCommitTime) {
|
if (
|
||||||
|
workspace.scope === WorkspaceScopeKeys.designer &&
|
||||||
|
lastCommitTime &&
|
||||||
|
apiSpec?.modified > lastCommitTime
|
||||||
|
) {
|
||||||
// Show locally unsaved changes for spec
|
// Show locally unsaved changes for spec
|
||||||
// NOTE: this doesn't work for non-spec workspaces
|
// NOTE: this doesn't work for non-spec workspaces
|
||||||
branch = lastActiveBranch + '*';
|
branch = lastActiveBranch + '*';
|
||||||
@ -383,7 +391,7 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
|||||||
const docMenu = (
|
const docMenu = (
|
||||||
<DocumentCardDropdown
|
<DocumentCardDropdown
|
||||||
apiSpec={apiSpec}
|
apiSpec={apiSpec}
|
||||||
workspace={w}
|
workspace={workspace}
|
||||||
handleSetActiveWorkspace={handleSetActiveWorkspace}
|
handleSetActiveWorkspace={handleSetActiveWorkspace}
|
||||||
isLastWorkspace={workspaces.length === 1}>
|
isLastWorkspace={workspaces.length === 1}>
|
||||||
<SvgIcon icon="ellipsis" />
|
<SvgIcon icon="ellipsis" />
|
||||||
@ -394,9 +402,9 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
|||||||
let format: string = '';
|
let format: string = '';
|
||||||
let labelIcon = <i className="fa fa-bars" />;
|
let labelIcon = <i className="fa fa-bars" />;
|
||||||
let defaultActivity = ACTIVITY_DEBUG;
|
let defaultActivity = ACTIVITY_DEBUG;
|
||||||
let title = w.name;
|
let title = workspace.name;
|
||||||
|
|
||||||
if (w.scope === 'designer') {
|
if (workspace.scope === WorkspaceScopeKeys.designer) {
|
||||||
label = 'Document';
|
label = 'Document';
|
||||||
labelIcon = <i className="fa fa-file-o" />;
|
labelIcon = <i className="fa fa-file-o" />;
|
||||||
if (specFormat === 'openapi') {
|
if (specFormat === 'openapi') {
|
||||||
@ -421,7 +429,17 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const lastModifiedFrom = [
|
||||||
|
workspace?.modified,
|
||||||
|
workspaceMeta?.modified,
|
||||||
|
apiSpec?.modified,
|
||||||
|
workspaceMeta?.cachedGitLastCommitTime,
|
||||||
|
];
|
||||||
|
const lastModifiedTimestamp = lastModifiedFrom
|
||||||
|
.filter(isNotNullOrUndefined)
|
||||||
|
.sort(descendingNumberSort)[0];
|
||||||
|
|
||||||
|
const card = (
|
||||||
<Card
|
<Card
|
||||||
key={apiSpec._id}
|
key={apiSpec._id}
|
||||||
docBranch={branch && <Highlight search={filter} text={branch} />}
|
docBranch={branch && <Highlight search={filter} text={branch} />}
|
||||||
@ -438,9 +456,14 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
|||||||
docLog={log}
|
docLog={log}
|
||||||
docMenu={docMenu}
|
docMenu={docMenu}
|
||||||
docFormat={format}
|
docFormat={format}
|
||||||
onClick={() => this._handleClickCard(w._id, defaultActivity)}
|
onClick={() => this._handleClickCard(workspace._id, defaultActivity)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
card,
|
||||||
|
lastModifiedTimestamp,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCreateMenu() {
|
renderCreateMenu() {
|
||||||
@ -508,7 +531,11 @@ class WrapperHome extends React.PureComponent<Props, State> {
|
|||||||
const { filter } = this.state;
|
const { filter } = this.state;
|
||||||
|
|
||||||
// Render each card, removing all the ones that don't match the filter
|
// Render each card, removing all the ones that don't match the filter
|
||||||
const cards = workspaces.map(this.renderCard).filter(c => c !== null);
|
const cards = workspaces
|
||||||
|
.map(this.renderCard)
|
||||||
|
.filter(isNotNullOrUndefined)
|
||||||
|
.sort((a, b) => descendingNumberSort(a.lastModifiedTimestamp, b.lastModifiedTimestamp))
|
||||||
|
.map(c => c.card);
|
||||||
|
|
||||||
const countLabel = cards.length > 1 ? pluralize(strings.document) : strings.document;
|
const countLabel = cards.length > 1 ? pluralize(strings.document) : strings.document;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user