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",
|
||||
"json-loader": "^0.5.4",
|
||||
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
||||
"lbry-redux": "lbryio/lbry-redux#04ae0913a444abac200731c7ed53796d763a0bbb",
|
||||
"lbryinc": "lbryio/lbryinc#d99232ebc754a49649a2ff4132478415faef08e2",
|
||||
"lbry-redux": "lbryio/lbry-redux#d44cd9ca56dee784dba42c0cc13061ae75cbd46c",
|
||||
"lbryinc": "lbryio/lbryinc#368040d64658cf2a4b8a7a6725ec1787329ce65d",
|
||||
"lint-staged": "^7.0.2",
|
||||
"localforage": "^1.7.1",
|
||||
"lodash-es": "^4.17.14",
|
||||
|
@ -151,6 +151,7 @@
|
|||
"raw-loader": "^2.0.0",
|
||||
"rc-progress": "^2.0.6",
|
||||
"react": "^16.8.2",
|
||||
"react-confetti": "^4.0.1",
|
||||
"react-dom": "^16.8.2",
|
||||
"react-draggable": "^3.3.0",
|
||||
"react-ga": "^2.5.7",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as SETTINGS from 'constants/settings';
|
||||
import { hot } from 'react-hot-loader/root';
|
||||
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 { makeSelectClientSetting, selectThemePath } from 'redux/selectors/settings';
|
||||
import { selectIsUpgradeAvailable, selectAutoUpdateDownloaded } from 'redux/selectors/app';
|
||||
|
@ -12,7 +12,6 @@ const select = state => ({
|
|||
user: selectUser(state),
|
||||
theme: selectThemePath(state),
|
||||
language: makeSelectClientSetting(SETTINGS.LANGUAGE)(state),
|
||||
accessToken: selectAccessToken(state),
|
||||
autoUpdateDownloaded: selectAutoUpdateDownloaded(state),
|
||||
isUpgradeAvailable: selectIsUpgradeAvailable(state),
|
||||
});
|
||||
|
@ -22,6 +21,8 @@ const perform = dispatch => ({
|
|||
fetchRewardedContent: () => dispatch(doFetchRewardedContent()),
|
||||
fetchTransactions: () => dispatch(doFetchTransactions()),
|
||||
fetchAccessToken: () => dispatch(doFetchAccessToken()),
|
||||
fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
|
||||
onSignedIn: () => dispatch(doSignIn()),
|
||||
requestDownloadUpgrade: () => dispatch(doDownloadUpgradeRequested()),
|
||||
fetchChannelListMine: () => dispatch(doFetchChannelListMine()),
|
||||
onSignedIn: () => dispatch(doSignIn()),
|
||||
|
|
|
@ -12,6 +12,7 @@ import Yrbl from 'component/yrbl';
|
|||
import FileViewer from 'component/fileViewer';
|
||||
import { withRouter } from 'react-router';
|
||||
import usePrevious from 'util/use-previous';
|
||||
import SyncBackgroundManager from 'component/syncBackgroundManager';
|
||||
import Button from 'component/button';
|
||||
|
||||
export const MAIN_WRAPPER_CLASS = 'main-wrapper';
|
||||
|
@ -27,11 +28,13 @@ type Props = {
|
|||
fetchRewardedContent: () => void,
|
||||
fetchTransactions: () => void,
|
||||
fetchAccessToken: () => void,
|
||||
autoUpdateDownloaded: boolean,
|
||||
isUpgradeAvailable: boolean,
|
||||
fetchChannelListMine: () => void,
|
||||
onSignedIn: () => void,
|
||||
requestDownloadUpgrade: () => void,
|
||||
fetchChannelListMine: () => void,
|
||||
onSignedIn: () => void,
|
||||
isUpgradeAvailable: boolean,
|
||||
autoUpdateDownloaded: boolean,
|
||||
};
|
||||
|
||||
function App(props: Props) {
|
||||
|
@ -42,11 +45,11 @@ function App(props: Props) {
|
|||
fetchTransactions,
|
||||
user,
|
||||
fetchAccessToken,
|
||||
requestDownloadUpgrade,
|
||||
autoUpdateDownloaded,
|
||||
isUpgradeAvailable,
|
||||
fetchChannelListMine,
|
||||
onSignedIn,
|
||||
autoUpdateDownloaded,
|
||||
isUpgradeAvailable,
|
||||
requestDownloadUpgrade,
|
||||
} = props;
|
||||
const appRef = useRef();
|
||||
const isEnhancedLayout = useKonamiListener();
|
||||
|
@ -118,6 +121,7 @@ function App(props: Props) {
|
|||
<Router />
|
||||
<ModalRouter />
|
||||
<FileViewer pageUri={uri} />
|
||||
<SyncBackgroundManager />
|
||||
|
||||
{/* @if TARGET='app' */}
|
||||
{showUpgradeButton && (
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// @flow
|
||||
import type { Node } from 'react';
|
||||
import React, { Fragment, useEffect, forwardRef } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { parseURI, convertToShareLink } from 'lbry-redux';
|
||||
|
@ -45,6 +46,9 @@ type Props = {
|
|||
channelIsBlocked: boolean,
|
||||
isSubscribed: boolean,
|
||||
beginPublish: string => void,
|
||||
actions: boolean | Node | string | number,
|
||||
properties: boolean | Node | string | number,
|
||||
onClick?: any => any,
|
||||
};
|
||||
|
||||
const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
||||
|
@ -70,12 +74,14 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
channelIsBlocked,
|
||||
isSubscribed,
|
||||
beginPublish,
|
||||
actions,
|
||||
properties,
|
||||
onClick,
|
||||
} = props;
|
||||
const shouldFetch = claim === undefined || (claim !== null && claim.value_type === 'channel' && isEmpty(claim.meta));
|
||||
const abandoned = !isResolvingUri && !claim;
|
||||
const claimsInChannel = (claim && claim.meta.claims_in_channel) || 0;
|
||||
const showPublishLink = abandoned && placeholder === 'publish';
|
||||
const includeChannelTooltip = type !== 'inline' && type !== 'tooltip';
|
||||
const hideActions = type === 'small' || type === 'tooltip';
|
||||
|
||||
let name;
|
||||
|
@ -90,6 +96,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
}
|
||||
|
||||
const isChannel = isValid ? parseURI(uri).isChannel : false;
|
||||
const includeChannelTooltip = type !== 'inline' && type !== 'tooltip' && !isChannel;
|
||||
const signingChannel = claim && claim.signing_channel;
|
||||
let shouldHide =
|
||||
placeholder !== 'loading' && ((abandoned && !showPublishLink) || (!claimIsMine && obscureNsfw && nsfw));
|
||||
|
@ -129,8 +136,10 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
}
|
||||
}
|
||||
|
||||
function onClick(e) {
|
||||
if ((isChannel || title) && !pending) {
|
||||
function handleOnClick(e) {
|
||||
if (onClick) {
|
||||
onClick(e);
|
||||
} else if ((isChannel || title) && !pending) {
|
||||
history.push(formatLbryUriForWeb(uri));
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +170,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
<li
|
||||
ref={ref}
|
||||
role="link"
|
||||
onClick={pending || type === 'inline' ? undefined : onClick}
|
||||
onClick={pending || type === 'inline' ? undefined : handleOnClick}
|
||||
onContextMenu={handleContextMenu}
|
||||
className={classnames('claim-preview', {
|
||||
'claim-preview--small': type === 'small' || type === 'tooltip',
|
||||
|
@ -178,15 +187,19 @@ 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 && (
|
||||
<div className="card__actions--inline">
|
||||
{isChannel && !channelIsBlocked && (
|
||||
<SubscribeButton uri={uri.startsWith('lbry://') ? uri : `lbry://${uri}`} />
|
||||
{actions !== undefined
|
||||
? actions
|
||||
: !hideActions && (
|
||||
<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 className="claim-preview-properties">
|
||||
|
@ -219,7 +232,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
<ClaimTags uri={uri} type={type} />
|
||||
{properties !== undefined ? properties : <ClaimTags uri={uri} type={type} />}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
|
|
@ -104,9 +104,11 @@ export class FormField extends React.PureComponent<Props> {
|
|||
input = (
|
||||
<Wrapper>
|
||||
<checkbox-element {...elementProps}>
|
||||
<input id={name} type="checkbox" {...inputProps} />
|
||||
<label htmlFor={name}>{label}</label>
|
||||
<checkbox-toggle onClick={inputProps.onChange} />
|
||||
<input id={name} type="checkbox" {...inputProps} tabIndex={0} />
|
||||
<label htmlFor={name} tabIndex={-1}>
|
||||
{label}
|
||||
</label>
|
||||
<checkbox-toggle onClick={inputProps.onChange} tabIndex={-1} />
|
||||
</checkbox-element>
|
||||
</Wrapper>
|
||||
);
|
||||
|
|
|
@ -303,6 +303,13 @@ export const icons = {
|
|||
<line x1="15" y1="12" x2="3" y2="12" />
|
||||
</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(
|
||||
<g>
|
||||
<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 { selectUserVerifiedEmail } from 'lbryinc';
|
||||
import { doSetClientSetting } from 'redux/actions/settings';
|
||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
import { doSignOut } from 'redux/actions/app';
|
||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
import Header from './view';
|
||||
|
||||
const select = state => ({
|
||||
|
|
|
@ -13,11 +13,8 @@ import { Menu, MenuList, MenuButton, MenuItem } from '@reach/menu-button';
|
|||
import Tooltip from 'component/common/tooltip';
|
||||
|
||||
type Props = {
|
||||
autoUpdateDownloaded: boolean,
|
||||
balance: string,
|
||||
isUpgradeAvailable: boolean,
|
||||
roundedBalance: number,
|
||||
downloadUpgradeRequested: any => void,
|
||||
history: { push: string => void, goBack: () => void, goForward: () => void },
|
||||
currentTheme: string,
|
||||
automaticDarkModeEnabled: boolean,
|
||||
|
@ -137,7 +134,10 @@ const Header = (props: Props) => {
|
|||
{__('Sign Out')}
|
||||
</MenuItem>
|
||||
) : (
|
||||
<MenuItem onSelect={() => {}} />
|
||||
<MenuItem className="menu__link" onSelect={() => history.push(`/$/${PAGES.AUTH}`)}>
|
||||
<Icon aria-hidden icon={ICONS.SIGN_IN} />
|
||||
{__('Sign In')}
|
||||
</MenuItem>
|
||||
)}
|
||||
</MenuList>
|
||||
</Menu>
|
||||
|
|
|
@ -77,7 +77,7 @@ class InviteNew extends React.PureComponent<Props> {
|
|||
<Card
|
||||
title={__('Invite a Friend')}
|
||||
subtitle={__('When your friends start using LBRY, the network gets stronger!')}
|
||||
body={
|
||||
actions={
|
||||
<React.Fragment>
|
||||
<FormInviteNew
|
||||
errorMessage={errorMessage}
|
||||
|
|
|
@ -16,7 +16,7 @@ class RewardSummary extends React.Component<Props> {
|
|||
const hasRewards = unclaimedRewardAmount > 0;
|
||||
return (
|
||||
<Card
|
||||
title={__('Rewards')}
|
||||
title={__('Available Rewards')}
|
||||
subtitle={
|
||||
<React.Fragment>
|
||||
{fetching && __('You have...')}
|
||||
|
|
|
@ -24,6 +24,7 @@ import FollowingPage from 'page/following';
|
|||
import ListBlockedPage from 'page/listBlocked';
|
||||
import FourOhFourPage from 'page/fourOhFour';
|
||||
import SignInPage from 'page/signIn';
|
||||
import ChannelsPage from 'page/channels';
|
||||
|
||||
// Tell the browser we are handling scroll restoration
|
||||
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_SEND}`} exact component={WalletSendPage} />
|
||||
<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 */}
|
||||
<Route path="/:claimName" exact component={ShowPage} />
|
||||
|
|
|
@ -49,6 +49,9 @@ function SideBar(props: Props) {
|
|||
...buildLink(PAGES.LIBRARY, __('Library'), ICONS.LIBRARY),
|
||||
},
|
||||
// @endif
|
||||
{
|
||||
...buildLink(PAGES.CHANNELS, __('Channels'), ICONS.CHANNEL),
|
||||
},
|
||||
{
|
||||
...buildLink(PAGES.PUBLISHED, __('Publishes'), ICONS.PUBLISH),
|
||||
},
|
||||
|
|
|
@ -124,6 +124,11 @@ export default class SplashScreen extends React.PureComponent<Props, State> {
|
|||
clearTimeout(this.timeout);
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Try to unlock by default here
|
||||
//
|
||||
//
|
||||
// Make sure there isn't another active modal (like INCOMPATIBLE_DAEMON)
|
||||
if (launchedModal === false && !modal) {
|
||||
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={
|
||||
<React.Fragment>
|
||||
<section className="section">
|
||||
<TagsSearch
|
||||
onRemove={handleTagClick}
|
||||
onSelect={onSelect}
|
||||
suggestMature={suggestMature && !hasMatureTag}
|
||||
tagsPasssedIn={tagsToDisplay}
|
||||
/>
|
||||
{help !== false && (
|
||||
<p className="help">
|
||||
{help || __("The tags you follow will change what's trending for you.")}{' '}
|
||||
<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/trending" />.
|
||||
</p>
|
||||
)}
|
||||
</section>
|
||||
<TagsSearch
|
||||
onRemove={handleTagClick}
|
||||
onSelect={onSelect}
|
||||
suggestMature={suggestMature && !hasMatureTag}
|
||||
tagsPasssedIn={tagsToDisplay}
|
||||
/>
|
||||
{help !== false && (
|
||||
<p className="help">
|
||||
{help || __("The tags you follow will change what's trending for you.")}{' '}
|
||||
<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/trending" />.
|
||||
</p>
|
||||
)}
|
||||
</React.Fragment>
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -39,8 +39,10 @@ function UserEmail(props: Props) {
|
|||
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.'
|
||||
)}
|
||||
body={
|
||||
isVerified ? (
|
||||
actions={
|
||||
!isVerified ? (
|
||||
<Button button="primary" label={__('Add Email')} navigate={`/$/${PAGES.AUTH}`} />
|
||||
) : (
|
||||
<FormField
|
||||
type="text"
|
||||
className="form-field--copyable"
|
||||
|
@ -58,9 +60,8 @@ function UserEmail(props: Props) {
|
|||
inputButton={<UserSignOutButton button="inverse" />}
|
||||
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 { selectBalance } from 'lbry-redux';
|
||||
import { selectEmailNewIsPending, selectEmailNewErrorMessage, doUserEmailNew } from 'lbryinc';
|
||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
import { doSetClientSetting } from 'redux/actions/settings';
|
||||
import UserEmailNew from './view';
|
||||
|
||||
const select = state => ({
|
||||
isPending: selectEmailNewIsPending(state),
|
||||
errorMessage: selectEmailNewErrorMessage(state),
|
||||
syncEnabled: makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state),
|
||||
balance: selectBalance(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
addUserEmail: email => dispatch(doUserEmailNew(email)),
|
||||
setSync: value => dispatch(doSetClientSetting(SETTINGS.ENABLE_SYNC, value)),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
|
|
|
@ -4,19 +4,19 @@ import { FormField, Form } from 'component/common/form';
|
|||
import Button from 'component/button';
|
||||
import { Lbryio } from 'lbryinc';
|
||||
import analytics from 'analytics';
|
||||
import { EMAIL_REGEX } from 'constants/email';
|
||||
|
||||
type Props = {
|
||||
errorMessage: ?string,
|
||||
isPending: boolean,
|
||||
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) {
|
||||
const { errorMessage, isPending, addUserEmail } = props;
|
||||
const { errorMessage, isPending, addUserEmail, syncEnabled, setSync, balance } = props;
|
||||
const [newEmail, setEmail] = useState('');
|
||||
const valid = newEmail.match(EMAIL_REGEX);
|
||||
|
||||
|
@ -29,6 +29,12 @@ function UserEmailNew(props: Props) {
|
|||
// @endif
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (syncEnabled && balance) {
|
||||
setSync(false);
|
||||
}
|
||||
}, [balance, syncEnabled, setSync]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="section__title--large">{__('Welcome To LBRY')}</h1>
|
||||
|
@ -39,12 +45,23 @@ function UserEmailNew(props: Props) {
|
|||
className="form-field--short"
|
||||
placeholder={__('hotstuff_96@hotmail.com')}
|
||||
type="email"
|
||||
id="sign_up_email"
|
||||
name="sign_up_email"
|
||||
label={__('Email')}
|
||||
value={newEmail}
|
||||
error={errorMessage}
|
||||
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">
|
||||
<Button button="primary" type="submit" label={__('Continue')} disabled={!newEmail || !valid || isPending} />
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import * as SETTINGS from 'constants/settings';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
selectEmailToVerify,
|
||||
|
@ -7,12 +8,19 @@ import {
|
|||
selectClaimedRewards,
|
||||
rewards as REWARD_TYPES,
|
||||
doClaimRewardType,
|
||||
doUserFetch,
|
||||
selectUserIsPending,
|
||||
selectYoutubeChannels,
|
||||
selectGetSyncIsPending,
|
||||
selectGetSyncErrorMessage,
|
||||
selectSyncHash,
|
||||
} from 'lbryinc';
|
||||
import { selectMyChannelClaims, selectBalance, selectFetchingMyChannels } from 'lbry-redux';
|
||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
import UserSignIn from './view';
|
||||
|
||||
const select = state => ({
|
||||
email: selectEmailToVerify(state),
|
||||
emailToVerify: selectEmailToVerify(state),
|
||||
user: selectUser(state),
|
||||
accessToken: selectAccessToken(state),
|
||||
channels: selectMyChannelClaims(state),
|
||||
|
@ -22,9 +30,16 @@ const select = state => ({
|
|||
}),
|
||||
balance: selectBalance(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 => ({
|
||||
fetchUser: () => dispatch(doUserFetch()),
|
||||
claimReward: () =>
|
||||
dispatch(
|
||||
doClaimRewardType(REWARD_TYPES.TYPE_CONFIRM_EMAIL, {
|
||||
|
|
|
@ -4,24 +4,38 @@ import { withRouter } from 'react-router';
|
|||
import UserEmailNew from 'component/userEmailNew';
|
||||
import UserEmailVerify from 'component/userEmailVerify';
|
||||
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 { rewards as REWARDS } from 'lbryinc';
|
||||
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 = {
|
||||
user: ?User,
|
||||
email: ?string,
|
||||
fetchingChannels: boolean,
|
||||
emailToVerify: ?string,
|
||||
channels: ?Array<string>,
|
||||
balance: ?number,
|
||||
fetchingChannels: boolean,
|
||||
claimingReward: boolean,
|
||||
claimReward: () => void,
|
||||
fetchUser: () => void,
|
||||
claimedRewards: Array<Reward>,
|
||||
history: { replace: string => void },
|
||||
location: { search: string },
|
||||
youtubeChannels: Array<any>,
|
||||
syncIsPending: boolean,
|
||||
getSyncError: ?string,
|
||||
hasSyncedSuccessfully: boolean,
|
||||
};
|
||||
|
||||
function useFetched(fetching) {
|
||||
|
@ -38,64 +52,96 @@ function useFetched(fetching) {
|
|||
}
|
||||
|
||||
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 urlParams = new URLSearchParams(search);
|
||||
const redirect = urlParams.get('redirect');
|
||||
const hasFetchedReward = useFetched(claimingReward);
|
||||
const hasVerifiedEmail = user && user.has_verified_email;
|
||||
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 hasFetchedChannels = channels !== undefined;
|
||||
const hasClaimedEmailAward = claimedRewards.some(reward => reward.reward_type === REWARDS.TYPE_CONFIRM_EMAIL);
|
||||
const memoizedClaimReward = React.useCallback(() => {
|
||||
claimReward();
|
||||
}, [claimReward]);
|
||||
const hasYoutubeChannels = youtubeChannels && youtubeChannels.length;
|
||||
const hasTransferrableYoutubeChannels = hasYoutubeChannels && youtubeChannels.some(channel => channel.transferable);
|
||||
const hasPendingYoutubeTransfer =
|
||||
hasYoutubeChannels && youtubeChannels.some(channel => channel.transfer_state === 'pending_transfer');
|
||||
|
||||
React.useEffect(() => {
|
||||
if (hasVerifiedEmail && balance !== undefined && !hasClaimedEmailAward && !hasFetchedReward) {
|
||||
memoizedClaimReward();
|
||||
if (
|
||||
hasVerifiedEmail &&
|
||||
balance !== undefined &&
|
||||
!hasClaimedEmailAward &&
|
||||
!hasFetchedReward &&
|
||||
(!syncEnabled || (syncEnabled && syncHash))
|
||||
) {
|
||||
claimReward();
|
||||
}
|
||||
}, [hasVerifiedEmail, memoizedClaimReward, balance, hasClaimedEmailAward, hasFetchedReward]);
|
||||
}, [hasVerifiedEmail, claimReward, balance, hasClaimedEmailAward, hasFetchedReward, syncEnabled, syncHash]);
|
||||
|
||||
if (
|
||||
!user ||
|
||||
(balance === 0 && !hasFetchedReward) ||
|
||||
(hasVerifiedEmail && balance === undefined) ||
|
||||
(hasVerifiedEmail && !hasFetchedChannels)
|
||||
) {
|
||||
return (
|
||||
<div className="main--empty">
|
||||
<Spinner delayed />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
React.useEffect(() => {
|
||||
fetchUser();
|
||||
}, [fetchUser]);
|
||||
|
||||
if (balance === 0 && hasClaimedEmailAward) {
|
||||
history.replace(redirect || '/');
|
||||
}
|
||||
|
||||
if (rewardsApproved && channelCount > 0) {
|
||||
history.replace(redirect || '/');
|
||||
}
|
||||
|
||||
if (rewardsApproved && hasFetchedReward && balance && (balance === 0 || balance < DEFAULT_BID_FOR_FIRST_CHANNEL)) {
|
||||
history.replace(redirect || '/');
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
{hasVerifiedEmail && !rewardsApproved ? (
|
||||
<UserVerify />
|
||||
) : (
|
||||
<div className="main--contained">
|
||||
{!email && !hasVerifiedEmail && <UserEmailNew />}
|
||||
{email && !hasVerifiedEmail && <UserEmailVerify />}
|
||||
{hasVerifiedEmail && balance && balance > 0 && channelCount === 0 && <UserFirstChannel />}
|
||||
const SIGN_IN_FLOW = [
|
||||
!emailToVerify && !hasVerifiedEmail && <UserEmailNew />,
|
||||
emailToVerify && !hasVerifiedEmail && <UserEmailVerify />,
|
||||
hasVerifiedEmail && !rewardsApproved && <UserVerify />,
|
||||
getSyncError && !syncHash && <SyncPassword />,
|
||||
hasVerifiedEmail && balance > DEFAULT_BID_FOR_FIRST_CHANNEL && channelCount === 0 && !hasYoutubeChannels && (
|
||||
<UserFirstChannel />
|
||||
),
|
||||
hasVerifiedEmail && hasYoutubeChannels && (hasTransferrableYoutubeChannels || hasPendingYoutubeTransfer) && (
|
||||
<YoutubeTransferWelcome />
|
||||
),
|
||||
hasVerifiedEmail &&
|
||||
balance === 0 &&
|
||||
!getSyncError &&
|
||||
(fetchingChannels ||
|
||||
!hasFetchedReward ||
|
||||
claimingReward ||
|
||||
syncIsPending ||
|
||||
(syncEnabled && !syncHash) ||
|
||||
// Just claimed the email award, wait until the balance updates to move forward
|
||||
(balance === 0 && hasFetchedReward && hasClaimedEmailAward)) && (
|
||||
<div className="main--empty">
|
||||
<Spinner />
|
||||
</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);
|
||||
|
|
|
@ -97,7 +97,6 @@ class WalletSendTip extends React.PureComponent<Props, State> {
|
|||
inputButton={
|
||||
<Button
|
||||
button="primary"
|
||||
type="submit"
|
||||
label={__('Send')}
|
||||
disabled={isPending || tipError || !tipAmount}
|
||||
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,
|
||||
doUserFetch,
|
||||
} from 'lbryinc';
|
||||
|
||||
import YoutubeChannelList from './view';
|
||||
|
||||
const select = state => ({
|
||||
ytChannels: selectYoutubeChannels(state),
|
||||
ytImportPending: selectYTImportPending(state),
|
||||
youtubeChannels: selectYoutubeChannels(state),
|
||||
youtubeImportPending: selectYTImportPending(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_OFF = 'EyeOff';
|
||||
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_RECEIVE = 'wallet/receive';
|
||||
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 FLOATING_PLAYER = 'floating_player';
|
||||
export const DARK_MODE_TIMES = 'dark_mode_times';
|
||||
export const ENABLE_SYNC = 'enable_sync';
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { Modal } from 'modal/modal';
|
||||
import { deleteAuthToken } from 'util/saved-passwords';
|
||||
import { deleteSavedPassword } from 'util/saved-passwords';
|
||||
|
||||
type Props = {
|
||||
closeModal: () => void,
|
||||
callback?: () => void,
|
||||
};
|
||||
|
||||
class ModalPasswordUnsave extends React.PureComponent<Props> {
|
||||
|
@ -18,8 +19,11 @@ class ModalPasswordUnsave extends React.PureComponent<Props> {
|
|||
confirmButtonLabel={__('Forget')}
|
||||
abortButtonLabel={__('Nevermind')}
|
||||
onConfirmed={() =>
|
||||
deleteAuthToken().then(() => {
|
||||
deleteSavedPassword().then(() => {
|
||||
this.props.closeModal();
|
||||
if (this.props.callback) {
|
||||
this.props.callback();
|
||||
}
|
||||
})
|
||||
}
|
||||
onAborted={this.props.closeModal}
|
||||
|
|
|
@ -67,11 +67,11 @@ class ModalWalletUnlock extends React.PureComponent<Props, State> {
|
|||
onConfirmed={() => unlockWallet(password)}
|
||||
onAborted={quit}
|
||||
>
|
||||
<Form onSubmit={() => unlockWallet(password)}>
|
||||
<p>
|
||||
{__('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" />.
|
||||
</p>
|
||||
<p>
|
||||
{__('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" />.
|
||||
</p>
|
||||
<Form className="section" onSubmit={() => unlockWallet(password)}>
|
||||
<FormField
|
||||
autoFocus
|
||||
error={walletUnlockSucceded === false ? 'Incorrect Password' : false}
|
||||
|
@ -88,7 +88,6 @@ class ModalWalletUnlock extends React.PureComponent<Props, State> {
|
|||
name="wallet-remember-password"
|
||||
onChange={event => this.onChangeRememberPassword(event)}
|
||||
checked={rememberPassword}
|
||||
helper={__('You will no longer see this at startup')}
|
||||
/>
|
||||
</fieldset-section>
|
||||
</Form>
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import { connect } from 'react-redux';
|
||||
import AccountPage from './view';
|
||||
import { selectYoutubeChannels } from 'lbryinc';
|
||||
|
||||
const select = state => ({
|
||||
ytChannels: selectYoutubeChannels(state),
|
||||
});
|
||||
const select = state => ({});
|
||||
|
||||
export default connect(
|
||||
select,
|
||||
|
|
|
@ -7,16 +7,16 @@ import UserEmail from 'component/userEmail';
|
|||
import InviteNew from 'component/inviteNew';
|
||||
import InviteList from 'component/inviteList';
|
||||
|
||||
const AccountPage = () => {
|
||||
const AccountPage = (props: Props) => {
|
||||
return (
|
||||
<Page>
|
||||
<div className="columns">
|
||||
<div className="columns section">
|
||||
<div>
|
||||
<RewardSummary />
|
||||
<RewardTotal />
|
||||
<UserEmail />
|
||||
</div>
|
||||
<div>
|
||||
<UserEmail />
|
||||
<RewardSummary />
|
||||
<InviteNew />
|
||||
</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 * as SETTINGS from 'constants/settings';
|
||||
import { doClearCache, doNotifyEncryptWallet, doNotifyDecryptWallet } from 'redux/actions/app';
|
||||
import { doSetDaemonSetting, doSetClientSetting, doGetThemes, doSetDarkTime } from 'redux/actions/settings';
|
||||
import { selectIsPasswordSaved } from 'redux/selectors/app';
|
||||
import { doClearCache, doNotifyEncryptWallet, doNotifyDecryptWallet, doNotifyForgetPassword } from 'redux/actions/app';
|
||||
import {
|
||||
doSetDaemonSetting,
|
||||
doSetClientSetting,
|
||||
doGetThemes,
|
||||
doChangeLanguage,
|
||||
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';
|
||||
|
|
|
@ -11,7 +11,6 @@ import I18nMessage from 'component/i18nMessage';
|
|||
import Page from 'component/page';
|
||||
import SettingLanguage from 'component/settingLanguage';
|
||||
import FileSelector from 'component/common/file-selector';
|
||||
import WalletSecurityAndSync from '../../component/walletSecurityAndSync';
|
||||
import { getSavedPassword } from 'util/saved-passwords';
|
||||
|
||||
type Price = {
|
||||
|
@ -62,7 +61,7 @@ type Props = {
|
|||
supportOption: boolean,
|
||||
userBlockedChannelsCount?: number,
|
||||
hideBalance: boolean,
|
||||
confirmForgetPassword: () => void,
|
||||
confirmForgetPassword: ({}) => void,
|
||||
floatingPlayer: boolean,
|
||||
clearPlayingUri: () => void,
|
||||
darkModeTimes: DarkModeTimes,
|
||||
|
@ -150,7 +149,11 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
|||
|
||||
onConfirmForgetPassword() {
|
||||
const { confirmForgetPassword } = this.props;
|
||||
confirmForgetPassword();
|
||||
confirmForgetPassword({
|
||||
callback: () => {
|
||||
this.setState({ storedPassword: false });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
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>
|
||||
</div>
|
||||
</section>
|
||||
<WalletSecurityAndSync />
|
||||
<section className="card card--section">
|
||||
<h2 className="card__title">{__('Network and Data Settings')}</h2>
|
||||
<Form>
|
||||
|
@ -277,9 +279,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
|||
</Form>
|
||||
</section>
|
||||
<section className="card card--section">
|
||||
<header className="card__header">
|
||||
<h2 className="card__title">{__('Max Purchase Price')}</h2>
|
||||
</header>
|
||||
<h2 className="card__title">{__('Max Purchase Price')}</h2>
|
||||
|
||||
<Form>
|
||||
<FormField
|
||||
|
@ -514,16 +514,18 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
|||
</React.Fragment>
|
||||
}
|
||||
/>
|
||||
{this.state.storedPassword && (
|
||||
<p className="card__subtitle card__help">
|
||||
{__('Your password is saved in your OS keychain.')}{' '}
|
||||
<Button
|
||||
button="link"
|
||||
label={__('I want to type it manually')}
|
||||
onClick={this.onConfirmForgetPassword}
|
||||
/>
|
||||
</p>
|
||||
|
||||
{walletEncrypted && this.state.storedPassword && (
|
||||
<FormField
|
||||
type="checkbox"
|
||||
name="save_password"
|
||||
onChange={this.onConfirmForgetPassword}
|
||||
checked={this.state.storedPassword}
|
||||
label={__('Save Password')}
|
||||
helper={<React.Fragment>{__('Automatically unlock your wallet on startup')}</React.Fragment>}
|
||||
/>
|
||||
)}
|
||||
|
||||
<FormField
|
||||
type="checkbox"
|
||||
name="hide_balance"
|
||||
|
|
|
@ -312,9 +312,9 @@ export function doNotifyUnlockWallet() {
|
|||
};
|
||||
}
|
||||
|
||||
export function doNotifyForgetPassword() {
|
||||
export function doNotifyForgetPassword(props) {
|
||||
return dispatch => {
|
||||
dispatch(doOpenModal(MODALS.WALLET_PASSWORD_UNSAVE));
|
||||
dispatch(doOpenModal(MODALS.WALLET_PASSWORD_UNSAVE, props));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ const defaultState = {
|
|||
// UX
|
||||
[SETTINGS.NEW_USER_ACKNOWLEDGED]: false,
|
||||
[SETTINGS.EMAIL_COLLECTION_ACKNOWLEDGED]: false,
|
||||
[SETTINGS.ENABLE_SYNC]: true,
|
||||
|
||||
// UI
|
||||
[SETTINGS.LANGUAGE]: window.localStorage.getItem(SETTINGS.LANGUAGE) || 'en',
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -61,13 +61,27 @@
|
|||
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
|
||||
// A C T I O N S
|
||||
|
||||
.card__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: var(--spacing-medium);
|
||||
margin-top: var(--spacing-large);
|
||||
font-size: var(--font-body);
|
||||
|
||||
&:only-child {
|
||||
|
|
|
@ -91,7 +91,6 @@ $border-color--dark: var(--dm-color-04);
|
|||
padding: var(--spacing-medium);
|
||||
|
||||
&:not(.claim-preview--inline) {
|
||||
@include mediaThumbHoverZoom;
|
||||
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 {
|
||||
border-top: 1px solid $border-color;
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ form {
|
|||
.button--inverse {
|
||||
&:not(:hover),
|
||||
&:hover {
|
||||
// @extend .button--inverse;
|
||||
@extend .button--inverse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
}
|
||||
|
||||
.main--contained {
|
||||
max-width: 35rem;
|
||||
max-width: 40rem;
|
||||
min-width: 25rem;
|
||||
margin: auto;
|
||||
margin-top: 5rem;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
height: 100vh;
|
||||
|
||||
align-items: center;
|
||||
background-color: var(--color-background);
|
||||
background-color: var(--color-background--splash);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
|
|
@ -10,8 +10,16 @@
|
|||
margin-bottom: var(--spacing-main-padding);
|
||||
}
|
||||
|
||||
.section--padded {
|
||||
padding: var(--spacing-large);
|
||||
}
|
||||
|
||||
.section--small {
|
||||
max-width: 30rem;
|
||||
max-width: 35rem;
|
||||
}
|
||||
|
||||
.section__header {
|
||||
margin-bottom: var(--spacing-large);
|
||||
}
|
||||
|
||||
.section__flex {
|
||||
|
|
|
@ -293,3 +293,7 @@ radio-toggle,
|
|||
border-color: var(--dm-color-04);
|
||||
}
|
||||
}
|
||||
|
||||
.rc-progress-line-path {
|
||||
stroke: $lbry-teal-3;
|
||||
}
|
||||
|
|
|
@ -1,49 +1,47 @@
|
|||
import { ipcRenderer } from 'electron';
|
||||
|
||||
export const setSavedPassword = value => {
|
||||
return new Promise(
|
||||
resolve => {
|
||||
ipcRenderer.once('set-password-response', (event, success) => {
|
||||
resolve(success);
|
||||
});
|
||||
ipcRenderer.send('set-password', value);
|
||||
},
|
||||
reject => {
|
||||
reject(false);
|
||||
}
|
||||
);
|
||||
return new Promise(resolve => {
|
||||
ipcRenderer.once('set-password-response', (event, success) => {
|
||||
resolve(success);
|
||||
});
|
||||
ipcRenderer.send('set-password', value);
|
||||
});
|
||||
};
|
||||
|
||||
export const getSavedPassword = () => {
|
||||
return new Promise(
|
||||
resolve => {
|
||||
ipcRenderer.once('get-password-response', (event, password) => {
|
||||
resolve(password);
|
||||
});
|
||||
ipcRenderer.send('get-password');
|
||||
},
|
||||
reject => reject(false)
|
||||
);
|
||||
return new Promise(resolve => {
|
||||
ipcRenderer.once('get-password-response', (event, password) => {
|
||||
resolve(password);
|
||||
});
|
||||
ipcRenderer.send('get-password');
|
||||
});
|
||||
};
|
||||
|
||||
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 = () => {
|
||||
return new Promise(
|
||||
resolve => {
|
||||
// @if TARGET='app'
|
||||
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';
|
||||
return new Promise(resolve => {
|
||||
// @if TARGET='app'
|
||||
ipcRenderer.once('delete-auth-token-response', (event, success) => {
|
||||
resolve();
|
||||
// @endif
|
||||
},
|
||||
reject => {
|
||||
reject(false);
|
||||
}
|
||||
);
|
||||
});
|
||||
ipcRenderer.send('delete-auth-token');
|
||||
// @endif;
|
||||
// @if TARGET='web'
|
||||
document.cookie = 'auth_token= ; expires = Thu, 01 Jan 1970 00:00:00 GMT';
|
||||
resolve();
|
||||
// @endif
|
||||
});
|
||||
};
|
||||
|
||||
export const testKeychain = () => {
|
||||
|
|
|
@ -701,14 +701,8 @@
|
|||
"Get ??? LBC": "Get ??? LBC",
|
||||
"LBRY names cannot contain spaces or reserved symbols ($#@;/\"<>%{}|^~[]`)": "LBRY names cannot contain spaces or reserved symbols ($#@;/\"<>%{}|^~[]`)",
|
||||
"Creating channel...": "Creating channel...",
|
||||
"From": "From",
|
||||
"To": "To",
|
||||
"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.",
|
||||
"Remember Password": "Remember Password",
|
||||
"You will no longer see this at startup": "You will no longer see this at startup",
|
||||
"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.",
|
||||
"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"
|
||||
zstd-codec "^0.1.1"
|
||||
|
||||
lbry-redux@lbryio/lbry-redux#04ae0913a444abac200731c7ed53796d763a0bbb:
|
||||
lbry-redux@lbryio/lbry-redux#d44cd9ca56dee784dba42c0cc13061ae75cbd46c:
|
||||
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:
|
||||
proxy-polyfill "0.1.6"
|
||||
reselect "^3.0.0"
|
||||
uuid "^3.3.2"
|
||||
|
||||
lbryinc@lbryio/lbryinc#d99232ebc754a49649a2ff4132478415faef08e2:
|
||||
lbryinc@lbryio/lbryinc#368040d64658cf2a4b8a7a6725ec1787329ce65d:
|
||||
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:
|
||||
reselect "^3.0.0"
|
||||
|
||||
|
@ -9660,6 +9660,13 @@ react-compound-slider@^1.2.2:
|
|||
prop-types "^15.7.2"
|
||||
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:
|
||||
version "16.8.6"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f"
|
||||
|
@ -11696,6 +11703,11 @@ tunnel-agent@^0.6.0:
|
|||
dependencies:
|
||||
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:
|
||||
version "0.14.5"
|
||||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
|
||||
|
|
Loading…
Reference in a new issue