change: less intrusive first run experience

This commit is contained in:
Sean Yesmunt 2019-01-22 13:29:45 -05:00
parent 1f74ff3c63
commit defc08eccb
24 changed files with 449 additions and 191 deletions

View file

@ -67,6 +67,7 @@
"react-feather": "^1.0.8", "react-feather": "^1.0.8",
"react-modal": "^3.1.7", "react-modal": "^3.1.7",
"react-paginate": "^5.2.1", "react-paginate": "^5.2.1",
"react-pose": "^4.0.5",
"react-redux": "^5.0.3", "react-redux": "^5.0.3",
"react-simplemde-editor": "^3.6.16", "react-simplemde-editor": "^3.6.16",
"react-toggle": "^4.0.2", "react-toggle": "^4.0.2",

View 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);

View 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;

View 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);

View 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>
);
}
}

View file

@ -61,7 +61,7 @@ class SocialShare extends React.PureComponent<Props> {
<div className="card__content"> <div className="card__content">
<label className="card__subtitle">{__('Web link')}</label> <label className="card__subtitle">{__('Web link')}</label>
<CopyableText copyable={speechURL} /> <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')}> <ToolTip onComponent body={__('Facebook')}>
<Button <Button
iconColor="blue" iconColor="blue"
@ -95,7 +95,7 @@ class SocialShare extends React.PureComponent<Props> {
<div className="card__content"> <div className="card__content">
<label className="card__subtitle">{__('LBRY App link')}</label> <label className="card__subtitle">{__('LBRY App link')}</label>
<CopyableText copyable={lbryURL} noSnackbar /> <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')}> <ToolTip onComponent body={__('Facebook')}>
<Button <Button
iconColor="blue" iconColor="blue"

View file

@ -42,16 +42,14 @@ class UserEmailNew extends React.PureComponent<Props, State> {
return ( return (
<React.Fragment> <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.")} {__("We'll let you know about LBRY updates, security issues, and great new content.")}
</p> </p>
</header>
<p> <Form className="card__content" onSubmit={this.handleSubmit}>
{__(
'In addition, your email address will never be sold and you can unsubscribe at any time.'
)}
</p>
<Form onSubmit={this.handleSubmit}>
<FormRow> <FormRow>
<FormField <FormField
stretch stretch
@ -66,10 +64,13 @@ class UserEmailNew extends React.PureComponent<Props, State> {
</FormRow> </FormRow>
<div className="card__actions"> <div className="card__actions">
<Submit label="Submit" disabled={isPending} /> <Submit label="Submit" disabled={isPending || !this.state.email} />
{cancelButton} {cancelButton}
</div> </div>
</Form> </Form>
<p className="help">
{__('Your email address will never be sold and you can unsubscribe at any time.')}
</p>
</React.Fragment> </React.Fragment>
); );
} }

View file

@ -50,7 +50,12 @@ class UserEmailVerify extends React.PureComponent<Props> {
const { cancelButton, email } = this.props; const { cancelButton, email } = this.props;
return ( return (
<div> <React.Fragment>
<header className="card__header">
<h2 className="card__title">{__('Waiting For Verification')}</h2>
</header>
<div className="card__content">
<p> <p>
{__('An email was sent to')} {email}.{' '} {__('An email was sent to')} {email}.{' '}
{__('Follow the link and you will be good to go. This will update automatically.')} {__('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> </div>
<p className="help"> <p className="help">
{__('Email')} <Button button="link" href="mailto:help@lbry.io" label="help@lbry.io" /> or {__('Email')} <Button button="link" href="mailto:help@lbry.io" label="help@lbry.io" />{' '}
join our <Button button="link" href="https://chat.lbry.io" label="chat" />{' '} or join our <Button button="link" href="https://chat.lbry.io" label="chat" />{' '}
{__('if you encounter any trouble verifying.')} {__('if you encounter any trouble verifying.')}
</p> </p>
</div> </div>
</React.Fragment>
); );
} }
} }

View file

@ -4,6 +4,7 @@
export const CREDIT_REQUIRED_ACKNOWLEDGED = 'credit_required_acknowledged'; export const CREDIT_REQUIRED_ACKNOWLEDGED = 'credit_required_acknowledged';
export const NEW_USER_ACKNOWLEDGED = 'welcome_acknowledged'; export const NEW_USER_ACKNOWLEDGED = 'welcome_acknowledged';
export const EMAIL_COLLECTION_ACKNOWLEDGED = 'email_collection_acknowledged'; export const EMAIL_COLLECTION_ACKNOWLEDGED = 'email_collection_acknowledged';
export const FIRST_RUN_COMPLETED = 'first_run_completed';
export const LANGUAGE = 'language'; export const LANGUAGE = 'language';
export const SHOW_NSFW = 'showNsfw'; export const SHOW_NSFW = 'showNsfw';
export const SHOW_UNAVAILABLE = 'showUnavailable'; export const SHOW_UNAVAILABLE = 'showUnavailable';

View file

@ -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);

View file

@ -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;

View file

@ -16,7 +16,6 @@ import ModalTransactionFailed from 'modal/modalTransactionFailed';
import ModalFileTimeout from 'modal/modalFileTimeout'; import ModalFileTimeout from 'modal/modalFileTimeout';
import ModalAffirmPurchase from 'modal/modalAffirmPurchase'; import ModalAffirmPurchase from 'modal/modalAffirmPurchase';
import ModalRevokeClaim from 'modal/modalRevokeClaim'; import ModalRevokeClaim from 'modal/modalRevokeClaim';
import ModalEmailCollection from 'modal/modalEmailCollection';
import ModalPhoneCollection from 'modal/modalPhoneCollection'; import ModalPhoneCollection from 'modal/modalPhoneCollection';
import ModalFirstSubscription from 'modal/modalFirstSubscription'; import ModalFirstSubscription from 'modal/modalFirstSubscription';
import ModalConfirmTransaction from 'modal/modalConfirmTransaction'; import ModalConfirmTransaction from 'modal/modalConfirmTransaction';
@ -78,11 +77,10 @@ class ModalRouter extends React.PureComponent<Props, State> {
return; return;
} }
const transitionModal = [ const transitionModal = [this.checkShowCreditIntro].reduce(
this.checkShowWelcome, (acc, func) => (!acc ? func.bind(this)(props) : acc),
this.checkShowEmail, false
this.checkShowCreditIntro, );
].reduce((acc, func) => (!acc ? func.bind(this)(props) : acc), false);
if ( if (
transitionModal && 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) { checkShowCreditIntro(props: Props) {
const { balance, page, isCreditIntroAcknowledged } = props; const { balance, page, isCreditIntroAcknowledged } = props;
@ -185,8 +159,6 @@ class ModalRouter extends React.PureComponent<Props, State> {
return <ModalRevokeClaim {...modalProps} />; return <ModalRevokeClaim {...modalProps} />;
case MODALS.PHONE_COLLECTION: case MODALS.PHONE_COLLECTION:
return <ModalPhoneCollection {...modalProps} />; return <ModalPhoneCollection {...modalProps} />;
case MODALS.EMAIL_COLLECTION:
return <ModalEmailCollection {...modalProps} />;
case MODALS.FIRST_SUBSCRIPTION: case MODALS.FIRST_SUBSCRIPTION:
return <ModalFirstSubscription {...modalProps} />; return <ModalFirstSubscription {...modalProps} />;
case MODALS.SEND_TIP: case MODALS.SEND_TIP:

View file

@ -30,19 +30,6 @@ class AuthPage extends React.PureComponent<Props> {
this.navigateIfAuthenticated(nextProps); 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) => { navigateIfAuthenticated = (props: Props) => {
const { isPending, user, pathAfterAuth, navigate } = props; const { isPending, user, pathAfterAuth, navigate } = props;
if ( if (
@ -78,11 +65,6 @@ class AuthPage extends React.PureComponent<Props> {
<Page> <Page>
{useTemplate ? ( {useTemplate ? (
<section className="card card--section"> <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} {innerContent}
<p className="help"> <p className="help">
@ -95,7 +77,6 @@ class AuthPage extends React.PureComponent<Props> {
label={__('Return home.')} label={__('Return home.')}
/> />
</p> </p>
</div>
</section> </section>
) : ( ) : (
innerContent innerContent

View file

@ -2,10 +2,12 @@
import React from 'react'; import React from 'react';
import Page from 'component/page'; import Page from 'component/page';
import CategoryList from 'component/categoryList'; import CategoryList from 'component/categoryList';
import FirstRun from 'component/firstRun';
type Props = { type Props = {
fetchFeaturedUris: () => void, fetchFeaturedUris: () => void,
fetchRewardedContent: () => void, fetchRewardedContent: () => void,
fetchRewards: () => void,
fetchingFeaturedUris: boolean, fetchingFeaturedUris: boolean,
featuredUris: {}, featuredUris: {},
}; };
@ -59,6 +61,7 @@ class DiscoverPage extends React.PureComponent<Props> {
const failedToLoad = !fetchingFeaturedUris && !hasContent; const failedToLoad = !fetchingFeaturedUris && !hasContent;
return ( return (
<Page noPadding isLoading={!hasContent && fetchingFeaturedUris}> <Page noPadding isLoading={!hasContent && fetchingFeaturedUris}>
<FirstRun />
{hasContent && {hasContent &&
Object.keys(featuredUris).map( Object.keys(featuredUris).map(
category => category =>

View file

@ -10,24 +10,34 @@ function getLocalStorageSetting(setting, fallback) {
const reducers = {}; const reducers = {};
const defaultState = { const defaultState = {
clientSettings: { clientSettings: {
instantPurchaseEnabled: getLocalStorageSetting(SETTINGS.INSTANT_PURCHASE_ENABLED, false), [SETTINGS.INSTANT_PURCHASE_ENABLED]: getLocalStorageSetting(
instantPurchaseMax: getLocalStorageSetting(SETTINGS.INSTANT_PURCHASE_MAX, { SETTINGS.INSTANT_PURCHASE_ENABLED,
false
),
[SETTINGS.INSTANT_PURCHASE_MAX]: getLocalStorageSetting(SETTINGS.INSTANT_PURCHASE_MAX, {
currency: 'LBC', currency: 'LBC',
amount: 0.1, amount: 0.1,
}), }),
showNsfw: getLocalStorageSetting(SETTINGS.SHOW_NSFW, false), [SETTINGS.SHOW_NSFW]: getLocalStorageSetting(SETTINGS.SHOW_NSFW, false),
showUnavailable: getLocalStorageSetting(SETTINGS.SHOW_UNAVAILABLE, true), [SETTINGS.SHOW_UNAVAILABLE]: getLocalStorageSetting(SETTINGS.SHOW_UNAVAILABLE, true),
welcome_acknowledged: getLocalStorageSetting(SETTINGS.NEW_USER_ACKNOWLEDGED, false), [SETTINGS.NEW_USER_ACKNOWLEDGED]: getLocalStorageSetting(SETTINGS.NEW_USER_ACKNOWLEDGED, false),
email_collection_acknowledged: getLocalStorageSetting(SETTINGS.EMAIL_COLLECTION_ACKNOWLEDGED), [SETTINGS.EMAIL_COLLECTION_ACKNOWLEDGED]: getLocalStorageSetting(
credit_required_acknowledged: false, // this needs to be re-acknowledged every run SETTINGS.EMAIL_COLLECTION_ACKNOWLEDGED,
language: getLocalStorageSetting(SETTINGS.LANGUAGE, 'en'), false
theme: getLocalStorageSetting(SETTINGS.THEME, 'light'), ),
themes: getLocalStorageSetting(SETTINGS.THEMES, []), [SETTINGS.FIRST_RUN_COMPLETED]: getLocalStorageSetting(SETTINGS.FIRST_RUN_COMPLETED, false),
automaticDarkModeEnabled: getLocalStorageSetting(SETTINGS.AUTOMATIC_DARK_MODE_ENABLED, false), [SETTINGS.CREDIT_REQUIRED_ACKNOWLEDGED]: false, // this needs to be re-acknowledged every run
autoplay: getLocalStorageSetting(SETTINGS.AUTOPLAY, false), [SETTINGS.LANGUAGE]: getLocalStorageSetting(SETTINGS.LANGUAGE, 'en'),
resultCount: Number(getLocalStorageSetting(SETTINGS.RESULT_COUNT, 50)), [SETTINGS.THEME]: getLocalStorageSetting(SETTINGS.THEME, 'dark'),
autoDownload: getLocalStorageSetting(SETTINGS.AUTO_DOWNLOAD, true), [SETTINGS.THEMES]: getLocalStorageSetting(SETTINGS.THEMES, []),
osNotificationsEnabled: Boolean( [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) getLocalStorageSetting(SETTINGS.OS_NOTIFICATIONS_ENABLED, true)
), ),
}, },

View file

@ -9,6 +9,7 @@
@import 'init/gui'; @import 'init/gui';
@import 'component/animation'; @import 'component/animation';
@import 'component/badge'; @import 'component/badge';
@import 'component/banner';
@import 'component/button'; @import 'component/button';
@import 'component/card'; @import 'component/card';
@import 'component/channel'; @import 'component/channel';
@ -45,3 +46,4 @@
@import 'component/toggle'; @import 'component/toggle';
@import 'component/tooltip'; @import 'component/tooltip';
@import 'component/wunderbar'; @import 'component/wunderbar';
@import 'component/yrbl';

View file

@ -21,6 +21,7 @@
.badge--cost:not(.badge--large) { .badge--cost:not(.badge--large) {
background-color: $lbry-yellow-2; background-color: $lbry-yellow-2;
color: $lbry-black;
html[data-theme='dark'] & { html[data-theme='dark'] & {
background-color: $lbry-yellow-3; background-color: $lbry-yellow-3;

View 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%;
}

View file

@ -81,7 +81,7 @@
// C O N T E N T // C O N T E N T
.card__content { .card__content {
font-size: 1.15rem; font-size: 1.25rem;
p:not(:last-child) { p:not(:last-child) {
margin-bottom: var(--spacing-vertical-medium); margin-bottom: var(--spacing-vertical-medium);
@ -174,9 +174,13 @@
// S U B T I T L E // S U B T I T L E
.card__subtitle { .card__subtitle {
font-size: 1.25rem; font-size: 1.15rem;
margin-bottom: var(--spacing-vertical-small); margin-bottom: var(--spacing-vertical-small);
p {
margin-bottom: var(--spacing-vertical-small);
}
.badge { .badge {
bottom: -0.12rem; bottom: -0.12rem;
position: relative; position: relative;

View file

@ -613,10 +613,10 @@
.media-group__header-navigation { .media-group__header-navigation {
display: flex; display: flex;
padding-right: var(--spacing-vertical-medium); padding-right: var(--spacing-vertical-large);
button:not(:last-of-type) { button:not(:last-of-type) {
margin-right: var(--spacing-vertical-tiny); margin-right: var(--spacing-vertical-medium);
} }
svg { svg {

View file

@ -0,0 +1,10 @@
.yrbl {
height: 300px;
}
.yrbl--first-run {
align-self: center;
height: 200px;
width: auto;
margin: 0 var(--spacing-vertical-large);
}

View file

@ -150,7 +150,6 @@ input {
.card { .card {
.help { .help {
margin-bottom: 0; margin-bottom: 0;
margin-top: var(--spacing-vertical-large);
} }
} }
@ -160,6 +159,7 @@ input {
color: $lbry-gray-5; color: $lbry-gray-5;
display: block; display: block;
padding: 1rem; padding: 1rem;
margin-top: var(--spacing-vertical-large);
margin-bottom: var(--spacing-vertical-large); margin-bottom: var(--spacing-vertical-large);
html[data-theme='dark'] & { html[data-theme='dark'] & {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 265 KiB

View file

@ -101,6 +101,16 @@
lodash "^4.2.0" lodash "^4.2.0"
to-fast-properties "^2.0.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": "@lbry/color@^1.0.2":
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/@lbry/color/-/color-1.0.3.tgz#ec22b2c48b0e358759528fb3bbe7ba468d4e41ca" resolved "https://registry.yarnpkg.com/@lbry/color/-/color-1.0.3.tgz#ec22b2c48b0e358759528fb3bbe7ba468d4e41ca"
@ -128,12 +138,33 @@
node-fetch "^2.1.1" node-fetch "^2.1.1"
url-template "^2.0.8" 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": "@samverschueren/stream-to-observable@^0.3.0":
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f"
dependencies: dependencies:
any-observable "^0.3.0" 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": "@types/node@^8.0.24":
version "8.10.21" version "8.10.21"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.21.tgz#12b3f2359b27aa05a45d886c8ba1eb8d1a77e285" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.21.tgz#12b3f2359b27aa05a45d886c8ba1eb8d1a77e285"
@ -4222,6 +4253,12 @@ fragment-cache@^0.2.1:
dependencies: dependencies:
map-cache "^0.2.2" 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: fresh@0.5.2:
version "0.5.2" version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
@ -4667,6 +4704,10 @@ he@1.1.x:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" 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: highlight.js@^9.3.0:
version "9.12.0" version "9.12.0"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e" 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" version "7.0.0"
resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" 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: portfinder@^1.0.9:
version "1.0.13" version "1.0.13"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9"
@ -7330,6 +7394,15 @@ portfinder@^1.0.9:
debug "^2.2.0" debug "^2.2.0"
mkdirp "0.5.x" 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: posix-character-classes@^0.1.0:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" 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: dependencies:
prop-types "^15.6.1" 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: react-redux@^5.0.3:
version "5.0.7" version "5.0.7"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.7.tgz#0dc1076d9afb4670f993ffaef44b8f8c1155a4c8" 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" loader-utils "^1.1.0"
schema-utils "^0.4.5" 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: sumchecker@^1.2.0:
version "1.3.1" version "1.3.1"
resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-1.3.1.tgz#79bb3b4456dd04f18ebdbc0d703a1d1daec5105d" resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-1.3.1.tgz#79bb3b4456dd04f18ebdbc0d703a1d1daec5105d"
@ -9592,7 +9686,7 @@ truncate-utf8-bytes@^1.0.0:
dependencies: dependencies:
utf8-byte-length "^1.0.1" utf8-byte-length "^1.0.1"
tslib@^1.9.0: tslib@^1.9.0, tslib@^1.9.1:
version "1.9.3" version "1.9.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"