From 0dbaec423c36cb972bcedd02c2809d2f1641946d Mon Sep 17 00:00:00 2001 From: Mehmood Ahmad <31419912+mehmoodak@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:06:55 +0500 Subject: [PATCH 1/9] =?UTF-8?q?=F0=9F=94=A8=20REFACTOR:=20refactors=20reco?= =?UTF-8?q?mmended=20sites=20store=20to=20TS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reader/following/{main.jsx => main.tsx} | 12 +-- .../{index.jsx => index.tsx} | 40 +++++++- .../wpcom/read/recommendations/sites/index.js | 44 --------- .../wpcom/read/recommendations/sites/index.ts | 92 +++++++++++++++++++ .../state/reader/recommended-sites/actions.ts | 62 +++++++++++-- .../state/reader/recommended-sites/reducer.ts | 4 +- ...-reader-recommended-sites-paging-offset.js | 9 -- ...-reader-recommended-sites-paging-offset.ts | 9 ++ .../selectors/get-reader-recommended-sites.js | 9 -- .../selectors/get-reader-recommended-sites.ts | 9 ++ .../selectors/{index.js => index.ts} | 0 .../state/reader/recommended-sites/types.ts | 7 -- 12 files changed, 208 insertions(+), 89 deletions(-) rename client/reader/following/{main.jsx => main.tsx} (87%) rename client/reader/stream/reader-popular-sites-sidebar/{index.jsx => index.tsx} (59%) delete mode 100644 client/state/data-layer/wpcom/read/recommendations/sites/index.js create mode 100644 client/state/data-layer/wpcom/read/recommendations/sites/index.ts delete mode 100644 client/state/reader/recommended-sites/selectors/get-reader-recommended-sites-paging-offset.js create mode 100644 client/state/reader/recommended-sites/selectors/get-reader-recommended-sites-paging-offset.ts delete mode 100644 client/state/reader/recommended-sites/selectors/get-reader-recommended-sites.js create mode 100644 client/state/reader/recommended-sites/selectors/get-reader-recommended-sites.ts rename client/state/reader/recommended-sites/selectors/{index.js => index.ts} (100%) delete mode 100644 client/state/reader/recommended-sites/types.ts diff --git a/client/reader/following/main.jsx b/client/reader/following/main.tsx similarity index 87% rename from client/reader/following/main.jsx rename to client/reader/following/main.tsx index 24173d06752f7..c92217100b428 100644 --- a/client/reader/following/main.jsx +++ b/client/reader/following/main.tsx @@ -7,7 +7,7 @@ import NavigationHeader from 'calypso/components/navigation-header'; import withDimensions from 'calypso/lib/with-dimensions'; import ReaderOnboarding from 'calypso/reader/onboarding'; import SuggestionProvider from 'calypso/reader/search-stream/suggestion-provider'; -import Stream, { WIDE_DISPLAY_CUTOFF } from 'calypso/reader/stream'; +import ReaderStream, { WIDE_DISPLAY_CUTOFF } from 'calypso/reader/stream'; import ReaderListFollowedSites from 'calypso/reader/stream/reader-list-followed-sites'; import Recent from '../recent'; import { useFollowingView } from './view-preference'; @@ -15,18 +15,16 @@ import ViewToggle from './view-toggle'; import './style.scss'; function FollowingStream( { ...props } ) { - const { currentView, setView } = useFollowingView(); + const { currentView } = useFollowingView(); - const viewToggle = config.isEnabled( 'reader/recent-feed-overhaul' ) ? ( - - ) : null; + const viewToggle = config.isEnabled( 'reader/recent-feed-overhaul' ) ? : null; return ( <> { currentView === 'recent' && config.isEnabled( 'reader/recent-feed-overhaul' ) ? ( ) : ( - } @@ -42,7 +40,7 @@ function FollowingStream( { ...props } ) { { viewToggle } - + ) } diff --git a/client/reader/stream/reader-popular-sites-sidebar/index.jsx b/client/reader/stream/reader-popular-sites-sidebar/index.tsx similarity index 59% rename from client/reader/stream/reader-popular-sites-sidebar/index.jsx rename to client/reader/stream/reader-popular-sites-sidebar/index.tsx index 1e2e9c8c585b7..2338d345b507b 100644 --- a/client/reader/stream/reader-popular-sites-sidebar/index.jsx +++ b/client/reader/stream/reader-popular-sites-sidebar/index.tsx @@ -1,15 +1,46 @@ import ConnectedReaderSubscriptionListItem from 'calypso/blocks/reader-subscription-list-item/connected'; import '../style.scss'; -function unescape( str ) { +interface PopularSitesSidebarProps { + followSource: string; + items: PopularSiteItemProp[]; +} + +/** + * Represents a popular site item which is passed as a prop to the ReaderPopularSitesSidebar component. + */ +interface PopularSiteItemProp { + blogId: number; + feed_ID: number; + feed_URL: string; + site_name: string; + site_description: string; + site_icon: string; + url: string; +} + +/** + * Represents a popular site which is displayed in the ReaderPopularSitesSidebar component. + */ +interface ReaderPopularSite { + blog_ID: number; + description: string; + feed_ID: number; + name: string; + site_icon: string; + URL: string; +} + +function unescape( str: string ): string { return str.replace( /&#(\d+);/g, ( match, entity ) => String.fromCharCode( entity ) ); } // create function to transform item into a site object -const getSiteFromItem = ( item ) => { +const getSiteFromItem = ( item: PopularSiteItemProp ): ReaderPopularSite | null => { if ( item.site_name === undefined || item.site_description === undefined ) { return null; } + return { feed_ID: item.feed_ID, blog_ID: item.blogId, @@ -17,12 +48,11 @@ const getSiteFromItem = ( item ) => { name: unescape( item?.site_name ), site_icon: item.site_icon ?? null, description: unescape( item?.site_description ), - last_updated: 0, - unseen_count: 0, }; }; -const ReaderPopularSitesSidebar = ( { items, followSource } ) => { +const ReaderPopularSitesSidebar = ( props: PopularSitesSidebarProps ) => { + const { followSource, items } = props; const sites = items .map( ( item ) => getSiteFromItem( item ) ) .filter( ( site ) => site !== null ); diff --git a/client/state/data-layer/wpcom/read/recommendations/sites/index.js b/client/state/data-layer/wpcom/read/recommendations/sites/index.js deleted file mode 100644 index 9f81f5b184968..0000000000000 --- a/client/state/data-layer/wpcom/read/recommendations/sites/index.js +++ /dev/null @@ -1,44 +0,0 @@ -import { decodeEntities } from 'calypso/lib/formatting'; -import { registerHandlers } from 'calypso/state/data-layer/handler-registry'; -import { http } from 'calypso/state/data-layer/wpcom-http/actions'; -import { dispatchRequest } from 'calypso/state/data-layer/wpcom-http/utils'; -import { READER_RECOMMENDED_SITES_REQUEST } from 'calypso/state/reader/action-types'; -import { receiveRecommendedSites } from 'calypso/state/reader/recommended-sites/actions'; - -const noop = () => {}; - -export const requestRecommendedSites = ( action ) => { - const { seed = 1, number = 10, offset = 0 } = action.payload; - return http( { - method: 'GET', - path: '/read/recommendations/sites', - query: { number, offset, seed, posts_per_site: 0 }, - apiVersion: '1.2', - onSuccess: action, - onFailure: action, - } ); -}; - -export const fromApi = ( { algorithm, sites } ) => - sites.map( ( site ) => ( { - feedId: site.feed_id, - blogId: site.blog_id, - title: decodeEntities( site.blog_title ), - url: site.blog_url ?? site.URL, - railcar: site.railcar, - algorithm, - } ) ); - -export const addRecommendedSites = ( { payload: { seed, offset } }, sites ) => - receiveRecommendedSites( { sites, seed, offset } ); - -registerHandlers( 'state/data-layer/wpcom/read/recommendations/sites/index.js', { - [ READER_RECOMMENDED_SITES_REQUEST ]: [ - dispatchRequest( { - fetch: requestRecommendedSites, - onSuccess: addRecommendedSites, - onError: noop, - fromApi, - } ), - ], -} ); diff --git a/client/state/data-layer/wpcom/read/recommendations/sites/index.ts b/client/state/data-layer/wpcom/read/recommendations/sites/index.ts new file mode 100644 index 0000000000000..c750f08de881a --- /dev/null +++ b/client/state/data-layer/wpcom/read/recommendations/sites/index.ts @@ -0,0 +1,92 @@ +import { Action } from 'redux'; +import { Railcar } from 'calypso/data/marketplace/types'; +import { decodeEntities } from 'calypso/lib/formatting'; +import { registerHandlers } from 'calypso/state/data-layer/handler-registry'; +import { http } from 'calypso/state/data-layer/wpcom-http/actions'; +import { dispatchRequest } from 'calypso/state/data-layer/wpcom-http/utils'; +import { READER_RECOMMENDED_SITES_REQUEST } from 'calypso/state/reader/action-types'; +import { + receiveRecommendedSites, + RecommendedSitesRequestAction, +} from 'calypso/state/reader/recommended-sites/actions'; + +const noop = () => {}; + +export const requestRecommendedSites = ( action: RecommendedSitesRequestAction ): Action => { + const { seed = 1, number = 10, offset = 0 } = action.payload; + + return http( { + method: 'GET', + path: '/read/recommendations/sites', + query: { number, offset, seed, posts_per_site: 0 }, + apiVersion: '1.2', + onSuccess: action, + onFailure: action, + } ); +}; + +interface RecommendedSitesBody { + algorithm: string; + sites: RecommendedSiteResponse[]; + meta: { + next_page: string; + }; +} + +interface RecommendedSiteResponse { + blog_id: number; + blog_title?: string; + blog_url?: string; + description: string; + feed_id: number; + feed_url: string; + icon: { + ico: string; + img: string; + media_id: string; + }; + ID: number; + name: string; + railcar: Railcar; + URL: string; +} + +export interface RecommendedSite { + algorithm: string; + blogId: number; + feedId: number; + railcar: Railcar; + title: string; + url: string; +} + +const mapResponseToRecommendedSites = ( { + algorithm, + sites, +}: RecommendedSitesBody ): RecommendedSite[] => + sites.map( + ( site: RecommendedSiteResponse ): RecommendedSite => ( { + algorithm, + blogId: site.blog_id, + feedId: site.feed_id, + railcar: site.railcar, + title: decodeEntities( site.blog_title ?? '' ), + url: site.blog_url ?? site.URL, + } ) + ); + +export const addRecommendedSites = ( + { payload: { seed, offset } }: RecommendedSitesRequestAction, + sites: RecommendedSite[] +) => receiveRecommendedSites( { sites, seed, offset } ); + +registerHandlers( 'state/data-layer/wpcom/read/recommendations/sites/index.js', { + [ READER_RECOMMENDED_SITES_REQUEST ]: [ + dispatchRequest( { + fetch: requestRecommendedSites, + onSuccess: addRecommendedSites, + onError: noop, + fromApi: mapResponseToRecommendedSites, + } ), + ], +} ); diff --git a/client/state/reader/recommended-sites/actions.ts b/client/state/reader/recommended-sites/actions.ts index 316ce3ae9e6ab..734fad2d5b776 100644 --- a/client/state/reader/recommended-sites/actions.ts +++ b/client/state/reader/recommended-sites/actions.ts @@ -4,15 +4,43 @@ import { READER_RECOMMENDED_SITES_RECEIVE, READER_RECOMMENDED_SITE_FOLLOWED, } from 'calypso/state/reader/action-types'; -import 'calypso/state/data-layer/wpcom/read/recommendations/sites'; +import type { RecommendedSite } from 'calypso/state/data-layer/wpcom/read/recommendations/sites'; import 'calypso/state/reader/init'; -import { RecommendedSite } from './types'; +import 'calypso/state/data-layer/wpcom/read/recommendations/sites'; + +export interface RecommendedSitesRequestAction { + type: 'READER_RECOMMENDED_SITES_REQUEST'; + payload: RecommendedSitesRequestPayload; +} + +interface RecommendedSitesRequestPayload { + offset: number; + number: number; + seed: number; +} -export const requestRecommendedSites = ( { offset = 0, number = 4, seed = 0 } ) => ( { +export const requestRecommendedSites = ( { + offset = 0, + number = 4, + seed = 0, +}: { + offset?: number; + number?: number; + seed?: number; +} ): RecommendedSitesRequestAction => ( { type: READER_RECOMMENDED_SITES_REQUEST, payload: { offset, number, seed }, } ); +export interface RecommendedSitesReceiveAction { + type: 'READER_RECOMMENDED_SITES_RECEIVE'; + payload: { + offset?: number; + sites: RecommendedSite[]; + }; + seed: number; +} + export const receiveRecommendedSites = ( { seed, sites, @@ -21,25 +49,47 @@ export const receiveRecommendedSites = ( { seed: number; sites: RecommendedSite[]; offset?: number; -} ) => ( { +} ): RecommendedSitesReceiveAction => ( { type: READER_RECOMMENDED_SITES_RECEIVE, payload: { sites, offset }, seed, } ); +export interface RecommendedSiteDismissedAction { + type: 'READER_RECOMMENDED_SITE_DISMISSED'; + payload: { + siteId: number; + }; + seed: number; +} + export const dismissedRecommendedSite = ( { siteId, seed, }: { siteId: number; seed: number; -} ) => ( { +} ): RecommendedSiteDismissedAction => ( { type: READER_RECOMMENDED_SITE_DISMISSED, payload: { siteId }, seed, } ); -export const followedRecommendedSite = ( { siteId, seed }: { siteId: number; seed: number } ) => ( { +export interface RecommendedSiteFollowedAction { + type: 'READER_RECOMMENDED_SITE_FOLLOWED'; + payload: { + siteId: number; + }; + seed: number; +} + +export const followedRecommendedSite = ( { + siteId, + seed, +}: { + siteId: number; + seed: number; +} ): RecommendedSiteFollowedAction => ( { type: READER_RECOMMENDED_SITE_FOLLOWED, payload: { siteId }, seed, diff --git a/client/state/reader/recommended-sites/reducer.ts b/client/state/reader/recommended-sites/reducer.ts index 73da8d3eca562..80e9ff8105503 100644 --- a/client/state/reader/recommended-sites/reducer.ts +++ b/client/state/reader/recommended-sites/reducer.ts @@ -1,4 +1,5 @@ import { uniqueBy } from '@automattic/js-utils'; +import { RecommendedSite } from 'calypso/state/data-layer/wpcom/read/recommendations/sites'; import { READER_RECOMMENDED_SITE_DISMISSED, READER_RECOMMENDED_SITE_FOLLOWED, @@ -10,7 +11,6 @@ import { followedRecommendedSite, receiveRecommendedSites, } from './actions'; -import { RecommendedSite } from './types'; /** * Tracks mappings between randomization seeds and site recs. @@ -47,7 +47,7 @@ export const pagingOffset = keyedReducer< number >( 'seed', ( state = 0, action switch ( action.type ) { case READER_RECOMMENDED_SITES_RECEIVE: return Math.max( - ( action as ReturnType< typeof receiveRecommendedSites > ).payload.offset, + ( action as ReturnType< typeof receiveRecommendedSites > ).payload.offset ?? 0, state ); } diff --git a/client/state/reader/recommended-sites/selectors/get-reader-recommended-sites-paging-offset.js b/client/state/reader/recommended-sites/selectors/get-reader-recommended-sites-paging-offset.js deleted file mode 100644 index be7c1967772e6..0000000000000 --- a/client/state/reader/recommended-sites/selectors/get-reader-recommended-sites-paging-offset.js +++ /dev/null @@ -1,9 +0,0 @@ -import 'calypso/state/reader/init'; - -/** - * Returns the recommended sites paging offset - * @param {number} seed the elasticsearch seed for which to grab recs - * @returns {number} the paging offset - */ - -export default ( state, seed ) => state.reader.recommendedSites.pagingOffset[ seed ]; diff --git a/client/state/reader/recommended-sites/selectors/get-reader-recommended-sites-paging-offset.ts b/client/state/reader/recommended-sites/selectors/get-reader-recommended-sites-paging-offset.ts new file mode 100644 index 0000000000000..f6465f1535326 --- /dev/null +++ b/client/state/reader/recommended-sites/selectors/get-reader-recommended-sites-paging-offset.ts @@ -0,0 +1,9 @@ +import 'calypso/state/reader/init'; +import { AppState } from 'calypso/types'; + +/** + * Returns the recommended sites paging offset + */ +export default ( state: AppState, seed: number ) => { + return state.reader.recommendedSites.pagingOffset[ seed ]; +}; diff --git a/client/state/reader/recommended-sites/selectors/get-reader-recommended-sites.js b/client/state/reader/recommended-sites/selectors/get-reader-recommended-sites.js deleted file mode 100644 index cc7be652148a0..0000000000000 --- a/client/state/reader/recommended-sites/selectors/get-reader-recommended-sites.js +++ /dev/null @@ -1,9 +0,0 @@ -import 'calypso/state/reader/init'; - -/** - * Returns the recommended sites for a given seed. - * @param {number} seed the elasticsearch seed for which to grab recs - * @returns {Array} array of recommended sites for a given seed - */ - -export default ( state, seed ) => state.reader.recommendedSites.items[ seed ]; diff --git a/client/state/reader/recommended-sites/selectors/get-reader-recommended-sites.ts b/client/state/reader/recommended-sites/selectors/get-reader-recommended-sites.ts new file mode 100644 index 0000000000000..683ba0953c2b7 --- /dev/null +++ b/client/state/reader/recommended-sites/selectors/get-reader-recommended-sites.ts @@ -0,0 +1,9 @@ +import 'calypso/state/reader/init'; +import { AppState } from 'calypso/types'; + +/** + * Returns the recommended sites for a given seed. + */ +export default < T >( state: AppState, seed: number ): T[] => { + return state.reader.recommendedSites.items[ seed ]; +}; diff --git a/client/state/reader/recommended-sites/selectors/index.js b/client/state/reader/recommended-sites/selectors/index.ts similarity index 100% rename from client/state/reader/recommended-sites/selectors/index.js rename to client/state/reader/recommended-sites/selectors/index.ts diff --git a/client/state/reader/recommended-sites/types.ts b/client/state/reader/recommended-sites/types.ts deleted file mode 100644 index 53fd0c00c9da2..0000000000000 --- a/client/state/reader/recommended-sites/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Railcar } from 'calypso/data/marketplace/types'; - -export type RecommendedSite = { - feedId: number; - blogId: number; - railcar?: Railcar; -}; From 8299392fd1d3c52105f77d288a2230bd153f6b08 Mon Sep 17 00:00:00 2001 From: Mehmood Ahmad <31419912+mehmoodak@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:10:24 +0500 Subject: [PATCH 2/9] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20dispatch=20typing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/reader/recommended-sites/recommended-sites.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client/reader/recommended-sites/recommended-sites.tsx b/client/reader/recommended-sites/recommended-sites.tsx index d8875f4d685d1..e92e51901508b 100644 --- a/client/reader/recommended-sites/recommended-sites.tsx +++ b/client/reader/recommended-sites/recommended-sites.tsx @@ -5,8 +5,12 @@ import { __experimentalHStack as HStack } from '@wordpress/components'; import { useTranslate } from 'i18n-calypso'; import React, { useEffect, useMemo } from 'react'; import { useDispatch, useSelector } from 'react-redux'; +import { Dispatch } from 'redux'; import { isCurrentUserEmailVerified } from 'calypso/state/current-user/selectors'; -import { requestRecommendedSites } from 'calypso/state/reader/recommended-sites/actions'; +import { + RecommendedSitesRequestAction, + requestRecommendedSites, +} from 'calypso/state/reader/recommended-sites/actions'; import { getReaderRecommendedSites, getReaderRecommendedSitesPagingOffset, @@ -54,7 +58,7 @@ const RecommendedSitesPlaceholder = ( { count }: { count: number } ) => { const RecommendedSites = () => { const translate = useTranslate(); - const dispatch = useDispatch(); + const dispatch = useDispatch< Dispatch< RecommendedSitesRequestAction > >(); const isEmailVerified = useSelector( isCurrentUserEmailVerified ); const amountOfPlaceHolders = useBreakpoint( '<1040px' ) ? 1 : 2; From 49729eabf1341819273f40b72ba7b64e425061e0 Mon Sep 17 00:00:00 2001 From: Mehmood Ahmad <31419912+mehmoodak@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:33:49 +0500 Subject: [PATCH 3/9] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20recommended=20sites?= =?UTF-8?q?=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wpcom/read/recommendations/sites/index.ts | 4 +- .../sites/test/{index.js => index.ts} | 60 +++++++++++++++---- 2 files changed, 52 insertions(+), 12 deletions(-) rename client/state/data-layer/wpcom/read/recommendations/sites/test/{index.js => index.ts} (58%) diff --git a/client/state/data-layer/wpcom/read/recommendations/sites/index.ts b/client/state/data-layer/wpcom/read/recommendations/sites/index.ts index c750f08de881a..6d895400c882d 100644 --- a/client/state/data-layer/wpcom/read/recommendations/sites/index.ts +++ b/client/state/data-layer/wpcom/read/recommendations/sites/index.ts @@ -25,7 +25,7 @@ export const requestRecommendedSites = ( action: RecommendedSitesRequestAction ) } ); }; -interface RecommendedSitesBody { +export interface RecommendedSitesBody { algorithm: string; sites: RecommendedSiteResponse[]; meta: { @@ -60,7 +60,7 @@ export interface RecommendedSite { url: string; } -const mapResponseToRecommendedSites = ( { +export const mapResponseToRecommendedSites = ( { algorithm, sites, }: RecommendedSitesBody ): RecommendedSite[] => diff --git a/client/state/data-layer/wpcom/read/recommendations/sites/test/index.js b/client/state/data-layer/wpcom/read/recommendations/sites/test/index.ts similarity index 58% rename from client/state/data-layer/wpcom/read/recommendations/sites/test/index.js rename to client/state/data-layer/wpcom/read/recommendations/sites/test/index.ts index 16f861cf1a36f..f944f94a628f7 100644 --- a/client/state/data-layer/wpcom/read/recommendations/sites/test/index.js +++ b/client/state/data-layer/wpcom/read/recommendations/sites/test/index.ts @@ -3,35 +3,74 @@ import { requestRecommendedSites as requestRecommendedSitesAction, receiveRecommendedSites, } from 'calypso/state/reader/recommended-sites/actions'; -import { requestRecommendedSites, addRecommendedSites, fromApi } from '../'; +import { + requestRecommendedSites, + addRecommendedSites, + mapResponseToRecommendedSites, + RecommendedSitesBody, + RecommendedSite, +} from '../index'; const algorithm = 'chicken-recs/es1'; const seed = 42; -const response = { +const response: RecommendedSitesBody = { algorithm, sites: [ { blog_id: 19096129, - feed_id: 185124, blog_title: 'Bente Haarstad Photography&', blog_url: 'http://bentehaarstad.wordpress.com', + description: 'Description 1', + feed_id: 185124, + feed_url: 'http://bentehaarstad.wordpress.com/feed/', + icon: { + ico: 'http://bentehaarstad.wordpress.com/favicon.ico', + img: 'http://bentehaarstad.wordpress.com/favicon.ico', + media_id: '12345', + }, + ID: 12345, + name: 'Bente Haarstad Photography&', railcar: {}, + URL: 'http://bentehaarstad.wordpress.com', }, { blog_id: 38492359, - feed_id: 42081376, blog_title: 'The Renegade Press', blog_url: 'http://chrisnicholaswrites.wordpress.com', + description: 'Description 2', + feed_id: 42081376, + feed_url: 'http://chrisnicholaswrites.wordpress.com/feed/', + icon: { + ico: 'http://chrisnicholaswrites.wordpress.com/favicon.ico', + img: 'http://chrisnicholaswrites.wordpress.com/favicon.ico', + media_id: '12345', + }, + ID: 12345, + name: 'The Renegade Press', railcar: {}, + URL: 'http://chrisnicholaswrites.wordpress.com', }, { blog_id: 30436600, - feed_id: 1098976, blog_title: 'Make Something Mondays!', blog_url: 'http://makesomethingmondays.wordpress.com', + description: 'Description 3', + feed_id: 1098976, + feed_url: 'http://makesomethingmondays.wordpress.com/feed/', + icon: { + ico: 'http://makesomethingmondays.wordpress.com/favicon.ico', + img: 'http://makesomethingmondays.wordpress.com/favicon.ico', + media_id: '12345', + }, + ID: 12345, + name: 'Make Something Mondays!', railcar: {}, + URL: 'http://makesomethingmondays.wordpress.com', }, ], + meta: { + next_page: 'next_page', + }, }; describe( 'recommended sites', () => { @@ -55,10 +94,11 @@ describe( 'recommended sites', () => { describe( '#receiveRecommendedSites', () => { test( 'should dispatch action with sites if successful', () => { const action = requestRecommendedSitesAction( { seed } ); - const result = addRecommendedSites( action, response ); + const recommendedSites = mapResponseToRecommendedSites( response ); + const result = addRecommendedSites( action, recommendedSites ); expect( result ).toEqual( receiveRecommendedSites( { - sites: response, + sites: recommendedSites, seed, offset: 0, } ) @@ -66,9 +106,9 @@ describe( 'recommended sites', () => { } ); } ); - describe( '#fromApi', () => { + describe( '#mapResponseToRecommendedSites', () => { test( 'should extract only what we care about from the api response. and decode entities', () => { - const expected = [ + const expected: RecommendedSite[] = [ { algorithm, railcar: {}, @@ -95,7 +135,7 @@ describe( 'recommended sites', () => { }, ]; - expect( fromApi( response ) ).toEqual( expected ); + expect( mapResponseToRecommendedSites( response ) ).toEqual( expected ); } ); } ); } ); From 63aca72313879f8bd40c8cb289b0761a8b54fa71 Mon Sep 17 00:00:00 2001 From: Mehmood Ahmad <31419912+mehmoodak@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:52:22 +0500 Subject: [PATCH 4/9] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/reader/stream/reader-popular-sites-sidebar/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/reader/stream/reader-popular-sites-sidebar/index.tsx b/client/reader/stream/reader-popular-sites-sidebar/index.tsx index 2338d345b507b..0bce5fc902d43 100644 --- a/client/reader/stream/reader-popular-sites-sidebar/index.tsx +++ b/client/reader/stream/reader-popular-sites-sidebar/index.tsx @@ -54,8 +54,8 @@ const getSiteFromItem = ( item: PopularSiteItemProp ): ReaderPopularSite | null const ReaderPopularSitesSidebar = ( props: PopularSitesSidebarProps ) => { const { followSource, items } = props; const sites = items - .map( ( item ) => getSiteFromItem( item ) ) - .filter( ( site ) => site !== null ); + .map( ( item ): ReaderPopularSite | null => getSiteFromItem( item ) ) + .filter( ( site ): site is ReaderPopularSite => site !== null ); const popularSitesLinks = sites.map( ( site ) => ( Date: Mon, 2 Dec 2024 13:20:29 +0500 Subject: [PATCH 5/9] ADD: title prop in ReaderPopularSitesSidebar component --- client/reader/discover/discover-stream.js | 24 ++++++++----------- .../reader-popular-sites-sidebar/index.tsx | 22 +++++++++++------ 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/client/reader/discover/discover-stream.js b/client/reader/discover/discover-stream.js index aadc40c33cf45..6e4ee9a8ce670 100644 --- a/client/reader/discover/discover-stream.js +++ b/client/reader/discover/discover-stream.js @@ -100,25 +100,21 @@ const DiscoverStream = ( props ) => { const streamSidebar = () => { if ( selectedTab === FIRST_POSTS_TAB && recommendedSites?.length ) { return ( - <> -

{ translate( 'New sites' ) }

- - + ); } if ( ( isDefaultTab || selectedTab === 'latest' ) && recommendedSites?.length ) { return ( - <> -

{ translate( 'Popular sites' ) }

- - + ); } else if ( ! ( isDefaultTab || selectedTab === 'latest' ) ) { return ; diff --git a/client/reader/stream/reader-popular-sites-sidebar/index.tsx b/client/reader/stream/reader-popular-sites-sidebar/index.tsx index 0bce5fc902d43..a9ea25b1c82d7 100644 --- a/client/reader/stream/reader-popular-sites-sidebar/index.tsx +++ b/client/reader/stream/reader-popular-sites-sidebar/index.tsx @@ -4,6 +4,7 @@ import '../style.scss'; interface PopularSitesSidebarProps { followSource: string; items: PopularSiteItemProp[]; + title?: string; } /** @@ -35,8 +36,10 @@ function unescape( str: string ): string { return str.replace( /&#(\d+);/g, ( match, entity ) => String.fromCharCode( entity ) ); } -// create function to transform item into a site object -const getSiteFromItem = ( item: PopularSiteItemProp ): ReaderPopularSite | null => { +/** + * Converts a popular site item, provided as a prop, into a popular site object for display in the ReaderPopularSitesSidebar component. + */ +const getPopularSiteFromItem = ( item: PopularSiteItemProp ): ReaderPopularSite | null => { if ( item.site_name === undefined || item.site_description === undefined ) { return null; } @@ -52,12 +55,12 @@ const getSiteFromItem = ( item: PopularSiteItemProp ): ReaderPopularSite | null }; const ReaderPopularSitesSidebar = ( props: PopularSitesSidebarProps ) => { - const { followSource, items } = props; - const sites = items - .map( ( item ): ReaderPopularSite | null => getSiteFromItem( item ) ) + const { followSource, items, title } = props; + const sites: ReaderPopularSite[] = items + .map( ( item ): ReaderPopularSite | null => getPopularSiteFromItem( item ) ) .filter( ( site ): site is ReaderPopularSite => site !== null ); - const popularSitesLinks = sites.map( ( site ) => ( + const popularSitesLinks: JSX.Element[] = sites.map( ( site ) => ( { return null; } - return
{ popularSitesLinks }
; + return ( + <> + { title ?

{ title }

: null } +
{ popularSitesLinks }
+ + ); }; export default ReaderPopularSitesSidebar; From adf443e81afa8e42a77aa1a2f29836d7bc2e724d Mon Sep 17 00:00:00 2001 From: Mehmood Ahmad <31419912+mehmoodak@users.noreply.github.com> Date: Mon, 2 Dec 2024 13:21:01 +0500 Subject: [PATCH 6/9] =?UTF-8?q?=F0=9F=92=AB=20UPDATE:=20replace=20subscrip?= =?UTF-8?q?tions=20available=20on=20recent=20sidebar=20with=20popular=20si?= =?UTF-8?q?tes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{follow-sources.jsx => follow-sources.ts} | 1 + client/reader/following/main.tsx | 55 +++++- .../reader-list-followed-sites/index.jsx | 157 ------------------ .../wpcom/read/recommendations/sites/index.ts | 10 +- 4 files changed, 61 insertions(+), 162 deletions(-) rename client/reader/{follow-sources.jsx => follow-sources.ts} (83%) delete mode 100644 client/reader/stream/reader-list-followed-sites/index.jsx diff --git a/client/reader/follow-sources.jsx b/client/reader/follow-sources.ts similarity index 83% rename from client/reader/follow-sources.jsx rename to client/reader/follow-sources.ts index be50b21df6b54..28556d1c5e1a1 100644 --- a/client/reader/follow-sources.jsx +++ b/client/reader/follow-sources.ts @@ -3,5 +3,6 @@ export const IN_STREAM_RECOMMENDATION = 'in-stream-recommendation'; export const SEARCH_RESULTS_SITES = 'search-results-sites'; export const READER_POST_OPTIONS_MENU = 'reader-post-options-menu'; export const READER_SUGGESTED_FOLLOWS_DIALOG = 'reader-suggested-follows-dialog'; +export const READER_RECENT_SIDEBAR_POPULAR_SITES = 'reader-recent-sidebar-popular-sites'; export const READER_SEARCH_POPULAR_SITES = 'reader-search-popular-sites'; export const READER_DISCOVER_POPULAR_SITES = 'reader-discover-popular-sites'; diff --git a/client/reader/following/main.tsx b/client/reader/following/main.tsx index c92217100b428..9e5fdc3b22ceb 100644 --- a/client/reader/following/main.tsx +++ b/client/reader/following/main.tsx @@ -1,6 +1,9 @@ import config from '@automattic/calypso-config'; import clsx from 'clsx'; import { translate } from 'i18n-calypso'; +import { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { Dispatch } from 'redux'; import AsyncLoad from 'calypso/components/async-load'; import BloganuaryHeader from 'calypso/components/bloganuary-header'; import NavigationHeader from 'calypso/components/navigation-header'; @@ -8,15 +11,23 @@ import withDimensions from 'calypso/lib/with-dimensions'; import ReaderOnboarding from 'calypso/reader/onboarding'; import SuggestionProvider from 'calypso/reader/search-stream/suggestion-provider'; import ReaderStream, { WIDE_DISPLAY_CUTOFF } from 'calypso/reader/stream'; -import ReaderListFollowedSites from 'calypso/reader/stream/reader-list-followed-sites'; +import { RecommendedSite } from 'calypso/state/data-layer/wpcom/read/recommendations/sites'; +import { + RecommendedSitesRequestAction, + requestRecommendedSites, +} from 'calypso/state/reader/recommended-sites/actions'; +import { getReaderRecommendedSites } from 'calypso/state/reader/recommended-sites/selectors'; +import { READER_RECENT_SIDEBAR_POPULAR_SITES } from '../follow-sources'; import Recent from '../recent'; +import ReaderPopularSitesSidebar from '../stream/reader-popular-sites-sidebar'; import { useFollowingView } from './view-preference'; import ViewToggle from './view-toggle'; import './style.scss'; +export const RECOMMENDED_SITES_SEED = Math.floor( Math.random() * 10001 ); + function FollowingStream( { ...props } ) { const { currentView } = useFollowingView(); - const viewToggle = config.isEnabled( 'reader/recent-feed-overhaul' ) ? : null; return ( @@ -27,7 +38,7 @@ function FollowingStream( { ...props } ) { } + streamSidebar={ () => } > >(); + const recommendedSites = useSelector( ( state ) => { + return getReaderRecommendedSites< RecommendedSite >( state, RECOMMENDED_SITES_SEED ) || []; + } ); + + useEffect( () => { + // Avoid fetching recommended sites if they are already present in the store. + if ( recommendedSites.length > 0 ) { + return; + } + + dispatch( requestRecommendedSites( { seed: RECOMMENDED_SITES_SEED, number: 10 } ) ); + }, [ dispatch, recommendedSites ] ); + + if ( recommendedSites.length === 0 ) { + return null; + } + + return ( + { + return { + blogId: s.blogId, + feed_ID: s.feedId, + feed_URL: s.feed_url, + site_name: s.title, + site_description: s.description, + site_icon: s.icon, + url: s.url, + }; + } ) } + title={ translate( 'Popular sites' ) } + /> + ); +} + export default SuggestionProvider( withDimensions( FollowingStream ) ); diff --git a/client/reader/stream/reader-list-followed-sites/index.jsx b/client/reader/stream/reader-list-followed-sites/index.jsx deleted file mode 100644 index c8c3955e9d8ac..0000000000000 --- a/client/reader/stream/reader-list-followed-sites/index.jsx +++ /dev/null @@ -1,157 +0,0 @@ -import { Button } from '@automattic/components'; -import { isMobile } from '@automattic/viewport'; -import { localize } from 'i18n-calypso'; -import { map } from 'lodash'; -import PropTypes from 'prop-types'; -import { Component } from 'react'; -import { connect } from 'react-redux'; -import SearchCard from 'calypso/components/search-card'; -import UrlSearch from 'calypso/lib/url-search'; -import { filterFollowsByIsFollowed, filterFollowsByQuery } from 'calypso/reader/follow-helpers'; -import { isEligibleForUnseen } from 'calypso/reader/get-helpers'; -import { hasReaderFollowOrganization } from 'calypso/state/reader/follows/selectors'; -import getReaderFollowedSites from 'calypso/state/reader/follows/selectors/get-reader-followed-sites'; -import isFeedWPForTeams from 'calypso/state/selectors/is-feed-wpforteams'; -import isSiteWPForTeams from 'calypso/state/selectors/is-site-wpforteams'; -import ReaderListFollowingItem from './item'; -import '../style.scss'; - -export class ReaderListFollowedSites extends Component { - constructor( props ) { - super( props ); - this.state = { - sitePage: 1, - query: '', - }; - } - - static defaultProps = { - sitesPerPage: 25, - }; - - static propTypes = { - path: PropTypes.string.isRequired, - sites: PropTypes.array, - doSearch: PropTypes.func.isRequired, - isWPForTeamsItem: PropTypes.bool, - hasOrganization: PropTypes.bool, - sitesPerPage: PropTypes.number, - }; - - isUnseen = () => { - const { isWPForTeamsItem, hasOrganization } = this.props; - return isEligibleForUnseen( { isWPForTeamsItem, hasOrganization } ); - }; - - loadMoreSites = () => { - const { sitePage } = this.state; - const { sites, sitesPerPage } = this.props; - - //If we've reached the end of the set of sites, all sites have loaded - if ( sitesPerPage * sitePage >= sites.length ) { - return; - } - - this.setState( { - sitePage: this.state.sitePage + 1, - } ); - }; - - renderSites = ( follows ) => { - const { path } = this.props; - return map( - follows, - ( follow ) => - follow && ( - - ) - ); - }; - - searchEvent = ( query ) => { - this.setState( { - query: query, - } ); - this.props.doSearch( query ); - }; - - render() { - const { sites, sitesPerPage, translate } = this.props; - const { sitePage, query } = this.state; - const searchThreshold = 15; - let filteredFollows = filterFollowsByQuery( query, sites ); - filteredFollows = filterFollowsByIsFollowed( filteredFollows ); - const allSitesLoaded = sitesPerPage * sitePage >= filteredFollows.length; - const sitesToShow = filteredFollows.slice( 0, sitesPerPage * sitePage ); - - if ( ! sitesToShow ) { - return null; - } - - return ( - <> - { ! isMobile() ? ( -

- { translate( 'Subscriptions' ) }{ ' ' } - { translate( 'Manage' ) } -

- ) : null } - { sites.length >= searchThreshold && ( - - ) } - - { isMobile() ? ( -

- { translate( 'Manage your subscriptions' ) } -

- ) : null } - -
    - { this.renderSites( sitesToShow ) } - { ! allSitesLoaded && ( -
  • - -
  • - ) } -
- - ); - } -} - -export default connect( ( state, ownProps ) => { - return { - isWPForTeamsItem: - isSiteWPForTeams( state, ownProps.site && ownProps.site.ID ) || - isFeedWPForTeams( state, ownProps.feed && ownProps.feed.feed_ID ), - hasOrganization: hasReaderFollowOrganization( - state, - ownProps.feed && ownProps.feed.feed_ID, - ownProps.site && ownProps.site.ID - ), - sites: getReaderFollowedSites( state ), - }; -} )( localize( UrlSearch( ReaderListFollowedSites ) ) ); diff --git a/client/state/data-layer/wpcom/read/recommendations/sites/index.ts b/client/state/data-layer/wpcom/read/recommendations/sites/index.ts index 6d895400c882d..0b89bfbc7e60a 100644 --- a/client/state/data-layer/wpcom/read/recommendations/sites/index.ts +++ b/client/state/data-layer/wpcom/read/recommendations/sites/index.ts @@ -54,13 +54,16 @@ interface RecommendedSiteResponse { export interface RecommendedSite { algorithm: string; blogId: number; + description: string; feedId: number; + feed_url: string; + icon: string; railcar: Railcar; title: string; url: string; } -export const mapResponseToRecommendedSites = ( { +const mapResponseToRecommendedSites = ( { algorithm, sites, }: RecommendedSitesBody ): RecommendedSite[] => @@ -68,9 +71,12 @@ export const mapResponseToRecommendedSites = ( { ( site: RecommendedSiteResponse ): RecommendedSite => ( { algorithm, blogId: site.blog_id, + description: site.description, feedId: site.feed_id, + feed_url: site.feed_url, + icon: site.icon?.img, railcar: site.railcar, - title: decodeEntities( site.blog_title ?? '' ), + title: decodeEntities( site.blog_title ?? site.name ), url: site.blog_url ?? site.URL, } ) ); From 72e12efc48c9e3549fe079da76078cbc1d4e1443 Mon Sep 17 00:00:00 2001 From: Mehmood Ahmad <31419912+mehmoodak@users.noreply.github.com> Date: Mon, 2 Dec 2024 13:48:46 +0500 Subject: [PATCH 7/9] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/reader/following/main.tsx | 2 +- .../wpcom/read/recommendations/sites/index.ts | 6 +++--- .../read/recommendations/sites/test/index.ts | 15 ++++++++++++--- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/client/reader/following/main.tsx b/client/reader/following/main.tsx index 9e5fdc3b22ceb..e1defc81c90ad 100644 --- a/client/reader/following/main.tsx +++ b/client/reader/following/main.tsx @@ -84,7 +84,7 @@ function ReaderStreamSidebar(): JSX.Element | null { return { blogId: s.blogId, feed_ID: s.feedId, - feed_URL: s.feed_url, + feed_URL: s.feedUrl, site_name: s.title, site_description: s.description, site_icon: s.icon, diff --git a/client/state/data-layer/wpcom/read/recommendations/sites/index.ts b/client/state/data-layer/wpcom/read/recommendations/sites/index.ts index 0b89bfbc7e60a..e095dab632431 100644 --- a/client/state/data-layer/wpcom/read/recommendations/sites/index.ts +++ b/client/state/data-layer/wpcom/read/recommendations/sites/index.ts @@ -56,14 +56,14 @@ export interface RecommendedSite { blogId: number; description: string; feedId: number; - feed_url: string; + feedUrl: string; icon: string; railcar: Railcar; title: string; url: string; } -const mapResponseToRecommendedSites = ( { +export const mapResponseToRecommendedSites = ( { algorithm, sites, }: RecommendedSitesBody ): RecommendedSite[] => @@ -73,7 +73,7 @@ const mapResponseToRecommendedSites = ( { blogId: site.blog_id, description: site.description, feedId: site.feed_id, - feed_url: site.feed_url, + feedUrl: site.feed_url, icon: site.icon?.img, railcar: site.railcar, title: decodeEntities( site.blog_title ?? site.name ), diff --git a/client/state/data-layer/wpcom/read/recommendations/sites/test/index.ts b/client/state/data-layer/wpcom/read/recommendations/sites/test/index.ts index f944f94a628f7..3ef88b297090c 100644 --- a/client/state/data-layer/wpcom/read/recommendations/sites/test/index.ts +++ b/client/state/data-layer/wpcom/read/recommendations/sites/test/index.ts @@ -111,25 +111,34 @@ describe( 'recommended sites', () => { const expected: RecommendedSite[] = [ { algorithm, - railcar: {}, blogId: 19096129, + description: 'Description 1', feedId: 185124, + feedUrl: 'http://bentehaarstad.wordpress.com/feed/', + icon: 'http://bentehaarstad.wordpress.com/favicon.ico', + railcar: {}, title: 'Bente Haarstad Photography&', url: 'http://bentehaarstad.wordpress.com', }, { algorithm, - railcar: {}, blogId: 38492359, + description: 'Description 2', feedId: 42081376, + feedUrl: 'http://chrisnicholaswrites.wordpress.com/feed/', + icon: 'http://chrisnicholaswrites.wordpress.com/favicon.ico', + railcar: {}, title: 'The Renegade Press', url: 'http://chrisnicholaswrites.wordpress.com', }, { algorithm, - railcar: {}, blogId: 30436600, + description: 'Description 3', feedId: 1098976, + feedUrl: 'http://makesomethingmondays.wordpress.com/feed/', + icon: 'http://makesomethingmondays.wordpress.com/favicon.ico', + railcar: {}, title: 'Make Something Mondays!', url: 'http://makesomethingmondays.wordpress.com', }, From 9397732504547821793dd3646525025de00b0386 Mon Sep 17 00:00:00 2001 From: Mehmood Ahmad <31419912+mehmoodak@users.noreply.github.com> Date: Tue, 3 Dec 2024 19:58:46 +0500 Subject: [PATCH 8/9] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20psimplify=20title?= =?UTF-8?q?=20implementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/reader/stream/reader-popular-sites-sidebar/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/reader/stream/reader-popular-sites-sidebar/index.tsx b/client/reader/stream/reader-popular-sites-sidebar/index.tsx index a9ea25b1c82d7..e5e622eb5aadd 100644 --- a/client/reader/stream/reader-popular-sites-sidebar/index.tsx +++ b/client/reader/stream/reader-popular-sites-sidebar/index.tsx @@ -80,7 +80,7 @@ const ReaderPopularSitesSidebar = ( props: PopularSitesSidebarProps ) => { return ( <> - { title ?

{ title }

: null } + { title &&

{ title }

}
{ popularSitesLinks }
); From ea5e8921436e53785af102b824b1b566285fbf3d Mon Sep 17 00:00:00 2001 From: Mehmood Ahmad <31419912+mehmoodak@users.noreply.github.com> Date: Tue, 3 Dec 2024 20:04:08 +0500 Subject: [PATCH 9/9] =?UTF-8?q?=F0=9F=94=A8=20REFACTOR:=20move=20ReaderStr?= =?UTF-8?q?eamSidebar=20component=20to=20a=20separate=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/reader/following/main.tsx | 52 +----------------- .../following/reader-stream-sidebar.tsx | 54 +++++++++++++++++++ 2 files changed, 55 insertions(+), 51 deletions(-) create mode 100644 client/reader/following/reader-stream-sidebar.tsx diff --git a/client/reader/following/main.tsx b/client/reader/following/main.tsx index e1defc81c90ad..579e9f0132acd 100644 --- a/client/reader/following/main.tsx +++ b/client/reader/following/main.tsx @@ -1,9 +1,6 @@ import config from '@automattic/calypso-config'; import clsx from 'clsx'; import { translate } from 'i18n-calypso'; -import { useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { Dispatch } from 'redux'; import AsyncLoad from 'calypso/components/async-load'; import BloganuaryHeader from 'calypso/components/bloganuary-header'; import NavigationHeader from 'calypso/components/navigation-header'; @@ -11,21 +8,12 @@ import withDimensions from 'calypso/lib/with-dimensions'; import ReaderOnboarding from 'calypso/reader/onboarding'; import SuggestionProvider from 'calypso/reader/search-stream/suggestion-provider'; import ReaderStream, { WIDE_DISPLAY_CUTOFF } from 'calypso/reader/stream'; -import { RecommendedSite } from 'calypso/state/data-layer/wpcom/read/recommendations/sites'; -import { - RecommendedSitesRequestAction, - requestRecommendedSites, -} from 'calypso/state/reader/recommended-sites/actions'; -import { getReaderRecommendedSites } from 'calypso/state/reader/recommended-sites/selectors'; -import { READER_RECENT_SIDEBAR_POPULAR_SITES } from '../follow-sources'; import Recent from '../recent'; -import ReaderPopularSitesSidebar from '../stream/reader-popular-sites-sidebar'; +import ReaderStreamSidebar from './reader-stream-sidebar'; import { useFollowingView } from './view-preference'; import ViewToggle from './view-toggle'; import './style.scss'; -export const RECOMMENDED_SITES_SEED = Math.floor( Math.random() * 10001 ); - function FollowingStream( { ...props } ) { const { currentView } = useFollowingView(); const viewToggle = config.isEnabled( 'reader/recent-feed-overhaul' ) ? : null; @@ -58,42 +46,4 @@ function FollowingStream( { ...props } ) { ); } -function ReaderStreamSidebar(): JSX.Element | null { - const dispatch = useDispatch< Dispatch< RecommendedSitesRequestAction > >(); - const recommendedSites = useSelector( ( state ) => { - return getReaderRecommendedSites< RecommendedSite >( state, RECOMMENDED_SITES_SEED ) || []; - } ); - - useEffect( () => { - // Avoid fetching recommended sites if they are already present in the store. - if ( recommendedSites.length > 0 ) { - return; - } - - dispatch( requestRecommendedSites( { seed: RECOMMENDED_SITES_SEED, number: 10 } ) ); - }, [ dispatch, recommendedSites ] ); - - if ( recommendedSites.length === 0 ) { - return null; - } - - return ( - { - return { - blogId: s.blogId, - feed_ID: s.feedId, - feed_URL: s.feedUrl, - site_name: s.title, - site_description: s.description, - site_icon: s.icon, - url: s.url, - }; - } ) } - title={ translate( 'Popular sites' ) } - /> - ); -} - export default SuggestionProvider( withDimensions( FollowingStream ) ); diff --git a/client/reader/following/reader-stream-sidebar.tsx b/client/reader/following/reader-stream-sidebar.tsx new file mode 100644 index 0000000000000..5d9f150140721 --- /dev/null +++ b/client/reader/following/reader-stream-sidebar.tsx @@ -0,0 +1,54 @@ +import { translate } from 'i18n-calypso'; +import { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { Dispatch } from 'redux'; +import { RecommendedSite } from 'calypso/state/data-layer/wpcom/read/recommendations/sites'; +import { + RecommendedSitesRequestAction, + requestRecommendedSites, +} from 'calypso/state/reader/recommended-sites/actions'; +import { getReaderRecommendedSites } from 'calypso/state/reader/recommended-sites/selectors'; +import { READER_RECENT_SIDEBAR_POPULAR_SITES } from '../follow-sources'; +import ReaderPopularSitesSidebar from '../stream/reader-popular-sites-sidebar'; + +export const RECOMMENDED_SITES_SEED = Math.floor( Math.random() * 10001 ); + +function ReaderStreamSidebar(): JSX.Element | null { + const dispatch = useDispatch< Dispatch< RecommendedSitesRequestAction > >(); + const recommendedSites = useSelector( ( state ) => { + return getReaderRecommendedSites< RecommendedSite >( state, RECOMMENDED_SITES_SEED ) || []; + } ); + + useEffect( () => { + // Avoid fetching recommended sites if they are already present in the store. + if ( recommendedSites.length > 0 ) { + return; + } + + dispatch( requestRecommendedSites( { seed: RECOMMENDED_SITES_SEED, number: 10 } ) ); + }, [ dispatch, recommendedSites ] ); + + if ( recommendedSites.length === 0 ) { + return null; + } + + return ( + { + return { + blogId: s.blogId, + feed_ID: s.feedId, + feed_URL: s.feedUrl, + site_name: s.title, + site_description: s.description, + site_icon: s.icon, + url: s.url, + }; + } ) } + title={ translate( 'Popular sites' ) } + /> + ); +} + +export default ReaderStreamSidebar;