mirror of
https://github.com/Kong/insomnia
synced 2024-11-07 22:30:15 +00:00
feat: display uncommit&unpush change - [INS-4138] (#7816)
* feat: display uncommit * modify workspaceMeta model * git sync uncommit * uncommit&unpush ui * add dot for workspace card * UI: replace icon color * move workspace update operation to loader&action * del log Co-authored-by: James Gatz <jamesgatzos@gmail.com> --------- Co-authored-by: James Gatz <jamesgatzos@gmail.com>
This commit is contained in:
parent
df5729d6ae
commit
76976db4dc
@ -19,6 +19,8 @@ export interface BaseWorkspaceMeta {
|
||||
gitRepositoryId: string | null;
|
||||
parentId: string | null;
|
||||
pushSnapshotOnInitialize: boolean;
|
||||
hasUncommittedChanges: boolean;
|
||||
hasUnpushedChanges: boolean;
|
||||
}
|
||||
|
||||
export type WorkspaceMeta = BaseWorkspaceMeta & BaseModel;
|
||||
@ -40,6 +42,8 @@ export function init(): BaseWorkspaceMeta {
|
||||
gitRepositoryId: null,
|
||||
parentId: null,
|
||||
pushSnapshotOnInitialize: false,
|
||||
hasUncommittedChanges: false,
|
||||
hasUnpushedChanges: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -107,3 +107,8 @@ export interface Status {
|
||||
stage: Stage;
|
||||
unstaged: Record<DocumentKey, StageEntry>;
|
||||
}
|
||||
|
||||
export interface Compare {
|
||||
ahead: number;
|
||||
behind: number;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import { deleteKeys, resetKeys, shouldIgnoreKey } from '../ignore-keys';
|
||||
import { deterministicStringify } from '../lib/deterministicStringify';
|
||||
import type {
|
||||
Branch,
|
||||
Compare,
|
||||
DocumentKey,
|
||||
MergeConflict,
|
||||
Snapshot,
|
||||
@ -241,10 +242,7 @@ export function threeWayMerge(
|
||||
export function compareBranches(
|
||||
a: Branch | null,
|
||||
b: Branch | null,
|
||||
): {
|
||||
ahead: number;
|
||||
behind: number;
|
||||
} {
|
||||
): Compare {
|
||||
const snapshotsA = a ? a.snapshots : [];
|
||||
const snapshotsB = b ? b.snapshots : [];
|
||||
const latestA = snapshotsA[snapshotsA.length - 1] || null;
|
||||
|
@ -9,6 +9,8 @@ import type { GitRepository } from '../../../models/git-repository';
|
||||
import { deleteGitRepository } from '../../../models/helpers/git-repository-operations';
|
||||
import { getOauth2FormatName } from '../../../sync/git/utils';
|
||||
import type {
|
||||
GitCanPushLoaderData,
|
||||
GitChangesLoaderData,
|
||||
GitFetchLoaderData,
|
||||
GitRepoLoaderData,
|
||||
GitStatusResult,
|
||||
@ -56,6 +58,8 @@ export const GitSyncDropdown: FC<Props> = ({ gitRepository, isInsomniaSyncEnable
|
||||
const gitRepoDataFetcher = useFetcher<GitRepoLoaderData>();
|
||||
const gitFetchFetcher = useFetcher<GitFetchLoaderData>();
|
||||
const gitStatusFetcher = useFetcher<GitStatusResult>();
|
||||
const gitChangesFetcher = useFetcher<GitChangesLoaderData>();
|
||||
const gitCanPushFetcher = useFetcher<GitCanPushLoaderData>();
|
||||
|
||||
const loadingPush = gitPushFetcher.state === 'loading';
|
||||
const loadingPull = gitPullFetcher.state === 'loading';
|
||||
@ -81,6 +85,18 @@ export const GitSyncDropdown: FC<Props> = ({ gitRepository, isInsomniaSyncEnable
|
||||
workspaceId,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (gitRepository?.uri && gitRepository?._id && gitChangesFetcher.state === 'idle' && !gitChangesFetcher.data && gitRepoDataFetcher.data) {
|
||||
gitChangesFetcher.load(`/organization/${organizationId}/project/${projectId}/workspace/${workspaceId}/git/changes`);
|
||||
}
|
||||
}, [gitChangesFetcher, gitRepoDataFetcher.data, gitRepository?._id, gitRepository?.uri, organizationId, projectId, workspaceId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (gitRepository?.uri && gitRepository?._id && gitCanPushFetcher.state === 'idle' && !gitCanPushFetcher.data && gitRepoDataFetcher.data) {
|
||||
gitCanPushFetcher.load(`/organization/${organizationId}/project/${projectId}/workspace/${workspaceId}/git/can-push`);
|
||||
}
|
||||
}, [gitCanPushFetcher, gitRepoDataFetcher.data, gitRepository?._id, gitRepository?.uri, organizationId, projectId, workspaceId]);
|
||||
|
||||
// Only fetch the repo status if we have a repo uri and we don't have the status already
|
||||
const shouldFetchGitRepoStatus = Boolean(gitRepository?.uri && gitRepository?._id && gitStatusFetcher.state === 'idle' && !gitStatusFetcher.data && gitRepoDataFetcher.data);
|
||||
|
||||
|
@ -853,6 +853,11 @@ async function renderApp() {
|
||||
loader: async (...args) =>
|
||||
(await import('./routes/git-actions')).gitChangesLoader(...args),
|
||||
},
|
||||
{
|
||||
path: 'can-push',
|
||||
loader: async (...args) =>
|
||||
(await import('./routes/git-actions')).canPushLoader(...args),
|
||||
},
|
||||
{
|
||||
path: 'log',
|
||||
loader: async (...args) =>
|
||||
|
@ -302,7 +302,10 @@ export const gitChangesLoader: LoaderFunction = async ({
|
||||
const branch = await GitVCS.getCurrentBranch();
|
||||
try {
|
||||
const { changes, statusNames } = await getGitChanges(GitVCS, workspace);
|
||||
|
||||
// update workspace meta with git sync data, use for show uncommit changes on collection card
|
||||
models.workspaceMeta.updateByParentId(workspaceId, {
|
||||
hasUncommittedChanges: changes.length > 0,
|
||||
});
|
||||
return {
|
||||
branch,
|
||||
changes,
|
||||
@ -318,6 +321,35 @@ export const gitChangesLoader: LoaderFunction = async ({
|
||||
}
|
||||
};
|
||||
|
||||
export interface GitCanPushLoaderData {
|
||||
canPush: boolean;
|
||||
}
|
||||
|
||||
export const canPushLoader: LoaderFunction = async ({ params }): Promise<GitCanPushLoaderData> => {
|
||||
const { workspaceId } = params;
|
||||
invariant(workspaceId, 'Workspace ID is required');
|
||||
|
||||
const workspaceMeta = await models.workspaceMeta.getByParentId(workspaceId);
|
||||
|
||||
const repoId = workspaceMeta?.gitRepositoryId;
|
||||
|
||||
invariant(repoId, 'Workspace is not linked to a git repository');
|
||||
|
||||
const gitRepository = await models.gitRepository.getById(repoId);
|
||||
|
||||
invariant(gitRepository, 'Git Repository not found');
|
||||
let canPush = false;
|
||||
try {
|
||||
canPush = await GitVCS.canPush(gitRepository.credentials);
|
||||
// update workspace meta with git sync data, use for show unpushed changes on collection card
|
||||
models.workspaceMeta.update(workspaceMeta, {
|
||||
hasUnpushedChanges: canPush,
|
||||
});
|
||||
} catch (err) { }
|
||||
|
||||
return { canPush };
|
||||
};
|
||||
|
||||
// Actions
|
||||
type CloneGitActionResult =
|
||||
| Response
|
||||
|
@ -263,6 +263,8 @@ export interface InsomniaFile {
|
||||
mockServer?: MockServer;
|
||||
workspace?: Workspace;
|
||||
apiSpec?: ApiSpec;
|
||||
hasUncommittedChanges?: boolean;
|
||||
hasUnpushedChanges?: boolean;
|
||||
}
|
||||
|
||||
export interface ProjectIdLoaderData {
|
||||
@ -371,9 +373,10 @@ async function getAllLocalFiles({
|
||||
mockServer,
|
||||
apiSpec,
|
||||
workspace,
|
||||
hasUncommittedChanges: workspaceMeta?.hasUncommittedChanges,
|
||||
hasUnpushedChanges: workspaceMeta?.hasUnpushedChanges,
|
||||
};
|
||||
});
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
@ -1008,6 +1011,10 @@ const ProjectRoute: FC = () => {
|
||||
}
|
||||
}, [projectId]);
|
||||
|
||||
const showUnCommitOrUnpushIndicator = useMemo(() => {
|
||||
return filesWithPresence.some(file => file?.hasUncommittedChanges || file?.hasUnpushedChanges);
|
||||
}, [filesWithPresence]);
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<Fragment>
|
||||
@ -1123,6 +1130,7 @@ const ProjectRoute: FC = () => {
|
||||
icon={
|
||||
isRemoteProject(item) ? 'globe-americas' : 'laptop'
|
||||
}
|
||||
className={(showUnCommitOrUnpushIndicator && item._id === activeProject?._id) ? 'text-[--color-warning]' : ''}
|
||||
/>
|
||||
<span className="truncate">{item.name}</span>
|
||||
<span className="flex-1" />
|
||||
@ -1453,6 +1461,14 @@ const ProjectRoute: FC = () => {
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{(item.hasUncommittedChanges || item.hasUnpushedChanges) && (
|
||||
<div className="text-sm text-[--color-warning] flex items-center gap-2">
|
||||
<div className='rounded-full bg-[--color-warning] w-3 h-3 flex-shrink-0' />
|
||||
<span>
|
||||
{item.hasUncommittedChanges ? 'Uncommitted changes' : 'Unpushed changes'}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</GridListItem>
|
||||
);
|
||||
|
@ -19,6 +19,7 @@ import type { WebSocketRequest } from '../../models/websocket-request';
|
||||
import { scopeToActivity, type Workspace } from '../../models/workspace';
|
||||
import type {
|
||||
BackendProject,
|
||||
Compare,
|
||||
Snapshot,
|
||||
Status,
|
||||
StatusCandidate,
|
||||
@ -250,14 +251,11 @@ interface SyncData {
|
||||
historyCount: number;
|
||||
status: Status;
|
||||
syncItems: StatusCandidate[];
|
||||
compare: {
|
||||
ahead: number;
|
||||
behind: number;
|
||||
};
|
||||
compare: Compare;
|
||||
}
|
||||
|
||||
const remoteBranchesCache: Record<string, string[]> = {};
|
||||
const remoteCompareCache: Record<string, { ahead: number; behind: number }> =
|
||||
const remoteCompareCache: Record<string, Compare> =
|
||||
{};
|
||||
const remoteBackendProjectsCache: Record<string, BackendProject[]> = {};
|
||||
|
||||
@ -332,17 +330,29 @@ export const syncDataLoader: LoaderFunction = async ({
|
||||
remoteBranches = (
|
||||
remoteBranchesCache[workspaceId] || (await vcs.getRemoteBranchNames())
|
||||
).sort();
|
||||
compare =
|
||||
remoteCompareCache[workspaceId] || (await vcs.compareRemoteBranch());
|
||||
const remoteBackendProjects =
|
||||
remoteBackendProjectsCache[workspaceId] ||
|
||||
(await vcs.remoteBackendProjects({
|
||||
teamId: project.parentId,
|
||||
teamProjectId: project.remoteId,
|
||||
}));
|
||||
remoteBranchesCache[workspaceId] = remoteBranches;
|
||||
remoteCompareCache[workspaceId] = compare;
|
||||
remoteBackendProjectsCache[workspaceId] = remoteBackendProjects;
|
||||
compare = remoteCompareCache[workspaceId] || (await vcs.compareRemoteBranch());
|
||||
const remoteBackendProjects =
|
||||
remoteBackendProjectsCache[workspaceId] ||
|
||||
(await vcs.remoteBackendProjects({
|
||||
teamId: project.parentId,
|
||||
teamProjectId: project.remoteId,
|
||||
}));
|
||||
remoteBranchesCache[workspaceId] = remoteBranches;
|
||||
remoteCompareCache[workspaceId] = compare;
|
||||
remoteBackendProjectsCache[workspaceId] = remoteBackendProjects;
|
||||
|
||||
let hasUncommittedChanges = false;
|
||||
if (status?.unstaged && Object.keys(status.unstaged).length > 0) {
|
||||
hasUncommittedChanges = true;
|
||||
}
|
||||
if (status?.stage && Object.keys(status.stage).length > 0) {
|
||||
hasUncommittedChanges = true;
|
||||
}
|
||||
// update workspace meta with sync data, use for show unpushed changes on collection card
|
||||
models.workspaceMeta.updateByParentId(workspaceId, {
|
||||
hasUncommittedChanges,
|
||||
hasUnpushedChanges: compare?.ahead > 0,
|
||||
});
|
||||
} catch (e) { }
|
||||
|
||||
return {
|
||||
|
Loading…
Reference in New Issue
Block a user