Attempt to speed up sidebar menu for mobile (#283)

* Exclude default homepage data at compile time

The youtuber IDs alone is pretty huge, and is unused in the `CUSTOM_HOMEPAGE=true` configuration.

* Remove Desktop items and other cleanup

- Moved constants out of the component.
- Remove SIMPLE_SITE check.
- Remove Desktop-only items

* Sidebar: limit subscription and tag section

Too slow for huge lists

Limit to 10 initially, and load everything on "Show more"

* Fix makeSelectThumbnailForUri

- Fix memo
- Expose function to extract directly from claim if client already have it.
This commit is contained in:
infinite-persistence 2021-11-12 23:59:11 +08:00 committed by zeppi
parent ea072febae
commit 049b1c727e
15 changed files with 68 additions and 54 deletions

View file

@ -1,7 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { import {
makeSelectTitleForUri, makeSelectTitleForUri,
makeSelectThumbnailForUri, selectThumbnailForUri,
makeSelectCoverForUri, makeSelectCoverForUri,
makeSelectMetadataItemForUri, makeSelectMetadataItemForUri,
makeSelectAmountForUri, makeSelectAmountForUri,
@ -22,7 +22,7 @@ import ChannelForm from './view';
const select = (state, props) => ({ const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state), claim: makeSelectClaimForUri(props.uri)(state),
title: makeSelectTitleForUri(props.uri)(state), title: makeSelectTitleForUri(props.uri)(state),
thumbnailUrl: makeSelectThumbnailForUri(props.uri)(state), thumbnailUrl: selectThumbnailForUri(state, props.uri),
coverUrl: makeSelectCoverForUri(props.uri)(state), coverUrl: makeSelectCoverForUri(props.uri)(state),
description: makeSelectMetadataItemForUri(props.uri, 'description')(state), description: makeSelectMetadataItemForUri(props.uri, 'description')(state),
website: makeSelectMetadataItemForUri(props.uri, 'website_url')(state), website: makeSelectMetadataItemForUri(props.uri, 'website_url')(state),

View file

@ -1,11 +1,11 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { makeSelectThumbnailForUri, makeSelectClaimForUri, makeSelectIsUriResolving } from 'redux/selectors/claims'; import { selectThumbnailForUri, selectClaimForUri, makeSelectIsUriResolving } from 'redux/selectors/claims';
import { doResolveUri } from 'redux/actions/claims'; import { doResolveUri } from 'redux/actions/claims';
import ChannelThumbnail from './view'; import ChannelThumbnail from './view';
const select = (state, props) => ({ const select = (state, props) => ({
thumbnail: makeSelectThumbnailForUri(props.uri)(state), thumbnail: selectThumbnailForUri(state, props.uri),
claim: makeSelectClaimForUri(props.uri)(state), claim: selectClaimForUri(state, props.uri),
isResolving: makeSelectIsUriResolving(props.uri)(state), isResolving: makeSelectIsUriResolving(props.uri)(state),
}); });

View file

@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import { import {
makeSelectClaimForUri, makeSelectClaimForUri,
makeSelectIsUriResolving, makeSelectIsUriResolving,
makeSelectThumbnailForUri, getThumbnailFromClaim,
makeSelectTitleForUri, makeSelectTitleForUri,
makeSelectChannelForClaimUri, makeSelectChannelForClaimUri,
makeSelectClaimIsNsfw, makeSelectClaimIsNsfw,
@ -27,7 +27,7 @@ const select = (state, props) => {
date: props.uri && selectDateForUri(state, props.uri), date: props.uri && selectDateForUri(state, props.uri),
channel: props.uri && makeSelectChannelForClaimUri(props.uri)(state), channel: props.uri && makeSelectChannelForClaimUri(props.uri)(state),
isResolvingUri: props.uri && makeSelectIsUriResolving(props.uri)(state), isResolvingUri: props.uri && makeSelectIsUriResolving(props.uri)(state),
thumbnail: props.uri && makeSelectThumbnailForUri(props.uri)(state), thumbnail: getThumbnailFromClaim(claim),
title: props.uri && makeSelectTitleForUri(props.uri)(state), title: props.uri && makeSelectTitleForUri(props.uri)(state),
banState: selectBanStateForUri(state, props.uri), banState: selectBanStateForUri(state, props.uri),
blockedChannelUris: selectMutedChannels(state), blockedChannelUris: selectMutedChannels(state),

View file

@ -1,7 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { import {
makeSelectTitleForUri, makeSelectTitleForUri,
makeSelectThumbnailForUri, selectThumbnailForUri,
makeSelectMetadataItemForUri, makeSelectMetadataItemForUri,
makeSelectAmountForUri, makeSelectAmountForUri,
makeSelectClaimForUri, makeSelectClaimForUri,
@ -26,7 +26,7 @@ import { doSetActiveChannel, doSetIncognito } from 'redux/actions/app';
const select = (state, props) => ({ const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state), claim: makeSelectClaimForUri(props.uri)(state),
title: makeSelectTitleForUri(props.uri)(state), title: makeSelectTitleForUri(props.uri)(state),
thumbnailUrl: makeSelectThumbnailForUri(props.uri)(state), thumbnailUrl: selectThumbnailForUri(state, props.uri),
description: makeSelectMetadataItemForUri(props.uri, 'description')(state), description: makeSelectMetadataItemForUri(props.uri, 'description')(state),
tags: makeSelectMetadataItemForUri(props.uri, 'tags')(state), tags: makeSelectMetadataItemForUri(props.uri, 'tags')(state),
locations: makeSelectMetadataItemForUri(props.uri, 'locations')(state), locations: makeSelectMetadataItemForUri(props.uri, 'locations')(state),

View file

@ -1,7 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { import {
makeSelectIsUriResolving, makeSelectIsUriResolving,
makeSelectThumbnailForUri, getThumbnailFromClaim,
makeSelectTitleForUri, makeSelectTitleForUri,
makeSelectChannelForClaimUri, makeSelectChannelForClaimUri,
makeSelectClaimIsNsfw, makeSelectClaimIsNsfw,
@ -40,7 +40,7 @@ const select = (state, props) => {
isResolvingCollectionClaims: makeSelectIsResolvingCollectionForId(collectionId)(state), isResolvingCollectionClaims: makeSelectIsResolvingCollectionForId(collectionId)(state),
channelClaim: collectionUri && makeSelectChannelForClaimUri(collectionUri)(state), channelClaim: collectionUri && makeSelectChannelForClaimUri(collectionUri)(state),
isResolvingUri: collectionUri && makeSelectIsUriResolving(collectionUri)(state), isResolvingUri: collectionUri && makeSelectIsUriResolving(collectionUri)(state),
thumbnail: collectionUri && makeSelectThumbnailForUri(collectionUri)(state), thumbnail: getThumbnailFromClaim(claim),
title: collectionUri && makeSelectTitleForUri(collectionUri)(state), title: collectionUri && makeSelectTitleForUri(collectionUri)(state),
blackListedOutpoints: selectBlackListedOutpoints(state), blackListedOutpoints: selectBlackListedOutpoints(state),
filteredOutpoints: selectFilteredOutpoints(state), filteredOutpoints: selectFilteredOutpoints(state),

View file

@ -2,7 +2,8 @@ import { connect } from 'react-redux';
import { import {
selectTotalStakedAmountForChannelUri, selectTotalStakedAmountForChannelUri,
makeSelectClaimForUri, makeSelectClaimForUri,
makeSelectThumbnailForUri, selectThumbnailForUri,
selectHasChannels,
selectMyChannelClaims, selectMyChannelClaims,
} from 'redux/selectors/claims'; } from 'redux/selectors/claims';
import { doCommentUpdate, doCommentList } from 'redux/actions/comments'; import { doCommentUpdate, doCommentList } from 'redux/actions/comments';
@ -25,11 +26,12 @@ const select = (state, props) => {
return { return {
claim: makeSelectClaimForUri(props.uri)(state), claim: makeSelectClaimForUri(props.uri)(state),
thumbnail: props.authorUri && makeSelectThumbnailForUri(props.authorUri)(state), thumbnail: props.authorUri && selectThumbnailForUri(state, props.authorUri),
channelIsBlocked: props.authorUri && makeSelectChannelIsMuted(props.authorUri)(state), channelIsBlocked: props.authorUri && makeSelectChannelIsMuted(props.authorUri)(state),
commentingEnabled: true, commentingEnabled: true,
othersReacts: selectOthersReactsForComment(state, reactionKey), othersReacts: selectOthersReactsForComment(state, reactionKey),
activeChannelClaim, activeChannelClaim,
hasChannels: selectHasChannels(state), //
myChannels: selectMyChannelClaims(state), myChannels: selectMyChannelClaims(state),
playingUri: selectPlayingUri(state), playingUri: selectPlayingUri(state),
stakedLevel: selectTotalStakedAmountForChannelUri(state, props.authorUri), stakedLevel: selectTotalStakedAmountForChannelUri(state, props.authorUri),

View file

@ -1,5 +1,5 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { makeSelectThumbnailForUri, makeSelectClaimForUri } from 'redux/selectors/claims'; import { selectThumbnailForUri, makeSelectClaimForUri } from 'redux/selectors/claims';
import { doResolveUri } from 'redux/actions/claims'; import { doResolveUri } from 'redux/actions/claims';
import * as SETTINGS from 'constants/settings'; import * as SETTINGS from 'constants/settings';
import { doFetchCostInfoForUri, makeSelectCostInfoForUri } from 'lbryinc'; import { doFetchCostInfoForUri, makeSelectCostInfoForUri } from 'lbryinc';
@ -11,7 +11,7 @@ import { makeSelectFileRenderModeForUri } from 'redux/selectors/content';
import ChannelThumbnail from './view'; import ChannelThumbnail from './view';
const select = (state, props) => ({ const select = (state, props) => ({
thumbnail: makeSelectThumbnailForUri(props.uri)(state), thumbnail: selectThumbnailForUri(state, props.uri),
claim: makeSelectClaimForUri(props.uri)(state), claim: makeSelectClaimForUri(props.uri)(state),
floatingPlayerEnabled: makeSelectClientSetting(SETTINGS.FLOATING_PLAYER)(state), floatingPlayerEnabled: makeSelectClientSetting(SETTINGS.FLOATING_PLAYER)(state),
costInfo: makeSelectCostInfoForUri(props.uri)(state), costInfo: makeSelectCostInfoForUri(props.uri)(state),

View file

@ -1,6 +1,6 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { makeSelectDownloadPathForUri, makeSelectStreamingUrlForUri } from 'redux/selectors/file_info'; import { makeSelectDownloadPathForUri, makeSelectStreamingUrlForUri } from 'redux/selectors/file_info';
import { makeSelectClaimForUri, makeSelectThumbnailForUri, makeSelectContentTypeForUri } from 'redux/selectors/claims'; import { makeSelectClaimForUri, selectThumbnailForUri, makeSelectContentTypeForUri } from 'redux/selectors/claims';
import * as SETTINGS from 'constants/settings'; import * as SETTINGS from 'constants/settings';
import { makeSelectClientSetting } from 'redux/selectors/settings'; import { makeSelectClientSetting } from 'redux/selectors/settings';
import { makeSelectFileRenderModeForUri, makeSelectFileExtensionForUri } from 'redux/selectors/content'; import { makeSelectFileRenderModeForUri, makeSelectFileExtensionForUri } from 'redux/selectors/content';
@ -11,7 +11,7 @@ const select = (state, props) => {
return { return {
currentTheme: makeSelectClientSetting(SETTINGS.THEME)(state), currentTheme: makeSelectClientSetting(SETTINGS.THEME)(state),
claim: makeSelectClaimForUri(props.uri)(state), claim: makeSelectClaimForUri(props.uri)(state),
thumbnail: makeSelectThumbnailForUri(props.uri)(state), thumbnail: selectThumbnailForUri(state, props.uri),
contentType: makeSelectContentTypeForUri(props.uri)(state), contentType: makeSelectContentTypeForUri(props.uri)(state),
downloadPath: makeSelectDownloadPathForUri(props.uri)(state), downloadPath: makeSelectDownloadPathForUri(props.uri)(state),
fileExtension: makeSelectFileExtensionForUri(props.uri)(state), fileExtension: makeSelectFileExtensionForUri(props.uri)(state),

View file

@ -1,6 +1,6 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { doPlayUri, doSetPlayingUri, doSetPrimaryUri } from 'redux/actions/content'; import { doPlayUri, doSetPlayingUri, doSetPrimaryUri } from 'redux/actions/content';
import { makeSelectThumbnailForUri, makeSelectClaimForUri, makeSelectClaimWasPurchased } from 'redux/selectors/claims'; import { selectThumbnailForUri, makeSelectClaimForUri, makeSelectClaimWasPurchased } from 'redux/selectors/claims';
import { makeSelectFileInfoForUri } from 'redux/selectors/file_info'; import { makeSelectFileInfoForUri } from 'redux/selectors/file_info';
import * as SETTINGS from 'constants/settings'; import * as SETTINGS from 'constants/settings';
import * as COLLECTIONS_CONSTS from 'constants/collections'; import * as COLLECTIONS_CONSTS from 'constants/collections';
@ -22,7 +22,7 @@ const select = (state, props) => {
const collectionId = urlParams.get(COLLECTIONS_CONSTS.COLLECTION_ID); const collectionId = urlParams.get(COLLECTIONS_CONSTS.COLLECTION_ID);
return { return {
claimThumbnail: makeSelectThumbnailForUri(props.uri)(state), claimThumbnail: selectThumbnailForUri(state, props.uri),
fileInfo: makeSelectFileInfoForUri(props.uri)(state), fileInfo: makeSelectFileInfoForUri(props.uri)(state),
obscurePreview: makeSelectShouldObscurePreview(props.uri)(state), obscurePreview: makeSelectShouldObscurePreview(props.uri)(state),
isPlaying: makeSelectIsPlaying(props.uri)(state), isPlaying: makeSelectIsPlaying(props.uri)(state),

View file

@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import { import {
selectClaimIsMine, selectClaimIsMine,
makeSelectTitleForUri, makeSelectTitleForUri,
makeSelectThumbnailForUri, getThumbnailFromClaim,
selectClaimForUri, selectClaimForUri,
makeSelectIsUriResolving, makeSelectIsUriResolving,
makeSelectMetadataItemForUri, makeSelectMetadataItemForUri,
@ -18,7 +18,7 @@ const select = (state, props) => {
uri: props.uri, uri: props.uri,
claim, claim,
title: makeSelectTitleForUri(props.uri)(state), title: makeSelectTitleForUri(props.uri)(state),
thumbnail: makeSelectThumbnailForUri(props.uri)(state), thumbnail: getThumbnailFromClaim(claim),
description: makeSelectMetadataItemForUri(props.uri, 'description')(state), description: makeSelectMetadataItemForUri(props.uri, 'description')(state),
channelIsMine: selectClaimIsMine(state, claim), channelIsMine: selectClaimIsMine(state, claim),
isResolvingUri: makeSelectIsUriResolving(props.uri)(state), isResolvingUri: makeSelectIsUriResolving(props.uri)(state),

View file

@ -1,5 +1,5 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { makeSelectClaimForUri, makeSelectThumbnailForUri } from 'redux/selectors/claims'; import { makeSelectClaimForUri, selectThumbnailForUri } from 'redux/selectors/claims';
import { import {
makeSelectNextUrlForCollectionAndUrl, makeSelectNextUrlForCollectionAndUrl,
makeSelectPreviousUrlForCollectionAndUrl, makeSelectPreviousUrlForCollectionAndUrl,
@ -56,7 +56,7 @@ const select = (state, props) => {
volume: selectVolume(state), volume: selectVolume(state),
muted: selectMute(state), muted: selectMute(state),
videoPlaybackRate: makeSelectClientSetting(SETTINGS.VIDEO_PLAYBACK_RATE)(state), videoPlaybackRate: makeSelectClientSetting(SETTINGS.VIDEO_PLAYBACK_RATE)(state),
thumbnail: makeSelectThumbnailForUri(uri)(state), thumbnail: selectThumbnailForUri(state, uri),
claim: makeSelectClaimForUri(uri)(state), claim: makeSelectClaimForUri(uri)(state),
homepageData: selectHomepageData(state), homepageData: selectHomepageData(state),
shareTelemetry: selectDaemonSettings(state).share_usage_data, shareTelemetry: selectDaemonSettings(state).share_usage_data,

View file

@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import { import {
selectClaimIsMine, selectClaimIsMine,
makeSelectTitleForUri, makeSelectTitleForUri,
makeSelectThumbnailForUri, getThumbnailFromClaim,
makeSelectCoverForUri, makeSelectCoverForUri,
selectCurrentChannelPage, selectCurrentChannelPage,
selectClaimForUri, selectClaimForUri,
@ -22,7 +22,7 @@ const select = (state, props) => {
return { return {
title: makeSelectTitleForUri(props.uri)(state), title: makeSelectTitleForUri(props.uri)(state),
thumbnail: makeSelectThumbnailForUri(props.uri)(state), thumbnail: getThumbnailFromClaim(claim),
cover: makeSelectCoverForUri(props.uri)(state), cover: makeSelectCoverForUri(props.uri)(state),
channelIsMine: selectClaimIsMine(state, claim), channelIsMine: selectClaimIsMine(state, claim),
page: selectCurrentChannelPage(state), page: selectCurrentChannelPage(state),

View file

@ -4,7 +4,7 @@ import { withRouter } from 'react-router-dom';
import CollectionPage from './view'; import CollectionPage from './view';
import { import {
makeSelectTitleForUri, makeSelectTitleForUri,
makeSelectThumbnailForUri, getThumbnailFromClaim,
selectClaimIsMine, selectClaimIsMine,
makeSelectClaimIsPending, makeSelectClaimIsPending,
makeSelectClaimForClaimId, makeSelectClaimForClaimId,
@ -39,7 +39,7 @@ const select = (state, props) => {
collectionCount: makeSelectCountForCollectionId(collectionId)(state), collectionCount: makeSelectCountForCollectionId(collectionId)(state),
isResolvingCollection: makeSelectIsResolvingCollectionForId(collectionId)(state), isResolvingCollection: makeSelectIsResolvingCollectionForId(collectionId)(state),
title: makeSelectTitleForUri(uri)(state), title: makeSelectTitleForUri(uri)(state),
thumbnail: makeSelectThumbnailForUri(uri)(state), thumbnail: getThumbnailFromClaim(claim),
isMyClaim: selectClaimIsMine(state, claim), // or collection is mine? isMyClaim: selectClaimIsMine(state, claim), // or collection is mine?
isMyCollection: makeSelectCollectionIsMine(collectionId)(state), isMyCollection: makeSelectCollectionIsMine(collectionId)(state),
claimIsPending: makeSelectClaimIsPending(uri)(state), claimIsPending: makeSelectClaimIsPending(uri)(state),

View file

@ -388,11 +388,14 @@ export const makeSelectContentTypeForUri = (uri: string) =>
return source ? source.media_type : undefined; return source ? source.media_type : undefined;
}); });
export const makeSelectThumbnailForUri = (uri: string) => export const getThumbnailFromClaim = (claim: Claim) => {
createSelector(makeSelectClaimForUri(uri), (claim) => { const thumbnail = claim && claim.value && claim.value.thumbnail;
const thumbnail = claim && claim.value && claim.value.thumbnail; return thumbnail && thumbnail.url ? thumbnail.url.trim().replace(/^http:\/\//i, 'https://') : undefined;
return thumbnail && thumbnail.url ? thumbnail.url.trim().replace(/^http:\/\//i, 'https://') : undefined; };
});
export const selectThumbnailForUri = createCachedSelector(selectClaimForUri, (claim) => {
return getThumbnailFromClaim(claim);
})((state, uri) => String(uri));
export const makeSelectCoverForUri = (uri: string) => export const makeSelectCoverForUri = (uri: string) =>
createSelector(makeSelectClaimForUri(uri), (claim) => { createSelector(makeSelectClaimForUri(uri), (claim) => {

View file

@ -144,6 +144,32 @@ export function GetLinksData(
let rowData: Array<RowDataItem> = []; let rowData: Array<RowDataItem> = [];
const individualTagDataItems: Array<RowDataItem> = []; const individualTagDataItems: Array<RowDataItem> = [];
if (isHomepage && showPersonalizedChannels && subscribedChannels) {
const RECENT_FROM_FOLLOWING = {
title: __('Recent From Following'),
link: `/$/${PAGES.CHANNELS_FOLLOWING}`,
icon: ICONS.SUBSCRIBE,
options: {
orderBy: CS.ORDER_BY_NEW_VALUE,
releaseTime:
subscribedChannels.length > 20
? `>${Math.floor(moment().subtract(9, 'months').startOf('week').unix())}`
: `>${Math.floor(moment().subtract(1, 'year').startOf('week').unix())}`,
pageSize: getPageSize(subscribedChannels.length > 3 ? (subscribedChannels.length > 6 ? 16 : 8) : 4),
streamTypes: null,
channelIds: subscribedChannels.map((subscription: Subscription) => {
const { channelClaimId } = parseURI(subscription.uri);
if (channelClaimId) return channelClaimId;
}),
},
};
// $FlowFixMe flow thinks this might not be Array<string>
rowData.push(RECENT_FROM_FOLLOWING);
}
// **************************************************************************
// @if CUSTOM_HOMEPAGE='false'
const YOUTUBER_CHANNEL_IDS = [ const YOUTUBER_CHANNEL_IDS = [
'fb364ef587872515f545a5b4b3182b58073f230f', 'fb364ef587872515f545a5b4b3182b58073f230f',
'589276465a23c589801d874f484cc39f307d7ec7', '589276465a23c589801d874f484cc39f307d7ec7',
@ -274,28 +300,6 @@ export function GetLinksData(
}, },
}; };
if (isHomepage && showPersonalizedChannels && subscribedChannels) {
const RECENT_FROM_FOLLOWING = {
title: __('Recent From Following'),
link: `/$/${PAGES.CHANNELS_FOLLOWING}`,
icon: ICONS.SUBSCRIBE,
options: {
orderBy: CS.ORDER_BY_NEW_VALUE,
releaseTime:
subscribedChannels.length > 20
? `>${Math.floor(moment().subtract(9, 'months').startOf('week').unix())}`
: `>${Math.floor(moment().subtract(1, 'year').startOf('week').unix())}`,
pageSize: getPageSize(subscribedChannels.length > 3 ? (subscribedChannels.length > 6 ? 16 : 8) : 4),
streamTypes: null,
channelIds: subscribedChannels.map((subscription: Subscription) => {
const { channelClaimId } = parseURI(subscription.uri);
if (channelClaimId) return channelClaimId;
}),
},
};
// $FlowFixMe flow thinks this might not be Array<string>
rowData.push(RECENT_FROM_FOLLOWING);
}
if (isHomepage && !CUSTOM_HOMEPAGE) { if (isHomepage && !CUSTOM_HOMEPAGE) {
if (followedTags) { if (followedTags) {
const TRENDING_FOR_TAGS = { const TRENDING_FOR_TAGS = {
@ -330,6 +334,7 @@ export function GetLinksData(
} }
} }
} }
if (!CUSTOM_HOMEPAGE) { if (!CUSTOM_HOMEPAGE) {
if (!authenticated) { if (!authenticated) {
rowData.push(YOUTUBE_CREATOR_ROW); rowData.push(YOUTUBE_CREATOR_ROW);
@ -338,6 +343,10 @@ export function GetLinksData(
rowData.push(LATEST_FROM_LBRY); rowData.push(LATEST_FROM_LBRY);
if (!showPersonalizedChannels) rowData.push(TOP_CHANNELS); if (!showPersonalizedChannels) rowData.push(TOP_CHANNELS);
} }
// @endif
// **************************************************************************
// TODO: provide better method for exempting from homepage // TODO: provide better method for exempting from homepage
(Object.values(all): any) (Object.values(all): any)
.filter((row) => !(isHomepage && row.name === 'news')) .filter((row) => !(isHomepage && row.name === 'news'))