diff --git a/intuita-webview/src/main/App.css b/intuita-webview/src/main/App.css
index d0d4dbcf..fdab19d9 100644
--- a/intuita-webview/src/main/App.css
+++ b/intuita-webview/src/main/App.css
@@ -60,3 +60,8 @@
flex-direction: column;
align-items: center;
}
+
+.clearing-progress-ring {
+ height: 4em;
+ width: 100%;
+}
diff --git a/intuita-webview/src/main/CodemodRuns.tsx b/intuita-webview/src/main/CodemodRuns.tsx
index 79b527e2..e258b584 100644
--- a/intuita-webview/src/main/CodemodRuns.tsx
+++ b/intuita-webview/src/main/CodemodRuns.tsx
@@ -13,6 +13,8 @@ import { App as FileExplorer } from '../fileExplorer/App';
import { MainWebviewViewProps } from '../../../src/selectors/selectMainWebviewViewProps';
import { vscode } from '../shared/utilities/vscode';
import { useEffect, useMemo, useRef } from 'react';
+import { Command } from '../shared/types';
+import { VSCodeProgressRing } from '@vscode/webview-ui-toolkit/react';
export const CodemodRuns = (
props: MainWebviewViewProps & {
@@ -50,6 +52,22 @@ export const CodemodRuns = (
[props.panelGroupSettings],
);
+ const commands: (Command & { icon: string })[] = [];
+
+ if (props.clearingInProgress) {
+ commands.push({
+ icon: 'circle-slash',
+ title: 'Stop clearing',
+ command: 'intuita.stopStateClearing',
+ });
+ } else {
+ commands.push({
+ icon: 'clear-all',
+ title: 'Clear all',
+ command: 'intuita.clearState',
+ });
+ }
+
return (
{
event.preventDefault();
@@ -92,7 +104,11 @@ export const CodemodRuns = (
});
}}
>
-
+ {props.clearingInProgress ? (
+
+ ) : (
+
+ )}
;
+
+export const createClearStateCommand =
+ ({ fileService, store }: Dependencies) =>
+ async () => {
+ const state = store.getState();
+
+ store.dispatch(actions.clearState());
+
+ try {
+ const uris: Uri[] = [];
+
+ for (const job of Object.values(state.job.entities)) {
+ if (
+ !job ||
+ !doesJobAddNewFile(job.kind) ||
+ job.newContentUri === null ||
+ job.newContentUri.includes('.intuita/cases')
+ ) {
+ continue;
+ }
+
+ uris.push(Uri.parse(job.newContentUri));
+ }
+
+ await fileService.deleteFiles({ uris });
+ } catch (error) {
+ console.error(error);
+ }
+
+ try {
+ const casesDirectoryUri = Uri.parse(
+ join(homedir(), '.intuita', 'cases'),
+ );
+
+ const files = await workspace.fs.readDirectory(casesDirectoryUri);
+
+ const caseDirectoryUris = files
+ .filter(([, fileType]) => fileType === FileType.Directory)
+ .map(([name]) => Uri.joinPath(casesDirectoryUri, name));
+
+ await fileService.deleteDirectories({ uris: caseDirectoryUris });
+ } catch (error) {
+ console.error(error);
+ }
+
+ store.dispatch(actions.onStateCleared());
+ };
diff --git a/src/components/webview/MainProvider.ts b/src/components/webview/MainProvider.ts
index f7bdeb10..cec82ac4 100644
--- a/src/components/webview/MainProvider.ts
+++ b/src/components/webview/MainProvider.ts
@@ -218,7 +218,8 @@ export class MainViewProvider implements WebviewViewProvider {
if (
prevProps.activeTabId !== nextProps.activeTabId &&
- nextProps.activeTabId === 'codemodRuns'
+ nextProps.activeTabId === 'codemodRuns' &&
+ !nextProps.clearingInProgress
) {
this.__messageBus.publish({
kind: MessageKind.loadHomeDirectoryData,
diff --git a/src/data/slice.ts b/src/data/slice.ts
index d090c4e6..68bb4f5a 100644
--- a/src/data/slice.ts
+++ b/src/data/slice.ts
@@ -45,6 +45,7 @@ export const jobAdapter = createEntityAdapter({
export const getInitialState = (): RootState => {
return {
+ clearingInProgress: false,
codemod: codemodAdapter.getInitialState(),
privateCodemods: privateCodemodAdapter.getInitialState(),
case: caseAdapter.getInitialState(),
@@ -142,6 +143,8 @@ const rootSlice = createSlice({
jobAdapter.upsertMany(state.job, action.payload);
},
clearState(state) {
+ state.clearingInProgress = true;
+
caseAdapter.removeAll(state.case);
jobAdapter.removeAll(state.job);
@@ -157,6 +160,9 @@ const rootSlice = createSlice({
state.reviewedExplorerNodes = {};
state.focusedExplorerNodes = {};
},
+ onStateCleared(state) {
+ state.clearingInProgress = false;
+ },
setCodemods(state, action: PayloadAction>) {
codemodAdapter.setAll(state.codemod, action.payload);
},
diff --git a/src/extension.ts b/src/extension.ts
index 37628aaf..7fd3e291 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -31,7 +31,6 @@ import {
CodemodNodeHashDigest,
selectCodemodArguments,
} from './selectors/selectCodemodTree';
-import { doesJobAddNewFile } from './selectors/comparePersistedJobs';
import { buildHash, isNeitherNullNorUndefined } from './utilities';
import { mkdir, readFile, writeFile } from 'fs/promises';
import { homedir } from 'os';
@@ -47,6 +46,7 @@ import { parsePrivateCodemodsEnvelope } from './data/privateCodemodsEnvelopeSche
import { GlobalStateTokenStorage, UserService } from './components/userService';
import { HomeDirectoryService } from './data/readHomeDirectoryCases';
import { isLeft } from 'fp-ts/lib/Either';
+import { createClearStateCommand } from './commands/clearStateCommand';
export const enum SEARCH_PARAMS_KEYS {
ENGINE = 'engine',
@@ -849,30 +849,15 @@ export async function activate(context: vscode.ExtensionContext) {
);
context.subscriptions.push(
- vscode.commands.registerCommand('intuita.clearState', () => {
- const state = store.getState();
-
- const uris: vscode.Uri[] = [];
-
- for (const job of Object.values(state.job.entities)) {
- if (
- !job ||
- !doesJobAddNewFile(job.kind) ||
- job.newContentUri === null ||
- job.newContentUri.includes('.intuita/cases')
- ) {
- continue;
- }
-
- uris.push(vscode.Uri.parse(job.newContentUri));
- }
-
- store.dispatch(actions.clearState());
+ vscode.commands.registerCommand(
+ 'intuita.clearState',
+ createClearStateCommand({ fileService, store }),
+ ),
+ );
- messageBus.publish({
- kind: MessageKind.deleteDirectories,
- uris: [vscode.Uri.parse(join(homedir(), '.intuita', 'cases'))],
- });
+ context.subscriptions.push(
+ vscode.commands.registerCommand('intuita.stopStateClearing', () => {
+ store.dispatch(actions.onStateCleared());
}),
);
diff --git a/src/persistedState/codecs.ts b/src/persistedState/codecs.ts
index 3ff807a5..cd808b6c 100644
--- a/src/persistedState/codecs.ts
+++ b/src/persistedState/codecs.ts
@@ -53,6 +53,7 @@ export const panelGroupSettingsCodec = t.record(t.string, t.array(t.number));
export type PanelGroupSettings = t.TypeOf;
export const persistedStateCodecNew = buildTypeCodec({
+ clearingInProgress: withFallback(t.boolean, false),
case: buildCollectionCodec(caseCodec),
codemod: buildCollectionCodec(codemodEntryCodec),
privateCodemods: buildCollectionCodec(privateCodemodEntryCodec),
diff --git a/src/selectors/selectMainWebviewViewProps.ts b/src/selectors/selectMainWebviewViewProps.ts
index e48d55a9..75e0be52 100644
--- a/src/selectors/selectMainWebviewViewProps.ts
+++ b/src/selectors/selectMainWebviewViewProps.ts
@@ -39,6 +39,7 @@ export const selectMainWebviewViewProps = (
if (state.activeTabId === 'codemodRuns') {
return {
+ clearingInProgress: state.clearingInProgress,
activeTabId: state.activeTabId,
toaster: state.toaster,
applySelectedInProgress: state.applySelectedInProgress,