Few UX improvements

- add message about deleting/editing to comment create intro (feel free to remove, but I thought people would be wondering after they used em)
- add note on publish, link to faq
- better handling of own urls + takeovers
- Add manage email preferences to account page
- move recent believe send/receive (on the fence, but I think this is better)
- rewards messaging
- Add support button for own claims, fix up messaging around this
- Help page improvements
- Add privacy policy to settings/diagnostics option
- maybe a couple things I'm forgetting
This commit is contained in:
Thomas Zarebczan 2019-07-16 23:23:45 -04:00
parent 112ffe6696
commit a628776518
20 changed files with 142 additions and 83 deletions

View file

@ -98,7 +98,7 @@ class CardVerify extends React.Component<Props, State> {
componentWillUnmount() {
if (this.loadPromise) {
this.loadPromise.cancel();
this.loadPromise.reject();
}
if (CardVerify.stripeHandler && this.state.open) {
CardVerify.stripeHandler.close();

View file

@ -49,6 +49,11 @@ export function CommentCreate(props: Props) {
'During the alpha, comments are not decentralized or censorship resistant (but we repeat ourselves).'
)}
</li>
<li>
{__(
'For the initial release, deleting or editing comments is not possible. Please be mindful of this when posting.'
)}
</li>
<li>
{__(
'When the alpha ends, we will attempt to transition comments, but do not promise to do so. Any transition will likely involve publishing previous comments under a single archive handle.'

View file

@ -3,6 +3,7 @@ import React from 'react';
import { regexInvalidURI } from 'lbry-redux';
import classnames from 'classnames';
import FileSelector from 'component/common/file-selector';
import Button from 'component/button';
type Props = {
name: ?string,
@ -39,6 +40,12 @@ function PublishFile(props: Props) {
<div className="card__content">
<FileSelector currentPath={filePath} onFileChosen={handleFileChange} />
{!isStillEditing && (
<p className="help">
{__('For video content, use MP4s in H264/AAC format for best compatibility.')}{' '}
<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/how-to-publish" />.
</p>
)}
{!!isStillEditing && name && (
<p className="help">
{/* @i18nfixme */}

View file

@ -13,6 +13,8 @@ function BidHelpText(props: Props) {
if (uri) {
if (isResolvingUri) {
bidHelpText = __('Checking the winning claim amount...');
} else if (amountNeededForTakeover === 0) {
bidHelpText = __('You currently have the highest bid for this name.');
} else if (!amountNeededForTakeover) {
bidHelpText = __('Any amount will give you the winning bid.');
} else {

View file

@ -25,36 +25,17 @@ class SocialShare extends React.PureComponent<Props> {
input: ?HTMLInputElement;
render() {
const { claim, isChannel } = this.props;
const { claim_id: claimId, name: claimName } = claim;
const { claim } = this.props;
const { short_url: shortUrl } = claim;
const { speechShareable, onDone } = this.props;
const signingChannel = claim.signing_channel;
const channelClaimId = signingChannel && signingChannel.claim_id;
const channelName = signingChannel && signingChannel.name;
const getLbryTvUri = (): string => {
return `${claimName}/${claimId}`;
};
const getLbryUri = (): string => {
if (isChannel) {
// For channel claims, the channel name (@something) is in `claim.name`
return `${claimName}#${claimId}`;
} else {
// If it's for a regular claim, check if it has an associated channel
return channelName && channelClaimId
? `${channelName}#${channelClaimId}/${claimName}`
: `${claimName}#${claimId}`;
}
};
const lbryTvPrefix = 'https://beta.lbry.tv/';
const lbryPrefix = 'https://open.lbry.com/';
const lbryUri = getLbryUri();
const lbryTvUri = getLbryTvUri();
const lbryUri = shortUrl.split('lbry://')[1];
const lbryTvUri = lbryUri.replace('#', '/');
const encodedLbryURL: string = `${lbryPrefix}${encodeURIComponent(lbryUri)}`;
const lbryURL: string = `${lbryPrefix}${getLbryUri()}`;
const lbryURL: string = `${lbryPrefix}${lbryUri}`;
const encodedLbryTvUrl = `${lbryTvPrefix}${encodeURIComponent(lbryTvUri)}`;
const lbryTvUrl = `${lbryTvPrefix}${lbryTvUri}`;

View file

@ -1,15 +1,24 @@
import { connect } from 'react-redux';
import { selectEmailToVerify, doUserResendVerificationEmail, doUserCheckEmailVerified, selectUser } from 'lbryinc';
import {
selectEmailToVerify,
doUserResendVerificationEmail,
doUserCheckEmailVerified,
selectUser,
doFetchAccessToken,
selectAccessToken,
} from 'lbryinc';
import UserEmailVerify from './view';
const select = state => ({
email: selectEmailToVerify(state),
user: selectUser(state),
accessToken: selectAccessToken(state),
});
const perform = dispatch => ({
resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email)),
checkEmailVerified: () => dispatch(doUserCheckEmailVerified()),
fetchAccessToken: () => dispatch(doFetchAccessToken()),
});
export default connect(

View file

@ -14,15 +14,19 @@ type Props = {
user: {
has_verified_email: boolean,
},
fetchAccessToken: () => void,
accessToken: string,
};
function UserEmail(props: Props) {
const { email, user } = props;
const { email, user, accessToken, fetchAccessToken } = props;
let isVerified = false;
if (user) {
isVerified = user.has_verified_email;
}
if (!accessToken) fetchAccessToken();
const buttonsProps = IS_WEB
? {
onClick: () => {
@ -51,14 +55,23 @@ function UserEmail(props: Props) {
type="text"
className="form-field--copyable"
readOnly
label={__('Your Email')}
label={
<React.Fragment>
{__('Your Email - ')}{' '}
<Button
button="link"
label={__('Update mailing preferences')}
href={`http://lbry.io/list/edit/${accessToken}`}
/>
</React.Fragment>
}
value={email}
inputButton={<Button button="inverse" label={__('Change')} {...buttonsProps} />}
/>
)}
<p className="help">
{`${__(
'This information is disclosed only to LBRY, Inc. and not to the LBRY network. It is only required to earn LBRY rewards.'
'This information is disclosed only to LBRY, Inc. and not to the LBRY network. It is only required to save account information and earn rewards.'
)} `}
</p>
</React.Fragment>

View file

@ -30,14 +30,43 @@ class UserVerify extends React.PureComponent<Props> {
<header className="card__header--flat">
<h1 className="card__title">{__('Final Human Proof')}</h1>
<p className="card__subtitle">
Finally, please complete <strong>one and only one</strong> of the options below.
To be approved for rewards, please complete <strong>one and only one</strong> of the options below. This
is optional, and you can click back or Skip Rewards at the bottom.
</p>
</header>
</section>
<section className="card card--section">
<header className="card__header">
<h2 className="card__title">{__('1) Proof via Credit')}</h2>
<h2 className="card__title">{__('1) Proof via Phone')}</h2>
<p className="card__subtitle">
{`${__(
'You will receive an SMS text message confirming that your phone number is correct. Does not work for Canada and possibly other regions'
)}`}
</p>
</header>
<div className="card__content">
<div className="card__actions">
<Button
onClick={() => {
verifyPhone();
}}
button="inverse"
label={__('Submit Phone Number')}
/>
</div>
<div className="help">
{__('Standard messaging rates apply. LBRY will not text or call you otherwise. Having trouble?')}{' '}
<Button button="link" href="https://lbry.com/faq/phone" label={__('Read more.')} />
</div>
</div>
</section>
<section className="card card--section">
<header className="card__header">
<h2 className="card__title">{__('2) Proof via Credit')}</h2>
<p className="card__subtitle">
{`${__(
'If you have a valid credit or debit card, you can use it to instantly prove your humanity.'
@ -69,32 +98,6 @@ class UserVerify extends React.PureComponent<Props> {
</div>
</section>
<section className="card card--section">
<header className="card__header">
<h2 className="card__title">{__('2) Proof via Phone')}</h2>
<p className="card__subtitle">
{`${__('You will receive an SMS text message confirming that your phone number is correct.')}`}
</p>
</header>
<div className="card__content">
<div className="card__actions">
<Button
onClick={() => {
verifyPhone();
}}
button="inverse"
label={__('Submit Phone Number')}
/>
</div>
<div className="help">
{__('Standard messaging rates apply. Having trouble?')}{' '}
<Button button="link" href="https://lbry.com/faq/phone" label={__('Read more.')} />
</div>
</div>
</section>
<section className="card card--section">
<header className="card__header">
<h2 className="card__title">{__('3) Proof via Chat')}</h2>

View file

@ -71,9 +71,7 @@ class WalletAddress extends React.PureComponent<Props, State> {
</div>
<p className="help">
{__(
'You can generate a new address at any time, and any previous addresses will continue to work. Using multiple addresses can be helpful for keeping track of incoming payments from multiple sources.'
)}
{__('You can generate a new address at any time, and any previous addresses will continue to work.')}
</p>
</div>

View file

@ -5,6 +5,7 @@ import { FormField, Form } from 'component/common/form';
type Props = {
uri: string,
claimIsMine: boolean,
title: string,
claim: StreamClaim,
isPending: boolean,
@ -69,7 +70,7 @@ class WalletSendTip extends React.PureComponent<Props, State> {
}
render() {
const { title, isPending, uri, onCancel } = this.props;
const { title, isPending, uri, onCancel, claimIsMine } = this.props;
const { tipAmount, tipError } = this.state;
return (
@ -98,7 +99,10 @@ class WalletSendTip extends React.PureComponent<Props, State> {
}
helper={
<p>
{__(`This will appear as a tip for "${title}".`)}{' '}
{claimIsMine
? __('This will increase your overall bid amount for ')
: __('This will appear as a tip for ')}
{`"${title}" which will boost its ability to be discovered while active.`}{' '}
<Button label={__('Learn more')} button="link" href="https://lbry.com/faq/tipping" />
</p>
}

View file

@ -53,6 +53,7 @@ export const SECURE = 'Lock';
export const MENU = 'Menu';
export const BACKUP = 'Database';
export const TRANSACTIONS = 'FileText';
export const TRENDING_UP = 'TrendingUp';
export const LBRY = 'Lbry';
export const SEND = 'Send';
export const DISCOVER = 'Compass';

View file

@ -1 +1,2 @@
export const TIP = 'tip';
export const SUPPORT = 'support';

View file

@ -37,6 +37,17 @@ class ModalRevokeClaim extends React.PureComponent<Props> {
</p>
</React.Fragment>
);
} else if (type === txnTypes.SUPPORT) {
return (
<React.Fragment>
<p>{__('Are you sure you want to remove this support?')}</p>
<p>
{__(
"These credits are permanently yours and can be removed at any time. Removing this support will reduce the claim's discoverability and return the LBC to your spendable balance."
)}
</p>
</React.Fragment>
);
}
return (
<React.Fragment>

View file

@ -7,11 +7,12 @@ import UriIndicator from 'component/uriIndicator';
type Props = {
closeModal: () => void,
uri: string,
claimIsMine: boolean,
};
class ModalSendTip extends React.PureComponent<Props> {
render() {
const { closeModal, uri } = this.props;
const { closeModal, uri, claimIsMine } = this.props;
return (
<Modal
@ -20,11 +21,11 @@ class ModalSendTip extends React.PureComponent<Props> {
type="custom"
title={
<React.Fragment>
{__('Send a tip to')} <UriIndicator uri={uri} />
{claimIsMine ? __('Add support to') : __('Send a tip')} <UriIndicator uri={uri} />
</React.Fragment>
}
>
<SendTip uri={uri} onCancel={closeModal} sendTipCallback={closeModal} />
<SendTip uri={uri} claimIsMine={claimIsMine} onCancel={closeModal} sendTipCallback={closeModal} />
</Modal>
);
}

View file

@ -164,7 +164,7 @@ class FilePage extends React.Component<Props> {
const showFile = isPlayableType || isPreviewType;
const speechShareable =
costInfo && costInfo.cost === 0 && contentType && ['video', 'image'].includes(contentType.split('/')[0]);
costInfo && costInfo.cost === 0 && contentType && ['video', 'image', 'audio'].includes(contentType.split('/')[0]);
// We want to use the short form uri for editing
// This is what the user is used to seeing, they don't care about the claim id
// We will select the claim id before they publish
@ -240,18 +240,17 @@ class FilePage extends React.Component<Props> {
}}
/>
)}
{!claimIsMine && (
{
<React.Fragment>
{channelUri && <SubscribeButton uri={channelUri} channelName={channelName} />}
{!claimIsMine && channelUri && <SubscribeButton uri={channelUri} channelName={channelName} />}
<Button
button="alt"
icon={icons.TIP}
label={__('Send a tip')}
onClick={() => openModal(MODALS.SEND_TIP, { uri })}
icon={claimIsMine ? icons.TRENDING_UP : icons.TIP}
label={claimIsMine ? __('Add support') : __('Send a tip')}
onClick={() => openModal(MODALS.SEND_TIP, { uri, claimIsMine })}
/>
</React.Fragment>
)}
}
<Button
button="alt"
icon={icons.SHARE}

View file

@ -130,24 +130,36 @@ class HelpPage extends React.PureComponent<Props, State> {
<div className="card__content">
<div className="card__actions">
<Button href="https://lbry.com/faq" label={__('Read the FAQ')} icon={icons.HELP} button="inverse" />
<Button
href="https://lbry.com/faq/lbry-basics"
label={__('Read the App Basics FAQ')}
icon={icons.HELP}
button="inverse"
/>
<Button
href="https://lbry.com/faq"
label={__('See the All LBRY FAQs')}
icon={icons.HELP}
button="inverse"
/>
</div>
</div>
</section>
<section className="card card--section">
<header className="card__header">
<h2 className="card__title">{__('Get Live Help')}</h2>
<h2 className="card__title">{__('Find Assistance')}</h2>
<p className="card__subtitle">
{__('Live help is available most hours in the')} <strong>#help</strong>{' '}
{__('channel of our Discord chat room.')}
{__('channel of our Discord chat room. Or you can always email us at help@lbry.com.')}
</p>
</header>
<div className="card__content">
<div className="card__actions">
<Button button="inverse" label={__('Join Our Chat')} icon={icons.CHAT} href="https://chat.lbry.com" />
<Button button="inverse" label={__('Email Us')} icon={icons.WEB} href="mailto:help@lbry.com" />
</div>
</div>
</section>

View file

@ -35,11 +35,17 @@ class RewardsPage extends PureComponent<Props> {
!IS_WEB && (
<section className="card card--section">
<header className="card__header">
<h2 className="card__title">{__('Verification For Rewards')}</h2>
<h2 className="card__title">{__('Rewards Approval to Earn Credits (LBC)')}</h2>
<p className="card__subtitle">
{__('Rewards are for human beings only.')}{' '}
{__("You'll have to prove you're one of us before you can claim any rewards.")}{' '}
{__('This is optional.')}
{__(
'This step optional. You can continue to use this app without Rewards, but LBC may be needed for some tasks.'
)}{' '}
</p>
<p className="card__subtitle">
{__(
"Rewards are for human beings only. You'll have to prove you're one of us before you can claim any."
)}{' '}
{<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/rewards" />}
</p>
</header>

View file

@ -313,7 +313,10 @@ class SettingsPage extends React.PureComponent<Props, State> {
name="share_usage_data"
onChange={() => setDaemonSetting('share_usage_data', !daemonSettings.share_usage_data)}
checked={daemonSettings.share_usage_data}
label={__('Help make LBRY better by contributing analytics and diagnostic data about my usage.')}
label=<React.Fragment>
{__('Help make LBRY better by contributing analytics and diagnostic data about my usage.')}{' '}
<Button button="link" label={__('Learn more')} href="https://lbry.com/privacypolicy" />.
</React.Fragment>
helper={__('You will be ineligible to earn rewards while diagnostics are not being shared.')}
/>
</Form>

View file

@ -11,9 +11,9 @@ const WalletPage = () => (
<UnsupportedOnWeb />
<div className={IS_WEB && 'card--disabled'}>
<WalletBalance />
<TransactionListRecent />
<WalletSend />
<WalletAddress />
<TransactionListRecent />
</div>
</Page>
);

View file

@ -104,7 +104,10 @@ export const selectTakeOverAmount = createSelector(
// is needed to win the claim. Currently this is not possible during a takeover.
// With this, we could say something like, "You have x lbc in support, if you bid y additional LBC you will control the claim"
// For now just ignore supports. We will just show the winning claim's bid amount
return claimForShortUri.meta.effective_amount || claimForShortUri.amount;
// If the top claim is your claim, no takeover is necessary so we return 0.
return claimForShortUri.claim_id === myClaimForUri.claim_id
? 0
: claimForShortUri.meta.effective_amount || claimForShortUri.amount;
}
return null;