| /** |
| * @license |
| * Copyright (C) 2017 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 '../../shared/gr-rest-api-interface/gr-rest-api-interface'; |
| import '../gr-label-score-row/gr-label-score-row'; |
| import '../../../styles/shared-styles'; |
| import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners'; |
| import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin'; |
| import {PolymerElement} from '@polymer/polymer/polymer-element'; |
| import {htmlTemplate} from './gr-label-scores_html'; |
| import {customElement, property} from '@polymer/decorators'; |
| import {hasOwnProperty} from '../../../utils/common-util'; |
| import { |
| LabelNameToValueMap, |
| ChangeInfo, |
| AccountInfo, |
| DetailedLabelInfo, |
| LabelNameToInfoMap, |
| LabelNameToValuesMap, |
| } from '../../../types/common'; |
| import { |
| GrLabelScoreRow, |
| LabelValuesMap, |
| } from '../gr-label-score-row/gr-label-score-row'; |
| import {PolymerDeepPropertyChange} from '@polymer/polymer/interfaces'; |
| |
| type Labels = {[label: string]: number}; |
| @customElement('gr-label-scores') |
| export class GrLabelScores extends GestureEventListeners( |
| LegacyElementMixin(PolymerElement) |
| ) { |
| static get template() { |
| return htmlTemplate; |
| } |
| |
| @property({type: Array, computed: '_computeLabels(change.labels.*, account)'}) |
| _labels?: Labels; |
| |
| @property({type: Object, observer: '_computeColumns'}) |
| permittedLabels?: LabelNameToValueMap; |
| |
| @property({type: Object}) |
| change?: ChangeInfo; |
| |
| @property({type: Object}) |
| account?: AccountInfo; |
| |
| @property({type: Object}) |
| _labelValues?: LabelValuesMap; |
| |
| getLabelValues(includeDefaults = true): LabelNameToValuesMap { |
| const labels: LabelNameToValuesMap = {}; |
| if (this.shadowRoot === null || !this.change) { |
| return labels; |
| } |
| for (const label in this.permittedLabels) { |
| if (!hasOwnProperty(this.permittedLabels, label)) { |
| continue; |
| } |
| |
| const selectorEl = this.shadowRoot.querySelector( |
| `gr-label-score-row[name="${label}"]` |
| ) as null | GrLabelScoreRow; |
| if (!selectorEl) { |
| continue; |
| } |
| |
| // The user may have not voted on this label. |
| if (!selectorEl.selectedItem) { |
| continue; |
| } |
| |
| const selectedVal = |
| typeof selectorEl.selectedValue === 'string' |
| ? Number(selectorEl.selectedValue) |
| : selectorEl.selectedValue; |
| |
| if (selectedVal === undefined) { |
| continue; |
| } |
| |
| // Only send the selection if the user changed it. |
| const prevVal = this._getVoteForAccount( |
| this.change.labels, |
| label, |
| this.account |
| ); |
| |
| let prevValNum: number | null | undefined; |
| if (typeof prevVal === 'string') { |
| prevValNum = Number(prevVal); |
| } else { |
| prevValNum = prevVal; |
| } |
| |
| const defValNum = this._getDefaultValue(this.change.labels, label); |
| |
| if (selectedVal !== prevValNum) { |
| if (includeDefaults || !!prevValNum || selectedVal !== defValNum) { |
| labels[label] = selectedVal; |
| } |
| } |
| } |
| return labels; |
| } |
| |
| _getStringLabelValue( |
| labels: LabelNameToInfoMap, |
| labelName: string, |
| numberValue?: number |
| ) { |
| for (const k in (labels[labelName] as DetailedLabelInfo).values) { |
| if (Number(k) === numberValue) { |
| return k; |
| } |
| } |
| return numberValue; |
| } |
| |
| _getDefaultValue(labels?: LabelNameToInfoMap, labelName?: string) { |
| if (!labelName || !labels?.[labelName]) return undefined; |
| const labelInfo = labels[labelName] as DetailedLabelInfo; |
| return labelInfo.default_value; |
| } |
| |
| _getVoteForAccount( |
| labels: LabelNameToInfoMap | undefined, |
| labelName: string, |
| account?: AccountInfo |
| ) { |
| if (!labels) return null; |
| const votes = labels[labelName] as DetailedLabelInfo; |
| if (votes.all && votes.all.length > 0) { |
| for (let i = 0; i < votes.all.length; i++) { |
| // TODO(TS): Replace == with === and check code can assign string to _account_id instead of number |
| // eslint-disable-next-line eqeqeq |
| if (account && votes.all[i]._account_id == account._account_id) { |
| return this._getStringLabelValue( |
| labels, |
| labelName, |
| votes.all[i].value |
| ); |
| } |
| } |
| } |
| return null; |
| } |
| |
| _computeLabels( |
| labelRecord: PolymerDeepPropertyChange< |
| LabelNameToInfoMap, |
| LabelNameToInfoMap |
| >, |
| account?: AccountInfo |
| ) { |
| // Polymer 2: check for undefined |
| if ([labelRecord, account].includes(undefined)) { |
| return undefined; |
| } |
| |
| const labelsObj = labelRecord.base; |
| if (!labelsObj) { |
| return []; |
| } |
| return Object.keys(labelsObj) |
| .sort() |
| .map(key => { |
| return { |
| name: key, |
| value: this._getVoteForAccount(labelsObj, key, this.account), |
| }; |
| }); |
| } |
| |
| _computeColumns(permittedLabels?: LabelNameToValueMap) { |
| if (!permittedLabels) return; |
| const labels = Object.keys(permittedLabels); |
| const values: Set<number> = new Set(); |
| for (const label of labels) { |
| for (const value of permittedLabels[label]) { |
| values.add(Number(value)); |
| } |
| } |
| |
| const orderedValues = Array.from(values.values()).sort((a, b) => a - b); |
| |
| const labelValues: LabelValuesMap = {}; |
| for (let i = 0; i < orderedValues.length; i++) { |
| labelValues[orderedValues[i]] = i; |
| } |
| this._labelValues = labelValues; |
| } |
| |
| _changeIsMerged(changeStatus: string) { |
| return changeStatus === 'MERGED'; |
| } |
| |
| _computeLabelAccessClass( |
| label?: string, |
| permittedLabels?: LabelNameToValueMap |
| ) { |
| if (!permittedLabels || !label) { |
| return ''; |
| } |
| |
| return hasOwnProperty(permittedLabels, label) && |
| permittedLabels[label].length |
| ? 'access' |
| : 'no-access'; |
| } |
| } |
| |
| declare global { |
| interface HTMLElementTagNameMap { |
| 'gr-label-scores': GrLabelScores; |
| } |
| } |