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

Side by Side Diff: ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm

Issue 2877513003: ContentSuggestionsDataSource returns CollectionViewItem (Closed)
Patch Set: Address comments Created 3 years, 7 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 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collectio n_updater.h" 5 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collectio n_updater.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "base/mac/foundation_util.h" 8 #include "base/mac/foundation_util.h"
9 #include "base/strings/sys_string_conversions.h" 9 #include "base/strings/sys_string_conversions.h"
10 #include "base/time/time.h" 10 #include "base/time/time.h"
11 #include "components/strings/grit/components_strings.h" 11 #include "components/strings/grit/components_strings.h"
12 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_item+collec tion_view_controller.h"
12 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h " 13 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_text_item.h "
13 #import "ios/chrome/browser/ui/collection_view/collection_view_controller.h" 14 #import "ios/chrome/browser/ui/collection_view/collection_view_controller.h"
14 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h" 15 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
15 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_foo ter_item.h" 16 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_foo ter_item.h"
16 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_ite m.h" 17 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_ite m.h"
17 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_mos t_visited_item.h" 18 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_mos t_visited_item.h"
18 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tex t_item.h" 19 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tex t_item.h"
19 #import "ios/chrome/browser/ui/content_suggestions/content_suggestion.h" 20 #import "ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h"
20 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_data_sink .h" 21 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_data_sink .h"
21 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_data_sour ce.h" 22 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_data_sour ce.h"
22 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_image_fet cher.h" 23 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_image_fet cher.h"
23 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_cont roller.h" 24 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_cont roller.h"
24 #import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestion _identifier.h" 25 #import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestion _identifier.h"
25 #import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestion s_section_information.h" 26 #import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestion s_section_information.h"
26 #import "ios/chrome/browser/ui/favicon/favicon_attributes.h" 27 #import "ios/chrome/browser/ui/favicon/favicon_attributes.h"
27 #include "ui/base/l10n/l10n_util.h" 28 #include "ui/base/l10n/l10n_util.h"
28 #include "url/gurl.h" 29 #include "url/gurl.h"
29 30
30 #if !defined(__has_feature) || !__has_feature(objc_arc) 31 #if !defined(__has_feature) || !__has_feature(objc_arc)
31 #error "This file requires ARC support." 32 #error "This file requires ARC support."
32 #endif 33 #endif
33 34
34 namespace { 35 namespace {
35 36
36 using CSCollectionViewItem = 37 using CSCollectionViewItem = CollectionViewItem<SuggestedContent>;
37 CollectionViewItem<ContentSuggestionIdentification>;
38 using CSCollectionViewModel = CollectionViewModel<CSCollectionViewItem*>; 38 using CSCollectionViewModel = CollectionViewModel<CSCollectionViewItem*>;
39 39
40 // Enum defining the ItemType of this ContentSuggestionsCollectionUpdater. 40 // Enum defining the ItemType of this ContentSuggestionsCollectionUpdater.
41 typedef NS_ENUM(NSInteger, ItemType) { 41 typedef NS_ENUM(NSInteger, ItemType) {
42 ItemTypeArticle = kItemTypeEnumZero, 42 ItemTypeArticle = kItemTypeEnumZero,
43 ItemTypeFooter, 43 ItemTypeFooter,
44 ItemTypeHeader, 44 ItemTypeHeader,
45 ItemTypeEmpty, 45 ItemTypeEmpty,
46 ItemTypeReadingList, 46 ItemTypeReadingList,
47 ItemTypeMostVisited, 47 ItemTypeMostVisited,
48 ItemTypeUnknown,
48 }; 49 };
49 50
51 // Enum defining the SectionIdentifier of this
52 // ContentSuggestionsCollectionUpdater.
50 typedef NS_ENUM(NSInteger, SectionIdentifier) { 53 typedef NS_ENUM(NSInteger, SectionIdentifier) {
51 SectionIdentifierArticles = kSectionIdentifierEnumZero, 54 SectionIdentifierArticles = kSectionIdentifierEnumZero,
52 SectionIdentifierReadingList, 55 SectionIdentifierReadingList,
53 SectionIdentifierMostVisited, 56 SectionIdentifierMostVisited,
54 SectionIdentifierDefault, 57 SectionIdentifierDefault,
55 }; 58 };
56 59
57 // Update ContentSuggestionTypeForItemType if you update this function. 60 // Returns the ContentSuggestionType associated with an ItemType |type|.
58 ItemType ItemTypeForContentSuggestionType(ContentSuggestionType type) {
59 switch (type) {
60 case ContentSuggestionTypeArticle:
61 return ItemTypeArticle;
62 case ContentSuggestionTypeEmpty:
63 return ItemTypeEmpty;
64 case ContentSuggestionTypeReadingList:
65 return ItemTypeReadingList;
66 case ContentSuggestionTypeMostVisited:
67 return ItemTypeMostVisited;
68 }
69 }
70
71 ContentSuggestionType ContentSuggestionTypeForItemType(NSInteger type) { 61 ContentSuggestionType ContentSuggestionTypeForItemType(NSInteger type) {
72 if (type == ItemTypeArticle) 62 if (type == ItemTypeArticle)
73 return ContentSuggestionTypeArticle; 63 return ContentSuggestionTypeArticle;
74 if (type == ItemTypeEmpty) 64 if (type == ItemTypeEmpty)
75 return ContentSuggestionTypeEmpty; 65 return ContentSuggestionTypeEmpty;
76 if (type == ItemTypeReadingList) 66 if (type == ItemTypeReadingList)
77 return ContentSuggestionTypeReadingList; 67 return ContentSuggestionTypeReadingList;
78 if (type == ItemTypeMostVisited) 68 if (type == ItemTypeMostVisited)
79 return ContentSuggestionTypeMostVisited; 69 return ContentSuggestionTypeMostVisited;
80 // Add new type here 70 // Add new type here
81 71
82 // Default type. 72 // Default type.
83 return ContentSuggestionTypeEmpty; 73 return ContentSuggestionTypeEmpty;
84 } 74 }
85 75
86 // Returns the section identifier corresponding to the section |info|. 76 // Returns the section identifier corresponding to the section |info|.
77 ItemType ItemTypeForInfo(ContentSuggestionsSectionInformation* info) {
78 switch (info.sectionID) {
79 case ContentSuggestionsSectionArticles:
80 return ItemTypeArticle;
81 case ContentSuggestionsSectionReadingList:
82 return ItemTypeReadingList;
83 case ContentSuggestionsSectionMostVisited:
84 return ItemTypeMostVisited;
85
86 case ContentSuggestionsSectionUnknown:
87 return ItemTypeUnknown;
88 }
89 }
90
91 // Returns the section identifier corresponding to the section |info|.
87 SectionIdentifier SectionIdentifierForInfo( 92 SectionIdentifier SectionIdentifierForInfo(
88 ContentSuggestionsSectionInformation* info) { 93 ContentSuggestionsSectionInformation* info) {
89 switch (info.sectionID) { 94 switch (info.sectionID) {
90 case ContentSuggestionsSectionArticles: 95 case ContentSuggestionsSectionArticles:
91 return SectionIdentifierArticles; 96 return SectionIdentifierArticles;
92
93 case ContentSuggestionsSectionReadingList: 97 case ContentSuggestionsSectionReadingList:
94 return SectionIdentifierReadingList; 98 return SectionIdentifierReadingList;
95
96 case ContentSuggestionsSectionMostVisited: 99 case ContentSuggestionsSectionMostVisited:
97 return SectionIdentifierMostVisited; 100 return SectionIdentifierMostVisited;
98 101
99 case ContentSuggestionsSectionUnknown: 102 case ContentSuggestionsSectionUnknown:
100 return SectionIdentifierDefault; 103 return SectionIdentifierDefault;
101 } 104 }
102 } 105 }
103 106
104 } // namespace 107 } // namespace
105 108
106 @interface ContentSuggestionsCollectionUpdater ()< 109 @interface ContentSuggestionsCollectionUpdater ()<ContentSuggestionsDataSink,
107 ContentSuggestionsItemDelegate, 110 SuggestedContentDelegate>
108 ContentSuggestionsDataSink>
109 111
110 @property(nonatomic, weak) id<ContentSuggestionsDataSource> dataSource; 112 @property(nonatomic, weak) id<ContentSuggestionsDataSource> dataSource;
111 @property(nonatomic, strong) 113 @property(nonatomic, strong)
112 NSMutableDictionary<NSNumber*, ContentSuggestionsSectionInformation*>* 114 NSMutableDictionary<NSNumber*, ContentSuggestionsSectionInformation*>*
113 sectionInfoBySectionIdentifier; 115 sectionInfoBySectionIdentifier;
114 116
115 @end 117 @end
116 118
117 @implementation ContentSuggestionsCollectionUpdater 119 @implementation ContentSuggestionsCollectionUpdater
118 120
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
150 if ([model hasSectionForSectionIdentifier:sectionIdentifier]) { 152 if ([model hasSectionForSectionIdentifier:sectionIdentifier]) {
151 NSArray<CSCollectionViewItem*>* items = 153 NSArray<CSCollectionViewItem*>* items =
152 [model itemsInSectionWithIdentifier:sectionIdentifier]; 154 [model itemsInSectionWithIdentifier:sectionIdentifier];
153 if (items.count > 0 && items[0].type != ItemTypeEmpty) { 155 if (items.count > 0 && items[0].type != ItemTypeEmpty) {
154 // Do not dismiss the presented items. 156 // Do not dismiss the presented items.
155 return; 157 return;
156 } 158 }
157 } 159 }
158 160
159 [self.collectionViewController 161 [self.collectionViewController
160 addSuggestions:[self.dataSource suggestionsForSection:sectionInfo]]; 162 addSuggestions:[self.dataSource itemsForSectionInfo:sectionInfo]
163 toSectionInfo:sectionInfo];
161 } 164 }
162 165
163 - (void)clearSuggestion:(ContentSuggestionIdentifier*)suggestionIdentifier { 166 - (void)clearSuggestion:(ContentSuggestionIdentifier*)suggestionIdentifier {
164 SectionIdentifier sectionIdentifier = 167 SectionIdentifier sectionIdentifier =
165 SectionIdentifierForInfo(suggestionIdentifier.sectionInfo); 168 SectionIdentifierForInfo(suggestionIdentifier.sectionInfo);
166 if (![self.collectionViewController.collectionViewModel 169 if (![self.collectionViewController.collectionViewModel
167 hasSectionForSectionIdentifier:sectionIdentifier]) { 170 hasSectionForSectionIdentifier:sectionIdentifier]) {
168 return; 171 return;
169 } 172 }
170 173
(...skipping 15 matching lines...) Expand all
186 NSIndexPath* indexPath = [self.collectionViewController.collectionViewModel 189 NSIndexPath* indexPath = [self.collectionViewController.collectionViewModel
187 indexPathForItem:correspondingItem]; 190 indexPathForItem:correspondingItem];
188 [self.collectionViewController dismissEntryAtIndexPath:indexPath]; 191 [self.collectionViewController dismissEntryAtIndexPath:indexPath];
189 } 192 }
190 193
191 - (void)reloadAllData { 194 - (void)reloadAllData {
192 [self resetModels]; 195 [self resetModels];
193 196
194 // The data is reset, add the new data directly in the model then reload the 197 // The data is reset, add the new data directly in the model then reload the
195 // collection. 198 // collection.
196 NSArray<ContentSuggestion*>* suggestions = [self.dataSource allSuggestions]; 199 NSArray<ContentSuggestionsSectionInformation*>* sectionInfos =
197 [self addSectionsForSuggestionsToModel:suggestions]; 200 [self.dataSource sectionsInfo];
198 [self addSuggestionsToModel:suggestions]; 201 [self addSectionsForSectionInfoToModel:sectionInfos];
202 for (ContentSuggestionsSectionInformation* sectionInfo in sectionInfos) {
203 [self
204 addSuggestionsToModel:[self.dataSource itemsForSectionInfo:sectionInfo]
205 withSectionInfo:sectionInfo];
206 }
199 [self.collectionViewController.collectionView reloadData]; 207 [self.collectionViewController.collectionView reloadData];
200 } 208 }
201 209
202 - (void)clearSection:(ContentSuggestionsSectionInformation*)sectionInfo { 210 - (void)clearSection:(ContentSuggestionsSectionInformation*)sectionInfo {
203 SectionIdentifier sectionIdentifier = SectionIdentifierForInfo(sectionInfo); 211 SectionIdentifier sectionIdentifier = SectionIdentifierForInfo(sectionInfo);
204 NSInteger section = [self.collectionViewController.collectionViewModel 212 NSInteger section = [self.collectionViewController.collectionViewModel
205 sectionIdentifierForSection:sectionIdentifier]; 213 sectionIdentifierForSection:sectionIdentifier];
206 214
207 [self.collectionViewController dismissSection:section]; 215 [self.collectionViewController dismissSection:section];
208 } 216 }
(...skipping 14 matching lines...) Expand all
223 ContentSuggestionsSectionInformation* sectionInformation = 231 ContentSuggestionsSectionInformation* sectionInformation =
224 self.sectionInfoBySectionIdentifier[identifier]; 232 self.sectionInfoBySectionIdentifier[identifier];
225 return sectionInformation.layout == ContentSuggestionsSectionLayoutCustom; 233 return sectionInformation.layout == ContentSuggestionsSectionLayoutCustom;
226 } 234 }
227 235
228 - (ContentSuggestionType)contentSuggestionTypeForItem: 236 - (ContentSuggestionType)contentSuggestionTypeForItem:
229 (CollectionViewItem*)item { 237 (CollectionViewItem*)item {
230 return ContentSuggestionTypeForItemType(item.type); 238 return ContentSuggestionTypeForItemType(item.type);
231 } 239 }
232 240
233 - (NSArray<NSIndexPath*>*)addSuggestionsToModel: 241 - (NSArray<NSIndexPath*>*)
234 (NSArray<ContentSuggestion*>*)suggestions { 242 addSuggestionsToModel:(NSArray<CSCollectionViewItem*>*)suggestions
235 if (suggestions.count == 0) { 243 withSectionInfo:(ContentSuggestionsSectionInformation*)sectionInfo {
236 return [NSArray array]; 244 NSMutableArray<NSIndexPath*>* indexPaths = [NSMutableArray array];
237 }
238 245
239 CSCollectionViewModel* model = 246 CSCollectionViewModel* model =
240 self.collectionViewController.collectionViewModel; 247 self.collectionViewController.collectionViewModel;
241 NSMutableArray<NSIndexPath*>* indexPaths = [NSMutableArray array]; 248 NSInteger sectionIdentifier = SectionIdentifierForInfo(sectionInfo);
242 for (ContentSuggestion* suggestion in suggestions) {
243 ContentSuggestionsSectionInformation* sectionInfo =
244 suggestion.suggestionIdentifier.sectionInfo;
245 NSInteger sectionIdentifier = SectionIdentifierForInfo(sectionInfo);
246 249
247 if (![model hasSectionForSectionIdentifier:sectionIdentifier]) 250 if (suggestions.count == 0) {
248 continue; 251 if ([model hasSectionForSectionIdentifier:sectionIdentifier] &&
252 [model numberOfItemsInSection:[model sectionForSectionIdentifier:
253 sectionIdentifier]] == 0) {
254 [indexPaths
255 addObject:[self
256 addEmptyItemForSection:[model
257 sectionForSectionIdentifier:
258 sectionIdentifier]]];
259 }
260 return indexPaths;
261 }
249 262
250 NSInteger section = [model sectionForSectionIdentifier:sectionIdentifier]; 263 BOOL emptyItemRemoved = NO;
251 NSIndexPath* indexPath = [NSIndexPath indexPathForItem:0 inSection:section]; 264 NSArray<CSCollectionViewItem*>* existingItems =
265 [model itemsInSectionWithIdentifier:sectionIdentifier];
266 if (existingItems.count > 0 && existingItems[0].type == ItemTypeEmpty) {
267 [model removeItemWithType:ItemTypeEmpty
268 fromSectionWithIdentifier:sectionIdentifier];
269 emptyItemRemoved = YES;
270 }
252 271
253 if (suggestion.type != ContentSuggestionTypeEmpty && 272 [suggestions enumerateObjectsUsingBlock:^(CSCollectionViewItem* item,
254 [model hasItemAtIndexPath:indexPath] && 273 NSUInteger index, BOOL* stop) {
255 [model itemAtIndexPath:indexPath].type == ItemTypeEmpty) { 274 ItemType type = ItemTypeForInfo(sectionInfo);
256 [self.collectionViewController dismissEntryAtIndexPath:indexPath]; 275 item.type = type;
276 NSIndexPath* addedIndexPath =
277 [self addItem:item toSectionWithIdentifier:sectionIdentifier];
278 [self fetchFaviconForItem:item];
279 item.delegate = self;
280
281 if (!emptyItemRemoved || index > 0) {
282 [indexPaths addObject:addedIndexPath];
283 } else {
284 [self.collectionViewController.collectionViewLayout invalidateLayout];
257 } 285 }
258 286 }];
259 switch (suggestion.type) {
260 case ContentSuggestionTypeEmpty: {
261 if ([model hasSectionForSectionIdentifier:sectionIdentifier] &&
262 [model numberOfItemsInSection:[model sectionForSectionIdentifier:
263 sectionIdentifier]] == 0) {
264 CSCollectionViewItem* item =
265 [self emptyItemForSectionInfo:sectionInfo];
266 NSIndexPath* addedIndexPath =
267 [self addItem:item toSectionWithIdentifier:sectionIdentifier];
268 [indexPaths addObject:addedIndexPath];
269 }
270 break;
271 }
272 case ContentSuggestionTypeArticle: {
273 ContentSuggestionsItem* articleItem =
274 [self suggestionItemForSuggestion:suggestion];
275
276 articleItem.hasImage = YES;
277
278 __weak ContentSuggestionsItem* weakItem = articleItem;
279 [self fetchFaviconForItem:articleItem
280 withURL:articleItem.URL
281 callback:^void(FaviconAttributes* attributes) {
282 weakItem.attributes = attributes;
283 }];
284
285 __weak ContentSuggestionsCollectionUpdater* weakSelf = self;
286 [self.dataSource
287 fetchFaviconImageForSuggestion:articleItem.suggestionIdentifier
288 completion:^void(UIImage* favicon) {
289 ContentSuggestionsCollectionUpdater*
290 strongSelf = weakSelf;
291 ContentSuggestionsItem* strongItem = weakItem;
292 if (!strongItem || !strongSelf)
293 return;
294
295 strongItem.attributes = [FaviconAttributes
296 attributesWithImage:favicon];
297 [strongSelf.collectionViewController
298 reconfigureCellsForItems:@[ strongItem ]];
299 }];
300
301 NSIndexPath* addedIndexPath = [self addItem:articleItem
302 toSectionWithIdentifier:sectionIdentifier];
303 [indexPaths addObject:addedIndexPath];
304 break;
305 }
306 case ContentSuggestionTypeReadingList: {
307 ContentSuggestionsItem* readingListItem =
308 [self suggestionItemForSuggestion:suggestion];
309
310 __weak ContentSuggestionsItem* weakItem = readingListItem;
311 [self fetchFaviconForItem:readingListItem
312 withURL:readingListItem.URL
313 callback:^void(FaviconAttributes* attributes) {
314 weakItem.attributes = attributes;
315 }];
316
317 NSIndexPath* addedIndexPath = [self addItem:readingListItem
318 toSectionWithIdentifier:sectionIdentifier];
319 [indexPaths addObject:addedIndexPath];
320 break;
321 }
322 case ContentSuggestionTypeMostVisited: {
323 ContentSuggestionsMostVisitedItem* mostVisitedItem =
324 [[ContentSuggestionsMostVisitedItem alloc]
325 initWithType:ItemTypeMostVisited];
326 mostVisitedItem.title = suggestion.title;
327 [model addItem:mostVisitedItem
328 toSectionWithIdentifier:SectionIdentifierMostVisited];
329 [indexPaths addObject:indexPath];
330 break;
331 }
332 }
333 }
334 287
335 return indexPaths; 288 return indexPaths;
336 } 289 }
337 290
338 - (NSIndexSet*)addSectionsForSuggestionsToModel: 291 - (NSIndexSet*)addSectionsForSectionInfoToModel:
339 (NSArray<ContentSuggestion*>*)suggestions { 292 (NSArray<ContentSuggestionsSectionInformation*>*)sectionsInfo {
340 NSMutableIndexSet* indexSet = [NSMutableIndexSet indexSet]; 293 NSMutableIndexSet* addedSectionIdentifiers = [NSMutableIndexSet indexSet];
294 NSArray<ContentSuggestionsSectionInformation*>* orderedSectionsInfo =
295 [self.dataSource sectionsInfo];
341 296
342 CSCollectionViewModel* model = 297 CSCollectionViewModel* model =
343 self.collectionViewController.collectionViewModel; 298 self.collectionViewController.collectionViewModel;
344 for (ContentSuggestion* suggestion in suggestions) { 299 for (ContentSuggestionsSectionInformation* sectionInfo in sectionsInfo) {
345 ContentSuggestionsSectionInformation* sectionInfo =
346 suggestion.suggestionIdentifier.sectionInfo;
347 NSInteger sectionIdentifier = SectionIdentifierForInfo(sectionInfo); 300 NSInteger sectionIdentifier = SectionIdentifierForInfo(sectionInfo);
348 301
349 if ([model hasSectionForSectionIdentifier:sectionIdentifier] || 302 if ([model hasSectionForSectionIdentifier:sectionIdentifier] ||
350 (suggestion.type == ContentSuggestionTypeEmpty && 303 (!sectionInfo.showIfEmpty &&
351 !sectionInfo.showIfEmpty)) { 304 [self.dataSource itemsForSectionInfo:sectionInfo].count == 0)) {
352 continue; 305 continue;
353 } 306 }
354 307
355 [model addSectionWithIdentifier:sectionIdentifier]; 308 NSUInteger sectionIndex = 0;
309 for (ContentSuggestionsSectionInformation* orderedSectionInfo in
310 orderedSectionsInfo) {
311 NSInteger orderedSectionIdentifier =
312 SectionIdentifierForInfo(orderedSectionInfo);
313 if ([model hasSectionForSectionIdentifier:orderedSectionIdentifier]) {
314 sectionIndex++;
315 }
316 }
317 [model insertSectionWithIdentifier:sectionIdentifier atIndex:sectionIndex];
318
356 self.sectionInfoBySectionIdentifier[@(sectionIdentifier)] = sectionInfo; 319 self.sectionInfoBySectionIdentifier[@(sectionIdentifier)] = sectionInfo;
357 [indexSet addIndex:[model sectionForSectionIdentifier:sectionIdentifier]]; 320 [addedSectionIdentifiers addIndex:sectionIdentifier];
358 321
359 [self addHeader:sectionInfo]; 322 [self addHeaderIfNeeded:sectionInfo];
360 [self addFooterIfNeeded:sectionInfo]; 323 [self addFooterIfNeeded:sectionInfo];
361 } 324 }
325
326 NSMutableIndexSet* indexSet = [NSMutableIndexSet indexSet];
327 [addedSectionIdentifiers enumerateIndexesUsingBlock:^(
328 NSUInteger sectionIdentifier,
329 BOOL* _Nonnull stop) {
330 [indexSet addIndex:[model sectionForSectionIdentifier:sectionIdentifier]];
331 }];
362 return indexSet; 332 return indexSet;
363 } 333 }
364 334
365 - (NSIndexPath*)addEmptyItemForSection:(NSInteger)section { 335 - (NSIndexPath*)addEmptyItemForSection:(NSInteger)section {
366 CSCollectionViewModel* model = 336 CSCollectionViewModel* model =
367 self.collectionViewController.collectionViewModel; 337 self.collectionViewController.collectionViewModel;
368 NSInteger sectionIdentifier = [model sectionIdentifierForSection:section]; 338 NSInteger sectionIdentifier = [model sectionIdentifierForSection:section];
369 ContentSuggestionsSectionInformation* sectionInfo = 339 ContentSuggestionsSectionInformation* sectionInfo =
370 self.sectionInfoBySectionIdentifier[@(sectionIdentifier)]; 340 self.sectionInfoBySectionIdentifier[@(sectionIdentifier)];
371 341
372 CSCollectionViewItem* item = [self emptyItemForSectionInfo:sectionInfo]; 342 CSCollectionViewItem* item = [self emptyItemForSectionInfo:sectionInfo];
373 return [self addItem:item toSectionWithIdentifier:sectionIdentifier]; 343 return [self addItem:item toSectionWithIdentifier:sectionIdentifier];
374 } 344 }
375 345
376 #pragma mark - ContentSuggestionsItemDelegate 346 #pragma mark - SuggestedContentDelegate
377 347
378 - (void)loadImageForSuggestionItem:(ContentSuggestionsItem*)suggestionItem { 348 - (void)loadImageForSuggestedItem:(CSCollectionViewItem*)suggestedItem {
379 __weak ContentSuggestionsCollectionUpdater* weakSelf = self; 349 __weak ContentSuggestionsCollectionUpdater* weakSelf = self;
380 __weak ContentSuggestionsItem* weakArticle = suggestionItem; 350 __weak CSCollectionViewItem* weakItem = suggestedItem;
381 351
382 void (^imageFetchedCallback)(UIImage*) = ^(UIImage* image) { 352 void (^imageFetchedCallback)(UIImage*) = ^(UIImage* image) {
383 ContentSuggestionsCollectionUpdater* strongSelf = weakSelf; 353 ContentSuggestionsCollectionUpdater* strongSelf = weakSelf;
384 ContentSuggestionsItem* strongArticle = weakArticle; 354 CSCollectionViewItem* strongItem = weakItem;
385 if (!strongSelf || !strongArticle) { 355 if (!strongSelf || !strongItem) {
386 return; 356 return;
387 } 357 }
388 358
389 strongArticle.image = image; 359 strongItem.image = image;
390 [strongSelf.collectionViewController 360 [strongSelf.collectionViewController
391 reconfigureCellsForItems:@[ strongArticle ]]; 361 reconfigureCellsForItems:@[ strongItem ]];
392 }; 362 };
393 363
394 [self.dataSource.imageFetcher 364 [self.dataSource.imageFetcher
395 fetchImageForSuggestion:suggestionItem.suggestionIdentifier 365 fetchImageForSuggestion:suggestedItem.suggestionIdentifier
396 callback:imageFetchedCallback]; 366 callback:imageFetchedCallback];
397 } 367 }
398 368
399 #pragma mark - Private methods 369 #pragma mark - Private methods
400 370
401 // Adds a footer to the section identified by |sectionInfo| if there is none 371 // Adds a footer to the section identified by |sectionInfo| if there is none
402 // present and the section info contains a title for it. 372 // present and the section info contains a title for it.
403 - (void)addFooterIfNeeded:(ContentSuggestionsSectionInformation*)sectionInfo { 373 - (void)addFooterIfNeeded:(ContentSuggestionsSectionInformation*)sectionInfo {
404 NSInteger sectionIdentifier = SectionIdentifierForInfo(sectionInfo); 374 NSInteger sectionIdentifier = SectionIdentifierForInfo(sectionInfo);
405 375
406 __weak ContentSuggestionsCollectionUpdater* weakSelf = self; 376 __weak ContentSuggestionsCollectionUpdater* weakSelf = self;
407 if (sectionInfo.footerTitle && 377 if (sectionInfo.footerTitle &&
408 ![self.collectionViewController.collectionViewModel 378 ![self.collectionViewController.collectionViewModel
409 footerForSectionWithIdentifier:sectionIdentifier]) { 379 footerForSectionWithIdentifier:sectionIdentifier]) {
410 ContentSuggestionsFooterItem* footer = [[ContentSuggestionsFooterItem alloc] 380 ContentSuggestionsFooterItem* footer = [[ContentSuggestionsFooterItem alloc]
411 initWithType:ItemTypeFooter 381 initWithType:ItemTypeFooter
412 title:sectionInfo.footerTitle 382 title:sectionInfo.footerTitle
413 block:^{ 383 block:^{
414 [weakSelf runAdditionalActionForSection:sectionInfo]; 384 [weakSelf runAdditionalActionForSection:sectionInfo];
415 }]; 385 }];
416 386
417 [self.collectionViewController.collectionViewModel 387 [self.collectionViewController.collectionViewModel
418 setFooter:footer 388 setFooter:footer
419 forSectionWithIdentifier:sectionIdentifier]; 389 forSectionWithIdentifier:sectionIdentifier];
420 } 390 }
421 } 391 }
422 392
423 // Adds the header corresponding to |sectionInfo| to the section. 393 // Adds the header corresponding to |sectionInfo| to the section if there is
424 - (void)addHeader:(ContentSuggestionsSectionInformation*)sectionInfo { 394 // none present and the section info contains a title.
395 - (void)addHeaderIfNeeded:(ContentSuggestionsSectionInformation*)sectionInfo {
425 NSInteger sectionIdentifier = SectionIdentifierForInfo(sectionInfo); 396 NSInteger sectionIdentifier = SectionIdentifierForInfo(sectionInfo);
426 397
427 if (![self.collectionViewController.collectionViewModel 398 if (![self.collectionViewController.collectionViewModel
428 headerForSectionWithIdentifier:sectionIdentifier]) { 399 headerForSectionWithIdentifier:sectionIdentifier] &&
400 sectionInfo.title) {
429 CollectionViewTextItem* header = 401 CollectionViewTextItem* header =
430 [[CollectionViewTextItem alloc] initWithType:ItemTypeHeader]; 402 [[CollectionViewTextItem alloc] initWithType:ItemTypeHeader];
431 header.text = sectionInfo.title; 403 header.text = sectionInfo.title;
432 [self.collectionViewController.collectionViewModel 404 [self.collectionViewController.collectionViewModel
433 setHeader:header 405 setHeader:header
434 forSectionWithIdentifier:sectionIdentifier]; 406 forSectionWithIdentifier:sectionIdentifier];
435 } 407 }
436 } 408 }
437 409
438 // Resets the models, removing the current CollectionViewItem and the 410 // Resets the models, removing the current CollectionViewItem and the
439 // SectionInfo. 411 // SectionInfo.
440 - (void)resetModels { 412 - (void)resetModels {
441 [self.collectionViewController loadModel]; 413 [self.collectionViewController loadModel];
442 self.sectionInfoBySectionIdentifier = [[NSMutableDictionary alloc] init]; 414 self.sectionInfoBySectionIdentifier = [[NSMutableDictionary alloc] init];
443 } 415 }
444 416
417 // Fetches the favicon attributes for the |item|.
418 - (void)fetchFaviconForItem:(CSCollectionViewItem*)item {
419 __weak ContentSuggestionsCollectionUpdater* weakSelf = self;
420 __weak CSCollectionViewItem* weakItem = item;
421
422 [self.dataSource
423 fetchFaviconAttributesForItem:item
424 completion:^(FaviconAttributes* attributes) {
425 ContentSuggestionsCollectionUpdater* strongSelf =
426 weakSelf;
427 CSCollectionViewItem* strongItem = weakItem;
428 if (!strongSelf || !strongItem) {
429 return;
430 }
431
432 strongItem.attributes = attributes;
433 [strongSelf.collectionViewController
434 reconfigureCellsForItems:@[ strongItem ]];
435
436 [strongSelf fetchFaviconImageForItem:strongItem];
437 }];
438 }
439
440 // Fetches the favicon image for the |item|.
441 - (void)fetchFaviconImageForItem:(CSCollectionViewItem*)item {
442 __weak ContentSuggestionsCollectionUpdater* weakSelf = self;
443 __weak CSCollectionViewItem* weakItem = item;
444
445 [self.dataSource
446 fetchFaviconImageForItem:item
447 completion:^(UIImage* image) {
448 ContentSuggestionsCollectionUpdater* strongSelf =
449 weakSelf;
450 CSCollectionViewItem* strongItem = weakItem;
451 if (!strongSelf || !strongItem || !image) {
452 return;
453 }
454
455 strongItem.attributes =
456 [FaviconAttributes attributesWithImage:image];
457 [strongSelf.collectionViewController
458 reconfigureCellsForItems:@[ strongItem ]];
459 }];
460 }
461
445 // Runs the additional action for the section identified by |sectionInfo|. 462 // Runs the additional action for the section identified by |sectionInfo|.
446 - (void)runAdditionalActionForSection: 463 - (void)runAdditionalActionForSection:
447 (ContentSuggestionsSectionInformation*)sectionInfo { 464 (ContentSuggestionsSectionInformation*)sectionInfo {
448 SectionIdentifier sectionIdentifier = SectionIdentifierForInfo(sectionInfo); 465 SectionIdentifier sectionIdentifier = SectionIdentifierForInfo(sectionInfo);
449 466
467 // TODO(crbug.com/721229): Start spinner.
468
450 NSMutableArray<ContentSuggestionIdentifier*>* knownSuggestionIdentifiers = 469 NSMutableArray<ContentSuggestionIdentifier*>* knownSuggestionIdentifiers =
451 [NSMutableArray array]; 470 [NSMutableArray array];
452 471
453 NSArray<CSCollectionViewItem*>* knownSuggestions = 472 NSArray<CSCollectionViewItem*>* knownSuggestions =
454 [self.collectionViewController.collectionViewModel 473 [self.collectionViewController.collectionViewModel
455 itemsInSectionWithIdentifier:sectionIdentifier]; 474 itemsInSectionWithIdentifier:sectionIdentifier];
456 for (CSCollectionViewItem* suggestion in knownSuggestions) { 475 for (CSCollectionViewItem* suggestion in knownSuggestions) {
457 if (suggestion.type != ItemTypeEmpty) { 476 if (suggestion.type != ItemTypeEmpty) {
458 [knownSuggestionIdentifiers addObject:suggestion.suggestionIdentifier]; 477 [knownSuggestionIdentifiers addObject:suggestion.suggestionIdentifier];
459 } 478 }
460 } 479 }
461 480
462 __weak ContentSuggestionsCollectionUpdater* weakSelf = self; 481 __weak ContentSuggestionsCollectionUpdater* weakSelf = self;
463 [self.dataSource 482 [self.dataSource
464 fetchMoreSuggestionsKnowing:knownSuggestionIdentifiers 483 fetchMoreSuggestionsKnowing:knownSuggestionIdentifiers
465 fromSectionInfo:sectionInfo 484 fromSectionInfo:sectionInfo
466 callback:^(NSArray<ContentSuggestion*>* suggestions) { 485 callback:^(
467 [weakSelf moreSuggestionsFetched:suggestions]; 486 NSArray<CSCollectionViewItem*>* suggestions) {
487 [weakSelf moreSuggestionsFetched:suggestions
488 inSectionInfo:sectionInfo];
468 }]; 489 }];
469 } 490 }
470 491
471 // Adds the |suggestions| to the collection view. All the suggestions must have 492 // Adds the |suggestions| to the collection view. All the suggestions must have
472 // the same sectionInfo. 493 // the same sectionInfo.
473 - (void)moreSuggestionsFetched:(NSArray<ContentSuggestion*>*)suggestions { 494 - (void)moreSuggestionsFetched:(NSArray<CSCollectionViewItem*>*)suggestions
474 [self.collectionViewController addSuggestions:suggestions]; 495 inSectionInfo:
496 (ContentSuggestionsSectionInformation*)sectionInfo {
497 if (suggestions) {
498 [self.collectionViewController addSuggestions:suggestions
499 toSectionInfo:sectionInfo];
500 }
501 // TODO(crbug.com/721229):Stop spinner.
475 } 502 }
476 503
477 // Returns a item to be displayed when the section identified by |sectionInfo| 504 // Returns a item to be displayed when the section identified by |sectionInfo|
478 // is empty. 505 // is empty.
479 - (CSCollectionViewItem*)emptyItemForSectionInfo: 506 - (CSCollectionViewItem*)emptyItemForSectionInfo:
480 (ContentSuggestionsSectionInformation*)sectionInfo { 507 (ContentSuggestionsSectionInformation*)sectionInfo {
481 ContentSuggestionsTextItem* item = 508 ContentSuggestionsTextItem* item =
482 [[ContentSuggestionsTextItem alloc] initWithType:ItemTypeEmpty]; 509 [[ContentSuggestionsTextItem alloc] initWithType:ItemTypeEmpty];
483 item.text = l10n_util::GetNSString(IDS_NTP_TITLE_NO_SUGGESTIONS); 510 item.text = l10n_util::GetNSString(IDS_NTP_TITLE_NO_SUGGESTIONS);
484 item.detailText = sectionInfo.emptyText; 511 item.detailText = sectionInfo.emptyText;
485 512
486 return item; 513 return item;
487 } 514 }
488 515
489 // Returns a suggestion item built with the |suggestion|.
490 - (ContentSuggestionsItem*)suggestionItemForSuggestion:
491 (ContentSuggestion*)suggestion {
492 ContentSuggestionsItem* suggestionItem = [[ContentSuggestionsItem alloc]
493 initWithType:ItemTypeForContentSuggestionType(suggestion.type)
494 title:suggestion.title
495 subtitle:suggestion.text
496 delegate:self
497 url:suggestion.url];
498
499 suggestionItem.publisher = suggestion.publisher;
500 suggestionItem.publishDate = suggestion.publishDate;
501 suggestionItem.availableOffline = suggestion.availableOffline;
502
503 suggestionItem.suggestionIdentifier = suggestion.suggestionIdentifier;
504
505 return suggestionItem;
506 }
507
508 // Fetches the favicon associated with the |URL|, call the |callback| with the
509 // attributes then reconfigure the |item|.
510 - (void)fetchFaviconForItem:(CSCollectionViewItem*)item
511 withURL:(const GURL&)URL
512 callback:(void (^)(FaviconAttributes*))callback {
513 if (!callback)
514 return;
515
516 __weak ContentSuggestionsCollectionUpdater* weakSelf = self;
517 __weak CSCollectionViewItem* weakItem = item;
518 void (^completionBlock)(FaviconAttributes* attributes) =
519 ^(FaviconAttributes* attributes) {
520 CSCollectionViewItem* strongItem = weakItem;
521 ContentSuggestionsCollectionUpdater* strongSelf = weakSelf;
522 if (!strongSelf || !strongItem) {
523 return;
524 }
525
526 callback(attributes);
527
528 [strongSelf.collectionViewController
529 reconfigureCellsForItems:@[ strongItem ]];
530 };
531
532 [self.dataSource fetchFaviconAttributesForURL:URL completion:completionBlock];
533 }
534
535 // Adds |item| to |sectionIdentifier| section of the model of the 516 // Adds |item| to |sectionIdentifier| section of the model of the
536 // CollectionView. Returns the IndexPath of the newly added item. 517 // CollectionView. Returns the IndexPath of the newly added item.
537 - (NSIndexPath*)addItem:(CSCollectionViewItem*)item 518 - (NSIndexPath*)addItem:(CSCollectionViewItem*)item
538 toSectionWithIdentifier:(NSInteger)sectionIdentifier { 519 toSectionWithIdentifier:(NSInteger)sectionIdentifier {
539 CSCollectionViewModel* model = 520 CSCollectionViewModel* model =
540 self.collectionViewController.collectionViewModel; 521 self.collectionViewController.collectionViewModel;
541 NSInteger section = [model sectionForSectionIdentifier:sectionIdentifier]; 522 NSInteger section = [model sectionForSectionIdentifier:sectionIdentifier];
542 NSInteger itemNumber = [model numberOfItemsInSection:section]; 523 NSInteger itemNumber = [model numberOfItemsInSection:section];
543 [model addItem:item toSectionWithIdentifier:sectionIdentifier]; 524 [model addItem:item toSectionWithIdentifier:sectionIdentifier];
544 525
545 return [NSIndexPath indexPathForItem:itemNumber inSection:section]; 526 return [NSIndexPath indexPathForItem:itemNumber inSection:section];
546 } 527 }
547 528
548 @end 529 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698