Add Preferences > Subscription empty state that allows you to link ID later

This commit is contained in:
Ben Gotow 2021-02-15 09:55:08 -06:00 committed by Ben Gotow
parent 966dcfd92c
commit 48b7338e88
3 changed files with 191 additions and 157 deletions

View file

@ -5,10 +5,9 @@ import {
localized,
localizedReactFragment,
IIdentity,
EMPTY_IDENTITY,
} from 'mailspring-exports';
import { OpenIdentityPageButton, BillingModal, RetinaImg } from 'mailspring-component-kit';
import { shell } from 'electron';
import { shell, ipcRenderer } from 'electron';
class RefreshButton extends React.Component<{}, { refreshing: boolean }> {
constructor(props) {
@ -132,7 +131,7 @@ const ProTourFeatures = [
},
];
class PreferencesIdentity extends React.Component<{}, { identity: IIdentity }> {
class PreferencesIdentity extends React.Component<{}, { identity: IIdentity | null }> {
static displayName = 'PreferencesIdentity';
unsubscribe: () => void;
@ -154,7 +153,7 @@ class PreferencesIdentity extends React.Component<{}, { identity: IIdentity }> {
_getStateFromStores() {
return {
identity: IdentityStore.identity() || { ...EMPTY_IDENTITY },
identity: IdentityStore.identity(),
};
}
@ -166,7 +165,38 @@ class PreferencesIdentity extends React.Component<{}, { identity: IIdentity }> {
});
};
_renderBasic() {
_onLinkIdentity = () => {
ipcRenderer.send('command', 'application:add-identity');
};
_renderNoIdentity() {
return (
<>
<div className="row padded">
<div style={{ display: 'flex', alignItems: 'flex-start' }}>
<div className="basic-explanation" style={{ display: 'flex' }}>
{localizedReactFragment(
`You are not signed in to Mailspring. Link the app to a free Mailspring ID to use great free features like send later and snoozing, or upgrade to Mailspring Pro for unlimited message translation and more.`
)}
<div
className="btn btn-emphasis"
onClick={this._onLinkIdentity}
style={{ verticalAlign: 'top', flexShrink: 0, marginLeft: 30 }}
>
<RetinaImg name="ic-upgrade.png" mode={RetinaImg.Mode.ContentIsMask} />{' '}
{localized(`Setup Mailspring ID`)}
</div>
</div>
</div>
</div>
<div className="row padded" style={{ paddingTop: 0 }}>
<ExploreMailspringPro />
</div>
</>
);
}
_renderBasicPlan() {
const onLearnMore = () => shell.openExternal('https://getmailspring.com/pro');
return (
<div className="row padded">
@ -188,92 +218,7 @@ class PreferencesIdentity extends React.Component<{}, { identity: IIdentity }> {
`Upgrade to %@ to use all these great features permanently:`,
<a onClick={onLearnMore}>{localized('Mailspring Pro')}</a>
)}
<div className="features">
<ul>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Rich contact profiles`)}
</li>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Follow-up reminders`)}
</li>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Read Receipts`)}
</li>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Link tracking`)}
</li>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Powerful template support`)}
</li>
</ul>
<ul>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Send Later`)}
</li>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Company overviews`)}
</li>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Snooze messages`)}
</li>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Mailbox insights`)}
</li>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`... and much more!`)}
</li>
</ul>
</div>
<ExploreMailspringSmall />
</div>
<div className="subscription-actions">
<div className="pro-feature-ring">
@ -291,6 +236,7 @@ class PreferencesIdentity extends React.Component<{}, { identity: IIdentity }> {
</div>
</div>
</div>
<ExploreMailspringPro />
</div>
);
}
@ -320,25 +266,7 @@ class PreferencesIdentity extends React.Component<{}, { identity: IIdentity }> {
)}
{unpaidNote}
</div>
<div className="feature-explore-title">{localized('Explore Mailspring Pro')}</div>
<div className="feature-explore-grid">
{ProTourFeatures.map(item => (
<a key={item.title} className="feature" href={item.link}>
<div className="popout">
<RetinaImg name="thread-popout.png" mode={RetinaImg.Mode.ContentDark} />
</div>
<h3>
<RetinaImg
name={item.icon}
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{item.title}
</h3>
<p>{item.text}</p>
</a>
))}
</div>
<ExploreMailspringPro />
<div style={{ paddingTop: 15 }}>
<OpenIdentityPageButton
label={localized('Manage Billing')}
@ -353,46 +281,162 @@ class PreferencesIdentity extends React.Component<{}, { identity: IIdentity }> {
render() {
const { identity } = this.state;
const {
firstName,
lastName,
emailAddress,
stripePlan = '',
stripePlanEffective = '',
} = identity;
const logout = () => Actions.logoutMailspringIdentity();
const stripePlan = identity ? identity.stripePlan : null;
return (
<div className="container-identity">
<div className="identity-content-box">
<div className="row padded">
<div className="identity-info">
<RefreshButton />
<div className="name">
{firstName} {lastName}
</div>
<div className="email">{emailAddress}</div>
<div className="identity-actions">
<OpenIdentityPageButton
label={localized('Account Details')}
path="/dashboard"
source="Preferences"
campaign="Dashboard"
/>
<div className="btn minor-width" onClick={logout}>
{localized('Sign Out')}
</div>
</div>
</div>
</div>
{stripePlan === 'Basic'
? this._renderBasic()
: this._renderPaidPlan(stripePlan, stripePlanEffective)}
{identity && <IdentitySummary identity={identity} />}
{!stripePlan
? this._renderNoIdentity()
: stripePlan === 'Basic'
? this._renderBasicPlan()
: this._renderPaidPlan(stripePlan, identity.stripePlanEffective)}
</div>
</div>
);
}
}
const ExploreMailspringSmall: React.FunctionComponent = () => (
<div className="features">
<ul>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Rich contact profiles`)}
</li>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Follow-up reminders`)}
</li>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Read Receipts`)}
</li>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Link tracking`)}
</li>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Powerful template support`)}
</li>
</ul>
<ul>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Send Later`)}
</li>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Company overviews`)}
</li>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Snooze messages`)}
</li>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`Mailbox insights`)}
</li>
<li>
<RetinaImg
name="pro-feature-checkmark.png"
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{localized(`... and much more!`)}
</li>
</ul>
</div>
);
const ExploreMailspringPro: React.FunctionComponent = () => (
<>
<div className="feature-explore-title">{localized('Explore Mailspring Pro')}</div>
<div className="feature-explore-grid">
{ProTourFeatures.map(item => (
<a key={item.title} className="feature" href={item.link}>
<div className="popout">
<RetinaImg name="thread-popout.png" mode={RetinaImg.Mode.ContentDark} />
</div>
<h3>
<RetinaImg
name={item.icon}
style={{ paddingRight: 8 }}
mode={RetinaImg.Mode.ContentDark}
/>
{item.title}
</h3>
<p>{item.text}</p>
</a>
))}
</div>
</>
);
const IdentitySummary: React.FunctionComponent<{ identity: IIdentity }> = props => {
const { firstName, lastName, emailAddress } = props.identity;
const logout = () => Actions.logoutMailspringIdentity();
return (
<div className="row padded">
<div className="identity-info">
<RefreshButton />
<div className="name">
{firstName} {lastName}
</div>
<div className="email">{emailAddress}</div>
<div className="identity-actions">
<OpenIdentityPageButton
label={localized('Account Details')}
path="/dashboard"
source="Preferences"
campaign="Dashboard"
/>
<div className="btn minor-width" onClick={logout}>
{localized('Sign Out')}
</div>
</div>
</div>
</div>
);
};
export default PreferencesIdentity;

View file

@ -139,7 +139,7 @@
}
.feature-explore-grid {
margin-top: 7px;
width: 100%;
margin-right: -20px;
position: relative;
.feature {

View file

@ -31,17 +31,6 @@ export interface IIdentity {
export type IdentityAuthResponse = IIdentity | { skipped: true };
export const EMPTY_IDENTITY: IIdentity = {
id: '',
token: '',
firstName: '',
lastName: '',
emailAddress: '',
stripePlan: 'basic',
stripePlanEffective: '',
featureUsage: {},
};
export const EMPTY_FEATURE_USAGE = {
featureLimitName: 'pro',
period: 'monthly',
@ -140,9 +129,10 @@ class _IdentityStore extends MailspringStore {
* cache and set the token from the keychain.
*/
_onIdentityChanged = async () => {
const next = Object.assign({}, AppEnv.config.get('identity') || {});
next.token = await KeyManager.getPassword(KEYCHAIN_NAME);
this._identity = next;
const value = AppEnv.config.get('identity');
this._identity = value
? { ...value, token: await KeyManager.getPassword(KEYCHAIN_NAME) }
: null;
this.trigger();
};