json_to_categories.cc 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. // Copyright 2017 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. #include "components/ntp_snippets/remote/json_to_categories.h"
  5. #include "base/strings/utf_string_conversions.h"
  6. #include "components/strings/grit/components_strings.h"
  7. #include "third_party/abseil-cpp/absl/types/optional.h"
  8. #include "ui/base/l10n/l10n_util.h"
  9. namespace ntp_snippets {
  10. namespace {
  11. // Creates suggestions from dictionary values in |list| and adds them to
  12. // |suggestions|. Returns true on success, false if anything went wrong.
  13. bool AddSuggestionsFromListValue(int remote_category_id,
  14. const base::Value::List& list,
  15. RemoteSuggestion::PtrVector* suggestions,
  16. const base::Time& fetch_time) {
  17. for (const base::Value& value : list) {
  18. const base::Value::Dict* dict = value.GetIfDict();
  19. if (!dict) {
  20. return false;
  21. }
  22. std::unique_ptr<RemoteSuggestion> suggestion;
  23. suggestion = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
  24. *dict, remote_category_id, fetch_time);
  25. if (!suggestion) {
  26. return false;
  27. }
  28. suggestions->push_back(std::move(suggestion));
  29. }
  30. return true;
  31. }
  32. } // namespace
  33. FetchedCategory::FetchedCategory(Category c, CategoryInfo&& info)
  34. : category(c), info(info) {}
  35. FetchedCategory::FetchedCategory(FetchedCategory&&) = default;
  36. FetchedCategory::~FetchedCategory() = default;
  37. FetchedCategory& FetchedCategory::operator=(FetchedCategory&&) = default;
  38. CategoryInfo BuildArticleCategoryInfo(
  39. const absl::optional<std::u16string>& title) {
  40. return CategoryInfo(
  41. title.has_value() ? title.value()
  42. : l10n_util::GetStringUTF16(
  43. IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_HEADER),
  44. ContentSuggestionsCardLayout::FULL_CARD,
  45. ContentSuggestionsAdditionalAction::FETCH,
  46. /*show_if_empty=*/true,
  47. l10n_util::GetStringUTF16(IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_EMPTY));
  48. }
  49. CategoryInfo BuildRemoteCategoryInfo(const std::u16string& title,
  50. bool allow_fetching_more_results) {
  51. ContentSuggestionsAdditionalAction action =
  52. ContentSuggestionsAdditionalAction::NONE;
  53. if (allow_fetching_more_results) {
  54. action = ContentSuggestionsAdditionalAction::FETCH;
  55. }
  56. return CategoryInfo(
  57. title, ContentSuggestionsCardLayout::FULL_CARD, action,
  58. /*show_if_empty=*/false,
  59. // TODO(tschumann): The message for no-articles is likely wrong
  60. // and needs to be added to the stubby protocol if we want to
  61. // support it.
  62. l10n_util::GetStringUTF16(IDS_NTP_ARTICLE_SUGGESTIONS_SECTION_EMPTY));
  63. }
  64. bool JsonToCategories(const base::Value& parsed,
  65. FetchedCategoriesVector* categories,
  66. const base::Time& fetch_time) {
  67. const base::Value::Dict* top_dict = parsed.GetIfDict();
  68. if (!top_dict) {
  69. return false;
  70. }
  71. const base::Value::List* categories_value = top_dict->FindList("categories");
  72. if (!categories_value) {
  73. return false;
  74. }
  75. for (const base::Value& v : *categories_value) {
  76. if (!v.is_dict())
  77. return false;
  78. const base::Value::Dict& d = v.GetDict();
  79. const std::string* utf8_title = d.FindString("localizedTitle");
  80. int remote_category_id = d.FindInt("id").value_or(-1);
  81. if (!utf8_title || remote_category_id <= 0) {
  82. return false;
  83. }
  84. RemoteSuggestion::PtrVector suggestions;
  85. const base::Value::List* suggestions_list = d.FindList("suggestions");
  86. // Absence of a list of suggestions is treated as an empty list, which
  87. // is permissible.
  88. if (suggestions_list &&
  89. !AddSuggestionsFromListValue(remote_category_id, *suggestions_list,
  90. &suggestions, fetch_time)) {
  91. return false;
  92. }
  93. Category category = Category::FromRemoteCategory(remote_category_id);
  94. if (category.IsKnownCategory(KnownCategories::ARTICLES)) {
  95. categories->push_back(FetchedCategory(
  96. category, BuildArticleCategoryInfo(base::UTF8ToUTF16(*utf8_title))));
  97. } else {
  98. // TODO(tschumann): Right now, the backend does not yet populate this
  99. // field. Make it mandatory once the backends provide it.
  100. bool allow_fetching_more_results =
  101. d.FindBool("allowFetchingMoreResults").value_or(false);
  102. categories->push_back(FetchedCategory(
  103. category, BuildRemoteCategoryInfo(base::UTF8ToUTF16(*utf8_title),
  104. allow_fetching_more_results)));
  105. }
  106. categories->back().suggestions = std::move(suggestions);
  107. }
  108. return true;
  109. }
  110. } // namespace ntp_snippets