wip with channel prompts on comments

This commit is contained in:
Sean Yesmunt 2020-08-25 15:54:06 -04:00
parent 5beb219ff6
commit a5107f075c
9 changed files with 101 additions and 37 deletions

View file

@ -2,6 +2,7 @@
import * as MODALS from 'constants/modal_types'; import * as MODALS from 'constants/modal_types';
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import React from 'react'; import React from 'react';
import classnames from 'classnames';
import { FormField } from 'component/common/form'; import { FormField } from 'component/common/form';
import Button from 'component/button'; import Button from 'component/button';
import TagsSearch from 'component/tagsSearch'; import TagsSearch from 'component/tagsSearch';
@ -70,6 +71,7 @@ function ChannelForm(props: Props) {
createError, createError,
clearChannelErrors, clearChannelErrors,
openModal, openModal,
disabled,
} = props; } = props;
const [nameError, setNameError] = React.useState(undefined); const [nameError, setNameError] = React.useState(undefined);
const [bidError, setBidError] = React.useState(''); const [bidError, setBidError] = React.useState('');
@ -180,7 +182,7 @@ function ChannelForm(props: Props) {
// TODO clear and bail after submit // TODO clear and bail after submit
return ( return (
<> <>
<div className="main--contained"> <div className={classnames('main--contained', { 'card--disabled': disabled })}>
<header className="channel-cover"> <header className="channel-cover">
<div className="channel__quick-actions"> <div className="channel__quick-actions">
<Button <Button

View file

@ -1,13 +1,14 @@
// @flow // @flow
import { SIMPLE_SITE, SITE_NAME } from 'config';
import * as MODALS from 'constants/modal_types';
import * as PAGES from 'constants/pages';
import { CHANNEL_NEW } from 'constants/claim'; import { CHANNEL_NEW } from 'constants/claim';
import { SIMPLE_SITE } from 'config';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import { FormField, Form } from 'component/common/form'; import { FormField, Form } from 'component/common/form';
import Button from 'component/button'; import Button from 'component/button';
import ChannelSelection from 'component/selectChannel'; import ChannelSelection from 'component/selectChannel';
import usePersistedState from 'effects/use-persisted-state'; import usePersistedState from 'effects/use-persisted-state';
import * as MODALS from 'constants/modal_types';
import I18nMessage from 'component/i18nMessage'; import I18nMessage from 'component/i18nMessage';
import { FF_MAX_CHARS_IN_COMMENT } from 'constants/form-field'; import { FF_MAX_CHARS_IN_COMMENT } from 'constants/form-field';
@ -43,6 +44,7 @@ export function CommentCreate(props: Props) {
const [channel, setChannel] = usePersistedState('comment-channel', ''); const [channel, setChannel] = usePersistedState('comment-channel', '');
const [charCount, setCharCount] = useState(commentValue.length); const [charCount, setCharCount] = useState(commentValue.length);
const [advancedEditor, setAdvancedEditor] = usePersistedState('comment-editor-mode', false); const [advancedEditor, setAdvancedEditor] = usePersistedState('comment-editor-mode', false);
const hasChannels = channels && channels.length;
const topChannel = const topChannel =
channels && channels &&
@ -104,6 +106,18 @@ export function CommentCreate(props: Props) {
); );
} }
if (!hasChannels) {
return (
<div className="notice-message">
<h3 className="section__title">{__('Join the discussion')}</h3>
<p className="section__subtitle">{__('A channel is required to comment on %SITE_NAME%.', { SITE_NAME })}</p>
<div className="section__actions">
<Button button="primary" label={__('Create a channel')} navigate={`/$/${PAGES.CHANNEL_NEW}`} />
</div>
</div>
);
}
return ( return (
<Form <Form
onSubmit={handleSubmit} onSubmit={handleSubmit}

View file

@ -1,9 +1,9 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { makeSelectClaimIsMine, selectMyChannelClaims } from 'lbry-redux'; import { makeSelectClaimIsMine, selectMyChannelClaims } from 'lbry-redux';
import { makeSelectRepliesForParentId } from 'redux/selectors/comments'; import { makeSelectRepliesForParentId } from 'redux/selectors/comments';
import { doToast } from 'redux/actions/notifications';
import CommentsReplies from './view';
import { selectUserVerifiedEmail } from 'redux/selectors/user'; import { selectUserVerifiedEmail } from 'redux/selectors/user';
import CommentsReplies from './view';
const select = (state, props) => ({ const select = (state, props) => ({
myChannels: selectMyChannelClaims(state), myChannels: selectMyChannelClaims(state),
@ -12,4 +12,6 @@ const select = (state, props) => ({
commentingEnabled: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true, commentingEnabled: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true,
}); });
export default connect(select, null)(CommentsReplies); export default connect(select, {
doToast,
})(CommentsReplies);

View file

@ -1,9 +1,12 @@
// @flow // @flow
import { SITE_NAME } from 'config';
import * as ICONS from 'constants/icons';
import * as PAGES from 'constants/pages';
import React from 'react'; import React from 'react';
import Comment from 'component/comment'; import Comment from 'component/comment';
import Button from 'component/button'; import Button from 'component/button';
import * as ICONS from 'constants/icons';
import CommentCreate from 'component/commentCreate'; import CommentCreate from 'component/commentCreate';
import { useHistory } from 'react-router';
type Props = { type Props = {
comments: Array<any>, comments: Array<any>,
@ -13,31 +16,35 @@ type Props = {
linkedComment?: Comment, linkedComment?: Comment,
parentId: string, parentId: string,
commentingEnabled: boolean, commentingEnabled: boolean,
doToast: ({ message: string }) => void,
}; };
function CommentsReplies(props: Props) { function CommentsReplies(props: Props) {
const { uri, comments, claimIsMine, myChannels, linkedComment, parentId, commentingEnabled } = props; const { uri, comments, claimIsMine, myChannels, linkedComment, parentId, commentingEnabled, doToast } = props;
const {
push,
location: { pathname },
} = useHistory();
const [isReplying, setReplying] = React.useState(false); const [isReplying, setReplying] = React.useState(false);
const [isExpanded, setExpanded] = React.useState(false); const [isExpanded, setExpanded] = React.useState(false);
const [start, setStart] = React.useState(0); const [start, setStart] = React.useState(0);
const [end, setEnd] = React.useState(9); const [end, setEnd] = React.useState(9);
const sortedComments = comments ? [...comments].reverse() : []; const sortedComments = comments ? [...comments].reverse() : [];
const numberOfComments = comments ? comments.length : 0; const numberOfComments = comments ? comments.length : 0;
const linkedCommentId = linkedComment ? linkedComment.comment_id : '';
const commentsIndexOfLInked = comments && sortedComments.findIndex(e => e.comment_id === linkedCommentId);
const hasChannels = myChannels && myChannels.length > 0;
const showMore = () => { function showMore() {
if (start > 0) { if (start > 0) {
setStart(0); setStart(0);
} else { } else {
setEnd(numberOfComments); setEnd(numberOfComments);
} }
}; }
const linkedCommentId = linkedComment ? linkedComment.comment_id : '';
const commentsIndexOfLInked = comments && sortedComments.findIndex(e => e.comment_id === linkedCommentId);
// todo: implement comment_list --mine in SDK so redux can grab with selectCommentIsMine // todo: implement comment_list --mine in SDK so redux can grab with selectCommentIsMine
const isMyComment = (channelId: string) => { function isMyComment(channelId: string) {
if (myChannels != null && channelId != null) { if (myChannels != null && channelId != null) {
for (let i = 0; i < myChannels.length; i++) { for (let i = 0; i < myChannels.length; i++) {
if (myChannels[i].claim_id === channelId) { if (myChannels[i].claim_id === channelId) {
@ -46,16 +53,25 @@ function CommentsReplies(props: Props) {
} }
} }
return false; return false;
}; }
const handleCommentDone = () => { function handleCommentDone() {
if (!isExpanded) { if (!isExpanded) {
setExpanded(true); setExpanded(true);
setStart(numberOfComments || 0); setStart(numberOfComments || 0);
} }
setEnd(numberOfComments + 1); setEnd(numberOfComments + 1);
setReplying(false); setReplying(false);
}; }
function handleCommentReply() {
if (!hasChannels) {
push(`/$/${PAGES.CHANNEL_NEW}?redirect=${pathname}`);
doToast({ message: __('A channel is required to comment on %SITE_NAME%', { SITE_NAME }) });
} else {
setReplying(!isReplying);
}
}
React.useEffect(() => { React.useEffect(() => {
if ( if (
@ -75,13 +91,13 @@ function CommentsReplies(props: Props) {
const displayedComments = sortedComments.slice(start, end); const displayedComments = sortedComments.slice(start, end);
return ( return (
<li className={'comment__replies-container'}> <li className="comment__replies-container">
<div className="comment__actions"> <div className="comment__actions">
<Button <Button
requiresAuth={IS_WEB} requiresAuth={IS_WEB}
label={commentingEnabled ? __('Reply') : __('Log in to reply')} label={commentingEnabled ? __('Reply') : __('Log in to reply')}
className="comment__action" className="comment__action"
onClick={() => setReplying(!isReplying)} onClick={handleCommentReply}
icon={ICONS.REPLY} icon={ICONS.REPLY}
/> />
{!isExpanded && Boolean(numberOfComments) && ( {!isExpanded && Boolean(numberOfComments) && (
@ -124,7 +140,7 @@ function CommentsReplies(props: Props) {
requiresAuth={IS_WEB} requiresAuth={IS_WEB}
label={commentingEnabled ? __('Reply') : __('Log in to reply')} label={commentingEnabled ? __('Reply') : __('Log in to reply')}
className="comment__action--nested" className="comment__action--nested"
onClick={() => setReplying(!isReplying)} onClick={handleCommentReply}
icon={ICONS.REPLY} icon={ICONS.REPLY}
/> />
)} )}

View file

@ -20,6 +20,7 @@ type Props = {
function InviteNew(props: Props) { function InviteNew(props: Props) {
const { inviteNew, errorMessage, isPending, referralCode = '', channels } = props; const { inviteNew, errorMessage, isPending, referralCode = '', channels } = props;
const noChannels = !channels || !(channels.length > 0);
// Email // Email
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
@ -77,13 +78,15 @@ function InviteNew(props: Props) {
actions={ actions={
<React.Fragment> <React.Fragment>
<CopyableText label={__('Your invite link')} copyable={referral} /> <CopyableText label={__('Your invite link')} copyable={referral} />
<SelectChannel {!noChannels && (
channel={referralSource} <SelectChannel
onChannelChange={channel => handleReferralChange(channel)} channel={referralSource}
label={__('Customize link')} onChannelChange={channel => handleReferralChange(channel)}
hideAnon label={__('Customize link')}
injected={[referralCode]} hideAnon
/> injected={[referralCode]}
/>
)}
<p className="help"> <p className="help">
<I18nMessage <I18nMessage

View file

@ -1,7 +1,9 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { selectBalance } from 'lbry-redux';
import ChannelNew from './view'; import ChannelNew from './view';
const select = () => ({}); const select = state => ({
const perform = () => ({}); balance: selectBalance(state),
});
export default connect(select, perform)(ChannelNew); export default connect(select)(ChannelNew);

View file

@ -2,20 +2,39 @@
import React from 'react'; import React from 'react';
import ChannelEdit from 'component/channelEdit'; import ChannelEdit from 'component/channelEdit';
import Page from 'component/page'; import Page from 'component/page';
import { withRouter } from 'react-router'; import Button from 'component/button';
import { useHistory } from 'react-router';
import * as PAGES from 'constants/pages'; import * as PAGES from 'constants/pages';
type Props = { type Props = {
history: { push: string => void, goBack: () => void }, balance: number,
}; };
function ChannelNew(props: Props) { function ChannelNew(props: Props) {
const { history } = props; const { balance } = props;
const { push, location } = useHistory();
const urlSearchParams = new URLSearchParams(location.search);
const redirectUrl = urlSearchParams.get('redirect');
const emptyBalance = balance === 0;
return ( return (
<Page noSideNavigation authPage backout={{ title: __('Create Channel') }}> <Page noSideNavigation noFooter backout={{ title: __('Create A Channel'), backLabel: __('Cancel') }}>
<ChannelEdit onDone={() => history.push(`/$/${PAGES.CHANNELS}`)} /> {emptyBalance && (
<div className="main--contained">
<div className="notice-message--above-content">
<h1 className="section__title">You need LBC for this</h1>
<h1 className="section__subtitle">Get sum coinz</h1>
<div className="section__actions">
<Button button="primary" label={__('Earn Rewards')} navigate={`/$/${PAGES.REWARDS}`} />
<Button button="primary" label={__('Purchase LBC')} navigate={`/$/${PAGES.BUY}`} />
</div>
</div>
</div>
)}
<ChannelEdit disabled={emptyBalance} onDone={() => push(redirectUrl || `/$/${PAGES.CHANNELS}`)} />
</Page> </Page>
); );
} }
export default withRouter(ChannelNew); export default ChannelNew;

View file

@ -4,6 +4,7 @@ $thumbnailWidthSmall: 2rem;
.comments { .comments {
list-style-type: none; list-style-type: none;
font-size: var(--font-small); font-size: var(--font-small);
margin-top: var(--spacing-l);
} }
.comments--replies { .comments--replies {

View file

@ -301,6 +301,11 @@ textarea {
background-color: var(--color-primary-alt); background-color: var(--color-primary-alt);
} }
.notice-message--above-content {
@extend .notice-message;
margin-bottom: var(--spacing-l);
}
.privacy-img { .privacy-img {
height: 10rem; height: 10rem;
} }