aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/ui/Subscription.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/ui/Subscription.js')
-rw-r--r--src/components/ui/Subscription.js265
1 files changed, 265 insertions, 0 deletions
diff --git a/src/components/ui/Subscription.js b/src/components/ui/Subscription.js
new file mode 100644
index 000000000..ada5cc3e0
--- /dev/null
+++ b/src/components/ui/Subscription.js
@@ -0,0 +1,265 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { observer, PropTypes as MobxPropTypes } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl';
5
6import Form from '../../lib/Form';
7import Radio from '../ui/Radio';
8import Button from '../ui/Button';
9import Loader from '../ui/Loader';
10
11import { required } from '../../helpers/validation-helpers';
12
13const messages = defineMessages({
14 submitButtonLabel: {
15 id: 'subscription.submit.label',
16 defaultMessage: '!!!Support the development of Franz',
17 },
18 paymentSessionError: {
19 id: 'subscription.paymentSessionError',
20 defaultMessage: '!!!Could not initialize payment form',
21 },
22 typeFree: {
23 id: 'subscription.type.free',
24 defaultMessage: '!!!free',
25 },
26 typeMonthly: {
27 id: 'subscription.type.month',
28 defaultMessage: '!!!month',
29 },
30 typeYearly: {
31 id: 'subscription.type.year',
32 defaultMessage: '!!!year',
33 },
34 typeMining: {
35 id: 'subscription.type.mining',
36 defaultMessage: '!!!Support Franz with processing power',
37 },
38 includedFeatures: {
39 id: 'subscription.includedFeatures',
40 defaultMessage: '!!!The Franz Premium Supporter Account includes',
41 },
42 features: {
43 unlimitedServices: {
44 id: 'subscription.features.unlimitedServices',
45 defaultMessage: '!!!Add unlimited services',
46 },
47 onpremise: {
48 id: 'subscription.features.onpremise',
49 defaultMessage: '!!!Add on-premise/hosted services like HipChat',
50 },
51 customServices: {
52 id: 'subscription.features.customServices',
53 defaultMessage: '!!!Add your custom services',
54 },
55 encryptedSync: {
56 id: 'subscription.features.encryptedSync',
57 defaultMessage: '!!!Encrypted session synchronization',
58 },
59 vpn: {
60 id: 'subscription.features.vpn',
61 defaultMessage: '!!!Proxy & VPN support',
62 },
63 ads: {
64 id: 'subscription.features.ads',
65 defaultMessage: '!!!No ads, ever!',
66 },
67 comingSoon: {
68 id: 'subscription.features.comingSoon',
69 defaultMessage: '!!!coming soon',
70 },
71 },
72 miningHeadline: {
73 id: 'subscription.mining.headline',
74 defaultMessage: '!!!How does this work?',
75 },
76 experimental: {
77 id: 'subscription.mining.experimental',
78 defaultMessage: '!!!experimental',
79 },
80 miningDetail1: {
81 id: 'subscription.mining.line1',
82 defaultMessage: '!!!By enabling "Support with processing power", Franz will use about 20-50% of your CPU to mine cryptocurrency Monero which equals approximately $ 5/year.',
83 },
84 miningDetail2: {
85 id: 'subscription.mining.line2',
86 defaultMessage: '!!!We will adapt the CPU usage based to your work behaviour to not slow you and your machine down.',
87 },
88 miningDetail3: {
89 id: 'subscription.mining.line3',
90 defaultMessage: '!!!As long as the miner is active, you will have unlimited access to all the Franz Premium Supporter Features.',
91 },
92 miningMoreInfo: {
93 id: 'subscription.mining.moreInformation',
94 defaultMessage: '!!!Get more information about this plan',
95 },
96});
97
98@observer
99export default class SubscriptionForm extends Component {
100 static propTypes = {
101 plan: MobxPropTypes.objectOrObservableObject.isRequired,
102 isLoading: PropTypes.bool.isRequired,
103 handlePayment: PropTypes.func.isRequired,
104 retryPlanRequest: PropTypes.func.isRequired,
105 isCreatingHostedPage: PropTypes.bool.isRequired,
106 error: PropTypes.bool.isRequired,
107 showSkipOption: PropTypes.bool,
108 skipAction: PropTypes.func,
109 skipButtonLabel: PropTypes.string,
110 hideInfo: PropTypes.bool.isRequired,
111 openExternalUrl: PropTypes.func.isRequired,
112 };
113
114 static defaultProps ={
115 content: '',
116 showSkipOption: false,
117 skipAction: () => null,
118 skipButtonLabel: '',
119 }
120
121 static contextTypes = {
122 intl: intlShape,
123 };
124
125 componentWillMount() {
126 this.form = this.prepareForm();
127 }
128
129 prepareForm() {
130 const { intl } = this.context;
131
132 const form = {
133 fields: {
134 paymentTier: {
135 value: 'year',
136 validate: [required],
137 options: [{
138 value: 'month',
139 label: `$ ${Object.hasOwnProperty.call(this.props.plan, 'month')
140 ? `${this.props.plan.month.price} / ${intl.formatMessage(messages.typeMonthly)}`
141 : 'monthly'}`,
142 }, {
143 value: 'year',
144 label: `$ ${Object.hasOwnProperty.call(this.props.plan, 'year')
145 ? `${this.props.plan.year.price} / ${intl.formatMessage(messages.typeYearly)}`
146 : 'yearly'}`,
147 }, {
148 value: 'mining',
149 label: intl.formatMessage(messages.typeMining),
150 }],
151 },
152 },
153 };
154
155 if (this.props.showSkipOption) {
156 form.fields.paymentTier.options.unshift({
157 value: 'skip',
158 label: `$ 0 / ${intl.formatMessage(messages.typeFree)}`,
159 });
160 }
161
162 return new Form(form, this.context.intl);
163 }
164
165 render() {
166 const {
167 isLoading,
168 isCreatingHostedPage,
169 handlePayment,
170 retryPlanRequest,
171 error,
172 showSkipOption,
173 skipAction,
174 skipButtonLabel,
175 hideInfo,
176 openExternalUrl,
177 } = this.props;
178 const { intl } = this.context;
179
180 if (error) {
181 return (
182 <Button
183 label="Reload"
184 onClick={retryPlanRequest}
185 isLoaded={!isLoading}
186 />
187 );
188 }
189
190 return (
191 <Loader loaded={!isLoading}>
192 <Radio field={this.form.$('paymentTier')} showLabel={false} className="paymentTiers" />
193 {!hideInfo && (
194 <div className="subscription__premium-info">
195 {this.form.$('paymentTier').value !== 'mining' && (
196 <div>
197 <p>
198 <strong>{intl.formatMessage(messages.includedFeatures)}</strong>
199 </p>
200 <div className="subscription">
201 <ul className="subscription__premium-features">
202 <li>{intl.formatMessage(messages.features.onpremise)}</li>
203 <li>
204 {intl.formatMessage(messages.features.encryptedSync)}
205 <span className="badge">{intl.formatMessage(messages.features.comingSoon)}</span>
206 </li>
207 <li>
208 {intl.formatMessage(messages.features.customServices)}
209 <span className="badge">{intl.formatMessage(messages.features.comingSoon)}</span>
210 </li>
211 <li>
212 {intl.formatMessage(messages.features.vpn)}
213 <span className="badge">{intl.formatMessage(messages.features.comingSoon)}</span>
214 </li>
215 <li>
216 {intl.formatMessage(messages.features.ads)}
217 </li>
218 </ul>
219 </div>
220 </div>
221 )}
222 {this.form.$('paymentTier').value === 'mining' && (
223 <div className="subscription mining-details">
224 <p>
225 <strong>{intl.formatMessage(messages.miningHeadline)}</strong>
226 &nbsp;
227 <span className="badge">{intl.formatMessage(messages.experimental)}</span>
228 </p>
229 <p>{intl.formatMessage(messages.miningDetail1)}</p>
230 <p>{intl.formatMessage(messages.miningDetail2)}</p>
231 <p>{intl.formatMessage(messages.miningDetail3)}</p>
232 <p>
233 <button
234 onClick={() => openExternalUrl({ url: 'http://meetfranz.com/mining' })}
235 >
236 {intl.formatMessage(messages.miningMoreInfo)}
237 </button>
238 </p>
239 </div>
240 )}
241 </div>
242 )}
243 <div>
244 {error.code === 'no-payment-session' && (
245 <p className="error-message center">{intl.formatMessage(messages.paymentSessionError)}</p>
246 )}
247 </div>
248 {showSkipOption && this.form.$('paymentTier').value === 'skip' ? (
249 <Button
250 label={skipButtonLabel}
251 className="auth__button"
252 onClick={skipAction}
253 />
254 ) : (
255 <Button
256 label={intl.formatMessage(messages.submitButtonLabel)}
257 className="auth__button"
258 loaded={!isCreatingHostedPage}
259 onClick={() => handlePayment(this.form.$('paymentTier').value)}
260 />
261 )}
262 </Loader>
263 );
264 }
265}