Introduce `allFilesApproved` and `filesOwners` states to plugin UI
The `OwnersModel` was extended with this two states. They are loaded
reactively from plugin through the `OwnersService`. Both header and
content UI elements subscribe on these states updates and basing on that
update their corresponding internal states. As a result `render` method
is called for each element. At the moment values of `allFilesApproved`
and `filesOwners` are logged to the console.
Notes:
* UI elements have `change` property that gets updated by Gerrit UI when
update is detected while details screen is opened, as a result new
model and model loader are created, UI elements subscriptions are
refreshed and new state values are loaded, leading eventually to
reactive UI refresh
* despite `allFilesApproved` logic that prevents from unecessary calls
to obtain file owners it is still called for each file (as they form
independent UI elements). It will be further improved (in a later
follow up change) with an introducetion of cache (between service and
plugin api).
Change-Id: I1ac079a293033797c3b8dd1c06581d8a0c10fba7
diff --git a/owners/web/gr-owners.ts b/owners/web/gr-owners.ts
index 76b89d9..85e92dc 100644
--- a/owners/web/gr-owners.ts
+++ b/owners/web/gr-owners.ts
@@ -22,7 +22,11 @@
ChangeInfo,
ChangeStatus,
} from '@gerritcodereview/typescript-api/rest-api';
-import {OWNERS_SUBMIT_REQUIREMENT, OwnersService} from './owners-service';
+import {
+ FilesOwners,
+ OWNERS_SUBMIT_REQUIREMENT,
+ OwnersService,
+} from './owners-service';
import {RestPluginApi} from '@gerritcodereview/typescript-api/rest';
import {ModelLoader, OwnersModel, PatchRange, UserRole} from './owners-model';
@@ -35,6 +39,8 @@
patchRange?: PatchRange;
restApi?: RestPluginApi;
userRole?: UserRole;
+ allFilesApproved?: boolean;
+ filesOwners?: FilesOwners;
onModelUpdate(): void;
}
@@ -53,6 +59,12 @@
@state()
userRole?: UserRole;
+ @state()
+ allFilesApproved?: boolean;
+
+ @state()
+ filesOwners?: FilesOwners;
+
private _model?: OwnersModel;
modelLoader?: ModelLoader;
@@ -78,6 +90,18 @@
})
);
+ this.subscriptions.push(
+ model.state$.subscribe(s => {
+ this.allFilesApproved = s.allFilesApproved;
+ })
+ );
+
+ this.subscriptions.push(
+ model.state$.subscribe(s => {
+ this.filesOwners = s.filesOwners;
+ })
+ );
+
this.onModelUpdate();
}
@@ -104,6 +128,8 @@
protected onModelUpdate() {
this.modelLoader?.loadUserRole();
+ this.modelLoader?.loadAllFilesApproved();
+ this.modelLoader?.loadFilesOwners();
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -126,11 +152,7 @@
override render() {
console.log(
- `hidden: ${shouldHide(
- this.change,
- this.patchRange,
- this.userRole
- )}, userRole: ${this.userRole}`
+ `hidden: ${this.hidden}, userRole: ${this.userRole}, allFilesApproved: ${this.allFilesApproved}`
);
return nothing;
}
@@ -151,7 +173,11 @@
override render() {
console.log(
- `hidden: ${this.hidden}, userRole: ${this.userRole}, path: ${this.path}, oldPath: ${this.oldPath}`
+ `hidden: ${this.hidden}, userRole: ${this.userRole}, path: ${
+ this.path
+ }, oldPath: ${this.oldPath}, filesOwners: ${JSON.stringify(
+ this.filesOwners
+ )}`
);
return nothing;
}
diff --git a/owners/web/owners-model.ts b/owners/web/owners-model.ts
index 952fa3f..61ced72 100644
--- a/owners/web/owners-model.ts
+++ b/owners/web/owners-model.ts
@@ -21,7 +21,8 @@
ChangeInfo,
RevisionPatchSetNum,
} from '@gerritcodereview/typescript-api/rest-api';
-import {OwnersService} from './owners-service';
+import {FilesOwners, OwnersService} from './owners-service';
+import {deepEqual} from './utils';
export interface PatchRange {
patchNum: RevisionPatchSetNum;
@@ -36,6 +37,8 @@
export interface OwnersState {
userRole?: UserRole;
+ allFilesApproved?: boolean;
+ filesOwners?: FilesOwners;
}
let ownersModel: OwnersModel | undefined;
@@ -65,6 +68,18 @@
this.setState({...current, userRole});
}
+ setAllFilesApproved(allFilesApproved: boolean | undefined) {
+ const current = this.subject$.getValue();
+ if (current.allFilesApproved === allFilesApproved) return;
+ this.setState({...current, allFilesApproved});
+ }
+
+ setFilesOwners(filesOwners: FilesOwners | undefined) {
+ const current = this.subject$.getValue();
+ if (deepEqual(current.filesOwners, filesOwners)) return;
+ this.setState({...current, filesOwners});
+ }
+
static getModel(change: ChangeInfo) {
if (!ownersModel || ownersModel.change !== change) {
ownersModel = new OwnersModel(change);
@@ -87,6 +102,22 @@
);
}
+ async loadAllFilesApproved() {
+ await this._loadProperty(
+ 'allFilesApproved',
+ () => this.service.getAllFilesApproved(),
+ value => this.model.setAllFilesApproved(value)
+ );
+ }
+
+ async loadFilesOwners() {
+ await this._loadProperty(
+ 'filesOwners',
+ () => this.service.getFilesOwners(),
+ value => this.model.setFilesOwners(value)
+ );
+ }
+
private async _loadProperty<K extends keyof OwnersState, T>(
propertyName: K,
propertyLoader: () => Promise<T>,