feat: show guided tooltip to invite page on first run

This commit is contained in:
Sean Yesmunt 2019-01-26 19:23:47 -05:00
parent 919bd4c934
commit 21661fce85
17 changed files with 346 additions and 250 deletions

View file

@ -9,6 +9,7 @@ type Props = {
icon?: boolean, icon?: boolean,
direction: string, direction: string,
onComponent?: boolean, // extra padding to account for button/form field size onComponent?: boolean, // extra padding to account for button/form field size
alwaysVisible?: boolean, // should tooltip stay open, guide callbacks will close it manually
}; };
type State = { type State = {
@ -18,6 +19,7 @@ type State = {
class ToolTip extends React.PureComponent<Props, State> { class ToolTip extends React.PureComponent<Props, State> {
static defaultProps = { static defaultProps = {
direction: 'bottom', direction: 'bottom',
alwaysVisible: false,
}; };
constructor(props: Props) { constructor(props: Props) {
@ -88,7 +90,7 @@ class ToolTip extends React.PureComponent<Props, State> {
render() { render() {
const { direction } = this.state; const { direction } = this.state;
const { children, label, body, icon, onComponent } = this.props; const { children, label, body, icon, onComponent, alwaysVisible } = this.props;
const tooltipContent = children || label; const tooltipContent = children || label;
const bodyLength = body.length; const bodyLength = body.length;
@ -106,6 +108,7 @@ class ToolTip extends React.PureComponent<Props, State> {
'tooltip--bottom': direction === 'bottom', 'tooltip--bottom': direction === 'bottom',
'tooltip--left': direction === 'left', 'tooltip--left': direction === 'left',
'tooltip--on-component': onComponent, 'tooltip--on-component': onComponent,
'tooltip--always-visible': alwaysVisible,
})} })}
> >
{tooltipContent} {tooltipContent}
@ -113,7 +116,7 @@ class ToolTip extends React.PureComponent<Props, State> {
ref={ref => { ref={ref => {
this.tooltip = ref; this.tooltip = ref;
}} }}
className={classnames('tooltip__body', { className={classnames('card tooltip__body', {
'tooltip__body--short': isShortDescription, 'tooltip__body--short': isShortDescription,
})} })}
> >

View file

@ -0,0 +1,36 @@
// @flow
import React from 'react';
import Native from 'native';
type Props = {
title: string,
subtitle: string,
type: string,
};
const yrblTypes = {
happy: 'gerbil-happy.png',
sad: 'gerbil-sad.png',
};
export default class extends React.PureComponent<Props> {
static defaultProps = {
type: 'happy',
};
render() {
const { title, subtitle, type } = this.props;
const image = yrblTypes[type];
return (
<div className="yrbl-wrap">
<img alt="Friendly gerbil" className="yrbl" src={Native.imagePath(image)} />
<div className="card__content">
<h2 className="card__title">{title}</h2>
<p className="card__subtitle">{subtitle}</p>
</div>
</div>
);
}
}

View file

@ -3,6 +3,7 @@ import * as ICONS from 'constants/icons';
import React from 'react'; import React from 'react';
import Icon from 'component/common/icon'; import Icon from 'component/common/icon';
import RewardLink from 'component/rewardLink'; import RewardLink from 'component/rewardLink';
import Yrbl from 'component/common/yrbl';
import { rewards } from 'lbryinc'; import { rewards } from 'lbryinc';
type Props = { type Props = {
@ -22,6 +23,18 @@ class InviteList extends React.PureComponent<Props> {
return null; return null;
} }
if (!invitees.length) {
return (
<Yrbl
type="happy"
title={__('Power To The People')}
subtitle={__(
'LBRY is powered by the users. More users, more power… and with great power comes great responsibility.'
)}
/>
);
}
return ( return (
<section className="card card--section"> <section className="card card--section">
<header className="card__header"> <header className="card__header">
@ -29,43 +42,37 @@ class InviteList extends React.PureComponent<Props> {
</header> </header>
<div className="card__content"> <div className="card__content">
{invitees.length === 0 && ( <table className="table table--stretch">
<span className="empty">{__("You haven't invited anyone.")} </span> <thead>
)} <tr>
{invitees.length > 0 && ( <th>{__('Invitee Email')}</th>
<table className="table table--stretch"> <th className="text-center">{__('Invite Status')}</th>
<thead> <th className="text-center">{__('Reward')}</th>
<tr> </tr>
<th>{__('Invitee Email')}</th> </thead>
<th className="text-center">{__('Invite Status')}</th> <tbody>
<th className="text-center">{__('Reward')}</th> {invitees.map(invitee => (
<tr key={invitee.email}>
<td>{invitee.email}</td>
<td className="text-center">
{invitee.invite_accepted ? (
<Icon icon={ICONS.COMPLETED} />
) : (
<span className="empty">{__('unused')}</span>
)}
</td>
<td className="text-center">
{invitee.invite_reward_claimed && <Icon icon={ICONS.COMPLETED} />}
{!invitee.invite_reward_claimed && invitee.invite_reward_claimable ? (
<RewardLink label={__('claim')} reward_type={rewards.TYPE_REFERRAL} />
) : (
<span className="empty">{__('unclaimable')}</span>
)}
</td>
</tr> </tr>
</thead> ))}
<tbody> </tbody>
{invitees.map(invitee => ( </table>
<tr key={invitee.email}>
<td>{invitee.email}</td>
<td className="text-center">
{invitee.invite_accepted ? (
<Icon icon={ICONS.COMPLETED} />
) : (
<span className="empty">{__('unused')}</span>
)}
</td>
<td className="text-center">
{invitee.invite_reward_claimed ? (
<Icon icon={ICONS.COMPLETED} />
) : invitee.invite_reward_claimable ? (
<RewardLink label={__('claim')} reward_type={rewards.TYPE_REFERRAL} />
) : (
<span className="empty">{__('unclaimable')}</span>
)}
</td>
</tr>
))}
</tbody>
</table>
)}
<div className="help"> <div className="help">
{__( {__(

View file

@ -1,12 +1,20 @@
// I'll come back to this // @flow
/* eslint-disable */ /* eslint-disable react/no-multi-comp */
import React from 'react'; import React from 'react';
import BusyIndicator from 'component/common/busy-indicator';
import CreditAmount from 'component/common/credit-amount';
import Button from 'component/button'; import Button from 'component/button';
import { Form, FormRow, FormField, Submit } from 'component/common/form'; import { Form, FormRow, FormField, Submit } from 'component/common/form';
class FormInviteNew extends React.PureComponent { type FormProps = {
inviteNew: string => void,
errorMessage: ?string,
isPending: boolean,
};
type FormState = {
email: string,
};
class FormInviteNew extends React.PureComponent<FormProps, FormState> {
constructor() { constructor() {
super(); super();
@ -14,7 +22,7 @@ class FormInviteNew extends React.PureComponent {
email: '', email: '',
}; };
this.handleSubmit = this.handleSubmit.bind(this); (this: any).handleSubmit = this.handleSubmit.bind(this);
} }
handleEmailChanged(event) { handleEmailChanged(event) {
@ -56,16 +64,16 @@ class FormInviteNew extends React.PureComponent {
} }
} }
class InviteNew extends React.PureComponent { type Props = {
errorMessage: ?string,
inviteNew: string => void,
isPending: boolean,
rewardAmount: number,
};
class InviteNew extends React.PureComponent<Props> {
render() { render() {
const { const { errorMessage, inviteNew, isPending, rewardAmount } = this.props;
errorMessage,
invitesRemaining,
inviteNew,
inviteStatusIsPending,
isPending,
rewardAmount,
} = this.props;
return ( return (
<section className="card card--section"> <section className="card card--section">
@ -73,18 +81,10 @@ class InviteNew extends React.PureComponent {
<h2 className="card__title">{__('Invite a Friend')}</h2> <h2 className="card__title">{__('Invite a Friend')}</h2>
<p className="card__subtitle"> <p className="card__subtitle">
{__("Or an enemy. Or your cousin Jerry, who you're kind of unsure about.")} {__('When your friends start using LBRY, the network gets stronger!')}
</p> </p>
</header> </header>
{/*
<div className="card__content">
{invitesRemaining > 0 &&
<p>{__("You have %s invites remaining.", invitesRemaining)}</p>}
{invitesRemaining <= 0 &&
<p className="empty">{__("You have no invites.")}</p>}
</div> */}
<div className="card__content"> <div className="card__content">
<FormInviteNew <FormInviteNew
errorMessage={errorMessage} errorMessage={errorMessage}
@ -106,4 +106,4 @@ class InviteNew extends React.PureComponent {
} }
export default InviteNew; export default InviteNew;
/* eslint-enable */ /* eslint-enable react/no-multi-comp */

View file

@ -1,5 +1,5 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { selectCurrentPage, selectCurrentParams } from 'lbry-redux'; import { selectCurrentPage, selectCurrentParams, doToast } from 'lbry-redux';
import { doOpenModal } from 'redux/actions/app'; import { doOpenModal } from 'redux/actions/app';
import Router from './view'; import Router from './view';
@ -10,5 +10,5 @@ const select = state => ({
export default connect( export default connect(
select, select,
{ doOpenModal } { doOpenModal, doToast }
)(Router); )(Router);

View file

@ -2,6 +2,7 @@
import * as React from 'react'; import * as React from 'react';
import Button from 'component/button'; import Button from 'component/button';
import classnames from 'classnames'; import classnames from 'classnames';
import Tooltip from 'component/common/tooltip';
type SideBarLink = { type SideBarLink = {
label: string, label: string,
@ -9,6 +10,7 @@ type SideBarLink = {
active: boolean, active: boolean,
icon: ?string, icon: ?string,
subLinks: Array<SideBarLink>, subLinks: Array<SideBarLink>,
guide: ?string,
}; };
type Props = { type Props = {
@ -19,71 +21,86 @@ type Props = {
unreadSubscriptionTotal: number, unreadSubscriptionTotal: number,
}; };
const SideBar = (props: Props) => { class SideBar extends React.PureComponent<Props> {
const { navLinks, unreadSubscriptionTotal } = props; renderNavLink(navLink: SideBarLink) {
const { label, path, active, subLinks = [], icon, guide } = navLink;
return ( const inner = (
<nav className="navigation"> <li
<div className="navigation__links"> className={classnames('navigation__link', {
{navLinks.primary.map(({ label, path, active, icon }) => ( 'navigation__link--active': active,
<Button })}
icon={icon} key={label}
className={classnames('navigation__link', { >
'navigation__link--active': active, <Button icon={icon} label={label} navigate={path} />
})}
key={path}
label={
path === '/subscriptions' && unreadSubscriptionTotal
? `${label} (${unreadSubscriptionTotal})`
: label
}
navigate={path}
/>
))}
<ul> {
<li className="navigation__link navigation__link--title">Account</li> // The sublinks should be animated on open close
// Removing it because the current implementation with CSSTransitionGroup
// was really slow and looked pretty bad. Possible fix is upgrading to v2
// Not sure if that has better performance
}
{!!subLinks.length &&
active && (
<ul key="0" className="navigation__link-items">
{subLinks.map(({ active: subLinkActive, label: subLabel, path: subPath }) => (
<li
className={classnames('navigation__link-item', {
'navigation__link-item--active': subLinkActive,
})}
key={subPath}
>
{subPath ? (
<Button label={subLabel} navigate={subPath} />
) : (
<span>{subLabel}</span>
)}
</li>
))}
</ul>
)}
</li>
);
{navLinks.secondary.map(({ label, path, active, subLinks = [], icon }) => ( return guide ? (
<li <Tooltip key={guide} alwaysVisible direction="right" body={guide}>
{inner}
</Tooltip>
) : (
inner
);
}
render() {
const { navLinks, unreadSubscriptionTotal } = this.props;
return (
<nav className="navigation">
<div className="navigation__links">
{navLinks.primary.map(({ label, path, active, icon }) => (
<Button
icon={icon}
className={classnames('navigation__link', { className={classnames('navigation__link', {
'navigation__link--active': active, 'navigation__link--active': active,
})} })}
key={label} key={path}
> label={
<Button icon={icon} label={label} navigate={path} /> path === '/subscriptions' && unreadSubscriptionTotal
? `${label} (${unreadSubscriptionTotal})`
{ : label
// The sublinks should be animated on open close
// Removing it because the current implementation with CSSTransitionGroup
// was really slow and looked pretty bad. Possible fix is upgrading to v2
// Not sure if that has better performance
} }
{!!subLinks.length && navigate={path}
active && ( />
<ul key="0" className="navigation__link-items">
{subLinks.map(({ active: subLinkActive, label: subLabel, path: subPath }) => (
<li
className={classnames('navigation__link-item', {
'navigation__link-item--active': subLinkActive,
})}
key={subPath}
>
{subPath ? (
<Button label={subLabel} navigate={subPath} />
) : (
<span>{subLabel}</span>
)}
</li>
))}
</ul>
)}
</li>
))} ))}
</ul>
</div> <ul>
</nav> <li className="navigation__link navigation__link--title">Account</li>
); {navLinks.secondary.map(this.renderNavLink)}
}; </ul>
</div>
</nav>
);
}
}
export default SideBar; export default SideBar;

View file

@ -0,0 +1,20 @@
export const AUTH = 'auth';
export const BACKUP = 'backup';
export const CHANNEL = 'channel';
export const DISCOVER = 'discover';
export const DOWNLOADED = 'downloaded';
export const HELP = 'help';
export const HISTORY = 'history';
export const INVITE = 'invite';
export const PUBLISH = 'publish';
export const PUBLISHED = 'published';
export const GET_CREDITS = 'getcredits';
export const REPORT = 'report';
export const REWARDS = 'rewards';
export const SEND = 'send';
export const SETTINGS = 'settings';
export const SHOW = 'show';
export const WALLET = 'wallet';
export const SUBSCRIPTIONS = 'subscriptions';
export const SEARCH = 'search';
export const USER_HISTORY = 'user_history';

View file

@ -4,6 +4,7 @@ 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 FIRST_RUN_COMPLETED = 'first_run_completed';
export const INVITE_ACKNOWLEDGED = 'invite_acknowledged';
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,18 +1,23 @@
import * as SETTINGS from 'constants/settings';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { import {
doFetchInviteStatus, doFetchInviteStatus,
selectUserInviteStatusFailed, selectUserInviteStatusFailed,
selectUserInviteStatusIsPending, selectUserInviteStatusIsPending,
} from 'lbryinc'; } from 'lbryinc';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import { doSetClientSetting } from 'redux/actions/settings';
import InvitePage from './view'; import InvitePage from './view';
const select = state => ({ const select = state => ({
isFailed: selectUserInviteStatusFailed(state), isFailed: selectUserInviteStatusFailed(state),
isPending: selectUserInviteStatusIsPending(state), isPending: selectUserInviteStatusIsPending(state),
inviteAcknowledged: makeSelectClientSetting(state)(SETTINGS.INVITE_ACKNOWLEDGED),
}); });
const perform = dispatch => ({ const perform = dispatch => ({
fetchInviteStatus: () => dispatch(doFetchInviteStatus()), fetchInviteStatus: () => dispatch(doFetchInviteStatus()),
acknowledgeInivte: () => dispatch(doSetClientSetting(SETTINGS.INVITE_ACKNOWLEDGED, true)),
}); });
export default connect( export default connect(

View file

@ -1,12 +1,26 @@
// @flow
import React from 'react'; import React from 'react';
import BusyIndicator from 'component/common/busy-indicator'; import BusyIndicator from 'component/common/busy-indicator';
import InviteNew from 'component/inviteNew'; import InviteNew from 'component/inviteNew';
import InviteList from 'component/inviteList'; import InviteList from 'component/inviteList';
import Page from 'component/page'; import Page from 'component/page';
class InvitePage extends React.PureComponent { type Props = {
componentWillMount() { isPending: boolean,
this.props.fetchInviteStatus(); isFailed: boolean,
inviteAcknowledged: boolean,
acknowledgeInivte: () => void,
fetchInviteStatus: () => void,
};
class InvitePage extends React.PureComponent<Props> {
componentDidMount() {
const { fetchInviteStatus, inviteAcknowledged, acknowledgeInivte } = this.props;
fetchInviteStatus();
if (!inviteAcknowledged) {
acknowledgeInivte();
}
} }
render() { render() {

View file

@ -43,17 +43,18 @@ export default (props: Props) => {
<Button button="primary" label={__('Explore')} onClick={doShowSuggestedSubs} /> <Button button="primary" label={__('Explore')} onClick={doShowSuggestedSubs} />
</div> </div>
)} )}
{showSuggested && numberOfSubscriptions > 0 && ( {showSuggested &&
<div className="card__actions"> numberOfSubscriptions > 0 && (
<Button <div className="card__actions">
button="primary" <Button
onClick={onFinish} button="primary"
label={`${__('View your')} ${numberOfSubscriptions} ${ onClick={onFinish}
numberOfSubscriptions > 1 ? __('subscribed channels') : __('subscribed channel') label={`${__('View your')} ${numberOfSubscriptions} ${
}`} numberOfSubscriptions > 1 ? __('subscribed channels') : __('subscribed channel')
/> }`}
</div> />
)} </div>
)}
</div> </div>
</div> </div>
{showSuggested && !loadingSuggested && <SuggestedSubscriptions />} {showSuggested && !loadingSuggested && <SuggestedSubscriptions />}

View file

@ -9,10 +9,10 @@ import FileList from 'component/fileList';
import { FormField } from 'component/common/form'; import { FormField } from 'component/common/form';
import FileCard from 'component/fileCard'; import FileCard from 'component/fileCard';
import { parseURI } from 'lbry-redux'; import { parseURI } from 'lbry-redux';
import Native from 'native';
import SuggestedSubscriptions from 'component/subscribeSuggested'; import SuggestedSubscriptions from 'component/subscribeSuggested';
import MarkAsRead from 'component/subscribeMarkAsRead'; import MarkAsRead from 'component/subscribeMarkAsRead';
import Tooltip from 'component/common/tooltip'; import Tooltip from 'component/common/tooltip';
import Yrbl from 'component/common/yrbl';
type Props = { type Props = {
viewMode: ViewMode, viewMode: ViewMode,
@ -79,17 +79,11 @@ export default (props: Props) => {
{!hasSubscriptions && ( {!hasSubscriptions && (
<Fragment> <Fragment>
<div className="yrbl-wrap"> <Yrbl
<img type="sad"
alt="Sad gerbil" title={__('Oh no! What happened to your subscriptions?')}
className="subscriptions__gerbil" subtitle={__('These channels look pretty cool.')}
src={Native.imagePath('gerbil-sad.png')} />
/>
<div className="card__content">
<h2 className="card__title">{__('Oh no! What happened to your subscriptions?')}</h2>
<p className="card__subtitle">{__('These channels look pretty cool.')}</p>
</div>
</div>
<SuggestedSubscriptions /> <SuggestedSubscriptions />
</Fragment> </Fragment>
)} )}
@ -133,17 +127,10 @@ export default (props: Props) => {
}) })
) : ( ) : (
<Fragment> <Fragment>
<div className="yrbl-wrap"> <Yrbl
<img title={__('All caught up!')}
alt="Friendly gerbil" subtitle={__('You might like the channels below.')}
className="subscriptions__gerbil" />
src={Native.imagePath('gerbil-happy.png')}
/>
<div className="card__content">
<h2 className="card__title">{__('All caught up!')}</h2>
<p className="card__subtitle">{__('You might like the channels below.')}</p>
</div>
</div>
<SuggestedSubscriptions /> <SuggestedSubscriptions />
</Fragment> </Fragment>
)} )}

View file

@ -25,6 +25,7 @@ const defaultState = {
SETTINGS.EMAIL_COLLECTION_ACKNOWLEDGED, SETTINGS.EMAIL_COLLECTION_ACKNOWLEDGED,
false false
), ),
[SETTINGS.INVITE_ACKNOWLEDGED]: getLocalStorageSetting(SETTINGS.INVITE_ACKNOWLEDGED, false),
[SETTINGS.FIRST_RUN_COMPLETED]: getLocalStorageSetting(SETTINGS.FIRST_RUN_COMPLETED, 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.CREDIT_REQUIRED_ACKNOWLEDGED]: false, // this needs to be re-acknowledged every run
[SETTINGS.LANGUAGE]: getLocalStorageSetting(SETTINGS.LANGUAGE, 'en'), [SETTINGS.LANGUAGE]: getLocalStorageSetting(SETTINGS.LANGUAGE, 'en'),

View file

@ -1,6 +1,9 @@
import * as SETTINGS from 'constants/settings';
import * as PAGES from 'constants/pages';
import * as ICONS from 'constants/icons';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { selectCurrentPage, selectHistoryStack } from 'lbry-redux'; import { selectCurrentPage, selectHistoryStack } from 'lbry-redux';
import * as icons from 'constants/icons'; import { makeSelectClientSetting } from 'redux/selectors/settings';
export const selectState = state => state.app || {}; export const selectState = state => state.app || {};
@ -97,15 +100,29 @@ export const selectUpgradeTimer = createSelector(selectState, state => state.che
export const selectNavLinks = createSelector( export const selectNavLinks = createSelector(
selectCurrentPage, selectCurrentPage,
selectHistoryStack, selectHistoryStack,
(currentPage, historyStack) => { makeSelectClientSetting(SETTINGS.FIRST_RUN_COMPLETED),
const isWalletPage = page => makeSelectClientSetting(SETTINGS.INVITE_ACKNOWLEDGED),
page === 'wallet' || (currentPage, historyStack, firstRunCompleted, inviteAcknowledged) => {
page === 'send' || // Determine if any links should show a tooltip for a guided tour
page === 'getcredits' || // It will only show one at a time, in the order they are set.
page === 'rewards' || const guidedTourItem = [
page === 'history' || {
page === 'backup'; page: PAGES.INVITE,
hasBeenCompleted: inviteAcknowledged,
guide: 'Check this out!',
},
// Add more items below for tooltip guides that will happen after a user has completed the invite guide
].filter(({ hasBeenCompleted }) => !hasBeenCompleted)[0];
const isWalletPage = page =>
page === PAGES.WALLET ||
page === PAGES.SEND ||
page === PAGES.GET_CREDITS ||
page === PAGES.REWARDS ||
page === PAGES.HISTORY ||
page === PAGES.BACKUP;
const isCurrentlyWalletPage = isWalletPage(currentPage);
const previousStack = historyStack.slice().reverse(); const previousStack = historyStack.slice().reverse();
const getPreviousSubLinkPath = checkIfValidPage => { const getPreviousSubLinkPath = checkIfValidPage => {
@ -124,107 +141,92 @@ export const selectNavLinks = createSelector(
// Gets the last active sublink in a section // Gets the last active sublink in a section
const getActiveSublink = category => { const getActiveSublink = category => {
if (category === 'wallet') { if (category === PAGES.WALLET) {
const previousPath = getPreviousSubLinkPath(isWalletPage); const previousPath = getPreviousSubLinkPath(isWalletPage);
return previousPath || '/wallet'; return previousPath || `/${PAGES.WALLET}`;
} }
return undefined; return undefined;
}; };
const isCurrentlyWalletPage = isWalletPage(currentPage); // Is this path the first unacknowledged item in the guided tour list
const getGuideIfNecessary = page => {
if (!firstRunCompleted) {
return null;
}
return guidedTourItem && guidedTourItem.page === page ? guidedTourItem.guide : null;
};
const buildLink = (label, page) => ({
label,
path: `/${page}`,
active: currentPage === page,
guide: getGuideIfNecessary(page),
});
const walletSubLinks = [ const walletSubLinks = [
{ {
label: 'Overview', ...buildLink('Overview', PAGES.WALLET),
path: '/wallet',
active: currentPage === 'wallet',
}, },
{ {
label: 'Send & Receive', ...buildLink('Send & Receive', PAGES.SEND),
path: '/send',
active: currentPage === 'send',
}, },
{ {
label: 'Transactions', ...buildLink('Transactions', PAGES.HISTORY),
path: '/history',
active: currentPage === 'history',
}, },
{ {
label: 'Get Credits', ...buildLink('Get Credits', PAGES.GET_CREDITS),
path: '/getcredits',
active: currentPage === 'getcredits',
}, },
{ {
label: 'Rewards', ...buildLink('Rewards', PAGES.REWARDS),
path: '/rewards',
active: currentPage === 'rewards',
}, },
{ {
label: 'Backup', ...buildLink('Backup', PAGES.BACKUP),
path: '/backup',
active: currentPage === 'backup',
}, },
]; ];
const navLinks = { const navLinks = {
primary: [ primary: [
{ {
label: 'Explore', ...buildLink('Explore', PAGES.DISCOVER),
path: '/discover', icon: ICONS.HOME,
active: currentPage === 'discover',
icon: icons.HOME,
}, },
{ {
label: 'Subscriptions', ...buildLink('Subscriptions', PAGES.SUBSCRIPTIONS),
path: '/subscriptions', icon: ICONS.SUBSCRIPTION,
active: currentPage === 'subscriptions',
icon: icons.SUBSCRIPTION,
}, },
], ],
secondary: [ secondary: [
{ {
label: 'Wallet', label: 'Wallet',
icon: icons.WALLET, icon: ICONS.WALLET,
subLinks: walletSubLinks, subLinks: walletSubLinks,
path: isCurrentlyWalletPage ? '/wallet' : getActiveSublink('wallet'), path: isCurrentlyWalletPage ? `/${PAGES.WALLET}` : getActiveSublink(PAGES.WALLET),
active: isWalletPage(currentPage), active: isWalletPage(currentPage),
}, },
{ {
label: 'Invite', ...buildLink('Invite', PAGES.INVITE),
icon: icons.INVITE, icon: ICONS.INVITE,
path: '/invite',
active: currentPage === 'invite',
}, },
{ {
label: 'Downloads', ...buildLink('Downloads', PAGES.DOWNLOADED),
icon: icons.LOCAL, icon: ICONS.LOCAL,
path: '/downloaded',
active: currentPage === 'downloaded',
}, },
{ {
label: 'Publishes', ...buildLink('Publishes', PAGES.PUBLISHED),
icon: icons.PUBLISHED, icon: ICONS.PUBLISHED,
path: '/published',
active: currentPage === 'published',
}, },
{ {
label: 'History', ...buildLink('History', PAGES.USER_HISTORY),
icon: icons.HISTORY, icon: ICONS.HISTORY,
path: '/user_history',
active: currentPage === 'user_history',
}, },
{ {
label: 'Settings', ...buildLink('Settings', PAGES.SETTINGS),
icon: icons.SETTINGS, icon: ICONS.SETTINGS,
path: '/settings',
active: currentPage === 'settings',
}, },
{ {
label: 'Help', ...buildLink('Help', PAGES.HELP),
path: '/help', icon: ICONS.HELP,
icon: icons.HELP,
active: currentPage === 'help',
}, },
], ],
}; };

View file

@ -1,23 +1,8 @@
// The gerbil is tied to subscriptions currently, but this style should move to it's own file once // The gerbil is tied to subscriptions currently, but this style should move to it's own file once
// the gerbil is added in more places with different layouts // the gerbil is added in more places with different layouts
.subscriptions__gerbil {
}
.subscriptions__suggested { .subscriptions__suggested {
animation: expand 0.2s; animation: expand 0.2s;
left: -2rem; left: -2rem;
position: relative; position: relative;
width: calc(100% + 4rem); width: calc(100% + 4rem);
} }
.yrbl-wrap {
align-items: center;
display: flex;
justify-content: center;
vertical-align: middle;
margin-bottom: var(--spacing-vertical-large);
img {
height: 300px;
}
}

View file

@ -1,11 +1,10 @@
.tooltip { .tooltip {
display: inline-block; display: inline-block;
position: relative; position: relative;
z-index: 2;
&:not(:hover) { .tooltip__body {
.tooltip__body { visibility: hidden;
visibility: hidden;
}
} }
&:hover { &:hover {
@ -15,17 +14,20 @@
} }
.tooltip__body { .tooltip__body {
background-color: $lbry-gray-5;
border-radius: 8px;
color: $lbry-white;
font-size: 1rem; font-size: 1rem;
font-weight: 500; color: $lbry-black;
font-weight: 400;
padding: var(--spacing-vertical-small); padding: var(--spacing-vertical-small);
position: absolute; position: absolute;
text-align: center; text-align: center;
white-space: pre-wrap; white-space: pre-wrap;
width: 200px; width: 200px;
z-index: 1; box-shadow: 5px 5px 5px rgba($lbry-black, 0.15);
html[data-theme='dark'] & {
border: 1px solid #2f2f2f;
background-color: $lbry-gray-1;
}
&::after { &::after {
width: 0; width: 0;
@ -37,12 +39,18 @@
position: absolute; position: absolute;
} }
&--short { &.tooltip__body--short {
width: 130px; width: 130px;
} }
} }
} }
.tooltip--always-visible {
.tooltip__body {
visibility: visible;
}
}
.tooltip--bottom .tooltip__body { .tooltip--bottom .tooltip__body {
top: 90%; top: 90%;
left: 50%; left: 50%;

View file

@ -1,10 +1,19 @@
.yrbl-wrap {
align-items: center;
display: flex;
justify-content: center;
vertical-align: middle;
margin-bottom: var(--spacing-vertical-large);
}
.yrbl { .yrbl {
height: 300px; height: 300px;
margin-right: var(--spacing-vertical-large);
} }
.yrbl--first-run { .yrbl--first-run {
align-self: center; align-self: center;
height: 200px; height: 250px;
width: auto; width: auto;
margin: 0 var(--spacing-vertical-large); margin: 0 var(--spacing-vertical-large);
} }