From ba675f48c9d94de211f93aa82ffd382fdfc70f0f Mon Sep 17 00:00:00 2001 From: Sean Yesmunt Date: Mon, 20 Jan 2020 11:47:03 -0500 Subject: [PATCH] new homepage --- lbrytv/middleware/redirect.js | 5 - static/app-strings.json | 3 +- ui/component/claimPreviewTile/index.js | 32 ++++ ui/component/claimPreviewTile/view.jsx | 167 ++++++++++++++++++++ ui/component/claimTilesDiscover/index.js | 28 ++++ ui/component/claimTilesDiscover/view.jsx | 99 ++++++++++++ ui/component/common/icon-custom.jsx | 19 ++- ui/component/common/icon.jsx | 5 +- ui/component/fileThumbnail/view.jsx | 9 +- ui/component/header/view.jsx | 8 +- ui/component/router/view.jsx | 4 +- ui/component/sideNavigation/view.jsx | 3 + ui/constants/icons.js | 1 + ui/page/home/index.js | 2 +- ui/page/home/view.jsx | 188 +++++++++++++++++++++-- ui/scss/component/_button.scss | 1 + ui/scss/component/_claim-list.scss | 114 ++++++++++++++ ui/scss/component/_comments.scss | 2 +- ui/scss/component/_media.scss | 1 + ui/scss/component/_placeholder.scss | 11 ++ 20 files changed, 663 insertions(+), 39 deletions(-) create mode 100644 ui/component/claimPreviewTile/index.js create mode 100644 ui/component/claimPreviewTile/view.jsx create mode 100644 ui/component/claimTilesDiscover/index.js create mode 100644 ui/component/claimTilesDiscover/view.jsx diff --git a/lbrytv/middleware/redirect.js b/lbrytv/middleware/redirect.js index 0640a63d9..6a2445478 100644 --- a/lbrytv/middleware/redirect.js +++ b/lbrytv/middleware/redirect.js @@ -40,11 +40,6 @@ async function redirectMiddleware(ctx, next) { return; } - if (path === '/') { - ctx.redirect(`/$/${PAGES.CHANNELS_FOLLOWING}`); - return; - } - // No redirects needed await next(); } diff --git a/static/app-strings.json b/static/app-strings.json index d78058540..5eb653f57 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -905,5 +905,6 @@ "This Month": "This Month", "This Year": "This Year", "Khmer": "Khmer", - "Invites": "Invites" + "Invites": "Invites", + "This is an experiment, and may be removed in the future. Publish something with the #homepagecagematch tag to battle for the top spot on the home page!": "This is an experiment, and may be removed in the future. Publish something with the #homepagecagematch tag to battle for the top spot on the home page!" } \ No newline at end of file diff --git a/ui/component/claimPreviewTile/index.js b/ui/component/claimPreviewTile/index.js new file mode 100644 index 000000000..a3b85dde4 --- /dev/null +++ b/ui/component/claimPreviewTile/index.js @@ -0,0 +1,32 @@ +import { connect } from 'react-redux'; +import { + doResolveUri, + makeSelectClaimForUri, + makeSelectIsUriResolving, + makeSelectThumbnailForUri, + makeSelectTitleForUri, + doFileGet, + makeSelectChannelForClaimUri, +} from 'lbry-redux'; +import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc'; +import ClaimPreviewTile from './view'; + +const select = (state, props) => ({ + claim: props.uri && makeSelectClaimForUri(props.uri)(state), + channel: props.uri && makeSelectChannelForClaimUri(props.uri)(state), + isResolvingUri: props.uri && makeSelectIsUriResolving(props.uri)(state), + thumbnail: props.uri && makeSelectThumbnailForUri(props.uri)(state), + title: props.uri && makeSelectTitleForUri(props.uri)(state), + blackListedOutpoints: selectBlackListedOutpoints(state), + filteredOutpoints: selectFilteredOutpoints(state), +}); + +const perform = dispatch => ({ + resolveUri: uri => dispatch(doResolveUri(uri)), + getFile: uri => dispatch(doFileGet(uri, false)), +}); + +export default connect( + select, + perform +)(ClaimPreviewTile); diff --git a/ui/component/claimPreviewTile/view.jsx b/ui/component/claimPreviewTile/view.jsx new file mode 100644 index 000000000..eb0da0d83 --- /dev/null +++ b/ui/component/claimPreviewTile/view.jsx @@ -0,0 +1,167 @@ +// @flow +import React from 'react'; +import classnames from 'classnames'; +import { NavLink, withRouter } from 'react-router-dom'; +import FileThumbnail from 'component/fileThumbnail'; +import UriIndicator from 'component/uriIndicator'; +import TruncatedText from 'component/common/truncated-text'; +import DateTime from 'component/dateTime'; +import ChannelThumbnail from 'component/channelThumbnail'; +import SubscribeButton from 'component/subscribeButton'; +import useGetThumbnail from 'effects/use-get-thumbnail'; +import { formatLbryUrlForWeb } from 'util/url'; +import { parseURI } from 'lbry-redux'; + +type Props = { + uri: string, + claim: ?Claim, + resolveUri: string => void, + isResolvingUri: boolean, + history: { push: string => void }, + thumbnail: string, + title: string, + placeholder: boolean, + blackListedOutpoints: Array<{ + txid: string, + nout: number, + }>, + filteredOutpoints: Array<{ + txid: string, + nout: number, + }>, + blockedChannelUris: Array, + getFile: string => void, + placeholder: boolean, + streamingUrl: string, +}; + +function ClaimPreviewTile(props: Props) { + const { + history, + uri, + isResolvingUri, + thumbnail, + title, + resolveUri, + claim, + placeholder, + blackListedOutpoints, + filteredOutpoints, + getFile, + streamingUrl, + } = props; + const shouldFetch = claim === undefined; + const thumbnailUrl = useGetThumbnail(uri, claim, streamingUrl, getFile, placeholder) || thumbnail; + const navigateUrl = uri ? formatLbryUrlForWeb(uri) : undefined; + const claimsInChannel = (claim && claim.meta.claims_in_channel) || 0; + + let isChannel; + let isValid = false; + if (uri) { + try { + ({ isChannel } = parseURI(uri)); + isValid = true; + } catch (e) { + isValid = false; + } + } + + const signingChannel = claim && claim.signing_channel; + let channelThumbnail; + if (signingChannel) { + channelThumbnail = + // I should be able to just pass the the uri to but it wasn't working + // Come back to me + (signingChannel.value && signingChannel.value.thumbnail && signingChannel.value.thumbnail.url) || undefined; + } + + function handleClick(e) { + if (navigateUrl) { + history.push(navigateUrl); + } + } + + React.useEffect(() => { + if (isValid && !isResolvingUri && shouldFetch && uri) { + resolveUri(uri); + } + }, [isValid, isResolvingUri, uri, resolveUri, shouldFetch]); + + let shouldHide = false; + + // This will be replaced once blocking is done at the wallet server level + if (claim && !shouldHide && blackListedOutpoints) { + shouldHide = blackListedOutpoints.some( + outpoint => + (signingChannel && outpoint.txid === signingChannel.txid && outpoint.nout === signingChannel.nout) || + (outpoint.txid === claim.txid && outpoint.nout === claim.nout) + ); + } + // We're checking to see if the stream outpoint + // or signing channel outpoint is in the filter list + if (claim && !shouldHide && filteredOutpoints) { + shouldHide = filteredOutpoints.some( + outpoint => + (signingChannel && outpoint.txid === signingChannel.txid && outpoint.nout === signingChannel.nout) || + (outpoint.txid === claim.txid && outpoint.nout === claim.nout) + ); + } + + if (shouldHide) { + return null; + } + + if (placeholder || isResolvingUri) { + return ( +
  • +
    +
    +
    +
    +
    +
  • + ); + } + + return ( +
  • + + + + +

    + +

    +
    +
    + {isChannel ? ( +
    + + + {claimsInChannel === 1 + ? __('%claimsInChannel% publish', { claimsInChannel }) + : __('%claimsInChannel% publishes', { claimsInChannel })} + +
    + ) : ( + + + +
    + + +
    +
    + )} +
    +
  • + ); +} + +export default withRouter(ClaimPreviewTile); diff --git a/ui/component/claimTilesDiscover/index.js b/ui/component/claimTilesDiscover/index.js new file mode 100644 index 000000000..bcf0a521c --- /dev/null +++ b/ui/component/claimTilesDiscover/index.js @@ -0,0 +1,28 @@ +import * as SETTINGS from 'constants/settings'; +import { connect } from 'react-redux'; +import { + doClaimSearch, + selectClaimSearchByQuery, + selectFetchingClaimSearch, + doToggleTagFollow, + selectBlockedChannels, +} from 'lbry-redux'; +import { makeSelectClientSetting } from 'redux/selectors/settings'; +import ClaimListDiscover from './view'; + +const select = state => ({ + claimSearchByQuery: selectClaimSearchByQuery(state), + loading: selectFetchingClaimSearch(state), + showNsfw: makeSelectClientSetting(SETTINGS.SHOW_MATURE)(state), + hiddenUris: selectBlockedChannels(state), +}); + +const perform = { + doClaimSearch, + doToggleTagFollow, +}; + +export default connect( + select, + perform +)(ClaimListDiscover); diff --git a/ui/component/claimTilesDiscover/view.jsx b/ui/component/claimTilesDiscover/view.jsx new file mode 100644 index 000000000..2550125d5 --- /dev/null +++ b/ui/component/claimTilesDiscover/view.jsx @@ -0,0 +1,99 @@ +// @flow +import React from 'react'; +import { createNormalizedClaimSearchKey, MATURE_TAGS } from 'lbry-redux'; +import ClaimPreviewTile from 'component/claimPreviewTile'; + +type Props = { + uris: Array, + doClaimSearch: ({}) => void, + loading: boolean, + showNsfw: boolean, + history: { action: string, push: string => void, replace: string => void }, + claimSearchByQuery: { + [string]: Array, + }, + // claim search options are below + tags: Array, + hiddenUris: Array, + channelIds?: Array, + pageSize: number, + orderBy?: Array, + releaseTime?: string, + claimType?: string, + timestamp?: string, +}; + +function ClaimTilesDiscover(props: Props) { + const { + doClaimSearch, + claimSearchByQuery, + loading, + showNsfw, + hiddenUris, + // Below are options to pass that are forwarded to claim_search + tags, + channelIds, + orderBy, + pageSize = 8, + releaseTime, + claimType, + timestamp, + } = props; + + const options: { + page_size: number, + no_totals: boolean, + any_tags: Array, + channel_ids: Array, + channel_ids: Array, + not_channel_ids: Array, + not_tags: Array, + order_by: Array, + release_time?: string, + claim_type?: string, + timestamp?: string, + } = { + page_size: pageSize, + // no_totals makes it so the sdk doesn't have to calculate total number pages for pagination + // it's faster, but we will need to remove it if we start using total_pages + no_totals: true, + any_tags: tags || [], + not_tags: !showNsfw ? MATURE_TAGS : [], + channel_ids: channelIds || [], + not_channel_ids: hiddenUris && hiddenUris.length ? hiddenUris.map(hiddenUri => hiddenUri.split('#')[1]) : [], + order_by: orderBy || ['trending_group', 'trending_mixed'], + }; + + if (releaseTime) { + options.release_time = releaseTime; + } + + if (claimType) { + options.claim_type = claimType; + } + if (timestamp) { + options.timestamp = timestamp; + } + + const claimSearchCacheQuery = createNormalizedClaimSearchKey(options); + const uris = claimSearchByQuery[claimSearchCacheQuery] || []; + const shouldPerformSearch = uris.length === 0 || (!loading && uris.length < pageSize); + // Don't use the query from createNormalizedClaimSearchKey for the effect since that doesn't include page & release_time + const optionsStringForEffect = JSON.stringify(options); + + React.useEffect(() => { + if (shouldPerformSearch) { + const searchOptions = JSON.parse(optionsStringForEffect); + doClaimSearch(searchOptions); + } + }, [doClaimSearch, shouldPerformSearch, optionsStringForEffect]); + + return ( +
      + {uris && uris.length + ? uris.map(uri => ) + : new Array(pageSize).fill(1).map((x, i) => )} +
    + ); +} +export default ClaimTilesDiscover; diff --git a/ui/component/common/icon-custom.jsx b/ui/component/common/icon-custom.jsx index 40e5a090c..9db647df0 100644 --- a/ui/component/common/icon-custom.jsx +++ b/ui/component/common/icon-custom.jsx @@ -48,18 +48,12 @@ export const icons = { ), [ICONS.ARROW_LEFT]: buildIcon( - - + ), [ICONS.ARROW_RIGHT]: buildIcon( - - + ), [ICONS.HOME]: buildIcon( @@ -364,4 +358,13 @@ export const icons = { ), + [ICONS.SHUFFLE]: buildIcon( + + + + + + + + ), }; diff --git a/ui/component/common/icon.jsx b/ui/component/common/icon.jsx index 4951f050c..2b70126d9 100644 --- a/ui/component/common/icon.jsx +++ b/ui/component/common/icon.jsx @@ -14,6 +14,7 @@ const BLUE_COLOR = '#49b2e2'; type Props = { icon: string, tooltip?: boolean, + customTooltipText?: string, iconColor?: string, size?: number, className?: string, @@ -50,7 +51,7 @@ class IconComponent extends React.PureComponent { }; render() { - const { icon, tooltip, iconColor, size, className, sectionIcon = false } = this.props; + const { icon, tooltip, customTooltipText, iconColor, size, className, sectionIcon = false } = this.props; const Icon = icons[this.props.icon]; if (!Icon) { @@ -66,7 +67,7 @@ class IconComponent extends React.PureComponent { let tooltipText; if (tooltip) { - tooltipText = this.getTooltip(icon); + tooltipText = customTooltipText || this.getTooltip(icon); } const component = ( diff --git a/ui/component/fileThumbnail/view.jsx b/ui/component/fileThumbnail/view.jsx index 0901b25d9..5c720472f 100644 --- a/ui/component/fileThumbnail/view.jsx +++ b/ui/component/fileThumbnail/view.jsx @@ -1,6 +1,6 @@ // @flow import React from 'react'; -import FreezeframeWrapper from './FreezeframeWrapper'; +// import FreezeframeWrapper from './FreezeframeWrapper'; import Placeholder from './placeholder.png'; type Props = { @@ -13,9 +13,10 @@ class CardMedia extends React.PureComponent { render() { const { thumbnail } = this.props; - if (thumbnail && thumbnail.endsWith('gif')) { - return ; - } + // Disabling temporarily to see if people complain + // if (thumbnail && thumbnail.endsWith('gif')) { + // return ; + // } let url; // @if TARGET='web' diff --git a/ui/component/header/view.jsx b/ui/component/header/view.jsx index 9ab0277e1..f5afafb13 100644 --- a/ui/component/header/view.jsx +++ b/ui/component/header/view.jsx @@ -54,12 +54,8 @@ const Header = (props: Props) => { const isAuthPage = history.location.pathname.includes(PAGES.AUTH); // Sign out if they click the "x" when they are on the password prompt - const authHeaderAction = syncError ? { onClick: signOut } : { navigate: `/$/${PAGES.CHANNELS_FOLLOWING}` }; - const homeButtonNavigationProps = isVerifyPage - ? {} - : authHeader - ? authHeaderAction - : { navigate: `/$/${PAGES.CHANNELS_FOLLOWING}` }; + const authHeaderAction = syncError ? { onClick: signOut } : { navigate: '/' }; + const homeButtonNavigationProps = isVerifyPage ? {} : authHeader ? authHeaderAction : { navigate: '/' }; const closeButtonNavigationProps = authHeader ? authHeaderAction : { onClick: () => history.goBack() }; function handleThemeToggle() { diff --git a/ui/component/router/view.jsx b/ui/component/router/view.jsx index 5b4aa5440..2723f59f8 100644 --- a/ui/component/router/view.jsx +++ b/ui/component/router/view.jsx @@ -8,8 +8,8 @@ import ReportPage from 'page/report'; import ShowPage from 'page/show'; import PublishPage from 'page/publish'; import DiscoverPage from 'page/discover'; +import HomePage from 'page/home'; import InvitedPage from 'page/invited'; -// import HomePage from 'page/home'; import RewardsPage from 'page/rewards'; import FileListDownloaded from 'page/fileListDownloaded'; import FileListPublished from 'page/fileListPublished'; @@ -75,7 +75,7 @@ function AppRouter(props: Props) { return ( - } /> + diff --git a/ui/component/sideNavigation/view.jsx b/ui/component/sideNavigation/view.jsx index ce6f8c69d..e20970b93 100644 --- a/ui/component/sideNavigation/view.jsx +++ b/ui/component/sideNavigation/view.jsx @@ -96,6 +96,9 @@ function SideNavigation(props: Props) {