diff --git a/config.js b/config.js index 85a18883d..d1cd84164 100644 --- a/config.js +++ b/config.js @@ -52,6 +52,7 @@ const config = { STRIPE_PUBLIC_KEY: process.env.STRIPE_PUBLIC_KEY, ENABLE_UI_NOTIFICATIONS: process.env.ENABLE_UI_NOTIFICATIONS === 'true', ENABLE_MATURE: process.env.ENABLE_MATURE === 'true', + CUSTOM_HOMEPAGE: process.env.CUSTOM_HOMEPAGE === 'true', }; config.URL_LOCAL = `http://localhost:${config.WEB_SERVER_PORT}`; diff --git a/flow-typed/homepage.js b/flow-typed/homepage.js index ee4b1226f..0ea62495e 100644 --- a/flow-typed/homepage.js +++ b/flow-typed/homepage.js @@ -20,7 +20,7 @@ declare type RowDataItem = { options?: { channelIds?: Array, limitClaimsPerChannel?: number, - pageSize: number, + pageSize?: number, }, route?: string, hideForUnauth?: boolean, diff --git a/homepages/homepage.js b/homepages/homepage.js index 48181bcf1..36b893b47 100644 --- a/homepages/homepage.js +++ b/homepages/homepage.js @@ -1,38 +1,7 @@ -// @flow -import * as PAGES from 'constants/pages'; -import * as ICONS from 'constants/icons'; -import * as CS from 'constants/claim_search'; -import { parseURI } from 'lbry-redux'; -import moment from 'moment'; -import { toCapitalCase } from 'util/string'; -import { useIsLargeScreen } from 'effects/use-screensize'; +import * as PAGES from '../ui/constants/pages'; +import * as CS from '../ui/constants/claim_search'; -export type RowDataItem = { - title: string, - link?: string, - help?: any, - options?: {}, - icon?: string, -}; - -export default function GetHomePageRowData( - authenticated: boolean, - showPersonalizedChannels: boolean, - showPersonalizedTags: boolean, - subscribedChannels: Array, - followedTags: Array, - showIndividualTags: boolean, - showNsfw: boolean -) { - const isLargeScreen = useIsLargeScreen(); - - function getPageSize(originalSize) { - return isLargeScreen ? originalSize * (3 / 2) : originalSize; - } - - let rowData: Array = []; - const individualTagDataItems: Array = []; - const YOUTUBER_CHANNEL_IDS = [ +const YOUTUBER_CHANNEL_IDS = [ 'fb364ef587872515f545a5b4b3182b58073f230f', '589276465a23c589801d874f484cc39f307d7ec7', 'ba79c80788a9e1751e49ad401f5692d86f73a2db', @@ -116,142 +85,18 @@ export default function GetHomePageRowData( 'e8f68563d242f6ac9784dcbc41dd86c28a9391d6', ]; - const YOUTUBE_CREATOR_ROW = { - title: __('CableTube Escape Artists'), - link: `/$/${PAGES.DISCOVER}?${CS.CLAIM_TYPE}=${CS.CLAIM_STREAM}&${CS.CHANNEL_IDS_KEY}=${YOUTUBER_CHANNEL_IDS.join( - ',' - )}`, - options: { - claimType: ['stream'], - orderBy: ['release_time'], - pageSize: getPageSize(12), - channelIds: YOUTUBER_CHANNEL_IDS, - limitClaimsPerChannel: 1, - releaseTime: `>${Math.floor(moment().subtract(1, 'months').startOf('week').unix())}`, - }, + const YOUTUBERS = { + ids: YOUTUBER_CHANNEL_IDS, + link: `/$/${PAGES.DISCOVER}?${CS.CLAIM_TYPE}=${CS.CLAIM_STREAM}&${CS.CHANNEL_IDS_KEY}=${YOUTUBER_CHANNEL_IDS.join( + ',' + )}`, + name: 'general', + label: 'CableTube Escape Artists', + channelLimit: 1, + daysOfContent: 30, + pageSize: 24, + //pinnedUrls: [], + //mixIn: [], }; - if (followedTags.length) { - followedTags.forEach((tag: Tag) => { - const tagName = `#${toCapitalCase(tag.name)}`; - individualTagDataItems.push({ - title: __('Trending for %tagName%', { tagName: tagName }), - link: `/$/${PAGES.DISCOVER}?t=${tag.name}`, - options: { - pageSize: 4, - tags: [tag.name], - claimType: ['stream'], - }, - }); - }); - } - - const RECENT_FROM_FOLLOWING = { - title: __('Recent From Following'), - link: `/$/${PAGES.CHANNELS_FOLLOWING}`, - icon: ICONS.SUBSCRIBE, - options: { - streamTypes: null, - orderBy: ['release_time'], - releaseTime: - subscribedChannels.length > 20 - ? `>${Math.floor(moment().subtract(6, 'months').startOf('week').unix())}` - : `>${Math.floor(moment().subtract(1, 'year').startOf('week').unix())}`, - pageSize: getPageSize(subscribedChannels.length > 3 ? (subscribedChannels.length > 6 ? 16 : 8) : 4), - channelIds: subscribedChannels.map((subscription: Subscription) => { - const { channelClaimId } = parseURI(subscription.uri); - return channelClaimId; - }), - }, - }; - - const TOP_CONTENT_TODAY = { - title: __('Top Content from Today'), - link: `/$/${PAGES.DISCOVER}?${CS.ORDER_BY_KEY}=${CS.ORDER_BY_TOP}&${CS.FRESH_KEY}=${CS.FRESH_DAY}`, - options: { - pageSize: getPageSize(showPersonalizedChannels || showPersonalizedTags ? 4 : 8), - orderBy: ['effective_amount'], - claimType: ['stream'], - limitClaimsPerChannel: 2, - releaseTime: `>${Math.floor(moment().subtract(1, 'day').startOf('day').unix())}`, - }, - }; - - const TOP_CHANNELS = { - title: __('Top Channels On LBRY'), - link: `/$/${PAGES.DISCOVER}?claim_type=channel&${CS.ORDER_BY_KEY}=${CS.ORDER_BY_TOP}&${CS.FRESH_KEY}=${CS.FRESH_ALL}`, - options: { - orderBy: ['effective_amount'], - claimType: ['channel'], - }, - }; - - // const TRENDING_CLASSICS = { - // title: __('Trending Classics'), - // link: `/$/${PAGES.DISCOVER}?${CS.ORDER_BY_KEY}=${CS.ORDER_BY_TRENDING}&${CS.FRESH_KEY}=${CS.FRESH_WEEK}`, - // options: { - // pageSize: getPageSize(4), - // claimType: ['stream'], - // limitClaimsPerChannel: 1, - // releaseTime: `<${Math.floor( - // moment() - // .subtract(6, 'month') - // .startOf('day') - // .unix() - // )}`, - // }, - // }; - - // const TRENDING_ON_LBRY = { - // title: __('Trending On LBRY'), - // link: `/$/${PAGES.DISCOVER}`, - // options: { - // pageSize: showPersonalizedChannels || showPersonalizedTags ? 4 : 8, - // }, - // }; - - const TRENDING_FOR_TAGS = { - title: __('Trending For Your Tags'), - link: `/$/${PAGES.TAGS_FOLLOWING}`, - icon: ICONS.TAG, - - options: { - pageSize: getPageSize(4), - tags: followedTags.map((tag) => tag.name), - claimType: ['stream'], - limitClaimsPerChannel: 2, - }, - }; - - const LATEST_FROM_LBRY = { - title: __('Latest From @lbry'), - link: `/@lbry:3f`, - options: { - orderBy: ['release_time'], - pageSize: getPageSize(4), - channelIds: ['3fda836a92faaceedfe398225fb9b2ee2ed1f01a'], - }, - }; - - if (showPersonalizedChannels) rowData.push(RECENT_FROM_FOLLOWING); - if (showPersonalizedTags && !showIndividualTags) rowData.push(TRENDING_FOR_TAGS); - if (showPersonalizedTags && showIndividualTags) { - individualTagDataItems.forEach((item: RowDataItem) => { - rowData.push(item); - }); - } - - if (!authenticated) { - rowData.push(YOUTUBE_CREATOR_ROW); - } - - rowData.push(TOP_CONTENT_TODAY); - - // rowData.push(TRENDING_ON_LBRY); - - rowData.push(LATEST_FROM_LBRY); - - if (!showPersonalizedChannels) rowData.push(TOP_CHANNELS); - - return rowData; -} +module.exports = { YOUTUBERS }; diff --git a/homepages/index.js b/homepages/index.js index 2a89d99bb..53cf98a40 100644 --- a/homepages/index.js +++ b/homepages/index.js @@ -1,5 +1,3 @@ -import * as lbrytv from './homepage'; -// import all homepages -// export object of homepages -export default {'en': lbrytv}; -export const NO_ADS_CHANNEL_IDS = []; +module.exports = { + en: {}, +}; diff --git a/static/app-strings.json b/static/app-strings.json index 1aa778384..e3849098f 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -2042,5 +2042,7 @@ "Tip Creators": "Tip Creators", "Only select creators can receive tips at this time": "Only select creators can receive tips at this time", "The payment will be made from your saved card": "The payment will be made from your saved card", + "A channel is required to comment on lbry.tv": "A channel is required to comment on lbry.tv", + "Commenting...": "Commenting...", "--end--": "--end--" } diff --git a/ui/component/router/view.jsx b/ui/component/router/view.jsx index c84483ea4..94d5251e0 100644 --- a/ui/component/router/view.jsx +++ b/ui/component/router/view.jsx @@ -8,6 +8,7 @@ import { LINKED_COMMENT_QUERY_PARAM } from 'constants/comment'; import { parseURI, isURIValid } from 'lbry-redux'; import { SITE_TITLE, WELCOME_VERSION, SIMPLE_SITE } from 'config'; import LoadingBarOneOff from 'component/loadingBarOneOff'; +import { GetLinksData } from 'util/buildHomepage'; import HomePage from 'page/home'; @@ -21,7 +22,9 @@ const Code2257Page = lazyImport(() => import('web/page/code2257' /* webpackChunk // Chunk: "secondary" const SignInPage = lazyImport(() => import('page/signIn' /* webpackChunkName: "secondary" */)); -const SignInWalletPasswordPage = lazyImport(() => import('page/signInWalletPassword' /* webpackChunkName: "secondary" */)); +const SignInWalletPasswordPage = lazyImport(() => + import('page/signInWalletPassword' /* webpackChunkName: "secondary" */) +); const SignUpPage = lazyImport(() => import('page/signUp' /* webpackChunkName: "secondary" */)); const SignInVerifyPage = lazyImport(() => import('page/signInVerify' /* webpackChunkName: "secondary" */)); @@ -36,7 +39,9 @@ const WalletPage = lazyImport(() => import('page/wallet' /* webpackChunkName: "s const NotificationsPage = lazyImport(() => import('page/notifications' /* webpackChunkName: "secondary" */)); const CollectionPage = lazyImport(() => import('page/collection' /* webpackChunkName: "secondary" */)); const ChannelNew = lazyImport(() => import('page/channelNew' /* webpackChunkName: "secondary" */)); -const ChannelsFollowingDiscoverPage = lazyImport(() => import('page/channelsFollowingDiscover' /* webpackChunkName: "secondary" */)); +const ChannelsFollowingDiscoverPage = lazyImport(() => + import('page/channelsFollowingDiscover' /* webpackChunkName: "secondary" */) +); const ChannelsFollowingPage = lazyImport(() => import('page/channelsFollowing' /* webpackChunkName: "secondary" */)); const ChannelsPage = lazyImport(() => import('page/channels' /* webpackChunkName: "secondary" */)); const CheckoutPage = lazyImport(() => import('page/checkoutPage' /* webpackChunkName: "checkoutPage" */)); @@ -65,10 +70,14 @@ const SearchPage = lazyImport(() => import('page/search' /* webpackChunkName: "s const SettingsAdvancedPage = lazyImport(() => import('page/settingsAdvanced' /* webpackChunkName: "secondary" */)); const SettingsStripeCard = lazyImport(() => import('page/settingsStripeCard' /* webpackChunkName: "secondary" */)); const SettingsCreatorPage = lazyImport(() => import('page/settingsCreator' /* webpackChunkName: "secondary" */)); -const SettingsNotificationsPage = lazyImport(() => import('page/settingsNotifications' /* webpackChunkName: "secondary" */)); +const SettingsNotificationsPage = lazyImport(() => + import('page/settingsNotifications' /* webpackChunkName: "secondary" */) +); const SettingsPage = lazyImport(() => import('page/settings' /* webpackChunkName: "secondary" */)); const ShowPage = lazyImport(() => import('page/show' /* webpackChunkName: "secondary" */)); -const TagsFollowingManagePage = lazyImport(() => import('page/tagsFollowingManage' /* webpackChunkName: "secondary" */)); +const TagsFollowingManagePage = lazyImport(() => + import('page/tagsFollowingManage' /* webpackChunkName: "secondary" */) +); const TagsFollowingPage = lazyImport(() => import('page/tagsFollowing' /* webpackChunkName: "secondary" */)); const TopPage = lazyImport(() => import('page/top' /* webpackChunkName: "secondary" */)); const Welcome = lazyImport(() => import('page/welcome' /* webpackChunkName: "secondary" */)); @@ -150,7 +159,7 @@ function AppRouter(props: Props) { const resetScroll = urlParams.get('reset_scroll'); const hasLinkedCommentInUrl = urlParams.get(LINKED_COMMENT_QUERY_PARAM); - const dynamicRoutes = Object.values(homepageData).filter( + const dynamicRoutes = GetLinksData(homepageData).filter( (potentialRoute: any) => potentialRoute && potentialRoute.route ); diff --git a/ui/component/sideNavigation/view.jsx b/ui/component/sideNavigation/view.jsx index bc443a689..6ab12f2eb 100644 --- a/ui/component/sideNavigation/view.jsx +++ b/ui/component/sideNavigation/view.jsx @@ -9,15 +9,8 @@ import Icon from 'component/common/icon'; import NotificationBubble from 'component/notificationBubble'; import I18nMessage from 'component/i18nMessage'; import ChannelThumbnail from 'component/channelThumbnail'; -import { - PINNED_LABEL_1, - PINNED_URI_1, - PINNED_URI_2, - PINNED_LABEL_2, - SIMPLE_SITE, - DOMAIN, - ENABLE_UI_NOTIFICATIONS, -} from 'config'; +import { GetLinksData } from 'util/buildHomepage'; +import { SIMPLE_SITE, DOMAIN, ENABLE_UI_NOTIFICATIONS } from 'config'; // @if TARGET='app' import { IS_MAC } from 'component/app/view'; // @endif @@ -84,8 +77,7 @@ function SideNavigation(props: Props) { followedTags, } = props; - const { EXTRA_SIDEBAR_LINKS } = homepageData; - + const EXTRA_SIDEBAR_LINKS = GetLinksData(homepageData); const FULL_LINKS: Array = [ { title: 'Your Tags', @@ -205,22 +197,6 @@ function SideNavigation(props: Props) { }, ]; - if (PINNED_URI_1 && PINNED_LABEL_1) { - MOBILE_LINKS.push({ - title: PINNED_LABEL_1, - link: PINNED_URI_1, - icon: ICONS.PINNED, - }); - } - - if (PINNED_URI_2 && PINNED_LABEL_2) { - MOBILE_LINKS.push({ - title: PINNED_LABEL_2, - link: PINNED_URI_2, - icon: ICONS.PINNED, - }); - } - const notificationsEnabled = ENABLE_UI_NOTIFICATIONS || (user && user.experimental_ui); const isAuthenticated = Boolean(email); // SIDE LINKS: FOLLOWING, HOME, [FULL,] [EXTRA] @@ -247,8 +223,17 @@ function SideNavigation(props: Props) { }); } - if (EXTRA_SIDEBAR_LINKS) { + if (SIMPLE_SITE && EXTRA_SIDEBAR_LINKS) { + // $FlowFixMe SIDE_LINKS.push(...EXTRA_SIDEBAR_LINKS); + + const WILD_WEST = { + title: 'Wild West', + link: `/$/${PAGES.WILD_WEST}`, + icon: ICONS.WILD_WEST, + }; + + SIDE_LINKS.push(WILD_WEST); } const [pulseLibrary, setPulseLibrary] = React.useState(false); diff --git a/ui/page/home/view.jsx b/ui/page/home/view.jsx index 175f4b817..580d60b49 100644 --- a/ui/page/home/view.jsx +++ b/ui/page/home/view.jsx @@ -12,6 +12,7 @@ import I18nMessage from 'component/i18nMessage'; import LbcSymbol from 'component/common/lbc-symbol'; import WaitUntilOnPage from 'component/common/wait-until-on-page'; import useGetLivestreams from 'effects/use-get-livestreams'; +import { GetLinksData } from 'util/buildHomepage'; // @if TARGET='web' import Pixel from 'web/component/pixel'; @@ -30,10 +31,11 @@ function HomePage(props: Props) { const showPersonalizedChannels = (authenticated || !IS_WEB) && subscribedChannels && subscribedChannels.length > 0; const showPersonalizedTags = (authenticated || !IS_WEB) && followedTags && followedTags.length > 0; const showIndividualTags = showPersonalizedTags && followedTags.length < 5; - const { default: getHomepage } = homepageData; const { livestreamMap } = useGetLivestreams(); - const rowData: Array = getHomepage( + const rowData: Array = GetLinksData( + homepageData, + true, authenticated, showPersonalizedChannels, showPersonalizedTags, @@ -58,7 +60,7 @@ function HomePage(props: Props) { livestreamMap={livestreamMap} showNoSourceClaims={ENABLE_NO_SOURCE_CLAIMS} hasSource - pin={route === `/$/${PAGES.GENERAL}`} + pin={route === `/$/${PAGES.GENERAL}`} // use pinUrls here /> ); @@ -139,6 +141,7 @@ function HomePage(props: Props) { )} {rowData.map(({ title, route, link, icon, help, options = {} }, index) => { + // add pins here return getRowElements(title, route, link, icon, help, options, index); })} {/* @if TARGET='web' */} diff --git a/ui/redux/selectors/settings.js b/ui/redux/selectors/settings.js index b18b83c89..c10a6b052 100644 --- a/ui/redux/selectors/settings.js +++ b/ui/redux/selectors/settings.js @@ -1,8 +1,8 @@ import { SETTINGS, DAEMON_SETTINGS } from 'lbry-redux'; import { createSelector } from 'reselect'; -import homepages from 'homepages'; import { ENABLE_MATURE } from 'config'; import { getDefaultHomepageKey, getDefaultLanguage } from 'util/default-languages'; +const homepages = require('homepages'); const selectState = (state) => state.settings || {}; @@ -70,7 +70,8 @@ export const selectHomepageData = createSelector( selectHomepageCode, (homepageCode) => { // homepages = { 'en': homepageFile, ... } - return homepages[homepageCode] || homepages['en']; + // mixin Homepages here + return homepages[homepageCode] || homepages['en'] || {}; } ); diff --git a/ui/util/buildHomepage.js b/ui/util/buildHomepage.js new file mode 100644 index 000000000..aa421a3e2 --- /dev/null +++ b/ui/util/buildHomepage.js @@ -0,0 +1,324 @@ +// @flow +import * as PAGES from 'constants/pages'; +import * as ICONS from 'constants/icons'; +import * as CS from 'constants/claim_search'; +import { parseURI } from 'lbry-redux'; +import moment from 'moment'; +import { toCapitalCase } from 'util/string'; +import { useIsLargeScreen } from 'effects/use-screensize'; +import { CUSTOM_HOMEPAGE } from 'config'; + +export type RowDataItem = { + title: any, + link?: string, + help?: any, + icon?: string, + extra?: any, + options?: { + channelIds?: Array, + pageSize?: number, + limitClaimsPerChannel?: number, + }, + route?: string, + hideForUnauth?: boolean, +}; + +export type HomepageCat = { + name: string, + icon: string, + label: string, + channelIds?: Array, + daysOfContent?: number, + channelLimit?: string, + pageSize?: number, + claimType?: string, + order?: string, + tags?: Array, + pinnedUrls?: Array, + mixIn?: Array, +}; + +function getLimitPerChannel(size, isChannel) { + if (isChannel) { + return 1; + } else { + return size < 250 ? (size < 150 ? 3 : 2) : 1; + } +} + +export const getHomepageRowForCat = (cat: HomepageCat) => { + let orderValue; + switch (cat.order) { + case 'trending': + orderValue = CS.ORDER_BY_TRENDING_VALUE; + break; + case 'top': + orderValue = CS.ORDER_BY_TOP_VALUE; + break; + default: + orderValue = CS.ORDER_BY_NEW_VALUE; + } + + let urlParams = new URLSearchParams(); + if (cat.claimType) { + urlParams.set(CS.CLAIM_TYPE, cat.claimType); + } + if (cat.channelIds) { + urlParams.set(CS.CHANNEL_IDS_KEY, cat.channelIds.join(',')); + } + + const isChannelType = cat.claimType && cat.claimType === 'channel'; + + // can intend no limit, numerica auto limit, specific limit. + let limitClaims; + if (typeof cat.channelLimit === 'string' && cat.channelIds && cat.channelIds.length) { + if (cat.channelLimit === 'auto') { + limitClaims = getLimitPerChannel(cat.channelIds.length, isChannelType); + } else if (cat.channelLimit) { + const limitNumber = Number(cat.channelLimit); + // eslint-disable-next-line + if (limitNumber === limitNumber && limitNumber !== 0) { + // because javascript and NaN !== NaN + limitClaims = Math.floor(limitNumber); + } + } + } + + return { + link: `/$/${PAGES.DISCOVER}?${urlParams.toString()}`, + route: cat.name ? `/$/${cat.name}` : undefined, + icon: cat.icon || '', // some default + title: cat.label, + options: { + claimType: cat.claimType || 'stream', + channelIds: cat.channelIds, + orderBy: orderValue, + pageSize: cat.pageSize || undefined, + limitClaimsPerChannel: limitClaims, + releaseTime: `>${Math.floor( + moment() + .subtract(cat.daysOfContent || 30, 'days') + .startOf('week') + .unix() + )}`, + }, + }; +}; + +export function GetLinksData( + all: any, + isHomepage?: boolean = false, + authenticated?: boolean, + showPersonalizedChannels?: boolean, + showPersonalizedTags?: boolean, + subscribedChannels?: Array, + followedTags?: Array, + showIndividualTags?: boolean, + showNsfw?: boolean +) { + const isLargeScreen = useIsLargeScreen(); + + function getPageSize(originalSize) { + return isLargeScreen ? originalSize * (3 / 2) : originalSize; + } + + // $FlowFixMe + let rowData: Array = []; + const individualTagDataItems: Array = []; + + const YOUTUBER_CHANNEL_IDS = [ + 'fb364ef587872515f545a5b4b3182b58073f230f', + '589276465a23c589801d874f484cc39f307d7ec7', + 'ba79c80788a9e1751e49ad401f5692d86f73a2db', + 'b6e207c5f8c58e7c8362cd05a1501bf2f5b694f2', + 'c5724e280283cd985186af9a62494aae377daabd', + '243b6f18093ff97c861d0568c7d3379606201a4b', + '5b7c7a202201033d99e1be2930d290c127c0f4fe', + 'c9da929d12afe6066acc89eb044b552f0d63782a', + '89985db232ec2a9a31dbd985196de817da223fe6', + '187bf3616318b4bfb85223fc40724c307696f0c6', + 'aa3db8d2145340e26597b88fbb6d0e7ff09786be', + 'a9d289718f3f14e3d1fa8da7a7fcfdb6f40ae2d7', + '9a5dfcb1a4b29c3a1598392d039744b9938b5a26', + '0b998b98a2b9a88d9519739f99f2c74c95e3fc22', + '46be492ee0f56db11e005991c537c867a8682f77', + 'c5cd9b63e2ba0abc191feae48238f464baecb147', + '4b602d7a3e268abb45951f623a109d2a131ab0ba', + 'd25ae97a1516f5700fc717152b885f33da47f12b', + '8f4fecfc836ea33798ee3e5cef56926fa54e2cf9', + '8671dfd2f34302c1a4dcb4dd7361568a0bb23eba', + 'b9288432bd089c6f332145aab08a56eec155f307', + '87b13b074936b1f42a7c6758c7c2995f58c602e7', + '25f384bd95e218f6ac37fcaca99ed40f36760d8c', + '02c020b2fab7dd1fbd175c3b22947688c0a219e5', + '57dbc8fdc4d062e2824d8550861b380203539099', + '4e17d248adc0128afe969c2e1327e10afd9cb921', + '760da3ba3dd85830a843beaaed543a89b7a367e7', + '5a1b164d0a2e7adf1db08d7363ea1cb06c30cd74', + 'c9da929d12afe6066acc89eb044b552f0d63782a', + '113515e893b8186595595e594ecc410bae50c026', + '5fbfcf517d3df749bd032a44c1946b2baa738ecb', + '74333143a3dcc001a5602aa524583fc75a013d75', + '0d4e104ffc0ff0a6c8701e67cf13760f4e0335a8', + 'b924ac36b7499591f7929d9c4903de79b07b1cb9', + '13edd7e7e2fbaf845699cf2f8f0b1c095bacb05f', + '7b1c72ba903af4aecdc2595397a9cb91bb7f188d', + '6c0bf1fed2705d675da950a94e4af004ec975a06', + 'f33657a2fcbab2dc3ce555d5d6728f8758af7bc7', + '26c9b54d7e47dc8f7dc847821b26fce3009ee1a0', + '1516361918bfd02ddd460489f438e153c918521c', + 'd468989b4668bce7452fc3434a6dc7ba7d799378', + 'a1c8f84670da9a3371bc5832e86c8d32826b2f2e', + '70e56234217f30317c0e67fd0eede6e82b74aea0', + '7a88e0eabf60af5ac61240fe60f8f08fa3e48ab4', + '2f229d3ac26aa655c5123c29f1f7352403279ca3', + '7ea92a937f5755b40ac3d99ed37c53b40359b0a2', + '96ede5667bc4533ace8cfcbde4f33aa9fe1ae5f5', + '5097b175c4c58c431424ce3b60901de6ae650127', + '32de523ba228dd3f3159eb5a6cc07b6fd51f4dc0', + 'cdb6fe516afe08618b91a754f92412e7f98a8a62', + '1e9f582c2df798228e8583fe1101fee551487d4b', + 'b032695b52a78e0f5251b8d7f2f32183a5985d19', + 'c042155dfcb5c813345248bff18a62d0f585718e', + '294f5c164da5ac9735658b2d58d8fee6745dfc45', + '07e4546674268fc0222b2ca22d31d0549dc217ee', + '1487afc813124abbeb0629d2172be0f01ccec3bf', + 'ac471128a5ed05b80365170b29997d860afa33b7', + 'c101bac49ec048acca169fd6090e70f7488645b1', + 'c9282bbb89d3f9f5f1d972a02f96a5f0f0f40df8', + '9576be30de21b3b755828314d6ccbbaa3334f43a', + 'b12e255e9f84d8b4ed86343b27676dccbc8b6d8b', + '50ebba2b06908f93d7963b1c6826cc0fd6104477', + '84342ae85d216d5ffc0ef149a123aae649d5c253', + '80f78c4b8591390758b9e6303eaf9087180444ad', + '086d2bacf441cef45ff15b5afe163d0b03a9f7ea', + '5af39f818f668d8c00943c9326c5201c4fe3c423', + '057053dfb657aaa98553e2c544b06e1a2371557e', + 'fd1aee1d4858ec2ef6ccc3e60504c76e9d774386', + '930fc43ca7bae20d4706543e97175d1872b0671f', + 'e715c457b4a3e51214b62f49f05303bba4ee5be9', + 'ebf5bc6842638cefcf66904522ac96231ea7a9d8', + '1f9bb08bfa2259629f4aaa9ed40f97e9a41b6fa1', + 'ac415179241e0cd8a14ed71175b759254d381137', + '8e098d2042ad9b9074f52cc06b89d6d4db5231dd', + '149c4686ff0792b8d68dac1f17b6273a85628d34', + '199eba05b6ecccab919e26a0cb7dacd544f25700', + '6569758308f12a66001e28f5e6056cb84334e69c', + 'e50f82e2236274c54af762a9c2b897646477ef62', + '7e1a7afadc8734b33a3e219f5668470715fb063d', + 'ff80e24f41a2d706c70df9779542cba4715216c9', + 'e8f68563d242f6ac9784dcbc41dd86c28a9391d6', + ]; + + const YOUTUBE_CREATOR_ROW = { + title: __('CableTube Escape Artists'), + link: `/$/${PAGES.DISCOVER}?${CS.CLAIM_TYPE}=${CS.CLAIM_STREAM}&${CS.CHANNEL_IDS_KEY}=${YOUTUBER_CHANNEL_IDS.join( + ',' + )}`, + options: { + claimType: ['stream'], + orderBy: ['release_time'], + pageSize: getPageSize(12), + channelIds: YOUTUBER_CHANNEL_IDS, + limitClaimsPerChannel: 1, + releaseTime: `>${Math.floor(moment().subtract(1, 'months').startOf('week').unix())}`, + }, + }; + + const TOP_CONTENT_TODAY = { + title: __('Top Content from Today'), + link: `/$/${PAGES.DISCOVER}?${CS.ORDER_BY_KEY}=${CS.ORDER_BY_TOP}&${CS.FRESH_KEY}=${CS.FRESH_DAY}`, + options: { + pageSize: getPageSize(showPersonalizedChannels || showPersonalizedTags ? 4 : 8), + orderBy: ['effective_amount'], + claimType: ['stream'], + limitClaimsPerChannel: 2, + releaseTime: `>${Math.floor(moment().subtract(1, 'day').startOf('day').unix())}`, + }, + }; + + const TOP_CHANNELS = { + title: __('Top Channels On LBRY'), + link: `/$/${PAGES.DISCOVER}?claim_type=channel&${CS.ORDER_BY_KEY}=${CS.ORDER_BY_TOP}&${CS.FRESH_KEY}=${CS.FRESH_ALL}`, + options: { + orderBy: ['effective_amount'], + claimType: ['channel'], + }, + }; + + const LATEST_FROM_LBRY = { + title: __('Latest From @lbry'), + link: `/@lbry:3f`, + options: { + orderBy: ['release_time'], + pageSize: getPageSize(4), + channelIds: ['3fda836a92faaceedfe398225fb9b2ee2ed1f01a'], + }, + }; + + 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: CS.FILE_TYPES, + channelIds: subscribedChannels.map((subscription: Subscription) => { + const { channelClaimId } = parseURI(subscription.uri); + return channelClaimId; + }), + }, + }; + rowData.push(RECENT_FROM_FOLLOWING); + } + if (isHomepage && !CUSTOM_HOMEPAGE) { + if (followedTags) { + const TRENDING_FOR_TAGS = { + title: __('Trending For Your Tags'), + link: `/$/${PAGES.TAGS_FOLLOWING}`, + icon: ICONS.TAG, + + options: { + pageSize: getPageSize(4), + tags: followedTags.map((tag) => tag.name), + claimType: ['stream'], + limitClaimsPerChannel: 2, + }, + }; + followedTags.forEach((tag: Tag) => { + const tagName = `#${toCapitalCase(tag.name)}`; + individualTagDataItems.push({ + title: __('Trending for %tagName%', { tagName: tagName }), + link: `/$/${PAGES.DISCOVER}?t=${tag.name}`, + options: { + pageSize: 4, + tags: [tag.name], + claimType: ['stream'], + }, + }); + }); + if (showPersonalizedTags && !showIndividualTags) rowData.push(TRENDING_FOR_TAGS); + if (showPersonalizedTags && showIndividualTags) { + individualTagDataItems.forEach((item: RowDataItem) => { + rowData.push(item); + }); + } + } + } + if (!CUSTOM_HOMEPAGE) { + if (!authenticated) { + rowData.push(YOUTUBE_CREATOR_ROW); + } + rowData.push(TOP_CONTENT_TODAY); + rowData.push(LATEST_FROM_LBRY); + if (!showPersonalizedChannels) rowData.push(TOP_CHANNELS); + } + (Object.values(all): any).map((row) => rowData.push(getHomepageRowForCat(row))); + return rowData; +} diff --git a/web/src/getHomepageJSON.js b/web/src/getHomepageJSON.js new file mode 100644 index 000000000..345d39711 --- /dev/null +++ b/web/src/getHomepageJSON.js @@ -0,0 +1,13 @@ +const memo = {}; +// this didn't seem to help. +if (!memo.homepageData) { + try { + memo.homepageData = require('../../custom/homepages/v2'); + } catch (err) { + console.log('homepage data failed'); + } +} +const getHomepageJSON = () => { + return memo.homepageData || {}; +}; +module.exports = { getHomepageJSON }; diff --git a/web/src/routes.js b/web/src/routes.js index 112a546a0..d41d1a753 100644 --- a/web/src/routes.js +++ b/web/src/routes.js @@ -1,10 +1,10 @@ const { getHtml } = require('./html'); const { getRss } = require('./rss'); +const { getHomepageJSON } = require('./getHomepageJSON'); const { generateStreamUrl } = require('../../ui/util/web'); const fetch = require('node-fetch'); const Router = require('@koa/router'); -const fs = require('fs'); -const path = require('path'); +const { CUSTOM_HOMEPAGE } = require('../../config.js'); // So any code from 'lbry-redux'/'lbryinc' that uses `fetch` can be run on the server global.fetch = fetch; @@ -24,18 +24,28 @@ const rssMiddleware = async (ctx) => { ctx.body = xml; }; -router.get(`/$/api/content/get`, async (ctx) => { - let content; - try { - content = await fs.readFileSync(path.join(__dirname, '../../custom/content/test.json'), 'utf-8'); - } catch (e) { - content = await fs.readFileSync(path.join(__dirname, '../../custom/content/default.json'), 'utf-8'); +router.get(`/$/api/content/v1/get`, async (ctx) => { + if (!CUSTOM_HOMEPAGE) { + ctx.status = 404; + ctx.body = { + message: 'Not Found', + }; + } else { + let content; + try { + content = getHomepageJSON(); + ctx.set('Content-Type', 'application/json'); + ctx.body = { + status: 'success', + data: content, + }; + } catch (err) { + ctx.status = err.statusCode || err.status || 500; + ctx.body = { + message: err.message, + }; + } } - - ctx.body = { - status: 'success', - data: content, - }; }); router.get(`/$/download/:claimName/:claimId`, async (ctx) => { diff --git a/webpack.base.config.js b/webpack.base.config.js index f3bf3f184..5fca49380 100644 --- a/webpack.base.config.js +++ b/webpack.base.config.js @@ -39,17 +39,17 @@ let baseConfig = { { test: /\.s?css$/, use: [ - { loader: 'style-loader' }, - { loader: 'css-loader' }, - { loader: 'postcss-loader', - options: { - plugins: function () { - return [ require( 'postcss-rtl' )() ] - } - } - }, - { loader: 'sass-loader'}, - ], + { loader: 'style-loader' }, + { loader: 'css-loader' }, + { loader: 'postcss-loader', + options: { + plugins: function () { + return [ require('postcss-rtl')() ]; + }, + }, + }, + { loader: 'sass-loader'}, + ], }, { test: /\.(png|svg|gif)$/, @@ -76,7 +76,7 @@ let baseConfig = { alias: { config: path.resolve(__dirname, 'config.js'), homepage: 'util/homepage.js', - homepages: process.env.CUSTOM_HOMEPAGE === 'true' ? path.resolve(__dirname, 'custom/homepages/index.js') : ('homepages/index.js'), + homepages: process.env.CUSTOM_HOMEPAGE === 'true' ? path.resolve(__dirname, 'custom/homepages/v2/index.js') : ('homepages/index.js'), lbryinc: 'lbryinc/dist/bundle.es.js', // Build optimizations for 'redux-persist-transform-filter' 'redux-persist-transform-filter': 'redux-persist-transform-filter/index.js',