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>
|
||||
<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,7 +50,12 @@ class UserEmailVerify extends React.PureComponent<Props> {
|
|||
const { cancelButton, email } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<React.Fragment>
|
||||
<header className="card__header">
|
||||
<h2 className="card__title">{__('Waiting For Verification')}</h2>
|
||||
</header>
|
||||
|
||||
<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.')}
|
||||
|
@ -66,11 +71,12 @@ class UserEmailVerify extends React.PureComponent<Props> {
|
|||
</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" />{' '}
|
||||
{__('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,11 +65,6 @@ 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>
|
||||
|
||||
<div className="card__content">
|
||||
{innerContent}
|
||||
|
||||
<p className="help">
|
||||
|
@ -95,7 +77,6 @@ class AuthPage extends React.PureComponent<Props> {
|
|||
label={__('Return home.')}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</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…
Reference in a new issue