blob: d0865c9d4b0fe8f5f3d4e4bd3ce34db23898e839 [file] [log] [blame]
/**
* @license
* Copyright (C) 2016 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 '../../../styles/gr-font-styles';
import '../../../styles/shared-styles';
import '../../shared/gr-download-commands/gr-download-commands';
import {PolymerElement} from '@polymer/polymer/polymer-element';
import {htmlTemplate} from './gr-download-dialog_html';
import {changeBaseURL, getRevisionKey} from '../../../utils/change-util';
import {customElement, property, computed, observe} from '@polymer/decorators';
import {
ChangeInfo,
DownloadInfo,
PatchSetNum,
RevisionInfo,
} from '../../../types/common';
import {GrDownloadCommands} from '../../shared/gr-download-commands/gr-download-commands';
import {GrButton} from '../../shared/gr-button/gr-button';
import {hasOwnProperty} from '../../../utils/common-util';
import {GrOverlayStops} from '../../shared/gr-overlay/gr-overlay';
import {KeyboardShortcutMixin} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
import {fireAlert, fireEvent} from '../../../utils/event-util';
export interface GrDownloadDialog {
$: {
download: HTMLAnchorElement;
downloadCommands: GrDownloadCommands;
closeButton: GrButton;
};
}
// This avoids JSC_DYNAMIC_EXTENDS_WITHOUT_JSDOC closure compiler error.
const base = KeyboardShortcutMixin(PolymerElement);
@customElement('gr-download-dialog')
export class GrDownloadDialog extends base {
static get template() {
return htmlTemplate;
}
/**
* Fired when the user presses the close button.
*
* @event close
*/
@property({type: Object})
change: ChangeInfo | undefined;
@property({type: Object})
config?: DownloadInfo;
@property({type: String})
patchNum: PatchSetNum | undefined;
@property({type: String})
_selectedScheme?: string;
get keyBindings() {
return {
1: '_handleNumberKey',
2: '_handleNumberKey',
3: '_handleNumberKey',
4: '_handleNumberKey',
5: '_handleNumberKey',
};
}
@computed('change', 'patchNum')
get _schemes() {
// Polymer 2: check for undefined
if (this.change === undefined || this.patchNum === undefined) {
return [];
}
for (const rev of Object.values(this.change.revisions || {})) {
if (rev._number === this.patchNum) {
const fetch = rev.fetch;
if (fetch) {
return Object.keys(fetch).sort();
}
break;
}
}
return [];
}
_handleNumberKey(e: CustomEvent) {
const index = Number(e.detail.key) - 1;
const commands = this._computeDownloadCommands(
this.change,
this.patchNum,
this._selectedScheme
);
if (index > commands.length) return;
navigator.clipboard.writeText(commands[index].command).then(() => {
fireAlert(this, `${commands[index].title} command copied to clipboard`);
fireEvent(this, 'close');
});
}
override ready() {
super.ready();
this._ensureAttribute('role', 'dialog');
}
override focus() {
if (this._schemes.length) {
this.$.downloadCommands.focusOnCopy();
} else {
this.$.download.focus();
}
}
getFocusStops(): GrOverlayStops {
return {
start: this.$.downloadCommands.$.downloadTabs,
end: this.$.closeButton,
};
}
_computeDownloadCommands(
change?: ChangeInfo,
patchNum?: PatchSetNum,
selectedScheme?: string
) {
let commandObj;
if (!change || !selectedScheme) return [];
for (const rev of Object.values(change.revisions || {})) {
if (
rev._number === patchNum &&
rev &&
rev.fetch &&
hasOwnProperty(rev.fetch, selectedScheme)
) {
commandObj = rev.fetch[selectedScheme].commands;
break;
}
}
const commands = [];
for (const [title, command] of Object.entries(commandObj ?? {})) {
commands.push({title, command});
}
return commands;
}
_computeZipDownloadLink(change?: ChangeInfo, patchNum?: PatchSetNum) {
return this._computeDownloadLink(change, patchNum, true);
}
_computeZipDownloadFilename(change?: ChangeInfo, patchNum?: PatchSetNum) {
return this._computeDownloadFilename(change, patchNum, true);
}
_computeDownloadLink(
change?: ChangeInfo,
patchNum?: PatchSetNum,
zip?: boolean
) {
// Polymer 2: check for undefined
if (change === undefined || patchNum === undefined) {
return '';
}
return (
changeBaseURL(change.project, change._number, patchNum) +
'/patch?' +
(zip ? 'zip' : 'download')
);
}
_computeDownloadFilename(
change?: ChangeInfo,
patchNum?: PatchSetNum,
zip?: boolean
) {
// Polymer 2: check for undefined
if (change === undefined || patchNum === undefined) {
return '';
}
const rev = getRevisionKey(change, patchNum) ?? '';
const shortRev = rev.substr(0, 7);
return shortRev + '.diff.' + (zip ? 'zip' : 'base64');
}
_computeHidePatchFile(change?: ChangeInfo, patchNum?: PatchSetNum) {
// Polymer 2: check for undefined
if (change === undefined || patchNum === undefined) {
return false;
}
for (const rev of Object.values(change.revisions || {})) {
if (rev._number === patchNum) {
const parentLength =
rev.commit && rev.commit.parents ? rev.commit.parents.length : 0;
return parentLength === 0 || parentLength > 1;
}
}
return false;
}
_computeArchiveDownloadLink(
change?: ChangeInfo,
patchNum?: PatchSetNum,
format?: string
) {
// Polymer 2: check for undefined
if (
change === undefined ||
patchNum === undefined ||
format === undefined
) {
return '';
}
return (
changeBaseURL(change.project, change._number, patchNum) +
'/archive?format=' +
format
);
}
_computePatchSetQuantity(revisions?: {[revisionId: string]: RevisionInfo}) {
if (!revisions) {
return 0;
}
return Object.keys(revisions).length;
}
_handleCloseTap(e: Event) {
e.preventDefault();
e.stopPropagation();
fireEvent(this, 'close');
}
@observe('_schemes')
_schemesChanged(schemes: string[]) {
if (schemes.length === 0) {
return;
}
if (!this._selectedScheme || !schemes.includes(this._selectedScheme)) {
this._selectedScheme = schemes.sort()[0];
}
}
_computeShowDownloadCommands(schemes: string[]) {
return schemes.length ? '' : 'hidden';
}
}
declare global {
interface HTMLElementTagNameMap {
'gr-download-dialog': GrDownloadDialog;
}
}