Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(553)

Side by Side Diff: chrome/browser/resources/md_history/history_list.js

Issue 2684693004: MD History: Remove list-container and list-behavior (Closed)
Patch Set: Fix test Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 Polymer({ 5 Polymer({
6 is: 'history-list', 6 is: 'history-list',
7 7
8 behaviors: [HistoryListBehavior],
9
10 properties: { 8 properties: {
11 // The search term for the current query. Set when the query returns. 9 // The search term for the current query. Set when the query returns.
12 searchedTerm: { 10 searchedTerm: {
13 type: String, 11 type: String,
14 value: '', 12 value: '',
15 }, 13 },
16 14
17 resultLoadingDisabled_: { 15 resultLoadingDisabled_: {
18 type: Boolean, 16 type: Boolean,
19 value: false, 17 value: false,
20 }, 18 },
21 19
20 /**
21 * Indexes into historyData_ of selected items.
22 * @type {!Set<number>}
23 */
24 selectedItems: {
25 type: Object,
26 value: /** @return {!Set<string>} */ function() {
27 return new Set();
28 },
29 },
30
22 // An array of history entries in reverse chronological order. 31 // An array of history entries in reverse chronological order.
23 historyData_: Array, 32 historyData_: Array,
24 33
25 lastFocused_: Object, 34 lastFocused_: Object,
26 35
27 querying: Boolean, 36 lastSelectedIndex: Number,
37
38 /** @type {!QueryState} */
39 queryState: Object,
40
41 /**
42 * @private {?{
43 * index: number,
44 * item: !HistoryEntry,
45 * path: string,
46 * target: !HTMLElement
47 * }}
48 */
49 actionMenuModel_: Object,
28 }, 50 },
29 51
30 listeners: { 52 listeners: {
53 'history-checkbox-select': 'onItemSelected_',
54 'open-menu': 'onOpenMenu_',
31 'remove-bookmark-stars': 'removeBookmarkStars_', 55 'remove-bookmark-stars': 'removeBookmarkStars_',
32 'open-menu': 'onOpenMenu_',
33 }, 56 },
34 57
35 /** @override */ 58 /** @override */
36 attached: function() { 59 attached: function() {
37 // It is possible (eg, when middle clicking the reload button) for all other 60 // It is possible (eg, when middle clicking the reload button) for all other
38 // resize events to fire before the list is attached and can be measured. 61 // resize events to fire before the list is attached and can be measured.
39 // Adding another resize here ensures it will get sized correctly. 62 // Adding another resize here ensures it will get sized correctly.
40 /** @type {IronListElement} */ (this.$['infinite-list']).notifyResize(); 63 /** @type {IronListElement} */ (this.$['infinite-list']).notifyResize();
41 this.$['infinite-list'].scrollTarget = this; 64 this.$['infinite-list'].scrollTarget = this;
42 this.$['scroll-threshold'].scrollTarget = this; 65 this.$['scroll-threshold'].scrollTarget = this;
43 }, 66 },
44 67
68 /////////////////////////////////////////////////////////////////////////////
69 // Public methods:
70
45 /** 71 /**
46 * Remove bookmark star for history items with matching URLs. 72 * @param {HistoryQuery} info An object containing information about the
47 * @param {{detail: !string}} e 73 * query.
48 * @private 74 * @param {!Array<!HistoryEntry>} results A list of results.
49 */ 75 */
50 removeBookmarkStars_: function(e) { 76 historyResult: function(info, results) {
51 var url = e.detail; 77 this.initializeResults_(info, results);
78 this.closeMenu_();
52 79
53 if (this.historyData_ === undefined) 80 if (info.term && !this.queryState.incremental) {
54 return; 81 Polymer.IronA11yAnnouncer.requestAvailability();
82 this.fire('iron-announce', {
83 text:
84 md_history.HistoryItem.searchResultsTitle(results.length, info.term)
85 });
86 }
55 87
56 for (var i = 0; i < this.historyData_.length; i++) { 88 this.addNewResults(results, this.queryState.incremental, info.finished);
57 if (this.historyData_[i].url == url)
58 this.set('historyData_.' + i + '.starred', false);
59 }
60 }, 89 },
61 90
62 /** 91 /**
63 * Adds the newly updated history results into historyData_. Adds new fields 92 * Adds the newly updated history results into historyData_. Adds new fields
64 * for each result. 93 * for each result.
65 * @param {!Array<!HistoryEntry>} historyResults The new history results. 94 * @param {!Array<!HistoryEntry>} historyResults The new history results.
66 * @param {boolean} incremental Whether the result is from loading more 95 * @param {boolean} incremental Whether the result is from loading more
67 * history, or a new search/list reload. 96 * history, or a new search/list reload.
68 * @param {boolean} finished True if there are no more results available and 97 * @param {boolean} finished True if there are no more results available and
69 * result loading should be disabled. 98 * result loading should be disabled.
(...skipping 17 matching lines...) Expand all
87 this.push.apply(this, results); 116 this.push.apply(this, results);
88 } else { 117 } else {
89 // The first time we receive data, use set() to ensure the iron-list is 118 // The first time we receive data, use set() to ensure the iron-list is
90 // initialized correctly. 119 // initialized correctly.
91 this.set('historyData_', results); 120 this.set('historyData_', results);
92 } 121 }
93 122
94 this.resultLoadingDisabled_ = finished; 123 this.resultLoadingDisabled_ = finished;
95 }, 124 },
96 125
126 historyDeleted: function() {
127 // Do not reload the list when there are items checked.
128 if (this.getSelectedItemCount() > 0)
129 return;
130
131 // Reload the list with current search state.
132 this.fire('query-history', false);
133 },
134
135 /**
136 * Set the selection status for an item at a particular index.
137 * @param {number} index
138 * @param {boolean} selected
139 */
140 changeSelection: function(index, selected) {
141 this.set('historyData_.' + index + '.selected', selected);
calamity 2017/02/21 02:40:06 nit: Can be private.
tsergeant 2017/02/21 23:24:13 Done.
142 if (selected)
143 this.selectedItems.add(index);
144 else
145 this.selectedItems.delete(index);
146 },
147
148 /**
149 * Deselect each item in |selectedItems|.
150 */
151 unselectAllItems: function() {
152 this.selectedItems.forEach(function(index) {
153 this.changeSelection(index, false);
154 }.bind(this));
155
156 assert(this.selectedItems.size == 0);
157 },
158
159 /** @return {number} */
160 getSelectedItemCount: function() {
161 return this.selectedItems.size;
162 },
163
164 /**
165 * Delete all the currently selected history items. Will prompt the user with
166 * a dialog to confirm that the deletion should be performed.
167 */
168 deleteSelectedWithPrompt: function() {
169 if (!this.canDeleteHistory_())
170 return;
171
172 var browserService = md_history.BrowserService.getInstance();
173 browserService.recordAction('RemoveSelected');
174 if (this.queryState.searchTerm != '')
175 browserService.recordAction('SearchResultRemove');
176 this.$.dialog.get().showModal();
177
178 // TODO(dbeam): remove focus flicker caused by showModal() + focus().
179 this.$$('.action-button').focus();
180 },
181
182 /**
183 * Performs a request to the backend to delete all selected items. If
184 * successful, removes them from the view. Does not prompt the user before
185 * deleting -- see deleteSelectedWithPrompt for a version of this method which
186 * does prompt.
187 */
188 deleteSelected: function() {
calamity 2017/02/21 02:40:06 nit: private-able.
tsergeant 2017/02/21 23:24:13 Done.
189 var toBeRemoved =
190 Array.from(this.selectedItems.values()).map(function(index) {
191 return this.get('historyData_.' + index);
192 }.bind(this));
193
194 md_history.BrowserService.getInstance()
195 .deleteItems(toBeRemoved)
196 .then(function(items) {
197 this.removeItemsByIndex(Array.from(this.selectedItems));
198 this.fire('unselect-all');
199 }.bind(this));
200 },
201
202 /**
203 * Remove all |indices| from the history list. Uses notifySplices to send a
204 * single large notification to Polymer, rather than many small notifications,
205 * which greatly improves performance.
206 * @param {!Array<number>} indices
207 * @private
208 */
209 removeItemsByIndex: function(indices) {
calamity 2017/02/21 02:40:06 nit: private-able.
tsergeant 2017/02/21 23:24:12 Done.
210 var splices = [];
211 indices.sort(function(a, b) {
212 // Sort in reverse numerical order.
213 return b - a;
214 });
215 indices.forEach(function(index) {
216 var item = this.historyData_.splice(index, 1);
217 splices.push({
218 index: index,
219 removed: [item],
220 addedCount: 0,
221 object: this.historyData_,
222 type: 'splice'
223 });
224 }.bind(this));
225 this.notifySplices('historyData_', splices);
226 },
227
228 /////////////////////////////////////////////////////////////////////////////
229 // Event listeners:
230
231 /** @private */
232 onDialogConfirmTap_: function() {
233 md_history.BrowserService.getInstance().recordAction(
234 'ConfirmRemoveSelected');
235
236 this.deleteSelected();
237 var dialog = assert(this.$.dialog.getIfExists());
238 dialog.close();
239 },
240
241 /** @private */
242 onDialogCancelTap_: function() {
243 md_history.BrowserService.getInstance().recordAction(
244 'CancelRemoveSelected');
245
246 var dialog = assert(this.$.dialog.getIfExists());
247 dialog.close();
248 },
249
250 /**
251 * Remove bookmark star for history items with matching URLs.
252 * @param {{detail: !string}} e
253 * @private
254 */
255 removeBookmarkStars_: function(e) {
256 var url = e.detail;
257
258 if (this.historyData_ === undefined)
259 return;
260
261 for (var i = 0; i < this.historyData_.length; i++) {
262 if (this.historyData_[i].url == url)
263 this.set('historyData_.' + i + '.starred', false);
264 }
265 },
266
97 /** 267 /**
98 * Called when the page is scrolled to near the bottom of the list. 268 * Called when the page is scrolled to near the bottom of the list.
99 * @private 269 * @private
100 */ 270 */
101 loadMoreData_: function() { 271 loadMoreData_: function() {
calamity 2017/02/21 02:40:06 While you're here, you may as well make this and t
tsergeant 2017/02/22 01:54:27 Done, oops
102 if (this.resultLoadingDisabled_ || this.querying) 272 if (this.resultLoadingDisabled_ || this.queryState.querying)
103 return; 273 return;
104 274
105 this.fire('query-history', true); 275 this.fire('query-history', true);
106 }, 276 },
107 277
108 /** 278 /**
109 * Ensure that the item is visible in the scroll pane when its menu is 279 * Open the overflow menu and ensure that the item is visible in the scroll
110 * opened (it is possible to open off-screen items using keyboard shortcuts). 280 * pane when its menu is opened (it is possible to open off-screen items using
111 * @param {Event} e 281 * keyboard shortcuts).
282 * @param {{detail: {
283 * index: number, item: !HistoryEntry,
284 * path: string, target: !HTMLElement
285 * }}} e
112 * @private 286 * @private
113 */ 287 */
114 onOpenMenu_: function(e) { 288 onOpenMenu_: function(e) {
115 var index = e.detail.index; 289 var index = e.detail.index;
116 var list = /** @type {IronListElement} */ (this.$['infinite-list']); 290 var list = /** @type {IronListElement} */ (this.$['infinite-list']);
117 if (index < list.firstVisibleIndex || index > list.lastVisibleIndex) 291 if (index < list.firstVisibleIndex || index > list.lastVisibleIndex)
118 list.scrollToIndex(index); 292 list.scrollToIndex(index);
293
294 var target = e.detail.target;
295 this.actionMenuModel_ = e.detail;
296 var menu = /** @type {CrSharedMenuElement} */ this.$.sharedMenu.get();
297 menu.showAt(target);
119 }, 298 },
120 299
121 /** 300 /**
301 * Closes the overflow menu.
302 * @private
303 */
304 closeMenu_: function() {
calamity 2017/02/21 02:40:06 I don't think this is hooked to any events.
tsergeant 2017/02/21 23:24:12 Moved up into the new Private methods section
305 var menu = this.$.sharedMenu.getIfExists();
306 if (menu && menu.open) {
307 this.actionMenuModel_ = null;
308 menu.close();
309 }
310 },
311
312 /** @private */
313 onMoreFromSiteTap_: function() {
314 md_history.BrowserService.getInstance().recordAction(
315 'EntryMenuShowMoreFromSite');
316
317 var menu = assert(this.$.sharedMenu.getIfExists());
318 this.fire('change-query', {search: this.actionMenuModel_.item.domain});
319 this.actionMenuModel_ = null;
320 this.closeMenu_();
321 },
322
323 /** @private */
324 onRemoveFromHistoryTap_: function() {
325 var browserService = md_history.BrowserService.getInstance();
326 browserService.recordAction('EntryMenuRemoveFromHistory');
327 var menu = assert(this.$.sharedMenu.getIfExists());
328 var itemData = this.actionMenuModel_;
329 browserService.deleteItems([itemData.item]).then(function(items) {
330 // This unselect-all resets the toolbar when deleting a selected item
331 // and clears selection state which can be invalid if items move
332 // around during deletion.
333 // TODO(tsergeant): Make this automatic based on observing list
334 // modifications.
335 this.fire('unselect-all');
336 this.removeItemsByIndex([itemData.index]);
337
338 var index = itemData.index;
339 if (index == undefined)
340 return;
341
342 var browserService = md_history.BrowserService.getInstance();
343 browserService.recordHistogram(
344 'HistoryPage.RemoveEntryPosition',
345 Math.min(index, UMA_MAX_BUCKET_VALUE), UMA_MAX_BUCKET_VALUE);
346 if (index <= UMA_MAX_SUBSET_BUCKET_VALUE) {
347 browserService.recordHistogram(
348 'HistoryPage.RemoveEntryPositionSubset', index,
349 UMA_MAX_SUBSET_BUCKET_VALUE);
350 }
351 }.bind(this));
352 this.closeMenu_();
353 },
354
355 /**
356 * @param {Event} e
357 * @private
358 */
359 onItemSelected_: function(e) {
360 var index = e.detail.index;
361 var indices = [];
362
363 // Handle shift selection. Change the selection state of all items between
364 // |path| and |lastSelected| to the selection state of |item|.
365 if (e.detail.shiftKey && this.lastSelectedIndex != undefined) {
366 for (var i = Math.min(index, this.lastSelectedIndex);
367 i <= Math.max(index, this.lastSelectedIndex); i++) {
368 indices.push(i);
369 }
370 }
371
372 if (indices.length == 0)
373 indices.push(index);
374
375 var selected = !this.selectedItems.has(index);
376
377 indices.forEach(function(index) {
378 this.changeSelection(index, selected);
379 }.bind(this));
380
381 this.lastSelectedIndex = index;
382 },
383
384 /////////////////////////////////////////////////////////////////////////////
385 // Template helpers:
386
387 /**
122 * Check whether the time difference between the given history item and the 388 * Check whether the time difference between the given history item and the
123 * next one is large enough for a spacer to be required. 389 * next one is large enough for a spacer to be required.
124 * @param {HistoryEntry} item 390 * @param {HistoryEntry} item
125 * @param {number} index The index of |item| in |historyData_|. 391 * @param {number} index The index of |item| in |historyData_|.
126 * @param {number} length The length of |historyData_|. 392 * @param {number} length The length of |historyData_|.
127 * @return {boolean} Whether or not time gap separator is required. 393 * @return {boolean} Whether or not time gap separator is required.
128 * @private 394 * @private
129 */ 395 */
130 needsTimeGap_: function(item, index, length) { 396 needsTimeGap_: function(item, index, length) {
131 return md_history.HistoryItem.needsTimeGap( 397 if (index >= length - 1 || length == 0)
132 this.historyData_, index, this.searchedTerm); 398 return false;
399
400 var currentItem = this.historyData_[index];
401 var nextItem = this.historyData_[index + 1];
402
403 if (this.searchedTerm)
404 return currentItem.dateShort != nextItem.dateShort;
405
406 return currentItem.time - nextItem.time > BROWSING_GAP_TIME &&
407 currentItem.dateRelativeDay == nextItem.dateRelativeDay;
133 }, 408 },
134 409
135 /** 410 /**
136 * True if the given item is the beginning of a new card. 411 * True if the given item is the beginning of a new card.
137 * @param {HistoryEntry} item 412 * @param {HistoryEntry} item
138 * @param {number} i Index of |item| within |historyData_|. 413 * @param {number} i Index of |item| within |historyData_|.
139 * @param {number} length 414 * @param {number} length
140 * @return {boolean} 415 * @return {boolean}
141 * @private 416 * @private
142 */ 417 */
(...skipping 15 matching lines...) Expand all
158 */ 433 */
159 isCardEnd_: function(item, i, length) { 434 isCardEnd_: function(item, i, length) {
160 if (length == 0 || i > length - 1) 435 if (length == 0 || i > length - 1)
161 return false; 436 return false;
162 return i == length - 1 || 437 return i == length - 1 ||
163 this.historyData_[i].dateRelativeDay != 438 this.historyData_[i].dateRelativeDay !=
164 this.historyData_[i + 1].dateRelativeDay; 439 this.historyData_[i + 1].dateRelativeDay;
165 }, 440 },
166 441
167 /** 442 /**
168 * @param {number} index 443 * @param {number} historyDataLength
444 * @return {boolean}
445 * @private
446 */
447 hasResults_: function(historyDataLength) {
448 return historyDataLength > 0;
449 },
450
451 /**
452 * @param {string} searchedTerm
453 * @param {boolean} isLoading
169 * @return {string} 454 * @return {string}
170 * @private 455 * @private
171 */ 456 */
172 pathForItem_: function(index) { 457 noResultsMessage_: function(searchedTerm, isLoading) {
173 return 'historyData_.' + index; 458 if (isLoading)
459 return '';
460
461 var messageId = searchedTerm !== '' ? 'noSearchResults' : 'noResults';
462 return loadTimeData.getString(messageId);
463 },
464
465 /** @private */
466 canDeleteHistory_: function() {
467 return loadTimeData.getBoolean('allowDeletingHistory');
468 },
469
470 /**
471 * @param {HistoryQuery} info
472 * @param {!Array<HistoryEntry>} results
473 * @private
474 */
475 initializeResults_: function(info, results) {
476 if (results.length == 0)
477 return;
478
479 var currentDate = results[0].dateRelativeDay;
480
481 for (var i = 0; i < results.length; i++) {
482 // Sets the default values for these fields to prevent undefined types.
483 results[i].selected = false;
484 results[i].readableTimestamp =
485 info.term == '' ? results[i].dateTimeOfDay : results[i].dateShort;
486
487 if (results[i].dateRelativeDay != currentDate) {
488 currentDate = results[i].dateRelativeDay;
489 }
490 }
174 }, 491 },
175 }); 492 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698