| /** |
| * @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. |
| */ |
| (function() { |
| 'use strict'; |
| |
| // The maximum age of a keydown event to be used in a jump navigation. This is |
| // only for cases when the keyup event is lost. |
| const G_KEY_TIMEOUT_MS = 1000; |
| |
| // Eagerly render Polymer components when backgrounded. (Skips |
| // requestAnimationFrame.) |
| // @see https://github.com/Polymer/polymer/issues/3851 |
| // TODO: Reassess after Polymer 2.0 upgrade. |
| // @see Issue 4699 |
| Polymer.RenderStatus._makeReady(); |
| |
| Polymer({ |
| is: 'gr-app', |
| |
| /** |
| * Fired when the URL location changes. |
| * |
| * @event location-change |
| */ |
| |
| properties: { |
| /** |
| * @type {{ query: string, view: string, screen: string }} |
| */ |
| params: Object, |
| keyEventTarget: { |
| type: Object, |
| value() { return document.body; }, |
| }, |
| |
| _account: { |
| type: Object, |
| observer: '_accountChanged', |
| }, |
| |
| /** |
| * The last time the g key was pressed in milliseconds (or a keydown event |
| * was handled if the key is held down). |
| * @type {number|null} |
| */ |
| _lastGKeyPressTimestamp: { |
| type: Number, |
| value: null, |
| }, |
| |
| /** |
| * @type {{ plugin: Object }} |
| */ |
| _serverConfig: Object, |
| _version: String, |
| _showChangeListView: Boolean, |
| _showDashboardView: Boolean, |
| _showChangeView: Boolean, |
| _showDiffView: Boolean, |
| _showSettingsView: Boolean, |
| _showAdminView: Boolean, |
| _showCLAView: Boolean, |
| _showEditorView: Boolean, |
| _showPluginScreen: Boolean, |
| /** @type {?} */ |
| _viewState: Object, |
| /** @type {?} */ |
| _lastError: Object, |
| _lastSearchPage: String, |
| _path: String, |
| _isShadowDom: Boolean, |
| _pluginScreenName: { |
| type: String, |
| computed: '_computePluginScreenName(params)', |
| }, |
| _settingsUrl: String, |
| _feedbackUrl: { |
| type: String, |
| value: 'https://bugs.chromium.org/p/gerrit/issues/entry' + |
| '?template=PolyGerrit%20Issue', |
| }, |
| }, |
| |
| listeners: { |
| 'page-error': '_handlePageError', |
| 'title-change': '_handleTitleChange', |
| 'location-change': '_handleLocationChange', |
| 'rpc-log': '_handleRpcLog', |
| }, |
| |
| observers: [ |
| '_viewChanged(params.view)', |
| '_paramsChanged(params.*)', |
| ], |
| |
| behaviors: [ |
| Gerrit.BaseUrlBehavior, |
| Gerrit.KeyboardShortcutBehavior, |
| ], |
| |
| keyBindings: { |
| '?': '_showKeyboardShortcuts', |
| 'g:keydown': '_gKeyDown', |
| 'g:keyup': '_gKeyUp', |
| 'a m o': '_jumpKeyPressed', |
| }, |
| |
| ready() { |
| this._isShadowDom = Polymer.Settings.useShadow; |
| this.$.router.start(); |
| |
| this.$.restAPI.getAccount().then(account => { |
| this._account = account; |
| }); |
| this.$.restAPI.getConfig().then(config => { |
| this._serverConfig = config; |
| |
| if (config && config.gerrit && config.gerrit.report_bug_url) { |
| this._feedbackUrl = config.gerrit.report_bug_url; |
| } |
| }); |
| this.$.restAPI.getVersion().then(version => { |
| this._version = version; |
| this._logWelcome(); |
| }); |
| |
| if (window.localStorage.getItem('dark-theme')) { |
| this.$.libLoader.getDarkTheme().then(module => { |
| Polymer.dom(this.root).appendChild(module); |
| }); |
| } |
| |
| // Note: this is evaluated here to ensure that it only happens after the |
| // router has been initialized. @see Issue 7837 |
| this._settingsUrl = Gerrit.Nav.getUrlForSettings(); |
| |
| this.$.reporting.appStarted(document.visibilityState === 'hidden'); |
| |
| this._viewState = { |
| changeView: { |
| changeNum: null, |
| patchRange: null, |
| selectedFileIndex: 0, |
| showReplyDialog: false, |
| diffMode: null, |
| numFilesShown: null, |
| scrollTop: 0, |
| }, |
| changeListView: { |
| query: null, |
| offset: 0, |
| selectedChangeIndex: 0, |
| }, |
| dashboardView: { |
| selectedChangeIndex: 0, |
| }, |
| }; |
| }, |
| |
| _accountChanged(account) { |
| if (!account) { return; } |
| |
| // Preferences are cached when a user is logged in; warm them. |
| this.$.restAPI.getPreferences(); |
| this.$.restAPI.getDiffPreferences(); |
| this.$.restAPI.getEditPreferences(); |
| this.$.errorManager.knownAccountId = |
| this._account && this._account._account_id || null; |
| }, |
| |
| _viewChanged(view) { |
| this.$.errorView.classList.remove('show'); |
| this.set('_showChangeListView', view === Gerrit.Nav.View.SEARCH); |
| this.set('_showDashboardView', view === Gerrit.Nav.View.DASHBOARD); |
| this.set('_showChangeView', view === Gerrit.Nav.View.CHANGE); |
| this.set('_showDiffView', view === Gerrit.Nav.View.DIFF); |
| this.set('_showSettingsView', view === Gerrit.Nav.View.SETTINGS); |
| this.set('_showAdminView', view === Gerrit.Nav.View.ADMIN || |
| view === Gerrit.Nav.View.GROUP || view === Gerrit.Nav.View.REPO); |
| this.set('_showCLAView', view === Gerrit.Nav.View.AGREEMENTS); |
| this.set('_showEditorView', view === Gerrit.Nav.View.EDIT); |
| const isPluginScreen = view === Gerrit.Nav.View.PLUGIN_SCREEN; |
| this.set('_showPluginScreen', false); |
| // Navigation within plugin screens does not restamp gr-endpoint-decorator |
| // because _showPluginScreen value does not change. To force restamp, |
| // change _showPluginScreen value between true and false. |
| if (isPluginScreen) { |
| this.async(() => this.set('_showPluginScreen', true), 1); |
| } |
| if (this.params.justRegistered) { |
| this.$.registrationOverlay.open(); |
| this.$.registrationDialog.loadData().then(() => { |
| this.$.registrationOverlay.refit(); |
| }); |
| } |
| this.$.header.unfloat(); |
| }, |
| |
| _computeShowGwtUiLink(config) { |
| return !window.DEPRECATE_GWT_UI && |
| config.gerrit.web_uis && config.gerrit.web_uis.includes('GWT'); |
| }, |
| |
| _handlePageError(e) { |
| const props = [ |
| '_showChangeListView', |
| '_showDashboardView', |
| '_showChangeView', |
| '_showDiffView', |
| '_showSettingsView', |
| '_showAdminView', |
| ]; |
| for (const showProp of props) { |
| this.set(showProp, false); |
| } |
| |
| this.$.errorView.classList.add('show'); |
| const response = e.detail.response; |
| const err = {text: [response.status, response.statusText].join(' ')}; |
| if (response.status === 404) { |
| err.emoji = '¯\\_(ツ)_/¯'; |
| this._lastError = err; |
| } else { |
| err.emoji = 'o_O'; |
| response.text().then(text => { |
| err.moreInfo = text; |
| this._lastError = err; |
| }); |
| } |
| }, |
| |
| _handleLocationChange(e) { |
| const hash = e.detail.hash.substring(1); |
| let pathname = e.detail.pathname; |
| if (pathname.startsWith('/c/') && parseInt(hash, 10) > 0) { |
| pathname += '@' + hash; |
| } |
| this.set('_path', pathname); |
| }, |
| |
| _paramsChanged(paramsRecord) { |
| const params = paramsRecord.base; |
| const viewsToCheck = [Gerrit.Nav.View.SEARCH, Gerrit.Nav.View.DASHBOARD]; |
| if (viewsToCheck.includes(params.view)) { |
| this.set('_lastSearchPage', location.pathname); |
| } |
| }, |
| |
| _handleTitleChange(e) { |
| if (e.detail.title) { |
| document.title = e.detail.title + ' · Gerrit Code Review'; |
| } else { |
| document.title = ''; |
| } |
| }, |
| |
| _showKeyboardShortcuts(e) { |
| if (this.shouldSuppressKeyboardShortcut(e)) { return; } |
| this.$.keyboardShortcuts.open(); |
| }, |
| |
| _handleKeyboardShortcutDialogClose() { |
| this.$.keyboardShortcuts.close(); |
| }, |
| |
| _handleAccountDetailUpdate(e) { |
| this.$.mainHeader.reload(); |
| if (this.params.view === Gerrit.Nav.View.SETTINGS) { |
| this.$$('gr-settings-view').reloadAccountDetail(); |
| } |
| }, |
| |
| _handleRegistrationDialogClose(e) { |
| this.params.justRegistered = false; |
| this.$.registrationOverlay.close(); |
| }, |
| |
| _computeShadowClass(isShadowDom) { |
| return isShadowDom ? 'shadow' : ''; |
| }, |
| |
| _gKeyDown(e) { |
| if (this.modifierPressed(e)) { return; } |
| this._lastGKeyPressTimestamp = Date.now(); |
| }, |
| |
| _gKeyUp() { |
| this._lastGKeyPressTimestamp = null; |
| }, |
| |
| _jumpKeyPressed(e) { |
| if (!this._lastGKeyPressTimestamp || |
| (Date.now() - this._lastGKeyPressTimestamp > G_KEY_TIMEOUT_MS) || |
| this.shouldSuppressKeyboardShortcut(e)) { return; } |
| e.preventDefault(); |
| |
| let status = null; |
| if (e.detail.key === 'a') { |
| status = 'abandoned'; |
| } else if (e.detail.key === 'm') { |
| status = 'merged'; |
| } else if (e.detail.key === 'o') { |
| status = 'open'; |
| } |
| if (status !== null) { |
| Gerrit.Nav.navigateToStatusSearch(status); |
| } |
| }, |
| |
| _computePluginScreenName({plugin, screen}) { |
| return Gerrit._getPluginScreenName(plugin, screen); |
| }, |
| |
| _logWelcome() { |
| console.group('Runtime Info'); |
| console.log('Gerrit UI (PolyGerrit)'); |
| console.log(`Gerrit Server Version: ${this._version}`); |
| if (window.VERSION_INFO) { |
| console.log(`UI Version Info: ${window.VERSION_INFO}`); |
| } |
| const renderTime = new Date(window.performance.timing.loadEventStart); |
| console.log(`Document loaded at: ${renderTime}`); |
| console.log(`Please file bugs and feedback at: ${this._feedbackUrl}`); |
| console.groupEnd(); |
| }, |
| |
| /** |
| * Intercept RPC log events emitted by REST API interfaces. |
| * Note: the REST API interface cannot use gr-reporting directly because |
| * that would create a cyclic dependency. |
| */ |
| _handleRpcLog(e) { |
| this.$.reporting.reportRpcTiming(e.detail.anonymizedUrl, |
| e.detail.elapsed); |
| }, |
| }); |
| })(); |