basic channel discovery in first run
This commit is contained in:
parent
cf4dc25f34
commit
3e08d8e231
14 changed files with 109 additions and 24 deletions
|
@ -70,14 +70,14 @@ export default function ClaimList(props: Props) {
|
|||
useEffect(() => {
|
||||
function handleScroll(e) {
|
||||
if (page && pageSize && onScrollBottom && !scrollBottomCbMap[page]) {
|
||||
const x = document.querySelector(`.${MAIN_WRAPPER_CLASS}`);
|
||||
const mc = document.querySelector(`.${MAIN_CLASS}`);
|
||||
const mainElWrapper = document.querySelector(`.${MAIN_WRAPPER_CLASS}`);
|
||||
const main = document.querySelector(`.${MAIN_CLASS}`);
|
||||
|
||||
if (
|
||||
x &&
|
||||
mc &&
|
||||
(window.scrollY + window.innerHeight >= x.offsetHeight ||
|
||||
x.offsetHeight - mc.offsetHeight > PADDING_ALLOWANCE) &&
|
||||
mainElWrapper &&
|
||||
main &&
|
||||
(window.scrollY + window.innerHeight >= mainElWrapper.offsetHeight ||
|
||||
mainElWrapper.offsetHeight - main.offsetHeight > PADDING_ALLOWANCE) &&
|
||||
!loading &&
|
||||
urisLength >= pageSize
|
||||
) {
|
||||
|
|
|
@ -50,9 +50,11 @@ type Props = {
|
|||
header?: Node,
|
||||
headerLabel?: string | Node,
|
||||
name?: string,
|
||||
pageSize?: number,
|
||||
claimType?: string | Array<string>,
|
||||
renderProperties?: Claim => Node,
|
||||
includeSupportAction?: boolean,
|
||||
noInfiniteScroll: boolean,
|
||||
};
|
||||
|
||||
function ClaimListDiscover(props: Props) {
|
||||
|
@ -76,8 +78,10 @@ function ClaimListDiscover(props: Props) {
|
|||
header,
|
||||
name,
|
||||
claimType,
|
||||
pageSize,
|
||||
renderProperties,
|
||||
includeSupportAction,
|
||||
noInfiniteScroll,
|
||||
} = props;
|
||||
const didNavigateForward = history.action === 'PUSH';
|
||||
const [page, setPage] = useState(1);
|
||||
|
@ -100,7 +104,7 @@ function ClaimListDiscover(props: Props) {
|
|||
name?: string,
|
||||
claim_type?: string | Array<string>,
|
||||
} = {
|
||||
page_size: PAGE_SIZE,
|
||||
page_size: pageSize || PAGE_SIZE,
|
||||
page,
|
||||
name,
|
||||
// no_totals makes it so the sdk doesn't have to calculate total number pages for pagination
|
||||
|
@ -226,7 +230,7 @@ function ClaimListDiscover(props: Props) {
|
|||
}
|
||||
|
||||
function handleScrollBottom() {
|
||||
if (!loading) {
|
||||
if (!loading && !noInfiniteScroll) {
|
||||
setPage(page + 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import classnames from 'classnames';
|
|||
import Tag from 'component/tag';
|
||||
|
||||
const SLIM_TAGS = 1;
|
||||
const NORMAL_TAGS = 4;
|
||||
const NORMAL_TAGS = 3;
|
||||
const LARGE_TAGS = 10;
|
||||
|
||||
type Props = {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// @flow
|
||||
import type { Node } from 'react';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import classnames from 'classnames';
|
||||
import React from 'react';
|
||||
import Button from 'component/button';
|
||||
|
||||
|
@ -8,19 +9,20 @@ type Props = {
|
|||
message: string | Node,
|
||||
actionText: string,
|
||||
href?: string,
|
||||
type?: string,
|
||||
onClick?: () => void,
|
||||
onClose?: () => void,
|
||||
};
|
||||
|
||||
export default function Nag(props: Props) {
|
||||
const { message, actionText, href, onClick, onClose } = props;
|
||||
const { message, actionText, href, onClick, onClose, type } = props;
|
||||
|
||||
const buttonProps = onClick ? { onClick } : { href };
|
||||
|
||||
return (
|
||||
<div className="nag">
|
||||
<div className={classnames('nag', { 'nag--helpful': type === 'helpful' })}>
|
||||
{message}
|
||||
<Button className="nag__button" {...buttonProps}>
|
||||
<Button className={classnames('nag__button', { 'nag__button--helpful': type === 'helpful' })} {...buttonProps}>
|
||||
{actionText}
|
||||
</Button>
|
||||
{onClose && <Button className="nag__button nag__close" icon={ICONS.REMOVE} onClick={onClose} />}
|
||||
|
|
|
@ -139,7 +139,9 @@ function AppRouter(props: Props) {
|
|||
{/* @if TARGET='app' */}
|
||||
{welcomeVersion < WELCOME_VERSION && <Route path="/*" component={Welcome} />}
|
||||
{/* @endif */}
|
||||
|
||||
<Redirect from={`/$/${PAGES.CHANNELS_FOLLOWING_MANAGE}`} to={`/$/${PAGES.CHANNELS_FOLLOWING_DISCOVER}`} />
|
||||
|
||||
<Route path={`/`} exact component={HomePage} />
|
||||
<Route path={`/$/${PAGES.DISCOVER}`} exact component={DiscoverPage} />
|
||||
<Route path={`/$/${PAGES.AUTH}`} exact component={SignInPage} />
|
||||
|
|
11
ui/component/userChannelFollowIntro/index.js
Normal file
11
ui/component/userChannelFollowIntro/index.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { selectFollowedTags } from 'lbry-redux';
|
||||
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
||||
import UserChannelFollowIntro from './view';
|
||||
|
||||
const select = state => ({
|
||||
followedTags: selectFollowedTags(state),
|
||||
subscribedChannels: selectSubscriptions(state),
|
||||
});
|
||||
|
||||
export default connect(select)(UserChannelFollowIntro);
|
47
ui/component/userChannelFollowIntro/view.jsx
Normal file
47
ui/component/userChannelFollowIntro/view.jsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import ClaimListDiscover from 'component/claimListDiscover';
|
||||
import { TYPE_TOP, TIME_ALL } from 'component/claimListDiscover/view';
|
||||
import Nag from 'component/common/nag';
|
||||
|
||||
type Props = {
|
||||
subscribedChannels: Array<Subscription>,
|
||||
onContinue: () => void,
|
||||
};
|
||||
|
||||
function UserChannelFollowIntro(props: Props) {
|
||||
const { subscribedChannels, onContinue } = props;
|
||||
const followingCount = (subscribedChannels && subscribedChannels.length) || 0;
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<h1 className="section__title--large">{__('Find Channels to Follow')}</h1>
|
||||
<p className="section__subtitle">
|
||||
{__(
|
||||
'LBRY works better if you find and follow at least 5 creators you like. You can also block channels you never want to see.'
|
||||
)}
|
||||
</p>
|
||||
<ClaimListDiscover
|
||||
defaultTypeSort={TYPE_TOP}
|
||||
defaultTimeSort={TIME_ALL}
|
||||
pageSize={99}
|
||||
claimType="channel"
|
||||
noInfiniteScroll
|
||||
/>
|
||||
{followingCount > 0 && (
|
||||
<Nag
|
||||
type="helpful"
|
||||
message={
|
||||
followingCount === 1
|
||||
? __('Nice! You are currently following %followingCount% creator', { followingCount })
|
||||
: __('Nice! You are currently following %followingCount% creators', { followingCount })
|
||||
}
|
||||
actionText={__('Continue')}
|
||||
onClick={onContinue}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default UserChannelFollowIntro;
|
|
@ -4,6 +4,7 @@ import { withRouter } from 'react-router';
|
|||
import UserEmailNew from 'component/userEmailNew';
|
||||
import UserEmailVerify from 'component/userEmailVerify';
|
||||
import UserFirstChannel from 'component/userFirstChannel';
|
||||
import UserChannelFollowIntro from 'component/userChannelFollowIntro';
|
||||
import { DEFAULT_BID_FOR_FIRST_CHANNEL } from 'component/userFirstChannel/view';
|
||||
import { rewards as REWARDS, YOUTUBE_STATUSES } from 'lbryinc';
|
||||
import UserVerify from 'component/userVerify';
|
||||
|
@ -11,6 +12,7 @@ import Spinner from 'component/spinner';
|
|||
import YoutubeTransferStatus from 'component/youtubeTransferStatus';
|
||||
import SyncPassword from 'component/syncPassword';
|
||||
import useFetched from 'effects/use-fetched';
|
||||
import usePersistedState from 'effects/use-persisted-state';
|
||||
import Confetti from 'react-confetti';
|
||||
|
||||
type Props = {
|
||||
|
@ -58,6 +60,7 @@ function UserSignIn(props: Props) {
|
|||
const redirect = urlParams.get('redirect');
|
||||
const shouldRedirectImmediately = urlParams.get('immediate');
|
||||
const [initialSignInStep, setInitialSignInStep] = React.useState();
|
||||
const [hasSeenFollowList, setHasSeenFollowList] = usePersistedState('channel-follow-intro', false);
|
||||
const hasVerifiedEmail = user && user.has_verified_email;
|
||||
const rewardsApproved = user && user.is_reward_approved;
|
||||
const hasFetchedReward = useFetched(claimingReward);
|
||||
|
@ -73,11 +76,6 @@ function UserSignIn(props: Props) {
|
|||
// We may want to keep a component rendered while something is being fetched, instead of replacing it with the large spinner
|
||||
// The verbose variable names are an attempt to alleviate _some_ of the confusion from handling all edge cases that come from
|
||||
// reward claiming, channel creation, account syncing, and youtube transfer
|
||||
const canHijackSignInFlowWithSpinner = hasVerifiedEmail && !getSyncError;
|
||||
const isCurrentlyFetchingSomething = fetchingChannels || claimingReward || syncingWallet || creatingChannel;
|
||||
const isWaitingForSomethingToFinish =
|
||||
// If the user has claimed the email award, we need to wait until the balance updates sometime in the future
|
||||
(!hasFetchedReward && !hasClaimedEmailAward) || (syncEnabled && !hasSynced);
|
||||
// The possible screens for the sign in flow
|
||||
const showEmail = !emailToVerify && !hasVerifiedEmail;
|
||||
const showEmailVerification = emailToVerify && !hasVerifiedEmail;
|
||||
|
@ -91,6 +89,12 @@ function UserSignIn(props: Props) {
|
|||
channelCount === 0 &&
|
||||
!hasYoutubeChannels;
|
||||
const showYoutubeTransfer = hasVerifiedEmail && hasYoutubeChannels && !isYoutubeTransferComplete;
|
||||
const showFollowIntro = hasVerifiedEmail && !hasSeenFollowList && channelCount > 0;
|
||||
const canHijackSignInFlowWithSpinner = hasVerifiedEmail && !getSyncError && !showFollowIntro;
|
||||
const isCurrentlyFetchingSomething = fetchingChannels || claimingReward || syncingWallet || creatingChannel;
|
||||
const isWaitingForSomethingToFinish =
|
||||
// If the user has claimed the email award, we need to wait until the balance updates sometime in the future
|
||||
(!hasFetchedReward && !hasClaimedEmailAward) || (syncEnabled && !hasSynced);
|
||||
const showLoadingSpinner =
|
||||
canHijackSignInFlowWithSpinner && (isCurrentlyFetchingSomething || isWaitingForSomethingToFinish);
|
||||
|
||||
|
@ -115,6 +119,7 @@ function UserSignIn(props: Props) {
|
|||
showEmailVerification && <UserEmailVerify />,
|
||||
showUserVerification && <UserVerify skipLink={redirect} />,
|
||||
showChannelCreation && <UserFirstChannel />,
|
||||
showFollowIntro && <UserChannelFollowIntro onContinue={() => setHasSeenFollowList(true)} />,
|
||||
showYoutubeTransfer && (
|
||||
<div>
|
||||
<YoutubeTransferStatus /> <Confetti recycle={false} style={{ position: 'fixed' }} />
|
||||
|
|
|
@ -24,7 +24,7 @@ exports.TAGS_FOLLOWING = 'following/tags';
|
|||
exports.TAGS_FOLLOWING_MANAGE = 'following/tags/manage';
|
||||
exports.CHANNELS_FOLLOWING = 'following/channels';
|
||||
exports.CHANNELS_FOLLOWING_MANAGE = 'following/channels/manage';
|
||||
exports.CHANNELS_FOLLOWING_DISCOVER = 'following/channels/discover';
|
||||
exports.CHANNELS_FOLLOWING_DISCOVER = 'following/discover';
|
||||
exports.WALLET = 'wallet';
|
||||
exports.BLOCKED = 'blocked';
|
||||
exports.CHANNELS = 'channels';
|
||||
|
|
|
@ -31,7 +31,7 @@ function ChannelsFollowingDiscover(props: Props) {
|
|||
rowData.push({
|
||||
title: 'Top Channels Of All Time',
|
||||
options: {
|
||||
pageSize: 8,
|
||||
pageSize: 12,
|
||||
claimType: 'channel',
|
||||
orderBy: ['effective_amount'],
|
||||
},
|
||||
|
@ -50,7 +50,7 @@ function ChannelsFollowingDiscover(props: Props) {
|
|||
rowData.push({
|
||||
title: 'Trending Channels',
|
||||
options: {
|
||||
pageSize: 4,
|
||||
pageSize: 8,
|
||||
claimType: 'channel',
|
||||
orderBy: ['trending_group', 'trending_mixed'],
|
||||
},
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
.wunderbar {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
cursor: text;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
.nag {
|
||||
z-index: 9999;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding: var(--spacing-small);
|
||||
|
@ -16,6 +17,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.nag--helpful {
|
||||
background-color: var(--color-secondary);
|
||||
color: var(--color-white);
|
||||
}
|
||||
|
||||
.nag__button {
|
||||
line-height: 1;
|
||||
margin-left: var(--spacing-small);
|
||||
|
@ -27,7 +33,17 @@
|
|||
|
||||
&:hover {
|
||||
background-color: var(--color-white);
|
||||
color: var(--color-nag);
|
||||
color: var(--color-white);
|
||||
}
|
||||
}
|
||||
|
||||
.nag__button--helpful {
|
||||
// color: var(--color-primary);
|
||||
// border-color: var(--color-primary);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-secondary-alt);
|
||||
color: var(--color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
// Search
|
||||
--color-search-suggestion: #212529;
|
||||
--color-search-suggestion-background: #cce6fb;
|
||||
--color-wunderbar: var(--color-primary);
|
||||
|
||||
// Snack
|
||||
--color-snack-bg: var(--color-primary);
|
||||
|
|
Loading…
Reference in a new issue