Only allow one open model alerts view (#3461)

This commit is contained in:
Shati Patel
2024-03-13 14:05:47 +00:00
committed by GitHub
parent 3005dacf4e
commit 84a8ffdad2
6 changed files with 102 additions and 6 deletions

View File

@@ -11,6 +11,9 @@ import type { App } from "../../common/app";
import { redactableError } from "../../common/errors";
import { extLogger } from "../../common/logging/vscode";
import { showAndLogExceptionWithTelemetry } from "../../common/logging";
import type { ModelingEvents } from "../modeling-events";
import type { ModelingStore } from "../modeling-store";
import type { DatabaseItem } from "../../databases/local-databases";
export class ModelAlertsView extends AbstractWebview<
ToModelAlertsMessage,
@@ -18,8 +21,15 @@ export class ModelAlertsView extends AbstractWebview<
> {
public static readonly viewType = "codeQL.modelAlerts";
public constructor(app: App) {
public constructor(
app: App,
private readonly modelingEvents: ModelingEvents,
private readonly modelingStore: ModelingStore,
private readonly dbItem: DatabaseItem,
) {
super(app);
this.registerToModelingEvents();
}
public async showView() {
@@ -40,7 +50,7 @@ export class ModelAlertsView extends AbstractWebview<
}
protected onPanelDispose(): void {
// Nothing to dispose
this.modelingStore.updateIsModelAlertsViewOpen(this.dbItem, false);
}
protected async onMessage(msg: FromModelAlertsMessage): Promise<void> {
@@ -64,4 +74,18 @@ export class ModelAlertsView extends AbstractWebview<
assertNever(msg);
}
}
public async focusView(): Promise<void> {
this.panel?.reveal();
}
private registerToModelingEvents() {
this.push(
this.modelingEvents.onFocusModelAlertsView(async (event) => {
if (event.dbUri === this.dbItem.databaseUri.toString()) {
await this.focusView();
}
}),
);
}
}

View File

@@ -107,8 +107,21 @@ export class ModelEvaluator extends DisposableObject {
}
public async openModelAlertsView() {
const view = new ModelAlertsView(this.app);
await view.showView();
if (this.modelingStore.isModelAlertsViewOpen(this.dbItem)) {
this.modelingEvents.fireFocusModelAlertsViewEvent(
this.dbItem.databaseUri.toString(),
);
return;
} else {
this.modelingStore.updateIsModelAlertsViewOpen(this.dbItem, true);
const view = new ModelAlertsView(
this.app,
this.modelingEvents,
this.modelingStore,
this.dbItem,
);
await view.showView();
}
}
private registerToModelingEvents() {

View File

@@ -65,6 +65,10 @@ interface FocusModelEditorEvent {
dbUri: string;
}
interface FocusModelAlertsViewEvent {
dbUri: string;
}
export class ModelingEvents extends DisposableObject {
public readonly onActiveDbChanged: AppEvent<void>;
public readonly onDbOpened: AppEvent<DatabaseItem>;
@@ -79,6 +83,7 @@ export class ModelingEvents extends DisposableObject {
public readonly onModelEvaluationRunChanged: AppEvent<ModelEvaluationRunChangedEvent>;
public readonly onRevealInModelEditor: AppEvent<RevealInModelEditorEvent>;
public readonly onFocusModelEditor: AppEvent<FocusModelEditorEvent>;
public readonly onFocusModelAlertsView: AppEvent<FocusModelAlertsViewEvent>;
private readonly onActiveDbChangedEventEmitter: AppEventEmitter<void>;
private readonly onDbOpenedEventEmitter: AppEventEmitter<DatabaseItem>;
@@ -93,6 +98,7 @@ export class ModelingEvents extends DisposableObject {
private readonly onModelEvaluationRunChangedEventEmitter: AppEventEmitter<ModelEvaluationRunChangedEvent>;
private readonly onRevealInModelEditorEventEmitter: AppEventEmitter<RevealInModelEditorEvent>;
private readonly onFocusModelEditorEventEmitter: AppEventEmitter<FocusModelEditorEvent>;
private readonly onFocusModelAlertsViewEventEmitter: AppEventEmitter<FocusModelAlertsViewEvent>;
constructor(app: App) {
super();
@@ -165,6 +171,11 @@ export class ModelingEvents extends DisposableObject {
app.createEventEmitter<FocusModelEditorEvent>(),
);
this.onFocusModelEditor = this.onFocusModelEditorEventEmitter.event;
this.onFocusModelAlertsViewEventEmitter = this.push(
app.createEventEmitter<FocusModelAlertsViewEvent>(),
);
this.onFocusModelAlertsView = this.onFocusModelAlertsViewEventEmitter.event;
}
public fireActiveDbChangedEvent() {
@@ -286,4 +297,8 @@ export class ModelingEvents extends DisposableObject {
dbUri,
});
}
public fireFocusModelAlertsViewEvent(dbUri: string) {
this.onFocusModelAlertsViewEventEmitter.fire({ dbUri });
}
}

View File

@@ -20,6 +20,7 @@ interface InternalDbModelingState {
selectedMethod: Method | undefined;
selectedUsage: Usage | undefined;
modelEvaluationRun: ModelEvaluationRun | undefined;
isModelAlertsViewOpen: boolean;
}
export interface DbModelingState {
@@ -34,6 +35,7 @@ export interface DbModelingState {
readonly selectedMethod: Method | undefined;
readonly selectedUsage: Usage | undefined;
readonly modelEvaluationRun: ModelEvaluationRun | undefined;
readonly isModelAlertsViewOpen: boolean;
}
export interface SelectedMethodDetails {
@@ -71,6 +73,7 @@ export class ModelingStore extends DisposableObject {
selectedUsage: undefined,
inProgressMethods: new Set(),
modelEvaluationRun: undefined,
isModelAlertsViewOpen: false,
});
this.modelingEvents.fireDbOpenedEvent(databaseItem);
@@ -498,4 +501,23 @@ export class ModelingStore extends DisposableObject {
state.modelEvaluationRun,
);
}
public isModelAlertsViewOpen(dbItem: DatabaseItem): boolean {
return this.getState(dbItem).isModelAlertsViewOpen ?? false;
}
private changeIsModelAlertsViewOpen(
dbItem: DatabaseItem,
updateState: (state: InternalDbModelingState) => void,
) {
const state = this.getState(dbItem);
updateState(state);
}
public updateIsModelAlertsViewOpen(dbItem: DatabaseItem, isOpen: boolean) {
this.changeIsModelAlertsViewOpen(dbItem, (state) => {
state.isModelAlertsViewOpen = isOpen;
});
}
}

View File

@@ -34,7 +34,8 @@ export const ModelEvaluation = ({
const shouldShowStopButton = !shouldShowEvaluateButton;
const shouldShowEvaluationRunLink = !!evaluationRun;
const shouldShowEvaluationRunLink =
!!evaluationRun && evaluationRun.variantAnalysis;
const customModelsExist = Object.values(modeledMethods).some(
(methods) => methods.filter((m) => m.type !== "none").length > 0,

View File

@@ -98,7 +98,7 @@ describe(ModelEvaluation.name, () => {
expect(screen.queryByText("Stop evaluation")).not.toBeInTheDocument();
});
it("renders 'Stop evaluation' button and 'Evaluation run' link when there is an in progress evaluation", () => {
it("renders 'Stop evaluation' button when there is an in progress evaluation, but no variant analysis yet", () => {
render({
evaluationRun: {
isPreparing: true,
@@ -112,6 +112,27 @@ describe(ModelEvaluation.name, () => {
stopEvaluationButton?.getElementsByTagName("input")[0],
).toBeEnabled();
expect(screen.queryByText("Evaluation run")).not.toBeInTheDocument();
expect(screen.queryByText("Evaluate")).not.toBeInTheDocument();
});
it("renders 'Stop evaluation' button and 'Evaluation run' link when there is an in progress evaluation with variant analysis", () => {
render({
evaluationRun: {
isPreparing: false,
variantAnalysis: createMockVariantAnalysis({
status: VariantAnalysisStatus.InProgress,
}),
},
});
const stopEvaluationButton = screen.queryByText("Stop evaluation");
expect(stopEvaluationButton).toBeInTheDocument();
expect(
stopEvaluationButton?.getElementsByTagName("input")[0],
).toBeEnabled();
expect(screen.queryByText("Evaluation run")).toBeInTheDocument();
expect(screen.queryByText("Evaluate")).not.toBeInTheDocument();