Merge pull request #2210 from lbryio/first-run
Less intrusive first run experience
This commit is contained in:
commit
8c0608bc74
31 changed files with 668 additions and 195 deletions
|
@ -67,6 +67,7 @@
|
|||
"react-feather": "^1.0.8",
|
||||
"react-modal": "^3.1.7",
|
||||
"react-paginate": "^5.2.1",
|
||||
"react-pose": "^4.0.5",
|
||||
"react-redux": "^5.0.3",
|
||||
"react-simplemde-editor": "^3.6.16",
|
||||
"react-toggle": "^4.0.2",
|
||||
|
|
|
@ -7,8 +7,10 @@ import {
|
|||
doError,
|
||||
} from 'lbry-redux';
|
||||
import { doRecordScroll } from 'redux/actions/navigation';
|
||||
import { doToggleEnhancedLayout } from 'redux/actions/app';
|
||||
import { selectUser } from 'lbryinc';
|
||||
import { selectThemePath } from 'redux/selectors/settings';
|
||||
import { selectEnhancedLayout } from 'redux/selectors/app';
|
||||
import App from './view';
|
||||
|
||||
const select = state => ({
|
||||
|
@ -17,12 +19,14 @@ const select = state => ({
|
|||
currentStackIndex: selectHistoryIndex(state),
|
||||
currentPageAttributes: selectActiveHistoryEntry(state),
|
||||
theme: selectThemePath(state),
|
||||
enhancedLayout: selectEnhancedLayout(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
alertError: errorList => dispatch(doError(errorList)),
|
||||
recordScroll: scrollPosition => dispatch(doRecordScroll(scrollPosition)),
|
||||
updateBlockHeight: () => dispatch(doUpdateBlockHeight()),
|
||||
toggleEnhancedLayout: () => dispatch(doToggleEnhancedLayout()),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
|
|
|
@ -6,7 +6,9 @@ import ReactModal from 'react-modal';
|
|||
import throttle from 'util/throttle';
|
||||
import SideBar from 'component/sideBar';
|
||||
import Header from 'component/header';
|
||||
import { openContextMenu } from '../../util/context-menu';
|
||||
import { openContextMenu } from 'util/context-menu';
|
||||
import EnhancedLayoutListener from 'util/enhanced-layout';
|
||||
import Native from 'native';
|
||||
|
||||
const TWO_POINT_FIVE_MINUTES = 1000 * 60 * 2.5;
|
||||
|
||||
|
@ -18,6 +20,8 @@ type Props = {
|
|||
pageTitle: ?string,
|
||||
theme: string,
|
||||
updateBlockHeight: () => void,
|
||||
toggleEnhancedLayout: () => void,
|
||||
enhancedLayout: boolean,
|
||||
};
|
||||
|
||||
class App extends React.PureComponent<Props> {
|
||||
|
@ -41,17 +45,18 @@ class App extends React.PureComponent<Props> {
|
|||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { updateBlockHeight } = this.props;
|
||||
const { updateBlockHeight, toggleEnhancedLayout } = this.props;
|
||||
|
||||
const mainContent = document.getElementById('content');
|
||||
this.mainContent = mainContent;
|
||||
|
||||
if (this.mainContent) {
|
||||
this.mainContent.addEventListener('scroll', throttle(this.scrollListener, 750));
|
||||
}
|
||||
|
||||
ReactModal.setAppElement('#window'); // fuck this
|
||||
|
||||
this.enhance = new EnhancedLayoutListener(() => toggleEnhancedLayout());
|
||||
|
||||
updateBlockHeight();
|
||||
setInterval(() => {
|
||||
updateBlockHeight();
|
||||
|
@ -81,6 +86,8 @@ class App extends React.PureComponent<Props> {
|
|||
if (this.mainContent) {
|
||||
this.mainContent.removeEventListener('scroll', this.scrollListener);
|
||||
}
|
||||
|
||||
this.enhance = null;
|
||||
}
|
||||
|
||||
setTitleFromProps = (title: ?string) => {
|
||||
|
@ -96,12 +103,22 @@ class App extends React.PureComponent<Props> {
|
|||
}
|
||||
|
||||
mainContent: ?HTMLElement;
|
||||
enhance: ?any;
|
||||
|
||||
render() {
|
||||
const { enhancedLayout } = this.props;
|
||||
|
||||
return (
|
||||
<div id="window" onContextMenu={e => openContextMenu(e)}>
|
||||
<Header />
|
||||
<main className="page">
|
||||
{enhancedLayout && (
|
||||
<img
|
||||
alt="Friendly gerbil"
|
||||
className="yrbl--enhanced"
|
||||
src={Native.imagePath('gerbil-happy.png')}
|
||||
/>
|
||||
)}
|
||||
<SideBar />
|
||||
<div className="content" id="content">
|
||||
<Router />
|
||||
|
|
29
src/renderer/component/emailCollection/index.js
Normal file
29
src/renderer/component/emailCollection/index.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
import * as SETTINGS from 'constants/settings';
|
||||
import { connect } from 'react-redux';
|
||||
import { doSetClientSetting } from 'redux/actions/settings';
|
||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
import { selectEmailToVerify, selectUser } from 'lbryinc';
|
||||
import FirstRunEmailCollection from './view';
|
||||
|
||||
const select = state => ({
|
||||
emailCollectionAcknowledged: makeSelectClientSetting(SETTINGS.EMAIL_COLLECTION_ACKNOWLEDGED)(
|
||||
state
|
||||
),
|
||||
email: selectEmailToVerify(state),
|
||||
user: selectUser(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => () => ({
|
||||
completeFirstRun: () => {
|
||||
dispatch(doSetClientSetting(SETTINGS.EMAIL_COLLECTION_ACKNOWLEDGED, true));
|
||||
dispatch(doSetClientSetting(SETTINGS.FIRST_RUN_COMPLETED, true));
|
||||
},
|
||||
acknowledgeEmail: () => {
|
||||
dispatch(doSetClientSetting(SETTINGS.EMAIL_COLLECTION_ACKNOWLEDGED, true));
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(
|
||||
select,
|
||||
perform
|
||||
)(FirstRunEmailCollection);
|
46
src/renderer/component/emailCollection/view.jsx
Normal file
46
src/renderer/component/emailCollection/view.jsx
Normal file
|
@ -0,0 +1,46 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import Button from 'component/button';
|
||||
import UserEmailNew from 'component/userEmailNew';
|
||||
import UserEmailVerify from 'component/userEmailVerify';
|
||||
|
||||
type Props = {
|
||||
email: string,
|
||||
emailCollectionAcknowledged: boolean,
|
||||
user: ?{ has_verified_email: boolean },
|
||||
completeFirstRun: () => void,
|
||||
acknowledgeEmail: () => void,
|
||||
};
|
||||
|
||||
class FirstRunEmailCollection extends React.PureComponent<Props> {
|
||||
render() {
|
||||
const {
|
||||
completeFirstRun,
|
||||
email,
|
||||
user,
|
||||
emailCollectionAcknowledged,
|
||||
acknowledgeEmail,
|
||||
} = this.props;
|
||||
|
||||
// this shouldn't happen
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const cancelButton = <Button button="link" onClick={completeFirstRun} label={__('Not Now')} />;
|
||||
if (user && !user.has_verified_email && !email) {
|
||||
return <UserEmailNew cancelButton={cancelButton} />;
|
||||
} else if (user && !user.has_verified_email) {
|
||||
return <UserEmailVerify cancelButton={cancelButton} />;
|
||||
}
|
||||
|
||||
// Try to acknowledge here so users don't see an empty email screen in the first run banner
|
||||
if (!emailCollectionAcknowledged) {
|
||||
acknowledgeEmail();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default FirstRunEmailCollection;
|
29
src/renderer/component/firstRun/index.js
Normal file
29
src/renderer/component/firstRun/index.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
import * as SETTINGS from 'constants/settings';
|
||||
import { connect } from 'react-redux';
|
||||
import { selectUser } from 'lbryinc';
|
||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
import { doSetClientSetting } from 'redux/actions/settings';
|
||||
import FirstRun from './view';
|
||||
|
||||
const select = state => ({
|
||||
emailCollectionAcknowledged: makeSelectClientSetting(SETTINGS.EMAIL_COLLECTION_ACKNOWLEDGED)(
|
||||
state
|
||||
),
|
||||
welcomeAcknowledged: makeSelectClientSetting(SETTINGS.NEW_USER_ACKNOWLEDGED)(state),
|
||||
firstRunComplete: makeSelectClientSetting(SETTINGS.FIRST_RUN_COMPLETED)(state),
|
||||
user: selectUser(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
acknowledgeWelcome: () => {
|
||||
dispatch(doSetClientSetting(SETTINGS.NEW_USER_ACKNOWLEDGED, true));
|
||||
},
|
||||
completeFirstRun: () => {
|
||||
dispatch(doSetClientSetting(SETTINGS.FIRST_RUN_COMPLETED, true));
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(
|
||||
select,
|
||||
perform
|
||||
)(FirstRun);
|
115
src/renderer/component/firstRun/view.jsx
Normal file
115
src/renderer/component/firstRun/view.jsx
Normal file
|
@ -0,0 +1,115 @@
|
|||
// @flow
|
||||
import React, { PureComponent } from 'react';
|
||||
import posed from 'react-pose';
|
||||
import Button from 'component/button';
|
||||
import EmailCollection from 'component/emailCollection';
|
||||
import Native from 'native';
|
||||
|
||||
//
|
||||
// Animation for items inside banner
|
||||
// The height for items must be static (in banner.scss) so that we can reliably animate into the banner and be vertically centered
|
||||
//
|
||||
const spring = {
|
||||
type: 'spring',
|
||||
stiffness: 100,
|
||||
damping: 10,
|
||||
mass: 1,
|
||||
};
|
||||
|
||||
const Welcome = posed.div({
|
||||
hide: { opacity: 0, y: '310px', ...spring },
|
||||
show: { opacity: 1, ...spring },
|
||||
});
|
||||
|
||||
const Email = posed.div({
|
||||
hide: { opacity: 0, y: '0', ...spring },
|
||||
show: { opacity: 1, y: '-310px', ...spring, delay: 175 },
|
||||
});
|
||||
|
||||
const Help = posed.div({
|
||||
hide: { opacity: 0, y: '0', ...spring },
|
||||
show: { opacity: 1, y: '-620px', ...spring, delay: 175 },
|
||||
});
|
||||
|
||||
type Props = {
|
||||
welcomeAcknowledged: boolean,
|
||||
emailCollectionAcknowledged: boolean,
|
||||
firstRunComplete: boolean,
|
||||
acknowledgeWelcome: () => void,
|
||||
completeFirstRun: () => void,
|
||||
};
|
||||
|
||||
export default class FirstRun extends PureComponent<Props> {
|
||||
render() {
|
||||
const {
|
||||
welcomeAcknowledged,
|
||||
emailCollectionAcknowledged,
|
||||
firstRunComplete,
|
||||
acknowledgeWelcome,
|
||||
completeFirstRun,
|
||||
} = this.props;
|
||||
|
||||
if (firstRunComplete) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const showWelcome = !welcomeAcknowledged;
|
||||
const showEmail = !emailCollectionAcknowledged && welcomeAcknowledged;
|
||||
const showHelp = !showWelcome && !showEmail;
|
||||
|
||||
return (
|
||||
<div className="banner banner--first-run">
|
||||
<img
|
||||
alt="Friendly gerbil"
|
||||
className="yrbl--first-run banner__item"
|
||||
src={Native.imagePath('gerbil-happy.png')}
|
||||
/>
|
||||
|
||||
<div className="banner__item">
|
||||
<div className="banner__item--static-for-animation">
|
||||
<Welcome className="banner__content" pose={showWelcome ? 'show' : 'hide'}>
|
||||
<div>
|
||||
<header className="card__header">
|
||||
<h1 className="card__title">{__('Hi There')}</h1>
|
||||
</header>
|
||||
<div className="card__content">
|
||||
<p>
|
||||
{__('Using LBRY is like dating a centaur. Totally normal up top, and')}{' '}
|
||||
<em>{__('way different')}</em> {__('underneath.')}
|
||||
</p>
|
||||
<p>{__('Up top, LBRY is similar to popular media sites.')}</p>
|
||||
<p>
|
||||
{__(
|
||||
'Below, LBRY is controlled by users -- you -- via blockchain and decentralization.'
|
||||
)}
|
||||
</p>
|
||||
<div className="card__actions">
|
||||
<Button button="primary" onClick={acknowledgeWelcome} label={__("I'm In")} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Welcome>
|
||||
</div>
|
||||
<div className="banner__item--static-for-animation">
|
||||
<Email pose={showEmail ? 'show' : 'hide'}>
|
||||
<EmailCollection />
|
||||
</Email>
|
||||
</div>
|
||||
<div className="banner__item--static-for-animation">
|
||||
<Help pose={showHelp ? 'show' : 'hide'}>
|
||||
<header className="card__header">
|
||||
<h1 className="card__title">{__('You Are Awesome!')}</h1>
|
||||
</header>
|
||||
<div className="card__content">
|
||||
<p>{__("Check out some of the neat files below me. I'll see you around!")}</p>
|
||||
<div className="card__actions">
|
||||
<Button button="primary" onClick={completeFirstRun} label={__('See You Later')} />
|
||||
</div>
|
||||
</div>
|
||||
</Help>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -61,7 +61,7 @@ class SocialShare extends React.PureComponent<Props> {
|
|||
<div className="card__content">
|
||||
<label className="card__subtitle">{__('Web link')}</label>
|
||||
<CopyableText copyable={speechURL} />
|
||||
<div className="card__actions card__actions--center card__actions--top-space">
|
||||
<div className="card__actions card__actions--center">
|
||||
<ToolTip onComponent body={__('Facebook')}>
|
||||
<Button
|
||||
iconColor="blue"
|
||||
|
@ -95,7 +95,7 @@ class SocialShare extends React.PureComponent<Props> {
|
|||
<div className="card__content">
|
||||
<label className="card__subtitle">{__('LBRY App link')}</label>
|
||||
<CopyableText copyable={lbryURL} noSnackbar />
|
||||
<div className="card__actions card__actions--center card__actions--top-space">
|
||||
<div className="card__actions card__actions--center">
|
||||
<ToolTip onComponent body={__('Facebook')}>
|
||||
<Button
|
||||
iconColor="blue"
|
||||
|
|
|
@ -42,16 +42,14 @@ class UserEmailNew extends React.PureComponent<Props, State> {
|
|||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<p>
|
||||
{__("We'll let you know about LBRY updates, security issues, and great new content.")}
|
||||
</p>
|
||||
<header className="card__header">
|
||||
<h2 className="card__title">{__("Don't Miss Out")}</h2>
|
||||
<p className="card__subtitle">
|
||||
{__("We'll let you know about LBRY updates, security issues, and great new content.")}
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<p>
|
||||
{__(
|
||||
'In addition, your email address will never be sold and you can unsubscribe at any time.'
|
||||
)}
|
||||
</p>
|
||||
<Form onSubmit={this.handleSubmit}>
|
||||
<Form className="card__content" onSubmit={this.handleSubmit}>
|
||||
<FormRow>
|
||||
<FormField
|
||||
stretch
|
||||
|
@ -66,10 +64,13 @@ class UserEmailNew extends React.PureComponent<Props, State> {
|
|||
</FormRow>
|
||||
|
||||
<div className="card__actions">
|
||||
<Submit label="Submit" disabled={isPending} />
|
||||
<Submit label="Submit" disabled={isPending || !this.state.email} />
|
||||
{cancelButton}
|
||||
</div>
|
||||
</Form>
|
||||
<p className="help">
|
||||
{__('Your email address will never be sold and you can unsubscribe at any time.')}
|
||||
</p>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -50,27 +50,33 @@ class UserEmailVerify extends React.PureComponent<Props> {
|
|||
const { cancelButton, email } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
{__('An email was sent to')} {email}.{' '}
|
||||
{__('Follow the link and you will be good to go. This will update automatically.')}
|
||||
</p>
|
||||
<React.Fragment>
|
||||
<header className="card__header">
|
||||
<h2 className="card__title">{__('Waiting For Verification')}</h2>
|
||||
</header>
|
||||
|
||||
<div className="card__actions">
|
||||
<Button
|
||||
button="primary"
|
||||
label={__('Resend verification email')}
|
||||
onClick={this.handleResendVerificationEmail}
|
||||
/>
|
||||
{cancelButton}
|
||||
<div className="card__content">
|
||||
<p>
|
||||
{__('An email was sent to')} {email}.{' '}
|
||||
{__('Follow the link and you will be good to go. This will update automatically.')}
|
||||
</p>
|
||||
|
||||
<div className="card__actions">
|
||||
<Button
|
||||
button="primary"
|
||||
label={__('Resend verification email')}
|
||||
onClick={this.handleResendVerificationEmail}
|
||||
/>
|
||||
{cancelButton}
|
||||
</div>
|
||||
|
||||
<p className="help">
|
||||
{__('Email')} <Button button="link" href="mailto:help@lbry.io" label="help@lbry.io" />{' '}
|
||||
or join our <Button button="link" href="https://chat.lbry.io" label="chat" />{' '}
|
||||
{__('if you encounter any trouble verifying.')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p className="help">
|
||||
{__('Email')} <Button button="link" href="mailto:help@lbry.io" label="help@lbry.io" /> or
|
||||
join our <Button button="link" href="https://chat.lbry.io" label="chat" />{' '}
|
||||
{__('if you encounter any trouble verifying.')}
|
||||
</p>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ export const VOLUME_CHANGED = 'VOLUME_CHANGED';
|
|||
export const ADD_COMMENT = 'ADD_COMMENT';
|
||||
export const SHOW_MODAL = 'SHOW_MODAL';
|
||||
export const HIDE_MODAL = 'HIDE_MODAL';
|
||||
export const ENNNHHHAAANNNCEEE = 'ENNNHHHAAANNNCEEE';
|
||||
|
||||
// Navigation
|
||||
export const CHANGE_AFTER_AUTH_PATH = 'CHANGE_AFTER_AUTH_PATH';
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
/* hardcoded names still exist for these in reducers/settings.js - only discovered when debugging */
|
||||
/* Many SETTINGS are stored in the localStorage by their name -
|
||||
be careful about changing the value of a SETTINGS constant, as doing so can invalidate existing SETTINGS */
|
||||
export const CREDIT_REQUIRED_ACKNOWLEDGED = 'credit_required_acknowledged';
|
||||
export const NEW_USER_ACKNOWLEDGED = 'welcome_acknowledged';
|
||||
export const EMAIL_COLLECTION_ACKNOWLEDGED = 'email_collection_acknowledged';
|
||||
export const FIRST_RUN_COMPLETED = 'first_run_completed';
|
||||
export const LANGUAGE = 'language';
|
||||
export const SHOW_NSFW = 'showNsfw';
|
||||
export const SHOW_UNAVAILABLE = 'showUnavailable';
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
import * as settings from 'constants/settings';
|
||||
import { connect } from 'react-redux';
|
||||
import { doHideModal } from 'redux/actions/app';
|
||||
import { doSetClientSetting } from 'redux/actions/settings';
|
||||
import { selectEmailToVerify, selectUser } from 'lbryinc';
|
||||
import ModalEmailCollection from './view';
|
||||
|
||||
const select = state => ({
|
||||
email: selectEmailToVerify(state),
|
||||
user: selectUser(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => () => ({
|
||||
closeModal: () => {
|
||||
dispatch(doSetClientSetting(settings.EMAIL_COLLECTION_ACKNOWLEDGED, true));
|
||||
dispatch(doHideModal());
|
||||
},
|
||||
});
|
||||
|
||||
export default connect(
|
||||
select,
|
||||
perform
|
||||
)(ModalEmailCollection);
|
|
@ -1,54 +0,0 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { Modal } from 'modal/modal';
|
||||
import Button from 'component/button';
|
||||
import UserEmailNew from 'component/userEmailNew';
|
||||
import UserEmailVerify from 'component/userEmailVerify';
|
||||
|
||||
type Props = {
|
||||
closeModal: () => void,
|
||||
email: string,
|
||||
user: ?{ has_verified_email: boolean },
|
||||
};
|
||||
|
||||
class ModalEmailCollection extends React.PureComponent<Props> {
|
||||
getTitle() {
|
||||
// const { user } = this.props;
|
||||
//
|
||||
// if (user && user.email && !user.has_verified_email) {
|
||||
// return __('Awaiting Confirmation');
|
||||
// }
|
||||
|
||||
return __('Can We Stay In Touch?');
|
||||
}
|
||||
|
||||
renderInner() {
|
||||
const { closeModal, email, user } = this.props;
|
||||
|
||||
const cancelButton = <Button button="link" onClick={closeModal} label={__('Not Now')} />;
|
||||
if (user && !user.has_verified_email && !email) {
|
||||
return <UserEmailNew cancelButton={cancelButton} />;
|
||||
} else if (user && !user.has_verified_email) {
|
||||
return <UserEmailVerify onModal cancelButton={cancelButton} />;
|
||||
}
|
||||
|
||||
return closeModal();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { user } = this.props;
|
||||
|
||||
// this shouldn't happen
|
||||
if (!user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal type="custom" isOpen contentLabel="Email" title={this.getTitle()}>
|
||||
<section className="card__content">{this.renderInner()}</section>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ModalEmailCollection;
|
|
@ -16,7 +16,6 @@ import ModalTransactionFailed from 'modal/modalTransactionFailed';
|
|||
import ModalFileTimeout from 'modal/modalFileTimeout';
|
||||
import ModalAffirmPurchase from 'modal/modalAffirmPurchase';
|
||||
import ModalRevokeClaim from 'modal/modalRevokeClaim';
|
||||
import ModalEmailCollection from 'modal/modalEmailCollection';
|
||||
import ModalPhoneCollection from 'modal/modalPhoneCollection';
|
||||
import ModalFirstSubscription from 'modal/modalFirstSubscription';
|
||||
import ModalConfirmTransaction from 'modal/modalConfirmTransaction';
|
||||
|
@ -78,11 +77,10 @@ class ModalRouter extends React.PureComponent<Props, State> {
|
|||
return;
|
||||
}
|
||||
|
||||
const transitionModal = [
|
||||
this.checkShowWelcome,
|
||||
this.checkShowEmail,
|
||||
this.checkShowCreditIntro,
|
||||
].reduce((acc, func) => (!acc ? func.bind(this)(props) : acc), false);
|
||||
const transitionModal = [this.checkShowCreditIntro].reduce(
|
||||
(acc, func) => (!acc ? func.bind(this)(props) : acc),
|
||||
false
|
||||
);
|
||||
|
||||
if (
|
||||
transitionModal &&
|
||||
|
@ -96,30 +94,6 @@ class ModalRouter extends React.PureComponent<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
checkShowWelcome(props: Props) {
|
||||
const { isWelcomeAcknowledged, user } = props;
|
||||
|
||||
if (!isWelcomeAcknowledged && user && !user.is_reward_approved && !user.is_identity_verified) {
|
||||
return MODALS.WELCOME;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
checkShowEmail(props: Props) {
|
||||
const { isEmailCollectionAcknowledged, isVerificationCandidate, user } = props;
|
||||
if (
|
||||
!isEmailCollectionAcknowledged &&
|
||||
isVerificationCandidate &&
|
||||
user &&
|
||||
!user.has_verified_email
|
||||
) {
|
||||
return MODALS.EMAIL_COLLECTION;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
checkShowCreditIntro(props: Props) {
|
||||
const { balance, page, isCreditIntroAcknowledged } = props;
|
||||
|
||||
|
@ -185,8 +159,6 @@ class ModalRouter extends React.PureComponent<Props, State> {
|
|||
return <ModalRevokeClaim {...modalProps} />;
|
||||
case MODALS.PHONE_COLLECTION:
|
||||
return <ModalPhoneCollection {...modalProps} />;
|
||||
case MODALS.EMAIL_COLLECTION:
|
||||
return <ModalEmailCollection {...modalProps} />;
|
||||
case MODALS.FIRST_SUBSCRIPTION:
|
||||
return <ModalFirstSubscription {...modalProps} />;
|
||||
case MODALS.SEND_TIP:
|
||||
|
|
|
@ -30,19 +30,6 @@ class AuthPage extends React.PureComponent<Props> {
|
|||
this.navigateIfAuthenticated(nextProps);
|
||||
}
|
||||
|
||||
getTitle() {
|
||||
const { email, isPending, user } = this.props;
|
||||
|
||||
if (isPending || (user && !user.has_verified_email && !email)) {
|
||||
return __('Human Proofing');
|
||||
} else if (user && !user.has_verified_email) {
|
||||
return __('Awaiting Confirmation');
|
||||
} else if (user && !user.is_identity_verified && !user.is_reward_approved) {
|
||||
return __('Final Verification');
|
||||
}
|
||||
return __('Welcome to LBRY');
|
||||
}
|
||||
|
||||
navigateIfAuthenticated = (props: Props) => {
|
||||
const { isPending, user, pathAfterAuth, navigate } = props;
|
||||
if (
|
||||
|
@ -78,24 +65,18 @@ class AuthPage extends React.PureComponent<Props> {
|
|||
<Page>
|
||||
{useTemplate ? (
|
||||
<section className="card card--section">
|
||||
<header className="card__header card__header--flat">
|
||||
<h2 className="card__title">{this.getTitle()}</h2>
|
||||
</header>
|
||||
{innerContent}
|
||||
|
||||
<div className="card__content">
|
||||
{innerContent}
|
||||
|
||||
<p className="help">
|
||||
{`${__(
|
||||
'This information is disclosed only to LBRY, Inc. and not to the LBRY network. It is only required to earn LBRY rewards and may be used to sync usage data across devices.'
|
||||
)} `}
|
||||
<Button
|
||||
button="link"
|
||||
onClick={() => navigate('/discover')}
|
||||
label={__('Return home.')}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<p className="help">
|
||||
{`${__(
|
||||
'This information is disclosed only to LBRY, Inc. and not to the LBRY network. It is only required to earn LBRY rewards and may be used to sync usage data across devices.'
|
||||
)} `}
|
||||
<Button
|
||||
button="link"
|
||||
onClick={() => navigate('/discover')}
|
||||
label={__('Return home.')}
|
||||
/>
|
||||
</p>
|
||||
</section>
|
||||
) : (
|
||||
innerContent
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
import React from 'react';
|
||||
import Page from 'component/page';
|
||||
import CategoryList from 'component/categoryList';
|
||||
import FirstRun from 'component/firstRun';
|
||||
|
||||
type Props = {
|
||||
fetchFeaturedUris: () => void,
|
||||
fetchRewardedContent: () => void,
|
||||
fetchRewards: () => void,
|
||||
fetchingFeaturedUris: boolean,
|
||||
featuredUris: {},
|
||||
};
|
||||
|
@ -59,6 +61,7 @@ class DiscoverPage extends React.PureComponent<Props> {
|
|||
const failedToLoad = !fetchingFeaturedUris && !hasContent;
|
||||
return (
|
||||
<Page noPadding isLoading={!hasContent && fetchingFeaturedUris}>
|
||||
<FirstRun />
|
||||
{hasContent &&
|
||||
Object.keys(featuredUris).map(
|
||||
category =>
|
||||
|
|
|
@ -371,3 +371,9 @@ export function doConditionalAuthNavigate(newSession) {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function doToggleEnhancedLayout() {
|
||||
return {
|
||||
type: ACTIONS.ENNNHHHAAANNNCEEE,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ export type AppState = {
|
|||
isUpgradeAvailable: ?boolean,
|
||||
isUpgradeSkipped: ?boolean,
|
||||
hasClickedComment: boolean,
|
||||
enhancedLayout: boolean,
|
||||
};
|
||||
|
||||
const defaultState: AppState = {
|
||||
|
@ -57,6 +58,7 @@ const defaultState: AppState = {
|
|||
checkUpgradeTimer: undefined,
|
||||
isUpgradeAvailable: undefined,
|
||||
isUpgradeSkipped: undefined,
|
||||
enhancedLayout: false,
|
||||
};
|
||||
|
||||
reducers[ACTIONS.DAEMON_READY] = state =>
|
||||
|
@ -213,6 +215,11 @@ reducers[ACTIONS.AUTHENTICATION_FAILURE] = state =>
|
|||
modal: MODALS.AUTHENTICATION_FAILURE,
|
||||
});
|
||||
|
||||
reducers[ACTIONS.ENNNHHHAAANNNCEEE] = state =>
|
||||
Object.assign({}, state, {
|
||||
enhancedLayout: !state.enhancedLayout,
|
||||
});
|
||||
|
||||
export default function reducer(state: AppState = defaultState, action: any) {
|
||||
const handler = reducers[action.type];
|
||||
if (handler) return handler(state, action);
|
||||
|
|
|
@ -10,24 +10,34 @@ function getLocalStorageSetting(setting, fallback) {
|
|||
const reducers = {};
|
||||
const defaultState = {
|
||||
clientSettings: {
|
||||
instantPurchaseEnabled: getLocalStorageSetting(SETTINGS.INSTANT_PURCHASE_ENABLED, false),
|
||||
instantPurchaseMax: getLocalStorageSetting(SETTINGS.INSTANT_PURCHASE_MAX, {
|
||||
[SETTINGS.INSTANT_PURCHASE_ENABLED]: getLocalStorageSetting(
|
||||
SETTINGS.INSTANT_PURCHASE_ENABLED,
|
||||
false
|
||||
),
|
||||
[SETTINGS.INSTANT_PURCHASE_MAX]: getLocalStorageSetting(SETTINGS.INSTANT_PURCHASE_MAX, {
|
||||
currency: 'LBC',
|
||||
amount: 0.1,
|
||||
}),
|
||||
showNsfw: getLocalStorageSetting(SETTINGS.SHOW_NSFW, false),
|
||||
showUnavailable: getLocalStorageSetting(SETTINGS.SHOW_UNAVAILABLE, true),
|
||||
welcome_acknowledged: getLocalStorageSetting(SETTINGS.NEW_USER_ACKNOWLEDGED, false),
|
||||
email_collection_acknowledged: getLocalStorageSetting(SETTINGS.EMAIL_COLLECTION_ACKNOWLEDGED),
|
||||
credit_required_acknowledged: false, // this needs to be re-acknowledged every run
|
||||
language: getLocalStorageSetting(SETTINGS.LANGUAGE, 'en'),
|
||||
theme: getLocalStorageSetting(SETTINGS.THEME, 'light'),
|
||||
themes: getLocalStorageSetting(SETTINGS.THEMES, []),
|
||||
automaticDarkModeEnabled: getLocalStorageSetting(SETTINGS.AUTOMATIC_DARK_MODE_ENABLED, false),
|
||||
autoplay: getLocalStorageSetting(SETTINGS.AUTOPLAY, false),
|
||||
resultCount: Number(getLocalStorageSetting(SETTINGS.RESULT_COUNT, 50)),
|
||||
autoDownload: getLocalStorageSetting(SETTINGS.AUTO_DOWNLOAD, true),
|
||||
osNotificationsEnabled: Boolean(
|
||||
[SETTINGS.SHOW_NSFW]: getLocalStorageSetting(SETTINGS.SHOW_NSFW, false),
|
||||
[SETTINGS.SHOW_UNAVAILABLE]: getLocalStorageSetting(SETTINGS.SHOW_UNAVAILABLE, true),
|
||||
[SETTINGS.NEW_USER_ACKNOWLEDGED]: getLocalStorageSetting(SETTINGS.NEW_USER_ACKNOWLEDGED, false),
|
||||
[SETTINGS.EMAIL_COLLECTION_ACKNOWLEDGED]: getLocalStorageSetting(
|
||||
SETTINGS.EMAIL_COLLECTION_ACKNOWLEDGED,
|
||||
false
|
||||
),
|
||||
[SETTINGS.FIRST_RUN_COMPLETED]: getLocalStorageSetting(SETTINGS.FIRST_RUN_COMPLETED, false),
|
||||
[SETTINGS.CREDIT_REQUIRED_ACKNOWLEDGED]: false, // this needs to be re-acknowledged every run
|
||||
[SETTINGS.LANGUAGE]: getLocalStorageSetting(SETTINGS.LANGUAGE, 'en'),
|
||||
[SETTINGS.THEME]: getLocalStorageSetting(SETTINGS.THEME, 'dark'),
|
||||
[SETTINGS.THEMES]: getLocalStorageSetting(SETTINGS.THEMES, []),
|
||||
[SETTINGS.AUTOMATIC_DARK_MODE_ENABLED]: getLocalStorageSetting(
|
||||
SETTINGS.AUTOMATIC_DARK_MODE_ENABLED,
|
||||
false
|
||||
),
|
||||
[SETTINGS.AUTOPLAY]: getLocalStorageSetting(SETTINGS.AUTOPLAY, false),
|
||||
[SETTINGS.RESULT_COUNT]: Number(getLocalStorageSetting(SETTINGS.RESULT_COUNT, 50)),
|
||||
[SETTINGS.AUTO_DOWNLOAD]: getLocalStorageSetting(SETTINGS.AUTO_DOWNLOAD, true),
|
||||
[SETTINGS.OS_NOTIFICATIONS_ENABLED]: Boolean(
|
||||
getLocalStorageSetting(SETTINGS.OS_NOTIFICATIONS_ENABLED, true)
|
||||
),
|
||||
},
|
||||
|
|
|
@ -263,3 +263,5 @@ export const selectModal = createSelector(selectState, state => {
|
|||
modalProps: state.modalProps,
|
||||
};
|
||||
});
|
||||
|
||||
export const selectEnhancedLayout = createSelector(selectState, state => state.enhancedLayout);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
@import 'init/gui';
|
||||
@import 'component/animation';
|
||||
@import 'component/badge';
|
||||
@import 'component/banner';
|
||||
@import 'component/button';
|
||||
@import 'component/card';
|
||||
@import 'component/channel';
|
||||
|
@ -45,3 +46,4 @@
|
|||
@import 'component/toggle';
|
||||
@import 'component/tooltip';
|
||||
@import 'component/wunderbar';
|
||||
@import 'component/yrbl';
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
.badge--cost:not(.badge--large) {
|
||||
background-color: $lbry-yellow-2;
|
||||
color: $lbry-black;
|
||||
|
||||
html[data-theme='dark'] & {
|
||||
background-color: $lbry-yellow-3;
|
||||
|
|
30
src/renderer/scss/component/_banner.scss
Normal file
30
src/renderer/scss/component/_banner.scss
Normal file
|
@ -0,0 +1,30 @@
|
|||
.banner {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
background-color: $lbry-black;
|
||||
color: $lbry-white;
|
||||
}
|
||||
|
||||
.banner--first-run {
|
||||
height: 310px;
|
||||
|
||||
// Adjust this class inside other `.banner--xxx` styles for control over animation
|
||||
.banner__item--static-for-animation {
|
||||
height: 310px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.banner__item {
|
||||
&:not(:first-child) {
|
||||
margin-left: var(--spacing-vertical-large);
|
||||
}
|
||||
}
|
||||
|
||||
.banner__content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
|
@ -81,7 +81,7 @@
|
|||
// C O N T E N T
|
||||
|
||||
.card__content {
|
||||
font-size: 1.15rem;
|
||||
font-size: 1.25rem;
|
||||
|
||||
p:not(:last-child) {
|
||||
margin-bottom: var(--spacing-vertical-medium);
|
||||
|
@ -174,9 +174,13 @@
|
|||
// S U B T I T L E
|
||||
|
||||
.card__subtitle {
|
||||
font-size: 1.25rem;
|
||||
font-size: 1.15rem;
|
||||
margin-bottom: var(--spacing-vertical-small);
|
||||
|
||||
p {
|
||||
margin-bottom: var(--spacing-vertical-small);
|
||||
}
|
||||
|
||||
.badge {
|
||||
bottom: -0.12rem;
|
||||
position: relative;
|
||||
|
|
|
@ -613,10 +613,10 @@
|
|||
|
||||
.media-group__header-navigation {
|
||||
display: flex;
|
||||
padding-right: var(--spacing-vertical-medium);
|
||||
padding-right: var(--spacing-vertical-large);
|
||||
|
||||
button:not(:last-of-type) {
|
||||
margin-right: var(--spacing-vertical-tiny);
|
||||
margin-right: var(--spacing-vertical-medium);
|
||||
}
|
||||
|
||||
svg {
|
||||
|
|
37
src/renderer/scss/component/_yrbl.scss
Normal file
37
src/renderer/scss/component/_yrbl.scss
Normal file
|
@ -0,0 +1,37 @@
|
|||
.yrbl {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.yrbl--first-run {
|
||||
align-self: center;
|
||||
height: 200px;
|
||||
width: auto;
|
||||
margin: 0 var(--spacing-vertical-large);
|
||||
}
|
||||
|
||||
// Get weird here
|
||||
.yrbl--enhanced {
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
height: 95vh;
|
||||
width: 95vh;
|
||||
left: 0;
|
||||
right: 0;
|
||||
opacity: 0.5;
|
||||
animation-name: enhancedAnimation;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
|
||||
@-webkit-keyframes enhancedAnimation {
|
||||
from {
|
||||
left: 0;
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
left: 50vw;
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
|
@ -150,7 +150,6 @@ input {
|
|||
.card {
|
||||
.help {
|
||||
margin-bottom: 0;
|
||||
margin-top: var(--spacing-vertical-large);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,6 +159,7 @@ input {
|
|||
color: $lbry-gray-5;
|
||||
display: block;
|
||||
padding: 1rem;
|
||||
margin-top: var(--spacing-vertical-large);
|
||||
margin-bottom: var(--spacing-vertical-large);
|
||||
|
||||
html[data-theme='dark'] & {
|
||||
|
|
152
src/renderer/util/enhanced-layout.js
Normal file
152
src/renderer/util/enhanced-layout.js
Normal file
|
@ -0,0 +1,152 @@
|
|||
/* eslint-disable */
|
||||
/*
|
||||
* Konami-JS ~
|
||||
* :: Now with support for touch events and multiple instances for
|
||||
* :: those situations that call for multiple easter eggs!
|
||||
* Code: https://github.com/snaptortoise/konami-js
|
||||
* Copyright (c) 2009 George Mandis (georgemandis.com, snaptortoise.com)
|
||||
* Version: 1.6.2 (7/17/2018)
|
||||
* Licensed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
* Tested in: Safari 4+, Google Chrome 4+, Firefox 3+, IE7+, Mobile Safari 2.2.1+ and Android
|
||||
*/
|
||||
|
||||
var Konami = function(callback) {
|
||||
var konami = {
|
||||
addEvent: function(obj, type, fn, ref_obj) {
|
||||
if (obj.addEventListener) obj.addEventListener(type, fn, false);
|
||||
else if (obj.attachEvent) {
|
||||
// IE
|
||||
obj['e' + type + fn] = fn;
|
||||
obj[type + fn] = function() {
|
||||
obj['e' + type + fn](window.event, ref_obj);
|
||||
};
|
||||
obj.attachEvent('on' + type, obj[type + fn]);
|
||||
}
|
||||
},
|
||||
removeEvent: function(obj, eventName, eventCallback) {
|
||||
if (obj.removeEventListener) {
|
||||
obj.removeEventListener(eventName, eventCallback);
|
||||
} else if (obj.attachEvent) {
|
||||
obj.detachEvent(eventName);
|
||||
}
|
||||
},
|
||||
input: '',
|
||||
pattern: '38384040373937396665',
|
||||
keydownHandler: function(e, ref_obj) {
|
||||
if (ref_obj) {
|
||||
konami = ref_obj;
|
||||
} // IE
|
||||
konami.input += e ? e.keyCode : event.keyCode;
|
||||
|
||||
if (konami.input.length > konami.pattern.length) {
|
||||
konami.input = konami.input.substr(konami.input.length - konami.pattern.length);
|
||||
}
|
||||
if (konami.input === konami.pattern) {
|
||||
konami.code(konami._currentLink);
|
||||
konami.input = '';
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
},
|
||||
load: function(link) {
|
||||
this._currentLink = link;
|
||||
this.addEvent(document, 'keydown', this.keydownHandler, this);
|
||||
this.iphone.load(link);
|
||||
},
|
||||
unload: function() {
|
||||
this.removeEvent(document, 'keydown', this.keydownHandler);
|
||||
this.iphone.unload();
|
||||
},
|
||||
code: function(link) {
|
||||
window.location = link;
|
||||
},
|
||||
iphone: {
|
||||
start_x: 0,
|
||||
start_y: 0,
|
||||
stop_x: 0,
|
||||
stop_y: 0,
|
||||
tap: false,
|
||||
capture: false,
|
||||
orig_keys: '',
|
||||
keys: ['UP', 'UP', 'DOWN', 'DOWN', 'LEFT', 'RIGHT', 'LEFT', 'RIGHT', 'TAP', 'TAP'],
|
||||
input: [],
|
||||
code: function(link) {
|
||||
konami.code(link);
|
||||
},
|
||||
touchmoveHandler: function(e) {
|
||||
if (e.touches.length === 1 && konami.iphone.capture === true) {
|
||||
var touch = e.touches[0];
|
||||
konami.iphone.stop_x = touch.pageX;
|
||||
konami.iphone.stop_y = touch.pageY;
|
||||
konami.iphone.tap = false;
|
||||
konami.iphone.capture = false;
|
||||
konami.iphone.check_direction();
|
||||
}
|
||||
},
|
||||
touchendHandler: function() {
|
||||
konami.iphone.input.push(konami.iphone.check_direction());
|
||||
|
||||
if (konami.iphone.input.length > konami.iphone.keys.length) konami.iphone.input.shift();
|
||||
|
||||
if (konami.iphone.input.length === konami.iphone.keys.length) {
|
||||
var match = true;
|
||||
for (var i = 0; i < konami.iphone.keys.length; i++) {
|
||||
if (konami.iphone.input[i] !== konami.iphone.keys[i]) {
|
||||
match = false;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
konami.iphone.code(konami._currentLink);
|
||||
}
|
||||
}
|
||||
},
|
||||
touchstartHandler: function(e) {
|
||||
konami.iphone.start_x = e.changedTouches[0].pageX;
|
||||
konami.iphone.start_y = e.changedTouches[0].pageY;
|
||||
konami.iphone.tap = true;
|
||||
konami.iphone.capture = true;
|
||||
},
|
||||
load: function(link) {
|
||||
this.orig_keys = this.keys;
|
||||
konami.addEvent(document, 'touchmove', this.touchmoveHandler);
|
||||
konami.addEvent(document, 'touchend', this.touchendHandler, false);
|
||||
konami.addEvent(document, 'touchstart', this.touchstartHandler);
|
||||
},
|
||||
unload: function() {
|
||||
konami.removeEvent(document, 'touchmove', this.touchmoveHandler);
|
||||
konami.removeEvent(document, 'touchend', this.touchendHandler);
|
||||
konami.removeEvent(document, 'touchstart', this.touchstartHandler);
|
||||
},
|
||||
check_direction: function() {
|
||||
x_magnitude = Math.abs(this.start_x - this.stop_x);
|
||||
y_magnitude = Math.abs(this.start_y - this.stop_y);
|
||||
x = this.start_x - this.stop_x < 0 ? 'RIGHT' : 'LEFT';
|
||||
y = this.start_y - this.stop_y < 0 ? 'DOWN' : 'UP';
|
||||
result = x_magnitude > y_magnitude ? x : y;
|
||||
result = this.tap === true ? 'TAP' : result;
|
||||
return result;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
typeof callback === 'string' && konami.load(callback);
|
||||
if (typeof callback === 'function') {
|
||||
konami.code = callback;
|
||||
konami.load();
|
||||
}
|
||||
|
||||
return konami;
|
||||
};
|
||||
|
||||
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
|
||||
module.exports = Konami;
|
||||
} else {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define([], function() {
|
||||
return Konami;
|
||||
});
|
||||
} else {
|
||||
window.Konami = Konami;
|
||||
}
|
||||
}
|
||||
/* eslint-enable */
|
Binary file not shown.
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 265 KiB |
96
yarn.lock
96
yarn.lock
|
@ -101,6 +101,16 @@
|
|||
lodash "^4.2.0"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@emotion/is-prop-valid@^0.7.3":
|
||||
version "0.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.7.3.tgz#a6bf4fa5387cbba59d44e698a4680f481a8da6cc"
|
||||
dependencies:
|
||||
"@emotion/memoize" "0.7.1"
|
||||
|
||||
"@emotion/memoize@0.7.1":
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.1.tgz#e93c13942592cf5ef01aa8297444dc192beee52f"
|
||||
|
||||
"@lbry/color@^1.0.2":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@lbry/color/-/color-1.0.3.tgz#ec22b2c48b0e358759528fb3bbe7ba468d4e41ca"
|
||||
|
@ -128,12 +138,33 @@
|
|||
node-fetch "^2.1.1"
|
||||
url-template "^2.0.8"
|
||||
|
||||
"@popmotion/easing@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@popmotion/easing/-/easing-1.0.1.tgz#48336aea29542113df10aeb81b4fd10f0b95f937"
|
||||
|
||||
"@popmotion/popcorn@^0.3.0":
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@popmotion/popcorn/-/popcorn-0.3.1.tgz#f0f33fbf7ff66f2cd0bf28be24bee0b1d46b65e8"
|
||||
dependencies:
|
||||
"@popmotion/easing" "^1.0.1"
|
||||
framesync "^4.0.1"
|
||||
hey-listen "^1.0.5"
|
||||
style-value-types "^3.0.7"
|
||||
|
||||
"@samverschueren/stream-to-observable@^0.3.0":
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f"
|
||||
dependencies:
|
||||
any-observable "^0.3.0"
|
||||
|
||||
"@types/invariant@^2.2.29":
|
||||
version "2.2.29"
|
||||
resolved "https://registry.yarnpkg.com/@types/invariant/-/invariant-2.2.29.tgz#aa845204cd0a289f65d47e0de63a6a815e30cc66"
|
||||
|
||||
"@types/node@^10.0.5":
|
||||
version "10.12.18"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67"
|
||||
|
||||
"@types/node@^8.0.24":
|
||||
version "8.10.21"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.21.tgz#12b3f2359b27aa05a45d886c8ba1eb8d1a77e285"
|
||||
|
@ -4222,6 +4253,12 @@ fragment-cache@^0.2.1:
|
|||
dependencies:
|
||||
map-cache "^0.2.2"
|
||||
|
||||
framesync@^4.0.0, framesync@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/framesync/-/framesync-4.0.1.tgz#ed7791baf0d266f58ab02000456f82cb384815bf"
|
||||
dependencies:
|
||||
hey-listen "^1.0.5"
|
||||
|
||||
fresh@0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
||||
|
@ -4667,6 +4704,10 @@ he@1.1.x:
|
|||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
|
||||
|
||||
hey-listen@^1.0.4, hey-listen@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.5.tgz#6d0a3a2f60177f65bc4404d571a00025bf5dc20e"
|
||||
|
||||
highlight.js@^9.3.0:
|
||||
version "9.12.0"
|
||||
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e"
|
||||
|
@ -7322,6 +7363,29 @@ pluralize@^7.0.0:
|
|||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777"
|
||||
|
||||
popmotion-pose@^3.4.0:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/popmotion-pose/-/popmotion-pose-3.4.1.tgz#e13e52ba9fe02051926ca6313a7615876f459990"
|
||||
dependencies:
|
||||
"@popmotion/easing" "^1.0.1"
|
||||
hey-listen "^1.0.5"
|
||||
popmotion "^8.5.0"
|
||||
pose-core "^2.0.0"
|
||||
style-value-types "^3.0.6"
|
||||
tslib "^1.9.1"
|
||||
|
||||
popmotion@^8.5.0:
|
||||
version "8.6.0"
|
||||
resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-8.6.0.tgz#14edf50376fc2d656b59e0d46390896577818abb"
|
||||
dependencies:
|
||||
"@popmotion/easing" "^1.0.1"
|
||||
"@popmotion/popcorn" "^0.3.0"
|
||||
framesync "^4.0.0"
|
||||
hey-listen "^1.0.5"
|
||||
style-value-types "^3.0.6"
|
||||
stylefire "^2.3.4"
|
||||
tslib "^1.9.1"
|
||||
|
||||
portfinder@^1.0.9:
|
||||
version "1.0.13"
|
||||
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9"
|
||||
|
@ -7330,6 +7394,15 @@ portfinder@^1.0.9:
|
|||
debug "^2.2.0"
|
||||
mkdirp "0.5.x"
|
||||
|
||||
pose-core@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/pose-core/-/pose-core-2.0.2.tgz#6a0bd1e7218e4bf30be9c26a30be8eeb5b8695df"
|
||||
dependencies:
|
||||
"@types/invariant" "^2.2.29"
|
||||
"@types/node" "^10.0.5"
|
||||
hey-listen "^1.0.5"
|
||||
tslib "^1.9.1"
|
||||
|
||||
posix-character-classes@^0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
|
||||
|
@ -7907,6 +7980,15 @@ react-paginate@^5.2.1:
|
|||
dependencies:
|
||||
prop-types "^15.6.1"
|
||||
|
||||
react-pose@^4.0.5:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/react-pose/-/react-pose-4.0.5.tgz#056309b62e32ce0190f90ef35d7ade1b4c463d85"
|
||||
dependencies:
|
||||
"@emotion/is-prop-valid" "^0.7.3"
|
||||
hey-listen "^1.0.5"
|
||||
popmotion-pose "^3.4.0"
|
||||
tslib "^1.9.1"
|
||||
|
||||
react-redux@^5.0.3:
|
||||
version "5.0.7"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.7.tgz#0dc1076d9afb4670f993ffaef44b8f8c1155a4c8"
|
||||
|
@ -9260,6 +9342,18 @@ style-loader@^0.20.2:
|
|||
loader-utils "^1.1.0"
|
||||
schema-utils "^0.4.5"
|
||||
|
||||
style-value-types@^3.0.6, style-value-types@^3.0.7:
|
||||
version "3.0.7"
|
||||
resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-3.0.7.tgz#6e7a22cc8b1a4465193268ed66ad5f2a82579054"
|
||||
|
||||
stylefire@^2.3.4:
|
||||
version "2.3.7"
|
||||
resolved "https://registry.yarnpkg.com/stylefire/-/stylefire-2.3.7.tgz#285b86a48f6c25bbdcccb5dafa26b9f578235bb5"
|
||||
dependencies:
|
||||
framesync "^4.0.0"
|
||||
hey-listen "^1.0.4"
|
||||
style-value-types "^3.0.6"
|
||||
|
||||
sumchecker@^1.2.0:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-1.3.1.tgz#79bb3b4456dd04f18ebdbc0d703a1d1daec5105d"
|
||||
|
@ -9592,7 +9686,7 @@ truncate-utf8-bytes@^1.0.0:
|
|||
dependencies:
|
||||
utf8-byte-length "^1.0.1"
|
||||
|
||||
tslib@^1.9.0:
|
||||
tslib@^1.9.0, tslib@^1.9.1:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue