aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/api/apiBase.js6
-rw-r--r--src/components/auth/Login.js22
-rw-r--r--src/components/auth/Signup.js22
-rw-r--r--src/components/auth/Welcome.js21
-rw-r--r--src/components/layout/Sidebar.js8
-rw-r--r--src/components/services/content/Services.js32
-rw-r--r--src/components/settings/team/TeamDashboard.js149
-rw-r--r--src/config.js2
-rw-r--r--src/containers/settings/EditSettingsScreen.js3
-rw-r--r--src/containers/settings/TeamScreen.js6
-rw-r--r--src/electron/ipc-api/index.js2
-rw-r--r--src/electron/ipc-api/localServer.js52
-rw-r--r--src/features/announcements/api.js3
-rw-r--r--src/helpers/serverless-helpers.js18
-rw-r--r--src/i18n/apply-branding.js1
-rw-r--r--src/i18n/locales/defaultMessages.json216
-rw-r--r--src/i18n/locales/en-US.json4
-rw-r--r--src/i18n/locales/zh-HANT.json6
-rw-r--r--src/i18n/messages/src/components/auth/Login.json70
-rw-r--r--src/i18n/messages/src/components/auth/Signup.json70
-rw-r--r--src/i18n/messages/src/components/auth/Welcome.json21
-rw-r--r--src/i18n/messages/src/components/services/content/Services.json29
-rw-r--r--src/i18n/messages/src/components/settings/team/TeamDashboard.json26
-rw-r--r--src/index.js2
m---------src/server0
-rw-r--r--src/stores/RequestStore.js9
-rw-r--r--src/stores/SettingsStore.js14
-rw-r--r--src/styles/layout.scss33
-rw-r--r--src/styles/services.scss2
-rw-r--r--src/styles/type.scss3
-rw-r--r--src/styles/welcome.scss1
31 files changed, 660 insertions, 193 deletions
diff --git a/src/api/apiBase.js b/src/api/apiBase.js
index e8d571171..561b025f0 100644
--- a/src/api/apiBase.js
+++ b/src/api/apiBase.js
@@ -4,6 +4,9 @@
4import { 4import {
5 API_VERSION, 5 API_VERSION,
6} from '../environment'; 6} from '../environment';
7import {
8 LOCAL_SERVER,
9} from '../config';
7 10
8const apiBase = () => { 11const apiBase = () => {
9 let url; 12 let url;
@@ -21,6 +24,9 @@ const apiBase = () => {
21 // on some routes. This would result in Ferdi deleting its current authToken as it thinks it 24 // on some routes. This would result in Ferdi deleting its current authToken as it thinks it
22 // has gone invalid. 25 // has gone invalid.
23 url = 'https://1.1.1.1'; 26 url = 'https://1.1.1.1';
27 } else if (window.ferdi.stores.settings.all.app.server === LOCAL_SERVER) {
28 // Use URL for local server
29 url = `http://127.0.0.1:${window.ferdi.stores.requests.localServerPort}`;
24 } else { 30 } else {
25 // Load URL from store 31 // Load URL from store
26 url = window.ferdi.stores.settings.all.app.server; 32 url = window.ferdi.stores.settings.all.app.server;
diff --git a/src/components/auth/Login.js b/src/components/auth/Login.js
index eea5a09bf..e58016e25 100644
--- a/src/components/auth/Login.js
+++ b/src/components/auth/Login.js
@@ -1,11 +1,13 @@
1/* eslint jsx-a11y/anchor-is-valid: 0 */
1import React, { Component } from 'react'; 2import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 3import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 4import { observer, inject } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl'; 5import { defineMessages, intlShape } from 'react-intl';
5 6
6import { isDevMode, useLiveAPI } from '../../environment'; 7import { isDevMode, useLiveAPI } from '../../environment';
7import Form from '../../lib/Form'; 8import Form from '../../lib/Form';
8import { required, email } from '../../helpers/validation-helpers'; 9import { required, email } from '../../helpers/validation-helpers';
10import serverlessLogin from '../../helpers/serverless-helpers';
9import Input from '../ui/Input'; 11import Input from '../ui/Input';
10import Button from '../ui/Button'; 12import Button from '../ui/Button';
11import Link from '../ui/Link'; 13import Link from '../ui/Link';
@@ -54,13 +56,21 @@ const messages = defineMessages({
54 id: 'login.link.signup', 56 id: 'login.link.signup',
55 defaultMessage: '!!!Create a free account', 57 defaultMessage: '!!!Create a free account',
56 }, 58 },
59 changeServer: {
60 id: 'login.changeServer',
61 defaultMessage: '!!!Change server',
62 },
63 serverless: {
64 id: 'services.serverless',
65 defaultMessage: '!!!Use Ferdi without an Account',
66 },
57 passwordLink: { 67 passwordLink: {
58 id: 'login.link.password', 68 id: 'login.link.password',
59 defaultMessage: '!!!Forgot password', 69 defaultMessage: '!!!Forgot password',
60 }, 70 },
61}); 71});
62 72
63export default @observer class Login extends Component { 73export default @observer @inject('actions') class Login extends Component {
64 static propTypes = { 74 static propTypes = {
65 onSubmit: PropTypes.func.isRequired, 75 onSubmit: PropTypes.func.isRequired,
66 isSubmitting: PropTypes.bool.isRequired, 76 isSubmitting: PropTypes.bool.isRequired,
@@ -69,6 +79,7 @@ export default @observer class Login extends Component {
69 signupRoute: PropTypes.string.isRequired, 79 signupRoute: PropTypes.string.isRequired,
70 passwordRoute: PropTypes.string.isRequired, 80 passwordRoute: PropTypes.string.isRequired,
71 error: globalErrorPropType.isRequired, 81 error: globalErrorPropType.isRequired,
82 actions: PropTypes.object.isRequired,
72 }; 83 };
73 84
74 static contextTypes = { 85 static contextTypes = {
@@ -103,6 +114,10 @@ export default @observer class Login extends Component {
103 }); 114 });
104 } 115 }
105 116
117 useLocalServer() {
118 serverlessLogin(this.props.actions);
119 }
120
106 render() { 121 render() {
107 const { form } = this; 122 const { form } = this;
108 const { intl } = this.context; 123 const { intl } = this.context;
@@ -179,7 +194,8 @@ export default @observer class Login extends Component {
179 )} 194 )}
180 </form> 195 </form>
181 <div className="auth__links"> 196 <div className="auth__links">
182 <Link to="/settings/app">Change server</Link> 197 <Link to="/settings/app">{intl.formatMessage(messages.changeServer)}</Link>
198 <a onClick={this.useLocalServer.bind(this)}>{intl.formatMessage(messages.serverless)}</a>
183 <Link to={signupRoute}>{intl.formatMessage(messages.signupLink)}</Link> 199 <Link to={signupRoute}>{intl.formatMessage(messages.signupLink)}</Link>
184 <Link to={passwordRoute}>{intl.formatMessage(messages.passwordLink)}</Link> 200 <Link to={passwordRoute}>{intl.formatMessage(messages.passwordLink)}</Link>
185 </div> 201 </div>
diff --git a/src/components/auth/Signup.js b/src/components/auth/Signup.js
index b36e71ce1..47e9daf18 100644
--- a/src/components/auth/Signup.js
+++ b/src/components/auth/Signup.js
@@ -1,11 +1,13 @@
1/* eslint jsx-a11y/anchor-is-valid: 0 */
1import React, { Component } from 'react'; 2import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 3import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 4import { observer, inject } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl'; 5import { defineMessages, intlShape } from 'react-intl';
5 6
6import { isDevMode, useLiveAPI } from '../../environment'; 7import { isDevMode, useLiveAPI } from '../../environment';
7import Form from '../../lib/Form'; 8import Form from '../../lib/Form';
8import { required, email, minLength } from '../../helpers/validation-helpers'; 9import { required, email, minLength } from '../../helpers/validation-helpers';
10import serverlessLogin from '../../helpers/serverless-helpers';
9import Input from '../ui/Input'; 11import Input from '../ui/Input';
10import Button from '../ui/Button'; 12import Button from '../ui/Button';
11import Link from '../ui/Link'; 13import Link from '../ui/Link';
@@ -58,18 +60,27 @@ const messages = defineMessages({
58 id: 'signup.link.login', 60 id: 'signup.link.login',
59 defaultMessage: '!!!Already have an account, sign in?', 61 defaultMessage: '!!!Already have an account, sign in?',
60 }, 62 },
63 changeServer: {
64 id: 'login.changeServer',
65 defaultMessage: '!!!Change server',
66 },
67 serverless: {
68 id: 'services.serverless',
69 defaultMessage: '!!!Use Ferdi without an Account',
70 },
61 emailDuplicate: { 71 emailDuplicate: {
62 id: 'signup.emailDuplicate', 72 id: 'signup.emailDuplicate',
63 defaultMessage: '!!!A user with that email address already exists', 73 defaultMessage: '!!!A user with that email address already exists',
64 }, 74 },
65}); 75});
66 76
67export default @observer class Signup extends Component { 77export default @observer @inject('actions') class Signup extends Component {
68 static propTypes = { 78 static propTypes = {
69 onSubmit: PropTypes.func.isRequired, 79 onSubmit: PropTypes.func.isRequired,
70 isSubmitting: PropTypes.bool.isRequired, 80 isSubmitting: PropTypes.bool.isRequired,
71 loginRoute: PropTypes.string.isRequired, 81 loginRoute: PropTypes.string.isRequired,
72 error: globalErrorPropType.isRequired, 82 error: globalErrorPropType.isRequired,
83 actions: PropTypes.object.isRequired,
73 }; 84 };
74 85
75 static contextTypes = { 86 static contextTypes = {
@@ -112,6 +123,10 @@ export default @observer class Signup extends Component {
112 }); 123 });
113 } 124 }
114 125
126 useLocalServer() {
127 serverlessLogin(this.props.actions);
128 }
129
115 render() { 130 render() {
116 const { form } = this; 131 const { form } = this;
117 const { intl } = this.context; 132 const { intl } = this.context;
@@ -183,7 +198,8 @@ export default @observer class Signup extends Component {
183 </p> 198 </p>
184 </form> 199 </form>
185 <div className="auth__links"> 200 <div className="auth__links">
186 <Link to="/settings/app">Change server</Link> 201 <Link to="/settings/app">{intl.formatMessage(messages.changeServer)}</Link>
202 <a onClick={this.useLocalServer.bind(this)}>{intl.formatMessage(messages.serverless)}</a>
187 <Link to={loginRoute}>{intl.formatMessage(messages.loginLink)}</Link> 203 <Link to={loginRoute}>{intl.formatMessage(messages.loginLink)}</Link>
188 </div> 204 </div>
189 </div> 205 </div>
diff --git a/src/components/auth/Welcome.js b/src/components/auth/Welcome.js
index ef917e336..2ca8b430f 100644
--- a/src/components/auth/Welcome.js
+++ b/src/components/auth/Welcome.js
@@ -1,7 +1,9 @@
1/* eslint jsx-a11y/anchor-is-valid: 0 */
1import React, { Component } from 'react'; 2import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 3import PropTypes from 'prop-types';
3import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; 4import { observer, PropTypes as MobxPropTypes, inject } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl'; 5import { defineMessages, intlShape } from 'react-intl';
6import serverlessLogin from '../../helpers/serverless-helpers';
5 7
6import Link from '../ui/Link'; 8import Link from '../ui/Link';
7 9
@@ -14,19 +16,28 @@ const messages = defineMessages({
14 id: 'welcome.loginButton', 16 id: 'welcome.loginButton',
15 defaultMessage: '!!!Login to your account', 17 defaultMessage: '!!!Login to your account',
16 }, 18 },
19 serverless: {
20 id: 'services.serverless',
21 defaultMessage: '!!!Use Ferdi without an Account',
22 },
17}); 23});
18 24
19export default @observer class Login extends Component { 25export default @observer @inject('actions') class Login extends Component {
20 static propTypes = { 26 static propTypes = {
21 loginRoute: PropTypes.string.isRequired, 27 loginRoute: PropTypes.string.isRequired,
22 signupRoute: PropTypes.string.isRequired, 28 signupRoute: PropTypes.string.isRequired,
23 recipes: MobxPropTypes.arrayOrObservableArray.isRequired, 29 recipes: MobxPropTypes.arrayOrObservableArray.isRequired,
30 actions: PropTypes.object.isRequired,
24 }; 31 };
25 32
26 static contextTypes = { 33 static contextTypes = {
27 intl: intlShape, 34 intl: intlShape,
28 }; 35 };
29 36
37 useLocalServer() {
38 serverlessLogin(this.props.actions);
39 }
40
30 render() { 41 render() {
31 const { intl } = this.context; 42 const { intl } = this.context;
32 const { 43 const {
@@ -53,6 +64,12 @@ export default @observer class Login extends Component {
53 </Link> 64 </Link>
54 <br /> 65 <br />
55 <br /> 66 <br />
67 <a className="button" onClick={this.useLocalServer.bind(this)}>
68 {intl.formatMessage(messages.serverless)}
69 </a>
70 <br />
71 <br />
72
56 73
57 <Link to="settings/app"> 74 <Link to="settings/app">
58 <span style={{ 75 <span style={{
diff --git a/src/components/layout/Sidebar.js b/src/components/layout/Sidebar.js
index 5e0be36bc..f7d10735c 100644
--- a/src/components/layout/Sidebar.js
+++ b/src/components/layout/Sidebar.js
@@ -116,7 +116,7 @@ export default @inject('stores', 'actions') @observer class Sidebar extends Comp
116 { stores.settings.all.app.lockingFeatureEnabled ? ( 116 { stores.settings.all.app.lockingFeatureEnabled ? (
117 <button 117 <button
118 type="button" 118 type="button"
119 className={`sidebar__button`} 119 className="sidebar__button"
120 onClick={() => { 120 onClick={() => {
121 // Disable lock first - otherwise the application might not update correctly 121 // Disable lock first - otherwise the application might not update correctly
122 actions.settings.update({ 122 actions.settings.update({
@@ -201,6 +201,12 @@ export default @inject('stores', 'actions') @observer class Sidebar extends Comp
201 data-tip={`${intl.formatMessage(messages.settings)} (${ctrlKey}+,)`} 201 data-tip={`${intl.formatMessage(messages.settings)} (${ctrlKey}+,)`}
202 > 202 >
203 <i className="mdi mdi-settings" /> 203 <i className="mdi mdi-settings" />
204 { (this.props.stores.app.updateStatus === this.props.stores.app.updateStatusTypes.AVAILABLE
205 || this.props.stores.app.updateStatus === this.props.stores.app.updateStatusTypes.DOWNLOADED) && (
206 <span className="update-availible">
207
208 </span>
209 ) }
204 </button> 210 </button>
205 {this.state.tooltipEnabled && ( 211 {this.state.tooltipEnabled && (
206 <ReactTooltip place="right" type="dark" effect="solid" /> 212 <ReactTooltip place="right" type="dark" effect="solid" />
diff --git a/src/components/services/content/Services.js b/src/components/services/content/Services.js
index caa3f3b23..80f17d8f2 100644
--- a/src/components/services/content/Services.js
+++ b/src/components/services/content/Services.js
@@ -1,6 +1,6 @@
1import React, { Component } from 'react'; 1import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 2import PropTypes from 'prop-types';
3import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; 3import { observer, PropTypes as MobxPropTypes, inject } from 'mobx-react';
4import { Link } from 'react-router'; 4import { Link } from 'react-router';
5import { defineMessages, intlShape } from 'react-intl'; 5import { defineMessages, intlShape } from 'react-intl';
6import Confetti from 'react-confetti'; 6import Confetti from 'react-confetti';
@@ -9,6 +9,7 @@ import injectSheet from 'react-jss';
9 9
10import ServiceView from './ServiceView'; 10import ServiceView from './ServiceView';
11import Appear from '../../ui/effects/Appear'; 11import Appear from '../../ui/effects/Appear';
12import serverlessLogin from '../../../helpers/serverless-helpers';
12 13
13const messages = defineMessages({ 14const messages = defineMessages({
14 welcome: { 15 welcome: {
@@ -23,6 +24,10 @@ const messages = defineMessages({
23 id: 'services.login', 24 id: 'services.login',
24 defaultMessage: '!!!Please login to use Ferdi.', 25 defaultMessage: '!!!Please login to use Ferdi.',
25 }, 26 },
27 serverless: {
28 id: 'services.serverless',
29 defaultMessage: '!!!Use Ferdi without an Account',
30 },
26 serverInfo: { 31 serverInfo: {
27 id: 'services.serverInfo', 32 id: 'services.serverInfo',
28 defaultMessage: '!!!Optionally, you can change your Ferdi server by clicking the cog in the bottom left corner.', 33 defaultMessage: '!!!Optionally, you can change your Ferdi server by clicking the cog in the bottom left corner.',
@@ -39,7 +44,7 @@ const styles = {
39 }, 44 },
40}; 45};
41 46
42export default @injectSheet(styles) @observer class Services extends Component { 47export default @injectSheet(styles) @inject('actions') @observer class Services extends Component {
43 static propTypes = { 48 static propTypes = {
44 services: MobxPropTypes.arrayOrObservableArray, 49 services: MobxPropTypes.arrayOrObservableArray,
45 setWebviewReference: PropTypes.func.isRequired, 50 setWebviewReference: PropTypes.func.isRequired,
@@ -52,6 +57,7 @@ export default @injectSheet(styles) @observer class Services extends Component {
52 userHasCompletedSignup: PropTypes.bool.isRequired, 57 userHasCompletedSignup: PropTypes.bool.isRequired,
53 hasActivatedTrial: PropTypes.bool.isRequired, 58 hasActivatedTrial: PropTypes.bool.isRequired,
54 classes: PropTypes.object.isRequired, 59 classes: PropTypes.object.isRequired,
60 actions: PropTypes.object.isRequired,
55 }; 61 };
56 62
57 static defaultProps = { 63 static defaultProps = {
@@ -68,6 +74,12 @@ export default @injectSheet(styles) @observer class Services extends Component {
68 74
69 _confettiTimeout = null; 75 _confettiTimeout = null;
70 76
77 constructor(props) {
78 super(props);
79
80 this.useLocalServer = this.useLocalServer.bind(this);
81 }
82
71 componentDidMount() { 83 componentDidMount() {
72 this._confettiTimeout = window.setTimeout(() => { 84 this._confettiTimeout = window.setTimeout(() => {
73 this.setState({ 85 this.setState({
@@ -82,6 +94,10 @@ export default @injectSheet(styles) @observer class Services extends Component {
82 } 94 }
83 } 95 }
84 96
97 useLocalServer() {
98 serverlessLogin(this.props.actions);
99 }
100
85 render() { 101 render() {
86 const { 102 const {
87 services, 103 services,
@@ -136,6 +152,18 @@ export default @injectSheet(styles) @observer class Services extends Component {
136 <Link to={isLoggedIn ? '/settings/services' : '/auth/welcome'} className="button"> 152 <Link to={isLoggedIn ? '/settings/services' : '/auth/welcome'} className="button">
137 { isLoggedIn ? intl.formatMessage(messages.getStarted) : 'Login' } 153 { isLoggedIn ? intl.formatMessage(messages.getStarted) : 'Login' }
138 </Link> 154 </Link>
155 {!isLoggedIn && (
156 <button
157 type="button"
158 className="button"
159 style={{
160 marginLeft: 10,
161 }}
162 onClick={this.useLocalServer}
163 >
164 {intl.formatMessage(messages.serverless)}
165 </button>
166 )}
139 </Appear> 167 </Appear>
140 </div> 168 </div>
141 </Appear> 169 </Appear>
diff --git a/src/components/settings/team/TeamDashboard.js b/src/components/settings/team/TeamDashboard.js
index 2bf46b48d..3a38d682b 100644
--- a/src/components/settings/team/TeamDashboard.js
+++ b/src/components/settings/team/TeamDashboard.js
@@ -38,6 +38,14 @@ const messages = defineMessages({
38 id: 'settings.team.upgradeAction', 38 id: 'settings.team.upgradeAction',
39 defaultMessage: '!!!Upgrade your Account', 39 defaultMessage: '!!!Upgrade your Account',
40 }, 40 },
41 teamsUnavailible: {
42 id: 'settings.team.teamsUnavailible',
43 defaultMessage: '!!!Teams are unavailible',
44 },
45 teamsUnavailibleInfo: {
46 id: 'settings.team.teamsUnavailibleInfo',
47 defaultMessage: '!!!Teams are currently only availible when using the Franz Server and after paying for Franz Professional. Please change your server to https://api.franzinfra.com to use teams.',
48 },
41}); 49});
42 50
43const styles = { 51const styles = {
@@ -98,6 +106,7 @@ export default @injectSheet(styles) @observer class TeamDashboard extends Compon
98 openTeamManagement: PropTypes.func.isRequired, 106 openTeamManagement: PropTypes.func.isRequired,
99 classes: PropTypes.object.isRequired, 107 classes: PropTypes.object.isRequired,
100 isProUser: PropTypes.bool.isRequired, 108 isProUser: PropTypes.bool.isRequired,
109 server: PropTypes.string.isRequired,
101 }; 110 };
102 111
103 static contextTypes = { 112 static contextTypes = {
@@ -112,9 +121,84 @@ export default @injectSheet(styles) @observer class TeamDashboard extends Compon
112 openTeamManagement, 121 openTeamManagement,
113 isProUser, 122 isProUser,
114 classes, 123 classes,
124 server,
115 } = this.props; 125 } = this.props;
116 const { intl } = this.context; 126 const { intl } = this.context;
117 127
128 if (server === 'https://api.franzinfra.com') {
129 return (
130 <div className="settings__main">
131 <div className="settings__header">
132 <span className="settings__header-item">
133 {intl.formatMessage(messages.headline)}
134 </span>
135 </div>
136 <div className="settings__body">
137 {isLoading && (
138 <Loader />
139 )}
140
141 {!isLoading && userInfoRequestFailed && (
142 <Infobox
143 icon="alert"
144 type="danger"
145 ctaLabel={intl.formatMessage(messages.tryReloadUserInfoRequest)}
146 ctaLoading={isLoading}
147 ctaOnClick={retryUserInfoRequest}
148 >
149 {intl.formatMessage(messages.userInfoRequestFailed)}
150 </Infobox>
151 )}
152
153 {!userInfoRequestFailed && (
154 <>
155 {!isLoading && (
156 <>
157 <>
158 <h1 className={classnames({
159 [classes.headline]: true,
160 [classes.headlineWithSpacing]: isProUser,
161 })}
162 >
163 {intl.formatMessage(messages.contentHeadline)}
164
165 </h1>
166 {!isProUser && (
167 <Badge className={classes.proRequired}>{intl.formatMessage(globalMessages.proRequired)}</Badge>
168 )}
169 <div className={classes.container}>
170 <div className={classes.content}>
171 <p>{intl.formatMessage(messages.intro)}</p>
172 <p>{intl.formatMessage(messages.copy)}</p>
173 </div>
174 <img className={classes.image} src="https://cdn.franzinfra.com/announcements/assets/teams.png" alt="Franz for Teams" />
175 </div>
176 <div className={classes.buttonContainer}>
177 {!isProUser ? (
178 <UpgradeButton
179 className={classes.cta}
180 gaEventInfo={{ category: 'Todos', event: 'upgrade' }}
181 requiresPro
182 short
183 />
184 ) : (
185 <Button
186 label={intl.formatMessage(messages.manageButton)}
187 onClick={openTeamManagement}
188 className={classes.cta}
189 />
190 )}
191 </div>
192 </>
193 </>
194 )}
195 </>
196 )}
197 </div>
198 <ReactTooltip place="right" type="dark" effect="solid" />
199 </div>
200 );
201 }
118 return ( 202 return (
119 <div className="settings__main"> 203 <div className="settings__main">
120 <div className="settings__header"> 204 <div className="settings__header">
@@ -123,68 +207,11 @@ export default @injectSheet(styles) @observer class TeamDashboard extends Compon
123 </span> 207 </span>
124 </div> 208 </div>
125 <div className="settings__body"> 209 <div className="settings__body">
126 {isLoading && ( 210 <h1 className={classes.headline}>
127 <Loader /> 211 {intl.formatMessage(messages.teamsUnavailible)}
128 )} 212 </h1>
129 213 {intl.formatMessage(messages.teamsUnavailibleInfo)}
130 {!isLoading && userInfoRequestFailed && (
131 <Infobox
132 icon="alert"
133 type="danger"
134 ctaLabel={intl.formatMessage(messages.tryReloadUserInfoRequest)}
135 ctaLoading={isLoading}
136 ctaOnClick={retryUserInfoRequest}
137 >
138 {intl.formatMessage(messages.userInfoRequestFailed)}
139 </Infobox>
140 )}
141
142 {!userInfoRequestFailed && (
143 <>
144 {!isLoading && (
145 <>
146 <>
147 <h1 className={classnames({
148 [classes.headline]: true,
149 [classes.headlineWithSpacing]: isProUser,
150 })}
151 >
152 {intl.formatMessage(messages.contentHeadline)}
153
154 </h1>
155 {!isProUser && (
156 <Badge className={classes.proRequired}>{intl.formatMessage(globalMessages.proRequired)}</Badge>
157 )}
158 <div className={classes.container}>
159 <div className={classes.content}>
160 <p>{intl.formatMessage(messages.intro)}</p>
161 <p>{intl.formatMessage(messages.copy)}</p>
162 </div>
163 <img className={classes.image} src="https://cdn.franzinfra.com/announcements/assets/teams.png" alt="Franz for Teams" />
164 </div>
165 <div className={classes.buttonContainer}>
166 {!isProUser ? (
167 <UpgradeButton
168 className={classes.cta}
169 gaEventInfo={{ category: 'Todos', event: 'upgrade' }}
170 requiresPro
171 short
172 />
173 ) : (
174 <Button
175 label={intl.formatMessage(messages.manageButton)}
176 onClick={openTeamManagement}
177 className={classes.cta}
178 />
179 )}
180 </div>
181 </>
182 </>
183 )}
184 </>
185 )}
186 </div> 214 </div>
187 <ReactTooltip place="right" type="dark" effect="solid" />
188 </div> 215 </div>
189 ); 216 );
190 } 217 }
diff --git a/src/config.js b/src/config.js
index 0673e994a..e30a6d4b2 100644
--- a/src/config.js
+++ b/src/config.js
@@ -111,6 +111,8 @@ export const FILE_SYSTEM_SETTINGS_TYPES = [
111 'proxy', 111 'proxy',
112]; 112];
113 113
114export const LOCAL_SERVER = 'You are using Ferdi without a server';
115
114export const SETTINGS_PATH = path.join(app.getPath('userData'), 'config'); 116export const SETTINGS_PATH = path.join(app.getPath('userData'), 'config');
115 117
116// Replacing app.asar is not beautiful but unforunately necessary 118// Replacing app.asar is not beautiful but unforunately necessary
diff --git a/src/containers/settings/EditSettingsScreen.js b/src/containers/settings/EditSettingsScreen.js
index 3898d2b99..ddee82e45 100644
--- a/src/containers/settings/EditSettingsScreen.js
+++ b/src/containers/settings/EditSettingsScreen.js
@@ -414,7 +414,6 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
414 cacheSize, 414 cacheSize,
415 updateStatusTypes, 415 updateStatusTypes,
416 isClearingAllCache, 416 isClearingAllCache,
417 server,
418 lockingFeatureEnabled, 417 lockingFeatureEnabled,
419 } = app; 418 } = app;
420 const { 419 const {
@@ -441,7 +440,7 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
441 isSpellcheckerIncludedInCurrentPlan={spellcheckerConfig.isIncludedInCurrentPlan} 440 isSpellcheckerIncludedInCurrentPlan={spellcheckerConfig.isIncludedInCurrentPlan}
442 isTodosEnabled={todos.isFeatureActive} 441 isTodosEnabled={todos.isFeatureActive}
443 isWorkspaceEnabled={workspaces.isFeatureActive} 442 isWorkspaceEnabled={workspaces.isFeatureActive}
444 server={server || 'https://api.franzinfra.com'} 443 server={this.props.stores.settings.app.server}
445 lockingFeatureEnabled={lockingFeatureEnabled} 444 lockingFeatureEnabled={lockingFeatureEnabled}
446 noUpdates={this.props.stores.settings.app.noUpdates} 445 noUpdates={this.props.stores.settings.app.noUpdates}
447 hibernationEnabled={this.props.stores.settings.app.hibernate} 446 hibernationEnabled={this.props.stores.settings.app.hibernate}
diff --git a/src/containers/settings/TeamScreen.js b/src/containers/settings/TeamScreen.js
index f600c9947..d0196923a 100644
--- a/src/containers/settings/TeamScreen.js
+++ b/src/containers/settings/TeamScreen.js
@@ -4,6 +4,7 @@ import { inject, observer } from 'mobx-react';
4 4
5import UserStore from '../../stores/UserStore'; 5import UserStore from '../../stores/UserStore';
6import AppStore from '../../stores/AppStore'; 6import AppStore from '../../stores/AppStore';
7import SettingsStore from '../../stores/SettingsStore';
7 8
8import TeamDashboard from '../../components/settings/team/TeamDashboard'; 9import TeamDashboard from '../../components/settings/team/TeamDashboard';
9import ErrorBoundary from '../../components/util/ErrorBoundary'; 10import ErrorBoundary from '../../components/util/ErrorBoundary';
@@ -19,9 +20,10 @@ export default @inject('stores', 'actions') @observer class TeamScreen extends C
19 } 20 }
20 21
21 render() { 22 render() {
22 const { user } = this.props.stores; 23 const { user, settings } = this.props.stores;
23 24
24 const isLoadingUserInfo = user.getUserInfoRequest.isExecuting; 25 const isLoadingUserInfo = user.getUserInfoRequest.isExecuting;
26 const { server } = settings.app;
25 27
26 return ( 28 return (
27 <ErrorBoundary> 29 <ErrorBoundary>
@@ -31,6 +33,7 @@ export default @inject('stores', 'actions') @observer class TeamScreen extends C
31 retryUserInfoRequest={() => this.reloadData()} 33 retryUserInfoRequest={() => this.reloadData()}
32 openTeamManagement={() => this.handleWebsiteLink('/user/team')} 34 openTeamManagement={() => this.handleWebsiteLink('/user/team')}
33 isProUser={user.isPro} 35 isProUser={user.isPro}
36 server={server}
34 /> 37 />
35 </ErrorBoundary> 38 </ErrorBoundary>
36 ); 39 );
@@ -41,6 +44,7 @@ TeamScreen.wrappedComponent.propTypes = {
41 stores: PropTypes.shape({ 44 stores: PropTypes.shape({
42 user: PropTypes.instanceOf(UserStore).isRequired, 45 user: PropTypes.instanceOf(UserStore).isRequired,
43 app: PropTypes.instanceOf(AppStore).isRequired, 46 app: PropTypes.instanceOf(AppStore).isRequired,
47 settings: PropTypes.instanceOf(SettingsStore).isRequired,
44 }).isRequired, 48 }).isRequired,
45 actions: PropTypes.shape({ 49 actions: PropTypes.shape({
46 payment: PropTypes.shape({ 50 payment: PropTypes.shape({
diff --git a/src/electron/ipc-api/index.js b/src/electron/ipc-api/index.js
index 3b7f31e4b..dcdef6b32 100644
--- a/src/electron/ipc-api/index.js
+++ b/src/electron/ipc-api/index.js
@@ -3,6 +3,7 @@ import settings from './settings';
3import appIndicator from './appIndicator'; 3import appIndicator from './appIndicator';
4import download from './download'; 4import download from './download';
5import processManager from './processManager'; 5import processManager from './processManager';
6import localServer from './localServer';
6 7
7export default (params) => { 8export default (params) => {
8 settings(params); 9 settings(params);
@@ -10,4 +11,5 @@ export default (params) => {
10 appIndicator(params); 11 appIndicator(params);
11 download(params); 12 download(params);
12 processManager(params); 13 processManager(params);
14 localServer(params);
13}; 15};
diff --git a/src/electron/ipc-api/localServer.js b/src/electron/ipc-api/localServer.js
new file mode 100644
index 000000000..2f8f1020a
--- /dev/null
+++ b/src/electron/ipc-api/localServer.js
@@ -0,0 +1,52 @@
1import { ipcMain, app } from 'electron';
2import path from 'path';
3import net from 'net';
4import startServer from '../../server/start';
5
6const DEFAULT_PORT = 45569;
7
8const portInUse = function (port) {
9 return new Promise((resolve) => {
10 const server = net.createServer((socket) => {
11 socket.write('Echo server\r\n');
12 socket.pipe(socket);
13 });
14
15 server.listen(port, '127.0.0.1');
16 server.on('error', () => {
17 resolve(true);
18 });
19 server.on('listening', () => {
20 server.close();
21 resolve(false);
22 });
23 });
24};
25
26let localServerStarted = false;
27
28export default (params) => {
29 ipcMain.on('startLocalServer', () => {
30 if (!localServerStarted) {
31 // Find next unused port for server
32 let port = DEFAULT_PORT;
33 (async () => {
34 // eslint-disable-next-line no-await-in-loop
35 while (await portInUse(port) && port < DEFAULT_PORT + 10) {
36 port += 1;
37 }
38 console.log('Starting local server on port', port);
39
40 startServer(
41 path.join(app.getPath('userData'), 'server.sqlite'),
42 port,
43 );
44
45 params.mainWindow.webContents.send('localServerPort', {
46 port,
47 });
48 })();
49 localServerStarted = true;
50 }
51 });
52};
diff --git a/src/features/announcements/api.js b/src/features/announcements/api.js
index f33980d0f..e5c5a7d6f 100644
--- a/src/features/announcements/api.js
+++ b/src/features/announcements/api.js
@@ -1,6 +1,5 @@
1import { remote } from 'electron'; 1import { remote } from 'electron';
2import Request from '../../stores/lib/Request'; 2import Request from '../../stores/lib/Request';
3import apiBase from '../../api/apiBase';
4 3
5const debug = require('debug')('Ferdi:feature:announcements:api'); 4const debug = require('debug')('Ferdi:feature:announcements:api');
6 5
@@ -21,7 +20,7 @@ export const announcementsApi = {
21 20
22 async getAnnouncement(version) { 21 async getAnnouncement(version) {
23 debug('fetching release announcement from api'); 22 debug('fetching release announcement from api');
24 const url = `${apiBase()}/announcements/${version}`; 23 const url = `https://api.getferdi.com/v1/announcements/${version}`;
25 const response = await window.fetch(url, { method: 'GET' }); 24 const response = await window.fetch(url, { method: 'GET' });
26 if (!response.ok) return null; 25 if (!response.ok) return null;
27 return response.json(); 26 return response.json();
diff --git a/src/helpers/serverless-helpers.js b/src/helpers/serverless-helpers.js
new file mode 100644
index 000000000..01549e038
--- /dev/null
+++ b/src/helpers/serverless-helpers.js
@@ -0,0 +1,18 @@
1import { LOCAL_SERVER } from '../config';
2
3export default function useLocalServer(actions) {
4 // Use local server for user
5 actions.settings.update({
6 type: 'app',
7 data: {
8 server: LOCAL_SERVER,
9 },
10 });
11
12 // Log into local server
13 // Credentials are ignored by the server but the client requires them
14 actions.user.login({
15 email: 'ferdi@localhost',
16 password: 'FERDI_',
17 });
18}
diff --git a/src/i18n/apply-branding.js b/src/i18n/apply-branding.js
index 521186d08..1494ce923 100644
--- a/src/i18n/apply-branding.js
+++ b/src/i18n/apply-branding.js
@@ -12,6 +12,7 @@ const ignore = [
12 'login.customServerQuestion', 12 'login.customServerQuestion',
13 'settings.app.todoServerInfo', 13 'settings.app.todoServerInfo',
14 'settings.app.serverMoneyInfo', 14 'settings.app.serverMoneyInfo',
15 'settings.team.teamsUnavailibleInfo',
15]; 16];
16 17
17// Files to ignore when applying branding 18// Files to ignore when applying branding
diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json
index 6f380af3b..c9f5ac50c 100644
--- a/src/i18n/locales/defaultMessages.json
+++ b/src/i18n/locales/defaultMessages.json
@@ -272,143 +272,169 @@
272 "defaultMessage": "!!!Sign in", 272 "defaultMessage": "!!!Sign in",
273 "end": { 273 "end": {
274 "column": 3, 274 "column": 3,
275 "line": 20 275 "line": 22
276 }, 276 },
277 "file": "src/components/auth/Login.js", 277 "file": "src/components/auth/Login.js",
278 "id": "login.headline", 278 "id": "login.headline",
279 "start": { 279 "start": {
280 "column": 12, 280 "column": 12,
281 "line": 17 281 "line": 19
282 } 282 }
283 }, 283 },
284 { 284 {
285 "defaultMessage": "!!!Email address", 285 "defaultMessage": "!!!Email address",
286 "end": { 286 "end": {
287 "column": 3, 287 "column": 3,
288 "line": 24 288 "line": 26
289 }, 289 },
290 "file": "src/components/auth/Login.js", 290 "file": "src/components/auth/Login.js",
291 "id": "login.email.label", 291 "id": "login.email.label",
292 "start": { 292 "start": {
293 "column": 14, 293 "column": 14,
294 "line": 21 294 "line": 23
295 } 295 }
296 }, 296 },
297 { 297 {
298 "defaultMessage": "!!!Password", 298 "defaultMessage": "!!!Password",
299 "end": { 299 "end": {
300 "column": 3, 300 "column": 3,
301 "line": 28 301 "line": 30
302 }, 302 },
303 "file": "src/components/auth/Login.js", 303 "file": "src/components/auth/Login.js",
304 "id": "login.password.label", 304 "id": "login.password.label",
305 "start": { 305 "start": {
306 "column": 17, 306 "column": 17,
307 "line": 25 307 "line": 27
308 } 308 }
309 }, 309 },
310 { 310 {
311 "defaultMessage": "!!!Sign in", 311 "defaultMessage": "!!!Sign in",
312 "end": { 312 "end": {
313 "column": 3, 313 "column": 3,
314 "line": 32 314 "line": 34
315 }, 315 },
316 "file": "src/components/auth/Login.js", 316 "file": "src/components/auth/Login.js",
317 "id": "login.submit.label", 317 "id": "login.submit.label",
318 "start": { 318 "start": {
319 "column": 21, 319 "column": 21,
320 "line": 29 320 "line": 31
321 } 321 }
322 }, 322 },
323 { 323 {
324 "defaultMessage": "!!!Email or password not valid", 324 "defaultMessage": "!!!Email or password not valid",
325 "end": { 325 "end": {
326 "column": 3, 326 "column": 3,
327 "line": 36 327 "line": 38
328 }, 328 },
329 "file": "src/components/auth/Login.js", 329 "file": "src/components/auth/Login.js",
330 "id": "login.invalidCredentials", 330 "id": "login.invalidCredentials",
331 "start": { 331 "start": {
332 "column": 22, 332 "column": 22,
333 "line": 33 333 "line": 35
334 } 334 }
335 }, 335 },
336 { 336 {
337 "defaultMessage": "!!!Using a Franz account to log in?", 337 "defaultMessage": "!!!Using a Franz account to log in?",
338 "end": { 338 "end": {
339 "column": 3, 339 "column": 3,
340 "line": 40 340 "line": 42
341 }, 341 },
342 "file": "src/components/auth/Login.js", 342 "file": "src/components/auth/Login.js",
343 "id": "login.customServerQuestion", 343 "id": "login.customServerQuestion",
344 "start": { 344 "start": {
345 "column": 24, 345 "column": 24,
346 "line": 37 346 "line": 39
347 } 347 }
348 }, 348 },
349 { 349 {
350 "defaultMessage": "!!!Try importing your Franz account into Ferdi", 350 "defaultMessage": "!!!Try importing your Franz account into Ferdi",
351 "end": { 351 "end": {
352 "column": 3, 352 "column": 3,
353 "line": 44 353 "line": 46
354 }, 354 },
355 "file": "src/components/auth/Login.js", 355 "file": "src/components/auth/Login.js",
356 "id": "login.customServerSuggestion", 356 "id": "login.customServerSuggestion",
357 "start": { 357 "start": {
358 "column": 26, 358 "column": 26,
359 "line": 41 359 "line": 43
360 } 360 }
361 }, 361 },
362 { 362 {
363 "defaultMessage": "!!!Your session expired, please login again.", 363 "defaultMessage": "!!!Your session expired, please login again.",
364 "end": { 364 "end": {
365 "column": 3, 365 "column": 3,
366 "line": 48 366 "line": 50
367 }, 367 },
368 "file": "src/components/auth/Login.js", 368 "file": "src/components/auth/Login.js",
369 "id": "login.tokenExpired", 369 "id": "login.tokenExpired",
370 "start": { 370 "start": {
371 "column": 16, 371 "column": 16,
372 "line": 45 372 "line": 47
373 } 373 }
374 }, 374 },
375 { 375 {
376 "defaultMessage": "!!!Your session expired, please login again.", 376 "defaultMessage": "!!!Your session expired, please login again.",
377 "end": { 377 "end": {
378 "column": 3, 378 "column": 3,
379 "line": 52 379 "line": 54
380 }, 380 },
381 "file": "src/components/auth/Login.js", 381 "file": "src/components/auth/Login.js",
382 "id": "login.serverLogout", 382 "id": "login.serverLogout",
383 "start": { 383 "start": {
384 "column": 16, 384 "column": 16,
385 "line": 49 385 "line": 51
386 } 386 }
387 }, 387 },
388 { 388 {
389 "defaultMessage": "!!!Create a free account", 389 "defaultMessage": "!!!Create a free account",
390 "end": { 390 "end": {
391 "column": 3, 391 "column": 3,
392 "line": 56 392 "line": 58
393 }, 393 },
394 "file": "src/components/auth/Login.js", 394 "file": "src/components/auth/Login.js",
395 "id": "login.link.signup", 395 "id": "login.link.signup",
396 "start": { 396 "start": {
397 "column": 14, 397 "column": 14,
398 "line": 53 398 "line": 55
399 }
400 },
401 {
402 "defaultMessage": "!!!Change server",
403 "end": {
404 "column": 3,
405 "line": 62
406 },
407 "file": "src/components/auth/Login.js",
408 "id": "login.changeServer",
409 "start": {
410 "column": 16,
411 "line": 59
412 }
413 },
414 {
415 "defaultMessage": "!!!Use Ferdi without an Account",
416 "end": {
417 "column": 3,
418 "line": 66
419 },
420 "file": "src/components/auth/Login.js",
421 "id": "services.serverless",
422 "start": {
423 "column": 14,
424 "line": 63
399 } 425 }
400 }, 426 },
401 { 427 {
402 "defaultMessage": "!!!Forgot password", 428 "defaultMessage": "!!!Forgot password",
403 "end": { 429 "end": {
404 "column": 3, 430 "column": 3,
405 "line": 60 431 "line": 70
406 }, 432 },
407 "file": "src/components/auth/Login.js", 433 "file": "src/components/auth/Login.js",
408 "id": "login.link.password", 434 "id": "login.link.password",
409 "start": { 435 "start": {
410 "column": 16, 436 "column": 16,
411 "line": 57 437 "line": 67
412 } 438 }
413 } 439 }
414 ], 440 ],
@@ -638,143 +664,169 @@
638 "defaultMessage": "!!!Sign up", 664 "defaultMessage": "!!!Sign up",
639 "end": { 665 "end": {
640 "column": 3, 666 "column": 3,
641 "line": 20 667 "line": 22
642 }, 668 },
643 "file": "src/components/auth/Signup.js", 669 "file": "src/components/auth/Signup.js",
644 "id": "signup.headline", 670 "id": "signup.headline",
645 "start": { 671 "start": {
646 "column": 12, 672 "column": 12,
647 "line": 17 673 "line": 19
648 } 674 }
649 }, 675 },
650 { 676 {
651 "defaultMessage": "!!!Firstname", 677 "defaultMessage": "!!!Firstname",
652 "end": { 678 "end": {
653 "column": 3, 679 "column": 3,
654 "line": 24 680 "line": 26
655 }, 681 },
656 "file": "src/components/auth/Signup.js", 682 "file": "src/components/auth/Signup.js",
657 "id": "signup.firstname.label", 683 "id": "signup.firstname.label",
658 "start": { 684 "start": {
659 "column": 18, 685 "column": 18,
660 "line": 21 686 "line": 23
661 } 687 }
662 }, 688 },
663 { 689 {
664 "defaultMessage": "!!!Lastname", 690 "defaultMessage": "!!!Lastname",
665 "end": { 691 "end": {
666 "column": 3, 692 "column": 3,
667 "line": 28 693 "line": 30
668 }, 694 },
669 "file": "src/components/auth/Signup.js", 695 "file": "src/components/auth/Signup.js",
670 "id": "signup.lastname.label", 696 "id": "signup.lastname.label",
671 "start": { 697 "start": {
672 "column": 17, 698 "column": 17,
673 "line": 25 699 "line": 27
674 } 700 }
675 }, 701 },
676 { 702 {
677 "defaultMessage": "!!!Email address", 703 "defaultMessage": "!!!Email address",
678 "end": { 704 "end": {
679 "column": 3, 705 "column": 3,
680 "line": 32 706 "line": 34
681 }, 707 },
682 "file": "src/components/auth/Signup.js", 708 "file": "src/components/auth/Signup.js",
683 "id": "signup.email.label", 709 "id": "signup.email.label",
684 "start": { 710 "start": {
685 "column": 14, 711 "column": 14,
686 "line": 29 712 "line": 31
687 } 713 }
688 }, 714 },
689 { 715 {
690 "defaultMessage": "!!!Password", 716 "defaultMessage": "!!!Password",
691 "end": { 717 "end": {
692 "column": 3, 718 "column": 3,
693 "line": 40 719 "line": 42
694 }, 720 },
695 "file": "src/components/auth/Signup.js", 721 "file": "src/components/auth/Signup.js",
696 "id": "signup.password.label", 722 "id": "signup.password.label",
697 "start": { 723 "start": {
698 "column": 17, 724 "column": 17,
699 "line": 37 725 "line": 39
700 } 726 }
701 }, 727 },
702 { 728 {
703 "defaultMessage": "!!!By creating a Ferdi account you accept the", 729 "defaultMessage": "!!!By creating a Ferdi account you accept the",
704 "end": { 730 "end": {
705 "column": 3, 731 "column": 3,
706 "line": 44 732 "line": 46
707 }, 733 },
708 "file": "src/components/auth/Signup.js", 734 "file": "src/components/auth/Signup.js",
709 "id": "signup.legal.info", 735 "id": "signup.legal.info",
710 "start": { 736 "start": {
711 "column": 13, 737 "column": 13,
712 "line": 41 738 "line": 43
713 } 739 }
714 }, 740 },
715 { 741 {
716 "defaultMessage": "!!!Terms of service", 742 "defaultMessage": "!!!Terms of service",
717 "end": { 743 "end": {
718 "column": 3, 744 "column": 3,
719 "line": 48 745 "line": 50
720 }, 746 },
721 "file": "src/components/auth/Signup.js", 747 "file": "src/components/auth/Signup.js",
722 "id": "signup.legal.terms", 748 "id": "signup.legal.terms",
723 "start": { 749 "start": {
724 "column": 9, 750 "column": 9,
725 "line": 45 751 "line": 47
726 } 752 }
727 }, 753 },
728 { 754 {
729 "defaultMessage": "!!!Privacy Statement", 755 "defaultMessage": "!!!Privacy Statement",
730 "end": { 756 "end": {
731 "column": 3, 757 "column": 3,
732 "line": 52 758 "line": 54
733 }, 759 },
734 "file": "src/components/auth/Signup.js", 760 "file": "src/components/auth/Signup.js",
735 "id": "signup.legal.privacy", 761 "id": "signup.legal.privacy",
736 "start": { 762 "start": {
737 "column": 11, 763 "column": 11,
738 "line": 49 764 "line": 51
739 } 765 }
740 }, 766 },
741 { 767 {
742 "defaultMessage": "!!!Create account", 768 "defaultMessage": "!!!Create account",
743 "end": { 769 "end": {
744 "column": 3, 770 "column": 3,
745 "line": 56 771 "line": 58
746 }, 772 },
747 "file": "src/components/auth/Signup.js", 773 "file": "src/components/auth/Signup.js",
748 "id": "signup.submit.label", 774 "id": "signup.submit.label",
749 "start": { 775 "start": {
750 "column": 21, 776 "column": 21,
751 "line": 53 777 "line": 55
752 } 778 }
753 }, 779 },
754 { 780 {
755 "defaultMessage": "!!!Already have an account, sign in?", 781 "defaultMessage": "!!!Already have an account, sign in?",
756 "end": { 782 "end": {
757 "column": 3, 783 "column": 3,
758 "line": 60 784 "line": 62
759 }, 785 },
760 "file": "src/components/auth/Signup.js", 786 "file": "src/components/auth/Signup.js",
761 "id": "signup.link.login", 787 "id": "signup.link.login",
762 "start": { 788 "start": {
763 "column": 13, 789 "column": 13,
764 "line": 57 790 "line": 59
791 }
792 },
793 {
794 "defaultMessage": "!!!Change server",
795 "end": {
796 "column": 3,
797 "line": 66
798 },
799 "file": "src/components/auth/Signup.js",
800 "id": "login.changeServer",
801 "start": {
802 "column": 16,
803 "line": 63
804 }
805 },
806 {
807 "defaultMessage": "!!!Use Ferdi without an Account",
808 "end": {
809 "column": 3,
810 "line": 70
811 },
812 "file": "src/components/auth/Signup.js",
813 "id": "services.serverless",
814 "start": {
815 "column": 14,
816 "line": 67
765 } 817 }
766 }, 818 },
767 { 819 {
768 "defaultMessage": "!!!A user with that email address already exists", 820 "defaultMessage": "!!!A user with that email address already exists",
769 "end": { 821 "end": {
770 "column": 3, 822 "column": 3,
771 "line": 64 823 "line": 74
772 }, 824 },
773 "file": "src/components/auth/Signup.js", 825 "file": "src/components/auth/Signup.js",
774 "id": "signup.emailDuplicate", 826 "id": "signup.emailDuplicate",
775 "start": { 827 "start": {
776 "column": 18, 828 "column": 18,
777 "line": 61 829 "line": 71
778 } 830 }
779 } 831 }
780 ], 832 ],
@@ -786,26 +838,39 @@
786 "defaultMessage": "!!!Create a free account", 838 "defaultMessage": "!!!Create a free account",
787 "end": { 839 "end": {
788 "column": 3, 840 "column": 3,
789 "line": 12 841 "line": 14
790 }, 842 },
791 "file": "src/components/auth/Welcome.js", 843 "file": "src/components/auth/Welcome.js",
792 "id": "welcome.signupButton", 844 "id": "welcome.signupButton",
793 "start": { 845 "start": {
794 "column": 16, 846 "column": 16,
795 "line": 9 847 "line": 11
796 } 848 }
797 }, 849 },
798 { 850 {
799 "defaultMessage": "!!!Login to your account", 851 "defaultMessage": "!!!Login to your account",
800 "end": { 852 "end": {
801 "column": 3, 853 "column": 3,
802 "line": 16 854 "line": 18
803 }, 855 },
804 "file": "src/components/auth/Welcome.js", 856 "file": "src/components/auth/Welcome.js",
805 "id": "welcome.loginButton", 857 "id": "welcome.loginButton",
806 "start": { 858 "start": {
807 "column": 15, 859 "column": 15,
808 "line": 13 860 "line": 15
861 }
862 },
863 {
864 "defaultMessage": "!!!Use Ferdi without an Account",
865 "end": {
866 "column": 3,
867 "line": 22
868 },
869 "file": "src/components/auth/Welcome.js",
870 "id": "services.serverless",
871 "start": {
872 "column": 14,
873 "line": 19
809 } 874 }
810 } 875 }
811 ], 876 ],
@@ -1167,52 +1232,65 @@
1167 "defaultMessage": "!!!Welcome to Ferdi", 1232 "defaultMessage": "!!!Welcome to Ferdi",
1168 "end": { 1233 "end": {
1169 "column": 3, 1234 "column": 3,
1170 "line": 17 1235 "line": 18
1171 }, 1236 },
1172 "file": "src/components/services/content/Services.js", 1237 "file": "src/components/services/content/Services.js",
1173 "id": "services.welcome", 1238 "id": "services.welcome",
1174 "start": { 1239 "start": {
1175 "column": 11, 1240 "column": 11,
1176 "line": 14 1241 "line": 15
1177 } 1242 }
1178 }, 1243 },
1179 { 1244 {
1180 "defaultMessage": "!!!Get started", 1245 "defaultMessage": "!!!Get started",
1181 "end": { 1246 "end": {
1182 "column": 3, 1247 "column": 3,
1183 "line": 21 1248 "line": 22
1184 }, 1249 },
1185 "file": "src/components/services/content/Services.js", 1250 "file": "src/components/services/content/Services.js",
1186 "id": "services.getStarted", 1251 "id": "services.getStarted",
1187 "start": { 1252 "start": {
1188 "column": 14, 1253 "column": 14,
1189 "line": 18 1254 "line": 19
1190 } 1255 }
1191 }, 1256 },
1192 { 1257 {
1193 "defaultMessage": "!!!Please login to use Ferdi.", 1258 "defaultMessage": "!!!Please login to use Ferdi.",
1194 "end": { 1259 "end": {
1195 "column": 3, 1260 "column": 3,
1196 "line": 25 1261 "line": 26
1197 }, 1262 },
1198 "file": "src/components/services/content/Services.js", 1263 "file": "src/components/services/content/Services.js",
1199 "id": "services.login", 1264 "id": "services.login",
1200 "start": { 1265 "start": {
1201 "column": 9, 1266 "column": 9,
1202 "line": 22 1267 "line": 23
1268 }
1269 },
1270 {
1271 "defaultMessage": "!!!Use Ferdi without an Account",
1272 "end": {
1273 "column": 3,
1274 "line": 30
1275 },
1276 "file": "src/components/services/content/Services.js",
1277 "id": "services.serverless",
1278 "start": {
1279 "column": 14,
1280 "line": 27
1203 } 1281 }
1204 }, 1282 },
1205 { 1283 {
1206 "defaultMessage": "!!!Optionally, you can change your Ferdi server by clicking the cog in the bottom left corner.", 1284 "defaultMessage": "!!!Optionally, you can change your Ferdi server by clicking the cog in the bottom left corner.",
1207 "end": { 1285 "end": {
1208 "column": 3, 1286 "column": 3,
1209 "line": 29 1287 "line": 34
1210 }, 1288 },
1211 "file": "src/components/services/content/Services.js", 1289 "file": "src/components/services/content/Services.js",
1212 "id": "services.serverInfo", 1290 "id": "services.serverInfo",
1213 "start": { 1291 "start": {
1214 "column": 14, 1292 "column": 14,
1215 "line": 26 1293 "line": 31
1216 } 1294 }
1217 } 1295 }
1218 ], 1296 ],
@@ -3003,6 +3081,32 @@
3003 "column": 17, 3081 "column": 17,
3004 "line": 37 3082 "line": 37
3005 } 3083 }
3084 },
3085 {
3086 "defaultMessage": "!!!Teams are unavailible",
3087 "end": {
3088 "column": 3,
3089 "line": 44
3090 },
3091 "file": "src/components/settings/team/TeamDashboard.js",
3092 "id": "settings.team.teamsUnavailible",
3093 "start": {
3094 "column": 20,
3095 "line": 41
3096 }
3097 },
3098 {
3099 "defaultMessage": "!!!Teams are currently only availible when using the Franz Server and after paying for Franz Professional. Please change your server to https://api.franzinfra.com to use teams.",
3100 "end": {
3101 "column": 3,
3102 "line": 48
3103 },
3104 "file": "src/components/settings/team/TeamDashboard.js",
3105 "id": "settings.team.teamsUnavailibleInfo",
3106 "start": {
3107 "column": 24,
3108 "line": 45
3109 }
3006 } 3110 }
3007 ], 3111 ],
3008 "path": "src/components/settings/team/TeamDashboard.json" 3112 "path": "src/components/settings/team/TeamDashboard.json"
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index e1ee6f824..10f0b7c6f 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -54,6 +54,7 @@
54 "locked.invalidCredentials": "Password invalid", 54 "locked.invalidCredentials": "Password invalid",
55 "locked.password.label": "Password", 55 "locked.password.label": "Password",
56 "locked.submit.label": "Unlock", 56 "locked.submit.label": "Unlock",
57 "login.changeServer": "Change server",
57 "login.customServerQuestion": "Using a Franz account to log in?", 58 "login.customServerQuestion": "Using a Franz account to log in?",
58 "login.customServerSuggestion": "Try importing your Franz account into Ferdi", 59 "login.customServerSuggestion": "Try importing your Franz account into Ferdi",
59 "login.email.label": "Email address", 60 "login.email.label": "Email address",
@@ -186,6 +187,7 @@
186 "services.getStarted": "Get started", 187 "services.getStarted": "Get started",
187 "services.login": "Please login to use Ferdi.", 188 "services.login": "Please login to use Ferdi.",
188 "services.serverInfo": "Optionally, you can change your Ferdi server by clicking the cog in the bottom left corner.", 189 "services.serverInfo": "Optionally, you can change your Ferdi server by clicking the cog in the bottom left corner.",
190 "services.serverless": "Use Ferdi without an Account",
189 "services.welcome": "Welcome to Ferdi", 191 "services.welcome": "Welcome to Ferdi",
190 "settings.account.account.editButton": "Edit account", 192 "settings.account.account.editButton": "Edit account",
191 "settings.account.accountType.basic": "Basic Account", 193 "settings.account.accountType.basic": "Basic Account",
@@ -354,6 +356,8 @@
354 "settings.team.headline": "Team", 356 "settings.team.headline": "Team",
355 "settings.team.intro": "You and your team use Ferdi? You can now manage Premium subscriptions for as many colleagues, friends or family members as you want, all from within one account.", 357 "settings.team.intro": "You and your team use Ferdi? You can now manage Premium subscriptions for as many colleagues, friends or family members as you want, all from within one account.",
356 "settings.team.manageAction": "Manage your Team on getferdi.com", 358 "settings.team.manageAction": "Manage your Team on getferdi.com",
359 "settings.team.teamsUnavailible": "Teams are unavailible",
360 "settings.team.teamsUnavailibleInfo": "Teams are currently only availible when using the Franz Server and after paying for Franz Professional. Please change your server to https://api.franzinfra.com to use teams.",
357 "settings.team.upgradeAction": "Upgrade your Account", 361 "settings.team.upgradeAction": "Upgrade your Account",
358 "settings.user.form.accountType.company": "Company", 362 "settings.user.form.accountType.company": "Company",
359 "settings.user.form.accountType.individual": "Individual", 363 "settings.user.form.accountType.individual": "Individual",
diff --git a/src/i18n/locales/zh-HANT.json b/src/i18n/locales/zh-HANT.json
index 5784c1ab6..678554c05 100644
--- a/src/i18n/locales/zh-HANT.json
+++ b/src/i18n/locales/zh-HANT.json
@@ -11,6 +11,7 @@
11 "feature.delayApp.upgrade.actionShort": "Upgrade account", 11 "feature.delayApp.upgrade.actionShort": "Upgrade account",
12 "feature.quickSwitch.info": "Select a service with TAB, ↑ and ↓. Open a service with ENTER.", 12 "feature.quickSwitch.info": "Select a service with TAB, ↑ and ↓. Open a service with ENTER.",
13 "feature.quickSwitch.search": "Search...", 13 "feature.quickSwitch.search": "Search...",
14 "feature.quickSwitch.title": "QuickSwitch",
14 "feature.serviceLimit.limitReached": "You have added {amount} out of {limit} services that are included in your plan. Please upgrade your account to add more services.", 15 "feature.serviceLimit.limitReached": "You have added {amount} out of {limit} services that are included in your plan. Please upgrade your account to add more services.",
15 "feature.shareFranz.action.email": "Send as email", 16 "feature.shareFranz.action.email": "Send as email",
16 "feature.shareFranz.action.facebook": "Share on Facebook", 17 "feature.shareFranz.action.facebook": "Share on Facebook",
@@ -213,11 +214,13 @@
213 "settings.account.upgradeToPro.label": "Upgrade to Ferdi Professional", 214 "settings.account.upgradeToPro.label": "Upgrade to Ferdi Professional",
214 "settings.account.userInfoRequestFailed": "無法載入帳戶資訊", 215 "settings.account.userInfoRequestFailed": "無法載入帳戶資訊",
215 "settings.account.yourLicense": "Your Ferdi License", 216 "settings.account.yourLicense": "Your Ferdi License",
217 "settings.app.accentColorInfo": "Write your accent color in a CSS-compatible format. (Default: #7367f0)",
216 "settings.app.buttonClearAllCache": "Clear cache", 218 "settings.app.buttonClearAllCache": "Clear cache",
217 "settings.app.buttonInstallUpdate": "重新啟動並且更新", 219 "settings.app.buttonInstallUpdate": "重新啟動並且更新",
218 "settings.app.buttonSearchForUpdate": "Check for updates", 220 "settings.app.buttonSearchForUpdate": "Check for updates",
219 "settings.app.cacheInfo": "Ferdi cache is currently using {size} of disk space.", 221 "settings.app.cacheInfo": "Ferdi cache is currently using {size} of disk space.",
220 "settings.app.currentVersion": "當前版本:", 222 "settings.app.currentVersion": "當前版本:",
223 "settings.app.form.accentColor": "Accent color",
221 "settings.app.form.autoLaunchInBackground": "背景啟動", 224 "settings.app.form.autoLaunchInBackground": "背景啟動",
222 "settings.app.form.autoLaunchOnStart": "開機時啟動", 225 "settings.app.form.autoLaunchOnStart": "開機時啟動",
223 "settings.app.form.beta": "包含開發中版本", 226 "settings.app.form.beta": "包含開發中版本",
@@ -244,6 +247,7 @@
244 "settings.app.form.showMessagesBadgesWhenMuted": "Show unread message badge when notifications are disabled", 247 "settings.app.form.showMessagesBadgesWhenMuted": "Show unread message badge when notifications are disabled",
245 "settings.app.form.showServiceNavigationBar": "Always show service navigation bar", 248 "settings.app.form.showServiceNavigationBar": "Always show service navigation bar",
246 "settings.app.form.todoServer": "Todo Server", 249 "settings.app.form.todoServer": "Todo Server",
250 "settings.app.form.universalDarkMode": "Enable universal Dark Mode",
247 "settings.app.headline": "Settings", 251 "settings.app.headline": "Settings",
248 "settings.app.headlineAdvanced": "Advanced", 252 "settings.app.headlineAdvanced": "Advanced",
249 "settings.app.headlineAppearance": "Appearance", 253 "settings.app.headlineAppearance": "Appearance",
@@ -263,6 +267,7 @@
263 "settings.app.subheadlineCache": "Cache", 267 "settings.app.subheadlineCache": "Cache",
264 "settings.app.todoServerInfo": "This server will be used for the \"Ferdi Todo\" feature. (default: https://app.franztodos.com)", 268 "settings.app.todoServerInfo": "This server will be used for the \"Ferdi Todo\" feature. (default: https://app.franztodos.com)",
265 "settings.app.translationHelp": "Help us to translate Ferdi into your language.", 269 "settings.app.translationHelp": "Help us to translate Ferdi into your language.",
270 "settings.app.universalDarkModeInfo": "Universal Dark Mode tries to dynamically generate dark mode styles for services that are otherwise not currently supported.",
266 "settings.app.updateStatusAvailable": "有可用更新,下載中...", 271 "settings.app.updateStatusAvailable": "有可用更新,下載中...",
267 "settings.app.updateStatusSearching": "檢查更新中...", 272 "settings.app.updateStatusSearching": "檢查更新中...",
268 "settings.app.updateStatusUpToDate": "已經是最新版本了", 273 "settings.app.updateStatusUpToDate": "已經是最新版本了",
@@ -315,6 +320,7 @@
315 "settings.service.form.indirectMessages": "針對全部訊息顯示通知", 320 "settings.service.form.indirectMessages": "針對全部訊息顯示通知",
316 "settings.service.form.isMutedInfo": "When disabled, all notification sounds and audio playback are muted", 321 "settings.service.form.isMutedInfo": "When disabled, all notification sounds and audio playback are muted",
317 "settings.service.form.name": "名子", 322 "settings.service.form.name": "名子",
323 "settings.service.form.openDarkmodeCss": "Open darkmode.css",
318 "settings.service.form.proxy.headline": "HTTP/HTTPS Proxy Settings", 324 "settings.service.form.proxy.headline": "HTTP/HTTPS Proxy Settings",
319 "settings.service.form.proxy.host": "Proxy Host/IP", 325 "settings.service.form.proxy.host": "Proxy Host/IP",
320 "settings.service.form.proxy.info": "Proxy settings will not synced with the Ferdi servers.", 326 "settings.service.form.proxy.info": "Proxy settings will not synced with the Ferdi servers.",
diff --git a/src/i18n/messages/src/components/auth/Login.json b/src/i18n/messages/src/components/auth/Login.json
index 7e4b32294..c3b4ee4eb 100644
--- a/src/i18n/messages/src/components/auth/Login.json
+++ b/src/i18n/messages/src/components/auth/Login.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Sign in", 4 "defaultMessage": "!!!Sign in",
5 "file": "src/components/auth/Login.js", 5 "file": "src/components/auth/Login.js",
6 "start": { 6 "start": {
7 "line": 17, 7 "line": 19,
8 "column": 12 8 "column": 12
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 20, 11 "line": 22,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,11 @@
17 "defaultMessage": "!!!Email address", 17 "defaultMessage": "!!!Email address",
18 "file": "src/components/auth/Login.js", 18 "file": "src/components/auth/Login.js",
19 "start": { 19 "start": {
20 "line": 21, 20 "line": 23,
21 "column": 14 21 "column": 14
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 24, 24 "line": 26,
25 "column": 3 25 "column": 3
26 } 26 }
27 }, 27 },
@@ -30,11 +30,11 @@
30 "defaultMessage": "!!!Password", 30 "defaultMessage": "!!!Password",
31 "file": "src/components/auth/Login.js", 31 "file": "src/components/auth/Login.js",
32 "start": { 32 "start": {
33 "line": 25, 33 "line": 27,
34 "column": 17 34 "column": 17
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 28, 37 "line": 30,
38 "column": 3 38 "column": 3
39 } 39 }
40 }, 40 },
@@ -43,11 +43,11 @@
43 "defaultMessage": "!!!Sign in", 43 "defaultMessage": "!!!Sign in",
44 "file": "src/components/auth/Login.js", 44 "file": "src/components/auth/Login.js",
45 "start": { 45 "start": {
46 "line": 29, 46 "line": 31,
47 "column": 21 47 "column": 21
48 }, 48 },
49 "end": { 49 "end": {
50 "line": 32, 50 "line": 34,
51 "column": 3 51 "column": 3
52 } 52 }
53 }, 53 },
@@ -56,11 +56,11 @@
56 "defaultMessage": "!!!Email or password not valid", 56 "defaultMessage": "!!!Email or password not valid",
57 "file": "src/components/auth/Login.js", 57 "file": "src/components/auth/Login.js",
58 "start": { 58 "start": {
59 "line": 33, 59 "line": 35,
60 "column": 22 60 "column": 22
61 }, 61 },
62 "end": { 62 "end": {
63 "line": 36, 63 "line": 38,
64 "column": 3 64 "column": 3
65 } 65 }
66 }, 66 },
@@ -69,11 +69,11 @@
69 "defaultMessage": "!!!Using a Franz account to log in?", 69 "defaultMessage": "!!!Using a Franz account to log in?",
70 "file": "src/components/auth/Login.js", 70 "file": "src/components/auth/Login.js",
71 "start": { 71 "start": {
72 "line": 37, 72 "line": 39,
73 "column": 24 73 "column": 24
74 }, 74 },
75 "end": { 75 "end": {
76 "line": 40, 76 "line": 42,
77 "column": 3 77 "column": 3
78 } 78 }
79 }, 79 },
@@ -82,11 +82,11 @@
82 "defaultMessage": "!!!Try importing your Franz account into Ferdi", 82 "defaultMessage": "!!!Try importing your Franz account into Ferdi",
83 "file": "src/components/auth/Login.js", 83 "file": "src/components/auth/Login.js",
84 "start": { 84 "start": {
85 "line": 41, 85 "line": 43,
86 "column": 26 86 "column": 26
87 }, 87 },
88 "end": { 88 "end": {
89 "line": 44, 89 "line": 46,
90 "column": 3 90 "column": 3
91 } 91 }
92 }, 92 },
@@ -95,11 +95,11 @@
95 "defaultMessage": "!!!Your session expired, please login again.", 95 "defaultMessage": "!!!Your session expired, please login again.",
96 "file": "src/components/auth/Login.js", 96 "file": "src/components/auth/Login.js",
97 "start": { 97 "start": {
98 "line": 45, 98 "line": 47,
99 "column": 16 99 "column": 16
100 }, 100 },
101 "end": { 101 "end": {
102 "line": 48, 102 "line": 50,
103 "column": 3 103 "column": 3
104 } 104 }
105 }, 105 },
@@ -108,11 +108,11 @@
108 "defaultMessage": "!!!Your session expired, please login again.", 108 "defaultMessage": "!!!Your session expired, please login again.",
109 "file": "src/components/auth/Login.js", 109 "file": "src/components/auth/Login.js",
110 "start": { 110 "start": {
111 "line": 49, 111 "line": 51,
112 "column": 16 112 "column": 16
113 }, 113 },
114 "end": { 114 "end": {
115 "line": 52, 115 "line": 54,
116 "column": 3 116 "column": 3
117 } 117 }
118 }, 118 },
@@ -121,11 +121,37 @@
121 "defaultMessage": "!!!Create a free account", 121 "defaultMessage": "!!!Create a free account",
122 "file": "src/components/auth/Login.js", 122 "file": "src/components/auth/Login.js",
123 "start": { 123 "start": {
124 "line": 53, 124 "line": 55,
125 "column": 14 125 "column": 14
126 }, 126 },
127 "end": { 127 "end": {
128 "line": 56, 128 "line": 58,
129 "column": 3
130 }
131 },
132 {
133 "id": "login.changeServer",
134 "defaultMessage": "!!!Change server",
135 "file": "src/components/auth/Login.js",
136 "start": {
137 "line": 59,
138 "column": 16
139 },
140 "end": {
141 "line": 62,
142 "column": 3
143 }
144 },
145 {
146 "id": "services.serverless",
147 "defaultMessage": "!!!Use Ferdi without an Account",
148 "file": "src/components/auth/Login.js",
149 "start": {
150 "line": 63,
151 "column": 14
152 },
153 "end": {
154 "line": 66,
129 "column": 3 155 "column": 3
130 } 156 }
131 }, 157 },
@@ -134,11 +160,11 @@
134 "defaultMessage": "!!!Forgot password", 160 "defaultMessage": "!!!Forgot password",
135 "file": "src/components/auth/Login.js", 161 "file": "src/components/auth/Login.js",
136 "start": { 162 "start": {
137 "line": 57, 163 "line": 67,
138 "column": 16 164 "column": 16
139 }, 165 },
140 "end": { 166 "end": {
141 "line": 60, 167 "line": 70,
142 "column": 3 168 "column": 3
143 } 169 }
144 } 170 }
diff --git a/src/i18n/messages/src/components/auth/Signup.json b/src/i18n/messages/src/components/auth/Signup.json
index 9aa7b25ab..2628c9aa3 100644
--- a/src/i18n/messages/src/components/auth/Signup.json
+++ b/src/i18n/messages/src/components/auth/Signup.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Sign up", 4 "defaultMessage": "!!!Sign up",
5 "file": "src/components/auth/Signup.js", 5 "file": "src/components/auth/Signup.js",
6 "start": { 6 "start": {
7 "line": 17, 7 "line": 19,
8 "column": 12 8 "column": 12
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 20, 11 "line": 22,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,11 @@
17 "defaultMessage": "!!!Firstname", 17 "defaultMessage": "!!!Firstname",
18 "file": "src/components/auth/Signup.js", 18 "file": "src/components/auth/Signup.js",
19 "start": { 19 "start": {
20 "line": 21, 20 "line": 23,
21 "column": 18 21 "column": 18
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 24, 24 "line": 26,
25 "column": 3 25 "column": 3
26 } 26 }
27 }, 27 },
@@ -30,11 +30,11 @@
30 "defaultMessage": "!!!Lastname", 30 "defaultMessage": "!!!Lastname",
31 "file": "src/components/auth/Signup.js", 31 "file": "src/components/auth/Signup.js",
32 "start": { 32 "start": {
33 "line": 25, 33 "line": 27,
34 "column": 17 34 "column": 17
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 28, 37 "line": 30,
38 "column": 3 38 "column": 3
39 } 39 }
40 }, 40 },
@@ -43,11 +43,11 @@
43 "defaultMessage": "!!!Email address", 43 "defaultMessage": "!!!Email address",
44 "file": "src/components/auth/Signup.js", 44 "file": "src/components/auth/Signup.js",
45 "start": { 45 "start": {
46 "line": 29, 46 "line": 31,
47 "column": 14 47 "column": 14
48 }, 48 },
49 "end": { 49 "end": {
50 "line": 32, 50 "line": 34,
51 "column": 3 51 "column": 3
52 } 52 }
53 }, 53 },
@@ -56,11 +56,11 @@
56 "defaultMessage": "!!!Password", 56 "defaultMessage": "!!!Password",
57 "file": "src/components/auth/Signup.js", 57 "file": "src/components/auth/Signup.js",
58 "start": { 58 "start": {
59 "line": 37, 59 "line": 39,
60 "column": 17 60 "column": 17
61 }, 61 },
62 "end": { 62 "end": {
63 "line": 40, 63 "line": 42,
64 "column": 3 64 "column": 3
65 } 65 }
66 }, 66 },
@@ -69,11 +69,11 @@
69 "defaultMessage": "!!!By creating a Ferdi account you accept the", 69 "defaultMessage": "!!!By creating a Ferdi account you accept the",
70 "file": "src/components/auth/Signup.js", 70 "file": "src/components/auth/Signup.js",
71 "start": { 71 "start": {
72 "line": 41, 72 "line": 43,
73 "column": 13 73 "column": 13
74 }, 74 },
75 "end": { 75 "end": {
76 "line": 44, 76 "line": 46,
77 "column": 3 77 "column": 3
78 } 78 }
79 }, 79 },
@@ -82,11 +82,11 @@
82 "defaultMessage": "!!!Terms of service", 82 "defaultMessage": "!!!Terms of service",
83 "file": "src/components/auth/Signup.js", 83 "file": "src/components/auth/Signup.js",
84 "start": { 84 "start": {
85 "line": 45, 85 "line": 47,
86 "column": 9 86 "column": 9
87 }, 87 },
88 "end": { 88 "end": {
89 "line": 48, 89 "line": 50,
90 "column": 3 90 "column": 3
91 } 91 }
92 }, 92 },
@@ -95,11 +95,11 @@
95 "defaultMessage": "!!!Privacy Statement", 95 "defaultMessage": "!!!Privacy Statement",
96 "file": "src/components/auth/Signup.js", 96 "file": "src/components/auth/Signup.js",
97 "start": { 97 "start": {
98 "line": 49, 98 "line": 51,
99 "column": 11 99 "column": 11
100 }, 100 },
101 "end": { 101 "end": {
102 "line": 52, 102 "line": 54,
103 "column": 3 103 "column": 3
104 } 104 }
105 }, 105 },
@@ -108,11 +108,11 @@
108 "defaultMessage": "!!!Create account", 108 "defaultMessage": "!!!Create account",
109 "file": "src/components/auth/Signup.js", 109 "file": "src/components/auth/Signup.js",
110 "start": { 110 "start": {
111 "line": 53, 111 "line": 55,
112 "column": 21 112 "column": 21
113 }, 113 },
114 "end": { 114 "end": {
115 "line": 56, 115 "line": 58,
116 "column": 3 116 "column": 3
117 } 117 }
118 }, 118 },
@@ -121,11 +121,37 @@
121 "defaultMessage": "!!!Already have an account, sign in?", 121 "defaultMessage": "!!!Already have an account, sign in?",
122 "file": "src/components/auth/Signup.js", 122 "file": "src/components/auth/Signup.js",
123 "start": { 123 "start": {
124 "line": 57, 124 "line": 59,
125 "column": 13 125 "column": 13
126 }, 126 },
127 "end": { 127 "end": {
128 "line": 60, 128 "line": 62,
129 "column": 3
130 }
131 },
132 {
133 "id": "login.changeServer",
134 "defaultMessage": "!!!Change server",
135 "file": "src/components/auth/Signup.js",
136 "start": {
137 "line": 63,
138 "column": 16
139 },
140 "end": {
141 "line": 66,
142 "column": 3
143 }
144 },
145 {
146 "id": "services.serverless",
147 "defaultMessage": "!!!Use Ferdi without an Account",
148 "file": "src/components/auth/Signup.js",
149 "start": {
150 "line": 67,
151 "column": 14
152 },
153 "end": {
154 "line": 70,
129 "column": 3 155 "column": 3
130 } 156 }
131 }, 157 },
@@ -134,11 +160,11 @@
134 "defaultMessage": "!!!A user with that email address already exists", 160 "defaultMessage": "!!!A user with that email address already exists",
135 "file": "src/components/auth/Signup.js", 161 "file": "src/components/auth/Signup.js",
136 "start": { 162 "start": {
137 "line": 61, 163 "line": 71,
138 "column": 18 164 "column": 18
139 }, 165 },
140 "end": { 166 "end": {
141 "line": 64, 167 "line": 74,
142 "column": 3 168 "column": 3
143 } 169 }
144 } 170 }
diff --git a/src/i18n/messages/src/components/auth/Welcome.json b/src/i18n/messages/src/components/auth/Welcome.json
index b4d2ce689..3f0c1e5c2 100644
--- a/src/i18n/messages/src/components/auth/Welcome.json
+++ b/src/i18n/messages/src/components/auth/Welcome.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Create a free account", 4 "defaultMessage": "!!!Create a free account",
5 "file": "src/components/auth/Welcome.js", 5 "file": "src/components/auth/Welcome.js",
6 "start": { 6 "start": {
7 "line": 9, 7 "line": 11,
8 "column": 16 8 "column": 16
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 12, 11 "line": 14,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,24 @@
17 "defaultMessage": "!!!Login to your account", 17 "defaultMessage": "!!!Login to your account",
18 "file": "src/components/auth/Welcome.js", 18 "file": "src/components/auth/Welcome.js",
19 "start": { 19 "start": {
20 "line": 13, 20 "line": 15,
21 "column": 15 21 "column": 15
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 16, 24 "line": 18,
25 "column": 3
26 }
27 },
28 {
29 "id": "services.serverless",
30 "defaultMessage": "!!!Use Ferdi without an Account",
31 "file": "src/components/auth/Welcome.js",
32 "start": {
33 "line": 19,
34 "column": 14
35 },
36 "end": {
37 "line": 22,
25 "column": 3 38 "column": 3
26 } 39 }
27 } 40 }
diff --git a/src/i18n/messages/src/components/services/content/Services.json b/src/i18n/messages/src/components/services/content/Services.json
index c2e57b8b5..6a5eb052e 100644
--- a/src/i18n/messages/src/components/services/content/Services.json
+++ b/src/i18n/messages/src/components/services/content/Services.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Welcome to Ferdi", 4 "defaultMessage": "!!!Welcome to Ferdi",
5 "file": "src/components/services/content/Services.js", 5 "file": "src/components/services/content/Services.js",
6 "start": { 6 "start": {
7 "line": 14, 7 "line": 15,
8 "column": 11 8 "column": 11
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": "!!!Get started", 17 "defaultMessage": "!!!Get started",
18 "file": "src/components/services/content/Services.js", 18 "file": "src/components/services/content/Services.js",
19 "start": { 19 "start": {
20 "line": 18, 20 "line": 19,
21 "column": 14 21 "column": 14
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,24 @@
30 "defaultMessage": "!!!Please login to use Ferdi.", 30 "defaultMessage": "!!!Please login to use Ferdi.",
31 "file": "src/components/services/content/Services.js", 31 "file": "src/components/services/content/Services.js",
32 "start": { 32 "start": {
33 "line": 22, 33 "line": 23,
34 "column": 9 34 "column": 9
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 25, 37 "line": 26,
38 "column": 3
39 }
40 },
41 {
42 "id": "services.serverless",
43 "defaultMessage": "!!!Use Ferdi without an Account",
44 "file": "src/components/services/content/Services.js",
45 "start": {
46 "line": 27,
47 "column": 14
48 },
49 "end": {
50 "line": 30,
38 "column": 3 51 "column": 3
39 } 52 }
40 }, 53 },
@@ -43,11 +56,11 @@
43 "defaultMessage": "!!!Optionally, you can change your Ferdi server by clicking the cog in the bottom left corner.", 56 "defaultMessage": "!!!Optionally, you can change your Ferdi server by clicking the cog in the bottom left corner.",
44 "file": "src/components/services/content/Services.js", 57 "file": "src/components/services/content/Services.js",
45 "start": { 58 "start": {
46 "line": 26, 59 "line": 31,
47 "column": 14 60 "column": 14
48 }, 61 },
49 "end": { 62 "end": {
50 "line": 29, 63 "line": 34,
51 "column": 3 64 "column": 3
52 } 65 }
53 } 66 }
diff --git a/src/i18n/messages/src/components/settings/team/TeamDashboard.json b/src/i18n/messages/src/components/settings/team/TeamDashboard.json
index 80adadb67..64693933c 100644
--- a/src/i18n/messages/src/components/settings/team/TeamDashboard.json
+++ b/src/i18n/messages/src/components/settings/team/TeamDashboard.json
@@ -76,5 +76,31 @@
76 "line": 40, 76 "line": 40,
77 "column": 3 77 "column": 3
78 } 78 }
79 },
80 {
81 "id": "settings.team.teamsUnavailible",
82 "defaultMessage": "!!!Teams are unavailible",
83 "file": "src/components/settings/team/TeamDashboard.js",
84 "start": {
85 "line": 41,
86 "column": 20
87 },
88 "end": {
89 "line": 44,
90 "column": 3
91 }
92 },
93 {
94 "id": "settings.team.teamsUnavailibleInfo",
95 "defaultMessage": "!!!Teams are currently only availible when using the Franz Server and after paying for Franz Professional. Please change your server to https://api.franzinfra.com to use teams.",
96 "file": "src/components/settings/team/TeamDashboard.js",
97 "start": {
98 "line": 45,
99 "column": 24
100 },
101 "end": {
102 "line": 48,
103 "column": 3
104 }
79 } 105 }
80] \ No newline at end of file 106] \ No newline at end of file
diff --git a/src/index.js b/src/index.js
index 799e762f5..4d7215d5e 100644
--- a/src/index.js
+++ b/src/index.js
@@ -11,7 +11,7 @@ import windowStateKeeper from 'electron-window-state';
11 11
12// Set app directory before loading user modules 12// Set app directory before loading user modules
13if (process.env.FERDI_APPDATA_DIR || process.env.PORTABLE_EXECUTABLE_DIR) { 13if (process.env.FERDI_APPDATA_DIR || process.env.PORTABLE_EXECUTABLE_DIR) {
14 const appDataPath = process.env.FERDI_APPDATA_DIR || process.env.PORTABLE_EXECUTABLE_DIR 14 const appDataPath = process.env.FERDI_APPDATA_DIR || process.env.PORTABLE_EXECUTABLE_DIR;
15 app.setPath('appData', appDataPath); 15 app.setPath('appData', appDataPath);
16 app.setPath('userData', path.join(app.getPath('appData'), app.getName())); 16 app.setPath('userData', path.join(app.getPath('appData'), app.getName()));
17} 17}
diff --git a/src/server b/src/server
new file mode 160000
Subproject a2b75e6d312304770d49254e8cb3f076efce326
diff --git a/src/stores/RequestStore.js b/src/stores/RequestStore.js
index 2587d4eef..a92f4c685 100644
--- a/src/stores/RequestStore.js
+++ b/src/stores/RequestStore.js
@@ -1,3 +1,4 @@
1import { ipcRenderer } from 'electron';
1import { action, computed, observable } from 'mobx'; 2import { action, computed, observable } from 'mobx';
2import ms from 'ms'; 3import ms from 'ms';
3 4
@@ -12,6 +13,8 @@ export default class RequestStore extends Store {
12 13
13 @observable showRequiredRequestsError = false; 14 @observable showRequiredRequestsError = false;
14 15
16 @observable localServerPort = 45569;
17
15 retries = 0; 18 retries = 0;
16 19
17 retryDelay = ms('2s'); 20 retryDelay = ms('2s');
@@ -29,6 +32,12 @@ export default class RequestStore extends Store {
29 setup() { 32 setup() {
30 this.userInfoRequest = this.stores.user.getUserInfoRequest; 33 this.userInfoRequest = this.stores.user.getUserInfoRequest;
31 this.servicesRequest = this.stores.services.allServicesRequest; 34 this.servicesRequest = this.stores.services.allServicesRequest;
35
36 ipcRenderer.on('localServerPort', (event, data) => {
37 if (data.port) {
38 this.localServerPort = data.port;
39 }
40 });
32 } 41 }
33 42
34 @computed get areRequiredRequestsSuccessful() { 43 @computed get areRequiredRequestsSuccessful() {
diff --git a/src/stores/SettingsStore.js b/src/stores/SettingsStore.js
index 8c4cd47eb..df0fc77e9 100644
--- a/src/stores/SettingsStore.js
+++ b/src/stores/SettingsStore.js
@@ -9,7 +9,7 @@ import Request from './lib/Request';
9import { getLocale } from '../helpers/i18n-helpers'; 9import { getLocale } from '../helpers/i18n-helpers';
10import { API } from '../environment'; 10import { API } from '../environment';
11 11
12import { DEFAULT_APP_SETTINGS, FILE_SYSTEM_SETTINGS_TYPES } from '../config'; 12import { DEFAULT_APP_SETTINGS, FILE_SYSTEM_SETTINGS_TYPES, LOCAL_SERVER } from '../config';
13import { SPELLCHECKER_LOCALES } from '../i18n/languages'; 13import { SPELLCHECKER_LOCALES } from '../i18n/languages';
14 14
15const debug = require('debug')('Ferdi:SettingsStore'); 15const debug = require('debug')('Ferdi:SettingsStore');
@@ -53,6 +53,18 @@ export default class SettingsStore extends Store {
53 ); 53 );
54 54
55 reaction( 55 reaction(
56 () => this.all.app.server,
57 (server) => {
58 if (server === LOCAL_SERVER) {
59 ipcRenderer.send('startLocalServer');
60 }
61 },
62 {
63 fireImmediately: true,
64 },
65 );
66
67 reaction(
56 () => this.all.app.locked, 68 () => this.all.app.locked,
57 () => { 69 () => {
58 const { router } = window.ferdi.stores; 70 const { router } = window.ferdi.stores;
diff --git a/src/styles/layout.scss b/src/styles/layout.scss
index b18bd6dcf..a7de4b247 100644
--- a/src/styles/layout.scss
+++ b/src/styles/layout.scss
@@ -2,6 +2,18 @@
2 2
3html { overflow: hidden; } 3html { overflow: hidden; }
4 4
5@keyframes pulse-danger {
6 0% {
7 background: darken($theme-brand-danger, 10%);
8 }
9 50% {
10 background: lighten($theme-brand-danger, 10%);
11 }
12 100% {
13 background: darken($theme-brand-danger, 10%);
14 }
15}
16
5.theme__dark .app { 17.theme__dark .app {
6 .sidebar { 18 .sidebar {
7 background: $dark-theme-gray-darker; 19 background: $dark-theme-gray-darker;
@@ -26,6 +38,27 @@ html { overflow: hidden; }
26 &.is-active { 38 &.is-active {
27 color: $theme-brand-primary; 39 color: $theme-brand-primary;
28 } 40 }
41
42 .update-availible {
43 align-items: center;
44 background: $theme-brand-danger;
45 border-radius: 20px;
46 bottom: 5px;
47 color: #FFF;
48 display: flex;
49 justify-content: center;
50 padding: 0px 5px;
51 position: absolute;
52 right: 16px;
53 padding-top: 0;
54 font-size: 0px;
55 min-height: 15px;
56 min-width: 15px;
57
58 animation-name: pulse-danger;
59 animation-duration: 0.75s;
60 animation-iteration-count: 6;
61 }
29 } 62 }
30 } 63 }
31 64
diff --git a/src/styles/services.scss b/src/styles/services.scss
index 5acf92d2c..ef1097791 100644
--- a/src/styles/services.scss
+++ b/src/styles/services.scss
@@ -64,7 +64,7 @@
64 margin: 25px 0 40px; 64 margin: 25px 0 40px;
65 } 65 }
66 66
67 a.button, 67 .button,
68 button { margin: 40px 0 20px; } 68 button { margin: 40px 0 20px; }
69 } 69 }
70 70
diff --git a/src/styles/type.scss b/src/styles/type.scss
index e0b27fe34..636b8fd36 100644
--- a/src/styles/type.scss
+++ b/src/styles/type.scss
@@ -33,7 +33,7 @@ p {
33 33
34strong { font-weight: bold; } 34strong { font-weight: bold; }
35 35
36a { 36a, button {
37 color: $theme-text-color; 37 color: $theme-text-color;
38 text-decoration: none; 38 text-decoration: none;
39 39
@@ -49,6 +49,7 @@ a {
49 position: relative; 49 position: relative;
50 text-align: center; 50 text-align: center;
51 transition: background .5s, color .5s; 51 transition: background .5s, color .5s;
52 cursor: pointer;
52 53
53 &:hover { 54 &:hover {
54 background: darken($theme-brand-primary, 5%); 55 background: darken($theme-brand-primary, 5%);
diff --git a/src/styles/welcome.scss b/src/styles/welcome.scss
index b517431f0..c1f85391e 100644
--- a/src/styles/welcome.scss
+++ b/src/styles/welcome.scss
@@ -48,6 +48,7 @@
48 .button { 48 .button {
49 border-color: #FFF; 49 border-color: #FFF;
50 color: #FFF; 50 color: #FFF;
51 cursor: pointer;
51 52
52 &:hover { 53 &:hover {
53 background: #FFF; 54 background: #FFF;