aboutsummaryrefslogtreecommitdiffstats
path: root/src/stores
diff options
context:
space:
mode:
Diffstat (limited to 'src/stores')
-rw-r--r--src/stores/AppStore.js13
-rw-r--r--src/stores/FeaturesStore.js6
-rw-r--r--src/stores/ServicesStore.js42
-rw-r--r--src/stores/UserStore.js68
-rw-r--r--src/stores/index.js6
5 files changed, 133 insertions, 2 deletions
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js
index 2bfcdf640..61b63d52e 100644
--- a/src/stores/AppStore.js
+++ b/src/stores/AppStore.js
@@ -95,6 +95,7 @@ export default class AppStore extends Store {
95 this._offlineCheck.bind(this), 95 this._offlineCheck.bind(this),
96 this._setLocale.bind(this), 96 this._setLocale.bind(this),
97 this._muteAppHandler.bind(this), 97 this._muteAppHandler.bind(this),
98 this._handleFullScreen.bind(this),
98 ]); 99 ]);
99 } 100 }
100 101
@@ -347,6 +348,8 @@ export default class AppStore extends Store {
347 this.locale = this._getDefaultLocale(); 348 this.locale = this._getDefaultLocale();
348 } 349 }
349 350
351 moment.locale(this.locale);
352
350 debug(`Set locale to "${this.locale}"`); 353 debug(`Set locale to "${this.locale}"`);
351 } 354 }
352 355
@@ -367,6 +370,16 @@ export default class AppStore extends Store {
367 } 370 }
368 } 371 }
369 372
373 _handleFullScreen() {
374 const body = document.querySelector('body');
375
376 if (this.isFullScreen) {
377 body.classList.add('isFullScreen');
378 } else {
379 body.classList.remove('isFullScreen');
380 }
381 }
382
370 // Helpers 383 // Helpers
371 _appStartsCounter() { 384 _appStartsCounter() {
372 this.actions.settings.update({ 385 this.actions.settings.update({
diff --git a/src/stores/FeaturesStore.js b/src/stores/FeaturesStore.js
index e7832088b..cf28b6bec 100644
--- a/src/stores/FeaturesStore.js
+++ b/src/stores/FeaturesStore.js
@@ -16,6 +16,9 @@ import workspaces from '../features/workspaces';
16import shareFranz from '../features/shareFranz'; 16import shareFranz from '../features/shareFranz';
17import announcements from '../features/announcements'; 17import announcements from '../features/announcements';
18import settingsWS from '../features/settingsWS'; 18import settingsWS from '../features/settingsWS';
19import serviceLimit from '../features/serviceLimit';
20import communityRecipes from '../features/communityRecipes';
21import todos from '../features/todos';
19 22
20import { DEFAULT_FEATURES_CONFIG } from '../config'; 23import { DEFAULT_FEATURES_CONFIG } from '../config';
21 24
@@ -75,5 +78,8 @@ export default class FeaturesStore extends Store {
75 shareFranz(this.stores, this.actions); 78 shareFranz(this.stores, this.actions);
76 announcements(this.stores, this.actions); 79 announcements(this.stores, this.actions);
77 settingsWS(this.stores, this.actions); 80 settingsWS(this.stores, this.actions);
81 serviceLimit(this.stores, this.actions);
82 communityRecipes(this.stores, this.actions);
83 todos(this.stores, this.actions);
78 } 84 }
79} 85}
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js
index 6c6b7589f..876851a66 100644
--- a/src/stores/ServicesStore.js
+++ b/src/stores/ServicesStore.js
@@ -12,6 +12,8 @@ import Request from './lib/Request';
12import CachedRequest from './lib/CachedRequest'; 12import CachedRequest from './lib/CachedRequest';
13import { matchRoute } from '../helpers/routing-helpers'; 13import { matchRoute } from '../helpers/routing-helpers';
14import { workspaceStore } from '../features/workspaces'; 14import { workspaceStore } from '../features/workspaces';
15import { serviceLimitStore } from '../features/serviceLimit';
16import { RESTRICTION_TYPES } from '../models/Service';
15 17
16const debug = require('debug')('Franz:ServiceStore'); 18const debug = require('debug')('Franz:ServiceStore');
17 19
@@ -74,6 +76,7 @@ export default class ServicesStore extends Store {
74 this._saveActiveService.bind(this), 76 this._saveActiveService.bind(this),
75 this._logoutReaction.bind(this), 77 this._logoutReaction.bind(this),
76 this._handleMuteSettings.bind(this), 78 this._handleMuteSettings.bind(this),
79 this._restrictServiceAccess.bind(this),
77 ]); 80 ]);
78 81
79 // Just bind this 82 // Just bind this
@@ -97,7 +100,10 @@ export default class ServicesStore extends Store {
97 if (this.stores.user.isLoggedIn) { 100 if (this.stores.user.isLoggedIn) {
98 const services = this.allServicesRequest.execute().result; 101 const services = this.allServicesRequest.execute().result;
99 if (services) { 102 if (services) {
100 return observable(services.slice().slice().sort((a, b) => a.order - b.order)); 103 return observable(services.slice().slice().sort((a, b) => a.order - b.order).map((s, index) => {
104 s.index = index;
105 return s;
106 }));
101 } 107 }
102 } 108 }
103 return []; 109 return [];
@@ -152,6 +158,8 @@ export default class ServicesStore extends Store {
152 158
153 // Actions 159 // Actions
154 @action async _createService({ recipeId, serviceData, redirect = true }) { 160 @action async _createService({ recipeId, serviceData, redirect = true }) {
161 if (serviceLimitStore.userHasReachedServiceLimit) return;
162
155 const data = this._cleanUpTeamIdAndCustomUrl(recipeId, serviceData); 163 const data = this._cleanUpTeamIdAndCustomUrl(recipeId, serviceData);
156 164
157 const response = await this.createServiceRequest.execute(recipeId, data)._promise; 165 const response = await this.createServiceRequest.execute(recipeId, data)._promise;
@@ -430,6 +438,9 @@ export default class ServicesStore extends Store {
430 redirect: false, 438 redirect: false,
431 }); 439 });
432 } 440 }
441 } else if (channel === 'feature:todos') {
442 Object.assign(args[0].data, { serviceId });
443 this.actions.todos.handleHostMessage(args[0]);
433 } 444 }
434 } 445 }
435 446
@@ -666,6 +677,35 @@ export default class ServicesStore extends Store {
666 return serviceData; 677 return serviceData;
667 } 678 }
668 679
680 _restrictServiceAccess() {
681 const { features } = this.stores.features;
682 const { userHasReachedServiceLimit, serviceLimit } = this.stores.serviceLimit;
683
684 this.all.map((service, index) => {
685 if (userHasReachedServiceLimit) {
686 service.isServiceAccessRestricted = index >= serviceLimit;
687
688 if (service.isServiceAccessRestricted) {
689 service.restrictionType = RESTRICTION_TYPES.SERVICE_LIMIT;
690
691 debug('Restricting access to server due to service limit');
692 }
693 }
694
695 if (service.isUsingCustomUrl) {
696 service.isServiceAccessRestricted = !features.isCustomUrlIncludedInCurrentPlan;
697
698 if (service.isServiceAccessRestricted) {
699 service.restrictionType = RESTRICTION_TYPES.CUSTOM_URL;
700
701 debug('Restricting access to server due to custom url');
702 }
703 }
704
705 return service;
706 });
707 }
708
669 // Helper 709 // Helper
670 _initializeServiceRecipeInWebview(serviceId) { 710 _initializeServiceRecipeInWebview(serviceId) {
671 const service = this.one(serviceId); 711 const service = this.one(serviceId);
diff --git a/src/stores/UserStore.js b/src/stores/UserStore.js
index d813e97b1..e1d9672b6 100644
--- a/src/stores/UserStore.js
+++ b/src/stores/UserStore.js
@@ -2,11 +2,15 @@ import { observable, computed, action } from 'mobx';
2import moment from 'moment'; 2import moment from 'moment';
3import jwt from 'jsonwebtoken'; 3import jwt from 'jsonwebtoken';
4import localStorage from 'mobx-localstorage'; 4import localStorage from 'mobx-localstorage';
5import ms from 'ms';
5 6
6import { isDevMode } from '../environment'; 7import { isDevMode } from '../environment';
7import Store from './lib/Store'; 8import Store from './lib/Store';
8import Request from './lib/Request'; 9import Request from './lib/Request';
9import CachedRequest from './lib/CachedRequest'; 10import CachedRequest from './lib/CachedRequest';
11import { sleep } from '../helpers/async-helpers';
12import { getPlan } from '../helpers/plan-helpers';
13import { PLANS } from '../config';
10 14
11const debug = require('debug')('Franz:UserStore'); 15const debug = require('debug')('Franz:UserStore');
12 16
@@ -36,6 +40,8 @@ export default class UserStore extends Store {
36 40
37 @observable passwordRequest = new Request(this.api.user, 'password'); 41 @observable passwordRequest = new Request(this.api.user, 'password');
38 42
43 @observable activateTrialRequest = new Request(this.api.user, 'activateTrial');
44
39 @observable inviteRequest = new Request(this.api.user, 'invite'); 45 @observable inviteRequest = new Request(this.api.user, 'invite');
40 46
41 @observable getUserInfoRequest = new CachedRequest(this.api.user, 'getInfo'); 47 @observable getUserInfoRequest = new CachedRequest(this.api.user, 'getInfo');
@@ -56,7 +62,9 @@ export default class UserStore extends Store {
56 62
57 @observable accountType; 63 @observable accountType;
58 64
59 @observable hasCompletedSignup = null; 65 @observable hasCompletedSignup = false;
66
67 @observable hasActivatedTrial = false;
60 68
61 @observable userData = {}; 69 @observable userData = {};
62 70
@@ -76,6 +84,7 @@ export default class UserStore extends Store {
76 this.actions.user.retrievePassword.listen(this._retrievePassword.bind(this)); 84 this.actions.user.retrievePassword.listen(this._retrievePassword.bind(this));
77 this.actions.user.logout.listen(this._logout.bind(this)); 85 this.actions.user.logout.listen(this._logout.bind(this));
78 this.actions.user.signup.listen(this._signup.bind(this)); 86 this.actions.user.signup.listen(this._signup.bind(this));
87 this.actions.user.activateTrial.listen(this._activateTrial.bind(this));
79 this.actions.user.invite.listen(this._invite.bind(this)); 88 this.actions.user.invite.listen(this._invite.bind(this));
80 this.actions.user.update.listen(this._update.bind(this)); 89 this.actions.user.update.listen(this._update.bind(this));
81 this.actions.user.resetStatus.listen(this._resetStatus.bind(this)); 90 this.actions.user.resetStatus.listen(this._resetStatus.bind(this));
@@ -86,6 +95,7 @@ export default class UserStore extends Store {
86 this.registerReactions([ 95 this.registerReactions([
87 // this._requireAuthenticatedUser, 96 // this._requireAuthenticatedUser,
88 this._getUserData.bind(this), 97 this._getUserData.bind(this),
98 this._resetTrialActivationState.bind(this),
89 ]); 99 ]);
90 } 100 }
91 101
@@ -141,10 +151,34 @@ export default class UserStore extends Store {
141 return this.getUserInfoRequest.execute().result || {}; 151 return this.getUserInfoRequest.execute().result || {};
142 } 152 }
143 153
154 @computed get team() {
155 return this.data.team || null;
156 }
157
144 @computed get isPremium() { 158 @computed get isPremium() {
145 return !!this.data.isPremium; 159 return !!this.data.isPremium;
146 } 160 }
147 161
162 @computed get isPremiumOverride() {
163 return ((!this.team || !this.team.plan) && this.isPremium) || (this.team.state === 'expired' && this.isPremium);
164 }
165
166 @computed get isPersonal() {
167 if (!this.team || !this.team.plan) return false;
168 const plan = getPlan(this.team.plan);
169
170 return plan === PLANS.PERSONAL;
171 }
172
173 @computed get isPro() {
174 if (this.isPremiumOverride) return true;
175
176 if (!this.team || (!this.team.plan || this.team.state === 'expired')) return false;
177 const plan = getPlan(this.team.plan);
178
179 return plan === PLANS.PRO || plan === PLANS.LEGACY;
180 }
181
148 @computed get legacyServices() { 182 @computed get legacyServices() {
149 return this.getLegacyServicesRequest.execute() || {}; 183 return this.getLegacyServicesRequest.execute() || {};
150 } 184 }
@@ -190,6 +224,21 @@ export default class UserStore extends Store {
190 this.actionStatus = request.result.status || []; 224 this.actionStatus = request.result.status || [];
191 } 225 }
192 226
227 @action async _activateTrial({ planId }) {
228 debug('activate trial', planId);
229
230 this.activateTrialRequest.execute({
231 plan: planId,
232 });
233
234 await this.activateTrialRequest._promise;
235
236 this.hasActivatedTrial = true;
237
238 this.stores.features.featuresRequest.invalidate({ immediately: true });
239 this.stores.user.getUserInfoRequest.invalidate({ immediately: true });
240 }
241
193 @action async _invite({ invites }) { 242 @action async _invite({ invites }) {
194 const data = invites.filter(invite => invite.email !== ''); 243 const data = invites.filter(invite => invite.email !== '');
195 244
@@ -305,6 +354,14 @@ export default class UserStore extends Store {
305 } 354 }
306 } 355 }
307 356
357 async _resetTrialActivationState() {
358 if (this.hasActivatedTrial) {
359 await sleep(ms('12s'));
360
361 this.hasActivatedTrial = false;
362 }
363 }
364
308 // Helpers 365 // Helpers
309 _parseToken(authToken) { 366 _parseToken(authToken) {
310 try { 367 try {
@@ -334,6 +391,15 @@ export default class UserStore extends Store {
334 } 391 }
335 } 392 }
336 393
394 getAuthURL(url) {
395 const parsedUrl = new URL(url);
396 const params = new URLSearchParams(parsedUrl.search.slice(1));
397
398 params.append('authToken', this.authToken);
399
400 return `${parsedUrl.origin}${parsedUrl.pathname}?${params.toString()}`;
401 }
402
337 async _migrateUserLocale() { 403 async _migrateUserLocale() {
338 await this.getUserInfoRequest._promise; 404 await this.getUserInfoRequest._promise;
339 405
diff --git a/src/stores/index.js b/src/stores/index.js
index 1912418a2..10dd56665 100644
--- a/src/stores/index.js
+++ b/src/stores/index.js
@@ -12,6 +12,9 @@ import RequestStore from './RequestStore';
12import GlobalErrorStore from './GlobalErrorStore'; 12import GlobalErrorStore from './GlobalErrorStore';
13import { workspaceStore } from '../features/workspaces'; 13import { workspaceStore } from '../features/workspaces';
14import { announcementsStore } from '../features/announcements'; 14import { announcementsStore } from '../features/announcements';
15import { serviceLimitStore } from '../features/serviceLimit';
16import { communityRecipesStore } from '../features/communityRecipes';
17import { todosStore } from '../features/todos';
15 18
16export default (api, actions, router) => { 19export default (api, actions, router) => {
17 const stores = {}; 20 const stores = {};
@@ -31,6 +34,9 @@ export default (api, actions, router) => {
31 globalError: new GlobalErrorStore(stores, api, actions), 34 globalError: new GlobalErrorStore(stores, api, actions),
32 workspaces: workspaceStore, 35 workspaces: workspaceStore,
33 announcements: announcementsStore, 36 announcements: announcementsStore,
37 serviceLimit: serviceLimitStore,
38 communityRecipes: communityRecipesStore,
39 todos: todosStore,
34 }); 40 });
35 // Initialize all stores 41 // Initialize all stores
36 Object.keys(stores).forEach((name) => { 42 Object.keys(stores).forEach((name) => {