aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/ui/src/infobox/index.tsx2
-rw-r--r--src/components/settings/recipes/RecipesDashboard.js2
-rw-r--r--src/components/settings/services/EditServiceForm.js5
-rw-r--r--src/components/settings/services/ServicesDashboard.js2
-rw-r--r--src/features/serviceLimit/components/LimitReachedInfobox.js74
-rw-r--r--src/features/serviceLimit/index.js33
-rw-r--r--src/features/serviceLimit/store.js41
-rw-r--r--src/i18n/locales/defaultMessages.json205
-rw-r--r--src/i18n/locales/en-US.json3
-rw-r--r--src/i18n/messages/src/components/settings/recipes/RecipesDashboard.json32
-rw-r--r--src/i18n/messages/src/components/settings/services/EditServiceForm.json88
-rw-r--r--src/i18n/messages/src/components/settings/services/ServicesDashboard.json36
-rw-r--r--src/i18n/messages/src/features/serviceLimit/components/AnnouncementScreen.json15
-rw-r--r--src/i18n/messages/src/features/serviceLimit/components/LimitReachedInfobox.json28
-rw-r--r--src/stores/FeaturesStore.js2
-rw-r--r--src/stores/ServicesStore.js3
-rw-r--r--src/stores/index.js2
17 files changed, 414 insertions, 159 deletions
diff --git a/packages/ui/src/infobox/index.tsx b/packages/ui/src/infobox/index.tsx
index 9066a623e..09fc4596a 100644
--- a/packages/ui/src/infobox/index.tsx
+++ b/packages/ui/src/infobox/index.tsx
@@ -48,13 +48,13 @@ const styles = (theme: Theme) => ({
48 position: 'relative', 48 position: 'relative',
49 overflow: 'hidden', 49 overflow: 'hidden',
50 height: 'auto', 50 height: 'auto',
51 marginBottom: 30,
51 }, 52 },
52 infobox: { 53 infobox: {
53 alignItems: 'center', 54 alignItems: 'center',
54 borderRadius: theme.borderRadiusSmall, 55 borderRadius: theme.borderRadiusSmall,
55 display: 'flex', 56 display: 'flex',
56 height: 'auto', 57 height: 'auto',
57 marginBottom: 30,
58 padding: '15px 20px', 58 padding: '15px 20px',
59 top: 0, 59 top: 0,
60 transition: 'all 0.5s', 60 transition: 'all 0.5s',
diff --git a/src/components/settings/recipes/RecipesDashboard.js b/src/components/settings/recipes/RecipesDashboard.js
index 00cd725cf..862bd4a27 100644
--- a/src/components/settings/recipes/RecipesDashboard.js
+++ b/src/components/settings/recipes/RecipesDashboard.js
@@ -10,6 +10,7 @@ import RecipeItem from './RecipeItem';
10import Loader from '../../ui/Loader'; 10import Loader from '../../ui/Loader';
11import Appear from '../../ui/effects/Appear'; 11import Appear from '../../ui/effects/Appear';
12import { FRANZ_SERVICE_REQUEST } from '../../../config'; 12import { FRANZ_SERVICE_REQUEST } from '../../../config';
13import LimitReachedInfobox from '../../../features/serviceLimit/components/LimitReachedInfobox';
13 14
14const messages = defineMessages({ 15const messages = defineMessages({
15 headline: { 16 headline: {
@@ -86,6 +87,7 @@ export default @observer class RecipesDashboard extends Component {
86 <div className="settings__header"> 87 <div className="settings__header">
87 <h1>{intl.formatMessage(messages.headline)}</h1> 88 <h1>{intl.formatMessage(messages.headline)}</h1>
88 </div> 89 </div>
90 <LimitReachedInfobox />
89 <div className="settings__body recipes"> 91 <div className="settings__body recipes">
90 {serviceStatus.length > 0 && serviceStatus.includes('created') && ( 92 {serviceStatus.length > 0 && serviceStatus.includes('created') && (
91 <Appear> 93 <Appear>
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js
index 4ba2eb844..c089a1582 100644
--- a/src/components/settings/services/EditServiceForm.js
+++ b/src/components/settings/services/EditServiceForm.js
@@ -17,6 +17,8 @@ import ImageUpload from '../../ui/ImageUpload';
17import Select from '../../ui/Select'; 17import Select from '../../ui/Select';
18 18
19import PremiumFeatureContainer from '../../ui/PremiumFeatureContainer'; 19import PremiumFeatureContainer from '../../ui/PremiumFeatureContainer';
20import LimitReachedInfobox from '../../../features/serviceLimit/components/LimitReachedInfobox';
21import { serviceLimitStore } from '../../../features/serviceLimit';
20 22
21const messages = defineMessages({ 23const messages = defineMessages({
22 saveService: { 24 saveService: {
@@ -252,6 +254,7 @@ export default @observer class EditServiceForm extends Component {
252 )} 254 )}
253 </span> 255 </span>
254 </div> 256 </div>
257 <LimitReachedInfobox />
255 <div className="settings__body"> 258 <div className="settings__body">
256 <form onSubmit={e => this.submit(e)} id="form"> 259 <form onSubmit={e => this.submit(e)} id="form">
257 <div className="service-name"> 260 <div className="service-name">
@@ -418,7 +421,7 @@ export default @observer class EditServiceForm extends Component {
418 type="submit" 421 type="submit"
419 label={intl.formatMessage(messages.saveService)} 422 label={intl.formatMessage(messages.saveService)}
420 htmlForm="form" 423 htmlForm="form"
421 disabled={action !== 'edit' && form.isPristine && requiresUserInput} 424 disabled={action !== 'edit' && ((form.isPristine && requiresUserInput) || serviceLimitStore.userHasReachedServiceLimit)}
422 /> 425 />
423 )} 426 )}
424 </div> 427 </div>
diff --git a/src/components/settings/services/ServicesDashboard.js b/src/components/settings/services/ServicesDashboard.js
index 53bae12df..78038e86a 100644
--- a/src/components/settings/services/ServicesDashboard.js
+++ b/src/components/settings/services/ServicesDashboard.js
@@ -9,6 +9,7 @@ import Infobox from '../../ui/Infobox';
9import Loader from '../../ui/Loader'; 9import Loader from '../../ui/Loader';
10import ServiceItem from './ServiceItem'; 10import ServiceItem from './ServiceItem';
11import Appear from '../../ui/effects/Appear'; 11import Appear from '../../ui/effects/Appear';
12import LimitReachedInfobox from '../../../features/serviceLimit/components/LimitReachedInfobox';
12 13
13const messages = defineMessages({ 14const messages = defineMessages({
14 headline: { 15 headline: {
@@ -91,6 +92,7 @@ export default @observer class ServicesDashboard extends Component {
91 <div className="settings__header"> 92 <div className="settings__header">
92 <h1>{intl.formatMessage(messages.headline)}</h1> 93 <h1>{intl.formatMessage(messages.headline)}</h1>
93 </div> 94 </div>
95 <LimitReachedInfobox />
94 <div className="settings__body"> 96 <div className="settings__body">
95 {!isLoading && ( 97 {!isLoading && (
96 <SearchInput 98 <SearchInput
diff --git a/src/features/serviceLimit/components/LimitReachedInfobox.js b/src/features/serviceLimit/components/LimitReachedInfobox.js
new file mode 100644
index 000000000..ee0d7cb27
--- /dev/null
+++ b/src/features/serviceLimit/components/LimitReachedInfobox.js
@@ -0,0 +1,74 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { inject, observer } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl';
5import injectSheet from 'react-jss';
6import { Infobox } from '@meetfranz/ui';
7
8import { gaEvent } from '../../../lib/analytics';
9
10const messages = defineMessages({
11 limitReached: {
12 id: 'feature.serviceLimit.limitReached',
13 defaultMessage: '!!!You have added {amount} of {limit} services. Please upgrade your account to add more services.',
14 },
15 action: {
16 id: 'premiumFeature.button.upgradeAccount',
17 defaultMessage: '!!!Upgrade account',
18 },
19});
20
21const styles = theme => ({
22 container: {
23 height: 'auto',
24 background: theme.styleTypes.primary.accent,
25 color: theme.styleTypes.primary.contrast,
26 borderRadius: 0,
27 marginBottom: 0,
28
29 '& button': {
30 color: theme.styleTypes.primary.contrast,
31 },
32 },
33});
34
35
36@inject('stores', 'actions') @injectSheet(styles) @observer
37class LimitReachedInfobox extends Component {
38 static propTypes = {
39 classes: PropTypes.object.isRequired,
40 stores: PropTypes.object.isRequired,
41 actions: PropTypes.object.isRequired,
42 };
43
44 static contextTypes = {
45 intl: intlShape,
46 };
47
48 render() {
49 const { classes, stores, actions } = this.props;
50 const { intl } = this.context;
51
52 const {
53 serviceLimit,
54 } = stores;
55
56 if (!serviceLimit.userHasReachedServiceLimit) return null;
57
58 return (
59 <Infobox
60 icon="mdiInformation"
61 className={classes.container}
62 ctaLabel={intl.formatMessage(messages.action)}
63 ctaOnClick={() => {
64 actions.ui.openSettings({ path: 'user' });
65 gaEvent('Service Limit', 'upgrade', 'Upgrade account');
66 }}
67 >
68 {intl.formatMessage(messages.limitReached, { amount: serviceLimit.serviceCount, limit: serviceLimit.serviceLimit })}
69 </Infobox>
70 );
71 }
72}
73
74export default LimitReachedInfobox;
diff --git a/src/features/serviceLimit/index.js b/src/features/serviceLimit/index.js
new file mode 100644
index 000000000..76f996195
--- /dev/null
+++ b/src/features/serviceLimit/index.js
@@ -0,0 +1,33 @@
1import { reaction } from 'mobx';
2import { ServiceLimitStore } from './store';
3
4const debug = require('debug')('Franz:feature:serviceLimit');
5
6export const DEFAULT_SERVICE_LIMIT = 3;
7
8let store = null;
9
10export const serviceLimitStore = new ServiceLimitStore();
11
12export default function initServiceLimit(stores, actions) {
13 const { features } = stores;
14
15 // Toggle serviceLimit feature
16 reaction(
17 () => (
18 features.features.hasServiceLimit
19 ),
20 (isEnabled) => {
21 if (isEnabled) {
22 debug('Initializing `serviceLimit` feature');
23 store = serviceLimitStore.start(stores, actions);
24 } else if (store) {
25 debug('Disabling `serviceLimit` feature');
26 serviceLimitStore.stop();
27 }
28 },
29 {
30 fireImmediately: true,
31 },
32 );
33}
diff --git a/src/features/serviceLimit/store.js b/src/features/serviceLimit/store.js
new file mode 100644
index 000000000..752f71371
--- /dev/null
+++ b/src/features/serviceLimit/store.js
@@ -0,0 +1,41 @@
1import { computed, observable } from 'mobx';
2import { FeatureStore } from '../utils/FeatureStore';
3import { DEFAULT_SERVICE_LIMIT } from '.';
4
5const debug = require('debug')('Franz:feature:serviceLimit:store');
6
7export class ServiceLimitStore extends FeatureStore {
8 @observable isServiceLimitEnabled = false;
9
10 start(stores, actions) {
11 debug('start');
12 this.stores = stores;
13 this.actions = actions;
14
15 this.isServiceLimitEnabled = true;
16 }
17
18 stop() {
19 super.stop();
20
21 this.isServiceLimitEnabled = false;
22 }
23
24 @computed get userHasReachedServiceLimit() {
25 if (!this.isServiceLimitEnabled) return false;
26
27 const { user } = this.stores;
28
29 return !user.isPremium && this.serviceCount >= this.serviceLimit;
30 }
31
32 @computed get serviceLimit() {
33 return this.stores.features.features.serviceLimitCount || DEFAULT_SERVICE_LIMIT;
34 }
35
36 @computed get serviceCount() {
37 return this.stores.services.all.length;
38 }
39}
40
41export default ServiceLimitStore;
diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json
index d517b456b..a5572bd32 100644
--- a/src/i18n/locales/defaultMessages.json
+++ b/src/i18n/locales/defaultMessages.json
@@ -1416,104 +1416,104 @@
1416 "defaultMessage": "!!!Available Services", 1416 "defaultMessage": "!!!Available Services",
1417 "end": { 1417 "end": {
1418 "column": 3, 1418 "column": 3,
1419 "line": 18 1419 "line": 19
1420 }, 1420 },
1421 "file": "src/components/settings/recipes/RecipesDashboard.js", 1421 "file": "src/components/settings/recipes/RecipesDashboard.js",
1422 "id": "settings.recipes.headline", 1422 "id": "settings.recipes.headline",
1423 "start": { 1423 "start": {
1424 "column": 12, 1424 "column": 12,
1425 "line": 15 1425 "line": 16
1426 } 1426 }
1427 }, 1427 },
1428 { 1428 {
1429 "defaultMessage": "!!!Search service", 1429 "defaultMessage": "!!!Search service",
1430 "end": { 1430 "end": {
1431 "column": 3, 1431 "column": 3,
1432 "line": 22 1432 "line": 23
1433 }, 1433 },
1434 "file": "src/components/settings/recipes/RecipesDashboard.js", 1434 "file": "src/components/settings/recipes/RecipesDashboard.js",
1435 "id": "settings.searchService", 1435 "id": "settings.searchService",
1436 "start": { 1436 "start": {
1437 "column": 17, 1437 "column": 17,
1438 "line": 19 1438 "line": 20
1439 } 1439 }
1440 }, 1440 },
1441 { 1441 {
1442 "defaultMessage": "!!!Most popular", 1442 "defaultMessage": "!!!Most popular",
1443 "end": { 1443 "end": {
1444 "column": 3, 1444 "column": 3,
1445 "line": 26 1445 "line": 27
1446 }, 1446 },
1447 "file": "src/components/settings/recipes/RecipesDashboard.js", 1447 "file": "src/components/settings/recipes/RecipesDashboard.js",
1448 "id": "settings.recipes.mostPopular", 1448 "id": "settings.recipes.mostPopular",
1449 "start": { 1449 "start": {
1450 "column": 22, 1450 "column": 22,
1451 "line": 23 1451 "line": 24
1452 } 1452 }
1453 }, 1453 },
1454 { 1454 {
1455 "defaultMessage": "!!!All services", 1455 "defaultMessage": "!!!All services",
1456 "end": { 1456 "end": {
1457 "column": 3, 1457 "column": 3,
1458 "line": 30 1458 "line": 31
1459 }, 1459 },
1460 "file": "src/components/settings/recipes/RecipesDashboard.js", 1460 "file": "src/components/settings/recipes/RecipesDashboard.js",
1461 "id": "settings.recipes.all", 1461 "id": "settings.recipes.all",
1462 "start": { 1462 "start": {
1463 "column": 14, 1463 "column": 14,
1464 "line": 27 1464 "line": 28
1465 } 1465 }
1466 }, 1466 },
1467 { 1467 {
1468 "defaultMessage": "!!!Development", 1468 "defaultMessage": "!!!Development",
1469 "end": { 1469 "end": {
1470 "column": 3, 1470 "column": 3,
1471 "line": 34 1471 "line": 35
1472 }, 1472 },
1473 "file": "src/components/settings/recipes/RecipesDashboard.js", 1473 "file": "src/components/settings/recipes/RecipesDashboard.js",
1474 "id": "settings.recipes.dev", 1474 "id": "settings.recipes.dev",
1475 "start": { 1475 "start": {
1476 "column": 14, 1476 "column": 14,
1477 "line": 31 1477 "line": 32
1478 } 1478 }
1479 }, 1479 },
1480 { 1480 {
1481 "defaultMessage": "!!!Sorry, but no service matched your search term.", 1481 "defaultMessage": "!!!Sorry, but no service matched your search term.",
1482 "end": { 1482 "end": {
1483 "column": 3, 1483 "column": 3,
1484 "line": 38 1484 "line": 39
1485 }, 1485 },
1486 "file": "src/components/settings/recipes/RecipesDashboard.js", 1486 "file": "src/components/settings/recipes/RecipesDashboard.js",
1487 "id": "settings.recipes.nothingFound", 1487 "id": "settings.recipes.nothingFound",
1488 "start": { 1488 "start": {
1489 "column": 16, 1489 "column": 16,
1490 "line": 35 1490 "line": 36
1491 } 1491 }
1492 }, 1492 },
1493 { 1493 {
1494 "defaultMessage": "!!!Service successfully added", 1494 "defaultMessage": "!!!Service successfully added",
1495 "end": { 1495 "end": {
1496 "column": 3, 1496 "column": 3,
1497 "line": 42 1497 "line": 43
1498 }, 1498 },
1499 "file": "src/components/settings/recipes/RecipesDashboard.js", 1499 "file": "src/components/settings/recipes/RecipesDashboard.js",
1500 "id": "settings.recipes.servicesSuccessfulAddedInfo", 1500 "id": "settings.recipes.servicesSuccessfulAddedInfo",
1501 "start": { 1501 "start": {
1502 "column": 31, 1502 "column": 31,
1503 "line": 39 1503 "line": 40
1504 } 1504 }
1505 }, 1505 },
1506 { 1506 {
1507 "defaultMessage": "!!!Missing a service?", 1507 "defaultMessage": "!!!Missing a service?",
1508 "end": { 1508 "end": {
1509 "column": 3, 1509 "column": 3,
1510 "line": 46 1510 "line": 47
1511 }, 1511 },
1512 "file": "src/components/settings/recipes/RecipesDashboard.js", 1512 "file": "src/components/settings/recipes/RecipesDashboard.js",
1513 "id": "settings.recipes.missingService", 1513 "id": "settings.recipes.missingService",
1514 "start": { 1514 "start": {
1515 "column": 18, 1515 "column": 18,
1516 "line": 43 1516 "line": 44
1517 } 1517 }
1518 } 1518 }
1519 ], 1519 ],
@@ -1525,286 +1525,286 @@
1525 "defaultMessage": "!!!Save service", 1525 "defaultMessage": "!!!Save service",
1526 "end": { 1526 "end": {
1527 "column": 3, 1527 "column": 3,
1528 "line": 25 1528 "line": 26
1529 }, 1529 },
1530 "file": "src/components/settings/services/EditServiceForm.js", 1530 "file": "src/components/settings/services/EditServiceForm.js",
1531 "id": "settings.service.form.saveButton", 1531 "id": "settings.service.form.saveButton",
1532 "start": { 1532 "start": {
1533 "column": 15, 1533 "column": 15,
1534 "line": 22 1534 "line": 23
1535 } 1535 }
1536 }, 1536 },
1537 { 1537 {
1538 "defaultMessage": "!!!Delete Service", 1538 "defaultMessage": "!!!Delete Service",
1539 "end": { 1539 "end": {
1540 "column": 3, 1540 "column": 3,
1541 "line": 29 1541 "line": 30
1542 }, 1542 },
1543 "file": "src/components/settings/services/EditServiceForm.js", 1543 "file": "src/components/settings/services/EditServiceForm.js",
1544 "id": "settings.service.form.deleteButton", 1544 "id": "settings.service.form.deleteButton",
1545 "start": { 1545 "start": {
1546 "column": 17, 1546 "column": 17,
1547 "line": 26 1547 "line": 27
1548 } 1548 }
1549 }, 1549 },
1550 { 1550 {
1551 "defaultMessage": "!!!Available services", 1551 "defaultMessage": "!!!Available services",
1552 "end": { 1552 "end": {
1553 "column": 3, 1553 "column": 3,
1554 "line": 33 1554 "line": 34
1555 }, 1555 },
1556 "file": "src/components/settings/services/EditServiceForm.js", 1556 "file": "src/components/settings/services/EditServiceForm.js",
1557 "id": "settings.service.form.availableServices", 1557 "id": "settings.service.form.availableServices",
1558 "start": { 1558 "start": {
1559 "column": 21, 1559 "column": 21,
1560 "line": 30 1560 "line": 31
1561 } 1561 }
1562 }, 1562 },
1563 { 1563 {
1564 "defaultMessage": "!!!Your services", 1564 "defaultMessage": "!!!Your services",
1565 "end": { 1565 "end": {
1566 "column": 3, 1566 "column": 3,
1567 "line": 37 1567 "line": 38
1568 }, 1568 },
1569 "file": "src/components/settings/services/EditServiceForm.js", 1569 "file": "src/components/settings/services/EditServiceForm.js",
1570 "id": "settings.service.form.yourServices", 1570 "id": "settings.service.form.yourServices",
1571 "start": { 1571 "start": {
1572 "column": 16, 1572 "column": 16,
1573 "line": 34 1573 "line": 35
1574 } 1574 }
1575 }, 1575 },
1576 { 1576 {
1577 "defaultMessage": "!!!Add {name}", 1577 "defaultMessage": "!!!Add {name}",
1578 "end": { 1578 "end": {
1579 "column": 3, 1579 "column": 3,
1580 "line": 41 1580 "line": 42
1581 }, 1581 },
1582 "file": "src/components/settings/services/EditServiceForm.js", 1582 "file": "src/components/settings/services/EditServiceForm.js",
1583 "id": "settings.service.form.addServiceHeadline", 1583 "id": "settings.service.form.addServiceHeadline",
1584 "start": { 1584 "start": {
1585 "column": 22, 1585 "column": 22,
1586 "line": 38 1586 "line": 39
1587 } 1587 }
1588 }, 1588 },
1589 { 1589 {
1590 "defaultMessage": "!!!Edit {name}", 1590 "defaultMessage": "!!!Edit {name}",
1591 "end": { 1591 "end": {
1592 "column": 3, 1592 "column": 3,
1593 "line": 45 1593 "line": 46
1594 }, 1594 },
1595 "file": "src/components/settings/services/EditServiceForm.js", 1595 "file": "src/components/settings/services/EditServiceForm.js",
1596 "id": "settings.service.form.editServiceHeadline", 1596 "id": "settings.service.form.editServiceHeadline",
1597 "start": { 1597 "start": {
1598 "column": 23, 1598 "column": 23,
1599 "line": 42 1599 "line": 43
1600 } 1600 }
1601 }, 1601 },
1602 { 1602 {
1603 "defaultMessage": "!!!Hosted", 1603 "defaultMessage": "!!!Hosted",
1604 "end": { 1604 "end": {
1605 "column": 3, 1605 "column": 3,
1606 "line": 49 1606 "line": 50
1607 }, 1607 },
1608 "file": "src/components/settings/services/EditServiceForm.js", 1608 "file": "src/components/settings/services/EditServiceForm.js",
1609 "id": "settings.service.form.tabHosted", 1609 "id": "settings.service.form.tabHosted",
1610 "start": { 1610 "start": {
1611 "column": 13, 1611 "column": 13,
1612 "line": 46 1612 "line": 47
1613 } 1613 }
1614 }, 1614 },
1615 { 1615 {
1616 "defaultMessage": "!!!Self hosted ⭐️", 1616 "defaultMessage": "!!!Self hosted ⭐️",
1617 "end": { 1617 "end": {
1618 "column": 3, 1618 "column": 3,
1619 "line": 53 1619 "line": 54
1620 }, 1620 },
1621 "file": "src/components/settings/services/EditServiceForm.js", 1621 "file": "src/components/settings/services/EditServiceForm.js",
1622 "id": "settings.service.form.tabOnPremise", 1622 "id": "settings.service.form.tabOnPremise",
1623 "start": { 1623 "start": {
1624 "column": 16, 1624 "column": 16,
1625 "line": 50 1625 "line": 51
1626 } 1626 }
1627 }, 1627 },
1628 { 1628 {
1629 "defaultMessage": "!!!Use the hosted {name} service.", 1629 "defaultMessage": "!!!Use the hosted {name} service.",
1630 "end": { 1630 "end": {
1631 "column": 3, 1631 "column": 3,
1632 "line": 57 1632 "line": 58
1633 }, 1633 },
1634 "file": "src/components/settings/services/EditServiceForm.js", 1634 "file": "src/components/settings/services/EditServiceForm.js",
1635 "id": "settings.service.form.useHostedService", 1635 "id": "settings.service.form.useHostedService",
1636 "start": { 1636 "start": {
1637 "column": 20, 1637 "column": 20,
1638 "line": 54 1638 "line": 55
1639 } 1639 }
1640 }, 1640 },
1641 { 1641 {
1642 "defaultMessage": "!!!Could not validate custom {name} server.", 1642 "defaultMessage": "!!!Could not validate custom {name} server.",
1643 "end": { 1643 "end": {
1644 "column": 3, 1644 "column": 3,
1645 "line": 61 1645 "line": 62
1646 }, 1646 },
1647 "file": "src/components/settings/services/EditServiceForm.js", 1647 "file": "src/components/settings/services/EditServiceForm.js",
1648 "id": "settings.service.form.customUrlValidationError", 1648 "id": "settings.service.form.customUrlValidationError",
1649 "start": { 1649 "start": {
1650 "column": 28, 1650 "column": 28,
1651 "line": 58 1651 "line": 59
1652 } 1652 }
1653 }, 1653 },
1654 { 1654 {
1655 "defaultMessage": "!!!To add self hosted services, you need a Franz Premium Supporter Account.", 1655 "defaultMessage": "!!!To add self hosted services, you need a Franz Premium Supporter Account.",
1656 "end": { 1656 "end": {
1657 "column": 3, 1657 "column": 3,
1658 "line": 65 1658 "line": 66
1659 }, 1659 },
1660 "file": "src/components/settings/services/EditServiceForm.js", 1660 "file": "src/components/settings/services/EditServiceForm.js",
1661 "id": "settings.service.form.customUrlPremiumInfo", 1661 "id": "settings.service.form.customUrlPremiumInfo",
1662 "start": { 1662 "start": {
1663 "column": 24, 1663 "column": 24,
1664 "line": 62 1664 "line": 63
1665 } 1665 }
1666 }, 1666 },
1667 { 1667 {
1668 "defaultMessage": "!!!Upgrade your account", 1668 "defaultMessage": "!!!Upgrade your account",
1669 "end": { 1669 "end": {
1670 "column": 3, 1670 "column": 3,
1671 "line": 69 1671 "line": 70
1672 }, 1672 },
1673 "file": "src/components/settings/services/EditServiceForm.js", 1673 "file": "src/components/settings/services/EditServiceForm.js",
1674 "id": "settings.service.form.customUrlUpgradeAccount", 1674 "id": "settings.service.form.customUrlUpgradeAccount",
1675 "start": { 1675 "start": {
1676 "column": 27, 1676 "column": 27,
1677 "line": 66 1677 "line": 67
1678 } 1678 }
1679 }, 1679 },
1680 { 1680 {
1681 "defaultMessage": "!!!You will be notified about all new messages in a channel, not just @username, @channel, @here, ...", 1681 "defaultMessage": "!!!You will be notified about all new messages in a channel, not just @username, @channel, @here, ...",
1682 "end": { 1682 "end": {
1683 "column": 3, 1683 "column": 3,
1684 "line": 73 1684 "line": 74
1685 }, 1685 },
1686 "file": "src/components/settings/services/EditServiceForm.js", 1686 "file": "src/components/settings/services/EditServiceForm.js",
1687 "id": "settings.service.form.indirectMessageInfo", 1687 "id": "settings.service.form.indirectMessageInfo",
1688 "start": { 1688 "start": {
1689 "column": 23, 1689 "column": 23,
1690 "line": 70 1690 "line": 71
1691 } 1691 }
1692 }, 1692 },
1693 { 1693 {
1694 "defaultMessage": "!!!When disabled, all notification sounds and audio playback are muted", 1694 "defaultMessage": "!!!When disabled, all notification sounds and audio playback are muted",
1695 "end": { 1695 "end": {
1696 "column": 3, 1696 "column": 3,
1697 "line": 77 1697 "line": 78
1698 }, 1698 },
1699 "file": "src/components/settings/services/EditServiceForm.js", 1699 "file": "src/components/settings/services/EditServiceForm.js",
1700 "id": "settings.service.form.isMutedInfo", 1700 "id": "settings.service.form.isMutedInfo",
1701 "start": { 1701 "start": {
1702 "column": 15, 1702 "column": 15,
1703 "line": 74 1703 "line": 75
1704 } 1704 }
1705 }, 1705 },
1706 { 1706 {
1707 "defaultMessage": "!!!Notifications", 1707 "defaultMessage": "!!!Notifications",
1708 "end": { 1708 "end": {
1709 "column": 3, 1709 "column": 3,
1710 "line": 81 1710 "line": 82
1711 }, 1711 },
1712 "file": "src/components/settings/services/EditServiceForm.js", 1712 "file": "src/components/settings/services/EditServiceForm.js",
1713 "id": "settings.service.form.headlineNotifications", 1713 "id": "settings.service.form.headlineNotifications",
1714 "start": { 1714 "start": {
1715 "column": 25, 1715 "column": 25,
1716 "line": 78 1716 "line": 79
1717 } 1717 }
1718 }, 1718 },
1719 { 1719 {
1720 "defaultMessage": "!!!Unread message badges", 1720 "defaultMessage": "!!!Unread message badges",
1721 "end": { 1721 "end": {
1722 "column": 3, 1722 "column": 3,
1723 "line": 85 1723 "line": 86
1724 }, 1724 },
1725 "file": "src/components/settings/services/EditServiceForm.js", 1725 "file": "src/components/settings/services/EditServiceForm.js",
1726 "id": "settings.service.form.headlineBadges", 1726 "id": "settings.service.form.headlineBadges",
1727 "start": { 1727 "start": {
1728 "column": 18, 1728 "column": 18,
1729 "line": 82 1729 "line": 83
1730 } 1730 }
1731 }, 1731 },
1732 { 1732 {
1733 "defaultMessage": "!!!General", 1733 "defaultMessage": "!!!General",
1734 "end": { 1734 "end": {
1735 "column": 3, 1735 "column": 3,
1736 "line": 89 1736 "line": 90
1737 }, 1737 },
1738 "file": "src/components/settings/services/EditServiceForm.js", 1738 "file": "src/components/settings/services/EditServiceForm.js",
1739 "id": "settings.service.form.headlineGeneral", 1739 "id": "settings.service.form.headlineGeneral",
1740 "start": { 1740 "start": {
1741 "column": 19, 1741 "column": 19,
1742 "line": 86 1742 "line": 87
1743 } 1743 }
1744 }, 1744 },
1745 { 1745 {
1746 "defaultMessage": "!!!Delete", 1746 "defaultMessage": "!!!Delete",
1747 "end": { 1747 "end": {
1748 "column": 3, 1748 "column": 3,
1749 "line": 93 1749 "line": 94
1750 }, 1750 },
1751 "file": "src/components/settings/services/EditServiceForm.js", 1751 "file": "src/components/settings/services/EditServiceForm.js",
1752 "id": "settings.service.form.iconDelete", 1752 "id": "settings.service.form.iconDelete",
1753 "start": { 1753 "start": {
1754 "column": 14, 1754 "column": 14,
1755 "line": 90 1755 "line": 91
1756 } 1756 }
1757 }, 1757 },
1758 { 1758 {
1759 "defaultMessage": "!!!Drop your image, or click here", 1759 "defaultMessage": "!!!Drop your image, or click here",
1760 "end": { 1760 "end": {
1761 "column": 3, 1761 "column": 3,
1762 "line": 97 1762 "line": 98
1763 }, 1763 },
1764 "file": "src/components/settings/services/EditServiceForm.js", 1764 "file": "src/components/settings/services/EditServiceForm.js",
1765 "id": "settings.service.form.iconUpload", 1765 "id": "settings.service.form.iconUpload",
1766 "start": { 1766 "start": {
1767 "column": 14, 1767 "column": 14,
1768 "line": 94 1768 "line": 95
1769 } 1769 }
1770 }, 1770 },
1771 { 1771 {
1772 "defaultMessage": "!!!HTTP/HTTPS Proxy Settings", 1772 "defaultMessage": "!!!HTTP/HTTPS Proxy Settings",
1773 "end": { 1773 "end": {
1774 "column": 3, 1774 "column": 3,
1775 "line": 101 1775 "line": 102
1776 }, 1776 },
1777 "file": "src/components/settings/services/EditServiceForm.js", 1777 "file": "src/components/settings/services/EditServiceForm.js",
1778 "id": "settings.service.form.proxy.headline", 1778 "id": "settings.service.form.proxy.headline",
1779 "start": { 1779 "start": {
1780 "column": 17, 1780 "column": 17,
1781 "line": 98 1781 "line": 99
1782 } 1782 }
1783 }, 1783 },
1784 { 1784 {
1785 "defaultMessage": "!!!Please restart Franz after changing proxy Settings.", 1785 "defaultMessage": "!!!Please restart Franz after changing proxy Settings.",
1786 "end": { 1786 "end": {
1787 "column": 3, 1787 "column": 3,
1788 "line": 105 1788 "line": 106
1789 }, 1789 },
1790 "file": "src/components/settings/services/EditServiceForm.js", 1790 "file": "src/components/settings/services/EditServiceForm.js",
1791 "id": "settings.service.form.proxy.restartInfo", 1791 "id": "settings.service.form.proxy.restartInfo",
1792 "start": { 1792 "start": {
1793 "column": 20, 1793 "column": 20,
1794 "line": 102 1794 "line": 103
1795 } 1795 }
1796 }, 1796 },
1797 { 1797 {
1798 "defaultMessage": "!!!Proxy settings will not be synchronized with the Franz servers.", 1798 "defaultMessage": "!!!Proxy settings will not be synchronized with the Franz servers.",
1799 "end": { 1799 "end": {
1800 "column": 3, 1800 "column": 3,
1801 "line": 109 1801 "line": 110
1802 }, 1802 },
1803 "file": "src/components/settings/services/EditServiceForm.js", 1803 "file": "src/components/settings/services/EditServiceForm.js",
1804 "id": "settings.service.form.proxy.info", 1804 "id": "settings.service.form.proxy.info",
1805 "start": { 1805 "start": {
1806 "column": 13, 1806 "column": 13,
1807 "line": 106 1807 "line": 107
1808 } 1808 }
1809 } 1809 }
1810 ], 1810 ],
@@ -1917,117 +1917,117 @@
1917 "defaultMessage": "!!!Your services", 1917 "defaultMessage": "!!!Your services",
1918 "end": { 1918 "end": {
1919 "column": 3, 1919 "column": 3,
1920 "line": 17 1920 "line": 18
1921 }, 1921 },
1922 "file": "src/components/settings/services/ServicesDashboard.js", 1922 "file": "src/components/settings/services/ServicesDashboard.js",
1923 "id": "settings.services.headline", 1923 "id": "settings.services.headline",
1924 "start": { 1924 "start": {
1925 "column": 12, 1925 "column": 12,
1926 "line": 14 1926 "line": 15
1927 } 1927 }
1928 }, 1928 },
1929 { 1929 {
1930 "defaultMessage": "!!!Search service", 1930 "defaultMessage": "!!!Search service",
1931 "end": { 1931 "end": {
1932 "column": 3, 1932 "column": 3,
1933 "line": 21 1933 "line": 22
1934 }, 1934 },
1935 "file": "src/components/settings/services/ServicesDashboard.js", 1935 "file": "src/components/settings/services/ServicesDashboard.js",
1936 "id": "settings.searchService", 1936 "id": "settings.searchService",
1937 "start": { 1937 "start": {
1938 "column": 17, 1938 "column": 17,
1939 "line": 18 1939 "line": 19
1940 } 1940 }
1941 }, 1941 },
1942 { 1942 {
1943 "defaultMessage": "!!!You haven't added any services yet.", 1943 "defaultMessage": "!!!You haven't added any services yet.",
1944 "end": { 1944 "end": {
1945 "column": 3, 1945 "column": 3,
1946 "line": 25 1946 "line": 26
1947 }, 1947 },
1948 "file": "src/components/settings/services/ServicesDashboard.js", 1948 "file": "src/components/settings/services/ServicesDashboard.js",
1949 "id": "settings.services.noServicesAdded", 1949 "id": "settings.services.noServicesAdded",
1950 "start": { 1950 "start": {
1951 "column": 19, 1951 "column": 19,
1952 "line": 22 1952 "line": 23
1953 } 1953 }
1954 }, 1954 },
1955 { 1955 {
1956 "defaultMessage": "!!!Sorry, but no service matched your search term.", 1956 "defaultMessage": "!!!Sorry, but no service matched your search term.",
1957 "end": { 1957 "end": {
1958 "column": 3, 1958 "column": 3,
1959 "line": 29 1959 "line": 30
1960 }, 1960 },
1961 "file": "src/components/settings/services/ServicesDashboard.js", 1961 "file": "src/components/settings/services/ServicesDashboard.js",
1962 "id": "settings.recipes.nothingFound", 1962 "id": "settings.recipes.nothingFound",
1963 "start": { 1963 "start": {
1964 "column": 18, 1964 "column": 18,
1965 "line": 26 1965 "line": 27
1966 } 1966 }
1967 }, 1967 },
1968 { 1968 {
1969 "defaultMessage": "!!!Discover services", 1969 "defaultMessage": "!!!Discover services",
1970 "end": { 1970 "end": {
1971 "column": 3, 1971 "column": 3,
1972 "line": 33 1972 "line": 34
1973 }, 1973 },
1974 "file": "src/components/settings/services/ServicesDashboard.js", 1974 "file": "src/components/settings/services/ServicesDashboard.js",
1975 "id": "settings.services.discoverServices", 1975 "id": "settings.services.discoverServices",
1976 "start": { 1976 "start": {
1977 "column": 20, 1977 "column": 20,
1978 "line": 30 1978 "line": 31
1979 } 1979 }
1980 }, 1980 },
1981 { 1981 {
1982 "defaultMessage": "!!!Could not load your services", 1982 "defaultMessage": "!!!Could not load your services",
1983 "end": { 1983 "end": {
1984 "column": 3, 1984 "column": 3,
1985 "line": 37 1985 "line": 38
1986 }, 1986 },
1987 "file": "src/components/settings/services/ServicesDashboard.js", 1987 "file": "src/components/settings/services/ServicesDashboard.js",
1988 "id": "settings.services.servicesRequestFailed", 1988 "id": "settings.services.servicesRequestFailed",
1989 "start": { 1989 "start": {
1990 "column": 25, 1990 "column": 25,
1991 "line": 34 1991 "line": 35
1992 } 1992 }
1993 }, 1993 },
1994 { 1994 {
1995 "defaultMessage": "!!!Try again", 1995 "defaultMessage": "!!!Try again",
1996 "end": { 1996 "end": {
1997 "column": 3, 1997 "column": 3,
1998 "line": 41 1998 "line": 42
1999 }, 1999 },
2000 "file": "src/components/settings/services/ServicesDashboard.js", 2000 "file": "src/components/settings/services/ServicesDashboard.js",
2001 "id": "settings.account.tryReloadServices", 2001 "id": "settings.account.tryReloadServices",
2002 "start": { 2002 "start": {
2003 "column": 21, 2003 "column": 21,
2004 "line": 38 2004 "line": 39
2005 } 2005 }
2006 }, 2006 },
2007 { 2007 {
2008 "defaultMessage": "!!!Your changes have been saved", 2008 "defaultMessage": "!!!Your changes have been saved",
2009 "end": { 2009 "end": {
2010 "column": 3, 2010 "column": 3,
2011 "line": 45 2011 "line": 46
2012 }, 2012 },
2013 "file": "src/components/settings/services/ServicesDashboard.js", 2013 "file": "src/components/settings/services/ServicesDashboard.js",
2014 "id": "settings.services.updatedInfo", 2014 "id": "settings.services.updatedInfo",
2015 "start": { 2015 "start": {
2016 "column": 15, 2016 "column": 15,
2017 "line": 42 2017 "line": 43
2018 } 2018 }
2019 }, 2019 },
2020 { 2020 {
2021 "defaultMessage": "!!!Service has been deleted", 2021 "defaultMessage": "!!!Service has been deleted",
2022 "end": { 2022 "end": {
2023 "column": 3, 2023 "column": 3,
2024 "line": 49 2024 "line": 50
2025 }, 2025 },
2026 "file": "src/components/settings/services/ServicesDashboard.js", 2026 "file": "src/components/settings/services/ServicesDashboard.js",
2027 "id": "settings.services.deletedInfo", 2027 "id": "settings.services.deletedInfo",
2028 "start": { 2028 "start": {
2029 "column": 15, 2029 "column": 15,
2030 "line": 46 2030 "line": 47
2031 } 2031 }
2032 } 2032 }
2033 ], 2033 ],
@@ -3271,6 +3271,55 @@
3271 { 3271 {
3272 "descriptors": [ 3272 "descriptors": [
3273 { 3273 {
3274 "defaultMessage": "!!!Changes in Franz {version}",
3275 "end": {
3276 "column": 3,
3277 "line": 23
3278 },
3279 "file": "src/features/serviceLimit/components/AnnouncementScreen.js",
3280 "id": "feature.announcements.changelog.headline",
3281 "start": {
3282 "column": 12,
3283 "line": 20
3284 }
3285 }
3286 ],
3287 "path": "src/features/serviceLimit/components/AnnouncementScreen.json"
3288 },
3289 {
3290 "descriptors": [
3291 {
3292 "defaultMessage": "!!!You have added {amount} of {amount} services. Please upgrade your account to add more services.",
3293 "end": {
3294 "column": 3,
3295 "line": 16
3296 },
3297 "file": "src/features/serviceLimit/components/LimitReachedInfobox.js",
3298 "id": "feature.serviceLimit.limitReached",
3299 "start": {
3300 "column": 16,
3301 "line": 13
3302 }
3303 },
3304 {
3305 "defaultMessage": "!!!Upgrade account",
3306 "end": {
3307 "column": 3,
3308 "line": 20
3309 },
3310 "file": "src/features/serviceLimit/components/LimitReachedInfobox.js",
3311 "id": "premiumFeature.button.upgradeAccount",
3312 "start": {
3313 "column": 10,
3314 "line": 17
3315 }
3316 }
3317 ],
3318 "path": "src/features/serviceLimit/components/LimitReachedInfobox.json"
3319 },
3320 {
3321 "descriptors": [
3322 {
3274 "defaultMessage": "!!!Franz is better together!", 3323 "defaultMessage": "!!!Franz is better together!",
3275 "end": { 3324 "end": {
3276 "column": 3, 3325 "column": 3,
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index e2edbd596..a821dbe39 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -5,6 +5,7 @@
5 "feature.delayApp.action": "Get a Franz Supporter License", 5 "feature.delayApp.action": "Get a Franz Supporter License",
6 "feature.delayApp.headline": "Please purchase a Franz Supporter License to skip waiting", 6 "feature.delayApp.headline": "Please purchase a Franz Supporter License to skip waiting",
7 "feature.delayApp.text": "Franz will continue in {seconds} seconds.", 7 "feature.delayApp.text": "Franz will continue in {seconds} seconds.",
8 "feature.serviceLimit.limitReached": "You have added {amount} of {limit} services. Please upgrade your account to add more services.",
8 "feature.shareFranz.action.email": "Send as email", 9 "feature.shareFranz.action.email": "Send as email",
9 "feature.shareFranz.action.facebook": "Share on Facebook", 10 "feature.shareFranz.action.facebook": "Share on Facebook",
10 "feature.shareFranz.action.twitter": "Share on Twitter", 11 "feature.shareFranz.action.twitter": "Share on Twitter",
@@ -329,4 +330,4 @@
329 "workspaceDrawer.workspaceFeatureInfo": "<p>Franz Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time.</p><p>You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.</p>", 330 "workspaceDrawer.workspaceFeatureInfo": "<p>Franz Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time.</p><p>You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.</p>",
330 "workspaceDrawer.workspacesSettingsTooltip": "Edit workspaces settings", 331 "workspaceDrawer.workspacesSettingsTooltip": "Edit workspaces settings",
331 "workspaces.switchingIndicator.switchingTo": "Switching to" 332 "workspaces.switchingIndicator.switchingTo": "Switching to"
332} \ No newline at end of file 333}
diff --git a/src/i18n/messages/src/components/settings/recipes/RecipesDashboard.json b/src/i18n/messages/src/components/settings/recipes/RecipesDashboard.json
index 7d9ed3283..07eada1dc 100644
--- a/src/i18n/messages/src/components/settings/recipes/RecipesDashboard.json
+++ b/src/i18n/messages/src/components/settings/recipes/RecipesDashboard.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Available Services", 4 "defaultMessage": "!!!Available Services",
5 "file": "src/components/settings/recipes/RecipesDashboard.js", 5 "file": "src/components/settings/recipes/RecipesDashboard.js",
6 "start": { 6 "start": {
7 "line": 15, 7 "line": 16,
8 "column": 12 8 "column": 12
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 18, 11 "line": 19,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,11 @@
17 "defaultMessage": "!!!Search service", 17 "defaultMessage": "!!!Search service",
18 "file": "src/components/settings/recipes/RecipesDashboard.js", 18 "file": "src/components/settings/recipes/RecipesDashboard.js",
19 "start": { 19 "start": {
20 "line": 19, 20 "line": 20,
21 "column": 17 21 "column": 17
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 22, 24 "line": 23,
25 "column": 3 25 "column": 3
26 } 26 }
27 }, 27 },
@@ -30,11 +30,11 @@
30 "defaultMessage": "!!!Most popular", 30 "defaultMessage": "!!!Most popular",
31 "file": "src/components/settings/recipes/RecipesDashboard.js", 31 "file": "src/components/settings/recipes/RecipesDashboard.js",
32 "start": { 32 "start": {
33 "line": 23, 33 "line": 24,
34 "column": 22 34 "column": 22
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 26, 37 "line": 27,
38 "column": 3 38 "column": 3
39 } 39 }
40 }, 40 },
@@ -43,11 +43,11 @@
43 "defaultMessage": "!!!All services", 43 "defaultMessage": "!!!All services",
44 "file": "src/components/settings/recipes/RecipesDashboard.js", 44 "file": "src/components/settings/recipes/RecipesDashboard.js",
45 "start": { 45 "start": {
46 "line": 27, 46 "line": 28,
47 "column": 14 47 "column": 14
48 }, 48 },
49 "end": { 49 "end": {
50 "line": 30, 50 "line": 31,
51 "column": 3 51 "column": 3
52 } 52 }
53 }, 53 },
@@ -56,11 +56,11 @@
56 "defaultMessage": "!!!Development", 56 "defaultMessage": "!!!Development",
57 "file": "src/components/settings/recipes/RecipesDashboard.js", 57 "file": "src/components/settings/recipes/RecipesDashboard.js",
58 "start": { 58 "start": {
59 "line": 31, 59 "line": 32,
60 "column": 14 60 "column": 14
61 }, 61 },
62 "end": { 62 "end": {
63 "line": 34, 63 "line": 35,
64 "column": 3 64 "column": 3
65 } 65 }
66 }, 66 },
@@ -69,11 +69,11 @@
69 "defaultMessage": "!!!Sorry, but no service matched your search term.", 69 "defaultMessage": "!!!Sorry, but no service matched your search term.",
70 "file": "src/components/settings/recipes/RecipesDashboard.js", 70 "file": "src/components/settings/recipes/RecipesDashboard.js",
71 "start": { 71 "start": {
72 "line": 35, 72 "line": 36,
73 "column": 16 73 "column": 16
74 }, 74 },
75 "end": { 75 "end": {
76 "line": 38, 76 "line": 39,
77 "column": 3 77 "column": 3
78 } 78 }
79 }, 79 },
@@ -82,11 +82,11 @@
82 "defaultMessage": "!!!Service successfully added", 82 "defaultMessage": "!!!Service successfully added",
83 "file": "src/components/settings/recipes/RecipesDashboard.js", 83 "file": "src/components/settings/recipes/RecipesDashboard.js",
84 "start": { 84 "start": {
85 "line": 39, 85 "line": 40,
86 "column": 31 86 "column": 31
87 }, 87 },
88 "end": { 88 "end": {
89 "line": 42, 89 "line": 43,
90 "column": 3 90 "column": 3
91 } 91 }
92 }, 92 },
@@ -95,11 +95,11 @@
95 "defaultMessage": "!!!Missing a service?", 95 "defaultMessage": "!!!Missing a service?",
96 "file": "src/components/settings/recipes/RecipesDashboard.js", 96 "file": "src/components/settings/recipes/RecipesDashboard.js",
97 "start": { 97 "start": {
98 "line": 43, 98 "line": 44,
99 "column": 18 99 "column": 18
100 }, 100 },
101 "end": { 101 "end": {
102 "line": 46, 102 "line": 47,
103 "column": 3 103 "column": 3
104 } 104 }
105 } 105 }
diff --git a/src/i18n/messages/src/components/settings/services/EditServiceForm.json b/src/i18n/messages/src/components/settings/services/EditServiceForm.json
index 42b741b7a..e66db807d 100644
--- a/src/i18n/messages/src/components/settings/services/EditServiceForm.json
+++ b/src/i18n/messages/src/components/settings/services/EditServiceForm.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Save service", 4 "defaultMessage": "!!!Save service",
5 "file": "src/components/settings/services/EditServiceForm.js", 5 "file": "src/components/settings/services/EditServiceForm.js",
6 "start": { 6 "start": {
7 "line": 22, 7 "line": 24,
8 "column": 15 8 "column": 15
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 25, 11 "line": 27,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,11 @@
17 "defaultMessage": "!!!Delete Service", 17 "defaultMessage": "!!!Delete Service",
18 "file": "src/components/settings/services/EditServiceForm.js", 18 "file": "src/components/settings/services/EditServiceForm.js",
19 "start": { 19 "start": {
20 "line": 26, 20 "line": 28,
21 "column": 17 21 "column": 17
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 29, 24 "line": 31,
25 "column": 3 25 "column": 3
26 } 26 }
27 }, 27 },
@@ -30,11 +30,11 @@
30 "defaultMessage": "!!!Available services", 30 "defaultMessage": "!!!Available services",
31 "file": "src/components/settings/services/EditServiceForm.js", 31 "file": "src/components/settings/services/EditServiceForm.js",
32 "start": { 32 "start": {
33 "line": 30, 33 "line": 32,
34 "column": 21 34 "column": 21
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 33, 37 "line": 35,
38 "column": 3 38 "column": 3
39 } 39 }
40 }, 40 },
@@ -43,11 +43,11 @@
43 "defaultMessage": "!!!Your services", 43 "defaultMessage": "!!!Your services",
44 "file": "src/components/settings/services/EditServiceForm.js", 44 "file": "src/components/settings/services/EditServiceForm.js",
45 "start": { 45 "start": {
46 "line": 34, 46 "line": 36,
47 "column": 16 47 "column": 16
48 }, 48 },
49 "end": { 49 "end": {
50 "line": 37, 50 "line": 39,
51 "column": 3 51 "column": 3
52 } 52 }
53 }, 53 },
@@ -56,11 +56,11 @@
56 "defaultMessage": "!!!Add {name}", 56 "defaultMessage": "!!!Add {name}",
57 "file": "src/components/settings/services/EditServiceForm.js", 57 "file": "src/components/settings/services/EditServiceForm.js",
58 "start": { 58 "start": {
59 "line": 38, 59 "line": 40,
60 "column": 22 60 "column": 22
61 }, 61 },
62 "end": { 62 "end": {
63 "line": 41, 63 "line": 43,
64 "column": 3 64 "column": 3
65 } 65 }
66 }, 66 },
@@ -69,11 +69,11 @@
69 "defaultMessage": "!!!Edit {name}", 69 "defaultMessage": "!!!Edit {name}",
70 "file": "src/components/settings/services/EditServiceForm.js", 70 "file": "src/components/settings/services/EditServiceForm.js",
71 "start": { 71 "start": {
72 "line": 42, 72 "line": 44,
73 "column": 23 73 "column": 23
74 }, 74 },
75 "end": { 75 "end": {
76 "line": 45, 76 "line": 47,
77 "column": 3 77 "column": 3
78 } 78 }
79 }, 79 },
@@ -82,11 +82,11 @@
82 "defaultMessage": "!!!Hosted", 82 "defaultMessage": "!!!Hosted",
83 "file": "src/components/settings/services/EditServiceForm.js", 83 "file": "src/components/settings/services/EditServiceForm.js",
84 "start": { 84 "start": {
85 "line": 46, 85 "line": 48,
86 "column": 13 86 "column": 13
87 }, 87 },
88 "end": { 88 "end": {
89 "line": 49, 89 "line": 51,
90 "column": 3 90 "column": 3
91 } 91 }
92 }, 92 },
@@ -95,11 +95,11 @@
95 "defaultMessage": "!!!Self hosted ⭐️", 95 "defaultMessage": "!!!Self hosted ⭐️",
96 "file": "src/components/settings/services/EditServiceForm.js", 96 "file": "src/components/settings/services/EditServiceForm.js",
97 "start": { 97 "start": {
98 "line": 50, 98 "line": 52,
99 "column": 16 99 "column": 16
100 }, 100 },
101 "end": { 101 "end": {
102 "line": 53, 102 "line": 55,
103 "column": 3 103 "column": 3
104 } 104 }
105 }, 105 },
@@ -108,11 +108,11 @@
108 "defaultMessage": "!!!Use the hosted {name} service.", 108 "defaultMessage": "!!!Use the hosted {name} service.",
109 "file": "src/components/settings/services/EditServiceForm.js", 109 "file": "src/components/settings/services/EditServiceForm.js",
110 "start": { 110 "start": {
111 "line": 54, 111 "line": 56,
112 "column": 20 112 "column": 20
113 }, 113 },
114 "end": { 114 "end": {
115 "line": 57, 115 "line": 59,
116 "column": 3 116 "column": 3
117 } 117 }
118 }, 118 },
@@ -121,11 +121,11 @@
121 "defaultMessage": "!!!Could not validate custom {name} server.", 121 "defaultMessage": "!!!Could not validate custom {name} server.",
122 "file": "src/components/settings/services/EditServiceForm.js", 122 "file": "src/components/settings/services/EditServiceForm.js",
123 "start": { 123 "start": {
124 "line": 58, 124 "line": 60,
125 "column": 28 125 "column": 28
126 }, 126 },
127 "end": { 127 "end": {
128 "line": 61, 128 "line": 63,
129 "column": 3 129 "column": 3
130 } 130 }
131 }, 131 },
@@ -134,11 +134,11 @@
134 "defaultMessage": "!!!To add self hosted services, you need a Franz Premium Supporter Account.", 134 "defaultMessage": "!!!To add self hosted services, you need a Franz Premium Supporter Account.",
135 "file": "src/components/settings/services/EditServiceForm.js", 135 "file": "src/components/settings/services/EditServiceForm.js",
136 "start": { 136 "start": {
137 "line": 62, 137 "line": 64,
138 "column": 24 138 "column": 24
139 }, 139 },
140 "end": { 140 "end": {
141 "line": 65, 141 "line": 67,
142 "column": 3 142 "column": 3
143 } 143 }
144 }, 144 },
@@ -147,11 +147,11 @@
147 "defaultMessage": "!!!Upgrade your account", 147 "defaultMessage": "!!!Upgrade your account",
148 "file": "src/components/settings/services/EditServiceForm.js", 148 "file": "src/components/settings/services/EditServiceForm.js",
149 "start": { 149 "start": {
150 "line": 66, 150 "line": 68,
151 "column": 27 151 "column": 27
152 }, 152 },
153 "end": { 153 "end": {
154 "line": 69, 154 "line": 71,
155 "column": 3 155 "column": 3
156 } 156 }
157 }, 157 },
@@ -160,11 +160,11 @@
160 "defaultMessage": "!!!You will be notified about all new messages in a channel, not just @username, @channel, @here, ...", 160 "defaultMessage": "!!!You will be notified about all new messages in a channel, not just @username, @channel, @here, ...",
161 "file": "src/components/settings/services/EditServiceForm.js", 161 "file": "src/components/settings/services/EditServiceForm.js",
162 "start": { 162 "start": {
163 "line": 70, 163 "line": 72,
164 "column": 23 164 "column": 23
165 }, 165 },
166 "end": { 166 "end": {
167 "line": 73, 167 "line": 75,
168 "column": 3 168 "column": 3
169 } 169 }
170 }, 170 },
@@ -173,11 +173,11 @@
173 "defaultMessage": "!!!When disabled, all notification sounds and audio playback are muted", 173 "defaultMessage": "!!!When disabled, all notification sounds and audio playback are muted",
174 "file": "src/components/settings/services/EditServiceForm.js", 174 "file": "src/components/settings/services/EditServiceForm.js",
175 "start": { 175 "start": {
176 "line": 74, 176 "line": 76,
177 "column": 15 177 "column": 15
178 }, 178 },
179 "end": { 179 "end": {
180 "line": 77, 180 "line": 79,
181 "column": 3 181 "column": 3
182 } 182 }
183 }, 183 },
@@ -186,11 +186,11 @@
186 "defaultMessage": "!!!Notifications", 186 "defaultMessage": "!!!Notifications",
187 "file": "src/components/settings/services/EditServiceForm.js", 187 "file": "src/components/settings/services/EditServiceForm.js",
188 "start": { 188 "start": {
189 "line": 78, 189 "line": 80,
190 "column": 25 190 "column": 25
191 }, 191 },
192 "end": { 192 "end": {
193 "line": 81, 193 "line": 83,
194 "column": 3 194 "column": 3
195 } 195 }
196 }, 196 },
@@ -199,11 +199,11 @@
199 "defaultMessage": "!!!Unread message badges", 199 "defaultMessage": "!!!Unread message badges",
200 "file": "src/components/settings/services/EditServiceForm.js", 200 "file": "src/components/settings/services/EditServiceForm.js",
201 "start": { 201 "start": {
202 "line": 82, 202 "line": 84,
203 "column": 18 203 "column": 18
204 }, 204 },
205 "end": { 205 "end": {
206 "line": 85, 206 "line": 87,
207 "column": 3 207 "column": 3
208 } 208 }
209 }, 209 },
@@ -212,11 +212,11 @@
212 "defaultMessage": "!!!General", 212 "defaultMessage": "!!!General",
213 "file": "src/components/settings/services/EditServiceForm.js", 213 "file": "src/components/settings/services/EditServiceForm.js",
214 "start": { 214 "start": {
215 "line": 86, 215 "line": 88,
216 "column": 19 216 "column": 19
217 }, 217 },
218 "end": { 218 "end": {
219 "line": 89, 219 "line": 91,
220 "column": 3 220 "column": 3
221 } 221 }
222 }, 222 },
@@ -225,11 +225,11 @@
225 "defaultMessage": "!!!Delete", 225 "defaultMessage": "!!!Delete",
226 "file": "src/components/settings/services/EditServiceForm.js", 226 "file": "src/components/settings/services/EditServiceForm.js",
227 "start": { 227 "start": {
228 "line": 90, 228 "line": 92,
229 "column": 14 229 "column": 14
230 }, 230 },
231 "end": { 231 "end": {
232 "line": 93, 232 "line": 95,
233 "column": 3 233 "column": 3
234 } 234 }
235 }, 235 },
@@ -238,11 +238,11 @@
238 "defaultMessage": "!!!Drop your image, or click here", 238 "defaultMessage": "!!!Drop your image, or click here",
239 "file": "src/components/settings/services/EditServiceForm.js", 239 "file": "src/components/settings/services/EditServiceForm.js",
240 "start": { 240 "start": {
241 "line": 94, 241 "line": 96,
242 "column": 14 242 "column": 14
243 }, 243 },
244 "end": { 244 "end": {
245 "line": 97, 245 "line": 99,
246 "column": 3 246 "column": 3
247 } 247 }
248 }, 248 },
@@ -251,11 +251,11 @@
251 "defaultMessage": "!!!HTTP/HTTPS Proxy Settings", 251 "defaultMessage": "!!!HTTP/HTTPS Proxy Settings",
252 "file": "src/components/settings/services/EditServiceForm.js", 252 "file": "src/components/settings/services/EditServiceForm.js",
253 "start": { 253 "start": {
254 "line": 98, 254 "line": 100,
255 "column": 17 255 "column": 17
256 }, 256 },
257 "end": { 257 "end": {
258 "line": 101, 258 "line": 103,
259 "column": 3 259 "column": 3
260 } 260 }
261 }, 261 },
@@ -264,11 +264,11 @@
264 "defaultMessage": "!!!Please restart Franz after changing proxy Settings.", 264 "defaultMessage": "!!!Please restart Franz after changing proxy Settings.",
265 "file": "src/components/settings/services/EditServiceForm.js", 265 "file": "src/components/settings/services/EditServiceForm.js",
266 "start": { 266 "start": {
267 "line": 102, 267 "line": 104,
268 "column": 20 268 "column": 20
269 }, 269 },
270 "end": { 270 "end": {
271 "line": 105, 271 "line": 107,
272 "column": 3 272 "column": 3
273 } 273 }
274 }, 274 },
@@ -277,11 +277,11 @@
277 "defaultMessage": "!!!Proxy settings will not be synchronized with the Franz servers.", 277 "defaultMessage": "!!!Proxy settings will not be synchronized with the Franz servers.",
278 "file": "src/components/settings/services/EditServiceForm.js", 278 "file": "src/components/settings/services/EditServiceForm.js",
279 "start": { 279 "start": {
280 "line": 106, 280 "line": 108,
281 "column": 13 281 "column": 13
282 }, 282 },
283 "end": { 283 "end": {
284 "line": 109, 284 "line": 111,
285 "column": 3 285 "column": 3
286 } 286 }
287 } 287 }
diff --git a/src/i18n/messages/src/components/settings/services/ServicesDashboard.json b/src/i18n/messages/src/components/settings/services/ServicesDashboard.json
index 3803c6512..fa661ea2f 100644
--- a/src/i18n/messages/src/components/settings/services/ServicesDashboard.json
+++ b/src/i18n/messages/src/components/settings/services/ServicesDashboard.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Your services", 4 "defaultMessage": "!!!Your services",
5 "file": "src/components/settings/services/ServicesDashboard.js", 5 "file": "src/components/settings/services/ServicesDashboard.js",
6 "start": { 6 "start": {
7 "line": 14, 7 "line": 15,
8 "column": 12 8 "column": 12
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 17, 11 "line": 18,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,11 @@
17 "defaultMessage": "!!!Search service", 17 "defaultMessage": "!!!Search service",
18 "file": "src/components/settings/services/ServicesDashboard.js", 18 "file": "src/components/settings/services/ServicesDashboard.js",
19 "start": { 19 "start": {
20 "line": 18, 20 "line": 19,
21 "column": 17 21 "column": 17
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 21, 24 "line": 22,
25 "column": 3 25 "column": 3
26 } 26 }
27 }, 27 },
@@ -30,11 +30,11 @@
30 "defaultMessage": "!!!You haven't added any services yet.", 30 "defaultMessage": "!!!You haven't added any services yet.",
31 "file": "src/components/settings/services/ServicesDashboard.js", 31 "file": "src/components/settings/services/ServicesDashboard.js",
32 "start": { 32 "start": {
33 "line": 22, 33 "line": 23,
34 "column": 19 34 "column": 19
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 25, 37 "line": 26,
38 "column": 3 38 "column": 3
39 } 39 }
40 }, 40 },
@@ -43,11 +43,11 @@
43 "defaultMessage": "!!!Sorry, but no service matched your search term.", 43 "defaultMessage": "!!!Sorry, but no service matched your search term.",
44 "file": "src/components/settings/services/ServicesDashboard.js", 44 "file": "src/components/settings/services/ServicesDashboard.js",
45 "start": { 45 "start": {
46 "line": 26, 46 "line": 27,
47 "column": 18 47 "column": 18
48 }, 48 },
49 "end": { 49 "end": {
50 "line": 29, 50 "line": 30,
51 "column": 3 51 "column": 3
52 } 52 }
53 }, 53 },
@@ -56,11 +56,11 @@
56 "defaultMessage": "!!!Discover services", 56 "defaultMessage": "!!!Discover services",
57 "file": "src/components/settings/services/ServicesDashboard.js", 57 "file": "src/components/settings/services/ServicesDashboard.js",
58 "start": { 58 "start": {
59 "line": 30, 59 "line": 31,
60 "column": 20 60 "column": 20
61 }, 61 },
62 "end": { 62 "end": {
63 "line": 33, 63 "line": 34,
64 "column": 3 64 "column": 3
65 } 65 }
66 }, 66 },
@@ -69,11 +69,11 @@
69 "defaultMessage": "!!!Could not load your services", 69 "defaultMessage": "!!!Could not load your services",
70 "file": "src/components/settings/services/ServicesDashboard.js", 70 "file": "src/components/settings/services/ServicesDashboard.js",
71 "start": { 71 "start": {
72 "line": 34, 72 "line": 35,
73 "column": 25 73 "column": 25
74 }, 74 },
75 "end": { 75 "end": {
76 "line": 37, 76 "line": 38,
77 "column": 3 77 "column": 3
78 } 78 }
79 }, 79 },
@@ -82,11 +82,11 @@
82 "defaultMessage": "!!!Try again", 82 "defaultMessage": "!!!Try again",
83 "file": "src/components/settings/services/ServicesDashboard.js", 83 "file": "src/components/settings/services/ServicesDashboard.js",
84 "start": { 84 "start": {
85 "line": 38, 85 "line": 39,
86 "column": 21 86 "column": 21
87 }, 87 },
88 "end": { 88 "end": {
89 "line": 41, 89 "line": 42,
90 "column": 3 90 "column": 3
91 } 91 }
92 }, 92 },
@@ -95,11 +95,11 @@
95 "defaultMessage": "!!!Your changes have been saved", 95 "defaultMessage": "!!!Your changes have been saved",
96 "file": "src/components/settings/services/ServicesDashboard.js", 96 "file": "src/components/settings/services/ServicesDashboard.js",
97 "start": { 97 "start": {
98 "line": 42, 98 "line": 43,
99 "column": 15 99 "column": 15
100 }, 100 },
101 "end": { 101 "end": {
102 "line": 45, 102 "line": 46,
103 "column": 3 103 "column": 3
104 } 104 }
105 }, 105 },
@@ -108,11 +108,11 @@
108 "defaultMessage": "!!!Service has been deleted", 108 "defaultMessage": "!!!Service has been deleted",
109 "file": "src/components/settings/services/ServicesDashboard.js", 109 "file": "src/components/settings/services/ServicesDashboard.js",
110 "start": { 110 "start": {
111 "line": 46, 111 "line": 47,
112 "column": 15 112 "column": 15
113 }, 113 },
114 "end": { 114 "end": {
115 "line": 49, 115 "line": 50,
116 "column": 3 116 "column": 3
117 } 117 }
118 } 118 }
diff --git a/src/i18n/messages/src/features/serviceLimit/components/AnnouncementScreen.json b/src/i18n/messages/src/features/serviceLimit/components/AnnouncementScreen.json
new file mode 100644
index 000000000..e6e3cef99
--- /dev/null
+++ b/src/i18n/messages/src/features/serviceLimit/components/AnnouncementScreen.json
@@ -0,0 +1,15 @@
1[
2 {
3 "id": "feature.announcements.changelog.headline",
4 "defaultMessage": "!!!Changes in Franz {version}",
5 "file": "src/features/serviceLimit/components/AnnouncementScreen.js",
6 "start": {
7 "line": 20,
8 "column": 12
9 },
10 "end": {
11 "line": 23,
12 "column": 3
13 }
14 }
15] \ No newline at end of file
diff --git a/src/i18n/messages/src/features/serviceLimit/components/LimitReachedInfobox.json b/src/i18n/messages/src/features/serviceLimit/components/LimitReachedInfobox.json
new file mode 100644
index 000000000..df5bc03e8
--- /dev/null
+++ b/src/i18n/messages/src/features/serviceLimit/components/LimitReachedInfobox.json
@@ -0,0 +1,28 @@
1[
2 {
3 "id": "feature.serviceLimit.limitReached",
4 "defaultMessage": "!!!You have added {amount} of {limit} services. Please upgrade your account to add more services.",
5 "file": "src/features/serviceLimit/components/LimitReachedInfobox.js",
6 "start": {
7 "line": 11,
8 "column": 16
9 },
10 "end": {
11 "line": 14,
12 "column": 3
13 }
14 },
15 {
16 "id": "premiumFeature.button.upgradeAccount",
17 "defaultMessage": "!!!Upgrade account",
18 "file": "src/features/serviceLimit/components/LimitReachedInfobox.js",
19 "start": {
20 "line": 15,
21 "column": 10
22 },
23 "end": {
24 "line": 18,
25 "column": 3
26 }
27 }
28] \ No newline at end of file
diff --git a/src/stores/FeaturesStore.js b/src/stores/FeaturesStore.js
index e7832088b..1ac05d3b9 100644
--- a/src/stores/FeaturesStore.js
+++ b/src/stores/FeaturesStore.js
@@ -16,6 +16,7 @@ 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';
19 20
20import { DEFAULT_FEATURES_CONFIG } from '../config'; 21import { DEFAULT_FEATURES_CONFIG } from '../config';
21 22
@@ -75,5 +76,6 @@ export default class FeaturesStore extends Store {
75 shareFranz(this.stores, this.actions); 76 shareFranz(this.stores, this.actions);
76 announcements(this.stores, this.actions); 77 announcements(this.stores, this.actions);
77 settingsWS(this.stores, this.actions); 78 settingsWS(this.stores, this.actions);
79 serviceLimit(this.stores, this.actions);
78 } 80 }
79} 81}
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js
index 109ac5cd7..ee47bf6db 100644
--- a/src/stores/ServicesStore.js
+++ b/src/stores/ServicesStore.js
@@ -13,6 +13,7 @@ import CachedRequest from './lib/CachedRequest';
13import { matchRoute } from '../helpers/routing-helpers'; 13import { matchRoute } from '../helpers/routing-helpers';
14import { gaEvent, statsEvent } from '../lib/analytics'; 14import { gaEvent, statsEvent } from '../lib/analytics';
15import { workspaceStore } from '../features/workspaces'; 15import { workspaceStore } from '../features/workspaces';
16import { serviceLimitStore } from '../features/serviceLimit';
16 17
17const debug = require('debug')('Franz:ServiceStore'); 18const debug = require('debug')('Franz:ServiceStore');
18 19
@@ -153,6 +154,8 @@ export default class ServicesStore extends Store {
153 154
154 // Actions 155 // Actions
155 @action async _createService({ recipeId, serviceData, redirect = true }) { 156 @action async _createService({ recipeId, serviceData, redirect = true }) {
157 if (serviceLimitStore.userHasReachedServiceLimit) return;
158
156 const data = this._cleanUpTeamIdAndCustomUrl(recipeId, serviceData); 159 const data = this._cleanUpTeamIdAndCustomUrl(recipeId, serviceData);
157 160
158 const response = await this.createServiceRequest.execute(recipeId, data)._promise; 161 const response = await this.createServiceRequest.execute(recipeId, data)._promise;
diff --git a/src/stores/index.js b/src/stores/index.js
index 1912418a2..ff01a3dd3 100644
--- a/src/stores/index.js
+++ b/src/stores/index.js
@@ -12,6 +12,7 @@ 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';
15 16
16export default (api, actions, router) => { 17export default (api, actions, router) => {
17 const stores = {}; 18 const stores = {};
@@ -31,6 +32,7 @@ export default (api, actions, router) => {
31 globalError: new GlobalErrorStore(stores, api, actions), 32 globalError: new GlobalErrorStore(stores, api, actions),
32 workspaces: workspaceStore, 33 workspaces: workspaceStore,
33 announcements: announcementsStore, 34 announcements: announcementsStore,
35 serviceLimit: serviceLimitStore,
34 }); 36 });
35 // Initialize all stores 37 // Initialize all stores
36 Object.keys(stores).forEach((name) => { 38 Object.keys(stores).forEach((name) => {