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 1.62] [Brave News]: Improve FeedV2 Buttons (#21347) #21376

Merged
merged 2 commits into from
Dec 20, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import Flex from '$web-common/Flex'
import { getLocale } from '$web-common/locale'
import Button from '@brave/leo/react/button'
import { spacing } from '@brave/leo/tokens/css'
import Icon from '@brave/leo/react/icon'
import { radius, spacing } from '@brave/leo/tokens/css'
import * as React from 'react'
import styled from 'styled-components'
import Feed from '../../../../brave_news/browser/resources/Feed'
Expand Down Expand Up @@ -38,8 +39,9 @@ const ButtonsContainer = styled.div`
visibility: hidden;
position: fixed;
bottom: ${spacing.xl};
right: ${spacing.xl};
bottom: ${spacing['5Xl']};
right: ${spacing['5Xl']};
border-radius: ${radius.m};
opacity: calc((var(--ntp-scroll-percent) - 0.5) / 0.5);
Expand All @@ -49,6 +51,15 @@ const ButtonsContainer = styled.div`
display: flex;
gap: ${spacing.m};
padding: ${spacing.m};
background: var(--bn-glass-container);
`

const NewsButton = styled(Button)`
--leo-button-color: var(--bn-glass-50);
--leo-button-radius: ${radius.s};
--leo-button-padding: ${spacing.m};
`

const LoadNewContentButton = styled(Button)`
Expand Down Expand Up @@ -102,12 +113,12 @@ export default function FeedV2() {
</Flex>

<ButtonsContainer>
<Button kind='outline' onClick={() => setCustomizePage('news')}>
{getLocale('braveNewsCustomizeFeed')}
</Button>
<Button isLoading={!feedV2} kind='outline' onClick={() => {
<NewsButton fab kind='outline' onClick={() => setCustomizePage('news')} title={getLocale('braveNewsCustomizeFeed')}>
<Icon name="settings" />
</NewsButton>
<NewsButton fab isLoading={!feedV2} kind='outline' title={getLocale('braveNewsRefreshFeed')} onClick={() => {
refreshFeedV2()
}}>{getLocale('braveNewsRefreshFeed')}</Button>
}}><Icon name="refresh" /></NewsButton>
</ButtonsContainer>
</Root>
}
2 changes: 1 addition & 1 deletion components/brave_news/browser/feed_v2_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ std::vector<mojom::FeedItemV2Ptr> GenerateTopicBlock(
result->type = mojom::ClusterType::TOPIC;

auto& [topic, articles] = topics.front();
result->id = topic.title;
result->id = topic.claude_title_short;

uint64_t max_articles = features::kBraveNewsMaxBlockCards.Get();
for (const auto& article : articles) {
Expand Down
3 changes: 3 additions & 0 deletions components/brave_news/browser/resources/FeedNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ const Container = styled(Card)`
max-height: calc(100vh - ${spacing.xl} * 2);
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: var(--bn-glass-10) var(--bn-glass-10);
`

const Heading = styled.h3`
Expand Down
3 changes: 2 additions & 1 deletion components/brave_news/browser/resources/feed/Ad.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const BatAdLabel = styled.a`
`

const CtaButton = styled(Button)`
--leo-button-color: var(--bn-glass-container);
align-self: flex-start;
`

Expand Down Expand Up @@ -121,6 +122,6 @@ export default function Advert(props: Props) {
{advert.title}
</SecureLink>
</Title>
<CtaButton kind='plain-faint'>{advert.ctaText}</CtaButton>
<CtaButton kind='filled'>{advert.ctaText}</CtaButton>
</Container>
}
8 changes: 6 additions & 2 deletions components/brave_news/browser/resources/feed/Article.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,20 @@ interface Props {
const Container = styled(Card)`
display: flex;
flex-direction: column;
gap: ${spacing.s};
padding-top: ${spacing.l};
`

export default function Article({ info, hideChannel }: Props) {
const { url: imageUrl, setElementRef } = useLazyUnpaddedImageUrl(info.data.image.paddedImageUrl?.url ?? info.data.image.imageUrl?.url, { useCache: true })
const { url: imageUrl, setElementRef } = useLazyUnpaddedImageUrl(info.data.image.paddedImageUrl?.url ?? info.data.image.imageUrl?.url, {
useCache: true,
rootMargin: '500px 0px'
})
const url = info.data.url.url;

return <Container ref={setElementRef} onClick={braveNewsCardClickHandler(url)}>
<ArticleMetaRow article={info.data} hideChannel={hideChannel} />
<Flex direction='row' gap={spacing.m} justify='space-between' align='start'>
<Flex direction='row' gap={spacing.xl} justify='space-between' align='start'>
<Title>
<BraveNewsLink href={url}>{info.data.title}</BraveNewsLink>
</Title>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const MetaInfoContainer = styled.h4`
const publisherDescription = (article: FeedItemMetadata) => {
if (article.publisherName) return article.publisherName
const url = new URL(article.url.url)
return url.origin
return url.hostname
}

export function MetaInfo(props: { article: FeedItemMetadata, hideChannel?: boolean }) {
Expand Down
26 changes: 19 additions & 7 deletions components/brave_news/browser/resources/feed/Cluster.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
// You can obtain one at https://mozilla.org/MPL/2.0/.
import * as React from 'react';
import { Cluster as Info, ClusterType } from 'gen/brave/components/brave_news/common/brave_news.mojom.m';
import Card from './Card';
import Card, { Title } from './Card';
import Article from './Article';
import styled from 'styled-components';
import { spacing } from '@brave/leo/tokens/css';
import { icon, radius, spacing } from '@brave/leo/tokens/css';
import { channelIcons } from '../shared/Icons';
import { MetaInfoContainer } from './ArticleMetaRow';
import { getTranslatedChannelName } from '../shared/channel';

interface Props {
Expand All @@ -19,18 +18,31 @@ interface Props {
const Container = styled(Card)`
display: flex;
flex-direction: column;
gap: ${spacing['2Xl']};
padding-top: ${spacing['2Xl']};
gap: ${spacing.l};
& > ${Title} {
--leo-icon-color: currentColor;
--leo-icon-size: ${icon.s};
gap: ${spacing.m};
align-items: center;
margin: ${spacing.m} 0;
}
& > ${Card} {
border-radius: ${radius.m};
}
`

export default function Cluster({ info }: Props) {
const groupName = info.type === ClusterType.CHANNEL
? getTranslatedChannelName(info.id)
: info.id
return <Container>
<MetaInfoContainer>
<Title>
{channelIcons[info.id] ?? channelIcons.default} {groupName}
</MetaInfoContainer>
</Title>
{info.articles.map((a, i) => {
const info: any = a.article || a.hero
return <Article key={i} info={info} hideChannel={info.type === ClusterType.CHANNEL} />
Expand Down
18 changes: 15 additions & 3 deletions components/brave_news/browser/resources/feed/Hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,34 @@ import * as React from 'react';
import { useLazyUnpaddedImageUrl } from '../shared/useUnpaddedImageUrl';
import ArticleMetaRow from './ArticleMetaRow';
import Card, { BraveNewsLink, LargeImage, Title, braveNewsCardClickHandler } from './Card';
import styled from 'styled-components';
import { spacing } from '@brave/leo/tokens/css';

interface Props {
info: Info
}

const Container = styled(Card)`
display: flex;
flex-direction: column;
gap: ${spacing.s};
& > ${LargeImage} {
margin-bottom: ${spacing.l};
}
`

export default function HeroArticle({ info }: Props) {
const { url, setElementRef } = useLazyUnpaddedImageUrl(info.data.image.paddedImageUrl?.url ?? info.data.image.imageUrl?.url, {
useCache: true,
rootElement: document.body,
rootMargin: '0px 0px 200px 0px'
rootMargin: '500px 0px'
})
return <Card onClick={braveNewsCardClickHandler(info.data.url.url)} ref={setElementRef}>
return <Container onClick={braveNewsCardClickHandler(info.data.url.url)} ref={setElementRef}>
<LargeImage src={url} />
<ArticleMetaRow article={info.data} />
<Title>
<BraveNewsLink href={info.data.url.url}>{info.data.title}</BraveNewsLink>
</Title>
</Card>
</Container>
}
16 changes: 8 additions & 8 deletions components/brave_news/browser/resources/feed/NoArticles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,30 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
import { getLocale } from '$web-common/locale';
import Button from '@brave/leo/react/button';
import { spacing } from '@brave/leo/tokens/css';
import * as React from 'react';
import styled from 'styled-components';
import Flex from '../../../../common/Flex';
import { useBraveNews } from '../shared/Context';
import Flex from '$web-common/Flex';
import { Title } from './Card';

const Container = styled(Flex)`
position: sticky;
top: ${spacing.xl};
text-align: center;
padding: ${spacing.xl};
padding: ${spacing['3Xl']};
gap: ${spacing.m};
color: var(--bn-glass-70);
& > svg {
margin-bottom: ${spacing['2Xl']};
fill: none;
}
& > leo-button {
flex: 0;
}
`

export default function NoArticles() {
const { refreshFeedV2 } = useBraveNews()
return <Container align='center' direction='column' justify='center' gap={spacing.m}>
<svg xmlns="http://www.w3.org/2000/svg" width="99" height="88" viewBox="0 0 99 88" fill="none">
<path d="M9.5 0.5C4.80558 0.5 1 4.30558 1 9V79C1 83.6944 4.80558 87.5 9.5 87.5H89.5C94.1944 87.5 98 83.6944 98 79V9C98 4.30558 94.1944 0.5 89.5 0.5H9.5Z" stroke="url(#paint0_linear_5051_10730)" />
Expand Down Expand Up @@ -60,6 +61,5 @@ export default function NoArticles() {
<div>
{getLocale('braveNewsNoArticlesMessage')}
</div>
<Button kind='filled' onClick={refreshFeedV2}>{getLocale('braveNewsRefreshFeed')}</Button>
</Container>
}
4 changes: 2 additions & 2 deletions components/brave_news/browser/resources/shared/Context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export const configurationCache = new ConfigurationCachingWrapper()

export function BraveNewsContextProvider(props: { children: React.ReactNode }) {
const [locale, setLocale] = useState('')
const [configuration, setConfiguration] = useState<Configuration>(configurationCache.value)

// Note: It's okay to fetch the FeedV2 even when the feature isn't enabled
// because the controller will just return an empty feed.
Expand All @@ -81,13 +82,12 @@ export function BraveNewsContextProvider(props: { children: React.ReactNode }) {
feedView,
setFeedView,
refresh: refreshFeedV2
} = useFeedV2()
} = useFeedV2(configuration.isOptedIn && configuration.showOnNTP)

const [customizePage, setCustomizePage] = useState<NewsPage>(null)
const [channels, setChannels] = useState<Channels>({})
const [publishers, setPublishers] = useState<Publishers>({})
const [suggestedPublisherIds, setSuggestedPublisherIds] = useState<string[]>([])
const [configuration, setConfiguration] = useState<Configuration>(configurationCache.value)

// Get the default locale on load.
useEffect(() => {
Expand Down
45 changes: 36 additions & 9 deletions components/brave_news/browser/resources/shared/useFeedV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,30 @@ const feedTypeToFeedView = (type: FeedV2Type | undefined): FeedView => {
const FEED_KEY = 'feedV2'
const localCache: { [feedView: string]: FeedV2 } = {}
const saveFeed = (feed?: FeedV2) => {
if (!feed) return
if (!feed || !feed.items.length) {
sessionStorage.removeItem(FEED_KEY)
localStorage.removeItem(FEED_KEY)
return
}

localCache[feedTypeToFeedView(feed.type)] = feed

// Note: We have to provide a replacer, because BigInt can't be serialized to JSON
const data = JSON.stringify(feed, (_, value) => typeof value === "bigint"
? value.toString()
: value);
sessionStorage.setItem(FEED_KEY, data)
localStorage.setItem(FEED_KEY, data)

try {
sessionStorage.setItem(FEED_KEY, data)
} catch (err) {
console.log(err)
}

try {
localStorage.setItem(FEED_KEY, data)
} catch (err) {
console.log(err)
}
}

const maybeLoadFeed = (view?: FeedView) => {
Expand Down Expand Up @@ -104,18 +118,32 @@ addFeedListener(latestHash => {
}
})

export const useFeedV2 = () => {
export const useFeedV2 = (enabled: boolean) => {
const [feedV2, setFeedV2] = useState<FeedV2 | undefined>(maybeLoadFeed())
const [feedView, setFeedView] = useState<FeedView>(feedTypeToFeedView(feedV2?.type))
const [hash, setHash] = useState<string>()

// Add a listener for the latest hash.
// Add a listener for the latest hash if Brave News is enabled. Note: We need
// to re-add the listener when the enabled state changes because the backing
// FeedV2Builder is created/destroyed.
useEffect(() => {
if (!enabled) return

let cancelled = false
// Note: A new feed listener will be notified with the latest hash.
addFeedListener(setHash)
}, [])
addFeedListener(newHash => {
if (cancelled) return
setHash(newHash)
})

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

useEffect(() => {
if (!enabled) return

setFeedV2(undefined)

const cachedFeed = maybeLoadFeed(feedView)
if (cachedFeed) {
setFeedV2(cachedFeed)
Expand All @@ -128,7 +156,7 @@ export const useFeedV2 = () => {
setFeedV2(feed)
})
return () => { cancelled = true }
}, [feedView])
}, [feedView, enabled])

const refresh = useCallback(() => {
// Set the feed to undefined - this will trigger the loading indicator.
Expand All @@ -140,7 +168,6 @@ export const useFeedV2 = () => {
// Updates are available if we've been told the latest hash, we have a feed
// and the hashes don't match.
const updatesAvailable = !!(hash && feedV2 && hash !== feedV2.sourceHash)
console.log("Latest hash: ", hash, "Current hash:", feedV2?.sourceHash)
return {
feedV2,
feedView,
Expand Down