mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
CB-6115 Add dispose to FormState instances (#3317)
* CB-6115 adds dispose API for forms * CB-6115 adds git workspace helper extension for repo recommendations * CB-6115 pr fixes * CB-6115 adds dispose methods to tables * CB-6115 eslint fix * CB-6115 fixes dispose leaks for freshly created states * CB-6115 fixes infinite loading of state --------- Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com> Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
This commit is contained in:
co-authored by
mr-anton-t
Daria Marutkina
parent
06e2aa9f0e
commit
d9a5ea053b
@@ -174,4 +174,5 @@ export abstract class FormPart<TPartState, TFormState = any> implements IFormPar
|
||||
|
||||
protected abstract loader(): Promise<void>;
|
||||
protected abstract saveChanges(data: IFormState<TFormState>, contexts: IExecutionContextProvider<IFormState<TFormState>>): Promise<void>;
|
||||
dispose(): void | Promise<void> {}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export class FormState<TState> implements IFormState<TState> {
|
||||
statusMessage: string | string[] | null;
|
||||
statusType: ENotificationType | null;
|
||||
|
||||
promise: Promise<any> | null;
|
||||
savingPromise: Promise<any> | null;
|
||||
|
||||
get isDisabled(): boolean {
|
||||
return this.partsValues.some(part => part.isSaving || part?.isLoading?.());
|
||||
@@ -48,6 +48,7 @@ export class FormState<TState> implements IFormState<TState> {
|
||||
readonly submitTask: IExecutor<IFormState<TState>>;
|
||||
readonly formatTask: IExecutor<IFormState<TState>>;
|
||||
readonly validationTask: IExecutor<IFormState<TState>>;
|
||||
readonly disposeTask: IExecutor<IFormState<TState>>;
|
||||
|
||||
constructor(serviceProvider: IServiceProvider, service: FormBaseService<TState, any>, state: TState) {
|
||||
this.id = uuid();
|
||||
@@ -61,7 +62,7 @@ export class FormState<TState> implements IFormState<TState> {
|
||||
this.statusMessage = null;
|
||||
this.statusType = null;
|
||||
|
||||
this.promise = null;
|
||||
this.savingPromise = null;
|
||||
|
||||
this.formStateTask = new Executor<TState>(state, () => true);
|
||||
this.formStateTask.addCollection(service.onState).addPostHandler(this.updateFormState.bind(this));
|
||||
@@ -78,6 +79,8 @@ export class FormState<TState> implements IFormState<TState> {
|
||||
this.submitTask = new Executor(this as IFormState<TState>, () => true);
|
||||
this.submitTask.addCollection(service.onSubmit).before(this.validationTask);
|
||||
|
||||
this.disposeTask = new Executor(this as IFormState<TState>, () => true);
|
||||
|
||||
this.dataContext.set(DATA_CONTEXT_LOADABLE_STATE, loadableStateContext(), this.id);
|
||||
this.dataContext.set(DATA_CONTEXT_FORM_STATE, this, this.id);
|
||||
dataContextAddDIProvider(this.dataContext, serviceProvider, this.id);
|
||||
@@ -85,7 +88,7 @@ export class FormState<TState> implements IFormState<TState> {
|
||||
makeObservable<this, 'updateFormState'>(this, {
|
||||
mode: observable,
|
||||
parts: observable.ref,
|
||||
promise: observable.ref,
|
||||
savingPromise: observable.ref,
|
||||
state: observable,
|
||||
isSaving: computed,
|
||||
exception: computed,
|
||||
@@ -171,14 +174,18 @@ export class FormState<TState> implements IFormState<TState> {
|
||||
|
||||
async save(): Promise<boolean> {
|
||||
try {
|
||||
const context = await this.submitTask.execute(this);
|
||||
this.savingPromise = this.submitTask.execute(this);
|
||||
const context = await this.savingPromise;
|
||||
|
||||
if (ExecutorInterrupter.isInterrupted(context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (exception: any) {}
|
||||
} catch (exception: any) {
|
||||
} finally {
|
||||
this.savingPromise = null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -202,4 +209,16 @@ export class FormState<TState> implements IFormState<TState> {
|
||||
this.statusMessage = context.statusMessage;
|
||||
this.statusType = context.statusType;
|
||||
}
|
||||
|
||||
async dispose(): Promise<void> {
|
||||
if (this.savingPromise) {
|
||||
await this.savingPromise;
|
||||
}
|
||||
|
||||
for (const part of this.parts.values()) {
|
||||
await part.dispose();
|
||||
}
|
||||
|
||||
await this.disposeTask.execute(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,5 @@ export interface IFormPart<TState> extends ILoadableState {
|
||||
|
||||
load(): Promise<void>;
|
||||
reset(): void;
|
||||
dispose(): void | Promise<void>;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export interface IFormState<TState> {
|
||||
readonly isDisabled: boolean;
|
||||
readonly exception: Error | (Error | null)[] | null;
|
||||
|
||||
readonly promise: Promise<any> | null;
|
||||
readonly savingPromise: Promise<any> | null;
|
||||
|
||||
readonly statusMessage: string | string[] | null;
|
||||
readonly statusType: ENotificationType | null;
|
||||
@@ -49,4 +49,5 @@ export interface IFormState<TState> {
|
||||
save(): Promise<boolean>;
|
||||
reset(): void;
|
||||
cancel(): void;
|
||||
dispose(): void | Promise<void>;
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2020-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -95,6 +95,7 @@ export class ConfigurationWizardPagesBootstrapService extends Bootstrap {
|
||||
onLoad: () => {
|
||||
this.serverConfigurationFormStateManager.create();
|
||||
},
|
||||
onDeActivate: this.serverConfigurationFormStateManager.destroy.bind(this.serverConfigurationFormStateManager),
|
||||
canDeActivate: async configurationWizard => {
|
||||
const state = this.serverConfigurationFormStateManager.formState;
|
||||
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2020-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -38,6 +38,7 @@ export class ServerConfigurationFormStateManager {
|
||||
|
||||
destroy() {
|
||||
if (this.formState) {
|
||||
this.formState?.dispose();
|
||||
this.formState = null;
|
||||
}
|
||||
}
|
||||
|
||||
+10
-2
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2020-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { useRef } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
import { IServiceProvider, useService } from '@cloudbeaver/core-di';
|
||||
|
||||
@@ -18,11 +18,19 @@ export function useTeamsAdministrationFormState(id: string | null, configure?: (
|
||||
const ref = useRef<null | TeamsAdministrationFormState>(null);
|
||||
|
||||
if (ref.current?.state.teamId !== id) {
|
||||
ref.current?.dispose();
|
||||
ref.current = new TeamsAdministrationFormState(serviceProvider, service, {
|
||||
teamId: id,
|
||||
});
|
||||
configure?.(ref.current);
|
||||
}
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
ref.current?.dispose();
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
+7
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2020-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,6 +39,7 @@ export class CreateTeamService {
|
||||
}
|
||||
|
||||
fillData(): void {
|
||||
this.dispose();
|
||||
this.data = new TeamsAdministrationFormState(this.serviceProvider, this.service, {
|
||||
teamId: null,
|
||||
});
|
||||
@@ -47,4 +48,9 @@ export class CreateTeamService {
|
||||
create(): void {
|
||||
this.teamsAdministrationNavService.navToCreate();
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.data?.dispose();
|
||||
this.data = null;
|
||||
}
|
||||
}
|
||||
|
||||
+15
-9
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2020-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -59,16 +59,12 @@ export class UsersAdministrationService extends Bootstrap {
|
||||
},
|
||||
{
|
||||
name: EUsersAdministrationSub.Users,
|
||||
onDeActivate: this.cancelCreate.bind(this),
|
||||
onDeActivate: this.cancelUserCreate.bind(this),
|
||||
},
|
||||
{
|
||||
name: EUsersAdministrationSub.Teams,
|
||||
onActivate: this.loadTeams.bind(this),
|
||||
onDeActivate: (param, configurationWizard, outside) => {
|
||||
if (outside) {
|
||||
this.teamsResource.cleanNewFlags();
|
||||
}
|
||||
},
|
||||
onDeActivate: this.cancelTeamCreate.bind(this),
|
||||
},
|
||||
],
|
||||
defaultSub: EUsersAdministrationSub.Users,
|
||||
@@ -78,7 +74,7 @@ export class UsersAdministrationService extends Bootstrap {
|
||||
this.userDetailsInfoPlaceholder.add(UserCredentialsList, 0);
|
||||
}
|
||||
|
||||
private async cancelCreate(param: string | null, configurationWizard: boolean, outside: boolean) {
|
||||
private cancelUserCreate(param: string | null, configurationWizard: boolean, outside: boolean) {
|
||||
if (param === 'create') {
|
||||
this.createUserService.close();
|
||||
}
|
||||
@@ -88,7 +84,17 @@ export class UsersAdministrationService extends Bootstrap {
|
||||
}
|
||||
}
|
||||
|
||||
private async loadTeams(param: string | null) {
|
||||
private cancelTeamCreate(param: string | null, configurationWizard: boolean, outside: boolean) {
|
||||
if (param === 'create') {
|
||||
this.createTeamService.dispose();
|
||||
}
|
||||
|
||||
if (outside) {
|
||||
this.teamsResource.cleanNewFlags();
|
||||
}
|
||||
}
|
||||
|
||||
private loadTeams(param: string | null) {
|
||||
if (param === 'create') {
|
||||
this.createTeamService.fillData();
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2020-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -55,6 +55,7 @@ export class CreateUserService {
|
||||
}
|
||||
|
||||
clearUserTemplate(): void {
|
||||
this.state?.dispose();
|
||||
this.state = null;
|
||||
}
|
||||
|
||||
|
||||
+12
-3
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2024 DBeaver Corp and others
|
||||
* Copyright (C) 2020-2025 DBeaver Corp and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
import { useRef } from 'react';
|
||||
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
import { IServiceProvider, useService } from '@cloudbeaver/core-di';
|
||||
|
||||
@@ -17,12 +18,20 @@ export function useAdministrationUserFormState(id: string | null, configure?: (s
|
||||
const serviceProvider = useService(IServiceProvider);
|
||||
const ref = useRef<null | AdministrationUserFormState>(null);
|
||||
|
||||
if (ref.current?.id !== id) {
|
||||
if (ref.current?.state.userId !== id) {
|
||||
ref.current?.dispose();
|
||||
ref.current = new AdministrationUserFormState(serviceProvider, service, {
|
||||
userId: id,
|
||||
});
|
||||
configure?.(ref.current);
|
||||
}
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
ref.current?.dispose();
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user