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

View file

@ -1,13 +1,14 @@
// @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 { SIMPLE_SITE } from 'config';
import React, { useEffect, useState } from 'react';
import classnames from 'classnames';
import { FormField, Form } from 'component/common/form';
import Button from 'component/button';
import ChannelSelection from 'component/selectChannel';
import usePersistedState from 'effects/use-persisted-state';
import * as MODALS from 'constants/modal_types';
import I18nMessage from 'component/i18nMessage';
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 [charCount, setCharCount] = useState(commentValue.length);
const [advancedEditor, setAdvancedEditor] = usePersistedState('comment-editor-mode', false);
const hasChannels = channels && channels.length;
const topChannel =
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 (
<Form
onSubmit={handleSubmit}

View file

@ -1,9 +1,9 @@
import { connect } from 'react-redux';
import { makeSelectClaimIsMine, selectMyChannelClaims } from 'lbry-redux';
import { makeSelectRepliesForParentId } from 'redux/selectors/comments';
import CommentsReplies from './view';
import { doToast } from 'redux/actions/notifications';
import { selectUserVerifiedEmail } from 'redux/selectors/user';
import CommentsReplies from './view';
const select = (state, props) => ({
myChannels: selectMyChannelClaims(state),
@ -12,4 +12,6 @@ const select = (state, props) => ({
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
import { SITE_NAME } from 'config';
import * as ICONS from 'constants/icons';
import * as PAGES from 'constants/pages';
import React from 'react';
import Comment from 'component/comment';
import Button from 'component/button';
import * as ICONS from 'constants/icons';
import CommentCreate from 'component/commentCreate';
import { useHistory } from 'react-router';
type Props = {
comments: Array<any>,
@ -13,31 +16,35 @@ type Props = {
linkedComment?: Comment,
parentId: string,
commentingEnabled: boolean,
doToast: ({ message: string }) => void,
};
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 [isExpanded, setExpanded] = React.useState(false);
const [start, setStart] = React.useState(0);
const [end, setEnd] = React.useState(9);
const sortedComments = comments ? [...comments].reverse() : [];
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) {
setStart(0);
} else {
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
const isMyComment = (channelId: string) => {
function isMyComment(channelId: string) {
if (myChannels != null && channelId != null) {
for (let i = 0; i < myChannels.length; i++) {
if (myChannels[i].claim_id === channelId) {
@ -46,16 +53,25 @@ function CommentsReplies(props: Props) {
}
}
return false;
};
}
const handleCommentDone = () => {
function handleCommentDone() {
if (!isExpanded) {
setExpanded(true);
setStart(numberOfComments || 0);
}
setEnd(numberOfComments + 1);
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(() => {
if (
@ -75,13 +91,13 @@ function CommentsReplies(props: Props) {
const displayedComments = sortedComments.slice(start, end);
return (
<li className={'comment__replies-container'}>
<li className="comment__replies-container">
<div className="comment__actions">
<Button
requiresAuth={IS_WEB}
label={commentingEnabled ? __('Reply') : __('Log in to reply')}
className="comment__action"
onClick={() => setReplying(!isReplying)}
onClick={handleCommentReply}
icon={ICONS.REPLY}
/>
{!isExpanded && Boolean(numberOfComments) && (
@ -124,7 +140,7 @@ function CommentsReplies(props: Props) {
requiresAuth={IS_WEB}
label={commentingEnabled ? __('Reply') : __('Log in to reply')}
className="comment__action--nested"
onClick={() => setReplying(!isReplying)}
onClick={handleCommentReply}
icon={ICONS.REPLY}
/>
)}

View file

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

View file

@ -1,7 +1,9 @@
import { connect } from 'react-redux';
import { selectBalance } from 'lbry-redux';
import ChannelNew from './view';
const select = () => ({});
const perform = () => ({});
const select = state => ({
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 ChannelEdit from 'component/channelEdit';
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';
type Props = {
history: { push: string => void, goBack: () => void },
balance: number,
};
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 (
<Page noSideNavigation authPage backout={{ title: __('Create Channel') }}>
<ChannelEdit onDone={() => history.push(`/$/${PAGES.CHANNELS}`)} />
<Page noSideNavigation noFooter backout={{ title: __('Create A Channel'), backLabel: __('Cancel') }}>
{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>
);
}
export default withRouter(ChannelNew);
export default ChannelNew;

View file

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

View file

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