aboutsummaryrefslogtreecommitdiffstats
path: root/src/containers
diff options
context:
space:
mode:
authorLibravatar vantezzen <properly@protonmail.com>2019-09-07 15:50:23 +0200
committerLibravatar vantezzen <properly@protonmail.com>2019-09-07 15:50:23 +0200
commite7a74514c1e7c3833dfdcf5900cb87f9e6e8354e (patch)
treeb8314e4155503b135dcb07e8b4a0e847e25c19cf /src/containers
parentUpdate CHANGELOG.md (diff)
parentUpdate CHANGELOG.md (diff)
downloadferdium-app-e7a74514c1e7c3833dfdcf5900cb87f9e6e8354e.tar.gz
ferdium-app-e7a74514c1e7c3833dfdcf5900cb87f9e6e8354e.tar.zst
ferdium-app-e7a74514c1e7c3833dfdcf5900cb87f9e6e8354e.zip
Merge branch 'master' of https://github.com/meetfranz/franz into franz-5.3.0
Diffstat (limited to 'src/containers')
-rw-r--r--src/containers/auth/PricingScreen.js40
-rw-r--r--src/containers/layout/AppLayoutContainer.js6
-rw-r--r--src/containers/settings/AccountScreen.js5
-rw-r--r--src/containers/settings/EditServiceScreen.js4
-rw-r--r--src/containers/settings/EditSettingsScreen.js47
-rw-r--r--src/containers/settings/RecipesScreen.js40
-rw-r--r--src/containers/settings/TeamScreen.js2
-rw-r--r--src/containers/subscription/SubscriptionFormScreen.js99
-rw-r--r--src/containers/subscription/SubscriptionPopupScreen.js3
9 files changed, 146 insertions, 100 deletions
diff --git a/src/containers/auth/PricingScreen.js b/src/containers/auth/PricingScreen.js
index 8d179a170..af1651931 100644
--- a/src/containers/auth/PricingScreen.js
+++ b/src/containers/auth/PricingScreen.js
@@ -5,7 +5,6 @@ import { RouterStore } from 'mobx-react-router';
5 5
6import Pricing from '../../components/auth/Pricing'; 6import Pricing from '../../components/auth/Pricing';
7import UserStore from '../../stores/UserStore'; 7import UserStore from '../../stores/UserStore';
8import PaymentStore from '../../stores/PaymentStore';
9 8
10import { globalError as globalErrorPropType } from '../../prop-types'; 9import { globalError as globalErrorPropType } from '../../prop-types';
11 10
@@ -14,20 +13,40 @@ export default @inject('stores', 'actions') @observer class PricingScreen extend
14 error: globalErrorPropType.isRequired, 13 error: globalErrorPropType.isRequired,
15 }; 14 };
16 15
16 async submit() {
17 const {
18 actions,
19 stores,
20 } = this.props;
21
22 const { activateTrialRequest } = stores.user;
23 const { defaultTrialPlan } = stores.features.features;
24
25 actions.user.activateTrial({ planId: defaultTrialPlan });
26 await activateTrialRequest._promise;
27
28 if (!activateTrialRequest.isError) {
29 stores.router.push('/');
30 stores.user.hasCompletedSignup = true;
31 }
32 }
33
17 render() { 34 render() {
18 const { actions, stores, error } = this.props; 35 const {
36 error,
37 stores,
38 } = this.props;
19 39
20 const nextStepRoute = stores.user.legacyServices.length ? stores.user.importRoute : stores.user.inviteRoute; 40 const { getUserInfoRequest, activateTrialRequest } = stores.user;
41 const { featuresRequest } = stores.features;
21 42
22 return ( 43 return (
23 <Pricing 44 <Pricing
24 donor={stores.user.data.donor || {}} 45 onSubmit={this.submit.bind(this)}
25 onSubmit={actions.user.signup} 46 isLoadingRequiredData={(getUserInfoRequest.isExecuting || !getUserInfoRequest.wasExecuted) || (featuresRequest.isExecuting || !featuresRequest.wasExecuted)}
26 onCloseSubscriptionWindow={() => this.props.stores.router.push(nextStepRoute)} 47 isActivatingTrial={activateTrialRequest.isExecuting}
27 isLoading={stores.payment.plansRequest.isExecuting} 48 trialActivationError={activateTrialRequest.isError}
28 isLoadingUser={stores.user.getUserInfoRequest.isExecuting}
29 error={error} 49 error={error}
30 skipAction={() => this.props.stores.router.push(nextStepRoute)}
31 /> 50 />
32 ); 51 );
33 } 52 }
@@ -36,12 +55,11 @@ export default @inject('stores', 'actions') @observer class PricingScreen extend
36PricingScreen.wrappedComponent.propTypes = { 55PricingScreen.wrappedComponent.propTypes = {
37 actions: PropTypes.shape({ 56 actions: PropTypes.shape({
38 user: PropTypes.shape({ 57 user: PropTypes.shape({
39 signup: PropTypes.func.isRequired, 58 activateTrial: PropTypes.func.isRequired,
40 }).isRequired, 59 }).isRequired,
41 }).isRequired, 60 }).isRequired,
42 stores: PropTypes.shape({ 61 stores: PropTypes.shape({
43 user: PropTypes.instanceOf(UserStore).isRequired, 62 user: PropTypes.instanceOf(UserStore).isRequired,
44 payment: PropTypes.instanceOf(PaymentStore).isRequired,
45 router: PropTypes.instanceOf(RouterStore).isRequired, 63 router: PropTypes.instanceOf(RouterStore).isRequired,
46 }).isRequired, 64 }).isRequired,
47}; 65};
diff --git a/src/containers/layout/AppLayoutContainer.js b/src/containers/layout/AppLayoutContainer.js
index 38ed85986..95fbd109f 100644
--- a/src/containers/layout/AppLayoutContainer.js
+++ b/src/containers/layout/AppLayoutContainer.js
@@ -10,6 +10,7 @@ import FeaturesStore from '../../stores/FeaturesStore';
10import UIStore from '../../stores/UIStore'; 10import UIStore from '../../stores/UIStore';
11import NewsStore from '../../stores/NewsStore'; 11import NewsStore from '../../stores/NewsStore';
12import SettingsStore from '../../stores/SettingsStore'; 12import SettingsStore from '../../stores/SettingsStore';
13import UserStore from '../../stores/UserStore';
13import RequestStore from '../../stores/RequestStore'; 14import RequestStore from '../../stores/RequestStore';
14import GlobalErrorStore from '../../stores/GlobalErrorStore'; 15import GlobalErrorStore from '../../stores/GlobalErrorStore';
15 16
@@ -39,6 +40,7 @@ export default @inject('stores', 'actions') @observer class AppLayoutContainer e
39 settings, 40 settings,
40 globalError, 41 globalError,
41 requests, 42 requests,
43 user,
42 } = this.props.stores; 44 } = this.props.stores;
43 45
44 const { 46 const {
@@ -125,6 +127,8 @@ export default @inject('stores', 'actions') @observer class AppLayoutContainer e
125 reload={reload} 127 reload={reload}
126 openSettings={openSettings} 128 openSettings={openSettings}
127 update={updateService} 129 update={updateService}
130 userHasCompletedSignup={user.hasCompletedSignup}
131 hasActivatedTrial={user.hasActivatedTrial}
128 /> 132 />
129 ); 133 );
130 134
@@ -150,6 +154,7 @@ export default @inject('stores', 'actions') @observer class AppLayoutContainer e
150 retryRequiredRequests={retryRequiredRequests} 154 retryRequiredRequests={retryRequiredRequests}
151 areRequiredRequestsLoading={requests.areRequiredRequestsLoading} 155 areRequiredRequestsLoading={requests.areRequiredRequestsLoading}
152 isDelayAppScreenVisible={delayAppState.isDelayAppScreenVisible} 156 isDelayAppScreenVisible={delayAppState.isDelayAppScreenVisible}
157 hasActivatedTrial={user.hasActivatedTrial}
153 > 158 >
154 {React.Children.count(children) > 0 ? children : null} 159 {React.Children.count(children) > 0 ? children : null}
155 </AppLayout> 160 </AppLayout>
@@ -167,6 +172,7 @@ AppLayoutContainer.wrappedComponent.propTypes = {
167 ui: PropTypes.instanceOf(UIStore).isRequired, 172 ui: PropTypes.instanceOf(UIStore).isRequired,
168 news: PropTypes.instanceOf(NewsStore).isRequired, 173 news: PropTypes.instanceOf(NewsStore).isRequired,
169 settings: PropTypes.instanceOf(SettingsStore).isRequired, 174 settings: PropTypes.instanceOf(SettingsStore).isRequired,
175 user: PropTypes.instanceOf(UserStore).isRequired,
170 requests: PropTypes.instanceOf(RequestStore).isRequired, 176 requests: PropTypes.instanceOf(RequestStore).isRequired,
171 globalError: PropTypes.instanceOf(GlobalErrorStore).isRequired, 177 globalError: PropTypes.instanceOf(GlobalErrorStore).isRequired,
172 }).isRequired, 178 }).isRequired,
diff --git a/src/containers/settings/AccountScreen.js b/src/containers/settings/AccountScreen.js
index fd3317301..41cdeb79a 100644
--- a/src/containers/settings/AccountScreen.js
+++ b/src/containers/settings/AccountScreen.js
@@ -30,7 +30,7 @@ export default @inject('stores', 'actions') @observer class AccountScreen extend
30 30
31 let url; 31 let url;
32 if (api === 'https://api.franzinfra.com') { 32 if (api === 'https://api.franzinfra.com') {
33 url = `${WEBSITE}${route}?authToken=${stores.user.authToken}&utm_source=app&utm_medium=account_dashboard`; 33 url = stores.user.getAuthURL(`${WEBSITE}${route}?utm_source=app&utm_medium=account_dashboard`);
34 } else { 34 } else {
35 url = `${api}${route}`; 35 url = `${api}${route}`;
36 } 36 }
@@ -49,6 +49,8 @@ export default @inject('stores', 'actions') @observer class AccountScreen extend
49 <ErrorBoundary> 49 <ErrorBoundary>
50 <AccountDashboard 50 <AccountDashboard
51 user={user.data} 51 user={user.data}
52 isPremiumOverrideUser={user.isPremiumOverride}
53 isProUser={user.isPro}
52 isLoading={isLoadingUserInfo} 54 isLoading={isLoadingUserInfo}
53 isLoadingPlans={isLoadingPlans} 55 isLoadingPlans={isLoadingPlans}
54 userInfoRequestFailed={user.getUserInfoRequest.wasExecuted && user.getUserInfoRequest.isError} 56 userInfoRequestFailed={user.getUserInfoRequest.wasExecuted && user.getUserInfoRequest.isError}
@@ -58,6 +60,7 @@ export default @inject('stores', 'actions') @observer class AccountScreen extend
58 isLoadingDeleteAccount={user.deleteAccountRequest.isExecuting} 60 isLoadingDeleteAccount={user.deleteAccountRequest.isExecuting}
59 isDeleteAccountSuccessful={user.deleteAccountRequest.wasExecuted && !user.deleteAccountRequest.isError} 61 isDeleteAccountSuccessful={user.deleteAccountRequest.wasExecuted && !user.deleteAccountRequest.isError}
60 openEditAccount={() => this.handleWebsiteLink('/user/profile')} 62 openEditAccount={() => this.handleWebsiteLink('/user/profile')}
63 upgradeToPro={() => this.handleWebsiteLink('/inapp/user/licenses')}
61 openBilling={() => this.handleWebsiteLink('/user/billing')} 64 openBilling={() => this.handleWebsiteLink('/user/billing')}
62 openInvoices={() => this.handleWebsiteLink('/user/invoices')} 65 openInvoices={() => this.handleWebsiteLink('/user/invoices')}
63 /> 66 />
diff --git a/src/containers/settings/EditServiceScreen.js b/src/containers/settings/EditServiceScreen.js
index 870ca4ecd..e4ff03bb3 100644
--- a/src/containers/settings/EditServiceScreen.js
+++ b/src/containers/settings/EditServiceScreen.js
@@ -330,8 +330,8 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
330 onSubmit={d => this.onSubmit(d)} 330 onSubmit={d => this.onSubmit(d)}
331 onDelete={() => this.deleteService()} 331 onDelete={() => this.deleteService()}
332 isProxyFeatureEnabled={proxyFeature.isEnabled} 332 isProxyFeatureEnabled={proxyFeature.isEnabled}
333 isProxyPremiumFeature={proxyFeature.isPremium} 333 isServiceProxyIncludedInCurrentPlan={proxyFeature.isIncludedInCurrentPlan}
334 isSpellcheckerPremiumFeature={spellcheckerFeature.isPremium} 334 isSpellcheckerIncludedInCurrentPlan={spellcheckerFeature.isIncludedInCurrentPlan}
335 /> 335 />
336 </ErrorBoundary> 336 </ErrorBoundary>
337 ); 337 );
diff --git a/src/containers/settings/EditSettingsScreen.js b/src/containers/settings/EditSettingsScreen.js
index 38f73c20f..954f64dc6 100644
--- a/src/containers/settings/EditSettingsScreen.js
+++ b/src/containers/settings/EditSettingsScreen.js
@@ -6,6 +6,7 @@ import { defineMessages, intlShape } from 'react-intl';
6import AppStore from '../../stores/AppStore'; 6import AppStore from '../../stores/AppStore';
7import SettingsStore from '../../stores/SettingsStore'; 7import SettingsStore from '../../stores/SettingsStore';
8import UserStore from '../../stores/UserStore'; 8import UserStore from '../../stores/UserStore';
9import TodosStore from '../../features/todos/store';
9import Form from '../../lib/Form'; 10import Form from '../../lib/Form';
10import { APP_LOCALES, SPELLCHECKER_LOCALES } from '../../i18n/languages'; 11import { APP_LOCALES, SPELLCHECKER_LOCALES } from '../../i18n/languages';
11import { DEFAULT_APP_SETTINGS } from '../../config'; 12import { DEFAULT_APP_SETTINGS } from '../../config';
@@ -19,6 +20,7 @@ import ErrorBoundary from '../../components/util/ErrorBoundary';
19import { API } from '../../environment'; 20import { API } from '../../environment';
20 21
21import globalMessages from '../../i18n/globalMessages'; 22import globalMessages from '../../i18n/globalMessages';
23import { DEFAULT_IS_FEATURE_ENABLED_BY_USER } from '../../features/todos';
22 24
23const messages = defineMessages({ 25const messages = defineMessages({
24 autoLaunchOnStart: { 26 autoLaunchOnStart: {
@@ -73,6 +75,10 @@ const messages = defineMessages({
73 id: 'settings.app.form.beta', 75 id: 'settings.app.form.beta',
74 defaultMessage: '!!!Include beta versions', 76 defaultMessage: '!!!Include beta versions',
75 }, 77 },
78 enableTodos: {
79 id: 'settings.app.form.enableTodos',
80 defaultMessage: '!!!Enable Franz Todos',
81 },
76}); 82});
77 83
78export default @inject('stores', 'actions') @observer class EditSettingsScreen extends Component { 84export default @inject('stores', 'actions') @observer class EditSettingsScreen extends Component {
@@ -81,7 +87,13 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
81 }; 87 };
82 88
83 onSubmit(settingsData) { 89 onSubmit(settingsData) {
84 const { app, settings, user } = this.props.actions; 90 const { todos } = this.props.stores;
91 const {
92 app,
93 settings,
94 user,
95 todos: todosActions,
96 } = this.props.actions;
85 97
86 app.launchOnStartup({ 98 app.launchOnStartup({
87 enable: settingsData.autoLaunchOnStart, 99 enable: settingsData.autoLaunchOnStart,
@@ -112,10 +124,16 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
112 locale: settingsData.locale, 124 locale: settingsData.locale,
113 }, 125 },
114 }); 126 });
127
128 if (todos.isFeatureActive) {
129 todosActions.toggleTodosFeatureVisibility();
130 }
115 } 131 }
116 132
117 prepareForm() { 133 prepareForm() {
118 const { app, settings, user } = this.props.stores; 134 const {
135 app, settings, user, todos,
136 } = this.props.stores;
119 const { intl } = this.context; 137 const { intl } = this.context;
120 138
121 const locales = getSelectOptions({ 139 const locales = getSelectOptions({
@@ -171,8 +189,8 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
171 }, 189 },
172 enableSpellchecking: { 190 enableSpellchecking: {
173 label: intl.formatMessage(messages.enableSpellchecking), 191 label: intl.formatMessage(messages.enableSpellchecking),
174 value: !this.props.stores.user.data.isPremium && spellcheckerConfig.isPremium ? false : settings.all.app.enableSpellchecking, 192 value: !this.props.stores.user.data.isPremium && !spellcheckerConfig.isIncludedInCurrentPlan ? false : settings.all.app.enableSpellchecking,
175 default: !this.props.stores.user.data.isPremium && spellcheckerConfig.isPremium ? false : DEFAULT_APP_SETTINGS.enableSpellchecking, 193 default: !this.props.stores.user.data.isPremium && !spellcheckerConfig.isIncludedInCurrentPlan ? false : DEFAULT_APP_SETTINGS.enableSpellchecking,
176 }, 194 },
177 spellcheckerLanguage: { 195 spellcheckerLanguage: {
178 label: intl.formatMessage(globalMessages.spellcheckerLanguage), 196 label: intl.formatMessage(globalMessages.spellcheckerLanguage),
@@ -204,16 +222,28 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
204 }, 222 },
205 }; 223 };
206 224
225 if (todos.isFeatureActive) {
226 config.fields.enableTodos = {
227 label: intl.formatMessage(messages.enableTodos),
228 value: todos.settings.isFeatureEnabledByUser,
229 default: DEFAULT_IS_FEATURE_ENABLED_BY_USER,
230 };
231 }
232
207 return new Form(config); 233 return new Form(config);
208 } 234 }
209 235
210 render() { 236 render() {
211 const { 237 const {
238 app,
239 todos,
240 } = this.props.stores;
241 const {
212 updateStatus, 242 updateStatus,
213 cacheSize, 243 cacheSize,
214 updateStatusTypes, 244 updateStatusTypes,
215 isClearingAllCache, 245 isClearingAllCache,
216 } = this.props.stores.app; 246 } = app;
217 const { 247 const {
218 checkForUpdates, 248 checkForUpdates,
219 installUpdate, 249 installUpdate,
@@ -235,7 +265,8 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
235 cacheSize={cacheSize} 265 cacheSize={cacheSize}
236 isClearingAllCache={isClearingAllCache} 266 isClearingAllCache={isClearingAllCache}
237 onClearAllCache={clearAllCache} 267 onClearAllCache={clearAllCache}
238 isSpellcheckerPremiumFeature={spellcheckerConfig.isPremium} 268 isSpellcheckerIncludedInCurrentPlan={spellcheckerConfig.isIncludedInCurrentPlan}
269 isTodosEnabled={todos.isFeatureActive}
239 /> 270 />
240 </ErrorBoundary> 271 </ErrorBoundary>
241 ); 272 );
@@ -247,6 +278,7 @@ EditSettingsScreen.wrappedComponent.propTypes = {
247 app: PropTypes.instanceOf(AppStore).isRequired, 278 app: PropTypes.instanceOf(AppStore).isRequired,
248 user: PropTypes.instanceOf(UserStore).isRequired, 279 user: PropTypes.instanceOf(UserStore).isRequired,
249 settings: PropTypes.instanceOf(SettingsStore).isRequired, 280 settings: PropTypes.instanceOf(SettingsStore).isRequired,
281 todos: PropTypes.instanceOf(TodosStore).isRequired,
250 }).isRequired, 282 }).isRequired,
251 actions: PropTypes.shape({ 283 actions: PropTypes.shape({
252 app: PropTypes.shape({ 284 app: PropTypes.shape({
@@ -261,5 +293,8 @@ EditSettingsScreen.wrappedComponent.propTypes = {
261 user: PropTypes.shape({ 293 user: PropTypes.shape({
262 update: PropTypes.func.isRequired, 294 update: PropTypes.func.isRequired,
263 }).isRequired, 295 }).isRequired,
296 todos: PropTypes.shape({
297 toggleTodosFeatureVisibility: PropTypes.func.isRequired,
298 }).isRequired,
264 }).isRequired, 299 }).isRequired,
265}; 300};
diff --git a/src/containers/settings/RecipesScreen.js b/src/containers/settings/RecipesScreen.js
index eda5ae54c..132820b6f 100644
--- a/src/containers/settings/RecipesScreen.js
+++ b/src/containers/settings/RecipesScreen.js
@@ -1,7 +1,9 @@
1import { remote, shell } from 'electron';
1import React, { Component } from 'react'; 2import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 3import PropTypes from 'prop-types';
3import { autorun } from 'mobx'; 4import { autorun } from 'mobx';
4import { inject, observer } from 'mobx-react'; 5import { inject, observer } from 'mobx-react';
6import path from 'path';
5 7
6import RecipePreviewsStore from '../../stores/RecipePreviewsStore'; 8import RecipePreviewsStore from '../../stores/RecipePreviewsStore';
7import RecipeStore from '../../stores/RecipesStore'; 9import RecipeStore from '../../stores/RecipesStore';
@@ -10,6 +12,11 @@ import UserStore from '../../stores/UserStore';
10 12
11import RecipesDashboard from '../../components/settings/recipes/RecipesDashboard'; 13import RecipesDashboard from '../../components/settings/recipes/RecipesDashboard';
12import ErrorBoundary from '../../components/util/ErrorBoundary'; 14import ErrorBoundary from '../../components/util/ErrorBoundary';
15import { FRANZ_DEV_DOCS } from '../../config';
16import { gaEvent } from '../../lib/analytics';
17import { communityRecipesStore } from '../../features/communityRecipes';
18
19const { app } = remote;
13 20
14export default @inject('stores', 'actions') @observer class RecipesScreen extends Component { 21export default @inject('stores', 'actions') @observer class RecipesScreen extends Component {
15 static propTypes = { 22 static propTypes = {
@@ -67,9 +74,16 @@ export default @inject('stores', 'actions') @observer class RecipesScreen extend
67 74
68 render() { 75 render() {
69 const { 76 const {
70 recipePreviews, recipes, services, user, 77 recipePreviews,
78 recipes,
79 services,
80 user,
71 } = this.props.stores; 81 } = this.props.stores;
72 const { showAddServiceInterface } = this.props.actions.service; 82
83 const {
84 app: appActions,
85 service: serviceActions,
86 } = this.props.actions;
73 87
74 const { filter } = this.props.params; 88 const { filter } = this.props.params;
75 let recipeFilter; 89 let recipeFilter;
@@ -77,7 +91,7 @@ export default @inject('stores', 'actions') @observer class RecipesScreen extend
77 if (filter === 'all') { 91 if (filter === 'all') {
78 recipeFilter = recipePreviews.all; 92 recipeFilter = recipePreviews.all;
79 } else if (filter === 'dev') { 93 } else if (filter === 'dev') {
80 recipeFilter = recipePreviews.dev; 94 recipeFilter = communityRecipesStore.communityRecipes;
81 } else { 95 } else {
82 recipeFilter = recipePreviews.featured; 96 recipeFilter = recipePreviews.featured;
83 } 97 }
@@ -89,6 +103,8 @@ export default @inject('stores', 'actions') @observer class RecipesScreen extend
89 || recipes.installRecipeRequest.isExecuting 103 || recipes.installRecipeRequest.isExecuting
90 || recipePreviews.searchRecipePreviewsRequest.isExecuting; 104 || recipePreviews.searchRecipePreviewsRequest.isExecuting;
91 105
106 const recipeDirectory = path.join(app.getPath('userData'), 'recipes', 'dev');
107
92 return ( 108 return (
93 <ErrorBoundary> 109 <ErrorBoundary>
94 <RecipesDashboard 110 <RecipesDashboard
@@ -97,12 +113,23 @@ export default @inject('stores', 'actions') @observer class RecipesScreen extend
97 addedServiceCount={services.all.length} 113 addedServiceCount={services.all.length}
98 isPremium={user.data.isPremium} 114 isPremium={user.data.isPremium}
99 hasLoadedRecipes={recipePreviews.featuredRecipePreviewsRequest.wasExecuted} 115 hasLoadedRecipes={recipePreviews.featuredRecipePreviewsRequest.wasExecuted}
100 showAddServiceInterface={showAddServiceInterface} 116 showAddServiceInterface={serviceActions.showAddServiceInterface}
101 searchRecipes={e => this.searchRecipes(e)} 117 searchRecipes={e => this.searchRecipes(e)}
102 resetSearch={() => this.resetSearch()} 118 resetSearch={() => this.resetSearch()}
103 searchNeedle={this.state.needle} 119 searchNeedle={this.state.needle}
104 serviceStatus={services.actionStatus} 120 serviceStatus={services.actionStatus}
105 devRecipesCount={recipePreviews.dev.length} 121 recipeFilter={filter}
122 recipeDirectory={recipeDirectory}
123 openRecipeDirectory={() => {
124 shell.openItem(recipeDirectory);
125 gaEvent('Recipe', 'open-recipe-folder', 'Open Folder');
126 }}
127 openDevDocs={() => {
128 appActions.openExternalUrl({ url: FRANZ_DEV_DOCS });
129 gaEvent('Recipe', 'open-dev-docs', 'Developer Documentation');
130 }}
131 isCommunityRecipesIncludedInCurrentPlan={communityRecipesStore.isCommunityRecipesIncludedInCurrentPlan}
132 isUserPremiumUser={user.isPremium}
106 /> 133 />
107 </ErrorBoundary> 134 </ErrorBoundary>
108 ); 135 );
@@ -117,6 +144,9 @@ RecipesScreen.wrappedComponent.propTypes = {
117 user: PropTypes.instanceOf(UserStore).isRequired, 144 user: PropTypes.instanceOf(UserStore).isRequired,
118 }).isRequired, 145 }).isRequired,
119 actions: PropTypes.shape({ 146 actions: PropTypes.shape({
147 app: PropTypes.shape({
148 openExternalUrl: PropTypes.func.isRequired,
149 }).isRequired,
120 service: PropTypes.shape({ 150 service: PropTypes.shape({
121 showAddServiceInterface: PropTypes.func.isRequired, 151 showAddServiceInterface: PropTypes.func.isRequired,
122 }).isRequired, 152 }).isRequired,
diff --git a/src/containers/settings/TeamScreen.js b/src/containers/settings/TeamScreen.js
index c69d5ad08..f600c9947 100644
--- a/src/containers/settings/TeamScreen.js
+++ b/src/containers/settings/TeamScreen.js
@@ -14,7 +14,6 @@ export default @inject('stores', 'actions') @observer class TeamScreen extends C
14 const { actions, stores } = this.props; 14 const { actions, stores } = this.props;
15 15
16 const url = `${WEBSITE}${route}?authToken=${stores.user.authToken}&utm_source=app&utm_medium=account_dashboard`; 16 const url = `${WEBSITE}${route}?authToken=${stores.user.authToken}&utm_source=app&utm_medium=account_dashboard`;
17 console.log(url);
18 17
19 actions.app.openExternalUrl({ url }); 18 actions.app.openExternalUrl({ url });
20 } 19 }
@@ -31,6 +30,7 @@ export default @inject('stores', 'actions') @observer class TeamScreen extends C
31 userInfoRequestFailed={user.getUserInfoRequest.wasExecuted && user.getUserInfoRequest.isError} 30 userInfoRequestFailed={user.getUserInfoRequest.wasExecuted && user.getUserInfoRequest.isError}
32 retryUserInfoRequest={() => this.reloadData()} 31 retryUserInfoRequest={() => this.reloadData()}
33 openTeamManagement={() => this.handleWebsiteLink('/user/team')} 32 openTeamManagement={() => this.handleWebsiteLink('/user/team')}
33 isProUser={user.isPro}
34 /> 34 />
35 </ErrorBoundary> 35 </ErrorBoundary>
36 ); 36 );
diff --git a/src/containers/subscription/SubscriptionFormScreen.js b/src/containers/subscription/SubscriptionFormScreen.js
index 3d153b8e8..726b10628 100644
--- a/src/containers/subscription/SubscriptionFormScreen.js
+++ b/src/containers/subscription/SubscriptionFormScreen.js
@@ -1,4 +1,3 @@
1import { remote } from 'electron';
2import React, { Component } from 'react'; 1import React, { Component } from 'react';
3import PropTypes from 'prop-types'; 2import PropTypes from 'prop-types';
4import { inject, observer } from 'mobx-react'; 3import { inject, observer } from 'mobx-react';
@@ -6,96 +5,50 @@ import { inject, observer } from 'mobx-react';
6import PaymentStore from '../../stores/PaymentStore'; 5import PaymentStore from '../../stores/PaymentStore';
7 6
8import SubscriptionForm from '../../components/subscription/SubscriptionForm'; 7import SubscriptionForm from '../../components/subscription/SubscriptionForm';
9 8import TrialForm from '../../components/subscription/TrialForm';
10const { BrowserWindow } = remote;
11 9
12export default @inject('stores', 'actions') @observer class SubscriptionFormScreen extends Component { 10export default @inject('stores', 'actions') @observer class SubscriptionFormScreen extends Component {
13 static propTypes = { 11 async openBrowser() {
14 onCloseWindow: PropTypes.func,
15 content: PropTypes.node,
16 showSkipOption: PropTypes.bool,
17 skipAction: PropTypes.func,
18 skipButtonLabel: PropTypes.string,
19 hideInfo: PropTypes.bool,
20 }
21
22 static defaultProps = {
23 onCloseWindow: () => null,
24 content: '',
25 showSkipOption: false,
26 skipAction: () => null,
27 skipButtonLabel: '',
28 hideInfo: false,
29 }
30
31 async handlePayment(plan) {
32 const { 12 const {
33 actions, 13 actions,
34 stores, 14 stores,
35 onCloseWindow,
36 } = this.props; 15 } = this.props;
37 16
38 const interval = plan; 17 const {
39 18 user,
40 const { id } = stores.payment.plan[interval]; 19 features,
41 actions.payment.createHostedPage({ 20 } = stores;
42 planId: id,
43 });
44
45 const hostedPage = await stores.payment.createHostedPageRequest;
46 21
47 if (hostedPage.url) { 22 let hostedPageURL = features.features.planSelectionURL;
48 if (hostedPage.legacyCheckoutFlow) { 23 hostedPageURL = user.getAuthURL(hostedPageURL);
49 const paymentWindow = new BrowserWindow({
50 parent: remote.getCurrentWindow(),
51 modal: true,
52 title: '🔒 Ferdi Supporter License',
53 width: 600,
54 height: window.innerHeight - 100,
55 maxWidth: 600,
56 minWidth: 600,
57 webPreferences: {
58 nodeIntegration: true,
59 webviewTag: true,
60 },
61 });
62 paymentWindow.loadURL(`file://${__dirname}/../../index.html#/payment/${encodeURIComponent(hostedPage.url)}`);
63 24
64 paymentWindow.on('closed', () => { 25 actions.app.openExternalUrl({ url: hostedPageURL });
65 onCloseWindow();
66 });
67 } else {
68 actions.app.openExternalUrl({
69 url: hostedPage.url,
70 });
71 }
72 }
73 } 26 }
74 27
75 render() { 28 render() {
76 const { 29 const {
77 content,
78 actions, 30 actions,
79 stores, 31 stores,
80 showSkipOption,
81 skipAction,
82 skipButtonLabel,
83 hideInfo,
84 } = this.props; 32 } = this.props;
33
34 const { data: user } = stores.user;
35
36 if (user.hadSubscription) {
37 return (
38 <SubscriptionForm
39 plan={stores.payment.plan}
40 selectPlan={() => this.openBrowser()}
41 isActivatingTrial={stores.user.activateTrialRequest.isExecuting || stores.user.getUserInfoRequest.isExecuting}
42 />
43 );
44 }
45
85 return ( 46 return (
86 <SubscriptionForm 47 <TrialForm
87 plan={stores.payment.plan} 48 plan={stores.payment.plan}
88 isLoading={stores.payment.plansRequest.isExecuting} 49 activateTrial={() => actions.user.activateTrial({ planId: stores.features.features.defaultTrialPlan })}
89 retryPlanRequest={() => stores.payment.plansRequest.reload()} 50 showAllOptions={() => this.openBrowser()}
90 isCreatingHostedPage={stores.payment.createHostedPageRequest.isExecuting} 51 isActivatingTrial={stores.user.activateTrialRequest.isExecuting || stores.user.getUserInfoRequest.isExecuting}
91 handlePayment={price => this.handlePayment(price)}
92 content={content}
93 error={stores.payment.plansRequest.isError}
94 showSkipOption={showSkipOption}
95 skipAction={skipAction}
96 skipButtonLabel={skipButtonLabel}
97 hideInfo={hideInfo}
98 openExternalUrl={actions.app.openExternalUrl}
99 /> 52 />
100 ); 53 );
101 } 54 }
diff --git a/src/containers/subscription/SubscriptionPopupScreen.js b/src/containers/subscription/SubscriptionPopupScreen.js
index f76d6c5a6..0de5a87c4 100644
--- a/src/containers/subscription/SubscriptionPopupScreen.js
+++ b/src/containers/subscription/SubscriptionPopupScreen.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
3import { inject, observer } from 'mobx-react'; 3import { inject, observer } from 'mobx-react';
4 4
5import SubscriptionPopup from '../../components/subscription/SubscriptionPopup'; 5import SubscriptionPopup from '../../components/subscription/SubscriptionPopup';
6import { isDevMode } from '../../environment';
6 7
7 8
8export default @inject('stores', 'actions') @observer class SubscriptionPopupScreen extends Component { 9export default @inject('stores', 'actions') @observer class SubscriptionPopupScreen extends Component {
@@ -13,7 +14,7 @@ export default @inject('stores', 'actions') @observer class SubscriptionPopupScr
13 completeCheck(event) { 14 completeCheck(event) {
14 const { url } = event; 15 const { url } = event;
15 16
16 if ((url.includes('recurly') && url.includes('confirmation')) || (url.includes('meetfranz') && url.includes('success'))) { 17 if ((url.includes('recurly') && url.includes('confirmation')) || ((url.includes('meetfranz') || isDevMode) && url.includes('success'))) {
17 this.setState({ 18 this.setState({
18 complete: true, 19 complete: true,
19 }); 20 });