blob: 23bb9fe6f71a351ed00b36cfad18ffeaca15741f [file] [log] [blame]
/**
* @license
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {Subscription} from 'rxjs';
import {CodeOwnerService} from './code-owners-service';
import {ModelLoader} from './code-owners-model-loader';
import {
CodeOwnersModel,
OwnedPathsInfoOpt,
PluginStatus,
Status,
SuggestionsState,
SuggestionsType,
UserRole,
} from './code-owners-model';
import {LitElement, PropertyValues} from 'lit';
import {property, state} from 'lit/decorators';
import {ChangeInfo} from '@gerritcodereview/typescript-api/rest-api';
import {ReportingPluginApi} from '@gerritcodereview/typescript-api/reporting';
import {RestPluginApi} from '@gerritcodereview/typescript-api/rest';
import {CodeOwnerBranchConfigInfo, FetchedFile} from './code-owners-api';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type Constructor<T> = new (...args: any[]) => T;
export interface CodeOwnersModelMixinInterface {
change?: ChangeInfo;
reporting?: ReportingPluginApi;
restApi?: RestPluginApi;
modelLoader?: ModelLoader;
model?: CodeOwnersModel;
pluginStatus?: PluginStatus;
branchConfig?: CodeOwnerBranchConfigInfo;
userRole?: UserRole;
areAllFilesApproved?: boolean;
status?: Status;
showSuggestions?: boolean;
allOwnersLoaded?: boolean;
selectedSuggestionsType?: SuggestionsType;
selectedSuggestionsFiles?: Array<FetchedFile>;
selectedSuggestionsState?: SuggestionsState;
selectedSuggestionsLoadProgress?: string;
ownedPaths?: OwnedPathsInfoOpt;
loadPropertiesAfterModelChanged(): void;
}
/**
* The CodeOwnersMixin adds several properties to a class and translates
* 'model-property-changed' events from the model to the notifyPath calls.
* This allows to use properties of the model in observers, calculated
* properties and bindings.
* It is guaranteed, that model and modelLoader are set only if change,
* reporting and restApi properties are set.
*/
export const CodeOwnersModelMixin = <T extends Constructor<LitElement>>(
superClass: T
) => {
/**
* @lit
* @mixinClass
*/
class Mixin extends superClass {
@property({type: Object})
change?: ChangeInfo;
@property({type: Object})
reporting?: ReportingPluginApi;
@property({type: Object})
restApi?: RestPluginApi;
@state()
pluginStatus?: PluginStatus;
@state()
branchConfig?: CodeOwnerBranchConfigInfo;
@state()
userRole?: UserRole;
@state()
areAllFilesApproved?: boolean;
@state()
status?: Status;
@property({type: Boolean, attribute: 'show-suggestions', reflect: true})
showSuggestions?: boolean;
@state()
selectedSuggestionsType?: SuggestionsType;
@state()
selectedSuggestionsFiles?: Array<FetchedFile>;
@state()
selectedSuggestionsState?: SuggestionsState;
@state()
selectedSuggestionsLoadProgress?: string;
@state()
ownedPaths?: OwnedPathsInfoOpt;
private model_?: CodeOwnersModel;
private subscriptions: Array<Subscription> = [];
modelLoader?: ModelLoader;
get model() {
return this.model_;
}
set model(model: CodeOwnersModel | undefined) {
if (this.model_ === model) return;
for (const s of this.subscriptions) {
s.unsubscribe();
}
this.subscriptions = [];
this.model_ = model;
if (!model) return;
this.subscriptions.push(
model.state$.subscribe(s => {
this.pluginStatus = s.pluginStatus;
})
);
this.subscriptions.push(
model.state$.subscribe(s => {
this.branchConfig = s.branchConfig;
})
);
this.subscriptions.push(
model.state$.subscribe(s => {
this.status = s.status;
})
);
this.subscriptions.push(
model.state$.subscribe(s => {
this.userRole = s.userRole;
})
);
this.subscriptions.push(
model.state$.subscribe(s => {
this.areAllFilesApproved = s.areAllFilesApproved;
})
);
this.subscriptions.push(
model.showSuggestions$.subscribe(s => {
this.showSuggestions = s;
})
);
this.subscriptions.push(
model.selectedSuggestionsType$.subscribe(s => {
this.selectedSuggestionsType = s;
})
);
this.subscriptions.push(
model.selectedSuggestionsFiles$.subscribe(f => {
this.selectedSuggestionsFiles = f;
})
);
this.subscriptions.push(
model.selectedSuggestionsLoadProgress$.subscribe(p => {
this.selectedSuggestionsLoadProgress = p;
})
);
this.subscriptions.push(
model.selectedSuggestionsState$.subscribe(s => {
this.selectedSuggestionsState = s;
})
);
this.subscriptions.push(
model.ownedPaths$.subscribe(s => {
this.ownedPaths = s;
})
);
this.loadPropertiesAfterModelChanged();
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
constructor(...args: any[]) {
super(...args);
}
protected override willUpdate(changedProperties: PropertyValues): void {
super.willUpdate(changedProperties);
if (
changedProperties.has('change') ||
changedProperties.has('restApi') ||
changedProperties.has('reporting')
) {
if (!this.restApi || !this.change || !this.reporting) {
this.model = undefined;
this.modelLoader = undefined;
return;
}
const ownerService = CodeOwnerService.getOwnerService(
this.restApi,
this.change
);
const model = CodeOwnersModel.getModel(this.change);
this.modelLoader = new ModelLoader(ownerService, model);
// Assign model after modelLoader, so modelLoader can be used in
// the loadPropertiesAfterModelChanged method
this.model = model;
}
}
protected loadPropertiesAfterModelChanged() {}
}
return Mixin as unknown as T & Constructor<CodeOwnersModelMixinInterface>;
};