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

Side by Side Diff: components/ntp_snippets/content_suggestions_service.cc

Issue 2102023002: Add ContentSuggestionsService (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: comments Created 4 years, 5 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "components/ntp_snippets/content_suggestions_service.h"
6
7 #include <algorithm>
8 #include <iterator>
9
10 #include "base/bind.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "ui/gfx/image/image.h"
13
14 namespace ntp_snippets {
15
16 namespace {
17
18 // Helper method get the provider_type from a combined ID.
tschumann 2016/07/01 09:37:11 nit: let's keep the comment focused on the essenti
Philipp Keck 2016/07/01 13:00:04 Done.
19 ContentSuggestionsProviderType GetProviderTypeFromCombinedID(
20 const std::string& id) {
21 size_t colon_index = id.find(":");
22 DCHECK_NE(std::string::npos, colon_index);
23 int provider_type;
24 DCHECK(base::StringToInt(id.substr(0, colon_index), &provider_type));
25 DCHECK(0 <= provider_type &&
26 provider_type < int(ContentSuggestionsProviderType::COUNT));
27 return ContentSuggestionsProviderType(provider_type);
28 }
29
30 // Helper method get the original ID from a combined ID.
31 std::string GetIDFromCombinedID(const std::string& id) {
32 size_t colon_index = id.find(":");
33 DCHECK_NE(std::string::npos, colon_index);
34 return id.substr(colon_index + 1);
35 }
36
37 // Helper method to remove all suggestions of the given |provider_type| from
38 // the given |suggestions|. Returns true if anything changed.
39 bool RemoveSuggestionsOfProvider(std::vector<ContentSuggestion>* suggestions,
40 ContentSuggestionsProviderType provider_type) {
41 auto remove_from = std::remove_if(
42 suggestions->begin(), suggestions->end(),
43 [provider_type](const ContentSuggestion& s) {
44 return GetProviderTypeFromCombinedID(s.id()) == provider_type;
45 });
46 if (remove_from == suggestions->end()) {
47 return false;
48 }
49 suggestions->erase(remove_from, suggestions->end());
50 return true;
51 }
52
53 } // namespace
54
55 ContentSuggestionsService::ContentSuggestionsService(Enabled enabled)
56 : enabled_(enabled) {}
57
58 ContentSuggestionsService::~ContentSuggestionsService() {}
59
60 void ContentSuggestionsService::Shutdown() {
61 FOR_EACH_OBSERVER(Observer, observers_, ContentSuggestionsServiceShutdown());
62 observers_.Clear();
63 for (auto& provider : providers_) {
tschumann 2016/07/01 09:37:11 Too bad we don't have value_view in Chrome (https:
Philipp Keck 2016/07/01 13:00:04 Acknowledged.
64 provider.second->SetObserver(nullptr);
65 }
66 providers_.clear();
67 enabled_ = Enabled::Disable;
68 }
69
70 const std::vector<ContentSuggestion>&
71 ContentSuggestionsService::GetSuggestionsForCategory(
72 ContentSuggestionCategory category) const {
73 return suggestions_by_category_.at(category);
74 }
75
76 void ContentSuggestionsService::FetchSuggestionImage(
77 const std::string& suggestion_id,
78 const ImageFetchedCallback& callback) {
79 ContentSuggestionsProviderType provider_type =
80 GetProviderTypeFromCombinedID(suggestion_id);
81 if (providers_.count(provider_type)) {
82 std::string original_id = GetIDFromCombinedID(suggestion_id);
83 providers_[provider_type]->FetchSuggestionImage(
84 original_id, base::Bind(callback, suggestion_id));
85 } else {
86 LOG(WARNING) << "Requested image for suggestion " << suggestion_id
87 << " for unavailable provider type " << int(provider_type);
88 callback.Run(suggestion_id, gfx::Image());
89 }
90 }
91
92 void ContentSuggestionsService::ClearCachedSuggestionsForDebugging() {
93 categories_.clear();
94 suggestions_by_category_.clear();
95 for (auto& provider : providers_) {
96 provider.second->ClearCachedSuggestionsForDebugging();
97 }
98 FOR_EACH_OBSERVER(Observer, observers_, OnSuggestionsChanged());
99 }
100
101 void ContentSuggestionsService::ClearDiscardedSuggestionsForDebugging() {
102 for (auto& provider : providers_) {
103 provider.second->ClearDiscardedSuggestionsForDebugging();
104 }
105 }
106
107 void ContentSuggestionsService::DiscardSuggestion(
108 const std::string& suggestion_id) {
109 ContentSuggestionsProviderType provider_type =
110 GetProviderTypeFromCombinedID(suggestion_id);
111 std::string original_id = GetIDFromCombinedID(suggestion_id);
112
113 if (providers_.count(provider_type)) {
114 providers_[provider_type]->DiscardSuggestion(original_id);
115
116 // Remove the suggestion locally.
117 for (auto& pair : suggestions_by_category_) {
118 auto position =
119 std::find_if(pair.second.begin(), pair.second.end(),
120 [&suggestion_id](const ContentSuggestion& suggestion) {
121 return suggestion_id == suggestion.id();
122 });
123 if (position != pair.second.end()) {
124 pair.second.erase(position);
125 }
126 }
127 } else {
128 LOG(WARNING) << "Discarded suggestion " << suggestion_id
129 << " for unavailable provider type " << int(provider_type);
130 }
131 }
132
133 void ContentSuggestionsService::AddObserver(Observer* observer) {
134 observers_.AddObserver(observer);
135 }
136
137 void ContentSuggestionsService::RemoveObserver(Observer* observer) {
138 observers_.RemoveObserver(observer);
139 }
140
141 void ContentSuggestionsService::RegisterProvider(
142 ContentSuggestionsProvider* provider) {
143 DCHECK_EQ(0ul, providers_.count(provider->GetProviderType()));
144 providers_[provider->GetProviderType()] = provider;
145 provider->SetObserver(this);
146 }
147
148 ////////////////////////////////////////////////////////////////////////////////
149 // Private methods
150
151 void ContentSuggestionsService::OnSuggestionsChanged(
152 ContentSuggestionsProviderType provider_type,
153 ContentSuggestionCategory changed_category,
154 std::vector<ContentSuggestion> new_suggestions) {
155 RemoveSuggestionsOfProvider(&suggestions_by_category_[changed_category],
156 provider_type);
157
158 std::move(new_suggestions.begin(), new_suggestions.end(),
159 std::back_inserter(suggestions_by_category_[changed_category]));
160
161 // Ensure the category exists exactly if its list of suggestions is nonempty.
162 auto position =
163 std::find(categories_.begin(), categories_.end(), changed_category);
164 if (suggestions_by_category_[changed_category].empty()) {
165 suggestions_by_category_.erase(changed_category);
166 if (position != categories_.end()) {
167 categories_.erase(position);
168 }
169 } else {
170 if (position == categories_.end()) {
171 // TODO(pke) In the future, make sure that the categories have some useful
172 // (maybe constant, at least consistent) ordering for the UI.
173 categories_.emplace_back(changed_category);
174 }
175 }
176
177 FOR_EACH_OBSERVER(Observer, observers_, OnSuggestionsChanged());
178 }
179
180 void ContentSuggestionsService::OnProviderShutdown(
181 ContentSuggestionsProviderType provider_type) {
182 auto iterator = suggestions_by_category_.begin();
183 while (iterator != suggestions_by_category_.end()) {
184 if (RemoveSuggestionsOfProvider(&iterator->second, provider_type) &&
185 iterator->second.empty()) {
186 categories_.erase(
187 std::find(categories_.begin(), categories_.end(), iterator->first));
188 iterator = suggestions_by_category_.erase(iterator);
189 } else {
190 ++iterator;
191 }
192 }
193 FOR_EACH_OBSERVER(Observer, observers_, OnSuggestionsChanged());
194 providers_.erase(provider_type);
195 // TODO(pke) Notify the UI to do a force refresh in this case, as the
196 // suggestions must disappear immediately.
197 }
198
199 } // namespace ntp_snippets
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698