summaryrefslogtreecommitdiffstats
path: root/polygerrit-ui/app/elements/gr-app-element.ts
diff options
context:
space:
mode:
Diffstat (limited to 'polygerrit-ui/app/elements/gr-app-element.ts')
-rw-r--r--polygerrit-ui/app/elements/gr-app-element.ts824
1 files changed, 531 insertions, 293 deletions
diff --git a/polygerrit-ui/app/elements/gr-app-element.ts b/polygerrit-ui/app/elements/gr-app-element.ts
index d88eeaf49f..32661f0b6e 100644
--- a/polygerrit-ui/app/elements/gr-app-element.ts
+++ b/polygerrit-ui/app/elements/gr-app-element.ts
@@ -14,8 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import '../styles/shared-styles';
+
import '../styles/themes/app-theme';
+import '../styles/themes/dark-theme';
import {applyTheme as applyDarkTheme} from '../styles/themes/dark-theme';
import './admin/gr-admin-view/gr-admin-view';
import './documentation/gr-documentation-search/gr-documentation-search';
@@ -25,7 +26,6 @@ import './change/gr-change-view/gr-change-view';
import './core/gr-error-manager/gr-error-manager';
import './core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog';
import './core/gr-main-header/gr-main-header';
-import './core/gr-router/gr-router';
import './core/gr-smart-search/gr-smart-search';
import './diff/gr-diff-view/gr-diff-view';
import './edit/gr-editor-view/gr-editor-view';
@@ -37,24 +37,12 @@ import './plugins/gr-plugin-host/gr-plugin-host';
import './settings/gr-cla-view/gr-cla-view';
import './settings/gr-registration-dialog/gr-registration-dialog';
import './settings/gr-settings-view/gr-settings-view';
-import {PolymerElement} from '@polymer/polymer/polymer-element';
-import {htmlTemplate} from './gr-app-element_html';
-import {getBaseUrl} from '../utils/url-util';
-import {
- KeyboardShortcutMixin,
- Shortcut,
- ShortcutListener,
-} from '../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
+import {loginUrl} from '../utils/url-util';
+import {Shortcut} from '../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
import {GerritNav} from './core/gr-navigation/gr-navigation';
-import {appContext} from '../services/app-context';
-import {flush} from '@polymer/polymer/lib/utils/flush';
-import {customElement, observe, property} from '@polymer/decorators';
+import {getAppContext} from '../services/app-context';
import {GrRouter} from './core/gr-router/gr-router';
-import {
- AccountDetailInfo,
- ElementPropertyDeepChange,
- ServerInfo,
-} from '../types/common';
+import {AccountDetailInfo, ServerInfo} from '../types/common';
import {
constructServerErrorMsg,
GrErrorManager,
@@ -64,6 +52,8 @@ import {GrRegistrationDialog} from './settings/gr-registration-dialog/gr-registr
import {
AppElementJustRegisteredParams,
AppElementParams,
+ AppElementPluginScreenParams,
+ AppElementSearchParam,
isAppElementJustRegisteredParams,
} from './gr-app-types';
import {GrMainHeader} from './core/gr-main-header/gr-main-header';
@@ -75,13 +65,21 @@ import {
PageErrorEventDetail,
RpcLogEvent,
TitleChangeEventDetail,
+ ValueChangedEvent,
} from '../types/events';
-import {ViewState} from '../types/types';
+import {ChangeListViewState, ChangeViewState, ViewState} from '../types/types';
import {GerritView} from '../services/router/router-model';
import {LifeCycle} from '../constants/reporting';
import {fireIronAnnounce} from '../utils/event-util';
+import {resolve} from '../models/dependency';
+import {browserModelToken} from '../models/browser/browser-model';
+import {sharedStyles} from '../styles/shared-styles';
+import {LitElement, PropertyValues, html, css, nothing} from 'lit';
+import {customElement, property, query, state} from 'lit/decorators';
+import {ShortcutController} from './lit/shortcut-controller';
+import {cache} from 'lit/directives/cache';
import {assertIsDefined} from '../utils/common-util';
-import {listen} from '../services/shortcuts/shortcuts-service';
+import './gr-css-mixins';
interface ErrorInfo {
text: string;
@@ -89,179 +87,152 @@ interface ErrorInfo {
moreInfo?: string;
}
-export interface GrAppElement {
- $: {
- router: GrRouter;
- errorManager: GrErrorManager;
- errorView: HTMLDivElement;
- mainHeader: GrMainHeader;
- };
-}
-
-type DomIf = PolymerElement & {
- restamp: boolean;
-};
-
-// This avoids JSC_DYNAMIC_EXTENDS_WITHOUT_JSDOC closure compiler error.
-const base = KeyboardShortcutMixin(PolymerElement);
-
// TODO(TS): implement AppElement interface from gr-app-types.ts
@customElement('gr-app-element')
-export class GrAppElement extends base {
- static get template() {
- return htmlTemplate;
- }
-
+export class GrAppElement extends LitElement {
/**
* Fired when the URL location changes.
*
* @event location-change
*/
- @property({type: Object})
- params?: AppElementParams;
+ @query('#errorManager') errorManager?: GrErrorManager;
- @property({type: Object, observer: '_accountChanged'})
- _account?: AccountDetailInfo;
+ @query('#errorView') errorView?: HTMLDivElement;
- @property({type: Number})
- _lastGKeyPressTimestamp: number | null = null;
+ @query('#mainHeader') mainHeader?: GrMainHeader;
- @property({type: Object})
- _serverConfig?: ServerInfo;
+ @query('#registrationOverlay') registrationOverlay?: GrOverlay;
- @property({type: String})
- _version?: string;
+ @query('#registrationDialog') registrationDialog?: GrRegistrationDialog;
- @property({type: Boolean})
- _showChangeListView?: boolean;
+ @query('#keyboardShortcuts') keyboardShortcuts?: GrOverlay;
- @property({type: Boolean})
- _showDashboardView?: boolean;
+ @query('gr-settings-view') settingdView?: GrSettingsView;
- @property({type: Boolean})
- _showChangeView?: boolean;
+ @property({type: Object})
+ params?: AppElementParams;
- @property({type: Boolean})
- _showDiffView?: boolean;
+ @state() private account?: AccountDetailInfo;
- @property({type: Boolean})
- _showSettingsView?: boolean;
+ @state() private serverConfig?: ServerInfo;
- @property({type: Boolean})
- _showAdminView?: boolean;
+ @state() private version?: string;
- @property({type: Boolean})
- _showCLAView?: boolean;
+ @state() private showChangeListView?: boolean;
- @property({type: Boolean})
- _showEditorView?: boolean;
+ @state() private showDashboardView?: boolean;
- @property({type: Boolean})
- _showPluginScreen?: boolean;
+ @state() private showChangeView?: boolean;
- @property({type: Boolean})
- _showDocumentationSearch?: boolean;
+ @state() private showDiffView?: boolean;
- @property({type: Object})
- _viewState?: ViewState;
+ @state() private showSettingsView?: boolean;
- @property({type: Object})
- _lastError?: ErrorInfo;
+ @state() private showAdminView?: boolean;
+
+ @state() private showCLAView?: boolean;
+
+ @state() private showEditorView?: boolean;
- @property({type: String})
- _lastSearchPage?: string;
+ @state() private showPluginScreen?: boolean;
- @property({type: String})
- _path?: string;
+ @state() private showDocumentationSearch?: boolean;
- @property({type: String, computed: '_computePluginScreenName(params)'})
- _pluginScreenName?: string;
+ @state() private viewState?: ViewState;
- @property({type: String})
- _settingsUrl?: string;
+ @state() private lastError?: ErrorInfo;
- @property({type: String})
- _feedbackUrl?: string;
+ // private but used in test
+ @state() lastSearchPage?: string;
- @property({type: Boolean})
- mobileSearch = false;
+ @state() private path?: string;
- @property({type: String})
- _loginUrl = '/login';
+ @state() private settingsUrl?: string;
- @property({type: Boolean})
- loadRegistrationDialog = false;
+ @state() private mobileSearch = false;
- @property({type: Boolean})
- loadKeyboardShortcutsDialog = false;
+ @state() private loadRegistrationDialog = false;
+
+ @state() private loadKeyboardShortcutsDialog = false;
// TODO(milutin) - remove once new gr-dialog will do it out of the box
// This removes footer, header from a11y tree, when a dialog on view
// (e.g. reply dialog) is open
- @property({type: Boolean})
- _footerHeaderAriaHidden = false;
+ @state() private footerHeaderAriaHidden = false;
// TODO(milutin) - remove once new gr-dialog will do it out of the box
// This removes main page from a11y tree, when a dialog on gr-app-element
// (e.g. shortcut dialog) is open
- @property({type: Boolean})
- _mainAriaHidden = false;
+ @state() private mainAriaHidden = false;
- private reporting = appContext.reportingService;
+ // Triggers dom-if unsetting/setting restamp behaviour in lit
+ @state() private invalidateChangeViewCache = false;
- private readonly restApiService = appContext.restApiService;
+ // Triggers dom-if unsetting/setting restamp behaviour in lit
+ @state() private invalidateDiffViewCache = false;
- override keyboardShortcuts(): ShortcutListener[] {
- return [
- listen(Shortcut.OPEN_SHORTCUT_HELP_DIALOG, _ =>
- this._showKeyboardShortcuts()
- ),
- listen(Shortcut.GO_TO_USER_DASHBOARD, _ => this._goToUserDashboard()),
- listen(Shortcut.GO_TO_OPENED_CHANGES, _ => this._goToOpenedChanges()),
- listen(Shortcut.GO_TO_MERGED_CHANGES, _ => this._goToMergedChanges()),
- listen(Shortcut.GO_TO_ABANDONED_CHANGES, _ =>
- this._goToAbandonedChanges()
- ),
- listen(Shortcut.GO_TO_WATCHED_CHANGES, _ => this._goToWatchedChanges()),
- ];
- }
+ readonly router = new GrRouter();
+
+ private reporting = getAppContext().reportingService;
+
+ private readonly restApiService = getAppContext().restApiService;
+
+ private readonly getBrowserModel = resolve(this, browserModelToken);
+
+ private readonly shortcuts = new ShortcutController(this);
constructor() {
super();
- // We just want to instantiate this service somewhere. It is reacting to
- // model changes and updates the config model, but at the moment the service
- // is not called from anywhere.
- appContext.configService;
document.addEventListener(EventType.PAGE_ERROR, e => {
- this._handlePageError(e);
+ this.handlePageError(e);
});
this.addEventListener(EventType.TITLE_CHANGE, e => {
- this._handleTitleChange(e);
+ this.handleTitleChange(e);
});
this.addEventListener(EventType.DIALOG_CHANGE, e => {
- this._handleDialogChange(e as CustomEvent<DialogChangeEventDetail>);
+ this.handleDialogChange(e as CustomEvent<DialogChangeEventDetail>);
});
- this.addEventListener(EventType.LOCATION_CHANGE, e =>
- this._handleLocationChange(e)
+ document.addEventListener(EventType.LOCATION_CHANGE, e =>
+ this.handleLocationChange(e)
);
this.addEventListener(EventType.RECREATE_CHANGE_VIEW, () =>
- this.handleRecreateView(GerritView.CHANGE)
+ this.handleRecreateView()
);
this.addEventListener(EventType.RECREATE_DIFF_VIEW, () =>
- this.handleRecreateView(GerritView.DIFF)
+ this.handleRecreateView()
+ );
+ document.addEventListener(EventType.GR_RPC_LOG, e => this.handleRpcLog(e));
+ this.shortcuts.addAbstract(Shortcut.OPEN_SHORTCUT_HELP_DIALOG, () =>
+ this.showKeyboardShortcuts()
+ );
+ this.shortcuts.addAbstract(Shortcut.GO_TO_USER_DASHBOARD, () =>
+ this.goToUserDashboard()
+ );
+ this.shortcuts.addAbstract(Shortcut.GO_TO_OPENED_CHANGES, () =>
+ this.goToOpenedChanges()
+ );
+ this.shortcuts.addAbstract(Shortcut.GO_TO_MERGED_CHANGES, () =>
+ this.goToMergedChanges()
+ );
+ this.shortcuts.addAbstract(Shortcut.GO_TO_ABANDONED_CHANGES, () =>
+ this.goToAbandonedChanges()
+ );
+ this.shortcuts.addAbstract(Shortcut.GO_TO_WATCHED_CHANGES, () =>
+ this.goToWatchedChanges()
);
- document.addEventListener(EventType.GR_RPC_LOG, e => this._handleRpcLog(e));
}
- override ready() {
- super.ready();
- this._updateLoginUrl();
+ override connectedCallback() {
+ super.connectedCallback();
+ const resizeObserver = this.getBrowserModel().observeWidth();
+ resizeObserver.observe(this);
+
this.reporting.appStarted();
- this.$.router.start();
+ this.router.start();
this.restApiService.getAccount().then(account => {
- this._account = account;
+ this.account = account;
if (account) {
this.reporting.reportLifeCycle(LifeCycle.STARTED_AS_USER);
} else {
@@ -269,32 +240,28 @@ export class GrAppElement extends base {
}
});
this.restApiService.getConfig().then(config => {
- this._serverConfig = config;
-
- if (config && config.gerrit && config.gerrit.report_bug_url) {
- this._feedbackUrl = config.gerrit.report_bug_url;
- }
+ this.serverConfig = config;
});
this.restApiService.getVersion().then(version => {
- this._version = version;
- this._logWelcome();
+ this.version = version;
+ this.logWelcome();
});
- if (window.localStorage.getItem('dark-theme')) {
- applyDarkTheme();
- }
+ const isDarkTheme = !!window.localStorage.getItem('dark-theme');
+ document.documentElement.classList.toggle('darkTheme', isDarkTheme);
+ document.documentElement.classList.toggle('lightTheme', !isDarkTheme);
+ if (isDarkTheme) applyDarkTheme();
// Note: this is evaluated here to ensure that it only happens after the
// router has been initialized. @see Issue 7837
- this._settingsUrl = GerritNav.getUrlForSettings();
+ this.settingsUrl = GerritNav.getUrlForSettings();
- this._viewState = {
+ this.viewState = {
changeView: {
changeNum: null,
patchRange: null,
selectedFileIndex: 0,
showReplyDialog: false,
- showDownloadDialog: false,
diffMode: null,
numFilesShown: null,
},
@@ -307,85 +274,374 @@ export class GrAppElement extends base {
};
}
- _accountChanged(account?: AccountDetailInfo) {
- if (!account) return;
+ static override get styles() {
+ return [
+ sharedStyles,
+ css`
+ :host {
+ background-color: var(--background-color-tertiary);
+ display: flex;
+ flex-direction: column;
+ min-height: 100%;
+ }
+ gr-main-header,
+ footer {
+ color: var(--primary-text-color);
+ }
+ gr-main-header {
+ background: var(
+ --header-background,
+ var(--header-background-color, #eee)
+ );
+ padding: var(--header-padding);
+ border-bottom: var(--header-border-bottom);
+ border-image: var(--header-border-image);
+ border-right: 0;
+ border-left: 0;
+ border-top: 0;
+ box-shadow: var(--header-box-shadow);
+ /* Make sure the header is above the main content, to preserve box-shadow
+ visibility. We need 2 here instead of 1, because dropdowns in the
+ header should be shown on top of the sticky diff header, which has a
+ z-index of 1. */
+ z-index: 2;
+ }
+ footer {
+ background: var(
+ --footer-background,
+ var(--footer-background-color, #eee)
+ );
+ border-top: var(--footer-border-top);
+ display: flex;
+ justify-content: space-between;
+ padding: var(--spacing-m) var(--spacing-l);
+ z-index: 100;
+ }
+ main {
+ flex: 1;
+ padding-bottom: var(--spacing-xxl);
+ position: relative;
+ }
+ .errorView {
+ align-items: center;
+ display: none;
+ flex-direction: column;
+ justify-content: center;
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ }
+ .errorView.show {
+ display: flex;
+ }
+ .errorEmoji {
+ font-size: 2.6rem;
+ }
+ .errorText,
+ .errorMoreInfo {
+ margin-top: var(--spacing-m);
+ }
+ .errorText {
+ font-family: var(--header-font-family);
+ font-size: var(--font-size-h3);
+ font-weight: var(--font-weight-h3);
+ line-height: var(--line-height-h3);
+ }
+ .errorMoreInfo {
+ color: var(--deemphasized-text-color);
+ }
+ `,
+ ];
+ }
+
+ override render() {
+ return html`
+ <gr-css-mixins></gr-css-mixins>
+ <gr-endpoint-decorator name="banner"></gr-endpoint-decorator>
+ <gr-main-header
+ id="mainHeader"
+ .searchQuery=${(this.params as AppElementSearchParam)?.query}
+ @mobile-search=${this.mobileSearchToggle}
+ @show-keyboard-shortcuts=${this.handleShowKeyboardShortcuts}
+ .mobileSearchHidden=${!this.mobileSearch}
+ .loginUrl=${loginUrl(this.serverConfig?.auth)}
+ .loginText=${this.serverConfig?.auth.login_text ?? 'Sign in'}
+ ?aria-hidden=${this.footerHeaderAriaHidden}
+ >
+ </gr-main-header>
+ <main ?aria-hidden=${this.mainAriaHidden}>
+ ${this.renderMobileSearch()} ${this.renderChangeListView()}
+ ${this.renderDashboardView()} ${this.renderChangeView()}
+ ${this.renderEditorView()} ${this.renderDiffView()}
+ ${this.renderSettingsView()} ${this.renderAdminView()}
+ ${this.renderPluginScreen()} ${this.renderCLAView()}
+ ${this.renderDocumentationSearch()}
+ <div id="errorView" class="errorView">
+ <div class="errorEmoji">${this.lastError?.emoji}</div>
+ <div class="errorText">${this.lastError?.text}</div>
+ <div class="errorMoreInfo">${this.lastError?.moreInfo}</div>
+ </div>
+ </main>
+ <footer ?aria-hidden=${this.footerHeaderAriaHidden}>
+ <div>
+ Powered by
+ <a
+ href="https://www.gerritcodereview.com/"
+ rel="noopener"
+ target="_blank"
+ >Gerrit Code Review</a
+ >
+ (${this.version})
+ <gr-endpoint-decorator name="footer-left"></gr-endpoint-decorator>
+ </div>
+ <div>
+ Press “?” for keyboard shortcuts
+ <gr-endpoint-decorator name="footer-right"></gr-endpoint-decorator>
+ </div>
+ </footer>
+ ${this.renderKeyboardShortcutsDialog()} ${this.renderRegistrationDialog()}
+ <gr-endpoint-decorator name="plugin-overlay"></gr-endpoint-decorator>
+ <gr-error-manager
+ id="errorManager"
+ .loginUrl=${loginUrl(this.serverConfig?.auth)}
+ .loginText=${this.serverConfig?.auth.login_text ?? 'Sign in'}
+ ></gr-error-manager>
+ <gr-plugin-host id="plugins" .config=${this.serverConfig}>
+ </gr-plugin-host>
+ <gr-external-style
+ id="externalStyleForAll"
+ name="app-theme"
+ ></gr-external-style>
+ <gr-external-style
+ id="externalStyleForTheme"
+ .name=${this.getThemeEndpoint()}
+ ></gr-external-style>
+ `;
+ }
+
+ private renderMobileSearch() {
+ if (!this.mobileSearch) return nothing;
+ return html`
+ <gr-smart-search
+ id="search"
+ label="Search for changes"
+ .searchQuery=${(this.params as AppElementSearchParam)?.query}
+ .serverConfig=${this.serverConfig}
+ >
+ </gr-smart-search>
+ `;
+ }
+
+ private renderChangeListView() {
+ if (!this.showChangeListView) return nothing;
+ return html`
+ <gr-change-list-view
+ .params=${this.params}
+ .account=${this.account}
+ .viewState=${this.viewState?.changeListView}
+ @view-state-change-list-view-changed=${this.handleViewStateChanged}
+ ></gr-change-list-view>
+ `;
+ }
+
+ private renderDashboardView() {
+ if (!this.showDashboardView) return nothing;
+ return html`
+ <gr-dashboard-view
+ .account=${this.account}
+ .params=${this.params}
+ .viewState=${this.viewState?.dashboardView}
+ ></gr-dashboard-view>
+ `;
+ }
+
+ private renderChangeView() {
+ if (this.invalidateChangeViewCache) {
+ this.updateComplete.then(() => (this.invalidateChangeViewCache = false));
+ return nothing;
+ }
+ return cache(this.showChangeView ? this.changeViewTemplate() : nothing);
+ }
+
+ // Template as not to create duplicates, for renderChangeView() only.
+ private changeViewTemplate() {
+ return html`
+ <gr-change-view
+ .params=${this.params}
+ .viewState=${this.viewState?.changeView}
+ .backPage=${this.lastSearchPage}
+ @view-state-change-view-changed=${this.handleViewStateChangeViewChanged}
+ ></gr-change-view>
+ `;
+ }
+
+ private renderEditorView() {
+ if (!this.showEditorView) return nothing;
+ return html`<gr-editor-view .params=${this.params}></gr-editor-view>`;
+ }
+
+ private renderDiffView() {
+ if (this.invalidateDiffViewCache) {
+ this.updateComplete.then(() => (this.invalidateDiffViewCache = false));
+ return nothing;
+ }
+ return cache(this.showDiffView ? this.diffViewTemplate() : nothing);
+ }
+
+ private diffViewTemplate() {
+ return html`
+ <gr-diff-view
+ .params=${this.params}
+ .changeViewState=${this.viewState?.changeView}
+ @view-state-change-view-changed=${this.handleViewStateChangeViewChanged}
+ ></gr-diff-view>
+ `;
+ }
+
+ private renderSettingsView() {
+ if (!this.showSettingsView) return nothing;
+ return html`
+ <gr-settings-view
+ .params=${this.params}
+ @account-detail-update=${this.handleAccountDetailUpdate}
+ >
+ </gr-settings-view>
+ `;
+ }
+
+ private renderAdminView() {
+ if (!this.showAdminView) return nothing;
+ return html`<gr-admin-view
+ .path=${this.path}
+ .params=${this.params}
+ ></gr-admin-view>`;
+ }
+
+ private renderPluginScreen() {
+ if (!this.showPluginScreen) return nothing;
+ return html`
+ <gr-endpoint-decorator .name=${this.computePluginScreenName()}>
+ <gr-endpoint-param
+ name="token"
+ .value=${(this.params as AppElementPluginScreenParams).screen}
+ ></gr-endpoint-param>
+ </gr-endpoint-decorator>
+ `;
+ }
+
+ private renderCLAView() {
+ if (!this.showCLAView) return nothing;
+ return html`<gr-cla-view></gr-cla-view>`;
+ }
+
+ private renderDocumentationSearch() {
+ if (!this.showDocumentationSearch) return nothing;
+ return html`
+ <gr-documentation-search .params=${this.params}></gr-documentation-search>
+ `;
+ }
+
+ private renderKeyboardShortcutsDialog() {
+ if (!this.loadKeyboardShortcutsDialog) return nothing;
+ return html`
+ <gr-overlay
+ id="keyboardShortcuts"
+ with-backdrop=""
+ @iron-overlay-canceled=${this.onOverlayCanceled}
+ >
+ <gr-keyboard-shortcuts-dialog
+ @close=${this.handleKeyboardShortcutDialogClose}
+ ></gr-keyboard-shortcuts-dialog>
+ </gr-overlay>
+ `;
+ }
+
+ private renderRegistrationDialog() {
+ if (!this.loadRegistrationDialog) return nothing;
+ return html`
+ <gr-overlay id="registrationOverlay" with-backdrop="">
+ <gr-registration-dialog
+ id="registrationDialog"
+ .settingsUrl=${this.settingsUrl}
+ @account-detail-update=${this.handleAccountDetailUpdate}
+ @close=${this.handleRegistrationDialogClose}
+ >
+ </gr-registration-dialog>
+ </gr-overlay>
+ `;
+ }
+
+ override willUpdate(changedProperties: PropertyValues) {
+ if (changedProperties.has('account')) {
+ this.accountChanged();
+ }
+
+ if (changedProperties.has('params')) {
+ this.viewChanged();
+
+ this.paramsChanged();
+ }
+ }
+
+ private accountChanged() {
+ if (!this.account) return;
// Preferences are cached when a user is logged in; warm them.
this.restApiService.getPreferences();
this.restApiService.getDiffPreferences();
this.restApiService.getEditPreferences();
- this.$.errorManager.knownAccountId =
- (this._account && this._account._account_id) || null;
+ if (this.errorManager)
+ this.errorManager.knownAccountId =
+ (this.account && this.account._account_id) || null;
}
/**
* Throws away the view and re-creates it. The view itself fires an event, if
* it wants to be re-created.
*/
- private handleRecreateView(view: GerritView.DIFF | GerritView.CHANGE) {
- const isDiff = view === GerritView.DIFF;
- const domId = isDiff ? '#dom-if-diff-view' : '#dom-if-change-view';
- const domIf = this.root!.querySelector(domId) as DomIf;
- assertIsDefined(domIf, '<dom-if> for the view');
- // The rendering of DomIf is debounced, so just changing _show...View and
- // restamp properties back and forth won't work. That is why we are using
- // timeouts.
- // The first timeout is needed, because the _viewChanged() observer also
- // affects _show...View and would change _show...View=false directly back to
- // _show...View=true.
- setTimeout(() => {
- this._showChangeView = false;
- this._showDiffView = false;
- domIf.restamp = true;
- setTimeout(() => {
- this._showChangeView = this.params?.view === GerritView.CHANGE;
- this._showDiffView = this.params?.view === GerritView.DIFF;
- domIf.restamp = false;
- }, 1);
- }, 1);
- }
-
- @observe('params.*')
- _viewChanged() {
+ private handleRecreateView() {
+ this.invalidateChangeViewCache = true;
+ this.invalidateDiffViewCache = true;
+ }
+
+ private async viewChanged() {
const view = this.params?.view;
- this.$.errorView.classList.remove('show');
- this._showChangeListView = view === GerritView.SEARCH;
- this._showDashboardView = view === GerritView.DASHBOARD;
- this._showChangeView = view === GerritView.CHANGE;
- this._showDiffView = view === GerritView.DIFF;
- this._showSettingsView = view === GerritView.SETTINGS;
- // _showAdminView must be in sync with the gr-admin-view AdminViewParams type
- this._showAdminView =
+ this.errorView?.classList.remove('show');
+ this.showChangeListView = view === GerritView.SEARCH;
+ this.showDashboardView = view === GerritView.DASHBOARD;
+ this.showChangeView = view === GerritView.CHANGE;
+ this.showDiffView = view === GerritView.DIFF;
+ this.showSettingsView = view === GerritView.SETTINGS;
+ // showAdminView must be in sync with the gr-admin-view AdminViewParams type
+ this.showAdminView =
view === GerritView.ADMIN ||
view === GerritView.GROUP ||
view === GerritView.REPO;
- this._showCLAView = view === GerritView.AGREEMENTS;
- this._showEditorView = view === GerritView.EDIT;
+ this.showCLAView = view === GerritView.AGREEMENTS;
+ this.showEditorView = view === GerritView.EDIT;
const isPluginScreen = view === GerritView.PLUGIN_SCREEN;
- this._showPluginScreen = false;
+ this.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.
+ // because showPluginScreen value does not change. To force restamp,
+ // change showPluginScreen value between true and false.
if (isPluginScreen) {
- setTimeout(() => (this._showPluginScreen = true), 1);
+ setTimeout(() => (this.showPluginScreen = true), 1);
}
- this._showDocumentationSearch = view === GerritView.DOCUMENTATION_SEARCH;
+ this.showDocumentationSearch = view === GerritView.DOCUMENTATION_SEARCH;
if (
this.params &&
isAppElementJustRegisteredParams(this.params) &&
this.params.justRegistered
) {
this.loadRegistrationDialog = true;
- flush();
- const registrationOverlay = this.shadowRoot!.querySelector(
- '#registrationOverlay'
- ) as GrOverlay;
- const registrationDialog = this.shadowRoot!.querySelector(
- '#registrationDialog'
- ) as GrRegistrationDialog;
- registrationOverlay.open();
- registrationDialog.loadData().then(() => {
- registrationOverlay.refit();
+ await this.updateComplete;
+ assertIsDefined(this.registrationOverlay, 'registrationOverlay');
+ assertIsDefined(this.registrationDialog, 'registrationDialog');
+ this.registrationOverlay.open();
+ this.registrationDialog.loadData().then(() => {
+ this.registrationOverlay!.refit();
});
}
// To fix bug announce read after each new view, we reset announce with
@@ -393,27 +649,28 @@ export class GrAppElement extends base {
fireIronAnnounce(this, ' ');
}
- _handlePageError(e: CustomEvent<PageErrorEventDetail>) {
+ private handlePageError(e: CustomEvent<PageErrorEventDetail>) {
const props = [
- '_showChangeListView',
- '_showDashboardView',
- '_showChangeView',
- '_showDiffView',
- '_showSettingsView',
- '_showAdminView',
+ 'showChangeListView',
+ 'showDashboardView',
+ 'showChangeView',
+ 'showDiffView',
+ 'showSettingsView',
+ 'showAdminView',
];
for (const showProp of props) {
- this.set(showProp, false);
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (this as any)[showProp as any] = false;
}
- this.$.errorView.classList.add('show');
+ this.errorView?.classList.add('show');
const response = e.detail.response;
const err: ErrorInfo = {
text: [response?.status, response?.statusText].join(' '),
};
if (response?.status === 404) {
err.emoji = '¯\\_(ツ)_/¯';
- this._lastError = err;
+ this.lastError = err;
} else {
err.emoji = 'o_O';
if (response) {
@@ -427,60 +684,31 @@ export class GrAppElement extends base {
errorText: text,
trace,
});
- this._lastError = err;
+ this.lastError = err;
});
}
}
}
- _handleLocationChange(e: LocationChangeEvent) {
- this._updateLoginUrl();
-
+ private handleLocationChange(e: LocationChangeEvent) {
+ this.requestUpdate();
const hash = e.detail.hash.substring(1);
let pathname = e.detail.pathname;
if (pathname.startsWith('/c/') && Number(hash) > 0) {
pathname += '@' + hash;
}
- this._path = pathname;
- }
-
- _updateLoginUrl() {
- const baseUrl = getBaseUrl();
- if (baseUrl) {
- // Strip the canonical path from the path since needing canonical in
- // the path is unneeded and breaks the url.
- this._loginUrl =
- baseUrl +
- '/login/' +
- encodeURIComponent(
- '/' +
- window.location.pathname.substring(baseUrl.length) +
- window.location.search +
- window.location.hash
- );
- } else {
- this._loginUrl =
- '/login/' +
- encodeURIComponent(
- window.location.pathname +
- window.location.search +
- window.location.hash
- );
- }
+ this.path = pathname;
}
- @observe('params.*')
- _paramsChanged(
- paramsRecord: ElementPropertyDeepChange<GrAppElement, 'params'>
- ) {
- const params = paramsRecord.base;
+ // private but used in test
+ paramsChanged() {
const viewsToCheck = [GerritView.SEARCH, GerritView.DASHBOARD];
- if (params?.view && viewsToCheck.includes(params.view)) {
- this._lastSearchPage = location.pathname;
+ if (this.params?.view && viewsToCheck.includes(this.params.view)) {
+ this.lastSearchPage = location.pathname;
}
}
- _handleTitleChange(e: CustomEvent<TitleChangeEventDetail>) {
+ private handleTitleChange(e: CustomEvent<TitleChangeEventDetail>) {
if (e.detail.title) {
document.title = e.detail.title + ' · Gerrit Code Review';
} else {
@@ -488,104 +716,96 @@ export class GrAppElement extends base {
}
}
- _handleDialogChange(e: CustomEvent<DialogChangeEventDetail>) {
+ private handleDialogChange(e: CustomEvent<DialogChangeEventDetail>) {
if (e.detail.canceled) {
- this._footerHeaderAriaHidden = false;
+ this.footerHeaderAriaHidden = false;
} else if (e.detail.opened) {
- this._footerHeaderAriaHidden = true;
+ this.footerHeaderAriaHidden = true;
}
}
- handleShowKeyboardShortcuts() {
+ private async handleShowKeyboardShortcuts() {
this.loadKeyboardShortcutsDialog = true;
- flush();
- (this.shadowRoot!.querySelector('#keyboardShortcuts') as GrOverlay).open();
+ await this.updateComplete;
+ assertIsDefined(this.keyboardShortcuts, 'keyboardShortcuts');
+ this.keyboardShortcuts.open();
}
- _showKeyboardShortcuts() {
+ private async showKeyboardShortcuts() {
// same shortcut should close the dialog if pressed again
// when dialog is open
this.loadKeyboardShortcutsDialog = true;
- flush();
- const keyboardShortcuts = this.shadowRoot!.querySelector(
- '#keyboardShortcuts'
- ) as GrOverlay;
- if (!keyboardShortcuts) return;
- if (keyboardShortcuts.opened) {
- keyboardShortcuts.cancel();
+ await this.updateComplete;
+ if (!this.keyboardShortcuts) return;
+ if (this.keyboardShortcuts.opened) {
+ this.keyboardShortcuts.cancel();
return;
}
- keyboardShortcuts.open();
- this._footerHeaderAriaHidden = true;
- this._mainAriaHidden = true;
+ this.keyboardShortcuts.open();
+ this.footerHeaderAriaHidden = true;
+ this.mainAriaHidden = true;
}
- _handleKeyboardShortcutDialogClose() {
- (
- this.shadowRoot!.querySelector('#keyboardShortcuts') as GrOverlay
- ).cancel();
+ private handleKeyboardShortcutDialogClose() {
+ assertIsDefined(this.keyboardShortcuts, 'keyboardShortcuts');
+ this.keyboardShortcuts.close();
}
onOverlayCanceled() {
- this._footerHeaderAriaHidden = false;
- this._mainAriaHidden = false;
+ this.footerHeaderAriaHidden = false;
+ this.mainAriaHidden = false;
}
- _handleAccountDetailUpdate() {
- this.$.mainHeader.reload();
+ private handleAccountDetailUpdate() {
+ this.mainHeader?.reload();
if (this.params?.view === GerritView.SETTINGS) {
- (
- this.shadowRoot!.querySelector('gr-settings-view') as GrSettingsView
- ).reloadAccountDetail();
+ assertIsDefined(this.settingdView, 'settingdView');
+ this.settingdView.reloadAccountDetail();
}
}
- _handleRegistrationDialogClose() {
+ private handleRegistrationDialogClose() {
// The registration dialog is visible only if this.params is
// instanceof AppElementJustRegisteredParams
(this.params as AppElementJustRegisteredParams).justRegistered = false;
- (
- this.shadowRoot!.querySelector('#registrationOverlay') as GrOverlay
- ).close();
+ assertIsDefined(this.registrationOverlay, 'registrationOverlay');
+ this.registrationOverlay.close();
}
- _goToOpenedChanges() {
+ private goToOpenedChanges() {
GerritNav.navigateToStatusSearch('open');
}
- _goToUserDashboard() {
+ private goToUserDashboard() {
GerritNav.navigateToUserDashboard();
}
- _goToMergedChanges() {
+ private goToMergedChanges() {
GerritNav.navigateToStatusSearch('merged');
}
- _goToAbandonedChanges() {
+ private goToAbandonedChanges() {
GerritNav.navigateToStatusSearch('abandoned');
}
- _goToWatchedChanges() {
+ private goToWatchedChanges() {
// The query is hardcoded, and doesn't respect custom menu entries
GerritNav.navigateToSearchQuery('is:watched is:open');
}
- _computePluginScreenName(params: AppElementParams) {
- if (params.view !== GerritView.PLUGIN_SCREEN) return '';
- if (!params.plugin || !params.screen) return '';
- return `${params.plugin}-screen-${params.screen}`;
+ private computePluginScreenName() {
+ if (this.params?.view !== GerritView.PLUGIN_SCREEN) return '';
+ if (!this.params.plugin || !this.params.screen) return '';
+ return `${this.params.plugin}-screen-${this.params.screen}`;
}
- _logWelcome() {
+ private logWelcome() {
console.group('Runtime Info');
console.info('Gerrit UI (PolyGerrit)');
- console.info(`Gerrit Server Version: ${this._version}`);
+ console.info(`Gerrit Server Version: ${this.version}`);
if (window.VERSION_INFO) {
console.info(`UI Version Info: ${window.VERSION_INFO}`);
}
- if (this._feedbackUrl) {
- console.info(`Please file bugs and feedback at: ${this._feedbackUrl}`);
- }
console.groupEnd();
}
@@ -594,11 +814,11 @@ export class GrAppElement extends base {
* Note: the REST API interface cannot use gr-reporting directly because
* that would create a cyclic dependency.
*/
- _handleRpcLog(e: RpcLogEvent) {
+ private handleRpcLog(e: RpcLogEvent) {
this.reporting.reportRpcTiming(e.detail.anonymizedUrl, e.detail.elapsed);
}
- _mobileSearchToggle() {
+ private mobileSearchToggle() {
this.mobileSearch = !this.mobileSearch;
}
@@ -608,6 +828,24 @@ export class GrAppElement extends base {
? 'app-theme-dark'
: 'app-theme-light';
}
+
+ private handleViewStateChanged(e: ValueChangedEvent<ChangeListViewState>) {
+ if (!this.viewState) return;
+ this.viewState.changeListView = {
+ ...this.viewState.changeListView,
+ ...e.detail.value,
+ };
+ }
+
+ private handleViewStateChangeViewChanged(
+ e: ValueChangedEvent<ChangeViewState>
+ ) {
+ if (!this.viewState) return;
+ this.viewState.changeView = {
+ ...this.viewState.changeView,
+ ...e.detail.value,
+ };
+ }
}
declare global {