| /** |
| * @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 '@polymer/iron-autogrow-textarea/iron-autogrow-textarea'; |
| import '../../../styles/gr-form-styles'; |
| import '../../../styles/gr-subpage-styles'; |
| import '../../../styles/shared-styles'; |
| import '../../shared/gr-autocomplete/gr-autocomplete'; |
| import '../../shared/gr-copy-clipboard/gr-copy-clipboard'; |
| import '../../shared/gr-rest-api-interface/gr-rest-api-interface'; |
| import '../../shared/gr-select/gr-select'; |
| 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-group_html'; |
| import {customElement, property, observe} from '@polymer/decorators'; |
| import { |
| AutocompleteSuggestion, |
| AutocompleteQuery, |
| } from '../../shared/gr-autocomplete/gr-autocomplete'; |
| import {GroupId, GroupInfo} from '../../../types/common'; |
| import { |
| ErrorCallback, |
| RestApiService, |
| } from '../../../services/services/gr-rest-api/gr-rest-api'; |
| import {hasOwnProperty} from '../../../utils/common-util'; |
| |
| const INTERNAL_GROUP_REGEX = /^[\da-f]{40}$/; |
| |
| const OPTIONS = { |
| submitFalse: { |
| value: false, |
| label: 'False', |
| }, |
| submitTrue: { |
| value: true, |
| label: 'True', |
| }, |
| }; |
| |
| export interface GrGroup { |
| $: { |
| restAPI: RestApiService & Element; |
| loading: HTMLDivElement; |
| }; |
| } |
| |
| declare global { |
| interface HTMLElementTagNameMap { |
| 'gr-group': GrGroup; |
| } |
| } |
| |
| @customElement('gr-group') |
| export class GrGroup extends GestureEventListeners( |
| LegacyElementMixin(PolymerElement) |
| ) { |
| static get template() { |
| return htmlTemplate; |
| } |
| |
| /** |
| * Fired when the group name changes. |
| * |
| * @event name-changed |
| */ |
| |
| @property({type: String}) |
| groupId?: GroupId; |
| |
| @property({type: Boolean}) |
| _rename = false; |
| |
| @property({type: Boolean}) |
| _groupIsInternal = false; |
| |
| @property({type: Boolean}) |
| _description = false; |
| |
| @property({type: Boolean}) |
| _owner = false; |
| |
| @property({type: Boolean}) |
| _options = false; |
| |
| @property({type: Boolean}) |
| _loading = true; |
| |
| @property({type: Object}) |
| _groupConfig?: GroupInfo; |
| |
| @property({type: String}) |
| _groupConfigOwner?: string; |
| |
| @property({type: Object}) |
| _groupName?: string; |
| |
| @property({type: Boolean}) |
| _groupOwner = false; |
| |
| @property({type: Array}) |
| _submitTypes = Object.values(OPTIONS); |
| |
| @property({type: Object}) |
| _query: AutocompleteQuery; |
| |
| @property({type: Boolean}) |
| _isAdmin = false; |
| |
| constructor() { |
| super(); |
| this._query = (input: string) => this._getGroupSuggestions(input); |
| } |
| |
| /** @override */ |
| attached() { |
| super.attached(); |
| this._loadGroup(); |
| } |
| |
| _loadGroup() { |
| if (!this.groupId) { |
| return; |
| } |
| |
| const promises: Promise<unknown>[] = []; |
| |
| const errFn: ErrorCallback = response => { |
| this.dispatchEvent( |
| new CustomEvent('page-error', { |
| detail: {response}, |
| composed: true, |
| bubbles: true, |
| }) |
| ); |
| }; |
| |
| return this.$.restAPI.getGroupConfig(this.groupId, errFn).then(config => { |
| if (!config || !config.name) { |
| return Promise.resolve(); |
| } |
| |
| this._groupName = config.name; |
| this._groupIsInternal = !!config.id.match(INTERNAL_GROUP_REGEX); |
| |
| promises.push( |
| this.$.restAPI.getIsAdmin().then(isAdmin => { |
| this._isAdmin = !!isAdmin; |
| }) |
| ); |
| |
| promises.push( |
| this.$.restAPI.getIsGroupOwner(config.name as GroupId).then(isOwner => { |
| this._groupOwner = !!isOwner; |
| }) |
| ); |
| |
| // If visible to all is undefined, set to false. If it is defined |
| // as false, setting to false is fine. If any optional values |
| // are added with a default of true, then this would need to be an |
| // undefined check and not a truthy/falsy check. |
| if (config.options && !config.options.visible_to_all) { |
| config.options.visible_to_all = false; |
| } |
| this._groupConfig = config; |
| |
| this.dispatchEvent( |
| new CustomEvent('title-change', { |
| detail: {title: config.name}, |
| composed: true, |
| bubbles: true, |
| }) |
| ); |
| |
| return Promise.all(promises).then(() => { |
| this._loading = false; |
| }); |
| }); |
| } |
| |
| _computeLoadingClass(loading: boolean) { |
| return loading ? 'loading' : ''; |
| } |
| |
| _isLoading() { |
| return this._loading || this._loading === undefined; |
| } |
| |
| _handleSaveName() { |
| const groupConfig = this._groupConfig; |
| if (!this.groupId || !groupConfig || !groupConfig.name) { |
| return Promise.reject(new Error('invalid groupId or config name')); |
| } |
| return this.$.restAPI |
| .saveGroupName(this.groupId, groupConfig.name) |
| .then(config => { |
| if (config.status === 200) { |
| this._groupName = groupConfig.name; |
| this.dispatchEvent( |
| new CustomEvent('name-changed', { |
| detail: { |
| name: groupConfig.name, |
| external: !this._groupIsInternal, |
| }, |
| composed: true, |
| bubbles: true, |
| }) |
| ); |
| this._rename = false; |
| } |
| }); |
| } |
| |
| _handleSaveOwner() { |
| if (!this.groupId || !this._groupConfig) return; |
| let owner = this._groupConfig.owner; |
| if (this._groupConfigOwner) { |
| owner = decodeURIComponent(this._groupConfigOwner); |
| } |
| if (!owner) return; |
| return this.$.restAPI.saveGroupOwner(this.groupId, owner).then(() => { |
| this._owner = false; |
| }); |
| } |
| |
| _handleSaveDescription() { |
| if (!this.groupId || !this._groupConfig || !this._groupConfig.description) |
| return; |
| return this.$.restAPI |
| .saveGroupDescription(this.groupId, this._groupConfig.description) |
| .then(() => { |
| this._description = false; |
| }); |
| } |
| |
| _handleSaveOptions() { |
| if (!this.groupId || !this._groupConfig || !this._groupConfig.options) |
| return; |
| const visible = this._groupConfig.options.visible_to_all; |
| |
| const options = {visible_to_all: visible}; |
| |
| return this.$.restAPI.saveGroupOptions(this.groupId, options).then(() => { |
| this._options = false; |
| }); |
| } |
| |
| @observe('_groupConfig.name') |
| _handleConfigName() { |
| if (this._isLoading()) { |
| return; |
| } |
| this._rename = true; |
| } |
| |
| @observe('_groupConfig.owner', '_groupConfigOwner') |
| _handleConfigOwner() { |
| if (this._isLoading()) { |
| return; |
| } |
| this._owner = true; |
| } |
| |
| @observe('_groupConfig.description') |
| _handleConfigDescription() { |
| if (this._isLoading()) { |
| return; |
| } |
| this._description = true; |
| } |
| |
| @observe('_groupConfig.options.visible_to_all') |
| _handleConfigOptions() { |
| if (this._isLoading()) { |
| return; |
| } |
| this._options = true; |
| } |
| |
| _computeHeaderClass(configChanged: boolean) { |
| return configChanged ? 'edited' : ''; |
| } |
| |
| _getGroupSuggestions(input: string) { |
| return this.$.restAPI.getSuggestedGroups(input).then(response => { |
| const groups: AutocompleteSuggestion[] = []; |
| for (const key in response) { |
| if (!hasOwnProperty(response, key)) { |
| continue; |
| } |
| groups.push({ |
| name: key, |
| value: decodeURIComponent(response[key].id), |
| }); |
| } |
| return groups; |
| }); |
| } |
| |
| _computeGroupDisabled( |
| owner: boolean, |
| admin: boolean, |
| groupIsInternal: boolean |
| ) { |
| return !(groupIsInternal && (admin || owner)); |
| } |
| |
| _getGroupUUID(id: GroupId) { |
| if (!id) return; |
| |
| return id.match(INTERNAL_GROUP_REGEX) ? id : decodeURIComponent(id); |
| } |
| } |