Skip to content

Commit c5d7ea6

Browse files
Update Plugins Manager (close esrlabs#2278)
1 parent 25a242b commit c5d7ea6

File tree

7 files changed

+157
-26
lines changed

7 files changed

+157
-26
lines changed

application/client/src/app/ui/styles/material.less

+11
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,17 @@ button[mat-stroked-button][disabled] {
8383
}
8484
}
8585
}
86+
button[mat-stroked-button] {
87+
& .mdc-button__label {
88+
white-space: nowrap;
89+
display: flex;
90+
flex-direction: row;
91+
align-items: center;
92+
& mat-spinner {
93+
margin-right: 16px;
94+
}
95+
}
96+
}
8697

8798
& mat-error {
8899
color: @scheme-color-warning!important;

application/client/src/app/ui/tabs/plugins/component.ts

+36-13
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { ChangesDetector } from '@ui/env/extentions/changes';
55
import { Provider } from './provider';
66
import { Target } from './list/component';
77
import { bridge } from '@service/bridge';
8+
import { Notification, notifications } from '@ui/service/notifications';
9+
import { error } from '@platform/log/utils';
810

911
@Component({
1012
selector: 'app-tabs-plugins-manager',
@@ -16,7 +18,6 @@ import { bridge } from '@service/bridge';
1618
@Ilc()
1719
export class PluginsManager extends ChangesDetector implements AfterContentInit, OnDestroy {
1820
public provider: Provider = new Provider();
19-
2021
public get Target(): typeof Target {
2122
return Target;
2223
}
@@ -36,27 +37,49 @@ export class PluginsManager extends ChangesDetector implements AfterContentInit,
3637
this.provider.subjects.get().selected.subscribe(() => {
3738
this.detectChanges();
3839
}),
40+
this.provider.subjects.get().add.subscribe(() => {
41+
this.detectChanges();
42+
}),
43+
this.provider.subjects.get().remove.subscribe(() => {
44+
this.detectChanges();
45+
}),
3946
);
4047
this.provider.load();
4148
}
4249

4350
public async reload() {
51+
if (this.provider.isBusy()) {
52+
return;
53+
}
4454
await this.provider.load(true);
4555
}
4656

4757
public async addPlugin() {
48-
await bridge
49-
.folders()
50-
.select()
51-
.then((dirs: string[]) => {
52-
if (dirs.length === 0) {
53-
return Promise.resolve();
54-
}
55-
return this.provider.addPlugin(dirs[0]);
56-
})
57-
.catch((err: Error) => {
58-
console.error(`Error while opening folders: ${err.message}`);
59-
});
58+
if (this.provider.isBusy()) {
59+
return;
60+
}
61+
try {
62+
const folder = await bridge.folders().select();
63+
if (folder.length === 0) {
64+
return;
65+
}
66+
await this.provider.addPlugin(folder[0]);
67+
this.reload();
68+
notifications.notify(
69+
new Notification({
70+
message: `New plugin has been added`,
71+
actions: [],
72+
}),
73+
);
74+
} catch (err) {
75+
notifications.notify(
76+
new Notification({
77+
message: this.log().error(`Fail to add plugin: ${error(err)}`),
78+
actions: [],
79+
pinned: true,
80+
}),
81+
);
82+
}
6083
}
6184
}
6285

application/client/src/app/ui/tabs/plugins/details/component.ts

+29-6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import { micromark } from 'micromark';
1616
import { PluginDescription } from '../desc';
1717
import { Provider } from '../provider';
1818
import { PluginRunData, PluginLogLevel } from '@platform/types/bindings/plugins';
19+
import { lockers, Locker } from '@ui/service/lockers';
20+
import { Notification, notifications } from '@ui/service/notifications';
1921

2022
import * as dom from '@ui/env/dom';
2123

@@ -164,10 +166,7 @@ export class Details extends ChangesDetector implements AfterViewInit, AfterCont
164166
// TODO: safe openening URL
165167
}
166168

167-
constructor(
168-
cdRef: ChangeDetectorRef,
169-
protected readonly sanitizer: DomSanitizer,
170-
) {
169+
constructor(cdRef: ChangeDetectorRef, protected readonly sanitizer: DomSanitizer) {
171170
super(cdRef);
172171
}
173172

@@ -202,11 +201,35 @@ export class Details extends ChangesDetector implements AfterViewInit, AfterCont
202201
};
203202
}
204203

205-
public async removePlugin(): Promise<void> {
204+
public removePlugin() {
206205
if (this.plugin.path === undefined) {
207206
return;
208207
}
209-
await this.provider.removePlugin(this.plugin.path.filename);
208+
const lock = lockers.lock(new Locker(true, `Removing plugin...`), {
209+
closable: false,
210+
});
211+
this.provider
212+
.removePlugin(this.plugin.path.filename)
213+
.then(() => {
214+
notifications.notify(
215+
new Notification({
216+
message: `Plugin has been removed`,
217+
actions: [],
218+
}),
219+
);
220+
})
221+
.catch((err: Error) => {
222+
notifications.notify(
223+
new Notification({
224+
message: `Fail to remove plugin: ${err.message}`,
225+
actions: [],
226+
pinned: true,
227+
}),
228+
);
229+
})
230+
.finally(() => {
231+
lock.popup.close();
232+
});
210233
}
211234
}
212235

application/client/src/app/ui/tabs/plugins/details/template.html

+5-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616
>
1717
Inspect
1818
</button>
19-
<button class="flat-codicon-button" [disabled]="loading" (click)="removePlugin()">
19+
<button
20+
class="flat-codicon-button"
21+
[disabled]="loading || provider.isBusy()"
22+
(click)="removePlugin()"
23+
>
2024
Remove
2125
</button>
2226
</div>

application/client/src/app/ui/tabs/plugins/module.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,22 @@ import { MatCardModule } from '@angular/material/card';
44
import { MatButtonModule } from '@angular/material/button';
55
import { MatProgressBarModule } from '@angular/material/progress-bar';
66
import { MatIconModule } from '@angular/material/icon';
7+
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
78

89
import { PluginsManager } from './component';
910
import { List } from './list/component';
1011
import { Plugin } from './plugin/component';
1112
import { Details } from './details/component';
1213

1314
@NgModule({
14-
imports: [CommonModule, MatCardModule, MatButtonModule, MatProgressBarModule, MatIconModule],
15+
imports: [
16+
CommonModule,
17+
MatCardModule,
18+
MatButtonModule,
19+
MatProgressBarModule,
20+
MatIconModule,
21+
MatProgressSpinnerModule,
22+
],
1523
declarations: [PluginsManager, Plugin, List, Details],
1624
exports: [PluginsManager],
1725
bootstrap: [PluginsManager],

application/client/src/app/ui/tabs/plugins/provider.ts

+61-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { scope } from '@platform/env/scope';
55
import { Logger } from '@platform/log';
66
import { InstalledPluginDesc, InvalidPluginDesc, PluginDescription } from './desc';
77
import { bridge } from '@service/bridge';
8+
import { error } from '@platform/log/utils';
89

910
export class Provider {
1011
protected log: Logger;
@@ -22,17 +23,27 @@ export class Provider {
2223
load: Subject<void>;
2324
state: Subject<void>;
2425
selected: Subject<string>;
26+
// true - add starting; false - finished
27+
add: Subject<boolean>;
28+
// true - add starting; false - finished
29+
remove: Subject<boolean>;
2530
}> = new Subjects({
2631
load: new Subject<void>(),
2732
state: new Subject<void>(),
2833
selected: new Subject<string>(),
34+
add: new Subject<boolean>(),
35+
remove: new Subject<boolean>(),
2936
});
3037
public selected: PluginDescription | undefined;
3138
public state: {
3239
loading: boolean;
40+
adding: boolean;
41+
removing: boolean;
3342
error: string | undefined;
3443
} = {
3544
loading: false,
45+
adding: false,
46+
removing: false,
3647
error: undefined,
3748
};
3849

@@ -126,9 +137,56 @@ export class Provider {
126137
this.subjects.get().selected.emit(path);
127138
}
128139
public addPlugin(pluginPath: string): Promise<void> {
129-
return plugins.addPlugin(pluginPath);
140+
if (this.isBusy()) {
141+
return Promise.reject(
142+
new Error(
143+
`Cannot add plugin, because previous plugin operation is still in progress.`,
144+
),
145+
);
146+
}
147+
this.state.adding = true;
148+
this.subjects.get().add.emit(this.state.adding);
149+
return plugins.addPlugin(pluginPath).finally(() => {
150+
this.state.adding = false;
151+
this.subjects.get().add.emit(this.state.adding);
152+
});
153+
}
154+
public async removePlugin(pluginPath: string): Promise<void> {
155+
if (this.isBusy()) {
156+
return Promise.reject(
157+
new Error(
158+
`Cannot add plugin, because previous plugin operation is still in progress.`,
159+
),
160+
);
161+
}
162+
this.state.removing = true;
163+
this.subjects.get().remove.emit(this.state.removing);
164+
try {
165+
await plugins.removePlugin(pluginPath);
166+
await this.load(true);
167+
const selected = this.selected;
168+
if (selected !== undefined) {
169+
if (
170+
[
171+
...this.plugins.installed,
172+
...this.plugins.available,
173+
...this.plugins.invalid,
174+
].find((pl) => pl.entity.dir_path === selected.getPath()) !== undefined
175+
) {
176+
this.selected = undefined;
177+
}
178+
}
179+
this.state.removing = false;
180+
this.subjects.get().remove.emit(this.state.removing);
181+
this.subjects.get().state.emit();
182+
return Promise.resolve();
183+
} catch (err) {
184+
this.state.removing = false;
185+
this.subjects.get().remove.emit(this.state.removing);
186+
return Promise.reject(new Error(error(err)));
187+
}
130188
}
131-
public removePlugin(pluginPath: string): Promise<void> {
132-
return plugins.removePlugin(pluginPath);
189+
public isBusy(): boolean {
190+
return this.state.adding || this.state.loading || this.state.removing;
133191
}
134192
}

application/client/src/app/ui/tabs/plugins/template.html

+6-2
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,18 @@
2727
<div class="controlls">
2828
<button
2929
mat-stroked-button
30-
[disabled]="provider.state.loading"
30+
[disabled]="provider.state.loading || provider.state.adding"
3131
(click)="addPlugin()"
3232
>
33+
<mat-spinner
34+
*ngIf="provider.state.adding"
35+
[diameter]="16"
36+
></mat-spinner>
3337
Add
3438
</button>
3539
<button
3640
mat-stroked-button
37-
[disabled]="provider.state.loading"
41+
[disabled]="provider.state.loading || provider.state.adding"
3842
(click)="reload()"
3943
>
4044
Reload

0 commit comments

Comments
 (0)