unified custom homepage
This commit is contained in:
parent
e2b30b7d17
commit
55768fc6b5
28 changed files with 629 additions and 132 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -22,6 +22,9 @@ package-lock.json
|
||||||
/web/.env
|
/web/.env
|
||||||
/web/.env.defaults
|
/web/.env.defaults
|
||||||
/custom/*
|
/custom/*
|
||||||
|
/custom/homepages/*
|
||||||
|
!/custom/homepages/.gitkeep
|
||||||
|
!/custom/homepages
|
||||||
!/custom/homepage.example.js
|
!/custom/homepage.example.js
|
||||||
!/custom/robots.disallowall
|
!/custom/robots.disallowall
|
||||||
!/custom/robots.allowall
|
!/custom/robots.allowall
|
||||||
|
|
0
custom/homepages/.gitkeep
Normal file
0
custom/homepages/.gitkeep
Normal file
26
flow-typed/homepage.js
vendored
Normal file
26
flow-typed/homepage.js
vendored
Normal file
|
@ -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<string>,
|
||||||
|
pageSize: number,
|
||||||
|
},
|
||||||
|
route?: string,
|
||||||
|
hideForUnauth?: boolean,
|
||||||
|
};
|
288
homepages/homepage.js
Normal file
288
homepages/homepage.js
Normal file
|
@ -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<Subscription>,
|
||||||
|
followedTags: Array<Tag>,
|
||||||
|
showIndividualTags: boolean,
|
||||||
|
showNsfw: boolean
|
||||||
|
) {
|
||||||
|
const isLargeScreen = useIsLargeScreen();
|
||||||
|
|
||||||
|
function getPageSize(originalSize) {
|
||||||
|
return isLargeScreen ? originalSize * (3 / 2) : originalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rowData: Array<RowDataItem> = [];
|
||||||
|
const individualTagDataItems: Array<RowDataItem> = [];
|
||||||
|
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;
|
||||||
|
}
|
4
homepages/index.js
Normal file
4
homepages/index.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import * as lbrytv from './homepage';
|
||||||
|
// import all homepages
|
||||||
|
// export object of homepages
|
||||||
|
export default {'en': lbrytv};
|
|
@ -1466,13 +1466,16 @@
|
||||||
"Your other content language": "Your other content language",
|
"Your other content language": "Your other content language",
|
||||||
"Search only in this language by default": "Search only in this language by default",
|
"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.",
|
"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",
|
"No Content Found": "No Content Found",
|
||||||
"Publish Something": "Publish Something",
|
"Publish Something": "Publish Something",
|
||||||
"Watch on lbry.tv": "Watch on lbry.tv",
|
"Watch on lbry.tv": "Watch on lbry.tv",
|
||||||
"Paid content cannot be embedded.": "Paid content cannot be embedded.",
|
"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",
|
"Most supported": "Most supported",
|
||||||
"View competing uploads for %name%": "View competing uploads for %name%",
|
"View competing uploads for %name%": "View competing uploads for %name%",
|
||||||
|
"Homepage": "Homepage",
|
||||||
"Currently winning": "Currently winning",
|
"Currently winning": "Currently winning",
|
||||||
"URL can not include a space": "URL can not include a space",
|
"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.",
|
"Creator analytics are down for maintenance. Please check back later.": "Creator analytics are down for maintenance. Please check back later.",
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { selectFollowedTags } from 'redux/selectors/tags';
|
||||||
import { doToggleTagFollowDesktop } from 'redux/actions/tags';
|
import { doToggleTagFollowDesktop } from 'redux/actions/tags';
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
import { doSetClientSetting } from 'redux/actions/settings';
|
import { doSetClientSetting } from 'redux/actions/settings';
|
||||||
import ClaimListDiscover from './view';
|
import ClaimListHeader from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
followedTags: selectFollowedTags(state),
|
followedTags: selectFollowedTags(state),
|
||||||
|
@ -19,4 +19,4 @@ const perform = {
|
||||||
doSetClientSetting,
|
doSetClientSetting,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(select, perform)(ClaimListDiscover);
|
export default connect(select, perform)(ClaimListHeader);
|
||||||
|
|
|
@ -249,7 +249,7 @@ function ClaimListHeader(props: Props) {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div className="claim-search__top-row">
|
||||||
{!hideAdvancedFilter && !SIMPLE_SITE && (
|
{!hideAdvancedFilter && !SIMPLE_SITE && (
|
||||||
<Button
|
<Button
|
||||||
button="alt"
|
button="alt"
|
||||||
|
|
|
@ -998,4 +998,21 @@ export const icons = {
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
|
[ICONS.CHEF]: (props: CustomProps) => (
|
||||||
|
<svg
|
||||||
|
{...props}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="-6 0 26 24"
|
||||||
|
width={props.size || '18'}
|
||||||
|
height={props.size || '18'}
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
>
|
||||||
|
<path d="m 5.8585986,19.685492 v 3.415632 c 0,0.269439 0.2185945,0.487951 0.4879486,0.487951 H 18.086586 c 0.270325,0 0.487946,-0.218539 0.45867,-0.487951 v -3.415632 z" />
|
||||||
|
<path d="m 18.089706,2.6673324 c -0.458672,0 -0.914415,0.081053 -1.342833,0.2381801 -0.726068,-1.5175206 -2.625165,-2.67785413 -4.515474,-2.67785413 -1.902023,0 -3.8128297,1.16033353 -4.5408481,2.67785413 C 7.2621303,2.7483855 6.8063878,2.6673324 6.348691,2.6673324 c -2.1528256,0 -3.9045598,1.7507491 -3.9045598,3.9035835 0,2.0230385 1.4648199,3.6410591 3.4146614,3.8752841 v 8.262918 h 2.9276892 v -3.415632 c 0.00968,-0.26944 0.2273915,-0.487951 0.4977084,-0.487951 0.2693563,0 0.4879486,0.218539 0.4879486,0.487951 v 3.415632 h 1.9420352 v -4.391535 c 0,-0.269439 0.217626,-0.487951 0.487948,-0.487951 0.269357,0 0.487946,0.218539 0.487946,0.487951 v 4.391535 h 1.951795 v -3.415632 c 0.01964,-0.26944 0.238125,-0.487951 0.507465,-0.487951 0.270325,0 0.487949,0.218539 0.468432,0.487951 v 3.415632 h 2.927689 V 10.4462 c 1.980095,-0.234307 3.445891,-1.8522456 3.445891,-3.8752841 0,-2.1528344 -1.750758,-3.9035835 -3.901634,-3.9035835" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
15
ui/component/homepageSelector/index.js
Normal file
15
ui/component/homepageSelector/index.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import SelectHomepage from './view';
|
||||||
|
import { SETTINGS } from 'lbry-redux';
|
||||||
|
import { doSetClientSetting } from 'redux/actions/settings';
|
||||||
|
import { selectHomepageCode } from 'redux/selectors/settings';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
homepage: selectHomepageCode(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
setHomepage: value => dispatch(doSetClientSetting(SETTINGS.HOMEPAGE, value)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, perform)(SelectHomepage);
|
45
ui/component/homepageSelector/view.jsx
Normal file
45
ui/component/homepageSelector/view.jsx
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
// $FlowFixMe
|
||||||
|
import homepages from 'homepages';
|
||||||
|
import LANGUAGES from 'constants/languages';
|
||||||
|
import { FormField } from 'component/common/form';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
homepage: string,
|
||||||
|
setHomepage: string => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
function SelectHomepage(props: Props) {
|
||||||
|
const { homepage, setHomepage } = props;
|
||||||
|
|
||||||
|
function handleSetHomepage(e) {
|
||||||
|
const { value } = e.target;
|
||||||
|
setHomepage(value);
|
||||||
|
}
|
||||||
|
if (Object.keys(homepages).length <= 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<FormField
|
||||||
|
name="homepage_select"
|
||||||
|
type="select"
|
||||||
|
label={__('Homepage')}
|
||||||
|
onChange={handleSetHomepage}
|
||||||
|
value={homepage}
|
||||||
|
helper={__(
|
||||||
|
'Multi-language support is brand new and incomplete. Switching your language may have unintended consequences, like glossolalia.'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{Object.keys(homepages).map(hp => (
|
||||||
|
<option key={'hp' + hp} value={hp}>
|
||||||
|
{`${LANGUAGES[hp][1]}`}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</FormField>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SelectHomepage;
|
|
@ -1,6 +1,7 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||||
import { selectHasNavigated, selectScrollStartingPosition, selectWelcomeVersion } from 'redux/selectors/app';
|
import { selectHasNavigated, selectScrollStartingPosition, selectWelcomeVersion } from 'redux/selectors/app';
|
||||||
|
import { selectHomepageData } from 'redux/selectors/settings';
|
||||||
import Router from './view';
|
import Router from './view';
|
||||||
import { normalizeURI, makeSelectTitleForUri } from 'lbry-redux';
|
import { normalizeURI, makeSelectTitleForUri } from 'lbry-redux';
|
||||||
import { doSetHasNavigated } from 'redux/actions/app';
|
import { doSetHasNavigated } from 'redux/actions/app';
|
||||||
|
@ -32,6 +33,7 @@ const select = state => {
|
||||||
welcomeVersion: selectWelcomeVersion(state),
|
welcomeVersion: selectWelcomeVersion(state),
|
||||||
hasNavigated: selectHasNavigated(state),
|
hasNavigated: selectHasNavigated(state),
|
||||||
hasUnclaimedRefereeReward: selectHasUnclaimedRefereeReward(state),
|
hasUnclaimedRefereeReward: selectHasUnclaimedRefereeReward(state),
|
||||||
|
homepageData: selectHomepageData(state),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
import type { RowDataItem } from 'homepage';
|
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import * as SIDEBAR_ROUTES from 'homepage';
|
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Route, Redirect, Switch, withRouter } from 'react-router-dom';
|
import { Route, Redirect, Switch, withRouter } from 'react-router-dom';
|
||||||
import SettingsPage from 'page/settings';
|
import SettingsPage from 'page/settings';
|
||||||
|
@ -55,10 +53,6 @@ import { LINKED_COMMENT_QUERY_PARAM } from 'constants/comment';
|
||||||
import { parseURI, isURIValid } from 'lbry-redux';
|
import { parseURI, isURIValid } from 'lbry-redux';
|
||||||
import { SITE_TITLE, WELCOME_VERSION } from 'config';
|
import { SITE_TITLE, WELCOME_VERSION } from 'config';
|
||||||
|
|
||||||
const dynamicRoutes = Object.values(SIDEBAR_ROUTES).filter(
|
|
||||||
(potentialRoute: any) => potentialRoute && potentialRoute.route
|
|
||||||
);
|
|
||||||
|
|
||||||
// Tell the browser we are handling scroll restoration
|
// Tell the browser we are handling scroll restoration
|
||||||
if ('scrollRestoration' in history) {
|
if ('scrollRestoration' in history) {
|
||||||
history.scrollRestoration = 'manual';
|
history.scrollRestoration = 'manual';
|
||||||
|
@ -87,6 +81,7 @@ type Props = {
|
||||||
setHasNavigated: () => void,
|
setHasNavigated: () => void,
|
||||||
setReferrer: string => void,
|
setReferrer: string => void,
|
||||||
hasUnclaimedRefereeReward: boolean,
|
hasUnclaimedRefereeReward: boolean,
|
||||||
|
homepageData: any,
|
||||||
};
|
};
|
||||||
|
|
||||||
type PrivateRouteProps = Props & {
|
type PrivateRouteProps = Props & {
|
||||||
|
@ -123,6 +118,7 @@ function AppRouter(props: Props) {
|
||||||
setHasNavigated,
|
setHasNavigated,
|
||||||
hasUnclaimedRefereeReward,
|
hasUnclaimedRefereeReward,
|
||||||
setReferrer,
|
setReferrer,
|
||||||
|
homepageData,
|
||||||
} = props;
|
} = props;
|
||||||
const { entries } = history;
|
const { entries } = history;
|
||||||
const entryIndex = history.index;
|
const entryIndex = history.index;
|
||||||
|
@ -130,6 +126,10 @@ function AppRouter(props: Props) {
|
||||||
const resetScroll = urlParams.get('reset_scroll');
|
const resetScroll = urlParams.get('reset_scroll');
|
||||||
const hasLinkedCommentInUrl = urlParams.get(LINKED_COMMENT_QUERY_PARAM);
|
const hasLinkedCommentInUrl = urlParams.get(LINKED_COMMENT_QUERY_PARAM);
|
||||||
|
|
||||||
|
const dynamicRoutes = Object.values(homepageData).filter(
|
||||||
|
(potentialRoute: any) => potentialRoute && potentialRoute.route
|
||||||
|
);
|
||||||
|
|
||||||
// For people arriving at settings page from deeplinks, know whether they can "go back"
|
// For people arriving at settings page from deeplinks, know whether they can "go back"
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unlisten = history.listen((location, action) => {
|
const unlisten = history.listen((location, action) => {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
||||||
import { selectPurchaseUriSuccess, doClearPurchasedUriSuccess, SETTINGS } from 'lbry-redux';
|
import { selectPurchaseUriSuccess, doClearPurchasedUriSuccess, SETTINGS } from 'lbry-redux';
|
||||||
import { selectFollowedTags } from 'redux/selectors/tags';
|
import { selectFollowedTags } from 'redux/selectors/tags';
|
||||||
import { selectUserVerifiedEmail, selectUser } from 'redux/selectors/user';
|
import { selectUserVerifiedEmail, selectUser } from 'redux/selectors/user';
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectHomepageData } from 'redux/selectors/settings';
|
||||||
import { doSignOut } from 'redux/actions/app';
|
import { doSignOut } from 'redux/actions/app';
|
||||||
import { selectUnreadNotificationCount } from 'redux/selectors/notifications';
|
import { selectUnreadNotificationCount } from 'redux/selectors/notifications';
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ const select = state => ({
|
||||||
purchaseSuccess: selectPurchaseUriSuccess(state),
|
purchaseSuccess: selectPurchaseUriSuccess(state),
|
||||||
unreadCount: selectUnreadNotificationCount(state),
|
unreadCount: selectUnreadNotificationCount(state),
|
||||||
user: selectUser(state),
|
user: selectUser(state),
|
||||||
|
homepageData: selectHomepageData(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, {
|
export default connect(select, {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import classnames from 'classnames';
|
||||||
import Icon from 'component/common/icon';
|
import Icon from 'component/common/icon';
|
||||||
import NotificationBubble from 'component/notificationBubble';
|
import NotificationBubble from 'component/notificationBubble';
|
||||||
import I18nMessage from 'component/i18nMessage';
|
import I18nMessage from 'component/i18nMessage';
|
||||||
import { PINNED_LABEL_1, PINNED_URI_1, PINNED_URI_2, PINNED_LABEL_2 } from 'config';
|
import { PINNED_LABEL_1, PINNED_URI_1, PINNED_URI_2, PINNED_LABEL_2, SIMPLE_SITE } from 'config';
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
import { IS_MAC } from 'component/app/view';
|
import { IS_MAC } from 'component/app/view';
|
||||||
// @endif
|
// @endif
|
||||||
|
@ -17,14 +17,14 @@ const ESCAPE_KEY_CODE = 27;
|
||||||
const BACKSLASH_KEY_CODE = 220;
|
const BACKSLASH_KEY_CODE = 220;
|
||||||
|
|
||||||
const HOME = {
|
const HOME = {
|
||||||
label: 'Home',
|
title: 'Home',
|
||||||
navigate: `/`,
|
link: `/`,
|
||||||
icon: ICONS.HOME,
|
icon: ICONS.HOME,
|
||||||
};
|
};
|
||||||
|
|
||||||
const RECENT_FROM_FOLLOWING = {
|
const RECENT_FROM_FOLLOWING = {
|
||||||
label: 'Following --[sidebar button]--',
|
title: 'Following --[sidebar button]--',
|
||||||
navigate: `/$/${PAGES.CHANNELS_FOLLOWING}`,
|
link: `/$/${PAGES.CHANNELS_FOLLOWING}`,
|
||||||
icon: ICONS.SUBSCRIBE,
|
icon: ICONS.SUBSCRIBE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,6 +42,16 @@ type Props = {
|
||||||
purchaseSuccess: boolean,
|
purchaseSuccess: boolean,
|
||||||
doClearPurchasedUriSuccess: () => void,
|
doClearPurchasedUriSuccess: () => void,
|
||||||
user: ?User,
|
user: ?User,
|
||||||
|
homepageData: any,
|
||||||
|
};
|
||||||
|
|
||||||
|
type SideNavLink = {
|
||||||
|
title: string,
|
||||||
|
link?: string,
|
||||||
|
onClick?: () => any,
|
||||||
|
icon: string,
|
||||||
|
extra?: Node,
|
||||||
|
hideForUnauth?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function SideNavigation(props: Props) {
|
function SideNavigation(props: Props) {
|
||||||
|
@ -57,104 +67,87 @@ function SideNavigation(props: Props) {
|
||||||
isMediumScreen,
|
isMediumScreen,
|
||||||
isOnFilePage,
|
isOnFilePage,
|
||||||
unreadCount,
|
unreadCount,
|
||||||
|
homepageData,
|
||||||
user,
|
user,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const TOP_LEVEL_LINKS: Array<{
|
const { EXTRA_SIDEBAR_LINKS } = homepageData;
|
||||||
label: string,
|
|
||||||
navigate: string,
|
const FULL_LINKS: Array<SideNavLink> = [
|
||||||
icon: string,
|
|
||||||
extra?: Node,
|
|
||||||
hideForUnauth?: boolean,
|
|
||||||
}> = [
|
|
||||||
HOME,
|
|
||||||
RECENT_FROM_FOLLOWING,
|
|
||||||
{
|
{
|
||||||
label: 'Your Tags',
|
title: 'Your Tags',
|
||||||
navigate: `/$/${PAGES.TAGS_FOLLOWING}`,
|
navigate: `/$/${PAGES.TAGS_FOLLOWING}`,
|
||||||
icon: ICONS.TAG,
|
icon: ICONS.TAG,
|
||||||
hideForUnauth: true,
|
hideForUnauth: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Discover',
|
title: 'Discover',
|
||||||
navigate: `/$/${PAGES.DISCOVER}`,
|
navigate: `/$/${PAGES.DISCOVER}`,
|
||||||
icon: ICONS.DISCOVER,
|
icon: ICONS.DISCOVER,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: IS_WEB ? 'Purchased' : 'Library',
|
title: IS_WEB ? 'Purchased' : 'Library',
|
||||||
navigate: `/$/${PAGES.LIBRARY}`,
|
navigate: `/$/${PAGES.LIBRARY}`,
|
||||||
icon: ICONS.PURCHASED,
|
icon: ICONS.PURCHASED,
|
||||||
hideForUnauth: true,
|
hideForUnauth: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (PINNED_URI_1 && PINNED_LABEL_1) {
|
const MOBILE_LINKS: Array<SideNavLink> = [
|
||||||
TOP_LEVEL_LINKS.push({
|
|
||||||
label: PINNED_LABEL_1,
|
|
||||||
navigate: PINNED_URI_1,
|
|
||||||
icon: ICONS.PINNED,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PINNED_URI_2 && PINNED_LABEL_2) {
|
|
||||||
TOP_LEVEL_LINKS.push({
|
|
||||||
label: PINNED_LABEL_2,
|
|
||||||
navigate: PINNED_URI_2,
|
|
||||||
icon: ICONS.PINNED,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const ABSOLUTE_LINKS: Array<{
|
|
||||||
label: string,
|
|
||||||
navigate?: string,
|
|
||||||
onClick?: () => any,
|
|
||||||
icon: string,
|
|
||||||
extra?: Node,
|
|
||||||
hideForUnauth?: boolean,
|
|
||||||
}> = [
|
|
||||||
{
|
{
|
||||||
label: 'Upload',
|
title: 'Notifications',
|
||||||
navigate: `/$/${PAGES.UPLOAD}`,
|
link: `/$/${PAGES.NOTIFICATIONS}`,
|
||||||
|
icon: ICONS.NOTIFICATION,
|
||||||
|
extra: <NotificationBubble inline />,
|
||||||
|
hideForUnauth: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Discover',
|
||||||
|
link: `/$/${PAGES.DISCOVER}`,
|
||||||
|
icon: ICONS.DISCOVER,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: IS_WEB ? 'Purchased' : 'Library',
|
||||||
|
navigate: `/$/${PAGES.LIBRARY}`,
|
||||||
|
icon: ICONS.PURCHASED,
|
||||||
|
hideForUnauth: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Upload',
|
||||||
|
link: `/$/${PAGES.UPLOAD}`,
|
||||||
icon: ICONS.PUBLISH,
|
icon: ICONS.PUBLISH,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'New Channel',
|
title: 'New Channel',
|
||||||
navigate: `/$/${PAGES.CHANNEL_NEW}`,
|
link: `/$/${PAGES.CHANNEL_NEW}`,
|
||||||
icon: ICONS.CHANNEL,
|
icon: ICONS.CHANNEL,
|
||||||
hideForUnauth: true,
|
hideForUnauth: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Uploads',
|
title: 'Uploads',
|
||||||
navigate: `/$/${PAGES.UPLOADS}`,
|
link: `/$/${PAGES.UPLOADS}`,
|
||||||
icon: ICONS.PUBLISH,
|
icon: ICONS.PUBLISH,
|
||||||
hideForUnauth: true,
|
hideForUnauth: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
label: 'Channels',
|
title: 'Channels',
|
||||||
navigate: `/$/${PAGES.CHANNELS}`,
|
link: `/$/${PAGES.CHANNELS}`,
|
||||||
icon: ICONS.CHANNEL,
|
icon: ICONS.CHANNEL,
|
||||||
hideForUnauth: true,
|
hideForUnauth: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Creator Analytics',
|
title: 'Creator Analytics',
|
||||||
navigate: `/$/${PAGES.CREATOR_DASHBOARD}`,
|
link: `/$/${PAGES.CREATOR_DASHBOARD}`,
|
||||||
icon: ICONS.ANALYTICS,
|
icon: ICONS.ANALYTICS,
|
||||||
hideForUnauth: true,
|
hideForUnauth: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Wallet',
|
title: 'Wallet',
|
||||||
navigate: `/$/${PAGES.WALLET}`,
|
link: `/$/${PAGES.WALLET}`,
|
||||||
icon: ICONS.WALLET,
|
icon: ICONS.WALLET,
|
||||||
hideForUnauth: true,
|
hideForUnauth: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: 'Notifications',
|
|
||||||
navigate: `/$/${PAGES.NOTIFICATIONS}`,
|
|
||||||
icon: ICONS.NOTIFICATION,
|
|
||||||
extra: <NotificationBubble inline />,
|
|
||||||
hideForUnauth: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: 'Rewards',
|
label: 'Rewards',
|
||||||
navigate: `/$/${PAGES.REWARDS}`,
|
navigate: `/$/${PAGES.REWARDS}`,
|
||||||
|
@ -162,68 +155,92 @@ function SideNavigation(props: Props) {
|
||||||
hideForUnauth: true,
|
hideForUnauth: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Invites',
|
title: 'Invites',
|
||||||
navigate: `/$/${PAGES.INVITE}`,
|
link: `/$/${PAGES.INVITE}`,
|
||||||
icon: ICONS.INVITE,
|
icon: ICONS.INVITE,
|
||||||
hideForUnauth: true,
|
hideForUnauth: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Settings',
|
title: 'Settings',
|
||||||
navigate: `/$/${PAGES.SETTINGS}`,
|
link: `/$/${PAGES.SETTINGS}`,
|
||||||
icon: ICONS.SETTINGS,
|
icon: ICONS.SETTINGS,
|
||||||
hideForUnauth: true,
|
hideForUnauth: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Help',
|
title: 'Help',
|
||||||
navigate: `/$/${PAGES.HELP}`,
|
link: `/$/${PAGES.HELP}`,
|
||||||
icon: ICONS.HELP,
|
icon: ICONS.HELP,
|
||||||
hideForUnauth: true,
|
hideForUnauth: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Sign Out',
|
title: 'Sign Out',
|
||||||
onClick: doSignOut,
|
onClick: doSignOut,
|
||||||
icon: ICONS.SIGN_OUT,
|
icon: ICONS.SIGN_OUT,
|
||||||
hideForUnauth: true,
|
hideForUnauth: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const UNAUTH_LINKS: Array<{
|
const UNAUTH_LINKS: Array<SideNavLink> = [
|
||||||
label: string,
|
|
||||||
navigate: string,
|
|
||||||
icon: string,
|
|
||||||
extra?: Node,
|
|
||||||
hideForUnauth?: boolean,
|
|
||||||
}> = [
|
|
||||||
{
|
{
|
||||||
label: 'Log In',
|
title: 'Log In',
|
||||||
navigate: `/$/${PAGES.AUTH_SIGNIN}`,
|
link: `/$/${PAGES.AUTH_SIGNIN}`,
|
||||||
icon: ICONS.SIGN_IN,
|
icon: ICONS.SIGN_IN,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Sign Up',
|
title: 'Sign Up',
|
||||||
navigate: `/$/${PAGES.AUTH}`,
|
link: `/$/${PAGES.AUTH}`,
|
||||||
icon: ICONS.SIGN_UP,
|
icon: ICONS.SIGN_UP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Settings',
|
title: 'Settings',
|
||||||
navigate: `/$/${PAGES.SETTINGS}`,
|
link: `/$/${PAGES.SETTINGS}`,
|
||||||
icon: ICONS.SETTINGS,
|
icon: ICONS.SETTINGS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Help',
|
title: 'Help',
|
||||||
navigate: `/$/${PAGES.HELP}`,
|
link: `/$/${PAGES.HELP}`,
|
||||||
icon: ICONS.HELP,
|
icon: ICONS.HELP,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
if (PINNED_URI_1 && PINNED_LABEL_1) {
|
||||||
|
MOBILE_LINKS.push({
|
||||||
|
label: PINNED_LABEL_1,
|
||||||
|
navigate: PINNED_URI_1,
|
||||||
|
icon: ICONS.PINNED,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PINNED_URI_2 && PINNED_LABEL_2) {
|
||||||
|
MOBILE_LINKS.push({
|
||||||
|
label: PINNED_LABEL_2,
|
||||||
|
navigate: PINNED_URI_2,
|
||||||
|
icon: ICONS.PINNED,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const notificationsEnabled = user && user.experimental_ui;
|
const notificationsEnabled = user && user.experimental_ui;
|
||||||
const isAuthenticated = Boolean(email);
|
const isAuthenticated = Boolean(email);
|
||||||
|
// SIDE LINKS: FOLLOWING, HOME, [FULL,] [EXTRA]
|
||||||
|
let SIDE_LINKS: Array<SideNavLink> = [];
|
||||||
|
if (isAuthenticated) {
|
||||||
|
SIDE_LINKS.push(RECENT_FROM_FOLLOWING);
|
||||||
|
}
|
||||||
|
SIDE_LINKS.push(HOME);
|
||||||
|
|
||||||
|
if (!SIMPLE_SITE) {
|
||||||
|
SIDE_LINKS.push(...FULL_LINKS);
|
||||||
|
}
|
||||||
|
// $FlowFixMe
|
||||||
|
if (EXTRA_SIDEBAR_LINKS) {
|
||||||
|
SIDE_LINKS.push(EXTRA_SIDEBAR_LINKS);
|
||||||
|
}
|
||||||
|
|
||||||
const [pulseLibrary, setPulseLibrary] = React.useState(false);
|
const [pulseLibrary, setPulseLibrary] = React.useState(false);
|
||||||
const isPersonalized = !IS_WEB || isAuthenticated;
|
const isPersonalized = !IS_WEB || isAuthenticated;
|
||||||
const isAbsolute = isOnFilePage || isMediumScreen;
|
const isAbsolute = isOnFilePage || isMediumScreen;
|
||||||
const microNavigation = !sidebarOpen || isMediumScreen;
|
const microNavigation = !sidebarOpen || isMediumScreen;
|
||||||
const subLinks = email
|
const subLinks = email
|
||||||
? ABSOLUTE_LINKS.filter(link => {
|
? MOBILE_LINKS.filter(link => {
|
||||||
if (!notificationsEnabled && link.icon === ICONS.NOTIFICATION) {
|
if (!notificationsEnabled && link.icon === ICONS.NOTIFICATION) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -231,7 +248,6 @@ function SideNavigation(props: Props) {
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
: UNAUTH_LINKS;
|
: UNAUTH_LINKS;
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (purchaseSuccess) {
|
if (purchaseSuccess) {
|
||||||
setPulseLibrary(true);
|
setPulseLibrary(true);
|
||||||
|
@ -291,13 +307,16 @@ function SideNavigation(props: Props) {
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<ul className={classnames('navigation-links', { 'navigation-links--micro': !sidebarOpen })}>
|
<ul className={classnames('navigation-links', { 'navigation-links--micro': !sidebarOpen })}>
|
||||||
{TOP_LEVEL_LINKS.map(linkProps => {
|
{SIDE_LINKS.map(linkProps => {
|
||||||
|
// $FlowFixMe
|
||||||
const { hideForUnauth, ...passedProps } = linkProps;
|
const { hideForUnauth, ...passedProps } = linkProps;
|
||||||
return !email && linkProps.hideForUnauth && IS_WEB ? null : (
|
return !email && linkProps.hideForUnauth && IS_WEB ? null : (
|
||||||
<li key={linkProps.icon}>
|
<li key={linkProps.icon}>
|
||||||
<Button
|
<Button
|
||||||
{...passedProps}
|
{...passedProps}
|
||||||
label={__(linkProps.label)}
|
label={__(linkProps.title)}
|
||||||
|
// $FlowFixMe
|
||||||
|
navigate={linkProps.route || linkProps.link}
|
||||||
icon={pulseLibrary && linkProps.icon === ICONS.LIBRARY ? ICONS.PURCHASED : linkProps.icon}
|
icon={pulseLibrary && linkProps.icon === ICONS.LIBRARY ? ICONS.PURCHASED : linkProps.icon}
|
||||||
className={classnames('navigation-link', {
|
className={classnames('navigation-link', {
|
||||||
'navigation-link--pulse': linkProps.icon === ICONS.LIBRARY && pulseLibrary,
|
'navigation-link--pulse': linkProps.icon === ICONS.LIBRARY && pulseLibrary,
|
||||||
|
@ -305,14 +324,14 @@ function SideNavigation(props: Props) {
|
||||||
})}
|
})}
|
||||||
activeClass="navigation-link--active"
|
activeClass="navigation-link--active"
|
||||||
/>
|
/>
|
||||||
{linkProps.extra}
|
{linkProps.extra && linkProps.extra}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{sidebarOpen && isPersonalized && subscriptions && subscriptions.length > 0 && (
|
{sidebarOpen && isPersonalized && subscriptions && subscriptions.length > 0 && (
|
||||||
<ul className="navigation__secondary navigation-links navigation-links--small">
|
<ul className="navigation__secondary navigation-links">
|
||||||
{subscriptions.map(({ uri, channelName }, index) => (
|
{subscriptions.map(({ uri, channelName }, index) => (
|
||||||
<li key={uri} className="navigation-link__wrapper">
|
<li key={uri} className="navigation-link__wrapper">
|
||||||
<Button
|
<Button
|
||||||
|
@ -325,7 +344,7 @@ function SideNavigation(props: Props) {
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
{sidebarOpen && isPersonalized && followedTags && followedTags.length > 0 && (
|
{!SIMPLE_SITE && sidebarOpen && isPersonalized && followedTags && followedTags.length > 0 && (
|
||||||
<ul className="navigation__secondary navigation-links navigation-links--small">
|
<ul className="navigation__secondary navigation-links navigation-links--small">
|
||||||
{followedTags.map(({ name }, key) => (
|
{followedTags.map(({ name }, key) => (
|
||||||
<li key={name} className="navigation-link__wrapper">
|
<li key={name} className="navigation-link__wrapper">
|
||||||
|
@ -349,6 +368,7 @@ function SideNavigation(props: Props) {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
{sidebarOpen && helpLinks}
|
||||||
</nav>
|
</nav>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -363,14 +383,15 @@ function SideNavigation(props: Props) {
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<ul className="navigation-links--absolute">
|
<ul className="navigation-links--absolute">
|
||||||
{TOP_LEVEL_LINKS.map(linkProps => {
|
{SIDE_LINKS.map(linkProps => {
|
||||||
const { hideForUnauth, ...passedProps } = linkProps;
|
// $FlowFixMe
|
||||||
|
const { hideForUnauth, link, route, ...passedProps } = linkProps;
|
||||||
return !email && hideForUnauth && IS_WEB ? null : (
|
return !email && linkProps.hideForUnauth && IS_WEB ? null : (
|
||||||
<li key={linkProps.icon}>
|
<li key={linkProps.icon}>
|
||||||
<Button
|
<Button
|
||||||
{...passedProps}
|
{...passedProps}
|
||||||
label={__(linkProps.label)}
|
navigate={route || link}
|
||||||
|
label={__(linkProps.title)}
|
||||||
icon={pulseLibrary && linkProps.icon === ICONS.LIBRARY ? ICONS.PURCHASED : linkProps.icon}
|
icon={pulseLibrary && linkProps.icon === ICONS.LIBRARY ? ICONS.PURCHASED : linkProps.icon}
|
||||||
className={classnames('navigation-link', {
|
className={classnames('navigation-link', {
|
||||||
'navigation-link--pulse': linkProps.icon === ICONS.LIBRARY && pulseLibrary,
|
'navigation-link--pulse': linkProps.icon === ICONS.LIBRARY && pulseLibrary,
|
||||||
|
@ -378,12 +399,12 @@ function SideNavigation(props: Props) {
|
||||||
})}
|
})}
|
||||||
activeClass="navigation-link--active"
|
activeClass="navigation-link--active"
|
||||||
/>
|
/>
|
||||||
{linkProps.extra}
|
{linkProps.extra && linkProps.extra}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
<ul className="navigation-links--absolute">
|
<ul className="navigation-links--absolute mobile-only">
|
||||||
{subLinks.map(linkProps => {
|
{subLinks.map(linkProps => {
|
||||||
const { hideForUnauth, ...passedProps } = linkProps;
|
const { hideForUnauth, ...passedProps } = linkProps;
|
||||||
|
|
||||||
|
@ -391,7 +412,8 @@ function SideNavigation(props: Props) {
|
||||||
<li key={linkProps.icon} className="mobile-only">
|
<li key={linkProps.icon} className="mobile-only">
|
||||||
<Button
|
<Button
|
||||||
{...passedProps}
|
{...passedProps}
|
||||||
label={__(linkProps.label)}
|
navigate={linkProps.link}
|
||||||
|
label={__(linkProps.title)}
|
||||||
className="navigation-link"
|
className="navigation-link"
|
||||||
activeClass="navigation-link--active"
|
activeClass="navigation-link--active"
|
||||||
/>
|
/>
|
||||||
|
@ -400,8 +422,8 @@ function SideNavigation(props: Props) {
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
{isPersonalized && subscriptions && subscriptions.length > 0 && (
|
{sidebarOpen && isPersonalized && subscriptions && subscriptions.length > 0 && (
|
||||||
<ul className="navigation__secondary navigation-links--small">
|
<ul className="navigation__secondary navigation-links">
|
||||||
{subscriptions.map(({ uri, channelName }, index) => (
|
{subscriptions.map(({ uri, channelName }, index) => (
|
||||||
<li key={uri} className="navigation-link__wrapper">
|
<li key={uri} className="navigation-link__wrapper">
|
||||||
<Button
|
<Button
|
||||||
|
@ -414,7 +436,7 @@ function SideNavigation(props: Props) {
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
{sidebarOpen && isPersonalized && followedTags && followedTags.length > 0 && (
|
{!SIMPLE_SITE && sidebarOpen && isPersonalized && followedTags && followedTags.length > 0 && (
|
||||||
<ul className="navigation__secondary navigation-links navigation-links--small">
|
<ul className="navigation__secondary navigation-links navigation-links--small">
|
||||||
{followedTags.map(({ name }, key) => (
|
{followedTags.map(({ name }, key) => (
|
||||||
<li key={name} className="navigation-link__wrapper">
|
<li key={name} className="navigation-link__wrapper">
|
||||||
|
@ -425,6 +447,7 @@ function SideNavigation(props: Props) {
|
||||||
)}
|
)}
|
||||||
{!isAuthenticated && unAuthNudge}
|
{!isAuthenticated && unAuthNudge}
|
||||||
</div>
|
</div>
|
||||||
|
{helpLinks}
|
||||||
</nav>
|
</nav>
|
||||||
<div
|
<div
|
||||||
className={classnames('navigation__overlay', {
|
className={classnames('navigation__overlay', {
|
||||||
|
|
|
@ -3,10 +3,12 @@ import { selectFollowedTags } from 'redux/selectors/tags';
|
||||||
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
||||||
import { doChannelSubscribe } from 'redux/actions/subscriptions';
|
import { doChannelSubscribe } from 'redux/actions/subscriptions';
|
||||||
import UserChannelFollowIntro from './view';
|
import UserChannelFollowIntro from './view';
|
||||||
|
import { selectHomepageData } from 'redux/selectors/settings';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
followedTags: selectFollowedTags(state),
|
followedTags: selectFollowedTags(state),
|
||||||
subscribedChannels: selectSubscriptions(state),
|
subscribedChannels: selectSubscriptions(state),
|
||||||
|
homepageData: selectHomepageData(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
|
|
@ -13,6 +13,7 @@ type Props = {
|
||||||
onContinue: () => void,
|
onContinue: () => void,
|
||||||
onBack: () => void,
|
onBack: () => void,
|
||||||
channelSubscribe: (sub: Subscription) => void,
|
channelSubscribe: (sub: Subscription) => void,
|
||||||
|
homepageData: any,
|
||||||
};
|
};
|
||||||
|
|
||||||
const channelsToSubscribe = AUTO_FOLLOW_CHANNELS.trim()
|
const channelsToSubscribe = AUTO_FOLLOW_CHANNELS.trim()
|
||||||
|
@ -20,7 +21,8 @@ const channelsToSubscribe = AUTO_FOLLOW_CHANNELS.trim()
|
||||||
.filter(x => x !== '');
|
.filter(x => x !== '');
|
||||||
|
|
||||||
function UserChannelFollowIntro(props: Props) {
|
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;
|
const followingCount = (subscribedChannels && subscribedChannels.length) || 0;
|
||||||
|
|
||||||
// subscribe to lbry
|
// subscribe to lbry
|
||||||
|
@ -56,6 +58,8 @@ function UserChannelFollowIntro(props: Props) {
|
||||||
defaultOrderBy={CS.ORDER_BY_TOP}
|
defaultOrderBy={CS.ORDER_BY_TOP}
|
||||||
defaultFreshness={CS.FRESH_ALL}
|
defaultFreshness={CS.FRESH_ALL}
|
||||||
claimType="channel"
|
claimType="channel"
|
||||||
|
// claimIds={PRIMARY_CONTENT_CHANNEL_IDS} // odysee
|
||||||
|
|
||||||
defaultTags={followingCount > 3 ? CS.TAGS_FOLLOWED : undefined}
|
defaultTags={followingCount > 3 ? CS.TAGS_FOLLOWED : undefined}
|
||||||
/>
|
/>
|
||||||
{followingCount > 0 && (
|
{followingCount > 0 && (
|
||||||
|
|
|
@ -129,3 +129,4 @@ export const SLIME = 'Slime';
|
||||||
export const PIN = 'Pin';
|
export const PIN = 'Pin';
|
||||||
export const BEST = 'Best';
|
export const BEST = 'Best';
|
||||||
export const CREATOR_LIKE = 'CreatorLike';
|
export const CREATOR_LIKE = 'CreatorLike';
|
||||||
|
export const CHEF = 'Chef';
|
||||||
|
|
|
@ -2,12 +2,14 @@ import { connect } from 'react-redux';
|
||||||
import { selectFollowedTags } from 'redux/selectors/tags';
|
import { selectFollowedTags } from 'redux/selectors/tags';
|
||||||
import { selectBlockedChannels } from 'redux/selectors/blocked';
|
import { selectBlockedChannels } from 'redux/selectors/blocked';
|
||||||
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
||||||
|
import { selectHomepageData } from 'redux/selectors/settings';
|
||||||
import ChannelsFollowingManagePage from './view';
|
import ChannelsFollowingManagePage from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
followedTags: selectFollowedTags(state),
|
followedTags: selectFollowedTags(state),
|
||||||
subscribedChannels: selectSubscriptions(state),
|
subscribedChannels: selectSubscriptions(state),
|
||||||
blockedChannels: selectBlockedChannels(state),
|
blockedChannels: selectBlockedChannels(state),
|
||||||
|
homepageData: selectHomepageData(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select)(ChannelsFollowingManagePage);
|
export default connect(select)(ChannelsFollowingManagePage);
|
||||||
|
|
|
@ -13,6 +13,7 @@ type Props = {
|
||||||
followedTags: Array<Tag>,
|
followedTags: Array<Tag>,
|
||||||
subscribedChannels: Array<Subscription>,
|
subscribedChannels: Array<Subscription>,
|
||||||
blockedChannels: Array<string>,
|
blockedChannels: Array<string>,
|
||||||
|
homepageData: any,
|
||||||
};
|
};
|
||||||
|
|
||||||
type ChannelsFollowingItem = {
|
type ChannelsFollowingItem = {
|
||||||
|
@ -23,7 +24,8 @@ type ChannelsFollowingItem = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function ChannelsFollowingDiscover(props: Props) {
|
function ChannelsFollowingDiscover(props: Props) {
|
||||||
const { followedTags, subscribedChannels, blockedChannels } = props;
|
const { followedTags, subscribedChannels, blockedChannels, homepageData } = props;
|
||||||
|
const { PRIMARY_CONTENT_CHANNEL_IDS } = homepageData;
|
||||||
let rowData: Array<ChannelsFollowingItem> = [];
|
let rowData: Array<ChannelsFollowingItem> = [];
|
||||||
const notChannels = subscribedChannels
|
const notChannels = subscribedChannels
|
||||||
.map(({ uri }) => uri)
|
.map(({ uri }) => uri)
|
||||||
|
@ -117,6 +119,7 @@ function ChannelsFollowingDiscover(props: Props) {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<h1 className="claim-grid__title">{__('More Channels')}</h1>
|
<h1 className="claim-grid__title">{__('More Channels')}</h1>
|
||||||
|
{/* odysee: claimIds = PRIMARY_CONTENT_CHANNEL_IDS if simplesite CLD */}
|
||||||
<ClaimListDiscover defaultOrderBy={CS.ORDER_BY_TOP} defaultFreshness={CS.FRESH_ALL} claimType="channel" />
|
<ClaimListDiscover defaultOrderBy={CS.ORDER_BY_TOP} defaultFreshness={CS.FRESH_ALL} claimType="channel" />
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,6 +21,7 @@ type Props = {
|
||||||
doToggleTagFollowDesktop: string => void,
|
doToggleTagFollowDesktop: string => void,
|
||||||
doResolveUri: string => void,
|
doResolveUri: string => void,
|
||||||
isAuthenticated: boolean,
|
isAuthenticated: boolean,
|
||||||
|
dynamicRouteProps: RowDataItem,
|
||||||
tileLayout: boolean,
|
tileLayout: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ function DiscoverPage(props: Props) {
|
||||||
doResolveUri,
|
doResolveUri,
|
||||||
isAuthenticated,
|
isAuthenticated,
|
||||||
tileLayout,
|
tileLayout,
|
||||||
|
dynamicRouteProps,
|
||||||
} = props;
|
} = props;
|
||||||
const buttonRef = useRef();
|
const buttonRef = useRef();
|
||||||
const isHovering = useHover(buttonRef);
|
const isHovering = useHover(buttonRef);
|
||||||
|
@ -83,8 +85,8 @@ function DiscoverPage(props: Props) {
|
||||||
} else {
|
} else {
|
||||||
headerLabel = (
|
headerLabel = (
|
||||||
<span>
|
<span>
|
||||||
<Icon icon={ICONS.DISCOVER} size={10} />
|
<Icon icon={(dynamicRouteProps && dynamicRouteProps.icon) || ICONS.DISCOVER} size={10} />
|
||||||
{__('All Content')}
|
{(dynamicRouteProps && dynamicRouteProps.title) || __('All Content')}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -92,14 +94,19 @@ function DiscoverPage(props: Props) {
|
||||||
return (
|
return (
|
||||||
<Page noFooter fullWidthPage={tileLayout}>
|
<Page noFooter fullWidthPage={tileLayout}>
|
||||||
<ClaimListDiscover
|
<ClaimListDiscover
|
||||||
|
limitClaimsPerChannel={3}
|
||||||
header={repostedUri ? <span /> : undefined}
|
header={repostedUri ? <span /> : undefined}
|
||||||
tileLayout={repostedUri ? false : tileLayout}
|
tileLayout={repostedUri ? false : tileLayout}
|
||||||
|
defaultOrderBy={CS.ORDER_BY_NEW}
|
||||||
claimType={claimType ? [claimType] : undefined}
|
claimType={claimType ? [claimType] : undefined}
|
||||||
headerLabel={headerLabel}
|
headerLabel={headerLabel}
|
||||||
tags={tags}
|
tags={tags}
|
||||||
hiddenNsfwMessage={<HiddenNsfw type="page" />}
|
hiddenNsfwMessage={<HiddenNsfw type="page" />}
|
||||||
repostedClaimId={repostedClaim ? repostedClaim.claim_id : null}
|
repostedClaimId={repostedClaim ? repostedClaim.claim_id : null}
|
||||||
injectedItem={SHOW_ADS && !isAuthenticated && IS_WEB && <Ads type="video" />}
|
injectedItem={SHOW_ADS && !isAuthenticated && IS_WEB && <Ads type="video" />}
|
||||||
|
channelIds={
|
||||||
|
(dynamicRouteProps && dynamicRouteProps.options && dynamicRouteProps.options.channelIds) || undefined
|
||||||
|
}
|
||||||
meta={
|
meta={
|
||||||
tag &&
|
tag &&
|
||||||
!isMobile && (
|
!isMobile && (
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { connect } from 'react-redux';
|
||||||
import { selectFollowedTags } from 'redux/selectors/tags';
|
import { selectFollowedTags } from 'redux/selectors/tags';
|
||||||
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||||
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectHomepageData } from 'redux/selectors/settings';
|
||||||
|
|
||||||
import DiscoverPage from './view';
|
import DiscoverPage from './view';
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ const select = state => ({
|
||||||
subscribedChannels: selectSubscriptions(state),
|
subscribedChannels: selectSubscriptions(state),
|
||||||
authenticated: selectUserVerifiedEmail(state),
|
authenticated: selectUserVerifiedEmail(state),
|
||||||
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_MATURE)(state),
|
showNsfw: makeSelectClientSetting(SETTINGS.SHOW_MATURE)(state),
|
||||||
|
homepageData: selectHomepageData(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = {};
|
const perform = {};
|
||||||
|
|
|
@ -1,27 +1,32 @@
|
||||||
// @flow
|
// @flow
|
||||||
import type { RowDataItem } from 'homepage';
|
// import type { RowDataItem } from 'homepages';
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
import * as PAGES from 'constants/pages';
|
import * as PAGES from 'constants/pages';
|
||||||
import { SITE_NAME } from 'config';
|
import { SITE_NAME } from 'config';
|
||||||
|
import classnames from 'classnames';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Page from 'component/page';
|
import Page from 'component/page';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import ClaimTilesDiscover from 'component/claimTilesDiscover';
|
import ClaimTilesDiscover from 'component/claimTilesDiscover';
|
||||||
import getHomepage from 'homepage';
|
|
||||||
import Icon from 'component/common/icon';
|
import Icon from 'component/common/icon';
|
||||||
|
import { useIsLargeScreen } from 'effects/use-screensize';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
authenticated: boolean,
|
authenticated: boolean,
|
||||||
followedTags: Array<Tag>,
|
followedTags: Array<Tag>,
|
||||||
subscribedChannels: Array<Subscription>,
|
subscribedChannels: Array<Subscription>,
|
||||||
showNsfw: boolean,
|
showNsfw: boolean,
|
||||||
|
homepageData: any,
|
||||||
};
|
};
|
||||||
|
|
||||||
function HomePage(props: Props) {
|
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 showPersonalizedChannels = (authenticated || !IS_WEB) && subscribedChannels && subscribedChannels.length > 0;
|
||||||
const showPersonalizedTags = (authenticated || !IS_WEB) && followedTags && followedTags.length > 0;
|
const showPersonalizedTags = (authenticated || !IS_WEB) && followedTags && followedTags.length > 0;
|
||||||
const showIndividualTags = showPersonalizedTags && followedTags.length < 5;
|
const showIndividualTags = showPersonalizedTags && followedTags.length < 5;
|
||||||
|
const { default: getHomepage } = homepageData;
|
||||||
|
|
||||||
const rowData: Array<RowDataItem> = getHomepage(
|
const rowData: Array<RowDataItem> = getHomepage(
|
||||||
authenticated,
|
authenticated,
|
||||||
|
@ -35,6 +40,7 @@ function HomePage(props: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page fullWidthPage>
|
<Page fullWidthPage>
|
||||||
|
{/* MAYBE NOT SIMPLESITE */}
|
||||||
{(authenticated || !IS_WEB) && !subscribedChannels.length && (
|
{(authenticated || !IS_WEB) && !subscribedChannels.length && (
|
||||||
<div className="notice-message">
|
<div className="notice-message">
|
||||||
<h1 className="section__title">
|
<h1 className="section__title">
|
||||||
|
@ -49,24 +55,36 @@ function HomePage(props: Props) {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{rowData.map(({ title, link, icon, help, options = {} }) => (
|
{rowData.map(({ title, route, link, icon, help, options = {} }, index) => (
|
||||||
<div key={title} className="claim-grid__wrapper">
|
<div key={title} className="claim-grid__wrapper">
|
||||||
|
{index === 0 ? (
|
||||||
|
<div className="section__header--actions">
|
||||||
|
<span
|
||||||
|
className={classnames('claim-grid__title', {
|
||||||
|
'claim-grid__title--first': index === 0,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
<h1 className="claim-grid__header">
|
<h1 className="claim-grid__header">
|
||||||
<Button navigate={link} button="link">
|
<Button navigate={route || link} button="link">
|
||||||
{icon && <Icon className="claim-grid__header-icon" sectionIcon icon={icon} size={20} />}
|
{icon && <Icon className="claim-grid__header-icon" sectionIcon icon={icon} size={20} />}
|
||||||
<span className="claim-grid__title">{title}</span>
|
<span className="claim-grid__title">{__(title)}</span>
|
||||||
{help}
|
{help}
|
||||||
</Button>
|
</Button>
|
||||||
</h1>
|
</h1>
|
||||||
|
)}
|
||||||
|
|
||||||
<ClaimTilesDiscover {...options} />
|
<ClaimTilesDiscover {...options} pageSize={isLargeScreen ? options.pageSize * (3 / 2) : options.pageSize} />
|
||||||
{link && (
|
{link && (
|
||||||
<Button
|
<Button
|
||||||
className="claim-grid__title--secondary"
|
className="claim-grid__title--secondary"
|
||||||
button="link"
|
button="link"
|
||||||
navigate={link}
|
navigate={route || link}
|
||||||
iconRight={ICONS.ARROW_RIGHT}
|
iconRight={ICONS.ARROW_RIGHT}
|
||||||
label={title}
|
label={__('View More')}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,10 +10,13 @@ import Page from 'component/page';
|
||||||
import SettingLanguage from 'component/settingLanguage';
|
import SettingLanguage from 'component/settingLanguage';
|
||||||
import FileSelector from 'component/common/file-selector';
|
import FileSelector from 'component/common/file-selector';
|
||||||
import SyncToggle from 'component/syncToggle';
|
import SyncToggle from 'component/syncToggle';
|
||||||
|
import HomepageSelector from 'component/homepageSelector';
|
||||||
import Card from 'component/common/card';
|
import Card from 'component/common/card';
|
||||||
import SettingAccountPassword from 'component/settingAccountPassword';
|
import SettingAccountPassword from 'component/settingAccountPassword';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { getPasswordFromCookie } from 'util/saved-passwords';
|
import { getPasswordFromCookie } from 'util/saved-passwords';
|
||||||
|
// $FlowFixMe
|
||||||
|
import homepages from 'homepages';
|
||||||
import { Lbryio } from 'lbryinc';
|
import { Lbryio } from 'lbryinc';
|
||||||
import Yrbl from 'component/yrbl';
|
import Yrbl from 'component/yrbl';
|
||||||
|
|
||||||
|
@ -214,6 +217,9 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
) : (
|
) : (
|
||||||
<div className={classnames({ 'card--disabled': IS_WEB && !isAuthenticated })}>
|
<div className={classnames({ 'card--disabled': IS_WEB && !isAuthenticated })}>
|
||||||
<Card title={__('Language')} actions={<SettingLanguage />} />
|
<Card title={__('Language')} actions={<SettingLanguage />} />
|
||||||
|
{homepages && Object.keys(homepages).length > 1 && (
|
||||||
|
<Card title={__('Homepage')} actions={<HomepageSelector />} />
|
||||||
|
)}
|
||||||
{isAuthenticated && <SettingAccountPassword />}
|
{isAuthenticated && <SettingAccountPassword />}
|
||||||
{/* @if TARGET='app' */}
|
{/* @if TARGET='app' */}
|
||||||
<Card
|
<Card
|
||||||
|
|
|
@ -4,6 +4,9 @@ import SUPPORTED_LANGUAGES from 'constants/supported_languages';
|
||||||
import { ACTIONS as LBRY_REDUX_ACTIONS, SETTINGS, SHARED_PREFERENCES } from 'lbry-redux';
|
import { ACTIONS as LBRY_REDUX_ACTIONS, SETTINGS, SHARED_PREFERENCES } from 'lbry-redux';
|
||||||
import { getSubsetFromKeysArray } from 'util/sync-settings';
|
import { getSubsetFromKeysArray } from 'util/sync-settings';
|
||||||
import { UNSYNCED_SETTINGS } from 'config';
|
import { UNSYNCED_SETTINGS } from 'config';
|
||||||
|
import homepages from 'homepages';
|
||||||
|
|
||||||
|
const homepageKeys = Object.keys(homepages);
|
||||||
const { CLIENT_SYNC_KEYS } = SHARED_PREFERENCES;
|
const { CLIENT_SYNC_KEYS } = SHARED_PREFERENCES;
|
||||||
const settingsToIgnore = (UNSYNCED_SETTINGS && UNSYNCED_SETTINGS.trim().split(' ')) || [];
|
const settingsToIgnore = (UNSYNCED_SETTINGS && UNSYNCED_SETTINGS.trim().split(' ')) || [];
|
||||||
const clientSyncKeys = settingsToIgnore.length
|
const clientSyncKeys = settingsToIgnore.length
|
||||||
|
@ -40,6 +43,7 @@ const defaultState = {
|
||||||
[SETTINGS.SEARCH_IN_LANGUAGE]: false,
|
[SETTINGS.SEARCH_IN_LANGUAGE]: false,
|
||||||
[SETTINGS.THEME]: __('light'),
|
[SETTINGS.THEME]: __('light'),
|
||||||
[SETTINGS.THEMES]: [__('light'), __('dark')],
|
[SETTINGS.THEMES]: [__('light'), __('dark')],
|
||||||
|
[SETTINGS.HOMEPAGE]: settingLanguage.find(language => homepageKeys.includes(language)) || 'en',
|
||||||
[SETTINGS.HIDE_SPLASH_ANIMATION]: false,
|
[SETTINGS.HIDE_SPLASH_ANIMATION]: false,
|
||||||
[SETTINGS.HIDE_BALANCE]: false,
|
[SETTINGS.HIDE_BALANCE]: false,
|
||||||
[SETTINGS.OS_NOTIFICATIONS_ENABLED]: true,
|
[SETTINGS.OS_NOTIFICATIONS_ENABLED]: true,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { SETTINGS, DAEMON_SETTINGS } from 'lbry-redux';
|
import { SETTINGS, DAEMON_SETTINGS } from 'lbry-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
import homepages from 'homepages';
|
||||||
|
|
||||||
const selectState = state => state.settings || {};
|
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);
|
export const selectosNotificationsEnabled = makeSelectClientSetting(SETTINGS.OS_NOTIFICATIONS_ENABLED);
|
||||||
|
|
|
@ -252,8 +252,11 @@ svg + .button__label {
|
||||||
&:last-of-type {
|
&:last-of-type {
|
||||||
border-top-right-radius: var(--border-radius);
|
border-top-right-radius: var(--border-radius);
|
||||||
border-bottom-right-radius: var(--border-radius);
|
border-bottom-right-radius: var(--border-radius);
|
||||||
|
// since we're abusing "button-toggle" let it stand alone properly
|
||||||
|
&:not(:first-of-type) {
|
||||||
margin-right: var(--spacing-s);
|
margin-right: var(--spacing-s);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-toggle--expandformobile {
|
.button-toggle--expandformobile {
|
||||||
|
|
|
@ -75,7 +75,8 @@ let baseConfig = {
|
||||||
extensions: ['.js', '.jsx', '.json', '.scss'],
|
extensions: ['.js', '.jsx', '.json', '.scss'],
|
||||||
alias: {
|
alias: {
|
||||||
config: path.resolve(__dirname, 'config.js'),
|
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',
|
lbryinc: 'lbryinc/dist/bundle.es.js',
|
||||||
// Build optimizations for 'redux-persist-transform-filter'
|
// Build optimizations for 'redux-persist-transform-filter'
|
||||||
'redux-persist-transform-filter': 'redux-persist-transform-filter/index.js',
|
'redux-persist-transform-filter': 'redux-persist-transform-filter/index.js',
|
||||||
|
|
Loading…
Reference in a new issue