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

Side by Side Diff: components/ntp_snippets/offline_pages/offline_page_suggestions_provider.cc

Issue 2251743002: Refactor OfflinePageSuggestionsProvider dismissed ID handling (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@offlinedelete
Patch Set: Rebase Created 4 years, 4 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
« no previous file with comments | « components/ntp_snippets/offline_pages/offline_page_suggestions_provider.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 #include "components/ntp_snippets/offline_pages/offline_page_suggestions_provide r.h" 5 #include "components/ntp_snippets/offline_pages/offline_page_suggestions_provide r.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/guid.h" 10 #include "base/guid.h"
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
53 ? CategoryStatus::AVAILABLE_LOADING 53 ? CategoryStatus::AVAILABLE_LOADING
54 : CategoryStatus::NOT_PROVIDED), 54 : CategoryStatus::NOT_PROVIDED),
55 downloads_status_(downloads_enabled ? CategoryStatus::AVAILABLE_LOADING 55 downloads_status_(downloads_enabled ? CategoryStatus::AVAILABLE_LOADING
56 : CategoryStatus::NOT_PROVIDED), 56 : CategoryStatus::NOT_PROVIDED),
57 offline_page_model_(offline_page_model), 57 offline_page_model_(offline_page_model),
58 recent_tabs_category_( 58 recent_tabs_category_(
59 category_factory->FromKnownCategory(KnownCategories::RECENT_TABS)), 59 category_factory->FromKnownCategory(KnownCategories::RECENT_TABS)),
60 downloads_category_( 60 downloads_category_(
61 category_factory->FromKnownCategory(KnownCategories::DOWNLOADS)), 61 category_factory->FromKnownCategory(KnownCategories::DOWNLOADS)),
62 pref_service_(pref_service), 62 pref_service_(pref_service),
63 dismissed_recent_tab_ids_(ReadDismissedIDsFromPrefs(
64 prefs::kDismissedRecentOfflineTabSuggestions)),
65 dismissed_download_ids_(
66 ReadDismissedIDsFromPrefs(prefs::kDismissedDownloadSuggestions)),
67 download_manager_ui_enabled_(download_manager_ui_enabled) { 63 download_manager_ui_enabled_(download_manager_ui_enabled) {
68 DCHECK(recent_tabs_enabled || downloads_enabled); 64 DCHECK(recent_tabs_enabled || downloads_enabled);
69 offline_page_model_->AddObserver(this); 65 offline_page_model_->AddObserver(this);
70 FetchOfflinePages(); 66 FetchOfflinePages();
71 } 67 }
72 68
73 OfflinePageSuggestionsProvider::~OfflinePageSuggestionsProvider() { 69 OfflinePageSuggestionsProvider::~OfflinePageSuggestionsProvider() {
74 offline_page_model_->RemoveObserver(this); 70 offline_page_model_->RemoveObserver(this);
75 } 71 }
76 72
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 NOTREACHED() << "Unknown category " << category.id(); 116 NOTREACHED() << "Unknown category " << category.id();
121 return CategoryInfo(base::string16(), 117 return CategoryInfo(base::string16(),
122 ContentSuggestionsCardLayout::MINIMAL_CARD, 118 ContentSuggestionsCardLayout::MINIMAL_CARD,
123 /* has_more_button */ false); 119 /* has_more_button */ false);
124 } 120 }
125 121
126 void OfflinePageSuggestionsProvider::DismissSuggestion( 122 void OfflinePageSuggestionsProvider::DismissSuggestion(
127 const std::string& suggestion_id) { 123 const std::string& suggestion_id) {
128 Category category = GetCategoryFromUniqueID(suggestion_id); 124 Category category = GetCategoryFromUniqueID(suggestion_id);
129 std::string offline_page_id = GetWithinCategoryIDFromUniqueID(suggestion_id); 125 std::string offline_page_id = GetWithinCategoryIDFromUniqueID(suggestion_id);
130 if (category == recent_tabs_category_) { 126 std::set<std::string> dismissed_ids = ReadDismissedIDsFromPrefs(category);
131 DCHECK_NE(CategoryStatus::NOT_PROVIDED, recent_tabs_status_); 127 dismissed_ids.insert(offline_page_id);
132 dismissed_recent_tab_ids_.insert(offline_page_id); 128 StoreDismissedIDsToPrefs(category, dismissed_ids);
133 StoreDismissedIDsToPrefs(prefs::kDismissedRecentOfflineTabSuggestions,
134 dismissed_recent_tab_ids_);
135 } else if (category == downloads_category_) {
136 DCHECK_NE(CategoryStatus::NOT_PROVIDED, downloads_status_);
137 dismissed_download_ids_.insert(offline_page_id);
138 StoreDismissedIDsToPrefs(prefs::kDismissedDownloadSuggestions,
139 dismissed_download_ids_);
140 } else {
141 NOTREACHED() << "Unknown category " << category.id();
142 }
143 } 129 }
144 130
145 void OfflinePageSuggestionsProvider::FetchSuggestionImage( 131 void OfflinePageSuggestionsProvider::FetchSuggestionImage(
146 const std::string& suggestion_id, 132 const std::string& suggestion_id,
147 const ImageFetchedCallback& callback) { 133 const ImageFetchedCallback& callback) {
148 // TODO(pke): Fetch proper thumbnail from OfflinePageModel once it's available 134 // TODO(pke): Fetch proper thumbnail from OfflinePageModel once it's available
149 // there. 135 // there.
150 base::ThreadTaskRunnerHandle::Get()->PostTask( 136 base::ThreadTaskRunnerHandle::Get()->PostTask(
151 FROM_HERE, base::Bind(callback, suggestion_id, gfx::Image())); 137 FROM_HERE, base::Bind(callback, suggestion_id, gfx::Image()));
152 } 138 }
153 139
154 void OfflinePageSuggestionsProvider::ClearCachedSuggestionsForDebugging( 140 void OfflinePageSuggestionsProvider::ClearCachedSuggestionsForDebugging(
155 Category category) { 141 Category category) {
156 // Ignored. 142 // Ignored.
157 } 143 }
158 144
159 std::vector<ContentSuggestion> 145 std::vector<ContentSuggestion>
160 OfflinePageSuggestionsProvider::GetDismissedSuggestionsForDebugging( 146 OfflinePageSuggestionsProvider::GetDismissedSuggestionsForDebugging(
161 Category category) { 147 Category category) {
162 // TODO(pke): Make GetDismissedSuggestionsForDebugging asynchronous so this 148 // TODO(pke): Make GetDismissedSuggestionsForDebugging asynchronous so this
163 // can return proper values. 149 // can return proper values.
150 std::set<std::string> dismissed_ids = ReadDismissedIDsFromPrefs(category);
164 std::vector<ContentSuggestion> suggestions; 151 std::vector<ContentSuggestion> suggestions;
165 const std::set<std::string>* dismissed_ids = nullptr; 152 for (const std::string& dismissed_id : dismissed_ids) {
166 if (category == recent_tabs_category_) {
167 DCHECK_NE(CategoryStatus::NOT_PROVIDED, recent_tabs_status_);
168 dismissed_ids = &dismissed_recent_tab_ids_;
169 } else if (category == downloads_category_) {
170 DCHECK_NE(CategoryStatus::NOT_PROVIDED, downloads_status_);
171 dismissed_ids = &dismissed_download_ids_;
172 } else {
173 NOTREACHED() << "Unknown category " << category.id();
174 return suggestions;
175 }
176
177 for (const std::string& dismissed_id : *dismissed_ids) {
178 ContentSuggestion suggestion( 153 ContentSuggestion suggestion(
179 MakeUniqueID(category, dismissed_id), 154 MakeUniqueID(category, dismissed_id),
180 GURL("http://dismissed-offline-page-" + dismissed_id)); 155 GURL("http://dismissed-offline-page-" + dismissed_id));
181 suggestion.set_title(base::UTF8ToUTF16("Title not available")); 156 suggestion.set_title(base::UTF8ToUTF16("Title not available"));
182 suggestions.push_back(std::move(suggestion)); 157 suggestions.push_back(std::move(suggestion));
183 } 158 }
184 return suggestions; 159 return suggestions;
185 } 160 }
186 161
187 void OfflinePageSuggestionsProvider::ClearDismissedSuggestionsForDebugging( 162 void OfflinePageSuggestionsProvider::ClearDismissedSuggestionsForDebugging(
188 Category category) { 163 Category category) {
189 if (category == recent_tabs_category_) { 164 StoreDismissedIDsToPrefs(category, std::set<std::string>());
190 DCHECK_NE(CategoryStatus::NOT_PROVIDED, recent_tabs_status_);
191 dismissed_recent_tab_ids_.clear();
192 StoreDismissedIDsToPrefs(prefs::kDismissedRecentOfflineTabSuggestions,
193 dismissed_recent_tab_ids_);
194 } else if (category == downloads_category_) {
195 DCHECK_NE(CategoryStatus::NOT_PROVIDED, downloads_status_);
196 dismissed_download_ids_.clear();
197 StoreDismissedIDsToPrefs(prefs::kDismissedDownloadSuggestions,
198 dismissed_download_ids_);
199 } else {
200 NOTREACHED() << "Unknown category " << category.id();
201 }
202 FetchOfflinePages(); 165 FetchOfflinePages();
203 } 166 }
204 167
205 void OfflinePageSuggestionsProvider::OfflinePageModelLoaded( 168 void OfflinePageSuggestionsProvider::OfflinePageModelLoaded(
206 OfflinePageModel* model) { 169 OfflinePageModel* model) {
207 DCHECK_EQ(offline_page_model_, model); 170 DCHECK_EQ(offline_page_model_, model);
208 } 171 }
209 172
210 void OfflinePageSuggestionsProvider::OfflinePageModelChanged( 173 void OfflinePageSuggestionsProvider::OfflinePageModelChanged(
211 OfflinePageModel* model) { 174 OfflinePageModel* model) {
212 DCHECK_EQ(offline_page_model_, model); 175 DCHECK_EQ(offline_page_model_, model);
213 FetchOfflinePages(); 176 FetchOfflinePages();
214 } 177 }
215 178
216 void OfflinePageSuggestionsProvider::OfflinePageDeleted( 179 void OfflinePageSuggestionsProvider::OfflinePageDeleted(
217 int64_t offline_id, 180 int64_t offline_id,
218 const offline_pages::ClientId& client_id) { 181 const offline_pages::ClientId& client_id) {
219 // Because we never switch to NOT_PROVIDED dynamically, there can be no open 182 // Because we never switch to NOT_PROVIDED dynamically, there can be no open
220 // UI containing an invalidated suggestion unless the status is something 183 // UI containing an invalidated suggestion unless the status is something
221 // other than NOT_PROVIDED, so only notify invalidation in that case. 184 // other than NOT_PROVIDED, so only notify invalidation in that case.
222 std::string offline_page_id = base::IntToString(offline_id);
223 if (recent_tabs_status_ != CategoryStatus::NOT_PROVIDED && 185 if (recent_tabs_status_ != CategoryStatus::NOT_PROVIDED &&
224 client_id.name_space == offline_pages::kLastNNamespace) { 186 client_id.name_space == offline_pages::kLastNNamespace) {
225 auto it = std::find(dismissed_recent_tab_ids_.begin(), 187 InvalidateSuggestion(recent_tabs_category_, offline_id);
226 dismissed_recent_tab_ids_.end(), offline_page_id);
227 if (it == dismissed_recent_tab_ids_.end()) {
228 observer()->OnSuggestionInvalidated(
229 this, recent_tabs_category_,
230 MakeUniqueID(recent_tabs_category_, offline_page_id));
231 } else {
232 dismissed_recent_tab_ids_.erase(it);
233 StoreDismissedIDsToPrefs(prefs::kDismissedRecentOfflineTabSuggestions,
234 dismissed_recent_tab_ids_);
235 }
236 } else if (downloads_status_ != CategoryStatus::NOT_PROVIDED && 188 } else if (downloads_status_ != CategoryStatus::NOT_PROVIDED &&
237 client_id.name_space == offline_pages::kAsyncNamespace && 189 client_id.name_space == offline_pages::kAsyncNamespace &&
238 base::IsValidGUID(client_id.id)) { 190 base::IsValidGUID(client_id.id)) {
239 auto it = std::find(dismissed_download_ids_.begin(), 191 InvalidateSuggestion(downloads_category_, offline_id);
240 dismissed_download_ids_.end(), offline_page_id);
241 if (it == dismissed_download_ids_.end()) {
242 observer()->OnSuggestionInvalidated(
243 this, downloads_category_,
244 MakeUniqueID(downloads_category_, offline_page_id));
245 } else {
246 dismissed_download_ids_.erase(it);
247 StoreDismissedIDsToPrefs(prefs::kDismissedDownloadSuggestions,
248 dismissed_download_ids_);
249 }
250 } 192 }
251 } 193 }
252 194
253 void OfflinePageSuggestionsProvider::FetchOfflinePages() { 195 void OfflinePageSuggestionsProvider::FetchOfflinePages() {
196 // TODO(pke): When something other than GetAllPages is used here, the
197 // dismissed IDs cleanup in OnOfflinePagesLoaded needs to be changed to avoid
198 // suggestions being undismissed accidentally.
254 offline_page_model_->GetAllPages( 199 offline_page_model_->GetAllPages(
255 base::Bind(&OfflinePageSuggestionsProvider::OnOfflinePagesLoaded, 200 base::Bind(&OfflinePageSuggestionsProvider::OnOfflinePagesLoaded,
256 base::Unretained(this))); 201 base::Unretained(this)));
257 } 202 }
258 203
259 void OfflinePageSuggestionsProvider::OnOfflinePagesLoaded( 204 void OfflinePageSuggestionsProvider::OnOfflinePagesLoaded(
260 const MultipleOfflinePageItemResult& result) { 205 const MultipleOfflinePageItemResult& result) {
261 bool need_recent_tabs = recent_tabs_status_ != CategoryStatus::NOT_PROVIDED; 206 bool need_recent_tabs = recent_tabs_status_ != CategoryStatus::NOT_PROVIDED;
262 bool need_downloads = downloads_status_ != CategoryStatus::NOT_PROVIDED; 207 bool need_downloads = downloads_status_ != CategoryStatus::NOT_PROVIDED;
263 if (need_recent_tabs) 208 if (need_recent_tabs)
264 NotifyStatusChanged(recent_tabs_category_, CategoryStatus::AVAILABLE); 209 NotifyStatusChanged(recent_tabs_category_, CategoryStatus::AVAILABLE);
265 if (need_downloads) 210 if (need_downloads)
266 NotifyStatusChanged(downloads_category_, CategoryStatus::AVAILABLE); 211 NotifyStatusChanged(downloads_category_, CategoryStatus::AVAILABLE);
267 212
213 std::set<std::string> dismissed_recent_tab_ids =
214 ReadDismissedIDsFromPrefs(recent_tabs_category_);
215 std::set<std::string> dismissed_download_ids =
216 ReadDismissedIDsFromPrefs(downloads_category_);
217 std::set<std::string> cleaned_recent_tab_ids;
218 std::set<std::string> cleaned_download_ids;
268 std::vector<const OfflinePageItem*> recent_tab_items; 219 std::vector<const OfflinePageItem*> recent_tab_items;
269 std::vector<const OfflinePageItem*> download_items; 220 std::vector<const OfflinePageItem*> download_items;
270 for (const OfflinePageItem& item : result) { 221 for (const OfflinePageItem& item : result) {
222 std::string offline_page_id = base::IntToString(item.offline_id);
271 if (need_recent_tabs && 223 if (need_recent_tabs &&
272 item.client_id.name_space == offline_pages::kLastNNamespace && 224 item.client_id.name_space == offline_pages::kLastNNamespace) {
273 !dismissed_recent_tab_ids_.count(base::IntToString(item.offline_id))) { 225 if (dismissed_recent_tab_ids.count(offline_page_id))
274 recent_tab_items.push_back(&item); 226 cleaned_recent_tab_ids.insert(offline_page_id);
227 else
228 recent_tab_items.push_back(&item);
275 } 229 }
276 230
277 // TODO(pke): Use kDownloadNamespace once the OfflinePageModel uses that. 231 // TODO(pke): Use kDownloadNamespace once the OfflinePageModel uses that.
278 // The current logic is taken from DownloadUIAdapter::IsVisibleInUI. 232 // The current logic is taken from DownloadUIAdapter::IsVisibleInUI.
279 // Note: This is also copied in OfflinePageDeleted above. 233 // Note: This is also copied in |OfflinePageDeleted| above.
280 if (need_downloads && 234 if (need_downloads &&
281 item.client_id.name_space == offline_pages::kAsyncNamespace && 235 item.client_id.name_space == offline_pages::kAsyncNamespace &&
282 base::IsValidGUID(item.client_id.id) && 236 base::IsValidGUID(item.client_id.id)) {
283 !dismissed_download_ids_.count(base::IntToString(item.offline_id))) { 237 if (dismissed_download_ids.count(offline_page_id))
284 download_items.push_back(&item); 238 cleaned_download_ids.insert(offline_page_id);
239 else
240 download_items.push_back(&item);
285 } 241 }
286 } 242 }
287 243
288 // TODO(pke): Once we have our OfflinePageModel getter and that doesn't do it 244 // TODO(pke): Once we have our OfflinePageModel getter and that doesn't do it
289 // already, filter out duplicate URLs for recent tabs here. Duplicates for 245 // already, filter out duplicate URLs for recent tabs here. Duplicates for
290 // downloads are fine. 246 // downloads are fine.
291 247
292 if (need_recent_tabs) { 248 if (need_recent_tabs) {
293 observer()->OnNewSuggestions( 249 observer()->OnNewSuggestions(
294 this, recent_tabs_category_, 250 this, recent_tabs_category_,
295 GetMostRecentlyVisited(recent_tabs_category_, 251 GetMostRecentlyVisited(recent_tabs_category_,
296 std::move(recent_tab_items))); 252 std::move(recent_tab_items)));
253 if (cleaned_recent_tab_ids.size() != dismissed_recent_tab_ids.size())
254 StoreDismissedIDsToPrefs(recent_tabs_category_, cleaned_recent_tab_ids);
297 } 255 }
298 if (need_downloads) { 256 if (need_downloads) {
299 observer()->OnNewSuggestions( 257 observer()->OnNewSuggestions(
300 this, downloads_category_, 258 this, downloads_category_,
301 GetMostRecentlyVisited(downloads_category_, std::move(download_items))); 259 GetMostRecentlyVisited(downloads_category_, std::move(download_items)));
260 if (cleaned_download_ids.size() != dismissed_download_ids.size())
261 StoreDismissedIDsToPrefs(downloads_category_, cleaned_download_ids);
302 } 262 }
303 } 263 }
304 264
305 void OfflinePageSuggestionsProvider::NotifyStatusChanged( 265 void OfflinePageSuggestionsProvider::NotifyStatusChanged(
306 Category category, 266 Category category,
307 CategoryStatus new_status) { 267 CategoryStatus new_status) {
308 if (category == recent_tabs_category_) { 268 if (category == recent_tabs_category_) {
309 DCHECK_NE(CategoryStatus::NOT_PROVIDED, recent_tabs_status_); 269 DCHECK_NE(CategoryStatus::NOT_PROVIDED, recent_tabs_status_);
310 if (recent_tabs_status_ == new_status) 270 if (recent_tabs_status_ == new_status)
311 return; 271 return;
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
352 OrderByMostRecentlyVisited()); 312 OrderByMostRecentlyVisited());
353 std::vector<ContentSuggestion> suggestions; 313 std::vector<ContentSuggestion> suggestions;
354 for (const OfflinePageItem* offline_page_item : offline_page_items) { 314 for (const OfflinePageItem* offline_page_item : offline_page_items) {
355 suggestions.push_back(ConvertOfflinePage(category, *offline_page_item)); 315 suggestions.push_back(ConvertOfflinePage(category, *offline_page_item));
356 if (suggestions.size() == kMaxSuggestionsCount) 316 if (suggestions.size() == kMaxSuggestionsCount)
357 break; 317 break;
358 } 318 }
359 return suggestions; 319 return suggestions;
360 } 320 }
361 321
322 void OfflinePageSuggestionsProvider::InvalidateSuggestion(Category category,
323 int64_t offline_id) {
324 std::string offline_page_id = base::IntToString(offline_id);
325 observer()->OnSuggestionInvalidated(this, category,
326 MakeUniqueID(category, offline_page_id));
327
328 std::set<std::string> dismissed_ids = ReadDismissedIDsFromPrefs(category);
329 auto it =
330 std::find(dismissed_ids.begin(), dismissed_ids.end(), offline_page_id);
331 if (it != dismissed_ids.end()) {
332 dismissed_ids.erase(it);
333 StoreDismissedIDsToPrefs(category, dismissed_ids);
334 }
335 }
336
337 std::string OfflinePageSuggestionsProvider::GetDismissedPref(
338 Category category) const {
339 if (category == recent_tabs_category_)
340 return prefs::kDismissedRecentOfflineTabSuggestions;
341 if (category == downloads_category_)
342 return prefs::kDismissedDownloadSuggestions;
343 NOTREACHED() << "Unknown category " << category.id();
344 return std::string();
345 }
346
362 std::set<std::string> OfflinePageSuggestionsProvider::ReadDismissedIDsFromPrefs( 347 std::set<std::string> OfflinePageSuggestionsProvider::ReadDismissedIDsFromPrefs(
363 const std::string& pref_name) const { 348 Category category) const {
364 std::set<std::string> dismissed_ids; 349 std::set<std::string> dismissed_ids;
365 const base::ListValue* list = pref_service_->GetList(pref_name); 350 const base::ListValue* list =
351 pref_service_->GetList(GetDismissedPref(category));
366 for (const std::unique_ptr<base::Value>& value : *list) { 352 for (const std::unique_ptr<base::Value>& value : *list) {
367 std::string dismissed_id; 353 std::string dismissed_id;
368 bool success = value->GetAsString(&dismissed_id); 354 bool success = value->GetAsString(&dismissed_id);
369 DCHECK(success) << "Failed to parse dismissed offline page ID from prefs"; 355 DCHECK(success) << "Failed to parse dismissed offline page ID from prefs";
370 dismissed_ids.insert(dismissed_id); 356 dismissed_ids.insert(dismissed_id);
371 } 357 }
372 return dismissed_ids; 358 return dismissed_ids;
373 } 359 }
374 360
375 void OfflinePageSuggestionsProvider::StoreDismissedIDsToPrefs( 361 void OfflinePageSuggestionsProvider::StoreDismissedIDsToPrefs(
376 const std::string& pref_name, 362 Category category,
377 const std::set<std::string>& dismissed_ids) { 363 const std::set<std::string>& dismissed_ids) {
378 base::ListValue list; 364 base::ListValue list;
379 for (const std::string& dismissed_id : dismissed_ids) 365 for (const std::string& dismissed_id : dismissed_ids)
380 list.AppendString(dismissed_id); 366 list.AppendString(dismissed_id);
381 pref_service_->Set(pref_name, list); 367 pref_service_->Set(GetDismissedPref(category), list);
382 } 368 }
383 369
384 } // namespace ntp_snippets 370 } // namespace ntp_snippets
OLDNEW
« no previous file with comments | « components/ntp_snippets/offline_pages/offline_page_suggestions_provider.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698