onboarding + youtube transfer + channel page
This commit is contained in:
parent
c0a8e02004
commit
4c014e3147
55 changed files with 775 additions and 337 deletions
|
@ -128,8 +128,8 @@
|
||||||
"husky": "^0.14.3",
|
"husky": "^0.14.3",
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
||||||
"lbry-redux": "lbryio/lbry-redux#04ae0913a444abac200731c7ed53796d763a0bbb",
|
"lbry-redux": "lbryio/lbry-redux#d44cd9ca56dee784dba42c0cc13061ae75cbd46c",
|
||||||
"lbryinc": "lbryio/lbryinc#d99232ebc754a49649a2ff4132478415faef08e2",
|
"lbryinc": "lbryio/lbryinc#368040d64658cf2a4b8a7a6725ec1787329ce65d",
|
||||||
"lint-staged": "^7.0.2",
|
"lint-staged": "^7.0.2",
|
||||||
"localforage": "^1.7.1",
|
"localforage": "^1.7.1",
|
||||||
"lodash-es": "^4.17.14",
|
"lodash-es": "^4.17.14",
|
||||||
|
@ -151,6 +151,7 @@
|
||||||
"raw-loader": "^2.0.0",
|
"raw-loader": "^2.0.0",
|
||||||
"rc-progress": "^2.0.6",
|
"rc-progress": "^2.0.6",
|
||||||
"react": "^16.8.2",
|
"react": "^16.8.2",
|
||||||
|
"react-confetti": "^4.0.1",
|
||||||
"react-dom": "^16.8.2",
|
"react-dom": "^16.8.2",
|
||||||
"react-draggable": "^3.3.0",
|
"react-draggable": "^3.3.0",
|
||||||
"react-ga": "^2.5.7",
|
"react-ga": "^2.5.7",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import * as SETTINGS from 'constants/settings';
|
import * as SETTINGS from 'constants/settings';
|
||||||
import { hot } from 'react-hot-loader/root';
|
import { hot } from 'react-hot-loader/root';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { selectUser, doRewardList, doFetchRewardedContent, doFetchAccessToken, selectAccessToken } from 'lbryinc';
|
import { selectUser, doRewardList, doFetchRewardedContent, doFetchAccessToken } from 'lbryinc';
|
||||||
import { doFetchTransactions, doFetchChannelListMine } from 'lbry-redux';
|
import { doFetchTransactions, doFetchChannelListMine } from 'lbry-redux';
|
||||||
import { makeSelectClientSetting, selectThemePath } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectThemePath } from 'redux/selectors/settings';
|
||||||
import { selectIsUpgradeAvailable, selectAutoUpdateDownloaded } from 'redux/selectors/app';
|
import { selectIsUpgradeAvailable, selectAutoUpdateDownloaded } from 'redux/selectors/app';
|
||||||
|
@ -12,7 +12,6 @@ const select = state => ({
|
||||||
user: selectUser(state),
|
user: selectUser(state),
|
||||||
theme: selectThemePath(state),
|
theme: selectThemePath(state),
|
||||||
language: makeSelectClientSetting(SETTINGS.LANGUAGE)(state),
|
language: makeSelectClientSetting(SETTINGS.LANGUAGE)(state),
|
||||||
accessToken: selectAccessToken(state),
|
|
||||||
autoUpdateDownloaded: selectAutoUpdateDownloaded(state),
|
autoUpdateDownloaded: selectAutoUpdateDownloaded(state),
|
||||||
isUpgradeAvailable: selectIsUpgradeAvailable(state),
|
isUpgradeAvailable: selectIsUpgradeAvailable(state),
|
||||||
});
|
});
|
||||||
|
@ -22,6 +21,8 @@ const perform = dispatch => ({
|
||||||
fetchRewardedContent: () => dispatch(doFetchRewardedContent()),
|
fetchRewardedContent: () => dispatch(doFetchRewardedContent()),
|
||||||
fetchTransactions: () => dispatch(doFetchTransactions()),
|
fetchTransactions: () => dispatch(doFetchTransactions()),
|
||||||
fetchAccessToken: () => dispatch(doFetchAccessToken()),
|
fetchAccessToken: () => dispatch(doFetchAccessToken()),
|
||||||
|
fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
|
||||||
|
onSignedIn: () => dispatch(doSignIn()),
|
||||||
requestDownloadUpgrade: () => dispatch(doDownloadUpgradeRequested()),
|
requestDownloadUpgrade: () => dispatch(doDownloadUpgradeRequested()),
|
||||||
fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
|
fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
|
||||||
onSignedIn: () => dispatch(doSignIn()),
|
onSignedIn: () => dispatch(doSignIn()),
|
||||||
|
|
|
@ -12,6 +12,7 @@ import Yrbl from 'component/yrbl';
|
||||||
import FileViewer from 'component/fileViewer';
|
import FileViewer from 'component/fileViewer';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import usePrevious from 'util/use-previous';
|
import usePrevious from 'util/use-previous';
|
||||||
|
import SyncBackgroundManager from 'component/syncBackgroundManager';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
|
|
||||||
export const MAIN_WRAPPER_CLASS = 'main-wrapper';
|
export const MAIN_WRAPPER_CLASS = 'main-wrapper';
|
||||||
|
@ -27,11 +28,13 @@ type Props = {
|
||||||
fetchRewardedContent: () => void,
|
fetchRewardedContent: () => void,
|
||||||
fetchTransactions: () => void,
|
fetchTransactions: () => void,
|
||||||
fetchAccessToken: () => void,
|
fetchAccessToken: () => void,
|
||||||
autoUpdateDownloaded: boolean,
|
fetchChannelListMine: () => void,
|
||||||
isUpgradeAvailable: boolean,
|
onSignedIn: () => void,
|
||||||
requestDownloadUpgrade: () => void,
|
requestDownloadUpgrade: () => void,
|
||||||
fetchChannelListMine: () => void,
|
fetchChannelListMine: () => void,
|
||||||
onSignedIn: () => void,
|
onSignedIn: () => void,
|
||||||
|
isUpgradeAvailable: boolean,
|
||||||
|
autoUpdateDownloaded: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function App(props: Props) {
|
function App(props: Props) {
|
||||||
|
@ -42,11 +45,11 @@ function App(props: Props) {
|
||||||
fetchTransactions,
|
fetchTransactions,
|
||||||
user,
|
user,
|
||||||
fetchAccessToken,
|
fetchAccessToken,
|
||||||
requestDownloadUpgrade,
|
|
||||||
autoUpdateDownloaded,
|
|
||||||
isUpgradeAvailable,
|
|
||||||
fetchChannelListMine,
|
fetchChannelListMine,
|
||||||
onSignedIn,
|
onSignedIn,
|
||||||
|
autoUpdateDownloaded,
|
||||||
|
isUpgradeAvailable,
|
||||||
|
requestDownloadUpgrade,
|
||||||
} = props;
|
} = props;
|
||||||
const appRef = useRef();
|
const appRef = useRef();
|
||||||
const isEnhancedLayout = useKonamiListener();
|
const isEnhancedLayout = useKonamiListener();
|
||||||
|
@ -118,6 +121,7 @@ function App(props: Props) {
|
||||||
<Router />
|
<Router />
|
||||||
<ModalRouter />
|
<ModalRouter />
|
||||||
<FileViewer pageUri={uri} />
|
<FileViewer pageUri={uri} />
|
||||||
|
<SyncBackgroundManager />
|
||||||
|
|
||||||
{/* @if TARGET='app' */}
|
{/* @if TARGET='app' */}
|
||||||
{showUpgradeButton && (
|
{showUpgradeButton && (
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
import type { Node } from 'react';
|
||||||
import React, { Fragment, useEffect, forwardRef } from 'react';
|
import React, { Fragment, useEffect, forwardRef } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { parseURI, convertToShareLink } from 'lbry-redux';
|
import { parseURI, convertToShareLink } from 'lbry-redux';
|
||||||
|
@ -45,6 +46,9 @@ type Props = {
|
||||||
channelIsBlocked: boolean,
|
channelIsBlocked: boolean,
|
||||||
isSubscribed: boolean,
|
isSubscribed: boolean,
|
||||||
beginPublish: string => void,
|
beginPublish: string => void,
|
||||||
|
actions: boolean | Node | string | number,
|
||||||
|
properties: boolean | Node | string | number,
|
||||||
|
onClick?: any => any,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
|
@ -70,12 +74,14 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
channelIsBlocked,
|
channelIsBlocked,
|
||||||
isSubscribed,
|
isSubscribed,
|
||||||
beginPublish,
|
beginPublish,
|
||||||
|
actions,
|
||||||
|
properties,
|
||||||
|
onClick,
|
||||||
} = props;
|
} = 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));
|
||||||
const abandoned = !isResolvingUri && !claim;
|
const abandoned = !isResolvingUri && !claim;
|
||||||
const claimsInChannel = (claim && claim.meta.claims_in_channel) || 0;
|
const claimsInChannel = (claim && claim.meta.claims_in_channel) || 0;
|
||||||
const showPublishLink = abandoned && placeholder === 'publish';
|
const showPublishLink = abandoned && placeholder === 'publish';
|
||||||
const includeChannelTooltip = type !== 'inline' && type !== 'tooltip';
|
|
||||||
const hideActions = type === 'small' || type === 'tooltip';
|
const hideActions = type === 'small' || type === 'tooltip';
|
||||||
|
|
||||||
let name;
|
let name;
|
||||||
|
@ -90,6 +96,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const isChannel = isValid ? parseURI(uri).isChannel : false;
|
const isChannel = isValid ? parseURI(uri).isChannel : false;
|
||||||
|
const includeChannelTooltip = type !== 'inline' && type !== 'tooltip' && !isChannel;
|
||||||
const signingChannel = claim && claim.signing_channel;
|
const signingChannel = claim && claim.signing_channel;
|
||||||
let shouldHide =
|
let shouldHide =
|
||||||
placeholder !== 'loading' && ((abandoned && !showPublishLink) || (!claimIsMine && obscureNsfw && nsfw));
|
placeholder !== 'loading' && ((abandoned && !showPublishLink) || (!claimIsMine && obscureNsfw && nsfw));
|
||||||
|
@ -129,8 +136,10 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClick(e) {
|
function handleOnClick(e) {
|
||||||
if ((isChannel || title) && !pending) {
|
if (onClick) {
|
||||||
|
onClick(e);
|
||||||
|
} else if ((isChannel || title) && !pending) {
|
||||||
history.push(formatLbryUriForWeb(uri));
|
history.push(formatLbryUriForWeb(uri));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,7 +170,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
<li
|
<li
|
||||||
ref={ref}
|
ref={ref}
|
||||||
role="link"
|
role="link"
|
||||||
onClick={pending || type === 'inline' ? undefined : onClick}
|
onClick={pending || type === 'inline' ? undefined : handleOnClick}
|
||||||
onContextMenu={handleContextMenu}
|
onContextMenu={handleContextMenu}
|
||||||
className={classnames('claim-preview', {
|
className={classnames('claim-preview', {
|
||||||
'claim-preview--small': type === 'small' || type === 'tooltip',
|
'claim-preview--small': type === 'small' || type === 'tooltip',
|
||||||
|
@ -178,15 +187,19 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
<div className="claim-preview-title">
|
<div className="claim-preview-title">
|
||||||
{claim ? <TruncatedText text={title || claim.name} lines={1} /> : <span>{__('Nothing here')}</span>}
|
{claim ? <TruncatedText text={title || claim.name} lines={1} /> : <span>{__('Nothing here')}</span>}
|
||||||
</div>
|
</div>
|
||||||
{!hideActions && (
|
{actions !== undefined
|
||||||
<div className="card__actions--inline">
|
? actions
|
||||||
{isChannel && !channelIsBlocked && (
|
: !hideActions && (
|
||||||
<SubscribeButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />
|
<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 && <BlockButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />}
|
|
||||||
{!isChannel && claim && <FileProperties uri={uri} />}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="claim-preview-properties">
|
<div className="claim-preview-properties">
|
||||||
|
@ -219,7 +232,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ClaimTags uri={uri} type={type} />
|
{properties !== undefined ? properties : <ClaimTags uri={uri} type={type} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -104,9 +104,11 @@ export class FormField extends React.PureComponent<Props> {
|
||||||
input = (
|
input = (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<checkbox-element {...elementProps}>
|
<checkbox-element {...elementProps}>
|
||||||
<input id={name} type="checkbox" {...inputProps} />
|
<input id={name} type="checkbox" {...inputProps} tabIndex={0} />
|
||||||
<label htmlFor={name}>{label}</label>
|
<label htmlFor={name} tabIndex={-1}>
|
||||||
<checkbox-toggle onClick={inputProps.onChange} />
|
{label}
|
||||||
|
</label>
|
||||||
|
<checkbox-toggle onClick={inputProps.onChange} tabIndex={-1} />
|
||||||
</checkbox-element>
|
</checkbox-element>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
|
|
|
@ -303,6 +303,13 @@ export const icons = {
|
||||||
<line x1="15" y1="12" x2="3" y2="12" />
|
<line x1="15" y1="12" x2="3" y2="12" />
|
||||||
</g>
|
</g>
|
||||||
),
|
),
|
||||||
|
[ICONS.SIGN_IN]: buildIcon(
|
||||||
|
<g>
|
||||||
|
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4" />
|
||||||
|
<polyline points="10 17 15 12 10 7" />
|
||||||
|
<line x1="15" y1="12" x2="3" y2="12" />
|
||||||
|
</g>
|
||||||
|
),
|
||||||
[ICONS.SIGN_OUT]: buildIcon(
|
[ICONS.SIGN_OUT]: buildIcon(
|
||||||
<g>
|
<g>
|
||||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
|
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
|
||||||
|
|
|
@ -3,8 +3,8 @@ import { connect } from 'react-redux';
|
||||||
import { selectBalance, formatCredits } from 'lbry-redux';
|
import { selectBalance, formatCredits } from 'lbry-redux';
|
||||||
import { selectUserVerifiedEmail } from 'lbryinc';
|
import { selectUserVerifiedEmail } from 'lbryinc';
|
||||||
import { doSetClientSetting } from 'redux/actions/settings';
|
import { doSetClientSetting } from 'redux/actions/settings';
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
|
||||||
import { doSignOut } from 'redux/actions/app';
|
import { doSignOut } from 'redux/actions/app';
|
||||||
|
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
import Header from './view';
|
import Header from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
|
|
|
@ -13,11 +13,8 @@ import { Menu, MenuList, MenuButton, MenuItem } from '@reach/menu-button';
|
||||||
import Tooltip from 'component/common/tooltip';
|
import Tooltip from 'component/common/tooltip';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
autoUpdateDownloaded: boolean,
|
|
||||||
balance: string,
|
balance: string,
|
||||||
isUpgradeAvailable: boolean,
|
|
||||||
roundedBalance: number,
|
roundedBalance: number,
|
||||||
downloadUpgradeRequested: any => void,
|
|
||||||
history: { push: string => void, goBack: () => void, goForward: () => void },
|
history: { push: string => void, goBack: () => void, goForward: () => void },
|
||||||
currentTheme: string,
|
currentTheme: string,
|
||||||
automaticDarkModeEnabled: boolean,
|
automaticDarkModeEnabled: boolean,
|
||||||
|
@ -137,7 +134,10 @@ const Header = (props: Props) => {
|
||||||
{__('Sign Out')}
|
{__('Sign Out')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
) : (
|
) : (
|
||||||
<MenuItem onSelect={() => {}} />
|
<MenuItem className="menu__link" onSelect={() => history.push(`/$/${PAGES.AUTH}`)}>
|
||||||
|
<Icon aria-hidden icon={ICONS.SIGN_IN} />
|
||||||
|
{__('Sign In')}
|
||||||
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
</MenuList>
|
</MenuList>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|
|
@ -77,7 +77,7 @@ class InviteNew extends React.PureComponent<Props> {
|
||||||
<Card
|
<Card
|
||||||
title={__('Invite a Friend')}
|
title={__('Invite a Friend')}
|
||||||
subtitle={__('When your friends start using LBRY, the network gets stronger!')}
|
subtitle={__('When your friends start using LBRY, the network gets stronger!')}
|
||||||
body={
|
actions={
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<FormInviteNew
|
<FormInviteNew
|
||||||
errorMessage={errorMessage}
|
errorMessage={errorMessage}
|
||||||
|
|
|
@ -16,7 +16,7 @@ class RewardSummary extends React.Component<Props> {
|
||||||
const hasRewards = unclaimedRewardAmount > 0;
|
const hasRewards = unclaimedRewardAmount > 0;
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
title={__('Rewards')}
|
title={__('Available Rewards')}
|
||||||
subtitle={
|
subtitle={
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{fetching && __('You have...')}
|
{fetching && __('You have...')}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import FollowingPage from 'page/following';
|
||||||
import ListBlockedPage from 'page/listBlocked';
|
import ListBlockedPage from 'page/listBlocked';
|
||||||
import FourOhFourPage from 'page/fourOhFour';
|
import FourOhFourPage from 'page/fourOhFour';
|
||||||
import SignInPage from 'page/signIn';
|
import SignInPage from 'page/signIn';
|
||||||
|
import ChannelsPage from 'page/channels';
|
||||||
|
|
||||||
// Tell the browser we are handling scroll restoration
|
// Tell the browser we are handling scroll restoration
|
||||||
if ('scrollRestoration' in history) {
|
if ('scrollRestoration' in history) {
|
||||||
|
@ -89,6 +90,7 @@ function AppRouter(props: Props) {
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.WALLET}`} exact component={WalletPage} />
|
<PrivateRoute {...props} path={`/$/${PAGES.WALLET}`} exact component={WalletPage} />
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.WALLET_SEND}`} exact component={WalletSendPage} />
|
<PrivateRoute {...props} path={`/$/${PAGES.WALLET_SEND}`} exact component={WalletSendPage} />
|
||||||
<PrivateRoute {...props} path={`/$/${PAGES.WALLET_RECEIVE}`} exact component={WalletReceivePage} />
|
<PrivateRoute {...props} path={`/$/${PAGES.WALLET_RECEIVE}`} exact component={WalletReceivePage} />
|
||||||
|
<PrivateRoute {...props} path={`/$/${PAGES.CHANNELS}`} component={ChannelsPage} />
|
||||||
|
|
||||||
{/* Below need to go at the end to make sure we don't match any of our pages first */}
|
{/* Below need to go at the end to make sure we don't match any of our pages first */}
|
||||||
<Route path="/:claimName" exact component={ShowPage} />
|
<Route path="/:claimName" exact component={ShowPage} />
|
||||||
|
|
|
@ -49,6 +49,9 @@ function SideBar(props: Props) {
|
||||||
...buildLink(PAGES.LIBRARY, __('Library'), ICONS.LIBRARY),
|
...buildLink(PAGES.LIBRARY, __('Library'), ICONS.LIBRARY),
|
||||||
},
|
},
|
||||||
// @endif
|
// @endif
|
||||||
|
{
|
||||||
|
...buildLink(PAGES.CHANNELS, __('Channels'), ICONS.CHANNEL),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
...buildLink(PAGES.PUBLISHED, __('Publishes'), ICONS.PUBLISH),
|
...buildLink(PAGES.PUBLISHED, __('Publishes'), ICONS.PUBLISH),
|
||||||
},
|
},
|
||||||
|
|
|
@ -124,6 +124,11 @@ export default class SplashScreen extends React.PureComponent<Props, State> {
|
||||||
clearTimeout(this.timeout);
|
clearTimeout(this.timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Try to unlock by default here
|
||||||
|
//
|
||||||
|
//
|
||||||
// Make sure there isn't another active modal (like INCOMPATIBLE_DAEMON)
|
// Make sure there isn't another active modal (like INCOMPATIBLE_DAEMON)
|
||||||
if (launchedModal === false && !modal) {
|
if (launchedModal === false && !modal) {
|
||||||
this.setState({ launchedModal: true }, () => notifyUnlockWallet());
|
this.setState({ launchedModal: true }, () => notifyUnlockWallet());
|
||||||
|
|
24
src/ui/component/syncBackgroundManager/index.js
Normal file
24
src/ui/component/syncBackgroundManager/index.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import * as SETTINGS from 'constants/settings';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { selectBalance } from 'lbry-redux';
|
||||||
|
import { doGetSync, selectSyncHash, selectUserVerifiedEmail } from 'lbryinc';
|
||||||
|
import { doSetClientSetting } from 'redux/actions/settings';
|
||||||
|
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
|
import WalletSecurityAndSync from './view';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
balance: selectBalance(state),
|
||||||
|
verifiedEmail: selectUserVerifiedEmail(state),
|
||||||
|
syncEnabled: makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state),
|
||||||
|
hasSyncHash: selectSyncHash(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
getSync: password => dispatch(doGetSync(password)),
|
||||||
|
setSync: value => dispatch(doSetClientSetting(SETTINGS.ENABLE_SYNC, value)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(WalletSecurityAndSync);
|
22
src/ui/component/syncBackgroundManager/view.jsx
Normal file
22
src/ui/component/syncBackgroundManager/view.jsx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
syncEnabled: boolean,
|
||||||
|
verifiedEmail?: string,
|
||||||
|
getSync: () => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
function SyncBackgroundManager(props: Props) {
|
||||||
|
const { syncEnabled, getSync, verifiedEmail } = props;
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (syncEnabled && verifiedEmail) {
|
||||||
|
getSync();
|
||||||
|
}
|
||||||
|
}, [syncEnabled, verifiedEmail, getSync]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SyncBackgroundManager;
|
18
src/ui/component/syncPassword/index.js
Normal file
18
src/ui/component/syncPassword/index.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { doGetSync, selectGetSyncIsPending } from 'lbryinc';
|
||||||
|
import { doSetClientSetting } from 'redux/actions/settings';
|
||||||
|
import SyncPassword from './view';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
getSyncIsPending: selectGetSyncIsPending(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
getSync: password => dispatch(doGetSync(password)),
|
||||||
|
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(SyncPassword);
|
54
src/ui/component/syncPassword/view.jsx
Normal file
54
src/ui/component/syncPassword/view.jsx
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
// @flow
|
||||||
|
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';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
getSync: (?string) => void,
|
||||||
|
getSyncIsPending: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
function SyncPassword(props: Props) {
|
||||||
|
const { getSync, getSyncIsPending } = props;
|
||||||
|
const [password, setPassword] = React.useState('');
|
||||||
|
const [rememberPassword, setRememberPassword] = React.useState(true);
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
if (rememberPassword) {
|
||||||
|
setSavedPassword(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSync(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form onSubmit={handleSubmit}>
|
||||||
|
<Card
|
||||||
|
title={__('Enter Your LBRY Password')}
|
||||||
|
actions={
|
||||||
|
<div>
|
||||||
|
<FormField
|
||||||
|
type="password"
|
||||||
|
label={__('Password')}
|
||||||
|
value={password}
|
||||||
|
onChange={e => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
type="checkbox"
|
||||||
|
label={__('Remember My Password')}
|
||||||
|
checked={rememberPassword}
|
||||||
|
onChange={() => setRememberPassword(!rememberPassword)}
|
||||||
|
/>
|
||||||
|
<div className="card__actions">
|
||||||
|
<Button type="submit" button="primary" label={__('Continue')} disabled={!password || getSyncIsPending} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SyncPassword;
|
|
@ -75,20 +75,18 @@ export default function TagSelect(props: Props) {
|
||||||
}
|
}
|
||||||
body={
|
body={
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<section className="section">
|
<TagsSearch
|
||||||
<TagsSearch
|
onRemove={handleTagClick}
|
||||||
onRemove={handleTagClick}
|
onSelect={onSelect}
|
||||||
onSelect={onSelect}
|
suggestMature={suggestMature && !hasMatureTag}
|
||||||
suggestMature={suggestMature && !hasMatureTag}
|
tagsPasssedIn={tagsToDisplay}
|
||||||
tagsPasssedIn={tagsToDisplay}
|
/>
|
||||||
/>
|
{help !== false && (
|
||||||
{help !== false && (
|
<p className="help">
|
||||||
<p className="help">
|
{help || __("The tags you follow will change what's trending for you.")}{' '}
|
||||||
{help || __("The tags you follow will change what's trending for you.")}{' '}
|
<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/trending" />.
|
||||||
<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/trending" />.
|
</p>
|
||||||
</p>
|
)}
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -39,8 +39,10 @@ function UserEmail(props: Props) {
|
||||||
subtitle={__(
|
subtitle={__(
|
||||||
'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.'
|
'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.'
|
||||||
)}
|
)}
|
||||||
body={
|
actions={
|
||||||
isVerified ? (
|
!isVerified ? (
|
||||||
|
<Button button="primary" label={__('Add Email')} navigate={`/$/${PAGES.AUTH}`} />
|
||||||
|
) : (
|
||||||
<FormField
|
<FormField
|
||||||
type="text"
|
type="text"
|
||||||
className="form-field--copyable"
|
className="form-field--copyable"
|
||||||
|
@ -58,9 +60,8 @@ function UserEmail(props: Props) {
|
||||||
inputButton={<UserSignOutButton button="inverse" />}
|
inputButton={<UserSignOutButton button="inverse" />}
|
||||||
value={email || ''}
|
value={email || ''}
|
||||||
/>
|
/>
|
||||||
) : null
|
)
|
||||||
}
|
}
|
||||||
actions={!isVerified ? <Button button="primary" label={__('Add Email')} navigate={`/$/${PAGES.AUTH}`} /> : null}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,21 @@
|
||||||
|
import * as SETTINGS from 'constants/settings';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { selectBalance } from 'lbry-redux';
|
||||||
import { selectEmailNewIsPending, selectEmailNewErrorMessage, doUserEmailNew } from 'lbryinc';
|
import { selectEmailNewIsPending, selectEmailNewErrorMessage, doUserEmailNew } from 'lbryinc';
|
||||||
|
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
|
import { doSetClientSetting } from 'redux/actions/settings';
|
||||||
import UserEmailNew from './view';
|
import UserEmailNew from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
isPending: selectEmailNewIsPending(state),
|
isPending: selectEmailNewIsPending(state),
|
||||||
errorMessage: selectEmailNewErrorMessage(state),
|
errorMessage: selectEmailNewErrorMessage(state),
|
||||||
|
syncEnabled: makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state),
|
||||||
|
balance: selectBalance(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
addUserEmail: email => dispatch(doUserEmailNew(email)),
|
addUserEmail: email => dispatch(doUserEmailNew(email)),
|
||||||
|
setSync: value => dispatch(doSetClientSetting(SETTINGS.ENABLE_SYNC, value)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|
|
@ -4,19 +4,19 @@ import { FormField, Form } from 'component/common/form';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import { Lbryio } from 'lbryinc';
|
import { Lbryio } from 'lbryinc';
|
||||||
import analytics from 'analytics';
|
import analytics from 'analytics';
|
||||||
|
import { EMAIL_REGEX } from 'constants/email';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
errorMessage: ?string,
|
errorMessage: ?string,
|
||||||
isPending: boolean,
|
isPending: boolean,
|
||||||
addUserEmail: string => void,
|
addUserEmail: string => void,
|
||||||
|
syncEnabled: boolean,
|
||||||
|
setSync: boolean => void,
|
||||||
|
balance: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
// "Email regex that 99.99% works"
|
|
||||||
// https://emailregex.com/
|
|
||||||
const EMAIL_REGEX = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/;
|
|
||||||
|
|
||||||
function UserEmailNew(props: Props) {
|
function UserEmailNew(props: Props) {
|
||||||
const { errorMessage, isPending, addUserEmail } = props;
|
const { errorMessage, isPending, addUserEmail, syncEnabled, setSync, balance } = props;
|
||||||
const [newEmail, setEmail] = useState('');
|
const [newEmail, setEmail] = useState('');
|
||||||
const valid = newEmail.match(EMAIL_REGEX);
|
const valid = newEmail.match(EMAIL_REGEX);
|
||||||
|
|
||||||
|
@ -29,6 +29,12 @@ function UserEmailNew(props: Props) {
|
||||||
// @endif
|
// @endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (syncEnabled && balance) {
|
||||||
|
setSync(false);
|
||||||
|
}
|
||||||
|
}, [balance, syncEnabled, setSync]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1 className="section__title--large">{__('Welcome To LBRY')}</h1>
|
<h1 className="section__title--large">{__('Welcome To LBRY')}</h1>
|
||||||
|
@ -39,12 +45,23 @@ function UserEmailNew(props: Props) {
|
||||||
className="form-field--short"
|
className="form-field--short"
|
||||||
placeholder={__('hotstuff_96@hotmail.com')}
|
placeholder={__('hotstuff_96@hotmail.com')}
|
||||||
type="email"
|
type="email"
|
||||||
id="sign_up_email"
|
name="sign_up_email"
|
||||||
label={__('Email')}
|
label={__('Email')}
|
||||||
value={newEmail}
|
value={newEmail}
|
||||||
error={errorMessage}
|
error={errorMessage}
|
||||||
onChange={e => setEmail(e.target.value)}
|
onChange={e => setEmail(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
<FormField
|
||||||
|
type="checkbox"
|
||||||
|
name="sync_checkbox"
|
||||||
|
label={__('Sync your balance between devices')}
|
||||||
|
helper={
|
||||||
|
balance > 0 ? __('This is only available for empty wallets') : __('Maybe some more text about something')
|
||||||
|
}
|
||||||
|
checked={syncEnabled}
|
||||||
|
onChange={() => setSync(!syncEnabled)}
|
||||||
|
disabled={balance > 0}
|
||||||
|
/>
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
<Button button="primary" type="submit" label={__('Continue')} disabled={!newEmail || !valid || isPending} />
|
<Button button="primary" type="submit" label={__('Continue')} disabled={!newEmail || !valid || isPending} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import * as SETTINGS from 'constants/settings';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
selectEmailToVerify,
|
selectEmailToVerify,
|
||||||
|
@ -7,12 +8,19 @@ import {
|
||||||
selectClaimedRewards,
|
selectClaimedRewards,
|
||||||
rewards as REWARD_TYPES,
|
rewards as REWARD_TYPES,
|
||||||
doClaimRewardType,
|
doClaimRewardType,
|
||||||
|
doUserFetch,
|
||||||
|
selectUserIsPending,
|
||||||
|
selectYoutubeChannels,
|
||||||
|
selectGetSyncIsPending,
|
||||||
|
selectGetSyncErrorMessage,
|
||||||
|
selectSyncHash,
|
||||||
} from 'lbryinc';
|
} from 'lbryinc';
|
||||||
import { selectMyChannelClaims, selectBalance, selectFetchingMyChannels } from 'lbry-redux';
|
import { selectMyChannelClaims, selectBalance, selectFetchingMyChannels } from 'lbry-redux';
|
||||||
|
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
import UserSignIn from './view';
|
import UserSignIn from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
email: selectEmailToVerify(state),
|
emailToVerify: selectEmailToVerify(state),
|
||||||
user: selectUser(state),
|
user: selectUser(state),
|
||||||
accessToken: selectAccessToken(state),
|
accessToken: selectAccessToken(state),
|
||||||
channels: selectMyChannelClaims(state),
|
channels: selectMyChannelClaims(state),
|
||||||
|
@ -22,9 +30,16 @@ const select = state => ({
|
||||||
}),
|
}),
|
||||||
balance: selectBalance(state),
|
balance: selectBalance(state),
|
||||||
fetchingChannels: selectFetchingMyChannels(state),
|
fetchingChannels: selectFetchingMyChannels(state),
|
||||||
|
youtubeChannels: selectYoutubeChannels(state),
|
||||||
|
userFetchPending: selectUserIsPending(state),
|
||||||
|
syncEnabled: makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state),
|
||||||
|
syncIsPending: selectGetSyncIsPending(state),
|
||||||
|
getSyncError: selectGetSyncErrorMessage(state),
|
||||||
|
syncHash: selectSyncHash(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
fetchUser: () => dispatch(doUserFetch()),
|
||||||
claimReward: () =>
|
claimReward: () =>
|
||||||
dispatch(
|
dispatch(
|
||||||
doClaimRewardType(REWARD_TYPES.TYPE_CONFIRM_EMAIL, {
|
doClaimRewardType(REWARD_TYPES.TYPE_CONFIRM_EMAIL, {
|
||||||
|
|
|
@ -4,24 +4,38 @@ import { withRouter } from 'react-router';
|
||||||
import UserEmailNew from 'component/userEmailNew';
|
import UserEmailNew from 'component/userEmailNew';
|
||||||
import UserEmailVerify from 'component/userEmailVerify';
|
import UserEmailVerify from 'component/userEmailVerify';
|
||||||
import UserFirstChannel from 'component/userFirstChannel';
|
import UserFirstChannel from 'component/userFirstChannel';
|
||||||
import UserVerify from 'component/userVerify';
|
|
||||||
import Spinner from 'component/spinner';
|
|
||||||
import { DEFAULT_BID_FOR_FIRST_CHANNEL } from 'component/userFirstChannel/view';
|
import { DEFAULT_BID_FOR_FIRST_CHANNEL } from 'component/userFirstChannel/view';
|
||||||
import { rewards as REWARDS } from 'lbryinc';
|
import { rewards as REWARDS } from 'lbryinc';
|
||||||
import usePrevious from 'util/use-previous';
|
import usePrevious from 'util/use-previous';
|
||||||
|
import UserVerify from 'component/userVerify';
|
||||||
|
import Spinner from 'component/spinner';
|
||||||
|
import YoutubeTransferWelcome from 'component/youtubeTransferWelcome';
|
||||||
|
import SyncPassword from 'component/syncPassword';
|
||||||
|
|
||||||
|
/*
|
||||||
|
- Brand new user
|
||||||
|
- Brand new user, not auto approved
|
||||||
|
- Second device (first time user), first device has a password + rewards not approved
|
||||||
|
- Second device (first time user), first device has a password + rewards approved
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
user: ?User,
|
user: ?User,
|
||||||
email: ?string,
|
emailToVerify: ?string,
|
||||||
fetchingChannels: boolean,
|
|
||||||
channels: ?Array<string>,
|
channels: ?Array<string>,
|
||||||
balance: ?number,
|
balance: ?number,
|
||||||
fetchingChannels: boolean,
|
fetchingChannels: boolean,
|
||||||
claimingReward: boolean,
|
claimingReward: boolean,
|
||||||
claimReward: () => void,
|
claimReward: () => void,
|
||||||
|
fetchUser: () => void,
|
||||||
claimedRewards: Array<Reward>,
|
claimedRewards: Array<Reward>,
|
||||||
history: { replace: string => void },
|
history: { replace: string => void },
|
||||||
location: { search: string },
|
location: { search: string },
|
||||||
|
youtubeChannels: Array<any>,
|
||||||
|
syncIsPending: boolean,
|
||||||
|
getSyncError: ?string,
|
||||||
|
hasSyncedSuccessfully: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
function useFetched(fetching) {
|
function useFetched(fetching) {
|
||||||
|
@ -38,64 +52,96 @@ function useFetched(fetching) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function UserSignIn(props: Props) {
|
function UserSignIn(props: Props) {
|
||||||
const { email, user, channels, claimingReward, claimReward, claimedRewards, balance, history, location } = props;
|
const {
|
||||||
|
emailToVerify,
|
||||||
|
user,
|
||||||
|
claimingReward,
|
||||||
|
claimedRewards,
|
||||||
|
channels,
|
||||||
|
claimReward,
|
||||||
|
balance,
|
||||||
|
history,
|
||||||
|
location,
|
||||||
|
fetchUser,
|
||||||
|
youtubeChannels,
|
||||||
|
syncEnabled,
|
||||||
|
syncIsPending,
|
||||||
|
getSyncError,
|
||||||
|
syncHash,
|
||||||
|
fetchingChannels,
|
||||||
|
} = props;
|
||||||
const { search } = location;
|
const { search } = location;
|
||||||
const urlParams = new URLSearchParams(search);
|
const urlParams = new URLSearchParams(search);
|
||||||
const redirect = urlParams.get('redirect');
|
const redirect = urlParams.get('redirect');
|
||||||
const hasFetchedReward = useFetched(claimingReward);
|
|
||||||
const hasVerifiedEmail = user && user.has_verified_email;
|
const hasVerifiedEmail = user && user.has_verified_email;
|
||||||
const rewardsApproved = user && user.is_reward_approved;
|
const rewardsApproved = user && user.is_reward_approved;
|
||||||
|
const hasFetchedReward = useFetched(claimingReward);
|
||||||
|
// const hasFetchedSync = useFetched(syncIsPending);
|
||||||
|
// const hasTriedSyncForReal = syncEnabled && hasFetchedSync;
|
||||||
const channelCount = channels ? channels.length : 0;
|
const channelCount = channels ? channels.length : 0;
|
||||||
const hasFetchedChannels = channels !== undefined;
|
|
||||||
const hasClaimedEmailAward = claimedRewards.some(reward => reward.reward_type === REWARDS.TYPE_CONFIRM_EMAIL);
|
const hasClaimedEmailAward = claimedRewards.some(reward => reward.reward_type === REWARDS.TYPE_CONFIRM_EMAIL);
|
||||||
const memoizedClaimReward = React.useCallback(() => {
|
const hasYoutubeChannels = youtubeChannels && youtubeChannels.length;
|
||||||
claimReward();
|
const hasTransferrableYoutubeChannels = hasYoutubeChannels && youtubeChannels.some(channel => channel.transferable);
|
||||||
}, [claimReward]);
|
const hasPendingYoutubeTransfer =
|
||||||
|
hasYoutubeChannels && youtubeChannels.some(channel => channel.transfer_state === 'pending_transfer');
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (hasVerifiedEmail && balance !== undefined && !hasClaimedEmailAward && !hasFetchedReward) {
|
if (
|
||||||
memoizedClaimReward();
|
hasVerifiedEmail &&
|
||||||
|
balance !== undefined &&
|
||||||
|
!hasClaimedEmailAward &&
|
||||||
|
!hasFetchedReward &&
|
||||||
|
(!syncEnabled || (syncEnabled && syncHash))
|
||||||
|
) {
|
||||||
|
claimReward();
|
||||||
}
|
}
|
||||||
}, [hasVerifiedEmail, memoizedClaimReward, balance, hasClaimedEmailAward, hasFetchedReward]);
|
}, [hasVerifiedEmail, claimReward, balance, hasClaimedEmailAward, hasFetchedReward, syncEnabled, syncHash]);
|
||||||
|
|
||||||
if (
|
React.useEffect(() => {
|
||||||
!user ||
|
fetchUser();
|
||||||
(balance === 0 && !hasFetchedReward) ||
|
}, [fetchUser]);
|
||||||
(hasVerifiedEmail && balance === undefined) ||
|
|
||||||
(hasVerifiedEmail && !hasFetchedChannels)
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<div className="main--empty">
|
|
||||||
<Spinner delayed />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (balance === 0 && hasClaimedEmailAward) {
|
const SIGN_IN_FLOW = [
|
||||||
history.replace(redirect || '/');
|
!emailToVerify && !hasVerifiedEmail && <UserEmailNew />,
|
||||||
}
|
emailToVerify && !hasVerifiedEmail && <UserEmailVerify />,
|
||||||
|
hasVerifiedEmail && !rewardsApproved && <UserVerify />,
|
||||||
if (rewardsApproved && channelCount > 0) {
|
getSyncError && !syncHash && <SyncPassword />,
|
||||||
history.replace(redirect || '/');
|
hasVerifiedEmail && balance > DEFAULT_BID_FOR_FIRST_CHANNEL && channelCount === 0 && !hasYoutubeChannels && (
|
||||||
}
|
<UserFirstChannel />
|
||||||
|
),
|
||||||
if (rewardsApproved && hasFetchedReward && balance && (balance === 0 || balance < DEFAULT_BID_FOR_FIRST_CHANNEL)) {
|
hasVerifiedEmail && hasYoutubeChannels && (hasTransferrableYoutubeChannels || hasPendingYoutubeTransfer) && (
|
||||||
history.replace(redirect || '/');
|
<YoutubeTransferWelcome />
|
||||||
}
|
),
|
||||||
|
hasVerifiedEmail &&
|
||||||
return (
|
balance === 0 &&
|
||||||
<section>
|
!getSyncError &&
|
||||||
{hasVerifiedEmail && !rewardsApproved ? (
|
(fetchingChannels ||
|
||||||
<UserVerify />
|
!hasFetchedReward ||
|
||||||
) : (
|
claimingReward ||
|
||||||
<div className="main--contained">
|
syncIsPending ||
|
||||||
{!email && !hasVerifiedEmail && <UserEmailNew />}
|
(syncEnabled && !syncHash) ||
|
||||||
{email && !hasVerifiedEmail && <UserEmailVerify />}
|
// Just claimed the email award, wait until the balance updates to move forward
|
||||||
{hasVerifiedEmail && balance && balance > 0 && channelCount === 0 && <UserFirstChannel />}
|
(balance === 0 && hasFetchedReward && hasClaimedEmailAward)) && (
|
||||||
|
<div className="main--empty">
|
||||||
|
<Spinner />
|
||||||
</div>
|
</div>
|
||||||
)}
|
),
|
||||||
</section>
|
];
|
||||||
);
|
|
||||||
|
let componentToRender;
|
||||||
|
for (var i = SIGN_IN_FLOW.length - 1; i > -1; i--) {
|
||||||
|
const Component = SIGN_IN_FLOW[i];
|
||||||
|
if (Component) {
|
||||||
|
componentToRender = Component;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!componentToRender) {
|
||||||
|
history.replace(redirect || '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return <section className="main--contained">{componentToRender}</section>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withRouter(UserSignIn);
|
export default withRouter(UserSignIn);
|
||||||
|
|
|
@ -97,7 +97,6 @@ class WalletSendTip extends React.PureComponent<Props, State> {
|
||||||
inputButton={
|
inputButton={
|
||||||
<Button
|
<Button
|
||||||
button="primary"
|
button="primary"
|
||||||
type="submit"
|
|
||||||
label={__('Send')}
|
label={__('Send')}
|
||||||
disabled={isPending || tipError || !tipAmount}
|
disabled={isPending || tipError || !tipAmount}
|
||||||
onClick={this.handleSendButtonClicked}
|
onClick={this.handleSendButtonClicked}
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
// @flow
|
|
||||||
import React from 'react';
|
|
||||||
import Button from 'component/button';
|
|
||||||
type Channel = {
|
|
||||||
yt_channel_name: string,
|
|
||||||
lbry_channel_name: string,
|
|
||||||
channel_claim_id: string,
|
|
||||||
sync_status: string,
|
|
||||||
status_token: string,
|
|
||||||
transferable: boolean,
|
|
||||||
transfer_state: string,
|
|
||||||
publish_to_address: Array<string>,
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
channel: Channel,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function YoutubeChannelItem(props: Props) {
|
|
||||||
const {
|
|
||||||
yt_channel_name: ytName,
|
|
||||||
lbry_channel_name: lbryName,
|
|
||||||
sync_status: syncStatus,
|
|
||||||
status_token: statusToken,
|
|
||||||
transferable,
|
|
||||||
transfer_state: transferState,
|
|
||||||
} = props.channel;
|
|
||||||
|
|
||||||
const LBRY_YT_URL = 'https://lbry.com/youtube/status/';
|
|
||||||
const NOT_TRANSFERED = 'not_transferred';
|
|
||||||
const PENDING_TRANSFER = 'pending_transfer';
|
|
||||||
const COMPLETED_TRANSFER = 'completed_transfer';
|
|
||||||
|
|
||||||
function renderSwitch(param) {
|
|
||||||
switch (param) {
|
|
||||||
case NOT_TRANSFERED:
|
|
||||||
return __('Not Transferred');
|
|
||||||
case PENDING_TRANSFER:
|
|
||||||
return __('Pending Transfer');
|
|
||||||
case COMPLETED_TRANSFER:
|
|
||||||
return __('Completed Transfer');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// | Youtube Name | LBRY Name | SyncStatus | TransferStatus |
|
|
||||||
|
|
||||||
return (
|
|
||||||
<tr>
|
|
||||||
<td>{ytName}</td>
|
|
||||||
<td>
|
|
||||||
<Button button={'link'} navigate={`lbry://${lbryName}`} label={lbryName} />
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<Button button={'link'} href={`${LBRY_YT_URL}${statusToken}`} label={syncStatus} />
|
|
||||||
</td>
|
|
||||||
<td>{transferable ? renderSwitch(transferState) : __('Not Transferable')}</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
// @flow
|
|
||||||
import * as React from 'react';
|
|
||||||
import YoutubeChannelListItem from './internal/youtubeChannel';
|
|
||||||
import Button from 'component/button';
|
|
||||||
import Spinner from 'component/spinner';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
ytChannels: Array<any>,
|
|
||||||
ytImportPending: boolean,
|
|
||||||
userFetchPending: boolean,
|
|
||||||
claimChannels: () => void,
|
|
||||||
updateUser: () => void,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function YoutubeChannelList(props: Props) {
|
|
||||||
const { ytChannels, ytImportPending, userFetchPending, claimChannels, updateUser } = props;
|
|
||||||
const hasChannels = ytChannels && ytChannels.length;
|
|
||||||
const transferEnabled = ytChannels && ytChannels.some(el => el.transferable === true);
|
|
||||||
return (
|
|
||||||
hasChannels && (
|
|
||||||
<section className="card card--section">
|
|
||||||
<h2 className="card__title--between">
|
|
||||||
<span>Synced Youtube Channels{userFetchPending && <Spinner type="small" />}</span>
|
|
||||||
<div className="card__actions--inline">
|
|
||||||
<Button button="inverse" onClick={updateUser} label={__('Refresh')} />
|
|
||||||
</div>
|
|
||||||
</h2>
|
|
||||||
{transferEnabled && !IS_WEB && (
|
|
||||||
<p className="card__subtitle">LBRY is currently holding channels you can take control of.</p>
|
|
||||||
)}
|
|
||||||
{!transferEnabled && !IS_WEB && (
|
|
||||||
<p className="card__subtitle">LBRY is currently holding channels but none are ready for transfer yet.</p>
|
|
||||||
)}
|
|
||||||
{IS_WEB && (
|
|
||||||
<p className="card__subtitle">
|
|
||||||
{__(`LBRY.tv can't import accounts yet. `)}
|
|
||||||
<Button button="link" label={__('Download the app')} href="https://lbry.com/get" />
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<table className="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>{__('Youtube Name')}</th>
|
|
||||||
<th>{__('LBRY Name')} </th>
|
|
||||||
<th>{__('Sync Status')} </th>
|
|
||||||
<th>{__('Transfer Status')}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{ytChannels.map(channel => (
|
|
||||||
<YoutubeChannelListItem
|
|
||||||
key={`yt${channel.yt_channel_name}${channel.lbry_channel_name}`}
|
|
||||||
channel={channel}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div className="card__actions">
|
|
||||||
<Button
|
|
||||||
disabled={IS_WEB || !transferEnabled || ytImportPending}
|
|
||||||
button="primary"
|
|
||||||
onClick={claimChannels}
|
|
||||||
label={__('Claim Channels')}
|
|
||||||
/>
|
|
||||||
<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/youtube#transfer" />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
29
src/ui/component/youtubeTransferStatus/index.js
Normal file
29
src/ui/component/youtubeTransferStatus/index.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
selectYoutubeChannels,
|
||||||
|
selectYTImportPending,
|
||||||
|
selectUserIsPending,
|
||||||
|
doClaimYoutubeChannels,
|
||||||
|
doUserFetch,
|
||||||
|
selectYTImportVideosComplete,
|
||||||
|
doCheckYoutubeTransfer,
|
||||||
|
} from 'lbryinc';
|
||||||
|
import YoutubeChannelList from './view';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
youtubeChannels: selectYoutubeChannels(state),
|
||||||
|
ytImportPending: selectYTImportPending(state),
|
||||||
|
userFetchPending: selectUserIsPending(state),
|
||||||
|
videosImported: selectYTImportVideosComplete(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
claimChannels: () => dispatch(doClaimYoutubeChannels()),
|
||||||
|
updateUser: () => dispatch(doUserFetch()),
|
||||||
|
checkYoutubeTransfer: () => dispatch(doCheckYoutubeTransfer()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(YoutubeChannelList);
|
121
src/ui/component/youtubeTransferStatus/view.jsx
Normal file
121
src/ui/component/youtubeTransferStatus/view.jsx
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
// @flow
|
||||||
|
import * as React from 'react';
|
||||||
|
import Button from 'component/button';
|
||||||
|
import ClaimPreview from 'component/claimPreview';
|
||||||
|
import Card from 'component/common/card';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
youtubeChannels: Array<any>,
|
||||||
|
ytImportPending: boolean,
|
||||||
|
claimChannels: () => void,
|
||||||
|
updateUser: () => void,
|
||||||
|
checkYoutubeTransfer: () => void,
|
||||||
|
videosImported: ?Array<number>, // [currentAmountImported, totalAmountToImport]
|
||||||
|
};
|
||||||
|
|
||||||
|
const LBRY_YT_URL = 'https://lbry.com/youtube/status/';
|
||||||
|
const NOT_TRANSFERED = 'not_transferred';
|
||||||
|
const PENDING_TRANSFER = 'pending_transfer';
|
||||||
|
const COMPLETED_TRANSFER = 'completed_transfer';
|
||||||
|
|
||||||
|
export default function YoutubeTransferStatus(props: Props) {
|
||||||
|
const { youtubeChannels, ytImportPending, claimChannels, videosImported, checkYoutubeTransfer, updateUser } = props;
|
||||||
|
|
||||||
|
const hasChannels = youtubeChannels && youtubeChannels.length;
|
||||||
|
const transferEnabled = youtubeChannels && youtubeChannels.some(el => el.transferable === true);
|
||||||
|
const transferComplete =
|
||||||
|
youtubeChannels &&
|
||||||
|
youtubeChannels.some(({ transfer_state: transferState }) => transferState === COMPLETED_TRANSFER);
|
||||||
|
|
||||||
|
let youtubeUrls =
|
||||||
|
youtubeChannels &&
|
||||||
|
youtubeChannels.map(
|
||||||
|
({ lbry_channel_name: channelName, channel_claim_id: claimId }) => `lbry://${channelName}#${claimId}`
|
||||||
|
);
|
||||||
|
|
||||||
|
let total;
|
||||||
|
let complete;
|
||||||
|
if (!transferComplete && videosImported) {
|
||||||
|
complete = videosImported[0];
|
||||||
|
total = videosImported[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMessage(channel) {
|
||||||
|
const { transferable, transfer_state: transferState, sync_status: syncStatus } = channel;
|
||||||
|
if (!transferable) {
|
||||||
|
switch (transferState) {
|
||||||
|
case NOT_TRANSFERED:
|
||||||
|
return syncStatus[0].toUpperCase() + syncStatus.slice(1);
|
||||||
|
case PENDING_TRANSFER:
|
||||||
|
return __('Transfer in progress');
|
||||||
|
case COMPLETED_TRANSFER:
|
||||||
|
return __('Completed transfer');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return __('Ready to transfer');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
// If a channel is transferrable, theres nothing to check
|
||||||
|
if (!transferComplete) {
|
||||||
|
checkYoutubeTransfer();
|
||||||
|
|
||||||
|
let interval = setInterval(() => {
|
||||||
|
checkYoutubeTransfer();
|
||||||
|
updateUser();
|
||||||
|
}, 60 * 1000);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [transferComplete, checkYoutubeTransfer, updateUser]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
hasChannels &&
|
||||||
|
!transferComplete && (
|
||||||
|
<div>
|
||||||
|
<Card
|
||||||
|
title={youtubeUrls.length > 1 ? __('Your YouTube Channels') : __('Your YouTube Channel')}
|
||||||
|
subtitle={
|
||||||
|
<span>
|
||||||
|
{__('Your videos are currently being transferred. There is nothing else for you to do.')}{' '}
|
||||||
|
<Button button="link" href={LBRY_YT_URL} label={__('Learn more')} />.
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
body={
|
||||||
|
<section>
|
||||||
|
{youtubeUrls.map((url, index) => {
|
||||||
|
const channel = youtubeChannels[index];
|
||||||
|
const transferState = getMessage(channel);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={url}
|
||||||
|
style={{ border: '1px solid #ccc', borderRadius: 'var(--card-radius)', marginBottom: '1rem' }}
|
||||||
|
>
|
||||||
|
<ClaimPreview uri={url} actions={<span className="help">{transferState}</span>} properties={''} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{videosImported && (
|
||||||
|
<div className="section help">
|
||||||
|
{complete} / {total} {__('videos transferred')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
actions={
|
||||||
|
transferEnabled &&
|
||||||
|
!ytImportPending && (
|
||||||
|
<div className="card__actions">
|
||||||
|
<Button button="primary" onClick={claimChannels} label={__('Claim Channels')} />
|
||||||
|
<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/youtube#transfer" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
|
@ -6,12 +6,11 @@ import {
|
||||||
doClaimYoutubeChannels,
|
doClaimYoutubeChannels,
|
||||||
doUserFetch,
|
doUserFetch,
|
||||||
} from 'lbryinc';
|
} from 'lbryinc';
|
||||||
|
|
||||||
import YoutubeChannelList from './view';
|
import YoutubeChannelList from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
ytChannels: selectYoutubeChannels(state),
|
youtubeChannels: selectYoutubeChannels(state),
|
||||||
ytImportPending: selectYTImportPending(state),
|
youtubeImportPending: selectYTImportPending(state),
|
||||||
userFetchPending: selectUserIsPending(state),
|
userFetchPending: selectUserIsPending(state),
|
||||||
});
|
});
|
||||||
|
|
76
src/ui/component/youtubeTransferWelcome/view.jsx
Normal file
76
src/ui/component/youtubeTransferWelcome/view.jsx
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
// @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';
|
||||||
|
|
||||||
|
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 === '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--claim-preview-wrap')}>
|
||||||
|
<ClaimPreview disabled onClick={() => {}} actions={false} uri={`lbry://${channelName}#${claimId}`} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{hasPendingYoutubeTransfer ? (
|
||||||
|
<section className="section">
|
||||||
|
<div className="section__header">
|
||||||
|
<h1 className="section__title">{__('Transfer In Progress...')}</h1>
|
||||||
|
<p className="section__subtitle">{__('You can now publish and comment using your official channel.')}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<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">
|
||||||
|
<div className="section__header">
|
||||||
|
<h1 className="section__title">{__('Begin Transfer')}</h1>
|
||||||
|
<p className="section__subtitle">{__('Do it to it.')}</p>
|
||||||
|
</div>
|
||||||
|
<div className="section__actions">
|
||||||
|
<Button button="primary" label={__('Transfer')} onClick={claimChannels} />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasPendingYoutubeTransfer && <Confetti recycle={false} style={{ position: 'fixed' }} />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
3
src/ui/constants/email.js
Normal file
3
src/ui/constants/email.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// "Email regex that 99.99% works"
|
||||||
|
// https://emailregex.com/
|
||||||
|
export const EMAIL_REGEX = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/;
|
|
@ -77,3 +77,4 @@ export const VIEW = 'View';
|
||||||
export const EYE = 'Eye';
|
export const EYE = 'Eye';
|
||||||
export const EYE_OFF = 'EyeOff';
|
export const EYE_OFF = 'EyeOff';
|
||||||
export const SIGN_OUT = 'SignOut';
|
export const SIGN_OUT = 'SignOut';
|
||||||
|
export const SIGN_IN = 'SignIn';
|
||||||
|
|
|
@ -23,3 +23,4 @@ export const WALLET = 'wallet';
|
||||||
export const WALLET_SEND = 'wallet/send';
|
export const WALLET_SEND = 'wallet/send';
|
||||||
export const WALLET_RECEIVE = 'wallet/receive';
|
export const WALLET_RECEIVE = 'wallet/receive';
|
||||||
export const BLOCKED = 'blocked';
|
export const BLOCKED = 'blocked';
|
||||||
|
export const CHANNELS = 'channels';
|
||||||
|
|
|
@ -20,3 +20,4 @@ export const HIDE_BALANCE = 'hide_balance';
|
||||||
export const HIDE_SPLASH_ANIMATION = 'hide_splash_animation';
|
export const HIDE_SPLASH_ANIMATION = 'hide_splash_animation';
|
||||||
export const FLOATING_PLAYER = 'floating_player';
|
export const FLOATING_PLAYER = 'floating_player';
|
||||||
export const DARK_MODE_TIMES = 'dark_mode_times';
|
export const DARK_MODE_TIMES = 'dark_mode_times';
|
||||||
|
export const ENABLE_SYNC = 'enable_sync';
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Modal } from 'modal/modal';
|
import { Modal } from 'modal/modal';
|
||||||
import { deleteAuthToken } from 'util/saved-passwords';
|
import { deleteSavedPassword } from 'util/saved-passwords';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
closeModal: () => void,
|
closeModal: () => void,
|
||||||
|
callback?: () => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ModalPasswordUnsave extends React.PureComponent<Props> {
|
class ModalPasswordUnsave extends React.PureComponent<Props> {
|
||||||
|
@ -18,8 +19,11 @@ class ModalPasswordUnsave extends React.PureComponent<Props> {
|
||||||
confirmButtonLabel={__('Forget')}
|
confirmButtonLabel={__('Forget')}
|
||||||
abortButtonLabel={__('Nevermind')}
|
abortButtonLabel={__('Nevermind')}
|
||||||
onConfirmed={() =>
|
onConfirmed={() =>
|
||||||
deleteAuthToken().then(() => {
|
deleteSavedPassword().then(() => {
|
||||||
this.props.closeModal();
|
this.props.closeModal();
|
||||||
|
if (this.props.callback) {
|
||||||
|
this.props.callback();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
onAborted={this.props.closeModal}
|
onAborted={this.props.closeModal}
|
||||||
|
|
|
@ -67,11 +67,11 @@ class ModalWalletUnlock extends React.PureComponent<Props, State> {
|
||||||
onConfirmed={() => unlockWallet(password)}
|
onConfirmed={() => unlockWallet(password)}
|
||||||
onAborted={quit}
|
onAborted={quit}
|
||||||
>
|
>
|
||||||
<Form onSubmit={() => unlockWallet(password)}>
|
<p>
|
||||||
<p>
|
{__('Your wallet has been encrypted with a local password. Please enter your wallet password to proceed.')}{' '}
|
||||||
{__('Your wallet has been encrypted with a local password. Please enter your wallet password to proceed.')}{' '}
|
<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" />.
|
</p>
|
||||||
</p>
|
<Form className="section" onSubmit={() => unlockWallet(password)}>
|
||||||
<FormField
|
<FormField
|
||||||
autoFocus
|
autoFocus
|
||||||
error={walletUnlockSucceded === false ? 'Incorrect Password' : false}
|
error={walletUnlockSucceded === false ? 'Incorrect Password' : false}
|
||||||
|
@ -88,7 +88,6 @@ class ModalWalletUnlock extends React.PureComponent<Props, State> {
|
||||||
name="wallet-remember-password"
|
name="wallet-remember-password"
|
||||||
onChange={event => this.onChangeRememberPassword(event)}
|
onChange={event => this.onChangeRememberPassword(event)}
|
||||||
checked={rememberPassword}
|
checked={rememberPassword}
|
||||||
helper={__('You will no longer see this at startup')}
|
|
||||||
/>
|
/>
|
||||||
</fieldset-section>
|
</fieldset-section>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import AccountPage from './view';
|
import AccountPage from './view';
|
||||||
import { selectYoutubeChannels } from 'lbryinc';
|
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({});
|
||||||
ytChannels: selectYoutubeChannels(state),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
select,
|
select,
|
||||||
|
|
|
@ -7,16 +7,16 @@ import UserEmail from 'component/userEmail';
|
||||||
import InviteNew from 'component/inviteNew';
|
import InviteNew from 'component/inviteNew';
|
||||||
import InviteList from 'component/inviteList';
|
import InviteList from 'component/inviteList';
|
||||||
|
|
||||||
const AccountPage = () => {
|
const AccountPage = (props: Props) => {
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<div className="columns">
|
<div className="columns section">
|
||||||
<div>
|
<div>
|
||||||
<RewardSummary />
|
|
||||||
<RewardTotal />
|
<RewardTotal />
|
||||||
|
<UserEmail />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<UserEmail />
|
<RewardSummary />
|
||||||
<InviteNew />
|
<InviteNew />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
19
src/ui/page/channels/index.js
Normal file
19
src/ui/page/channels/index.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { selectMyChannelClaims, doFetchChannelListMine, selectFetchingMyChannels } from 'lbry-redux';
|
||||||
|
import { selectYoutubeChannels } from 'lbryinc';
|
||||||
|
import ChannelsPage from './view';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
channels: selectMyChannelClaims(state),
|
||||||
|
fetchingChannels: selectFetchingMyChannels(state),
|
||||||
|
youtubeChannels: selectYoutubeChannels(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(ChannelsPage);
|
48
src/ui/page/channels/view.jsx
Normal file
48
src/ui/page/channels/view.jsx
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// @flow
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import ClaimList from 'component/claimList';
|
||||||
|
import Page from 'component/page';
|
||||||
|
import Button from 'component/button';
|
||||||
|
import YoutubeTransferStatus from 'component/youtubeTransferStatus';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
channels: Array<ChannelClaim>,
|
||||||
|
fetchChannelListMine: () => void,
|
||||||
|
fetchingChannels: boolean,
|
||||||
|
youtubeChannels: ?Array<any>,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ChannelsPage(props: Props) {
|
||||||
|
const { channels, fetchChannelListMine, fetchingChannels, youtubeChannels } = props;
|
||||||
|
const hasYoutubeChannels = youtubeChannels && Boolean(youtubeChannels.length);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchChannelListMine();
|
||||||
|
}, [fetchChannelListMine]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page>
|
||||||
|
{hasYoutubeChannels && <YoutubeTransferStatus />}
|
||||||
|
|
||||||
|
{channels && channels.length ? (
|
||||||
|
<div className="card">
|
||||||
|
<ClaimList
|
||||||
|
header={__('Your Channels On LBRY')}
|
||||||
|
loading={fetchingChannels}
|
||||||
|
uris={channels.map(channel => channel.permanent_url)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<section className="main--empty">
|
||||||
|
<div className=" section--small">
|
||||||
|
<h2 className="section__title--large">{__('No Channels Created Yet')}</h2>
|
||||||
|
|
||||||
|
<div className="section__actions">
|
||||||
|
<Button button="primary" navigate="/$/publish" label={__('Create A Channel')} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,8 +1,13 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import * as SETTINGS from 'constants/settings';
|
import * as SETTINGS from 'constants/settings';
|
||||||
import { doClearCache, doNotifyEncryptWallet, doNotifyDecryptWallet } from 'redux/actions/app';
|
import { doClearCache, doNotifyEncryptWallet, doNotifyDecryptWallet, doNotifyForgetPassword } from 'redux/actions/app';
|
||||||
import { doSetDaemonSetting, doSetClientSetting, doGetThemes, doSetDarkTime } from 'redux/actions/settings';
|
import {
|
||||||
import { selectIsPasswordSaved } from 'redux/selectors/app';
|
doSetDaemonSetting,
|
||||||
|
doSetClientSetting,
|
||||||
|
doGetThemes,
|
||||||
|
doChangeLanguage,
|
||||||
|
doSetDarkTime,
|
||||||
|
} from 'redux/actions/settings';
|
||||||
import { doSetPlayingUri } from 'redux/actions/content';
|
import { doSetPlayingUri } from 'redux/actions/content';
|
||||||
import { makeSelectClientSetting, selectDaemonSettings, selectosNotificationsEnabled } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectDaemonSettings, selectosNotificationsEnabled } from 'redux/selectors/settings';
|
||||||
import { doWalletStatus, selectWalletIsEncrypted, selectBlockedChannelsCount } from 'lbry-redux';
|
import { doWalletStatus, selectWalletIsEncrypted, selectBlockedChannelsCount } from 'lbry-redux';
|
||||||
|
|
|
@ -11,7 +11,6 @@ import I18nMessage from 'component/i18nMessage';
|
||||||
import Page from 'component/page';
|
import Page from 'component/page';
|
||||||
import SettingLanguage from 'component/settingLanguage';
|
import SettingLanguage from 'component/settingLanguage';
|
||||||
import FileSelector from 'component/common/file-selector';
|
import FileSelector from 'component/common/file-selector';
|
||||||
import WalletSecurityAndSync from '../../component/walletSecurityAndSync';
|
|
||||||
import { getSavedPassword } from 'util/saved-passwords';
|
import { getSavedPassword } from 'util/saved-passwords';
|
||||||
|
|
||||||
type Price = {
|
type Price = {
|
||||||
|
@ -62,7 +61,7 @@ type Props = {
|
||||||
supportOption: boolean,
|
supportOption: boolean,
|
||||||
userBlockedChannelsCount?: number,
|
userBlockedChannelsCount?: number,
|
||||||
hideBalance: boolean,
|
hideBalance: boolean,
|
||||||
confirmForgetPassword: () => void,
|
confirmForgetPassword: ({}) => void,
|
||||||
floatingPlayer: boolean,
|
floatingPlayer: boolean,
|
||||||
clearPlayingUri: () => void,
|
clearPlayingUri: () => void,
|
||||||
darkModeTimes: DarkModeTimes,
|
darkModeTimes: DarkModeTimes,
|
||||||
|
@ -150,7 +149,11 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
|
|
||||||
onConfirmForgetPassword() {
|
onConfirmForgetPassword() {
|
||||||
const { confirmForgetPassword } = this.props;
|
const { confirmForgetPassword } = this.props;
|
||||||
confirmForgetPassword();
|
confirmForgetPassword({
|
||||||
|
callback: () => {
|
||||||
|
this.setState({ storedPassword: false });
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeTime(event: SyntheticInputEvent<*>, options: OptionTimes) {
|
onChangeTime(event: SyntheticInputEvent<*>, options: OptionTimes) {
|
||||||
|
@ -245,7 +248,6 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
<p className="help">{__('LBRY downloads will be saved here.')}</p>
|
<p className="help">{__('LBRY downloads will be saved here.')}</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<WalletSecurityAndSync />
|
|
||||||
<section className="card card--section">
|
<section className="card card--section">
|
||||||
<h2 className="card__title">{__('Network and Data Settings')}</h2>
|
<h2 className="card__title">{__('Network and Data Settings')}</h2>
|
||||||
<Form>
|
<Form>
|
||||||
|
@ -277,9 +279,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
</Form>
|
</Form>
|
||||||
</section>
|
</section>
|
||||||
<section className="card card--section">
|
<section className="card card--section">
|
||||||
<header className="card__header">
|
<h2 className="card__title">{__('Max Purchase Price')}</h2>
|
||||||
<h2 className="card__title">{__('Max Purchase Price')}</h2>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<Form>
|
<Form>
|
||||||
<FormField
|
<FormField
|
||||||
|
@ -514,16 +514,18 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{this.state.storedPassword && (
|
|
||||||
<p className="card__subtitle card__help">
|
{walletEncrypted && this.state.storedPassword && (
|
||||||
{__('Your password is saved in your OS keychain.')}{' '}
|
<FormField
|
||||||
<Button
|
type="checkbox"
|
||||||
button="link"
|
name="save_password"
|
||||||
label={__('I want to type it manually')}
|
onChange={this.onConfirmForgetPassword}
|
||||||
onClick={this.onConfirmForgetPassword}
|
checked={this.state.storedPassword}
|
||||||
/>
|
label={__('Save Password')}
|
||||||
</p>
|
helper={<React.Fragment>{__('Automatically unlock your wallet on startup')}</React.Fragment>}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="hide_balance"
|
name="hide_balance"
|
||||||
|
|
|
@ -312,9 +312,9 @@ export function doNotifyUnlockWallet() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doNotifyForgetPassword() {
|
export function doNotifyForgetPassword(props) {
|
||||||
return dispatch => {
|
return dispatch => {
|
||||||
dispatch(doOpenModal(MODALS.WALLET_PASSWORD_UNSAVE));
|
dispatch(doOpenModal(MODALS.WALLET_PASSWORD_UNSAVE, props));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ const defaultState = {
|
||||||
// UX
|
// UX
|
||||||
[SETTINGS.NEW_USER_ACKNOWLEDGED]: false,
|
[SETTINGS.NEW_USER_ACKNOWLEDGED]: false,
|
||||||
[SETTINGS.EMAIL_COLLECTION_ACKNOWLEDGED]: false,
|
[SETTINGS.EMAIL_COLLECTION_ACKNOWLEDGED]: false,
|
||||||
|
[SETTINGS.ENABLE_SYNC]: true,
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
[SETTINGS.LANGUAGE]: window.localStorage.getItem(SETTINGS.LANGUAGE) || 'en',
|
[SETTINGS.LANGUAGE]: window.localStorage.getItem(SETTINGS.LANGUAGE) || 'en',
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
color: white !important;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,13 +61,27 @@
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card--claim-preview-wrap {
|
||||||
|
@extend .card;
|
||||||
|
margin: var(--spacing-xlarge) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card--claim-preview-selected {
|
||||||
|
background-color: rgba($lbry-teal-1, 0.1);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transition: transform 0.2s ease-in-out;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// C A R D
|
// C A R D
|
||||||
// A C T I O N S
|
// A C T I O N S
|
||||||
|
|
||||||
.card__actions {
|
.card__actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-top: var(--spacing-medium);
|
margin-top: var(--spacing-large);
|
||||||
font-size: var(--font-body);
|
font-size: var(--font-body);
|
||||||
|
|
||||||
&:only-child {
|
&:only-child {
|
||||||
|
|
|
@ -91,7 +91,6 @@ $border-color--dark: var(--dm-color-04);
|
||||||
padding: var(--spacing-medium);
|
padding: var(--spacing-medium);
|
||||||
|
|
||||||
&:not(.claim-preview--inline) {
|
&:not(.claim-preview--inline) {
|
||||||
@include mediaThumbHoverZoom;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,12 +101,6 @@ $border-color--dark: var(--dm-color-04);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.claim-preview--injected {
|
|
||||||
padding: var(--spacing-medium);
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.claim-preview--injected,
|
|
||||||
.claim-preview {
|
.claim-preview {
|
||||||
border-top: 1px solid $border-color;
|
border-top: 1px solid $border-color;
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ form {
|
||||||
.button--inverse {
|
.button--inverse {
|
||||||
&:not(:hover),
|
&:not(:hover),
|
||||||
&:hover {
|
&:hover {
|
||||||
// @extend .button--inverse;
|
@extend .button--inverse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.main--contained {
|
.main--contained {
|
||||||
max-width: 35rem;
|
max-width: 40rem;
|
||||||
min-width: 25rem;
|
min-width: 25rem;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
margin-top: 5rem;
|
margin-top: 5rem;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: var(--color-background);
|
background-color: var(--color-background--splash);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
|
@ -10,8 +10,16 @@
|
||||||
margin-bottom: var(--spacing-main-padding);
|
margin-bottom: var(--spacing-main-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.section--padded {
|
||||||
|
padding: var(--spacing-large);
|
||||||
|
}
|
||||||
|
|
||||||
.section--small {
|
.section--small {
|
||||||
max-width: 30rem;
|
max-width: 35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section__header {
|
||||||
|
margin-bottom: var(--spacing-large);
|
||||||
}
|
}
|
||||||
|
|
||||||
.section__flex {
|
.section__flex {
|
||||||
|
|
|
@ -293,3 +293,7 @@ radio-toggle,
|
||||||
border-color: var(--dm-color-04);
|
border-color: var(--dm-color-04);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rc-progress-line-path {
|
||||||
|
stroke: $lbry-teal-3;
|
||||||
|
}
|
||||||
|
|
|
@ -1,49 +1,47 @@
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
export const setSavedPassword = value => {
|
export const setSavedPassword = value => {
|
||||||
return new Promise(
|
return new Promise(resolve => {
|
||||||
resolve => {
|
ipcRenderer.once('set-password-response', (event, success) => {
|
||||||
ipcRenderer.once('set-password-response', (event, success) => {
|
resolve(success);
|
||||||
resolve(success);
|
});
|
||||||
});
|
ipcRenderer.send('set-password', value);
|
||||||
ipcRenderer.send('set-password', value);
|
});
|
||||||
},
|
|
||||||
reject => {
|
|
||||||
reject(false);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSavedPassword = () => {
|
export const getSavedPassword = () => {
|
||||||
return new Promise(
|
return new Promise(resolve => {
|
||||||
resolve => {
|
ipcRenderer.once('get-password-response', (event, password) => {
|
||||||
ipcRenderer.once('get-password-response', (event, password) => {
|
resolve(password);
|
||||||
resolve(password);
|
});
|
||||||
});
|
ipcRenderer.send('get-password');
|
||||||
ipcRenderer.send('get-password');
|
});
|
||||||
},
|
};
|
||||||
reject => reject(false)
|
|
||||||
);
|
export const deleteSavedPassword = () => {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
// @if TARGET='app'
|
||||||
|
ipcRenderer.once('delete-password-response', (event, success) => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
ipcRenderer.send('delete-password');
|
||||||
|
// @endif;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteAuthToken = () => {
|
export const deleteAuthToken = () => {
|
||||||
return new Promise(
|
return new Promise(resolve => {
|
||||||
resolve => {
|
// @if TARGET='app'
|
||||||
// @if TARGET='app'
|
ipcRenderer.once('delete-auth-token-response', (event, success) => {
|
||||||
ipcRenderer.once('delete-auth-token-response', (event, success) => {
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
ipcRenderer.send('delete-auth-token');
|
|
||||||
// @endif;
|
|
||||||
// @if TARGET='web'
|
|
||||||
document.cookie = 'auth_token= ; expires = Thu, 01 Jan 1970 00:00:00 GMT';
|
|
||||||
resolve();
|
resolve();
|
||||||
// @endif
|
});
|
||||||
},
|
ipcRenderer.send('delete-auth-token');
|
||||||
reject => {
|
// @endif;
|
||||||
reject(false);
|
// @if TARGET='web'
|
||||||
}
|
document.cookie = 'auth_token= ; expires = Thu, 01 Jan 1970 00:00:00 GMT';
|
||||||
);
|
resolve();
|
||||||
|
// @endif
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const testKeychain = () => {
|
export const testKeychain = () => {
|
||||||
|
|
|
@ -701,14 +701,8 @@
|
||||||
"Get ??? LBC": "Get ??? LBC",
|
"Get ??? LBC": "Get ??? LBC",
|
||||||
"LBRY names cannot contain spaces or reserved symbols ($#@;/\"<>%{}|^~[]`)": "LBRY names cannot contain spaces or reserved symbols ($#@;/\"<>%{}|^~[]`)",
|
"LBRY names cannot contain spaces or reserved symbols ($#@;/\"<>%{}|^~[]`)": "LBRY names cannot contain spaces or reserved symbols ($#@;/\"<>%{}|^~[]`)",
|
||||||
"Creating channel...": "Creating channel...",
|
"Creating channel...": "Creating channel...",
|
||||||
"From": "From",
|
"Remember Password": "Remember Password",
|
||||||
"To": "To",
|
"You will no longer see this at startup": "You will no longer see this at startup",
|
||||||
"Multi-language support is brand new and incomplete. Switching your language may have unintended consequences, like glossolalia.": "Multi-language support is brand new and incomplete. Switching your language may have unintended consequences, like glossolalia.",
|
|
||||||
"discovery": "discovery",
|
|
||||||
"This will add a Support button along side tipping. Similar to tips, supports help %discovery_link% but the LBC is returned to your wallet if revoked. Both also help secure your %vanity_names_link%.": "This will add a Support button along side tipping. Similar to tips, supports help %discovery_link% but the LBC is returned to your wallet if revoked. Both also help secure your %vanity_names_link%.",
|
|
||||||
"Tip %amount% LBC": "Tip %amount% LBC",
|
|
||||||
"Not enough credits": "Not enough credits",
|
|
||||||
"You have %credit_amount% in unclaimed rewards.": "You have %credit_amount% in unclaimed rewards.",
|
|
||||||
"URI does not include name.": "URI does not include name.",
|
"URI does not include name.": "URI does not include name.",
|
||||||
"to fix it. If that doesn't work, press CMD/CTRL-R to reset to the homepage.": "to fix it. If that doesn't work, press CMD/CTRL-R to reset to the homepage.",
|
"to fix it. If that doesn't work, press CMD/CTRL-R to reset to the homepage.": "to fix it. If that doesn't work, press CMD/CTRL-R to reset to the homepage.",
|
||||||
"Add Email": "Add Email",
|
"Add Email": "Add Email",
|
||||||
|
|
20
yarn.lock
20
yarn.lock
|
@ -6850,17 +6850,17 @@ lazy-val@^1.0.3, lazy-val@^1.0.4:
|
||||||
yargs "^13.2.2"
|
yargs "^13.2.2"
|
||||||
zstd-codec "^0.1.1"
|
zstd-codec "^0.1.1"
|
||||||
|
|
||||||
lbry-redux@lbryio/lbry-redux#04ae0913a444abac200731c7ed53796d763a0bbb:
|
lbry-redux@lbryio/lbry-redux#d44cd9ca56dee784dba42c0cc13061ae75cbd46c:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/04ae0913a444abac200731c7ed53796d763a0bbb"
|
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/d44cd9ca56dee784dba42c0cc13061ae75cbd46c"
|
||||||
dependencies:
|
dependencies:
|
||||||
proxy-polyfill "0.1.6"
|
proxy-polyfill "0.1.6"
|
||||||
reselect "^3.0.0"
|
reselect "^3.0.0"
|
||||||
uuid "^3.3.2"
|
uuid "^3.3.2"
|
||||||
|
|
||||||
lbryinc@lbryio/lbryinc#d99232ebc754a49649a2ff4132478415faef08e2:
|
lbryinc@lbryio/lbryinc#368040d64658cf2a4b8a7a6725ec1787329ce65d:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/d99232ebc754a49649a2ff4132478415faef08e2"
|
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/368040d64658cf2a4b8a7a6725ec1787329ce65d"
|
||||||
dependencies:
|
dependencies:
|
||||||
reselect "^3.0.0"
|
reselect "^3.0.0"
|
||||||
|
|
||||||
|
@ -9660,6 +9660,13 @@ react-compound-slider@^1.2.2:
|
||||||
prop-types "^15.7.2"
|
prop-types "^15.7.2"
|
||||||
warning "^3.0.0"
|
warning "^3.0.0"
|
||||||
|
|
||||||
|
react-confetti@^4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-confetti/-/react-confetti-4.0.1.tgz#f9e76b198ce02f1c13809a1d1ec1bc92f5450dde"
|
||||||
|
integrity sha512-uQrb1Q4p8Wg3xyxSGtsIxdd+hOd3jRNpVq5qET6m9B+fihsjF7mHbMngoiziya3DZtstaqCBPpTcyByXLu8CnQ==
|
||||||
|
dependencies:
|
||||||
|
tween-functions "^1.2.0"
|
||||||
|
|
||||||
react-dom@^16.8.2, react-dom@^16.8.6:
|
react-dom@^16.8.2, react-dom@^16.8.6:
|
||||||
version "16.8.6"
|
version "16.8.6"
|
||||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f"
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f"
|
||||||
|
@ -11696,6 +11703,11 @@ tunnel-agent@^0.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer "^5.0.1"
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
|
tween-functions@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tween-functions/-/tween-functions-1.2.0.tgz#1ae3a50e7c60bb3def774eac707acbca73bbc3ff"
|
||||||
|
integrity sha1-GuOlDnxguz3vd06scHrLynO7w/8=
|
||||||
|
|
||||||
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
|
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
|
||||||
version "0.14.5"
|
version "0.14.5"
|
||||||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
|
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
|
||||||
|
|
Loading…
Reference in a new issue