add back reverted changes

This commit is contained in:
Sean Yesmunt 2019-10-03 17:40:54 -04:00
parent 58b92fe4aa
commit b89c8a51c6
45 changed files with 326 additions and 300 deletions

View file

@ -1,6 +1,6 @@
{
"name": "lbry",
"version": "0.36.0-rc.1",
"version": "0.36.0-rc.5",
"description": "A browser for the LBRY network, a digital marketplace controlled by its users.",
"keywords": [
"lbry"
@ -128,8 +128,8 @@
"husky": "^0.14.3",
"json-loader": "^0.5.4",
"lbry-format": "https://github.com/lbryio/lbry-format.git",
"lbry-redux": "lbryio/lbry-redux#85fe532d69704a283fd2ec640ad6e3b8dacd6d0d",
"lbryinc": "lbryio/lbryinc#a44576194e1f5f60e37d328ddfdca40bd6165c2d",
"lbry-redux": "lbryio/lbry-redux#b6a7dd0d99f0f8cfc204d3734b6cfc6eb7f8303d",
"lbryinc": "lbryio/lbryinc#f5bee9cd300c4bdc05228e31ea15cfc430975f67",
"lint-staged": "^7.0.2",
"localforage": "^1.7.1",
"lodash-es": "^4.17.14",

View file

@ -187,6 +187,7 @@ function ChannelForm(props: Props) {
suggestMature
help={__('The better your tags are, the easier it will be for people to discover your channel.')}
empty={__('No tags added')}
placeholder={__('Add a tag')}
onSelect={newTag => {
if (!params.tags.map(savedTag => savedTag.name).includes(newTag.name)) {
setParams({ ...params, tags: [...params.tags, newTag] });

View file

@ -78,7 +78,8 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
properties,
onClick,
} = props;
const shouldFetch = claim === undefined || (claim !== null && claim.value_type === 'channel' && isEmpty(claim.meta));
const shouldFetch =
claim === undefined || (claim !== null && claim.value_type === 'channel' && isEmpty(claim.meta) && !pending);
const abandoned = !isResolvingUri && !claim;
const claimsInChannel = (claim && claim.meta.claims_in_channel) || 0;
const showPublishLink = abandoned && placeholder === 'publish';
@ -187,24 +188,27 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
<div className="claim-preview-title">
{claim ? <TruncatedText text={title || claim.name} lines={1} /> : <span>{__('Nothing here')}</span>}
</div>
{!hideActions && actions !== undefined ? (
actions
) : (
<div className="card__actions--inline">
{isChannel && !channelIsBlocked && !claimIsMine && (
<SubscribeButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />
{!pending && (
<React.Fragment>
{!hideActions && actions !== undefined ? (
actions
) : (
<div className="card__actions--inline">
{isChannel && !channelIsBlocked && !claimIsMine && (
<SubscribeButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />
)}
{isChannel && !isSubscribed && !claimIsMine && (
<BlockButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />
)}
{!isChannel && claim && <FileProperties uri={uri} />}
</div>
)}
{isChannel && !isSubscribed && !claimIsMine && (
<BlockButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />
)}
{!isChannel && claim && <FileProperties uri={uri} />}
</div>
</React.Fragment>
)}
</div>
<div className="claim-preview-properties">
<div className="media__subtitle">
{pending && <div>Pending...</div>}
{!isResolvingUri && (
<div>
{claim ? (
@ -222,12 +226,16 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
</Fragment>
)}
<div>
{claim &&
{pending ? (
<div>Pending...</div>
) : (
claim &&
(isChannel ? (
type !== 'inline' && `${claimsInChannel} ${__('publishes')}`
) : (
<DateTime timeAgo uri={uri} />
))}
))
)}
</div>
</div>
)}

View file

@ -0,0 +1,13 @@
// @flow
import * as ICONS from 'constants/icons';
import React from 'react';
import Button from 'component/button';
type Props = {
href: string,
};
export default function HelpLink(props: Props) {
const { href } = props;
return <Button className="icon--help" icon={ICONS.HELP} description={__('Help')} href={href} />;
}

View file

@ -37,7 +37,7 @@ class InviteList extends React.PureComponent<Props> {
return (
<section className="card">
<div className="table__header">
<h2 className="card__title">
<h2 className="card__title--between">
{__('Invite History')}
{referralReward && showClaimable && (
<RewardLink

View file

@ -34,7 +34,9 @@ function PublishFile(props: Props) {
icon={ICONS.PUBLISH}
disabled={disabled || balance === 0}
title={isStillEditing ? __('Edit') : __('Publish')}
subtitle={__('You are currently editing a claim.')}
subtitle={
isStillEditing ? __('You are currently editing a claim.') : __('Publish something totally wacky and wild.')
}
actions={
<React.Fragment>
<FileSelector currentPath={filePath} onFileChosen={handleFileChange} />

View file

@ -135,6 +135,7 @@ function PublishForm(props: Props) {
suggestMature
help={__('The better your tags are, the easier it will be for people to discover your content.')}
empty={__('No tags added')}
placeholder={__('Add a tag')}
onSelect={newTag => {
if (!tags.map(savedTag => savedTag.name).includes(newTag.name)) {
updatePublishForm({ tags: [...tags, newTag] });

View file

@ -1,6 +1,7 @@
// @flow
import React from 'react';
import { THUMBNAIL_STATUSES, isNameValid } from 'lbry-redux';
import { INVALID_NAME_ERROR } from 'constants/claim';
type Props = {
title: ?string,
@ -21,7 +22,7 @@ function PublishFormErrors(props: Props) {
<div className="error-text">
{!title && <div>{__('A title is required')}</div>}
{!name && <div>{__('A URL is required')}</div>}
{!isNameValid(name, false) && __('LBRY names cannot contain spaces or reserved symbols ($#@;/"<>%{}|^~[]`)')}
{!isNameValid(name, false) && INVALID_NAME_ERROR}
{!bid && <div>{__('A deposit amount is required')}</div>}
{uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS && (
<div>{__('Please wait for thumbnail to finish uploading')}</div>

View file

@ -1,5 +1,5 @@
// @flow
import { CHANNEL_NEW, CHANNEL_ANONYMOUS, MINIMUM_PUBLISH_BID } from 'constants/claim';
import { CHANNEL_NEW, CHANNEL_ANONYMOUS, MINIMUM_PUBLISH_BID, INVALID_NAME_ERROR } from 'constants/claim';
import React, { useState, useEffect } from 'react';
import { isNameValid } from 'lbry-redux';
import { FormField } from 'component/common/form';
@ -50,7 +50,7 @@ function PublishName(props: Props) {
if (!name) {
nameError = __('A name is required');
} else if (!isNameValid(name, false)) {
nameError = __('LBRY names cannot contain spaces or reserved symbols ($#@;/"<>%{}|^~[]`)');
nameError = INVALID_NAME_ERROR;
}
setNameError(nameError);

View file

@ -4,7 +4,7 @@ import { isNameValid } from 'lbry-redux';
import { FormField } from 'component/common/form';
import BusyIndicator from 'component/common/busy-indicator';
import Button from 'component/button';
import { CHANNEL_NEW, CHANNEL_ANONYMOUS } from 'constants/claim';
import { CHANNEL_NEW, CHANNEL_ANONYMOUS, INVALID_NAME_ERROR } from 'constants/claim';
type Props = {
channel: string, // currently selected channel
@ -77,7 +77,7 @@ class ChannelSection extends React.PureComponent<Props, State> {
let newChannelNameError;
if (newChannelName.length > 0 && !isNameValid(newChannelName, false)) {
newChannelNameError = __('LBRY names cannot contain spaces or reserved symbols ($#@;/"<>%{}|^~[]`)');
newChannelNameError = INVALID_NAME_ERROR;
}
this.setState({

View file

@ -3,7 +3,7 @@ import React from 'react';
import { Form, FormField } from 'component/common/form';
import Button from 'component/button';
import Card from 'component/common/card';
import { setSavedPassword } from 'util/saved-passwords';
import { setSavedPassword, deleteSavedPassword } from 'util/saved-passwords';
type Props = {
getSync: (?string) => void,
@ -18,6 +18,8 @@ function SyncPassword(props: Props) {
function handleSubmit() {
if (rememberPassword) {
setSavedPassword(password);
} else {
deleteSavedPassword();
}
getSync(password);

View file

@ -12,9 +12,10 @@ type Props = {
onSelect?: Tag => void,
suggestMature?: boolean,
onRemove: Tag => void,
placeholder?: string,
};
export default function TagSelect(props: Props) {
export default function TagsSearch(props: Props) {
const {
tagsPasssedIn,
unfollowedTags = [],
@ -24,6 +25,7 @@ export default function TagSelect(props: Props) {
onSelect,
onRemove,
suggestMature,
placeholder,
} = props;
const [newTag, setNewTag] = useState('');
@ -96,7 +98,7 @@ export default function TagSelect(props: Props) {
autoFocus
className="tag__input"
onChange={onChange}
placeholder={__('Follow more tags')}
placeholder={placeholder || __('Follow more tags')}
type="text"
value={newTag}
/>

View file

@ -20,6 +20,7 @@ type Props = {
tagsChosen?: Array<Tag>,
onSelect?: Tag => void,
onRemove?: Tag => void,
placeholder?: string,
};
export default function TagSelect(props: Props) {
@ -33,6 +34,7 @@ export default function TagSelect(props: Props) {
onSelect,
onRemove,
suggestMature,
placeholder,
} = props;
const [hasClosed, setHasClosed] = usePersistedState('tag-select:has-closed', false);
const tagsToDisplay = tagsChosen || followedTags;
@ -88,6 +90,7 @@ export default function TagSelect(props: Props) {
onSelect={onSelect}
suggestMature={suggestMature && !hasMatureTag}
tagsPasssedIn={tagsToDisplay}
placeholder={placeholder}
/>
</React.Fragment>
}

View file

@ -5,6 +5,7 @@ import Button from 'component/button';
import { Lbryio } from 'lbryinc';
import analytics from 'analytics';
import { EMAIL_REGEX } from 'constants/email';
import I18nMessage from 'component/i18nMessage';
type Props = {
errorMessage: ?string,
@ -18,6 +19,7 @@ type Props = {
function UserEmailNew(props: Props) {
const { errorMessage, isPending, addUserEmail, syncEnabled, setSync, balance } = props;
const [newEmail, setEmail] = useState('');
const [ageConfirmation, setAgeConfirmation] = useState(true);
const valid = newEmail.match(EMAIL_REGEX);
function handleSubmit() {
@ -52,28 +54,57 @@ function UserEmailNew(props: Props) {
error={errorMessage}
onChange={e => setEmail(e.target.value)}
/>
{!IS_WEB && (
<div className="section">
<FormField
type="checkbox"
name="sync_checkbox"
label={__('Sync balance and preferences across devices')}
helper={
balance > 0 ? (
__('This feature is not yet available for wallets with balances, but the gerbils are working on it.')
) : (
<React.Fragment>
{__('Blockchain expert?')}{' '}
<Button button="link" href="https://lbry.com/faq/account-sync" label={__('Learn More')} />
</React.Fragment>
)
name="age_checkbox"
label={
<I18nMessage
tokens={{
terms: (
<Button button="link" href="https://www.lbry.com/termsofservice" label={__('Terms of Service')} />
),
}}
>
I am over the age of 13 and agree to the %terms%.
</I18nMessage>
}
checked={syncEnabled}
onChange={() => setSync(!syncEnabled)}
disabled={balance > 0}
checked={ageConfirmation}
onChange={() => setAgeConfirmation(!ageConfirmation)}
/>
)}
<div className="card__actions">
<Button button="primary" type="submit" label={__('Continue')} disabled={!newEmail || !valid || isPending} />
{!IS_WEB && (
<FormField
type="checkbox"
name="sync_checkbox"
label={__('Sync balance and preferences across devices')}
helper={
balance > 0 ? (
__('This feature is not yet available for wallets with balances, but the gerbils are working on it.')
) : (
<I18nMessage
tokens={{
learn_more: (
<Button button="link" href="https://lbry.com/faq/account-sync" label={__('Learn More')} />
),
}}
>
Blockchain expert? %learn_more%
</I18nMessage>
)
}
checked={syncEnabled}
onChange={() => setSync(!syncEnabled)}
disabled={balance > 0}
/>
)}
<div className="card__actions">
<Button
button="primary"
type="submit"
label={__('Continue')}
disabled={!newEmail || !valid || !ageConfirmation || isPending}
/>
</div>
</div>
</Form>
</div>

View file

@ -3,7 +3,7 @@ import React, { useState } from 'react';
import { isNameValid } from 'lbry-redux';
import Button from 'component/button';
import { Form, FormField } from 'component/common/form';
import { INVALID_NAME_ERROR } from 'constants/claim';
export const DEFAULT_BID_FOR_FIRST_CHANNEL = 0.9;
type Props = {
@ -29,7 +29,7 @@ function UserFirstChannel(props: Props) {
const { value } = e.target;
setChannel(value);
if (!isNameValid(value, false)) {
setNameError(__('LBRY names cannot contain spaces or reserved symbols ($#@;/"<>%{}|^~[]`)'));
setNameError(INVALID_NAME_ERROR);
} else {
setNameError();
}

View file

@ -15,7 +15,7 @@ import {
selectGetSyncErrorMessage,
selectSyncHash,
} from 'lbryinc';
import { selectMyChannelClaims, selectBalance, selectFetchingMyChannels } from 'lbry-redux';
import { selectMyChannelClaims, selectBalance, selectFetchingMyChannels, selectCreatingChannel } from 'lbry-redux';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import UserSignIn from './view';
@ -36,6 +36,7 @@ const select = state => ({
syncingWallet: selectGetSyncIsPending(state),
getSyncError: selectGetSyncErrorMessage(state),
hasSynced: Boolean(selectSyncHash(state)),
creatingChannel: selectCreatingChannel(state),
});
const perform = dispatch => ({

View file

@ -8,9 +8,10 @@ import { DEFAULT_BID_FOR_FIRST_CHANNEL } from 'component/userFirstChannel/view';
import { rewards as REWARDS, YOUTUBE_STATUSES } from 'lbryinc';
import UserVerify from 'component/userVerify';
import Spinner from 'component/spinner';
import YoutubeTransferWelcome from 'component/youtubeTransferWelcome';
import YoutubeTransferStatus from 'component/youtubeTransferStatus';
import SyncPassword from 'component/syncPassword';
import useFetched from 'effects/use-fetched';
import Confetti from 'react-confetti';
type Props = {
user: ?User,
@ -29,6 +30,7 @@ type Props = {
hasSynced: boolean,
syncingWallet: boolean,
getSyncError: ?string,
creatingChannel: boolean,
};
function UserSignIn(props: Props) {
@ -49,6 +51,7 @@ function UserSignIn(props: Props) {
getSyncError,
hasSynced,
fetchingChannels,
creatingChannel,
} = props;
const { search } = location;
const urlParams = new URLSearchParams(search);
@ -59,30 +62,33 @@ function UserSignIn(props: Props) {
const channelCount = channels ? channels.length : 0;
const hasClaimedEmailAward = claimedRewards.some(reward => reward.reward_type === REWARDS.TYPE_CONFIRM_EMAIL);
const hasYoutubeChannels = youtubeChannels && Boolean(youtubeChannels.length);
const hasTransferrableYoutubeChannels = hasYoutubeChannels && youtubeChannels.some(channel => channel.transferable);
const hasPendingYoutubeTransfer =
hasYoutubeChannels && youtubeChannels.some(channel => channel.transfer_state === YOUTUBE_STATUSES.PENDING_TRANSFER);
const isYoutubeTransferComplete =
hasYoutubeChannels &&
youtubeChannels.every(channel => channel.transfer_state === YOUTUBE_STATUSES.COMPLETED_TRANSFER);
// Complexity warning
// We can't just check if we are currently fetching something
// 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 (plus the balance updating after), channel creation, account syncing, and youtube transfer
const canHijackSignInFlowWithSpinner = hasVerifiedEmail && !getSyncError && balance === 0;
const isCurrentlyFetchingSomething = fetchingChannels || claimingReward || syncingWallet;
// 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 || (hasFetchedReward && balance === 0) || (syncEnabled && !hasSynced);
(!hasFetchedReward && !hasClaimedEmailAward) || (syncEnabled && !hasSynced);
// The possible screens for the sign in flow
const showEmail = !emailToVerify && !hasVerifiedEmail;
const showEmailVerification = emailToVerify && !hasVerifiedEmail;
const showUserVerification = hasVerifiedEmail && !rewardsApproved;
const showSyncPassword = syncEnabled && getSyncError && !hasSynced;
const showChannelCreation =
hasVerifiedEmail && balance && balance > DEFAULT_BID_FOR_FIRST_CHANNEL && channelCount === 0 && !hasYoutubeChannels;
const showYoutubeTransfer =
hasVerifiedEmail && hasYoutubeChannels && (hasTransferrableYoutubeChannels || hasPendingYoutubeTransfer);
hasVerifiedEmail &&
balance !== undefined &&
balance !== null &&
balance > DEFAULT_BID_FOR_FIRST_CHANNEL &&
channelCount === 0 &&
!hasYoutubeChannels;
const showYoutubeTransfer = hasVerifiedEmail && hasYoutubeChannels && !isYoutubeTransferComplete;
const showLoadingSpinner =
canHijackSignInFlowWithSpinner && (isCurrentlyFetchingSomething || isWaitingForSomethingToFinish);
@ -109,7 +115,11 @@ function UserSignIn(props: Props) {
showSyncPassword && <SyncPassword />,
showChannelCreation && <UserFirstChannel />,
// @if TARGET='app'
showYoutubeTransfer && <YoutubeTransferWelcome />,
showYoutubeTransfer && (
<div>
<YoutubeTransferStatus /> <Confetti recycle={false} style={{ position: 'fixed' }} />
</div>
),
// @endif
showLoadingSpinner && (
<div className="main--empty">

View file

@ -12,12 +12,10 @@ type Props = {
claimsBalance: number,
supportsBalance: number,
tipsBalance: number,
rewards: Array<Reward>,
};
const WalletBalance = (props: Props) => {
const { balance, claimsBalance, supportsBalance, tipsBalance, rewards } = props;
const rewardTotal = rewards.reduce((acc, val) => acc + val.reward_amount, 0);
const { balance, claimsBalance, supportsBalance, tipsBalance } = props;
return (
<React.Fragment>
@ -29,24 +27,12 @@ const WalletBalance = (props: Props) => {
</span>
<div className="section__actions">
<Button button="inverse" label={__('Send Credits')} navigate={`$/${PAGES.WALLET_SEND}`} />
<Button button="inverse" icon={ICONS.SEND} label={__('Send Credits')} navigate={`$/${PAGES.WALLET_SEND}`} />
<Button button="inverse" label={__('Your Address')} navigate={`$/${PAGES.WALLET_RECEIVE}`} />
</div>
</div>
<div>
<div className="section">
<div className="section__flex">
<Icon sectionIcon icon={ICONS.FEATURED} />
<h2 className="section__title--small">
<strong>
<CreditAmount badge={false} amount={rewardTotal} precision={8} />
</strong>{' '}
{__('Earned From Rewards')}
</h2>
</div>
</div>
<div className="section">
<div className="section__flex">
<Icon sectionIcon icon={ICONS.TIP} />
@ -54,7 +40,7 @@ const WalletBalance = (props: Props) => {
<strong>
<CreditAmount badge={false} amount={tipsBalance} precision={8} />
</strong>{' '}
{__('Earned From Tips')}
{__('Earned and bound in tips')}
</h2>
</div>
</div>
@ -68,12 +54,12 @@ const WalletBalance = (props: Props) => {
</h2>
<div className="section__subtitle">
<dl>
<dt>{__('Your Publishes')}</dt>
<dt>{__('... in your publishes')}</dt>
<dd>
<CreditAmount badge={false} amount={claimsBalance} precision={8} />
</dd>
<dt>{__('Your Supports')}</dt>
<dt>{__('... in your supports')}</dt>
<dd>
<CreditAmount badge={false} amount={supportsBalance} precision={8} />
</dd>

View file

@ -1,10 +1,14 @@
// @flow
import * as PAGES from 'constants/pages';
import * as React from 'react';
import Button from 'component/button';
import ClaimPreview from 'component/claimPreview';
import Card from 'component/common/card';
import { YOUTUBE_STATUSES } from 'lbryinc';
import { buildURI } from 'lbry-redux';
import I18nMessage from 'component/i18nMessage';
const STATUS_URL = 'https://lbry.com/youtube/status/';
type Props = {
youtubeChannels: Array<any>,
@ -13,6 +17,7 @@ type Props = {
updateUser: () => void,
checkYoutubeTransfer: () => void,
videosImported: ?Array<number>, // [currentAmountImported, totalAmountToImport]
hideChannelLink: boolean,
};
export default function YoutubeTransferStatus(props: Props) {
@ -23,13 +28,15 @@ export default function YoutubeTransferStatus(props: Props) {
videosImported,
checkYoutubeTransfer,
updateUser,
hideChannelLink = false,
} = props;
const hasChannels = youtubeChannels && youtubeChannels.length;
const transferEnabled = youtubeChannels.some(status => status.transferable);
const hasPendingTransfers = youtubeChannels.some(
status => status.transfer_state === YOUTUBE_STATUSES.PENDING_TRANSFER
);
const isYoutubeTransferComplete =
hasChannels && youtubeChannels.every(channel => channel.transfer_state === YOUTUBE_STATUSES.COMPLETED_TRANSFER);
let total;
let complete;
@ -72,26 +79,49 @@ export default function YoutubeTransferStatus(props: Props) {
return (
hasChannels &&
(hasPendingTransfers || transferEnabled) && (
!isYoutubeTransferComplete && (
<div>
<Card
title={youtubeChannels.length > 1 ? __('Your YouTube Channels') : __('Your YouTube Channel')}
subtitle={
<span>
{hasPendingTransfers
? __('Your videos are currently being transferred. There is nothing else for you to do.')
: __('Your videos are ready to be transferred.')}
{hasPendingTransfers &&
__('Your videos are currently being transferred. There is nothing else for you to do.')}
{transferEnabled && !hasPendingTransfers && __('Your videos are ready to be transferred.')}
{!transferEnabled && !hasPendingTransfers && __('Please check back later.')}
</span>
}
body={
<section>
{youtubeChannels.map((channel, index) => {
const { lbry_channel_name: channelName, channel_claim_id: claimId } = channel;
const {
lbry_channel_name: channelName,
channel_claim_id: claimId,
status_token: statusToken,
} = channel;
const url = buildURI({ channelName, channelClaimId: claimId });
const transferState = getMessage(channel);
return (
<div key={url} className="card--inline">
<ClaimPreview uri={url} actions={<span className="help">{transferState}</span>} properties={''} />
{claimId ? (
<ClaimPreview
uri={url}
actions={<span className="help">{transferState}</span>}
properties={false}
/>
) : (
<p className="section--padded">
<I18nMessage
tokens={{
statusLink: <Button button="link" href={STATUS_URL + statusToken} label={__('here')} />,
channelName,
}}
>
%channelName% is not ready to be transferred. You can check the status %statusLink% or check
back later.
</I18nMessage>
</p>
)}
</div>
);
})}
@ -101,7 +131,7 @@ export default function YoutubeTransferStatus(props: Props) {
</section>
}
actions={
transferEnabled && (
transferEnabled ? (
<div className="card__actions">
<Button
button="primary"
@ -111,6 +141,12 @@ export default function YoutubeTransferStatus(props: Props) {
/>
<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/youtube#transfer" />
</div>
) : !hideChannelLink ? (
<div className="card__actions">
<Button button="primary" navigate={`/$/${PAGES.CHANNELS}`} label={__('View Your Channels')} />
</div>
) : (
false
)
}
/>

View file

@ -1,25 +0,0 @@
import { connect } from 'react-redux';
import {
selectYoutubeChannels,
selectYouTubeImportPending,
selectUserIsPending,
doClaimYoutubeChannels,
doUserFetch,
} from 'lbryinc';
import YoutubeChannelList from './view';
const select = state => ({
youtubeChannels: selectYoutubeChannels(state),
youtubeImportPending: selectYouTubeImportPending(state),
userFetchPending: selectUserIsPending(state),
});
const perform = dispatch => ({
claimChannels: () => dispatch(doClaimYoutubeChannels()),
updateUser: () => dispatch(doUserFetch()),
});
export default connect(
select,
perform
)(YoutubeChannelList);

View file

@ -1,72 +0,0 @@
// @flow
import * as PAGES from 'constants/pages';
import React from 'react';
import classnames from 'classnames';
import ClaimPreview from 'component/claimPreview';
import Button from 'component/button';
import Confetti from 'react-confetti';
import { YOUTUBE_STATUSES } from 'lbryinc';
type Props = {
youtubeChannels: Array<{ lbry_channel_name: string, channel_claim_id: string, transfer_state: string }>,
claimChannels: () => void,
};
export default function UserYoutubeTransfer(props: Props) {
const { youtubeChannels, claimChannels } = props;
const hasYoutubeChannels = youtubeChannels && youtubeChannels.length;
const hasPendingYoutubeTransfer =
hasYoutubeChannels && youtubeChannels.some(channel => channel.transfer_state === YOUTUBE_STATUSES.PENDING_TRANSFER);
return (
<div>
<div className="section__header">
{!hasPendingYoutubeTransfer ? (
<React.Fragment>
<h1 className="section__title--large">{__('Welcome back!')}</h1>
<p className="section__subtitle">{__('Your channel is ready to be sent over.')}</p>
</React.Fragment>
) : (
<React.Fragment>
<h1 className="section__title--large">{__('Good To Go!')}</h1>
<p className="section__subtitle">
{__('You now control your channel and your videos are being transferred to your account.')}
</p>
</React.Fragment>
)}
</div>
<section className="section">
{youtubeChannels.map(({ lbry_channel_name: channelName, channel_claim_id: claimId }) => (
<div key={channelName} className={classnames('card--inline')}>
<ClaimPreview disabled onClick={() => {}} actions={false} uri={`lbry://${channelName}#${claimId}`} />
</div>
))}
</section>
{hasPendingYoutubeTransfer ? (
<section className="section">
<h1 className="section__title">{__('Transfer In Progress...')}</h1>
<p className="section__subtitle">{__('You can now publish and comment using your official channel.')}</p>
<div className="card__actions">
<Button
button="primary"
label={youtubeChannels.length > 1 ? __('View Your Channels') : __('View Your Channel')}
navigate={`/$/${PAGES.CHANNELS}`}
/>
</div>
</section>
) : (
<section className="section">
<h1 className="section__title">{__('Begin Transfer')}</h1>
<div className="section__actions">
<Button button="primary" label={__('Transfer')} onClick={claimChannels} />
</div>
</section>
)}
{hasPendingYoutubeTransfer && <Confetti recycle={false} style={{ position: 'fixed' }} />}
</div>
);
}

View file

@ -3,3 +3,6 @@ export const MINIMUM_PUBLISH_BID = 0.00000001;
export const CHANNEL_ANONYMOUS = 'anonymous';
export const CHANNEL_NEW = 'new';
export const PAGE_SIZE = 20;
export const INVALID_NAME_ERROR =
__('LBRY names cannot contain spaces or reserved symbols') + ' ' + '($#@;/"<>%{}|^~[]`)';

View file

@ -20,6 +20,7 @@ import * as MODALS from 'constants/modal_types';
import { Form, FormField } from 'component/common/form';
import ClaimPreview from 'component/claimPreview';
import Icon from 'component/common/icon';
import HelpLink from 'component/common/help-link';
const PAGE_VIEW_QUERY = `view`;
const ABOUT_PAGE = `about`;
@ -190,6 +191,7 @@ function ChannelPage(props: Props) {
<ClaimUri uri={uri} />
<span>
{subCount} {subCount !== 1 ? __('Subscribers') : __('Subscriber')}
<HelpLink href="https://lbry.com/faq/views" />
</span>
</div>
</div>

View file

@ -15,15 +15,27 @@ type Props = {
export default function ChannelsPage(props: Props) {
const { channels, fetchChannelListMine, fetchingChannels, youtubeChannels } = props;
const hasYoutubeChannels = youtubeChannels && Boolean(youtubeChannels.length);
const hasPendingChannels = channels && channels.some(channel => channel.confirmations === -1);
useEffect(() => {
fetchChannelListMine();
}, [fetchChannelListMine]);
let interval;
if (hasPendingChannels) {
interval = setInterval(() => {
fetchChannelListMine();
}, 5000);
}
return () => {
clearInterval(interval);
};
}, [fetchChannelListMine, hasPendingChannels]);
return (
<Page>
{/* @if TARGET='app' */}
{hasYoutubeChannels && <YoutubeTransferStatus />}
{hasYoutubeChannels && <YoutubeTransferStatus hideChannelLink />}
{/* @endif */}
{channels && channels.length ? (

View file

@ -18,6 +18,7 @@ import CommentsList from 'component/commentsList';
import CommentCreate from 'component/commentCreate';
import ClaimUri from 'component/claimUri';
import ClaimPreview from 'component/claimPreview';
import HelpLink from 'component/common/help-link';
export const FILE_WRAPPER_CLASS = 'grid-area--content';
@ -161,6 +162,7 @@ class FilePage extends React.Component<Props> {
<DateTime uri={uri} show={DateTime.SHOW_DATE} />
<span>
{viewCount} {viewCount !== 1 ? __('Views') : __('View')}
<HelpLink href="https://lbry.com/faq/views" />
</span>
</div>

View file

@ -1,13 +1,7 @@
import { connect } from 'react-redux';
import * as SETTINGS from 'constants/settings';
import { doClearCache, doNotifyEncryptWallet, doNotifyDecryptWallet, doNotifyForgetPassword } from 'redux/actions/app';
import {
doSetDaemonSetting,
doSetClientSetting,
doGetThemes,
doChangeLanguage,
doSetDarkTime,
} from 'redux/actions/settings';
import { doSetDaemonSetting, doSetClientSetting, doSetDarkTime } from 'redux/actions/settings';
import { doSetPlayingUri } from 'redux/actions/content';
import { makeSelectClientSetting, selectDaemonSettings, selectosNotificationsEnabled } from 'redux/selectors/settings';
import { doWalletStatus, selectWalletIsEncrypted, selectBlockedChannelsCount } from 'lbry-redux';
@ -36,7 +30,6 @@ const perform = dispatch => ({
setDaemonSetting: (key, value) => dispatch(doSetDaemonSetting(key, value)),
clearCache: () => dispatch(doClearCache()),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
getThemes: () => dispatch(doGetThemes()),
encryptWallet: () => dispatch(doNotifyEncryptWallet()),
decryptWallet: () => dispatch(doNotifyDecryptWallet()),
updateWalletStatus: () => dispatch(doWalletStatus()),

View file

@ -44,7 +44,6 @@ type Props = {
setDaemonSetting: (string, ?SetDaemonSettingArg) => void,
setClientSetting: (string, SetDaemonSettingArg) => void,
clearCache: () => Promise<any>,
getThemes: () => void,
daemonSettings: DaemonSettings,
showNsfw: boolean,
instantPurchaseEnabled: boolean,
@ -95,7 +94,6 @@ class SettingsPage extends React.PureComponent<Props, State> {
}
componentDidMount() {
this.props.getThemes();
this.props.updateWalletStatus();
getSavedPassword().then(p => {
if (p) {
@ -513,6 +511,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
<React.Fragment>
{/* @if TARGET='app' */}
<FormField
disabled
type="checkbox"
name="encrypt_wallet"
onChange={() => this.onChangeEncryptWallet()}
@ -520,9 +519,19 @@ class SettingsPage extends React.PureComponent<Props, State> {
label={__('Encrypt my wallet with a custom password')}
helper={
<React.Fragment>
{__('Secure your local wallet data with a custom password.')}{' '}
<I18nMessage
tokens={{
learn_more: (
<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/account-sync" />
),
}}
>
Wallet encryption is currently unavailable until it's supported for synced accounts. It will
be added back soon. %learn_more%
</I18nMessage>
{/* {__('Secure your local wallet data with a custom password.')}{' '}
<strong>{__('Lost passwords cannot be recovered.')} </strong>
<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/wallet-encryption" />.
<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/wallet-encryption" />. */}
</React.Fragment>
}
/>

View file

@ -7,6 +7,7 @@ import path from 'path';
import * as ACTIONS from 'constants/action_types';
import * as MODALS from 'constants/modal_types';
import * as PAGES from 'constants/pages';
import * as SETTINGS from 'constants/settings';
import {
Lbry,
doBalanceSubscribe,
@ -16,6 +17,7 @@ import {
makeSelectClaimIsMine,
doPopulateSharedUserState,
doFetchChannelListMine,
selectBalance,
} from 'lbry-redux';
import Native from 'native';
import { doFetchDaemonSettings } from 'redux/actions/settings';
@ -31,12 +33,13 @@ import {
selectUpgradeTimer,
selectModal,
} from 'redux/selectors/app';
import { Lbryio, doAuthenticate, doGetSync } from 'lbryinc';
import { Lbryio, doAuthenticate, doGetSync, selectSyncHash, doResetSync } from 'lbryinc';
import { lbrySettings as config, version as appVersion } from 'package.json';
import { push } from 'connected-react-router';
import analytics from 'analytics';
import { deleteAuthToken } from 'util/saved-passwords';
import cookie from 'cookie';
import { makeSelectClientSetting } from 'redux/selectors/settings';
// @if TARGET='app'
const { autoUpdater } = remote.require('electron-updater');
@ -456,7 +459,19 @@ export function doSignIn() {
// @endif
// @if TARGET='app'
dispatch(doGetSync());
const state = getState();
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
const syncHash = selectSyncHash(state);
const balance = selectBalance(state);
// For existing users, check if they've synced before, or have 0 balance
if (syncEnabled && (syncHash || balance === 0)) {
dispatch(doGetSync());
setInterval(() => {
dispatch(doGetSync());
}, 1000 * 60 * 5);
}
// @endif
Lbryio.call('user_settings', 'get').then(settings => {
@ -468,9 +483,18 @@ export function doSignIn() {
export function doSignOut() {
return dispatch => {
deleteAuthToken()
.then(window.persistor.purge)
.then(() => {
location.reload();
// @if TARGET='web'
window.persistor.purge();
// @endif
// @if TARGET='app'
return dispatch(doResetSync());
// @endif
})
.then(() => {
setTimeout(() => {
location.reload();
});
})
.catch(() => location.reload());
};

View file

@ -54,7 +54,10 @@ export const doCheckPendingPublishesApp = () => (dispatch: Dispatch, getState: G
const onConfirmed = claim => {
if (selectosNotificationsEnabled(getState())) {
const notif = new window.Notification('LBRY Publish Complete', {
body: `${claim.value.title} has been published to lbry://${claim.name}. Click here to view it`,
body: __('%nameOrTitle% has been published to lbry://%name%. Click here to view it.', {
nameOrTitle: claim.value_type === 'channel' ? `@${claim.name}` : claim.value.title,
name: claim.name,
}),
silent: false,
});
notif.onclick = () => {

View file

@ -46,13 +46,6 @@ export function doSetClientSetting(key, value) {
};
}
export function doGetThemes() {
return dispatch => {
const themes = [__('light'), __('dark')];
dispatch(doSetClientSetting(SETTINGS.THEMES, themes));
};
}
export function doUpdateIsNight() {
return {
type: ACTIONS.UPDATE_IS_NIGHT,

View file

@ -15,8 +15,8 @@ const defaultState = {
// UI
[SETTINGS.LANGUAGE]: window.localStorage.getItem(SETTINGS.LANGUAGE) || 'en',
[SETTINGS.THEME]: 'light',
[SETTINGS.THEMES]: [],
[SETTINGS.THEME]: __('light'),
[SETTINGS.THEMES]: [__('light'), __('dark')],
[SETTINGS.SUPPORT_OPTION]: false,
[SETTINGS.HIDE_SPLASH_ANIMATION]: false,
[SETTINGS.HIDE_BALANCE]: false,

View file

@ -40,6 +40,7 @@
.button--inverse {
height: var(--button-height);
border-radius: var(--button-radius);
font-size: var(--font-body);
padding-top: 0;
padding-bottom: 0;
box-sizing: border-box;
@ -69,7 +70,11 @@
.button--link {
[data-mode='dark'] & {
color: $lbry-teal-3;
color: $lbry-teal-4;
&:hover {
color: $lbry-teal-3;
}
}
}

View file

@ -40,6 +40,11 @@
.card--inline {
border: 1px solid $lbry-gray-1;
border-radius: var(--card-radius);
margin-bottom: var(--spacing-medium);
&:last-of-type {
margin-bottom: 0;
}
[data-mode='dark'] & {
border-color: var(--dm-color-03);
@ -198,7 +203,7 @@
font-size: var(--font-body);
[data-mode='dark'] & {
background-color: var(--dm-color-04);
background-color: var(--color-card-actions--dark);
color: inherit;
}
}

View file

@ -15,7 +15,7 @@ $border-color--dark: var(--dm-color-04);
font-size: var(--font-body);
border-top-left-radius: var(--card-radius);
border-top-right-radius: var(--card-radius);
color: $lbry-white;
background-color: var(--color-card-actions);
& > *:not(:last-child) {
margin-right: 0.5rem;
@ -26,22 +26,6 @@ $border-color--dark: var(--dm-color-04);
margin-bottom: 0;
}
// Normal link buttons are too dark on the black file list background
.button--link {
color: $lbry-teal-3;
&:hover {
color: $lbry-teal-1;
}
[data-mode='dark'] & {
color: $lbry-teal-4;
&:hover {
color: $lbry-teal-1;
}
}
}
// Fix this in @lbry/components, we shouldn't need to be this specific
checkbox-element input[type='checkbox']:checked + label {
color: $lbry-white;
@ -49,6 +33,7 @@ $border-color--dark: var(--dm-color-04);
[data-mode='dark'] & {
color: var(--dm-color-01);
background-color: var(--color-card-actions--dark);
}
}
@ -61,25 +46,12 @@ $border-color--dark: var(--dm-color-04);
margin-bottom: 0;
padding: 0 var(--spacing-medium);
padding-right: var(--spacing-large);
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 96 96' xmlns='http://www.w3.org/2000/svg' fill='%23ffffff'%3E%3Cpath d='M17.172, 31.172c1.562, -1.562 4.095, -1.562 5.656, 0l25.172, 25.171l25.172, -25.171c1.562, -1.562 4.095, -1.562 5.656, 0c1.562, 1.562 1.562, 4.095 0, 5.656l-28, 28c-1.562, 1.562 -4.095, 1.562 -5.656, 0l-28, -28c-0.781, -0.781 -1.172, -1.805 -1.172, -2.828c0, -1.023 0.391, -2.047 1.172, -2.828Z'/%3E%3C/svg%3E%0A");
border: 1px solid $lbry-white;
color: $lbry-white;
background-color: lighten($lbry-black, 10%);
[data-mode='dark'] & {
color: var(--dm-color-01);
}
}
.claim-list__header,
.claim-list__dropdown {
background-color: lighten($lbry-black, 10%);
[data-mode='dark'] & {
background-color: var(--dm-color-07);
}
}
.claim-list__alt-controls {
display: flex;
align-items: center;

View file

@ -18,12 +18,6 @@ form {
}
}
textarea {
&::placeholder {
opacity: 0.4;
}
}
// lbry/components overrides and minor styles
// Some items have very specific styling
// This is because many styles inside `lbry/components/sass/form/` are very specific
@ -46,29 +40,35 @@ textarea {
height: var(--input-height);
padding-bottom: 0.1em;
&::placeholder {
color: $lbry-gray-5;
opacity: 0.4;
}
[data-mode='dark'] & {
&::placeholder {
opacity: 0.4;
color: $lbry-white;
}
}
}
input,
select,
textarea {
border-color: lighten($lbry-black, 20%);
border-radius: var(--input-border-radius);
background-color: $lbry-white;
border-width: 1px;
[data-mode='dark'] & {
background-color: var(--dm-color-02);
}
}
fieldset-section {
margin-bottom: var(--spacing-small);
input,
select,
textarea {
border-color: lighten($lbry-black, 20%);
border-radius: var(--input-border-radius);
background-color: $lbry-white;
border-width: 1px;
[data-mode='dark'] & {
background-color: var(--dm-color-03);
border-color: $lbry-black;
}
}
label {
width: auto;
text-transform: none;
@ -177,15 +177,6 @@ fieldset-group {
width: 0;
}
fieldset-section:first-child .form-field__prefix,
fieldset-section:last-child input {
border-color: $lbry-black;
[data-mode='dark'] {
border-color: $lbry-gray-4;
}
}
fieldset-section:first-child {
.form-field__prefix {
white-space: nowrap;
@ -201,7 +192,8 @@ fieldset-group {
background-color: $lbry-white;
[data-mode='dark'] & {
border-color: $lbry-gray-4;
background-color: var(--dm-color-03);
border-color: var(--dm-color-02);
}
}
}
@ -222,7 +214,7 @@ fieldset-group {
[data-mode='dark'] & {
&:focus {
border-image-source: linear-gradient(to right, $lbry-gray-4, $lbry-teal-5 5%);
border-image-source: linear-gradient(to right, var(--dm-color-02), $lbry-teal-5 5%);
}
}
}
@ -289,8 +281,6 @@ fieldset-section {
background-size: 1.2rem;
[data-mode='dark'] & {
background-color: transparent;
option {
background-color: $lbry-gray-5;
}
@ -304,10 +294,10 @@ fieldset-section {
color: $lbry-white;
}
input:not(:focus):not(.form-field--copyable),
textarea:not(:focus),
select:not(:focus) {
border-color: var(--dm-color-04);
input:not(.form-field--copyable),
textarea,
select {
border-color: $lbry-gray-5;
}
}
}

View file

@ -15,4 +15,16 @@
position: absolute;
stroke: $lbry-gray-5;
}
[data-mode='dark'] & {
background-color: var(--color-card-actions--dark);
}
}
.icon--help {
margin-left: var(--spacing-small);
bottom: -0.3rem;
opacity: 0.7;
height: 1rem;
width: 1rem;
}

View file

@ -74,6 +74,10 @@
align-items: flex-start;
max-width: 40rem;
text-align: left;
& > * {
width: 100%;
}
}
.main--full-width {

View file

@ -77,10 +77,10 @@
}
[data-mode='dark'] & {
color: var(--dm-color-01);
color: darken($lbry-white, 30%);
svg {
stroke: var(--dm-color-01);
stroke: darken($lbry-white, 30%);
}
&:hover,

View file

@ -48,8 +48,8 @@ $main: $lbry-teal-5;
margin-top: -2px; // To handle the border height
[data-mode='dark'] & {
background-color: var(--dm-color-02);
border-color: $lbry-white;
background-color: var(--dm-color-03);
border-color: $lbry-gray-4;
}
}
}

View file

@ -20,6 +20,10 @@
position: absolute;
z-index: 1;
stroke: $lbry-gray-5;
[data-mode='dark'] & {
stroke: $lbry-gray-3;
}
}
}

View file

@ -7,7 +7,7 @@
}
.section--padded {
padding: var(--spacing-large);
padding: var(--spacing-medium);
}
.section--small {
@ -31,7 +31,6 @@
@extend .section__flex;
flex-wrap: wrap;
margin-bottom: var(--spacing-large);
& > * {
margin-bottom: var(--spacing-large);
}

View file

@ -70,6 +70,7 @@ dl {
display: flex;
flex-direction: row;
flex-wrap: wrap;
overflow-x: visible;
margin-top: var(--spacing-medium);
}
@ -284,12 +285,3 @@ legend {
border-color: transparent;
}
}
checkbox-toggle,
.checkbox-toggle,
radio-toggle,
.radio-toggle {
[data-mode='dark'] & {
border-color: var(--dm-color-04);
}
}

View file

@ -41,6 +41,7 @@ $large-breakpoint: 1921px;
--color-background: #f7f7f7;
--color-background--splash: #270f34;
--color-card-actions: #f7fbfe;
--color-card-actions--dark: #545454;
// Dark Mode
--dm-color-01: #ddd;

View file

@ -63,6 +63,7 @@ const whiteListedReducers = [
'search',
'blocked',
'settings',
'sync',
];
const transforms = [

View file

@ -6858,9 +6858,9 @@ lbry-redux@lbryio/lbry-redux#85fe532d69704a283fd2ec640ad6e3b8dacd6d0d:
reselect "^3.0.0"
uuid "^3.3.2"
lbryinc@lbryio/lbryinc#a44576194e1f5f60e37d328ddfdca40bd6165c2d:
lbryinc@lbryio/lbryinc#f5bee9cd300c4bdc05228e31ea15cfc430975f67:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/a44576194e1f5f60e37d328ddfdca40bd6165c2d"
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/f5bee9cd300c4bdc05228e31ea15cfc430975f67"
dependencies:
reselect "^3.0.0"