Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Uplift] [Brave News] Fix direct feeds & scrolling bug #15820

Merged
merged 3 commits into from
Nov 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions browser/brave_news/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ source_set("brave_news") {
"//brave/browser/profiles:util",
"//brave/components/brave_today/browser",
"//brave/components/brave_today/common",
"//brave/components/brave_today/common:common",
"//brave/components/brave_today/common:mojom",
"//chrome/browser:browser",
"//chrome/browser/favicon",
"//chrome/browser/profiles",
"//chrome/browser/profiles:profile",
"//chrome/browser/profiles:profiles",
"//components/favicon/core",
"//components/keyed_service/content",
"//components/user_prefs",
"//content/public/browser",
Expand Down
10 changes: 8 additions & 2 deletions browser/brave_news/brave_news_controller_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@
#include "brave/browser/brave_ads/ads_service_factory.h"
#include "brave/browser/profiles/profile_util.h"
#include "brave/components/brave_today/browser/brave_news_controller.h"
#include "chrome/browser/favicon/favicon_service_factory.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/profiles/incognito_helpers.h"
#include "chrome/browser/profiles/profile.h"
#include "components/favicon/core/favicon_service.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/keyed_service/core/service_access_type.h"
#include "content/public/browser/browser_context.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"

Expand Down Expand Up @@ -49,6 +52,7 @@ BraveNewsControllerFactory::BraveNewsControllerFactory()
"BraveNewsControllerFactory",
BrowserContextDependencyManager::GetInstance()) {
DependsOn(brave_ads::AdsServiceFactory::GetInstance());
DependsOn(FaviconServiceFactory::GetInstance());
DependsOn(HistoryServiceFactory::GetInstance());
}

Expand All @@ -63,11 +67,13 @@ KeyedService* BraveNewsControllerFactory::BuildServiceInstanceFor(
if (!profile) {
return nullptr;
}
auto* favicon_service = FaviconServiceFactory::GetForProfile(
profile, ServiceAccessType::EXPLICIT_ACCESS);
auto* ads_service = brave_ads::AdsServiceFactory::GetForProfile(profile);
auto* history_service = HistoryServiceFactory::GetForProfile(
profile, ServiceAccessType::EXPLICIT_ACCESS);
return new BraveNewsController(profile->GetPrefs(), ads_service,
history_service,
return new BraveNewsController(profile->GetPrefs(), favicon_service,
ads_service, history_service,
profile->GetURLLoaderFactory());
}

Expand Down
3 changes: 2 additions & 1 deletion components/brave_new_tab_ui/api/brave_news/news.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,5 @@ class BraveNewsApi {
}
}

export const api = new BraveNewsApi()
export const api = new BraveNewsApi();
(window as any).api = api
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ const Grid = styled.div`
width: 100%;
height: 100%;

overflow: auto;
overscroll-behavior: none;

display: grid;
grid-template-columns: 250px auto;
grid-template-rows: 64px 2px auto;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import styled from 'styled-components'
import { getLocale } from '$web-common/locale'
import Flex from '../../../Flex'
import { useChannelSubscribed, usePublisher, usePublisherFollowed } from './Context'
import { useLazyUnpaddedImageUrl } from '../useUnpaddedImageUrl'
import { useLazyFavicon } from '../useUnpaddedImageUrl'
import { getTranslatedChannelName } from './ChannelCard'

interface Props {
Expand Down Expand Up @@ -61,11 +61,10 @@ const ChannelNameText = styled.span`
font-weight: 600;
`

function FavIcon (props: { src?: string }) {
const { url, setElementRef } = useLazyUnpaddedImageUrl(props.src, {
function FavIcon (props: { publisherId: string }) {
const { url, setElementRef } = useLazyFavicon(props.publisherId, {
rootElement: document.getElementById('brave-news-configure'),
rootMargin: '0px 0px 100px 0px',
useCache: true
rootMargin: '200px 0px 200px 0px'
})
const [error, setError] = React.useState(false)
return <FavIconContainer ref={setElementRef}>
Expand All @@ -79,7 +78,7 @@ export function FeedListEntry (props: Props) {

return <Container direction="row" justify="space-between" align='center'>
<Flex align='center' gap={8}>
<FavIcon src={publisher.faviconUrl?.url} />
<FavIcon publisherId={props.publisherId} />
<Text>{publisher.publisherName}</Text>
</Flex>
<ToggleButton onClick={() => setFollowed(false)}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,16 @@ export default function useSearch (query: string) {
p.feedSource?.url?.toLocaleLowerCase().includes(lowerQuery)))
const results = { publishers, direct: [] as FeedSearchResultItem[] }
for (const result of directResults) {
const publisherMatch = publishers.find(p => p.feedSource.url === result.feedUrl.url)
if (publisherMatch && !publishers.some(p => p.publisherId === publisherMatch.publisherId)) {
results.publishers.push(publisherMatch)
} else {
// If we have any publisher with this feed url, we should prefer showing
// that to creating a new direct feed.
// Note: We should only add the publisher to the list of publisher matches
// if it isn't already matching. The canonical case here is
// news.com (redirects to cnet.com), so we should show the cnet.com feed.
const publisherMatch = context.sortedPublishers.find(p => p.feedSource.url === result.feedUrl.url)
if (!publisherMatch) {
results.direct.push(result)
} else if (!results.publishers.some(p => p.publisherId === publisherMatch.publisherId)) {
results.publishers.push(publisherMatch)
}
}
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import * as React from 'react'
import getBraveNewsController from '../../../api/brave_news'
import { useVisible, VisibleOptions } from '$web-common/useVisible'

interface Options {
rootElement?: HTMLElement | null
Expand All @@ -16,6 +17,46 @@ interface Options {
}

const cache: { [url: string]: string } = {}
function useFavicon (publisherId: string | undefined) {
const [url, setUrl] = React.useState<string>(cache[publisherId!])
React.useEffect(() => {
if (!publisherId) {
return
}

if (cache[publisherId]) {
setUrl(cache[publisherId])
return
}

let cancelled = false
getBraveNewsController().getFavIconData(publisherId).then(({ imageData }) => {
if (cancelled) return
if (!imageData) return

const blob = new Blob([new Uint8Array(imageData)], { type: 'image/*' })
const url = URL.createObjectURL(blob)
cache[publisherId] = url
setUrl(url)
})

return () => {
cancelled = true
}
}, [publisherId])

return url
}

export function useLazyFavicon (publisherId: string, options: VisibleOptions) {
const { visible, setElementRef } = useVisible(options)
const url = useFavicon(visible ? publisherId : undefined)
return {
url,
setElementRef
}
}

export function useUnpaddedImageUrl (paddedUrl: string | undefined, onLoaded?: () => any, useCache?: boolean) {
const [unpaddedUrl, setUnpaddedUrl] = React.useState('')

Expand Down Expand Up @@ -67,28 +108,7 @@ export function useUnpaddedImageUrl (paddedUrl: string | undefined, onLoaded?: (
}

export function useLazyUnpaddedImageUrl (paddedUrl: string | undefined, options: Options) {
const [visible, setVisible] = React.useState(false)
const [elementRef, setElementRef] = React.useState<HTMLElement | null>(null)

React.useEffect(() => {
if (!elementRef) return

const observer = new IntersectionObserver(([intersectionInfo]) => {
if (!intersectionInfo.isIntersecting) {
return
}
setVisible(true)
}, {
root: options.rootElement,
rootMargin: options.rootMargin,
threshold: options.threshold
})

observer.observe(elementRef)
return () => {
observer.disconnect()
}
}, [options.rootElement, elementRef])
const { visible, setElementRef } = useVisible(options)

return {
url: useUnpaddedImageUrl(visible ? paddedUrl : undefined, options.onLoaded, options.useCache) || cache[paddedUrl!],
Expand Down
2 changes: 2 additions & 0 deletions components/brave_today/browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ static_library("browser") {
"//brave/components/l10n/common",
"//brave/components/p3a_utils",
"//brave/components/time_period_storage",
"//components/favicon/core:core",
"//components/favicon_base:favicon_base",
"//components/feed:buildflags",
"//components/history/core/browser",
"//components/keyed_service/core",
Expand Down
58 changes: 58 additions & 0 deletions components/brave_today/browser/brave_news_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "brave/components/brave_today/browser/brave_news_controller.h"

#include <algorithm>
#include <cmath>
#include <memory>
#include <string>
Expand Down Expand Up @@ -37,6 +38,8 @@
#include "brave/components/brave_today/common/features.h"
#include "brave/components/brave_today/common/pref_names.h"
#include "brave/components/l10n/common/locale_util.h"
#include "components/favicon/core/favicon_service.h"
#include "components/favicon_base/favicon_types.h"
#include "components/history/core/browser/history_service.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/scoped_user_pref_update.h"
Expand All @@ -45,6 +48,11 @@
#include "third_party/abseil-cpp/absl/types/optional.h"

namespace brave_news {
namespace {
// The favicon size we desire. The favicons are rendered at 24x24 pixels but
// they look quite a bit nicer if we get a 48x48 pixel icon and downscale it.
constexpr uint32_t kDesiredFaviconSizePixels = 48;
} // namespace

bool IsPublisherEnabled(const mojom::Publisher* publisher) {
if (!publisher)
Expand Down Expand Up @@ -77,10 +85,12 @@ void BraveNewsController::RegisterProfilePrefs(PrefRegistrySimple* registry) {

BraveNewsController::BraveNewsController(
PrefService* prefs,
favicon::FaviconService* favicon_service,
brave_ads::AdsService* ads_service,
history::HistoryService* history_service,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: prefs_(prefs),
favicon_service_(favicon_service),
ads_service_(ads_service),
api_request_helper_(GetNetworkTrafficAnnotationTag(), url_loader_factory),
private_cdn_request_helper_(GetNetworkTrafficAnnotationTag(),
Expand Down Expand Up @@ -302,6 +312,54 @@ void BraveNewsController::GetImageData(const GURL& padded_image_url,
std::move(callback), is_padded));
}

void BraveNewsController::GetFavIconData(const std::string& publisher_id,
GetFavIconDataCallback callback) {
GetPublishers(base::BindOnce(
[](BraveNewsController* controller, const std::string& publisher_id,
GetFavIconDataCallback callback, Publishers publishers) {
// If the publisher doesn't exist, there's nothing we can do.
const auto& it = publishers.find(publisher_id);
if (it == publishers.end()) {
std::move(callback).Run(absl::nullopt);
return;
}

// If we have a FavIcon url, use that.
const auto& publisher = it->second;
if (publisher->favicon_url) {
controller->GetImageData(publisher->favicon_url.value(),
std::move(callback));
return;
}

auto source_url = publisher->site_url.is_valid()
? publisher->site_url
: publisher->feed_source;
favicon_base::IconTypeSet icon_types{
favicon_base::IconType::kFavicon,
favicon_base::IconType::kTouchIcon};
controller->favicon_service_->GetRawFaviconForPageURL(
source_url, icon_types, kDesiredFaviconSizePixels, true,
base::BindOnce(
[](GetFavIconDataCallback callback,
const favicon_base::FaviconRawBitmapResult& result) {
if (!result.is_valid()) {
std::move(callback).Run(absl::nullopt);
return;
}

auto bytes = result.bitmap_data;
std::vector<uint8_t> bytes_vec(
bytes->front_as<uint8_t>(),
bytes->front_as<uint8_t>() + bytes->size());
std::move(callback).Run(std::move(bytes_vec));
},
std::move(callback)),
&controller->task_tracker_);
},
base::Unretained(this), publisher_id, std::move(callback)));
}

void BraveNewsController::SetPublisherPref(const std::string& publisher_id,
mojom::UserEnabled new_status) {
VLOG(1) << "set publisher pref: " << new_status;
Expand Down
11 changes: 11 additions & 0 deletions components/brave_today/browser/brave_news_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
#ifndef BRAVE_COMPONENTS_BRAVE_TODAY_BROWSER_BRAVE_NEWS_CONTROLLER_H_
#define BRAVE_COMPONENTS_BRAVE_TODAY_BROWSER_BRAVE_NEWS_CONTROLLER_H_

#include <cstddef>
#include <memory>
#include <string>

#include "base/callback_forward.h"
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/task/cancelable_task_tracker.h"
#include "base/timer/timer.h"
#include "brave/components/api_request_helper/api_request_helper.h"
#include "brave/components/brave_private_cdn/private_cdn_request_helper.h"
Expand All @@ -37,6 +39,10 @@ namespace brave_ads {
class AdsService;
} // namespace brave_ads

namespace favicon {
class FaviconService;
}

namespace history {
class HistoryService;
} // namespace history
Expand All @@ -56,6 +62,7 @@ class BraveNewsController : public KeyedService,

BraveNewsController(
PrefService* prefs,
favicon::FaviconService* favicon_service,
brave_ads::AdsService* ads_service,
history::HistoryService* history_service,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
Expand Down Expand Up @@ -92,6 +99,8 @@ class BraveNewsController : public KeyedService,
void RemoveDirectFeed(const std::string& publisher_id) override;
void GetImageData(const GURL& padded_image_url,
GetImageDataCallback callback) override;
void GetFavIconData(const std::string& publisher_id,
GetFavIconDataCallback callback) override;
void SetPublisherPref(const std::string& publisher_id,
mojom::UserEnabled new_status) override;
void ClearPrefs() override;
Expand Down Expand Up @@ -122,6 +131,7 @@ class BraveNewsController : public KeyedService,
void Prefetch();

raw_ptr<PrefService> prefs_ = nullptr;
raw_ptr<favicon::FaviconService> favicon_service_ = nullptr;
raw_ptr<brave_ads::AdsService> ads_service_ = nullptr;
api_request_helper::APIRequestHelper api_request_helper_;
brave_private_cdn::PrivateCDNRequestHelper private_cdn_request_helper_;
Expand All @@ -137,6 +147,7 @@ class BraveNewsController : public KeyedService,
base::OneShotTimer timer_prefetch_;
base::RepeatingTimer timer_feed_update_;
base::RepeatingTimer timer_publishers_update_;
base::CancelableTaskTracker task_tracker_;

mojo::ReceiverSet<mojom::BraveNewsController> receivers_;
base::WeakPtrFactory<BraveNewsController> weak_ptr_factory_;
Expand Down
Loading