From 55768fc6b54d8d5f595967209ff8b4b74caeb433 Mon Sep 17 00:00:00 2001 From: jessop Date: Sun, 8 Nov 2020 19:26:42 -0500 Subject: [PATCH] unified custom homepage --- .gitignore | 3 + custom/homepages/.gitkeep | 0 flow-typed/homepage.js | 26 ++ homepages/homepage.js | 288 +++++++++++++++++++ homepages/index.js | 4 + static/app-strings.json | 5 +- ui/component/claimListHeader/index.js | 4 +- ui/component/claimListHeader/view.jsx | 2 +- ui/component/common/icon-custom.jsx | 17 ++ ui/component/homepageSelector/index.js | 15 + ui/component/homepageSelector/view.jsx | 45 +++ ui/component/router/index.js | 2 + ui/component/router/view.jsx | 12 +- ui/component/sideNavigation/index.js | 3 +- ui/component/sideNavigation/view.jsx | 223 +++++++------- ui/component/userChannelFollowIntro/index.js | 2 + ui/component/userChannelFollowIntro/view.jsx | 6 +- ui/constants/icons.js | 1 + ui/page/channelsFollowingDiscover/index.js | 2 + ui/page/channelsFollowingDiscover/view.jsx | 5 +- ui/page/discover/view.jsx | 11 +- ui/page/home/index.js | 3 +- ui/page/home/view.jsx | 46 ++- ui/page/settings/view.jsx | 6 + ui/redux/reducers/settings.js | 4 + ui/redux/selectors/settings.js | 18 ++ ui/scss/component/_button.scss | 5 +- webpack.base.config.js | 3 +- 28 files changed, 629 insertions(+), 132 deletions(-) create mode 100644 custom/homepages/.gitkeep create mode 100644 flow-typed/homepage.js create mode 100644 homepages/homepage.js create mode 100644 homepages/index.js create mode 100644 ui/component/homepageSelector/index.js create mode 100644 ui/component/homepageSelector/view.jsx diff --git a/.gitignore b/.gitignore index 5ab7d743a..c7c46d6fb 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,9 @@ package-lock.json /web/.env /web/.env.defaults /custom/* +/custom/homepages/* +!/custom/homepages/.gitkeep +!/custom/homepages !/custom/homepage.example.js !/custom/robots.disallowall !/custom/robots.allowall diff --git a/custom/homepages/.gitkeep b/custom/homepages/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/flow-typed/homepage.js b/flow-typed/homepage.js new file mode 100644 index 000000000..a2bbeb7b7 --- /dev/null +++ b/flow-typed/homepage.js @@ -0,0 +1,26 @@ +declare type HomepageObject = { + icon: string, + link: string, + options: any, + route: string, + title: string, +} + +declare type HomepageData = { + [string]: HomepageObject, + default: any => any, +} + +declare type RowDataItem = { + title: any, + link?: string, + help?: any, + icon?: string, + extra?: any, + options?: { + channelIds?: Array, + pageSize: number, + }, + route?: string, + hideForUnauth?: boolean, +}; diff --git a/homepages/homepage.js b/homepages/homepage.js new file mode 100644 index 000000000..10e5fd08c --- /dev/null +++ b/homepages/homepage.js @@ -0,0 +1,288 @@ +// @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'; + +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 = [ + '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() + )}`, + }, + }; + + 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: { + 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'], + }, + }; + + const LATEST_FROM_LBRYCAST = { + title: __('Latest From @lbrycast'), + link: `/@lbrycast:4`, + options: { + orderBy: ['release_time'], + pageSize: getPageSize(4), + channelIds: ['4c29f8b013adea4d5cca1861fb2161d5089613ea'], + }, + }; + + 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(TRENDING_CLASSICS); + rowData.push(TOP_CONTENT_TODAY); + + // rowData.push(TRENDING_ON_LBRY); + + rowData.push(LATEST_FROM_LBRY); + rowData.push(LATEST_FROM_LBRYCAST); + + if (!showPersonalizedChannels) rowData.push(TOP_CHANNELS); + + return rowData; +} diff --git a/homepages/index.js b/homepages/index.js new file mode 100644 index 000000000..220bf5897 --- /dev/null +++ b/homepages/index.js @@ -0,0 +1,4 @@ +import * as lbrytv from './homepage'; +// import all homepages +// export object of homepages +export default {'en': lbrytv}; diff --git a/static/app-strings.json b/static/app-strings.json index 3730d8065..cbbe28356 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -1466,13 +1466,16 @@ "Your other content language": "Your other content language", "Search only in this language by default": "Search only in this language by default", "This link leads to an external website.": "This link leads to an external website.", - "Please wait a bit, we are still getting your account ready.": "Please wait a bit, we are still getting your account ready.", "No Content Found": "No Content Found", "Publish Something": "Publish Something", "Watch on lbry.tv": "Watch on lbry.tv", "Paid content cannot be embedded.": "Paid content cannot be embedded.", + "About": "About", + "Cooking": "Cooking", + "Please wait a bit, we are still getting your account ready.": "Please wait a bit, we are still getting your account ready.", "Most supported": "Most supported", "View competing uploads for %name%": "View competing uploads for %name%", + "Homepage": "Homepage", "Currently winning": "Currently winning", "URL can not include a space": "URL can not include a space", "Creator analytics are down for maintenance. Please check back later.": "Creator analytics are down for maintenance. Please check back later.", diff --git a/ui/component/claimListHeader/index.js b/ui/component/claimListHeader/index.js index b163182b7..b894b6f66 100644 --- a/ui/component/claimListHeader/index.js +++ b/ui/component/claimListHeader/index.js @@ -4,7 +4,7 @@ import { selectFollowedTags } from 'redux/selectors/tags'; import { doToggleTagFollowDesktop } from 'redux/actions/tags'; import { makeSelectClientSetting } from 'redux/selectors/settings'; import { doSetClientSetting } from 'redux/actions/settings'; -import ClaimListDiscover from './view'; +import ClaimListHeader from './view'; const select = state => ({ followedTags: selectFollowedTags(state), @@ -19,4 +19,4 @@ const perform = { doSetClientSetting, }; -export default connect(select, perform)(ClaimListDiscover); +export default connect(select, perform)(ClaimListHeader); diff --git a/ui/component/claimListHeader/view.jsx b/ui/component/claimListHeader/view.jsx index 5cc87bddf..7634ab219 100644 --- a/ui/component/claimListHeader/view.jsx +++ b/ui/component/claimListHeader/view.jsx @@ -249,7 +249,7 @@ function ClaimListHeader(props: Props) { ))} -
+
{!hideAdvancedFilter && !SIMPLE_SITE && (
))}
+ {sidebarOpen && helpLinks} )} @@ -363,14 +383,15 @@ function SideNavigation(props: Props) { >
    - {TOP_LEVEL_LINKS.map(linkProps => { - const { hideForUnauth, ...passedProps } = linkProps; - - return !email && hideForUnauth && IS_WEB ? null : ( + {SIDE_LINKS.map(linkProps => { + // $FlowFixMe + const { hideForUnauth, link, route, ...passedProps } = linkProps; + return !email && linkProps.hideForUnauth && IS_WEB ? null : (
  • ); })}
-
    +
      {subLinks.map(linkProps => { const { hideForUnauth, ...passedProps } = linkProps; @@ -391,7 +412,8 @@ function SideNavigation(props: Props) {
    - {isPersonalized && subscriptions && subscriptions.length > 0 && ( -
      + {sidebarOpen && isPersonalized && subscriptions && subscriptions.length > 0 && ( +
        {subscriptions.map(({ uri, channelName }, index) => (
+ {helpLinks}
({ followedTags: selectFollowedTags(state), subscribedChannels: selectSubscriptions(state), + homepageData: selectHomepageData(state), }); const perform = dispatch => ({ diff --git a/ui/component/userChannelFollowIntro/view.jsx b/ui/component/userChannelFollowIntro/view.jsx index 3f02ce4bb..ff02bd583 100644 --- a/ui/component/userChannelFollowIntro/view.jsx +++ b/ui/component/userChannelFollowIntro/view.jsx @@ -13,6 +13,7 @@ type Props = { onContinue: () => void, onBack: () => void, channelSubscribe: (sub: Subscription) => void, + homepageData: any, }; const channelsToSubscribe = AUTO_FOLLOW_CHANNELS.trim() @@ -20,7 +21,8 @@ const channelsToSubscribe = AUTO_FOLLOW_CHANNELS.trim() .filter(x => x !== ''); function UserChannelFollowIntro(props: Props) { - const { subscribedChannels, channelSubscribe, onContinue, onBack } = props; + const { subscribedChannels, channelSubscribe, onContinue, onBack, homepageData } = props; + const { PRIMARY_CONTENT_CHANNEL_IDS } = homepageData; const followingCount = (subscribedChannels && subscribedChannels.length) || 0; // subscribe to lbry @@ -56,6 +58,8 @@ function UserChannelFollowIntro(props: Props) { defaultOrderBy={CS.ORDER_BY_TOP} defaultFreshness={CS.FRESH_ALL} claimType="channel" + // claimIds={PRIMARY_CONTENT_CHANNEL_IDS} // odysee + defaultTags={followingCount > 3 ? CS.TAGS_FOLLOWED : undefined} /> {followingCount > 0 && ( diff --git a/ui/constants/icons.js b/ui/constants/icons.js index 85107101c..2a0b1144e 100644 --- a/ui/constants/icons.js +++ b/ui/constants/icons.js @@ -129,3 +129,4 @@ export const SLIME = 'Slime'; export const PIN = 'Pin'; export const BEST = 'Best'; export const CREATOR_LIKE = 'CreatorLike'; +export const CHEF = 'Chef'; diff --git a/ui/page/channelsFollowingDiscover/index.js b/ui/page/channelsFollowingDiscover/index.js index 90462bebf..e53c03463 100644 --- a/ui/page/channelsFollowingDiscover/index.js +++ b/ui/page/channelsFollowingDiscover/index.js @@ -2,12 +2,14 @@ import { connect } from 'react-redux'; import { selectFollowedTags } from 'redux/selectors/tags'; import { selectBlockedChannels } from 'redux/selectors/blocked'; import { selectSubscriptions } from 'redux/selectors/subscriptions'; +import { selectHomepageData } from 'redux/selectors/settings'; import ChannelsFollowingManagePage from './view'; const select = state => ({ followedTags: selectFollowedTags(state), subscribedChannels: selectSubscriptions(state), blockedChannels: selectBlockedChannels(state), + homepageData: selectHomepageData(state), }); export default connect(select)(ChannelsFollowingManagePage); diff --git a/ui/page/channelsFollowingDiscover/view.jsx b/ui/page/channelsFollowingDiscover/view.jsx index 53a56d706..b474c8c10 100644 --- a/ui/page/channelsFollowingDiscover/view.jsx +++ b/ui/page/channelsFollowingDiscover/view.jsx @@ -13,6 +13,7 @@ type Props = { followedTags: Array, subscribedChannels: Array, blockedChannels: Array, + homepageData: any, }; type ChannelsFollowingItem = { @@ -23,7 +24,8 @@ type ChannelsFollowingItem = { }; function ChannelsFollowingDiscover(props: Props) { - const { followedTags, subscribedChannels, blockedChannels } = props; + const { followedTags, subscribedChannels, blockedChannels, homepageData } = props; + const { PRIMARY_CONTENT_CHANNEL_IDS } = homepageData; let rowData: Array = []; const notChannels = subscribedChannels .map(({ uri }) => uri) @@ -117,6 +119,7 @@ function ChannelsFollowingDiscover(props: Props) {
))}

{__('More Channels')}

+ {/* odysee: claimIds = PRIMARY_CONTENT_CHANNEL_IDS if simplesite CLD */} ); diff --git a/ui/page/discover/view.jsx b/ui/page/discover/view.jsx index 7846a182c..e50805246 100644 --- a/ui/page/discover/view.jsx +++ b/ui/page/discover/view.jsx @@ -21,6 +21,7 @@ type Props = { doToggleTagFollowDesktop: string => void, doResolveUri: string => void, isAuthenticated: boolean, + dynamicRouteProps: RowDataItem, tileLayout: boolean, }; @@ -34,6 +35,7 @@ function DiscoverPage(props: Props) { doResolveUri, isAuthenticated, tileLayout, + dynamicRouteProps, } = props; const buttonRef = useRef(); const isHovering = useHover(buttonRef); @@ -83,8 +85,8 @@ function DiscoverPage(props: Props) { } else { headerLabel = ( - - {__('All Content')} + + {(dynamicRouteProps && dynamicRouteProps.title) || __('All Content')} ); } @@ -92,14 +94,19 @@ function DiscoverPage(props: Props) { return ( : undefined} tileLayout={repostedUri ? false : tileLayout} + defaultOrderBy={CS.ORDER_BY_NEW} claimType={claimType ? [claimType] : undefined} headerLabel={headerLabel} tags={tags} hiddenNsfwMessage={} repostedClaimId={repostedClaim ? repostedClaim.claim_id : null} injectedItem={SHOW_ADS && !isAuthenticated && IS_WEB && } + channelIds={ + (dynamicRouteProps && dynamicRouteProps.options && dynamicRouteProps.options.channelIds) || undefined + } meta={ tag && !isMobile && ( diff --git a/ui/page/home/index.js b/ui/page/home/index.js index 72a1853a7..d6a794b73 100644 --- a/ui/page/home/index.js +++ b/ui/page/home/index.js @@ -3,7 +3,7 @@ import { connect } from 'react-redux'; import { selectFollowedTags } from 'redux/selectors/tags'; import { selectUserVerifiedEmail } from 'redux/selectors/user'; import { selectSubscriptions } from 'redux/selectors/subscriptions'; -import { makeSelectClientSetting } from 'redux/selectors/settings'; +import { makeSelectClientSetting, selectHomepageData } from 'redux/selectors/settings'; import DiscoverPage from './view'; @@ -12,6 +12,7 @@ const select = state => ({ subscribedChannels: selectSubscriptions(state), authenticated: selectUserVerifiedEmail(state), showNsfw: makeSelectClientSetting(SETTINGS.SHOW_MATURE)(state), + homepageData: selectHomepageData(state), }); const perform = {}; diff --git a/ui/page/home/view.jsx b/ui/page/home/view.jsx index 4432a5172..8738c0d9c 100644 --- a/ui/page/home/view.jsx +++ b/ui/page/home/view.jsx @@ -1,27 +1,32 @@ // @flow -import type { RowDataItem } from 'homepage'; +// import type { RowDataItem } from 'homepages'; import * as ICONS from 'constants/icons'; import * as PAGES from 'constants/pages'; import { SITE_NAME } from 'config'; +import classnames from 'classnames'; import React from 'react'; import Page from 'component/page'; import Button from 'component/button'; import ClaimTilesDiscover from 'component/claimTilesDiscover'; -import getHomepage from 'homepage'; + import Icon from 'component/common/icon'; +import { useIsLargeScreen } from 'effects/use-screensize'; type Props = { authenticated: boolean, followedTags: Array, subscribedChannels: Array, showNsfw: boolean, + homepageData: any, }; function HomePage(props: Props) { - const { followedTags, subscribedChannels, authenticated, showNsfw } = props; + const { followedTags, subscribedChannels, authenticated, showNsfw, homepageData } = props; + const isLargeScreen = useIsLargeScreen(); 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 rowData: Array = getHomepage( authenticated, @@ -35,6 +40,7 @@ function HomePage(props: Props) { return ( + {/* MAYBE NOT SIMPLESITE */} {(authenticated || !IS_WEB) && !subscribedChannels.length && (

@@ -49,24 +55,36 @@ function HomePage(props: Props) {

)} - {rowData.map(({ title, link, icon, help, options = {} }) => ( + {rowData.map(({ title, route, link, icon, help, options = {} }, index) => (
-

- -

+ {index === 0 ? ( +
+ + {title} + +
+ ) : ( +

+ +

+ )} - + {link && (
diff --git a/ui/page/settings/view.jsx b/ui/page/settings/view.jsx index 89852605c..7fb5619cb 100644 --- a/ui/page/settings/view.jsx +++ b/ui/page/settings/view.jsx @@ -10,10 +10,13 @@ import Page from 'component/page'; import SettingLanguage from 'component/settingLanguage'; import FileSelector from 'component/common/file-selector'; import SyncToggle from 'component/syncToggle'; +import HomepageSelector from 'component/homepageSelector'; import Card from 'component/common/card'; import SettingAccountPassword from 'component/settingAccountPassword'; import classnames from 'classnames'; import { getPasswordFromCookie } from 'util/saved-passwords'; +// $FlowFixMe +import homepages from 'homepages'; import { Lbryio } from 'lbryinc'; import Yrbl from 'component/yrbl'; @@ -214,6 +217,9 @@ class SettingsPage extends React.PureComponent { ) : (
} /> + {homepages && Object.keys(homepages).length > 1 && ( + } /> + )} {isAuthenticated && } {/* @if TARGET='app' */} homepageKeys.includes(language)) || 'en', [SETTINGS.HIDE_SPLASH_ANIMATION]: false, [SETTINGS.HIDE_BALANCE]: false, [SETTINGS.OS_NOTIFICATIONS_ENABLED]: true, diff --git a/ui/redux/selectors/settings.js b/ui/redux/selectors/settings.js index 8e7f3580f..a56660b1e 100644 --- a/ui/redux/selectors/settings.js +++ b/ui/redux/selectors/settings.js @@ -1,5 +1,6 @@ import { SETTINGS, DAEMON_SETTINGS } from 'lbry-redux'; import { createSelector } from 'reselect'; +import homepages from 'homepages'; const selectState = state => state.settings || {}; @@ -52,4 +53,21 @@ export const selectThemePath = createSelector( } ); +export const selectHomepageCode = createSelector(makeSelectClientSetting(SETTINGS.HOMEPAGE), setting => { + return setting || 'en'; +}); + +export const selectHomepageData = createSelector( + // using homepage setting, + selectHomepageCode, + homepageCode => { + // homepages = { 'en': homepageFile, ... } + if (!homepageCode || !homepages[homepageCode]) { + return homepages['en']; + } else { + return homepages[homepageCode]; + } + } +); + export const selectosNotificationsEnabled = makeSelectClientSetting(SETTINGS.OS_NOTIFICATIONS_ENABLED); diff --git a/ui/scss/component/_button.scss b/ui/scss/component/_button.scss index cd1f8774f..94ca6d702 100644 --- a/ui/scss/component/_button.scss +++ b/ui/scss/component/_button.scss @@ -252,7 +252,10 @@ svg + .button__label { &:last-of-type { border-top-right-radius: var(--border-radius); border-bottom-right-radius: var(--border-radius); - margin-right: var(--spacing-s); + // since we're abusing "button-toggle" let it stand alone properly + &:not(:first-of-type) { + margin-right: var(--spacing-s); + } } } diff --git a/webpack.base.config.js b/webpack.base.config.js index 45e8eae41..f3bf3f184 100644 --- a/webpack.base.config.js +++ b/webpack.base.config.js @@ -75,7 +75,8 @@ let baseConfig = { extensions: ['.js', '.jsx', '.json', '.scss'], alias: { config: path.resolve(__dirname, 'config.js'), - homepage: process.env.CUSTOM_HOMEPAGE === 'true' ? path.resolve(__dirname, 'custom/homepage.js') : ('util/homepage.js'), + homepage: 'util/homepage.js', + homepages: process.env.CUSTOM_HOMEPAGE === 'true' ? path.resolve(__dirname, 'custom/homepages/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',