blob: 2086270f04e8801ab0ac46cb3fa591df51bc23fd [file] [log] [blame]
Dave Borowitz8cdc76b2018-03-26 10:04:27 -04001/**
2 * @license
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +01003 * Copyright (C) 2015 The Android Open Source Project
Dave Borowitz8cdc76b2018-03-26 10:04:27 -04004 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
Andrew Bonventre78792e82016-03-04 17:48:22 -050017
Dmitrii Filippov8814de12020-10-10 16:31:21 +020018import '../../shared/gr-icons/gr-icons';
Dmitrii Filippov8814de12020-10-10 16:31:21 +020019import '../gr-change-list/gr-change-list';
20import '../gr-repo-header/gr-repo-header';
21import '../gr-user-header/gr-user-header';
22import '../../../styles/shared-styles';
Dmitrii Filippov8814de12020-10-10 16:31:21 +020023import {PolymerElement} from '@polymer/polymer/polymer-element';
24import {htmlTemplate} from './gr-change-list-view_html';
25import {page} from '../../../utils/page-wrapper-utils';
Ben Rohlfsebe4acc2020-12-11 21:16:10 +010026import {GerritNav} from '../../core/gr-navigation/gr-navigation';
Dmitrii Filippov8814de12020-10-10 16:31:21 +020027import {customElement, property} from '@polymer/decorators';
28import {AppElementParams} from '../../gr-app-types';
29import {
30 AccountDetailInfo,
31 AccountId,
Dmitrii Filippov8814de12020-10-10 16:31:21 +020032 ChangeInfo,
33 EmailAddress,
Dmitrii Filippov8814de12020-10-10 16:31:21 +020034 PreferencesInput,
35} from '../../../types/common';
Dmitrii Filippov8814de12020-10-10 16:31:21 +020036import {ChangeStarToggleStarDetail} from '../../shared/gr-change-star/gr-change-star';
Dmitrii Filippov6a038002020-10-14 18:50:07 +020037import {ChangeListViewState} from '../../../types/types';
Milutin Kristofic60150132020-11-23 20:15:23 +010038import {fireTitleChange} from '../../../utils/event-util';
Ben Rohlfs43935a42020-12-01 19:14:09 +010039import {appContext} from '../../../services/app-context';
Ben Rohlfsebe4acc2020-12-11 21:16:10 +010040import {GerritView} from '../../../services/router/router-model';
Kasper Nilssona81a8a32017-03-13 14:46:44 -070041
Ben Rohlfs7b71b112021-02-12 10:36:08 +010042const LOOKUP_QUERY_PATTERNS: RegExp[] = [
43 /^\s*i?[0-9a-f]{7,40}\s*$/i, // CHANGE_ID
44 /^\s*[1-9][0-9]*\s*$/g, // CHANGE_NUM
45 /[0-9a-f]{40}/, // COMMIT
46];
Dhruv Srivastava6f8c9e32020-08-26 12:01:49 +000047
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010048const USER_QUERY_PATTERN = /^owner:\s?("[^"]+"|[^ ]+)$/;
Becky Siegel20789302018-01-05 14:18:05 -080049
Dmitrii Filippov8814de12020-10-10 16:31:21 +020050const REPO_QUERY_PATTERN = /^project:\s?("[^"]+"|[^ ]+)(\sstatus\s?:(open|"open"))?$/;
Logan Hanks29693ac2017-07-12 10:11:21 -070051
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010052const LIMIT_OPERATOR_PATTERN = /\blimit:(\d+)/i;
53
Dmitrii Filippov8814de12020-10-10 16:31:21 +020054export interface GrChangeListView {
55 $: {
Dmitrii Filippov8814de12020-10-10 16:31:21 +020056 prevArrow: HTMLAnchorElement;
57 nextArrow: HTMLAnchorElement;
58 };
59}
60
61@customElement('gr-change-list-view')
Ben Rohlfs27c856da2021-03-12 14:09:10 +010062export class GrChangeListView extends PolymerElement {
Dmitrii Filippov8814de12020-10-10 16:31:21 +020063 static get template() {
64 return htmlTemplate;
65 }
66
Dmitrii Filippov3fd2b102019-11-15 16:16:46 +010067 /**
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010068 * Fired when the title of the page should change.
69 *
70 * @event title-change
Tao Zhou9a076812019-12-17 09:59:28 +010071 */
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +010072
Dmitrii Filippov8814de12020-10-10 16:31:21 +020073 @property({type: Object, observer: '_paramsChanged'})
74 params?: AppElementParams;
Andrew Bonventre78792e82016-03-04 17:48:22 -050075
Dmitrii Filippov8814de12020-10-10 16:31:21 +020076 @property({type: Boolean, computed: '_computeLoggedIn(account)'})
77 _loggedIn?: boolean;
Andrew Bonventre78792e82016-03-04 17:48:22 -050078
Dmitrii Filippov8814de12020-10-10 16:31:21 +020079 @property({type: Object})
80 account: AccountDetailInfo | null = null;
Wyatt Allenfc0c84f2018-03-02 10:52:46 -080081
Dmitrii Filippov8814de12020-10-10 16:31:21 +020082 @property({type: Object, notify: true})
83 viewState: ChangeListViewState = {};
Andrew Bonventre78792e82016-03-04 17:48:22 -050084
Dmitrii Filippov8814de12020-10-10 16:31:21 +020085 @property({type: Object})
86 preferences?: PreferencesInput;
Andrew Bonventre78792e82016-03-04 17:48:22 -050087
Dmitrii Filippov8814de12020-10-10 16:31:21 +020088 @property({type: Number})
89 _changesPerPage?: number;
Kasper Nilsson8e6a64d2018-04-04 15:33:19 -070090
Dmitrii Filippov8814de12020-10-10 16:31:21 +020091 @property({type: String})
92 _query = '';
Urs Wolfer54a5a462016-03-09 21:04:22 +010093
Dmitrii Filippov8814de12020-10-10 16:31:21 +020094 @property({type: Number})
95 _offset?: number;
Andrew Bonventre78792e82016-03-04 17:48:22 -050096
Dmitrii Filippov8814de12020-10-10 16:31:21 +020097 @property({type: Array, observer: '_changesChanged'})
98 _changes?: ChangeInfo[];
Andrew Bonventre78792e82016-03-04 17:48:22 -050099
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200100 @property({type: Boolean})
101 _loading = true;
Andrew Bonventre78792e82016-03-04 17:48:22 -0500102
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200103 @property({type: String})
104 _userId: AccountId | EmailAddress | null = null;
Wyatt Allen5c3a3cd2017-08-30 13:43:37 -0700105
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200106 @property({type: String})
107 _repo: string | null = null;
Becky Siegel20789302018-01-05 14:18:05 -0800108
Dhruv Srivastava9f65d912021-02-22 10:36:19 +0100109 private readonly restApiService = appContext.restApiService;
Ben Rohlfs43935a42020-12-01 19:14:09 +0100110
Dhruv Srivastava5841dd82021-05-17 11:03:19 +0200111 private reporting = appContext.reportingService;
112
Ben Rohlfsf7f1e8e2021-03-12 14:36:40 +0100113 constructor() {
114 super();
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200115 this.addEventListener('next-page', () => this._handleNextPage());
116 this.addEventListener('previous-page', () => this._handlePreviousPage());
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100117 }
118
119 /** @override */
Ben Rohlfs5f520da2021-03-10 14:58:43 +0100120 connectedCallback() {
121 super.connectedCallback();
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100122 this._loadPreferences();
123 }
124
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200125 _paramsChanged(value: AppElementParams) {
126 if (value.view !== GerritView.SEARCH) return;
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100127
128 this._loading = true;
129 this._query = value.query;
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200130 const offset = Number(value.offset);
131 this._offset = isNaN(offset) ? 0 : offset;
132 if (
133 this.viewState.query !== this._query ||
134 this.viewState.offset !== this._offset
135 ) {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100136 this.set('viewState.selectedChangeIndex', 0);
137 this.set('viewState.query', this._query);
138 this.set('viewState.offset', this._offset);
Dmitrii Filippov3fd2b102019-11-15 16:16:46 +0100139 }
Andrew Bonventre78792e82016-03-04 17:48:22 -0500140
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100141 // NOTE: This method may be called before attachment. Fire title-change
142 // in an async so that attachment to the DOM can take place first.
Ben Rohlfs6b078932021-03-10 15:20:03 +0100143 setTimeout(() => fireTitleChange(this, this._query));
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200144
Ben Rohlfs43935a42020-12-01 19:14:09 +0100145 this.restApiService
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200146 .getPreferences()
147 .then(prefs => {
148 if (!prefs) {
149 throw new Error('getPreferences returned undefined');
150 }
151 this._changesPerPage = prefs.changes_per_page;
152 return this._getChanges();
153 })
154 .then(changes => {
155 changes = changes || [];
156 if (this._query && changes.length === 1) {
Ben Rohlfs7b71b112021-02-12 10:36:08 +0100157 for (const queryPattern of LOOKUP_QUERY_PATTERNS) {
158 if (this._query.match(queryPattern)) {
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200159 // "Back"/"Forward" buttons work correctly only with
160 // opt_redirect options
161 GerritNav.navigateToChange(
162 changes[0],
163 undefined,
164 undefined,
165 undefined,
166 true
167 );
168 return;
Dhruv Srivastava6f8c9e32020-08-26 12:01:49 +0000169 }
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100170 }
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200171 }
172 this._changes = changes;
173 this._loading = false;
174 });
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100175 }
Andrew Bonventre78792e82016-03-04 17:48:22 -0500176
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100177 _loadPreferences() {
Ben Rohlfs43935a42020-12-01 19:14:09 +0100178 return this.restApiService.getLoggedIn().then(loggedIn => {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100179 if (loggedIn) {
Ben Rohlfs43935a42020-12-01 19:14:09 +0100180 this.restApiService.getPreferences().then(preferences => {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100181 this.preferences = preferences;
182 });
183 } else {
184 this.preferences = {};
Logan Hanks29693ac2017-07-12 10:11:21 -0700185 }
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100186 });
187 }
Logan Hanks29693ac2017-07-12 10:11:21 -0700188
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100189 _getChanges() {
Ben Rohlfs43935a42020-12-01 19:14:09 +0100190 return this.restApiService.getChanges(
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200191 this._changesPerPage,
192 this._query,
193 this._offset
194 );
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100195 }
Andrew Bonventre78792e82016-03-04 17:48:22 -0500196
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200197 _limitFor(query: string, defaultLimit: number) {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100198 const match = query.match(LIMIT_OPERATOR_PATTERN);
199 if (!match) {
200 return defaultLimit;
Dmitrii Filippov3fd2b102019-11-15 16:16:46 +0100201 }
Dhruv Srivastavab8edee92020-10-19 10:20:07 +0200202 return Number(match[1]);
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100203 }
Kasper Nilsson97240922018-02-08 14:57:27 -0800204
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200205 _computeNavLink(
206 query: string,
207 offset: number | undefined,
208 direction: number,
209 changesPerPage: number
210 ) {
211 offset = offset ?? 0;
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100212 const limit = this._limitFor(query, changesPerPage);
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200213 const newOffset = Math.max(0, offset + limit * direction);
Dmitrii Filippoveb8b2692020-04-06 18:02:35 +0200214 return GerritNav.getUrlForSearchQuery(query, newOffset);
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100215 }
216
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200217 _computePrevArrowClass(offset?: number) {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100218 return offset === 0 ? 'hide' : '';
219 }
220
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200221 _computeNextArrowClass(changes?: ChangeInfo[]) {
222 const more = changes?.length && changes[changes.length - 1]._more_changes;
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100223 return more ? '' : 'hide';
224 }
225
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200226 _computeNavClass(loading?: boolean) {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100227 return loading || !this._changes || !this._changes.length ? 'hide' : '';
228 }
229
230 _handleNextPage() {
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200231 if (this.$.nextArrow.hidden || !this._changesPerPage) return;
232 page.show(
233 this._computeNavLink(this._query, this._offset, 1, this._changesPerPage)
234 );
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100235 }
236
237 _handlePreviousPage() {
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200238 if (this.$.prevArrow.hidden || !this._changesPerPage) return;
239 page.show(
240 this._computeNavLink(this._query, this._offset, -1, this._changesPerPage)
241 );
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100242 }
243
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200244 _changesChanged(changes?: ChangeInfo[]) {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100245 this._userId = null;
246 this._repo = null;
247 if (!changes || !changes.length) {
248 return;
Dmitrii Filippov3fd2b102019-11-15 16:16:46 +0100249 }
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100250 if (USER_QUERY_PATTERN.test(this._query)) {
251 const owner = changes[0].owner;
252 const userId = owner._account_id ? owner._account_id : owner.email;
253 if (userId) {
254 this._userId = userId;
Wyatt Allen5c3a3cd2017-08-30 13:43:37 -0700255 return;
256 }
Dmitrii Filippov3fd2b102019-11-15 16:16:46 +0100257 }
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100258 if (REPO_QUERY_PATTERN.test(this._query)) {
259 this._repo = changes[0].project;
Dmitrii Filippov3fd2b102019-11-15 16:16:46 +0100260 }
261 }
262
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200263 _computeHeaderClass(id?: string) {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100264 return id ? '' : 'hide';
265 }
266
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200267 _computePage(offset?: number, changesPerPage?: number) {
268 if (offset === undefined || changesPerPage === undefined) return;
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100269 return offset / changesPerPage + 1;
270 }
271
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200272 _computeLoggedIn(account?: AccountDetailInfo) {
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100273 return !!(account && Object.keys(account).length > 0);
274 }
275
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200276 _handleToggleStar(e: CustomEvent<ChangeStarToggleStarDetail>) {
Dhruv Srivastava5841dd82021-05-17 11:03:19 +0200277 if (e.detail.starred) {
278 this.reporting.reportInteraction('change-starred-from-change-list');
279 }
Ben Rohlfs43935a42020-12-01 19:14:09 +0100280 this.restApiService.saveChangeStarred(
281 e.detail.change._number,
282 e.detail.starred
283 );
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100284 }
Dmitrii Filippovdaf0ec92020-03-17 11:27:28 +0100285}
286
Dmitrii Filippov8814de12020-10-10 16:31:21 +0200287declare global {
288 interface HTMLElementTagNameMap {
289 'gr-change-list-view': GrChangeListView;
290 }
291}