Skip to content

Commit

Permalink
Merge pull request #7111 from brave/brave-today-publisher
Browse files Browse the repository at this point in the history
  • Loading branch information
petemill authored Dec 1, 2020
2 parents 8a51edd + 098e065 commit 9e1df50
Show file tree
Hide file tree
Showing 18 changed files with 141 additions and 77 deletions.
1 change: 1 addition & 0 deletions browser/brave_profile_prefs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {

// Brave Today
registry->RegisterDictionaryPref(kBraveTodaySources);
registry->RegisterBooleanPref(kBraveTodayIntroDismissed, false);

// Brave Wallet
#if BUILDFLAG(BRAVE_WALLET_ENABLED)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ base::DictionaryValue GetPreferencesDictionary(PrefService* prefs) {
pref_data.SetBoolean(
"isBrandedWallpaperNotificationDismissed",
prefs->GetBoolean(kBrandedWallpaperNotificationDismissed));
pref_data.SetBoolean(
"isBraveTodayIntroDismissed",
prefs->GetBoolean(kBraveTodayIntroDismissed));
pref_data.SetBoolean(
"showBinance",
prefs->GetBoolean(kNewTabPageShowBinance));
Expand Down Expand Up @@ -449,6 +452,8 @@ void BraveNewTabMessageHandler::HandleSaveNewTabPagePref(
settingsKey = kNewTabPageShowStats;
} else if (settingsKeyInput == "showToday") {
settingsKey = kNewTabPageShowToday;
} else if (settingsKeyInput == "isBraveTodayIntroDismissed") {
settingsKey = kBraveTodayIntroDismissed;
} else if (settingsKeyInput == "showRewards") {
settingsKey = kNewTabPageShowRewards;
} else if (settingsKeyInput == "isBrandedWallpaperNotificationDismissed") {
Expand Down
1 change: 1 addition & 0 deletions common/pref_names.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const char kNewTabPageShowGemini[] = "brave.new_tab_page.show_gemini";
const char kNewTabPageShowTogether[] = "brave.new_tab_page.show_together";
const char kNewTabPageShowsOptions[] = "brave.new_tab_page.shows_options";
const char kBraveTodaySources[] = "brave.today.sources";
const char kBraveTodayIntroDismissed[] = "brave.today.intro_dismissed";
const char kBraveEnabledMediaRouter[] = "brave.enable_media_router";
const char kBinanceAccessToken[] = "brave.binance.access_token";
const char kBinanceRefreshToken[] = "brave.binance.refresh_token";
Expand Down
1 change: 1 addition & 0 deletions common/pref_names.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ extern const char kNewTabPageShowGemini[];
extern const char kNewTabPageShowTogether[];
extern const char kNewTabPageShowsOptions[];
extern const char kBraveTodaySources[];
extern const char kBraveTodayIntroDismissed[];
extern const char kBraveEnabledMediaRouter[];
extern const char kAlwaysShowBookmarkBarOnNTP[];
extern const char kAutocompleteEnabled[];
Expand Down
3 changes: 1 addition & 2 deletions components/brave_new_tab_ui/actions/new_tab_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { action } from 'typesafe-actions'

// Constants
import { types } from '../constants/new_tab_types'
import { Preferences } from '../api/preferences'
import { Stats } from '../api/stats'
import { PrivateTabData } from '../api/privateTabData'
import { TorTabData } from '../api/torTabData'
Expand All @@ -32,7 +31,7 @@ export const dismissBrandedWallpaperNotification = (isUserAction: boolean) =>
isUserAction
})

export const preferencesUpdated = (preferences: Preferences) =>
export const preferencesUpdated = (preferences: NewTab.Preferences) =>
action(types.NEW_TAB_PREFERENCES_UPDATED, preferences)

export const clockWidgetUpdated = (showClockWidget: boolean,
Expand Down
2 changes: 1 addition & 1 deletion components/brave_new_tab_ui/api/initialData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as torTabDataAPI from './torTabData'
import * as brandedWallpaper from './brandedWallpaper'

export type InitialData = {
preferences: preferencesAPI.Preferences
preferences: NewTab.Preferences
stats: statsAPI.Stats
privateTabData: privateTabDataAPI.PrivateTabData
torTabData: torTabDataAPI.TorTabData
Expand Down
22 changes: 7 additions & 15 deletions components/brave_new_tab_ui/api/preferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,10 @@
// especially string keys.
//

export type Preferences = {
showBackgroundImage: boolean
brandedWallpaperOptIn: boolean
showStats: boolean
showToday: boolean
showClock: boolean
clockFormat: string
showTopSites: boolean
showRewards: boolean
isBrandedWallpaperNotificationDismissed: boolean
}

type PreferencesUpdatedHandler = (prefData: Preferences) => void
type PreferencesUpdatedHandler = (prefData: NewTab.Preferences) => void

export function getPreferences (): Promise<Preferences> {
return window.cr.sendWithPromise<Preferences>('getNewTabPagePreferences')
export function getPreferences (): Promise<NewTab.Preferences> {
return window.cr.sendWithPromise<NewTab.Preferences>('getNewTabPagePreferences')
}

function sendSavePref (key: string, value: any) {
Expand Down Expand Up @@ -84,6 +72,10 @@ export function saveShowCryptoDotCom (value: boolean): void {
sendSavePref('showCryptoDotCom', value)
}

export function saveIsBraveTodayIntroDismissed (value: boolean): void {
sendSavePref('isBraveTodayIntroDismissed', value)
}

export function addChangeListener (listener: PreferencesUpdatedHandler): void {
window.cr.addWebUIListener('preferences-changed', listener)
}
4 changes: 2 additions & 2 deletions components/brave_new_tab_ui/apiEventsToStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import * as privateTabDataAPI from './api/privateTabData'
import * as torTabDataAPI from './api/torTabData'
import { getInitialData, getRewardsInitialData, getRewardsPreInitialData } from './api/initialData'

async function updatePreferences (prefData: preferencesAPI.Preferences) {
async function updatePreferences (prefData: NewTab.Preferences) {
getActions().preferencesUpdated(prefData)
}

Expand All @@ -27,7 +27,7 @@ async function updateTorTabData (data: torTabDataAPI.TorTabData) {
getActions().torTabDataUpdated(data)
}

function onRewardsToggled (prefData: preferencesAPI.Preferences): void {
function onRewardsToggled (prefData: NewTab.Preferences): void {
if (prefData.showRewards) {
rewardsInitData()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,38 @@

import * as React from 'react'
import { getLocale } from '../../../../../common/locale'
import VisibilityTimer from '../../../../helpers/visibilityTimer'
import * as Card from '../cardIntro'
import BraveTodayLogo from '../braveTodayLogo.svg'
import BraveTodayLogoUrl from '../braveTodayLogo.svg'

class HeaderBlock extends React.PureComponent<{}, {}> {
render () {
return (
<Card.Intro>
<Card.Image src={BraveTodayLogo} />
<Card.Heading>{getLocale('braveTodayIntroTitle')}</Card.Heading>
<Card.Text>{getLocale('braveTodayIntroDescription')}</Card.Text>
</Card.Intro>
)
}
const timeToHideMs = 4000

type Props = {
onRead: () => void
}

export default HeaderBlock
export default function IntroCard (props: Props) {
const introElementRef = React.useRef(null)

// Only mark as 'read' when it's been in the viewport for a
// specific amount of time, and the tab is active.
React.useEffect(() => {
const element = introElementRef.current
if (!element) {
return
}
const observer = new VisibilityTimer(props.onRead, timeToHideMs, element)
observer.startTracking()
return () => {
observer.stopTracking()
}
}, [introElementRef.current, props.onRead])

return (
<Card.Intro innerRef={introElementRef}>
<Card.Image src={BraveTodayLogoUrl} />
<Card.Heading>{getLocale('braveTodayIntroTitle')}</Card.Heading>
<Card.Text>{getLocale('braveTodayIntroDescription')}</Card.Text>
</Card.Intro>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const Content = React.lazy(() => import('./content'))

type State = {
hasInteractionStarted: boolean
isIntroCardVisible: boolean
}

export type OnReadFeedItem = (args: ReadFeedItemPayload) => any
Expand All @@ -20,6 +21,7 @@ export type OnSetPublisherPref = (publisherId: string, enabled: boolean) => any
export type Props = {
isFetching: boolean
isUpdateAvailable: boolean
isIntroDismissed: boolean
feed?: BraveToday.Feed
publishers?: BraveToday.Publishers
articleToScrollTo?: BraveToday.FeedItem
Expand All @@ -31,14 +33,20 @@ export type Props = {
onCustomizeBraveToday: () => any
onRefresh: () => any
onCheckForUpdate: () => any
onReadCardIntro: () => any
}

class BraveToday extends React.PureComponent<Props, State> {
braveTodayHitsViewportObserver: IntersectionObserver
scrollTriggerToFocusBraveToday: any // React.RefObject<any>

state = {
hasInteractionStarted: false
constructor (props: Props) {
super(props)
// Don't remove Intro Card until the page refreshes
this.state = {
hasInteractionStarted: false,
isIntroCardVisible: !props.isIntroDismissed
}
}

componentDidMount () {
Expand Down Expand Up @@ -70,9 +78,9 @@ class BraveToday extends React.PureComponent<Props, State> {
ref={scrollTrigger => (this.scrollTriggerToFocusBraveToday = scrollTrigger)}
style={{ position: 'sticky', top: '100px' }}
/>

<CardIntro />

{ !this.props.isIntroDismissed &&
<CardIntro onRead={this.props.onReadCardIntro} />
}
{ shouldDisplayContent &&
<React.Suspense fallback={(<CardLoading />)}>
<Content {...this.props} />
Expand Down
58 changes: 31 additions & 27 deletions components/brave_new_tab_ui/containers/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,36 +27,40 @@ interface Props {
braveTodayData: BraveTodayState
}

class DefaultPage extends React.Component<Props, {}> {
render () {
const { newTabData, braveTodayData, gridSitesData, actions } = this.props
function dismissBraveTodayIntroCard () {
PreferencesAPI.saveIsBraveTodayIntroDismissed(true)
}

// don't render if user prefers an empty page
if (this.props.newTabData.showEmptyPage && !this.props.newTabData.isIncognito) {
return <div />
}
function DefaultPage (props: Props) {
const { newTabData, braveTodayData, gridSitesData, actions } = props

return this.props.newTabData.isIncognito
? <NewPrivateTabPage newTabData={newTabData} actions={actions} />
: (
<NewTabPage
newTabData={newTabData}
todayData={braveTodayData}
gridSitesData={gridSitesData}
actions={actions}
saveShowBackgroundImage={PreferencesAPI.saveShowBackgroundImage}
saveShowStats={PreferencesAPI.saveShowStats}
saveShowToday={PreferencesAPI.saveShowToday}
saveShowRewards={PreferencesAPI.saveShowRewards}
saveShowTogether={PreferencesAPI.saveShowTogether}
saveShowBinance={PreferencesAPI.saveShowBinance}
saveShowGemini={PreferencesAPI.saveShowGemini}
saveShowBitcoinDotCom={PreferencesAPI.saveShowBitcoinDotCom}
saveShowCryptoDotCom={PreferencesAPI.saveShowCryptoDotCom}
saveBrandedWallpaperOptIn={PreferencesAPI.saveBrandedWallpaperOptIn}
/>
)
// don't render if user prefers an empty page
if (props.newTabData.showEmptyPage && !props.newTabData.isIncognito) {
return <div />
}

return props.newTabData.isIncognito
? <NewPrivateTabPage newTabData={newTabData} actions={actions} />
: (
<NewTabPage
newTabData={newTabData}
todayData={braveTodayData}
gridSitesData={gridSitesData}
actions={actions}
saveShowBackgroundImage={PreferencesAPI.saveShowBackgroundImage}
saveShowStats={PreferencesAPI.saveShowStats}
saveShowToday={PreferencesAPI.saveShowToday}
saveShowRewards={PreferencesAPI.saveShowRewards}
saveShowTogether={PreferencesAPI.saveShowTogether}
saveShowBinance={PreferencesAPI.saveShowBinance}
saveShowAddCard={PreferencesAPI.saveShowAddCard}
saveShowGemini={PreferencesAPI.saveShowGemini}
saveShowBitcoinDotCom={PreferencesAPI.saveShowBitcoinDotCom}
saveShowCryptoDotCom={PreferencesAPI.saveShowCryptoDotCom}
saveBrandedWallpaperOptIn={PreferencesAPI.saveBrandedWallpaperOptIn}
onReadBraveTodayIntroCard={dismissBraveTodayIntroCard}
/>
)
}

const mapStateToProps = (state: ApplicationState): Partial<Props> => ({
Expand Down
3 changes: 3 additions & 0 deletions components/brave_new_tab_ui/containers/newTab/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ interface Props {
saveShowBitcoinDotCom: (value: boolean) => void
saveShowCryptoDotCom: (value: boolean) => void
saveBrandedWallpaperOptIn: (value: boolean) => void
onReadBraveTodayIntroCard: () => any
}

interface State {
Expand Down Expand Up @@ -1108,6 +1109,7 @@ class NewTabPage extends React.Component<Props, State> {
publishers={this.props.todayData.publishers}
isFetching={this.props.todayData.isFetching === true}
isUpdateAvailable={this.props.todayData.isUpdateAvailable}
isIntroDismissed={this.props.newTabData.isBraveTodayIntroDismissed}
onRefresh={this.props.actions.today.refresh}
onAnotherPageNeeded={this.props.actions.today.anotherPageNeeded}
onInteracting={this.onBraveTodayInteracting}
Expand All @@ -1116,6 +1118,7 @@ class NewTabPage extends React.Component<Props, State> {
onReadFeedItem={this.props.actions.today.readFeedItem}
onSetPublisherPref={this.props.actions.today.setPublisherPref}
onCheckForUpdate={this.props.actions.today.checkForUpdate}
onReadCardIntro={this.props.onReadBraveTodayIntroCard}
/>
}
<Settings
Expand Down
25 changes: 22 additions & 3 deletions components/brave_new_tab_ui/helpers/visibilityTimer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,40 @@
// you can obtain one at http://mozilla.org/MPL/2.0/.

/**
* Waits until the viewport has been in view for a certain number of seconds
* Waits until the viewport (and an optional element) has been in view for a certain number of seconds
* continuously before calling a provided function.
*/
export default class VisibilityTimer {
private element?: HTMLElement
private timerId?: number = undefined
private isIntersecting: boolean
private viewTimeMs: number = 0
private onTimerExpired: Function
private intersectionObserver?: IntersectionObserver

constructor (onTimerExpired: Function, viewTimeMs: number) {
constructor (onTimerExpired: Function, viewTimeMs: number, elementToObserve?: HTMLElement) {
this.element = elementToObserve
this.onTimerExpired = onTimerExpired
this.viewTimeMs = viewTimeMs
this.isIntersecting = elementToObserve ? false : true
if (elementToObserve) {
this.intersectionObserver = new IntersectionObserver(entries => {
this.isIntersecting = entries.some(entry => entry.isIntersecting)
this.handleVisibility()
}, {
threshold: 1.0
})
}
}

startTracking () {
document.addEventListener(
'visiblitychange',
this.handleVisibility
)
if (this.intersectionObserver && this.element) {
this.intersectionObserver.observe(this.element)
}
this.handleVisibility()
}

Expand All @@ -32,10 +48,13 @@ export default class VisibilityTimer {
'visiblitychange',
this.handleVisibility
)
if (this.intersectionObserver && this.element) {
this.intersectionObserver.unobserve(this.element)
}
}

private handleVisibility = () => {
if (document.visibilityState === 'visible') {
if (document.visibilityState === 'visible' && this.isIntersecting) {
if (!this.timerId) {
// Start the timer again
this.startWaiting()
Expand Down
2 changes: 1 addition & 1 deletion components/brave_new_tab_ui/reducers/new_tab_reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export const newTabReducer: Reducer<NewTab.State | undefined> = (state: NewTab.S
break

case types.NEW_TAB_PREFERENCES_UPDATED:
const preferences = payload as preferencesAPI.Preferences
const preferences = payload as NewTab.Preferences
const newState = {
...state,
...preferences
Expand Down
1 change: 1 addition & 0 deletions components/brave_new_tab_ui/storage/new_tab_storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const defaultState: NewTab.State = {
showCryptoDotCom: false,
brandedWallpaperOptIn: false,
isBrandedWallpaperNotificationDismissed: true,
isBraveTodayIntroDismissed: false,
showEmptyPage: false,
togetherSupported: false,
geminiSupported: false,
Expand Down
Loading

0 comments on commit 9e1df50

Please sign in to comment.