mirror of
https://github.com/LBRYFoundation/lbry-desktop.git
synced 2025-08-31 09:21:27 +00:00
add nudges to sign up
This commit is contained in:
parent
35ab5b1578
commit
f63de7f930
12 changed files with 192 additions and 39 deletions
|
@ -1476,5 +1476,6 @@
|
||||||
"Currently winning": "Currently winning",
|
"Currently winning": "Currently winning",
|
||||||
"URL can not include a space": "URL can not include a space",
|
"URL can not include a space": "URL can not include a space",
|
||||||
"Creator analytics are down for maintenance. Please check back later.": "Creator analytics are down for maintenance. Please check back later.",
|
"Creator analytics are down for maintenance. Please check back later.": "Creator analytics are down for maintenance. Please check back later.",
|
||||||
|
"Sign up to earn %lbc% for you and your favorite creators.": "Sign up to earn %lbc% for you and your favorite creators.",
|
||||||
"--end--": "--end--"
|
"--end--": "--end--"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { doOpenModal } from 'redux/actions/app';
|
import { doOpenModal } from 'redux/actions/app';
|
||||||
import ClaimSupportButton from './view';
|
import { selectUser } from 'redux/selectors/user';
|
||||||
import { makeSelectTagInClaimOrChannelForUri } from 'lbry-redux';
|
import { makeSelectTagInClaimOrChannelForUri } from 'lbry-redux';
|
||||||
|
import ClaimSupportButton from './view';
|
||||||
|
|
||||||
const DISABLE_SUPPORT_TAG = 'disable-support';
|
const DISABLE_SUPPORT_TAG = 'disable-support';
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
disableSupport: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_SUPPORT_TAG)(state),
|
disableSupport: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_SUPPORT_TAG)(state),
|
||||||
|
user: selectUser(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, {
|
export default connect(select, {
|
||||||
|
|
|
@ -4,29 +4,60 @@ import * as ICONS from 'constants/icons';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
|
import usePersistedState from 'effects/use-persisted-state';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
uri: string,
|
uri: string,
|
||||||
doOpenModal: (string, {}) => void,
|
doOpenModal: (string, {}) => void,
|
||||||
fileAction?: boolean,
|
fileAction?: boolean,
|
||||||
disableSupport: boolean,
|
disableSupport: boolean,
|
||||||
|
user: ?User,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ClaimSupportButton(props: Props) {
|
export default function ClaimSupportButton(props: Props) {
|
||||||
const { doOpenModal, uri, fileAction, disableSupport } = props;
|
const { doOpenModal, uri, fileAction, disableSupport, user } = props;
|
||||||
|
const [showNudge, setShowNudge] = React.useState(false);
|
||||||
|
const [nudgeAcknowledged, setNudgeAcknowledged] = usePersistedState('nudge:support-acknowledge', false);
|
||||||
|
const emailVerified = user && user.has_verified_email;
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!emailVerified && !nudgeAcknowledged && fileAction) {
|
||||||
|
setShowNudge(true);
|
||||||
|
}
|
||||||
|
}, [emailVerified, nudgeAcknowledged, fileAction]);
|
||||||
|
|
||||||
if (disableSupport) {
|
if (disableSupport) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<>
|
||||||
button={fileAction ? undefined : 'alt'}
|
<Button
|
||||||
className={classnames({ 'button--file-action': fileAction })}
|
button={fileAction ? undefined : 'alt'}
|
||||||
icon={ICONS.LBC}
|
className={classnames({ 'button--file-action': fileAction, 'button--highlighted': showNudge })}
|
||||||
iconSize={fileAction ? 22 : undefined}
|
icon={ICONS.LBC}
|
||||||
label={__('Support --[button to support a claim]--')}
|
iconSize={fileAction ? 22 : undefined}
|
||||||
requiresAuth={IS_WEB}
|
label={__('Support --[button to support a claim]--')}
|
||||||
title={__('Support this claim')}
|
requiresAuth={IS_WEB}
|
||||||
onClick={() => doOpenModal(MODALS.SEND_TIP, { uri, isSupport: true })}
|
title={__('Support this claim')}
|
||||||
/>
|
onClick={() => doOpenModal(MODALS.SEND_TIP, { uri, isSupport: true })}
|
||||||
|
/>
|
||||||
|
{showNudge && (
|
||||||
|
<div className="nudge">
|
||||||
|
<div className="nudge__wrapper">
|
||||||
|
<span className="nudge__text">{__('Create an account to support this creator!')}</span>
|
||||||
|
<Button
|
||||||
|
className="nudge__close"
|
||||||
|
button="close"
|
||||||
|
icon={ICONS.REMOVE}
|
||||||
|
onClick={() => {
|
||||||
|
setNudgeAcknowledged(true);
|
||||||
|
setShowNudge(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,6 +174,13 @@ const Header = (props: Props) => {
|
||||||
return hideBalance || Number(roundedBalance) === 0 ? __('Your Wallet') : roundedBalance;
|
return hideBalance || Number(roundedBalance) === 0 ? __('Your Wallet') : roundedBalance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loginButtons = (
|
||||||
|
<div className="header__auth-buttons">
|
||||||
|
<Button navigate={`/$/${PAGES.AUTH_SIGNIN}`} button="link" label={__('Log In')} className="mobile-hidden" />
|
||||||
|
<Button navigate={`/$/${PAGES.AUTH}`} button="primary" label={__('Sign Up')} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header
|
<header
|
||||||
className={classnames('header', {
|
className={classnames('header', {
|
||||||
|
@ -198,18 +205,22 @@ const Header = (props: Props) => {
|
||||||
icon={ICONS.ARROW_LEFT}
|
icon={ICONS.ARROW_LEFT}
|
||||||
/>
|
/>
|
||||||
{backTitle && <h1 className="header__auth-title">{isMobile ? simpleBackTitle || backTitle : backTitle}</h1>}
|
{backTitle && <h1 className="header__auth-title">{isMobile ? simpleBackTitle || backTitle : backTitle}</h1>}
|
||||||
<Button
|
{authenticated ? (
|
||||||
aria-label={__('Your wallet')}
|
<Button
|
||||||
navigate={`/$/${PAGES.WALLET}`}
|
aria-label={__('Your wallet')}
|
||||||
className="header__navigation-item menu__title header__navigation-item--balance"
|
navigate={`/$/${PAGES.WALLET}`}
|
||||||
label={getWalletTitle()}
|
className="header__navigation-item menu__title header__navigation-item--balance"
|
||||||
icon={ICONS.LBC}
|
label={getWalletTitle()}
|
||||||
// @if TARGET='app'
|
icon={ICONS.LBC}
|
||||||
onDoubleClick={e => {
|
// @if TARGET='app'
|
||||||
e.stopPropagation();
|
onDoubleClick={e => {
|
||||||
}}
|
e.stopPropagation();
|
||||||
// @endif
|
}}
|
||||||
/>
|
// @endif
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
loginButtons
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
@ -409,17 +420,7 @@ const Header = (props: Props) => {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{IS_WEB && !authenticated && (
|
{IS_WEB && !authenticated && loginButtons}
|
||||||
<div className="header__auth-buttons">
|
|
||||||
<Button
|
|
||||||
navigate={`/$/${PAGES.AUTH_SIGNIN}`}
|
|
||||||
button="link"
|
|
||||||
label={__('Log In')}
|
|
||||||
className="mobile-hidden"
|
|
||||||
/>
|
|
||||||
<Button navigate={`/$/${PAGES.AUTH}`} button="primary" label={__('Sign Up')} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
!isVerifyPage &&
|
!isVerifyPage &&
|
||||||
|
|
|
@ -5,7 +5,9 @@ import * as ICONS from 'constants/icons';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import Icon from 'component/common/icon';
|
||||||
import NotificationBubble from 'component/notificationBubble';
|
import NotificationBubble from 'component/notificationBubble';
|
||||||
|
import I18nMessage from 'component/i18nMessage';
|
||||||
import { PINNED_LABEL_1, PINNED_URI_1, PINNED_URI_2, PINNED_LABEL_2 } from 'config';
|
import { PINNED_LABEL_1, PINNED_URI_1, PINNED_URI_2, PINNED_LABEL_2 } from 'config';
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
import { IS_MAC } from 'component/app/view';
|
import { IS_MAC } from 'component/app/view';
|
||||||
|
@ -260,6 +262,17 @@ function SideNavigation(props: Props) {
|
||||||
return () => window.removeEventListener('keydown', handleKeydown);
|
return () => window.removeEventListener('keydown', handleKeydown);
|
||||||
}, [sidebarOpen, setSidebarOpen, isAbsolute]);
|
}, [sidebarOpen, setSidebarOpen, isAbsolute]);
|
||||||
|
|
||||||
|
const unAuthNudge = (
|
||||||
|
<div className="navigation__auth-nudge">
|
||||||
|
<span>
|
||||||
|
<I18nMessage tokens={{ lbc: <Icon icon={ICONS.LBC} /> }}>
|
||||||
|
Sign up to earn %lbc% for you and your favorite creators.
|
||||||
|
</I18nMessage>
|
||||||
|
</span>
|
||||||
|
<Button button="secondary" label={__('Sign Up')} navigate={`/$/${PAGES.AUTH}`} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classnames('navigation__wrapper', {
|
className={classnames('navigation__wrapper', {
|
||||||
|
@ -321,6 +334,20 @@ function SideNavigation(props: Props) {
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{!isAuthenticated &&
|
||||||
|
(sidebarOpen ? (
|
||||||
|
unAuthNudge
|
||||||
|
) : (
|
||||||
|
<div className="navigation-links--micro">
|
||||||
|
<Button
|
||||||
|
label={__('Sign In')}
|
||||||
|
icon={ICONS.SIGN_IN}
|
||||||
|
className={classnames('navigation-link')}
|
||||||
|
activeClass="navigation-link--active"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
)}
|
)}
|
||||||
|
@ -396,6 +423,7 @@ function SideNavigation(props: Props) {
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
|
{!isAuthenticated && unAuthNudge}
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -12,8 +12,10 @@ import FileSelector from 'component/common/file-selector';
|
||||||
import SyncToggle from 'component/syncToggle';
|
import SyncToggle from 'component/syncToggle';
|
||||||
import Card from 'component/common/card';
|
import Card from 'component/common/card';
|
||||||
import SettingAccountPassword from 'component/settingAccountPassword';
|
import SettingAccountPassword from 'component/settingAccountPassword';
|
||||||
|
import classnames from 'classnames';
|
||||||
import { getPasswordFromCookie } from 'util/saved-passwords';
|
import { getPasswordFromCookie } from 'util/saved-passwords';
|
||||||
import { Lbryio } from 'lbryinc';
|
import { Lbryio } from 'lbryinc';
|
||||||
|
import Yrbl from 'component/yrbl';
|
||||||
|
|
||||||
type Price = {
|
type Price = {
|
||||||
currency: string,
|
currency: string,
|
||||||
|
@ -190,12 +192,27 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
}}
|
}}
|
||||||
className="card-stack"
|
className="card-stack"
|
||||||
>
|
>
|
||||||
|
{!isAuthenticated && IS_WEB && (
|
||||||
|
<div className="main--empty">
|
||||||
|
<Yrbl
|
||||||
|
type="happy"
|
||||||
|
title={__('Sign up for full control')}
|
||||||
|
subtitle={__('Unlock new buttons that change things.')}
|
||||||
|
actions={
|
||||||
|
<div className="section__actions">
|
||||||
|
<Button button="primary" icon={ICONS.SIGN_UP} label={__('Sign Up')} navigate={`/$/${PAGES.AUTH}`} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{!IS_WEB && noDaemonSettings ? (
|
{!IS_WEB && noDaemonSettings ? (
|
||||||
<section className="card card--section">
|
<section className="card card--section">
|
||||||
<div className="card__title card__title--deprecated">{__('Failed to load settings.')}</div>
|
<div className="card__title card__title--deprecated">{__('Failed to load settings.')}</div>
|
||||||
</section>
|
</section>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div className={classnames({ 'card--disabled': IS_WEB && !isAuthenticated })}>
|
||||||
<Card title={__('Language')} actions={<SettingLanguage />} />
|
<Card title={__('Language')} actions={<SettingLanguage />} />
|
||||||
{isAuthenticated && <SettingAccountPassword />}
|
{isAuthenticated && <SettingAccountPassword />}
|
||||||
{/* @if TARGET='app' */}
|
{/* @if TARGET='app' */}
|
||||||
|
|
|
@ -46,7 +46,7 @@ import {
|
||||||
selectAllowAnalytics,
|
selectAllowAnalytics,
|
||||||
} from 'redux/selectors/app';
|
} from 'redux/selectors/app';
|
||||||
import { selectDaemonSettings, makeSelectClientSetting } from 'redux/selectors/settings';
|
import { selectDaemonSettings, makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
import { selectUser } from 'redux/selectors/user';
|
import { selectUser, selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||||
// import { selectDaemonSettings } from 'redux/selectors/settings';
|
// import { selectDaemonSettings } from 'redux/selectors/settings';
|
||||||
import { doSyncSubscribe, doSetPrefsReady } from 'redux/actions/sync';
|
import { doSyncSubscribe, doSetPrefsReady } from 'redux/actions/sync';
|
||||||
import { doAuthenticate } from 'redux/actions/user';
|
import { doAuthenticate } from 'redux/actions/user';
|
||||||
|
@ -335,13 +335,20 @@ export function doAlertError(errorList) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doAlertWaitingForSync() {
|
export function doAlertWaitingForSync() {
|
||||||
return dispatch =>
|
return (dispatch, getState) => {
|
||||||
|
const state = getState();
|
||||||
|
const authenticated = selectUserVerifiedEmail(state);
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
doToast({
|
doToast({
|
||||||
message: __('Please wait a bit, we are still getting your account ready.'),
|
message:
|
||||||
|
!authenticated && IS_WEB
|
||||||
|
? __('Sign in or create an account to change this setting.')
|
||||||
|
: __('Please wait a bit, we are still getting your account ready.'),
|
||||||
isError: false,
|
isError: false,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doDaemonReady() {
|
export function doDaemonReady() {
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
@import 'component/nag';
|
@import 'component/nag';
|
||||||
@import 'component/navigation';
|
@import 'component/navigation';
|
||||||
@import 'component/notification';
|
@import 'component/notification';
|
||||||
|
@import 'component/nudge';
|
||||||
@import 'component/pagination';
|
@import 'component/pagination';
|
||||||
@import 'component/purchase';
|
@import 'component/purchase';
|
||||||
@import 'component/placeholder';
|
@import 'component/placeholder';
|
||||||
|
|
|
@ -203,6 +203,10 @@
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button--highlighted {
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
|
||||||
.button__content {
|
.button__content {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -236,3 +236,24 @@
|
||||||
top: calc(var(--header-height) + var(--mac-titlebar-height));
|
top: calc(var(--header-height) + var(--mac-titlebar-height));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navigation__auth-nudge {
|
||||||
|
@extend .card;
|
||||||
|
margin: var(--spacing-s);
|
||||||
|
margin-top: var(--spacing-l);
|
||||||
|
padding: var(--spacing-xs);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
margin-top: var(--spacing-s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button__content {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-bottom: -2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
39
ui/scss/component/_nudge.scss
Normal file
39
ui/scss/component/_nudge.scss
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
.nudge {
|
||||||
|
z-index: 3;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: calc(var(--height-button) + var(--spacing-s));
|
||||||
|
white-space: normal;
|
||||||
|
overflow: visible;
|
||||||
|
padding: var(--spacing-m);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
box-shadow: var(--card-box-shadow);
|
||||||
|
background-color: var(--color-secondary);
|
||||||
|
color: var(--color-white);
|
||||||
|
|
||||||
|
.button--close {
|
||||||
|
.icon {
|
||||||
|
stroke: var(--color-white);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
height: 1rem;
|
||||||
|
width: 1rem;
|
||||||
|
top: -0.5rem;
|
||||||
|
left: 3rem;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
background-color: var(--color-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nudge__wrapper {
|
||||||
|
width: 10rem;
|
||||||
|
margin-right: var(--spacing-m);
|
||||||
|
|
||||||
|
@media (min-width: $breakpoint-small) {
|
||||||
|
width: 12.5rem;
|
||||||
|
}
|
||||||
|
}
|
|
@ -104,6 +104,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.section__actions {
|
.section__actions {
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-top: var(--spacing-l);
|
margin-top: var(--spacing-l);
|
||||||
|
|
Loading…
Add table
Reference in a new issue