aboutsummaryrefslogtreecommitdiffstats
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/auth/Welcome.js4
-rw-r--r--src/components/layout/AppLayout.js11
-rw-r--r--src/components/layout/Sidebar.js140
-rw-r--r--src/components/services/content/Services.js37
-rw-r--r--src/components/settings/account/AccountDashboard.js7
-rw-r--r--src/components/settings/navigation/SettingsNavigation.js52
-rw-r--r--src/components/settings/recipes/RecipesDashboard.js5
-rw-r--r--src/components/settings/settings/EditSettingsForm.js30
-rw-r--r--src/components/settings/supportFerdi/SupportFerdiDashboard.tsx155
-rw-r--r--src/components/ui/FullscreenLoader/index.js3
-rw-r--r--src/components/ui/ToggleRaw.js76
11 files changed, 132 insertions, 388 deletions
diff --git a/src/components/auth/Welcome.js b/src/components/auth/Welcome.js
index 4f7a5ce30..cfd26e2e9 100644
--- a/src/components/auth/Welcome.js
+++ b/src/components/auth/Welcome.js
@@ -27,7 +27,7 @@ const messages = defineMessages({
27 }, 27 },
28}); 28});
29 29
30class Login extends Component { 30class Welcome extends Component {
31 static propTypes = { 31 static propTypes = {
32 loginRoute: PropTypes.string.isRequired, 32 loginRoute: PropTypes.string.isRequired,
33 signupRoute: PropTypes.string.isRequired, 33 signupRoute: PropTypes.string.isRequired,
@@ -95,4 +95,4 @@ class Login extends Component {
95 } 95 }
96} 96}
97 97
98export default injectIntl(inject('actions')(observer(Login))); 98export default injectIntl(inject('actions')(observer(Welcome)));
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js
index 084d93ecd..eb36ea431 100644
--- a/src/components/layout/AppLayout.js
+++ b/src/components/layout/AppLayout.js
@@ -10,7 +10,6 @@ import { mdiFlash, mdiPowerPlug } from '@mdi/js';
10import InfoBar from '../ui/InfoBar'; 10import InfoBar from '../ui/InfoBar';
11import { Component as BasicAuth } from '../../features/basicAuth'; 11import { Component as BasicAuth } from '../../features/basicAuth';
12import { Component as QuickSwitch } from '../../features/quickSwitch'; 12import { Component as QuickSwitch } from '../../features/quickSwitch';
13import { Component as NightlyBuilds } from '../../features/nightlyBuilds';
14import { Component as PublishDebugInfo } from '../../features/publishDebugInfo'; 13import { Component as PublishDebugInfo } from '../../features/publishDebugInfo';
15import ErrorBoundary from '../util/ErrorBoundary'; 14import ErrorBoundary from '../util/ErrorBoundary';
16 15
@@ -23,6 +22,8 @@ import AppUpdateInfoBar from '../AppUpdateInfoBar';
23import Todos from '../../features/todos/containers/TodosScreen'; 22import Todos from '../../features/todos/containers/TodosScreen';
24import { Icon } from '../ui/icon'; 23import { Icon } from '../ui/icon';
25 24
25import LockedScreen from '../../containers/auth/LockedScreen';
26
26const messages = defineMessages({ 27const messages = defineMessages({
27 servicesUpdated: { 28 servicesUpdated: {
28 id: 'infobar.servicesUpdated', 29 id: 'infobar.servicesUpdated',
@@ -77,6 +78,7 @@ const toggleFullScreen = () => {
77class AppLayout extends Component { 78class AppLayout extends Component {
78 static propTypes = { 79 static propTypes = {
79 classes: PropTypes.object.isRequired, 80 classes: PropTypes.object.isRequired,
81 settings: PropTypes.object.isRequired,
80 isFullScreen: PropTypes.bool.isRequired, 82 isFullScreen: PropTypes.bool.isRequired,
81 sidebar: PropTypes.element.isRequired, 83 sidebar: PropTypes.element.isRequired,
82 workspacesDrawer: PropTypes.element.isRequired, 84 workspacesDrawer: PropTypes.element.isRequired,
@@ -115,6 +117,7 @@ class AppLayout extends Component {
115 authRequestFailed, 117 authRequestFailed,
116 reloadServicesAfterUpdate, 118 reloadServicesAfterUpdate,
117 installAppUpdate, 119 installAppUpdate,
120 settings,
118 showRequiredRequestsError, 121 showRequiredRequestsError,
119 areRequiredRequestsSuccessful, 122 areRequiredRequestsSuccessful,
120 retryRequiredRequests, 123 retryRequiredRequests,
@@ -123,6 +126,11 @@ class AppLayout extends Component {
123 126
124 const { intl } = this.props; 127 const { intl } = this.props;
125 128
129 const { locked } = settings.app;
130 if (locked) {
131 return <LockedScreen />;
132 }
133
126 return ( 134 return (
127 <ErrorBoundary> 135 <ErrorBoundary>
128 <div className="app"> 136 <div className="app">
@@ -193,7 +201,6 @@ class AppLayout extends Component {
193 )} 201 )}
194 <BasicAuth /> 202 <BasicAuth />
195 <QuickSwitch /> 203 <QuickSwitch />
196 <NightlyBuilds />
197 <PublishDebugInfo /> 204 <PublishDebugInfo />
198 {services} 205 {services}
199 {children} 206 {children}
diff --git a/src/components/layout/Sidebar.js b/src/components/layout/Sidebar.js
index 4f67a8719..e3df9c2ea 100644
--- a/src/components/layout/Sidebar.js
+++ b/src/components/layout/Sidebar.js
@@ -3,12 +3,10 @@ import PropTypes from 'prop-types';
3import ReactTooltip from 'react-tooltip'; 3import ReactTooltip from 'react-tooltip';
4import { defineMessages, injectIntl } from 'react-intl'; 4import { defineMessages, injectIntl } from 'react-intl';
5import { inject, observer } from 'mobx-react'; 5import { inject, observer } from 'mobx-react';
6import { Link } from 'react-router';
7import { 6import {
8 mdiCheckAll, 7 mdiCheckAll,
9 mdiViewGrid, 8 mdiViewGrid,
10 mdiPlusBox, 9 mdiPlusBox,
11 mdiLoginVariant,
12 mdiCog, 10 mdiCog,
13 mdiBellOff, 11 mdiBellOff,
14 mdiBell, 12 mdiBell,
@@ -137,7 +135,6 @@ class Sidebar extends Component {
137 const workspaceToggleMessage = isWorkspaceDrawerOpen 135 const workspaceToggleMessage = isWorkspaceDrawerOpen
138 ? messages.closeWorkspaceDrawer 136 ? messages.closeWorkspaceDrawer
139 : messages.openWorkspaceDrawer; 137 : messages.openWorkspaceDrawer;
140 const isLoggedIn = Boolean(localStorage.getItem('authToken'));
141 138
142 return ( 139 return (
143 <div className="sidebar"> 140 <div className="sidebar">
@@ -147,95 +144,86 @@ class Sidebar extends Component {
147 disableToolTip={() => this.disableToolTip()} 144 disableToolTip={() => this.disableToolTip()}
148 useVerticalStyle={stores.settings.all.app.useVerticalStyle} 145 useVerticalStyle={stores.settings.all.app.useVerticalStyle}
149 /> 146 />
150 {isLoggedIn ? ( 147 <>
151 <> 148 <button
152 {stores.settings.all.app.lockingFeatureEnabled ? ( 149 type="button"
153 <button 150 onClick={() => openSettings({ path: 'recipes' })}
154 type="button" 151 className="sidebar__button sidebar__button--new-service"
155 className="sidebar__button" 152 data-tip={`${intl.formatMessage(
156 onClick={() => { 153 messages.addNewService,
157 actions.settings.update({ 154 )} (${addNewServiceShortcutKey(false)})`}
158 type: 'app', 155 >
159 data: { 156 <Icon icon={mdiPlusBox} size={1.5} />
160 locked: true, 157 </button>
161 }, 158 <button
162 }); 159 type="button"
163 }} 160 onClick={() => {
164 data-tip={`${intl.formatMessage( 161 toggleWorkspaceDrawer();
165 messages.lockFerdi, 162 this.updateToolTip();
166 )} (${lockFerdiShortcutKey(false)})`} 163 }}
167 > 164 className={`sidebar__button sidebar__button--workspaces ${
168 <Icon icon={mdiLock} size={1.5} /> 165 isWorkspaceDrawerOpen ? 'is-active' : ''
169 </button> 166 }`}
170 ) : null} 167 data-tip={`${intl.formatMessage(
171 {todosStore.isFeatureEnabledByUser ? ( 168 workspaceToggleMessage,
172 <button 169 )} (${workspaceToggleShortcutKey(false)})`}
173 type="button" 170 >
174 onClick={() => { 171 <Icon icon={mdiViewGrid} size={1.5} />
175 todoActions.toggleTodosPanel(); 172 </button>
176 this.updateToolTip(); 173 {todosStore.isFeatureEnabledByUser ? (
177 }}
178 disabled={isTodosServiceActive}
179 className={`sidebar__button sidebar__button--todos ${
180 todosStore.isTodosPanelVisible ? 'is-active' : ''
181 }`}
182 data-tip={`${intl.formatMessage(
183 todosToggleMessage,
184 )} (${todosToggleShortcutKey(false)})`}
185 >
186 <Icon icon={mdiCheckAll} size={1.5} />
187 </button>
188 ) : null}
189 <button 174 <button
190 type="button" 175 type="button"
191 onClick={() => { 176 onClick={() => {
192 toggleWorkspaceDrawer(); 177 todoActions.toggleTodosPanel();
193 this.updateToolTip(); 178 this.updateToolTip();
194 }} 179 }}
195 className={`sidebar__button sidebar__button--workspaces ${ 180 disabled={isTodosServiceActive}
196 isWorkspaceDrawerOpen ? 'is-active' : '' 181 className={`sidebar__button sidebar__button--todos ${
182 todosStore.isTodosPanelVisible ? 'is-active' : ''
197 }`} 183 }`}
198 data-tip={`${intl.formatMessage( 184 data-tip={`${intl.formatMessage(
199 workspaceToggleMessage, 185 todosToggleMessage,
200 )} (${workspaceToggleShortcutKey(false)})`} 186 )} (${todosToggleShortcutKey(false)})`}
201 > 187 >
202 <Icon icon={mdiViewGrid} size={1.5} /> 188 <Icon icon={mdiCheckAll} size={1.5} />
203 </button> 189 </button>
190 ) : null}
191 <button
192 type="button"
193 onClick={() => {
194 toggleMuteApp();
195 this.updateToolTip();
196 }}
197 className={`sidebar__button sidebar__button--audio ${
198 isAppMuted ? 'is-muted' : ''
199 }`}
200 data-tip={`${intl.formatMessage(
201 isAppMuted ? messages.unmute : messages.mute,
202 )} (${muteFerdiShortcutKey(false)})`}
203 >
204 <Icon icon={isAppMuted ? mdiBellOff : mdiBell} size={1.5} />
205 </button>
206
207 {stores.settings.all.app.lockingFeatureEnabled ? (
204 <button 208 <button
205 type="button" 209 type="button"
210 className="sidebar__button"
206 onClick={() => { 211 onClick={() => {
207 toggleMuteApp(); 212 actions.settings.update({
208 this.updateToolTip(); 213 type: 'app',
214 data: {
215 locked: true,
216 },
217 });
209 }} 218 }}
210 className={`sidebar__button sidebar__button--audio ${
211 isAppMuted ? 'is-muted' : ''
212 }`}
213 data-tip={`${intl.formatMessage( 219 data-tip={`${intl.formatMessage(
214 isAppMuted ? messages.unmute : messages.mute, 220 messages.lockFerdi,
215 )} (${muteFerdiShortcutKey(false)})`} 221 )} (${lockFerdiShortcutKey(false)})`}
216 > 222 >
217 <Icon icon={isAppMuted ? mdiBellOff : mdiBell} size={1.5} /> 223 <Icon icon={mdiLock} size={1.5} />
218 </button> 224 </button>
219 <button 225 ) : null}
220 type="button" 226 </>
221 onClick={() => openSettings({ path: 'recipes' })}
222 className="sidebar__button sidebar__button--new-service"
223 data-tip={`${intl.formatMessage(
224 messages.addNewService,
225 )} (${addNewServiceShortcutKey(false)})`}
226 >
227 <Icon icon={mdiPlusBox} size={1.5} />
228 </button>
229 </>
230 ) : (
231 <Link
232 to="/auth/welcome"
233 className="sidebar__button sidebar__button--new-service"
234 data-tip="Login"
235 >
236 <Icon icon={mdiLoginVariant} size={1.5} />
237 </Link>
238 )}
239 <button 227 <button
240 type="button" 228 type="button"
241 onClick={() => openSettings({ path: 'app' })} 229 onClick={() => openSettings({ path: 'app' })}
diff --git a/src/components/services/content/Services.js b/src/components/services/content/Services.js
index cbc70563d..9b43ea177 100644
--- a/src/components/services/content/Services.js
+++ b/src/components/services/content/Services.js
@@ -9,7 +9,6 @@ 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';
13 12
14const messages = defineMessages({ 13const messages = defineMessages({
15 getStarted: { 14 getStarted: {
@@ -52,7 +51,6 @@ class Services extends Component {
52 update: PropTypes.func.isRequired, 51 update: PropTypes.func.isRequired,
53 userHasCompletedSignup: PropTypes.bool.isRequired, 52 userHasCompletedSignup: PropTypes.bool.isRequired,
54 classes: PropTypes.object.isRequired, 53 classes: PropTypes.object.isRequired,
55 actions: PropTypes.object.isRequired,
56 isSpellcheckerEnabled: PropTypes.bool.isRequired, 54 isSpellcheckerEnabled: PropTypes.bool.isRequired,
57 }; 55 };
58 56
@@ -66,12 +64,6 @@ class Services extends Component {
66 64
67 _confettiTimeout = null; 65 _confettiTimeout = null;
68 66
69 constructor(props) {
70 super(props);
71
72 this.useLocalServer = this.useLocalServer.bind(this);
73 }
74
75 componentDidMount() { 67 componentDidMount() {
76 this._confettiTimeout = window.setTimeout(() => { 68 this._confettiTimeout = window.setTimeout(() => {
77 this.setState({ 69 this.setState({
@@ -86,10 +78,6 @@ class Services extends Component {
86 } 78 }
87 } 79 }
88 80
89 useLocalServer() {
90 serverlessLogin(this.props.actions);
91 }
92
93 render() { 81 render() {
94 const { 82 const {
95 services, 83 services,
@@ -108,7 +96,6 @@ class Services extends Component {
108 const { showConfetti } = this.state; 96 const { showConfetti } = this.state;
109 97
110 const { intl } = this.props; 98 const { intl } = this.props;
111 const isLoggedIn = Boolean(localStorage.getItem('authToken'));
112 99
113 return ( 100 return (
114 <div className="services"> 101 <div className="services">
@@ -129,33 +116,13 @@ class Services extends Component {
129 alt="Logo" 116 alt="Logo"
130 style={{ maxHeight: '50vh' }} 117 style={{ maxHeight: '50vh' }}
131 /> 118 />
132 {!isLoggedIn && (
133 <>
134 <p>{intl.formatMessage(messages.login)}</p>
135 <p>{intl.formatMessage(messages.serverInfo)}</p>
136 </>
137 )}
138 <Appear timeout={300} transitionName="slideUp"> 119 <Appear timeout={300} transitionName="slideUp">
139 <Link 120 <Link
140 to={isLoggedIn ? '/settings/recipes' : '/auth/welcome'} 121 to='/settings/recipes'
141 className="button" 122 className="button"
142 > 123 >
143 {isLoggedIn 124 {intl.formatMessage(messages.getStarted)}
144 ? intl.formatMessage(messages.getStarted)
145 : 'Login'}
146 </Link> 125 </Link>
147 {!isLoggedIn && (
148 <button
149 type="button"
150 className="button"
151 style={{
152 marginLeft: 10,
153 }}
154 onClick={this.useLocalServer}
155 >
156 {intl.formatMessage(messages.serverless)}
157 </button>
158 )}
159 </Appear> 126 </Appear>
160 </div> 127 </div>
161 </Appear> 128 </Appear>
diff --git a/src/components/settings/account/AccountDashboard.js b/src/components/settings/account/AccountDashboard.js
index d0c56af05..6ef676eb4 100644
--- a/src/components/settings/account/AccountDashboard.js
+++ b/src/components/settings/account/AccountDashboard.js
@@ -165,13 +165,6 @@ class AccountDashboard extends Component {
165 /> 165 />
166 </div> 166 </div>
167 </div> 167 </div>
168 <Button
169 label={intl.formatMessage(
170 messages.accountEditButton,
171 )}
172 className="franz-form__button--inverted"
173 onClick={openEditAccount}
174 />
175 </div> 168 </div>
176 </div> 169 </div>
177 {user.isSubscriptionOwner && isUsingFranzServer && ( 170 {user.isSubscriptionOwner && isUsingFranzServer && (
diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js
index 2fdb6e574..e8370d2ef 100644
--- a/src/components/settings/navigation/SettingsNavigation.js
+++ b/src/components/settings/navigation/SettingsNavigation.js
@@ -57,42 +57,34 @@ class SettingsNavigation extends Component {
57 workspaceCount: PropTypes.number.isRequired, 57 workspaceCount: PropTypes.number.isRequired,
58 }; 58 };
59 59
60 handleLoginLogout() { 60 handleLogout() {
61 const isLoggedIn = Boolean(localStorage.getItem('authToken'));
62 const isUsingWithoutAccount = 61 const isUsingWithoutAccount =
63 this.props.stores.settings.app.server === LOCAL_SERVER; 62 this.props.stores.settings.app.server === LOCAL_SERVER;
64 63
65 if (isLoggedIn) { 64 // Remove current auth token
66 // Remove current auth token 65 localStorage.removeItem('authToken');
67 localStorage.removeItem('authToken');
68 66
69 if (isUsingWithoutAccount) { 67 if (isUsingWithoutAccount) {
70 // Reset server back to Ferdi API 68 // Reset server back to Ferdi API
71 this.props.actions.settings.update({ 69 this.props.actions.settings.update({
72 type: 'app', 70 type: 'app',
73 data: { 71 data: {
74 server: LIVE_FERDI_API, 72 server: LIVE_FERDI_API,
75 }, 73 },
76 }); 74 });
77 }
78 this.props.stores.user.isLoggingOut = true;
79 } 75 }
76 this.props.stores.user.isLoggingOut = true;
80 77
81 this.props.stores.router.push( 78 this.props.stores.router.push('/auth/welcome');
82 isLoggedIn ? '/auth/logout' : '/auth/welcome',
83 );
84 79
85 if (isLoggedIn) { 80 // Reload Ferdi, otherwise many settings won't sync correctly with the server
86 // Reload Ferdi, otherwise many settings won't sync correctly with the server 81 // after logging into another account
87 // after logging into another account 82 window.location.reload();
88 window.location.reload();
89 }
90 } 83 }
91 84
92 render() { 85 render() {
93 const { serviceCount, workspaceCount, stores } = this.props; 86 const { serviceCount, workspaceCount, stores } = this.props;
94 const { intl } = this.props; 87 const { intl } = this.props;
95 const isLoggedIn = Boolean(localStorage.getItem('authToken'));
96 const isUsingWithoutAccount = stores.settings.app.server === LOCAL_SERVER; 88 const isUsingWithoutAccount = stores.settings.app.server === LOCAL_SERVER;
97 const isUsingFranzServer = stores.settings.app.server === LIVE_FRANZ_API; 89 const isUsingFranzServer = stores.settings.app.server === LIVE_FRANZ_API;
98 90
@@ -109,7 +101,6 @@ class SettingsNavigation extends Component {
109 to="/settings/services" 101 to="/settings/services"
110 className="settings-navigation__link" 102 className="settings-navigation__link"
111 activeClassName="is-active" 103 activeClassName="is-active"
112 disabled={!isLoggedIn}
113 > 104 >
114 {intl.formatMessage(messages.yourServices)}{' '} 105 {intl.formatMessage(messages.yourServices)}{' '}
115 <span className="badge">{serviceCount}</span> 106 <span className="badge">{serviceCount}</span>
@@ -118,7 +109,6 @@ class SettingsNavigation extends Component {
118 to="/settings/workspaces" 109 to="/settings/workspaces"
119 className="settings-navigation__link" 110 className="settings-navigation__link"
120 activeClassName="is-active" 111 activeClassName="is-active"
121 disabled={!isLoggedIn}
122 > 112 >
123 {intl.formatMessage(messages.yourWorkspaces)}{' '} 113 {intl.formatMessage(messages.yourWorkspaces)}{' '}
124 <span className="badge">{workspaceCount}</span> 114 <span className="badge">{workspaceCount}</span>
@@ -128,7 +118,6 @@ class SettingsNavigation extends Component {
128 to="/settings/user" 118 to="/settings/user"
129 className="settings-navigation__link" 119 className="settings-navigation__link"
130 activeClassName="is-active" 120 activeClassName="is-active"
131 disabled={!isLoggedIn}
132 > 121 >
133 {intl.formatMessage(messages.account)} 122 {intl.formatMessage(messages.account)}
134 </Link> 123 </Link>
@@ -138,7 +127,6 @@ class SettingsNavigation extends Component {
138 to="/settings/team" 127 to="/settings/team"
139 className="settings-navigation__link" 128 className="settings-navigation__link"
140 activeClassName="is-active" 129 activeClassName="is-active"
141 disabled={!isLoggedIn}
142 > 130 >
143 {intl.formatMessage(messages.team)} 131 {intl.formatMessage(messages.team)}
144 </Link> 132 </Link>
@@ -160,13 +148,13 @@ class SettingsNavigation extends Component {
160 <span className="settings-navigation__expander" /> 148 <span className="settings-navigation__expander" />
161 <button 149 <button
162 type="button" 150 type="button"
163 to={isLoggedIn ? '/auth/logout' : '/auth/welcome'} 151 to='/auth/logout'
164 className="settings-navigation__link" 152 className="settings-navigation__link"
165 onClick={this.handleLoginLogout.bind(this)} 153 onClick={this.handleLogout.bind(this)}
166 > 154 >
167 {isLoggedIn && !isUsingWithoutAccount 155 {!isUsingWithoutAccount
168 ? intl.formatMessage(messages.logout) 156 ? intl.formatMessage(messages.logout)
169 : 'Login'} 157 : 'Exit session'}
170 </button> 158 </button>
171 </div> 159 </div>
172 ); 160 );
diff --git a/src/components/settings/recipes/RecipesDashboard.js b/src/components/settings/recipes/RecipesDashboard.js
index 0ba295369..4f19a6116 100644
--- a/src/components/settings/recipes/RecipesDashboard.js
+++ b/src/components/settings/recipes/RecipesDashboard.js
@@ -148,8 +148,6 @@ class RecipesDashboard extends Component {
148 const communityRecipes = recipes.filter(r => !r.isDevRecipe); 148 const communityRecipes = recipes.filter(r => !r.isDevRecipe);
149 const devRecipes = recipes.filter(r => r.isDevRecipe); 149 const devRecipes = recipes.filter(r => r.isDevRecipe);
150 150
151 const isLoggedIn = Boolean(localStorage.getItem('authToken'));
152
153 return ( 151 return (
154 <div className="settings__main"> 152 <div className="settings__main">
155 <div className="settings__header"> 153 <div className="settings__header">
@@ -240,7 +238,6 @@ class RecipesDashboard extends Component {
240 key={recipe.id} 238 key={recipe.id}
241 recipe={recipe} 239 recipe={recipe}
242 onClick={() => 240 onClick={() =>
243 isLoggedIn &&
244 showAddServiceInterface({ recipeId: recipe.id }) 241 showAddServiceInterface({ recipeId: recipe.id })
245 } 242 }
246 /> 243 />
@@ -253,7 +250,6 @@ class RecipesDashboard extends Component {
253 key={customWebsiteRecipe.id} 250 key={customWebsiteRecipe.id}
254 recipe={customWebsiteRecipe} 251 recipe={customWebsiteRecipe}
255 onClick={() => 252 onClick={() =>
256 isLoggedIn &&
257 showAddServiceInterface({ 253 showAddServiceInterface({
258 recipeId: customWebsiteRecipe.id, 254 recipeId: customWebsiteRecipe.id,
259 }) 255 })
@@ -274,7 +270,6 @@ class RecipesDashboard extends Component {
274 key={recipe.id} 270 key={recipe.id}
275 recipe={recipe} 271 recipe={recipe}
276 onClick={() => 272 onClick={() =>
277 isLoggedIn &&
278 showAddServiceInterface({ recipeId: recipe.id }) 273 showAddServiceInterface({ recipeId: recipe.id })
279 } 274 }
280 /> 275 />
diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js
index 1757ac297..01e609580 100644
--- a/src/components/settings/settings/EditSettingsForm.js
+++ b/src/components/settings/settings/EditSettingsForm.js
@@ -9,9 +9,9 @@ import { mdiGithub, mdiOpenInNew } from '@mdi/js';
9import Form from '../../../lib/Form'; 9import Form from '../../../lib/Form';
10import Button from '../../ui/Button'; 10import Button from '../../ui/Button';
11import Toggle from '../../ui/Toggle'; 11import Toggle from '../../ui/Toggle';
12import ToggleRaw from '../../ui/ToggleRaw';
13import Select from '../../ui/Select'; 12import Select from '../../ui/Select';
14import Input from '../../ui/Input'; 13import Input from '../../ui/Input';
14import Infobox from '../../ui/Infobox';
15 15
16import { 16import {
17 DEFAULT_APP_SETTINGS, 17 DEFAULT_APP_SETTINGS,
@@ -157,7 +157,7 @@ const messages = defineMessages({
157 }, 157 },
158 updateStatusSearching: { 158 updateStatusSearching: {
159 id: 'settings.app.updateStatusSearching', 159 id: 'settings.app.updateStatusSearching',
160 defaultMessage: 'Is searching for update', 160 defaultMessage: 'Searching for updates...',
161 }, 161 },
162 updateStatusAvailable: { 162 updateStatusAvailable: {
163 id: 'settings.app.updateStatusAvailable', 163 id: 'settings.app.updateStatusAvailable',
@@ -193,6 +193,7 @@ class EditSettingsForm extends Component {
193 isUpdateAvailable: PropTypes.bool.isRequired, 193 isUpdateAvailable: PropTypes.bool.isRequired,
194 noUpdateAvailable: PropTypes.bool.isRequired, 194 noUpdateAvailable: PropTypes.bool.isRequired,
195 updateIsReadyToInstall: PropTypes.bool.isRequired, 195 updateIsReadyToInstall: PropTypes.bool.isRequired,
196 updateFailed: PropTypes.bool.isRequired,
196 isClearingAllCache: PropTypes.bool.isRequired, 197 isClearingAllCache: PropTypes.bool.isRequired,
197 onClearAllCache: PropTypes.func.isRequired, 198 onClearAllCache: PropTypes.func.isRequired,
198 getCacheSize: PropTypes.func.isRequired, 199 getCacheSize: PropTypes.func.isRequired,
@@ -201,7 +202,6 @@ class EditSettingsForm extends Component {
201 isDarkmodeEnabled: PropTypes.bool.isRequired, 202 isDarkmodeEnabled: PropTypes.bool.isRequired,
202 isAdaptableDarkModeEnabled: PropTypes.bool.isRequired, 203 isAdaptableDarkModeEnabled: PropTypes.bool.isRequired,
203 isSplitModeEnabled: PropTypes.bool.isRequired, 204 isSplitModeEnabled: PropTypes.bool.isRequired,
204 isNightlyEnabled: PropTypes.bool.isRequired,
205 hasAddedTodosAsService: PropTypes.bool.isRequired, 205 hasAddedTodosAsService: PropTypes.bool.isRequired,
206 isOnline: PropTypes.bool.isRequired, 206 isOnline: PropTypes.bool.isRequired,
207 }; 207 };
@@ -242,6 +242,7 @@ class EditSettingsForm extends Component {
242 isUpdateAvailable, 242 isUpdateAvailable,
243 noUpdateAvailable, 243 noUpdateAvailable,
244 updateIsReadyToInstall, 244 updateIsReadyToInstall,
245 updateFailed,
245 isClearingAllCache, 246 isClearingAllCache,
246 onClearAllCache, 247 onClearAllCache,
247 getCacheSize, 248 getCacheSize,
@@ -249,7 +250,6 @@ class EditSettingsForm extends Component {
249 isDarkmodeEnabled, 250 isDarkmodeEnabled,
250 isSplitModeEnabled, 251 isSplitModeEnabled,
251 isTodosActivated, 252 isTodosActivated,
252 isNightlyEnabled,
253 hasAddedTodosAsService, 253 hasAddedTodosAsService,
254 isOnline, 254 isOnline,
255 } = this.props; 255 } = this.props;
@@ -767,17 +767,6 @@ class EditSettingsForm extends Component {
767 {automaticUpdates && ( 767 {automaticUpdates && (
768 <div> 768 <div>
769 <Toggle field={form.$('beta')} /> 769 <Toggle field={form.$('beta')} />
770 <ToggleRaw
771 field={{
772 value: isNightlyEnabled,
773 id: 'nightly',
774 label: 'Include nightly versions',
775 name: 'Nightly builds',
776 }}
777 onChange={
778 window['ferdi'].features.nightlyBuilds.toggleFeature
779 }
780 />
781 {updateIsReadyToInstall ? ( 770 {updateIsReadyToInstall ? (
782 <Button 771 <Button
783 label={intl.formatMessage(messages.buttonInstallUpdate)} 772 label={intl.formatMessage(messages.buttonInstallUpdate)}
@@ -800,14 +789,21 @@ class EditSettingsForm extends Component {
800 <br /> 789 <br />
801 </div> 790 </div>
802 )} 791 )}
803 {intl.formatMessage(messages.currentVersion)} {ferdiVersion} 792 <p>
793 {intl.formatMessage(messages.currentVersion)} {ferdiVersion}
794 </p>
804 {noUpdateAvailable && ( 795 {noUpdateAvailable && (
805 <> 796 <>
806 <br /> 797 <br />
807 <br /> 798 <br />
808 {intl.formatMessage(messages.updateStatusUpToDate)} 799 {intl.formatMessage(messages.updateStatusUpToDate)}.
809 </> 800 </>
810 )} 801 )}
802 {updateFailed && (
803 <Infobox type="danger" icon="alert">
804 An error occured (check the console for more details)
805 </Infobox>
806 )}
811 <p className="settings__message"> 807 <p className="settings__message">
812 <Icon icon={mdiGithub} /> 808 <Icon icon={mdiGithub} />
813 Ferdi is based on{' '} 809 Ferdi is based on{' '}
diff --git a/src/components/settings/supportFerdi/SupportFerdiDashboard.tsx b/src/components/settings/supportFerdi/SupportFerdiDashboard.tsx
index acebc979f..948d0c1d1 100644
--- a/src/components/settings/supportFerdi/SupportFerdiDashboard.tsx
+++ b/src/components/settings/supportFerdi/SupportFerdiDashboard.tsx
@@ -1,6 +1,4 @@
1import { defineMessages, useIntl } from 'react-intl'; 1import { defineMessages, useIntl } from 'react-intl';
2import { mdiOpenInNew } from '@mdi/js';
3import { Icon } from '../../ui/icon';
4 2
5const messages = defineMessages({ 3const messages = defineMessages({
6 headline: { 4 headline: {
@@ -9,60 +7,17 @@ const messages = defineMessages({
9 }, 7 },
10 aboutIntro: { 8 aboutIntro: {
11 id: 'settings.supportFerdi.aboutIntro', 9 id: 'settings.supportFerdi.aboutIntro',
12 defaultMessage: 10 defaultMessage: 'Special thanks goes to these awesome people:',
13 '<p>Ferdi is an open-source and a community-lead application.</p><p>Thanks to the people who make this possbile:</p>',
14 },
15 textListContributors: {
16 id: 'settings.supportFerdi.textListContributors',
17 defaultMessage: 'Full list of contributors',
18 },
19 textListContributorsHere: {
20 id: 'settings.supportFerdi.textListContributorsHere',
21 defaultMessage: 'here',
22 },
23 textVolunteers: {
24 id: 'settings.supportFerdi.textVolunteers',
25 defaultMessage:
26 'The development of Ferdi is done by volunteers. People who use Ferdi like you. They maintain, fix, and improve Ferdi in their spare time.',
27 },
28 textSupportWelcome: {
29 id: 'settings.supportFerdi.textSupportWelcome',
30 defaultMessage:
31 'Support is always welcome. You can find a list of the help we need',
32 },
33 textSupportWelcomeHere: {
34 id: 'settings.supportFerdi.textSupportWelcomeHere',
35 defaultMessage: 'here',
36 },
37 textExpenses: {
38 id: 'settings.supportFerdi.textExpenses',
39 defaultMessage:
40 'While volunteers do most of the work, we still need to pay for servers and certificates. As a community, we are fully transparent on funds we collect and spend - see our',
41 },
42 textOpenCollective: {
43 id: 'settings.supportFerdi.textOpenCollective',
44 defaultMessage: 'Open Collective',
45 },
46 textDonation: {
47 id: 'settings.supportFerdi.textDonation',
48 defaultMessage:
49 'If you feel like supporting Ferdi development with a donation, you can do so on both,',
50 },
51 textDonationAnd: {
52 id: 'settings.supportFerdi.textDonationAnd',
53 defaultMessage: 'and',
54 },
55 textGitHubSponsors: {
56 id: 'settings.supportFerdi.textGitHubSponsors',
57 defaultMessage: 'GitHub Sponsors',
58 }, 11 },
12 about: {
13 id: 'settings.supportFerdi.about',
14 defaultMessage: 'The development of Ferdi is done by contributors. People who use Ferdi like you. They maintain, fix, and improve Ferdi in their spare time.',
15 }
59}); 16});
60 17
61const SupportFerdiDashboard = () => { 18const SupportFerdiDashboard = () => {
62 const intl = useIntl(); 19 const intl = useIntl();
63 20
64 const aboutIntro = intl.formatMessage(messages.aboutIntro);
65
66 return ( 21 return (
67 <div className="settings__main"> 22 <div className="settings__main">
68 <div className="settings__header"> 23 <div className="settings__header">
@@ -74,16 +29,6 @@ const SupportFerdiDashboard = () => {
74 <div> 29 <div>
75 <p className="settings__support-badges"> 30 <p className="settings__support-badges">
76 <a 31 <a
77 href="https://github.com/getferdi/ferdi"
78 target="_blank"
79 rel="noreferrer"
80 >
81 <img
82 alt="GitHub Stars"
83 src="https://img.shields.io/github/stars/getferdi/ferdi?style=social"
84 />
85 </a>
86 <a
87 href="https://twitter.com/getferdi/" 32 href="https://twitter.com/getferdi/"
88 target="_blank" 33 target="_blank"
89 rel="noreferrer" 34 rel="noreferrer"
@@ -94,104 +39,44 @@ const SupportFerdiDashboard = () => {
94 /> 39 />
95 </a> 40 </a>
96 <a 41 <a
97 href="https://opencollective.com/getferdi#section-contributors" 42 href="https://github.com/getferdi/ferdi"
98 target="_blank" 43 target="_blank"
99 rel="noreferrer" 44 rel="noreferrer"
100 > 45 >
101 <img 46 <img
102 alt="Open Collective backers" 47 alt="GitHub Stars"
103 src="https://img.shields.io/opencollective/backers/getferdi?logo=open-collective" 48 src="https://img.shields.io/github/stars/getferdi/ferdi?style=social"
104 /> 49 />
105 </a> 50 </a>
51 <a target="_blank" href="https://crowdin.com/project/getferdi">
52 <img src="https://badges.crowdin.net/getferdi/localized.svg" alt="Crowdin"/>
53 </a>
106 <a 54 <a
107 href="https://opencollective.com/getferdi#section-contributors" 55 href="https://opencollective.com/getferdi#section-contributors"
108 target="_blank" 56 target="_blank"
109 rel="noreferrer" 57 rel="noreferrer"
110 > 58 >
111 <img 59 <img
112 alt="Open Collective sponsors" 60 alt="Open Collective backers"
113 src="https://img.shields.io/opencollective/sponsors/getferdi?logo=open-collective" 61 src="https://img.shields.io/opencollective/backers/getferdi?logo=open-collective"
114 />
115 </a>
116 </p>
117 <span dangerouslySetInnerHTML={{ __html: aboutIntro }} />
118 <br />
119 <br />
120 <p>
121 <a href="#contributors-via-opencollective">
122 <img
123 alt="GitHub contributors (non-exhaustive)"
124 width="100%"
125 src="https://opencollective.com/getferdi/contributors.svg?width=642&button=false"
126 /> 62 />
127 </a> 63 </a>
128 </p> 64 </p>
65 <p>{intl.formatMessage(messages.aboutIntro)}</p>
129 <p> 66 <p>
130 {intl.formatMessage(messages.textListContributors)}
131 <a 67 <a
132 href="https://github.com/getferdi/ferdi#contributors-" 68 href="https://github.com/getferdi/ferdi#contributors-"
133 target="_blank" 69 target="_blank"
134 className="link"
135 rel="noreferrer" 70 rel="noreferrer"
136 > 71 >
137 {' '} 72 <img
138 {intl.formatMessage(messages.textListContributorsHere)} 73 alt="GitHub contributors (non-exhaustive)"
139 <Icon icon={mdiOpenInNew} /> 74 width="100%"
140 </a> 75 src="https://opencollective.com/getferdi/contributors.svg?width=600&avatarHeight=42&button=off"
141 <br /> 76 />
142 <br />
143 </p>
144 <p>{intl.formatMessage(messages.textVolunteers)}</p>
145 <p>
146 {intl.formatMessage(messages.textSupportWelcome)}
147 <a
148 href="https://help.getferdi.com/general/support"
149 target="_blank"
150 className="link"
151 rel="noreferrer"
152 >
153 {' '}
154 {intl.formatMessage(messages.textSupportWelcomeHere)}
155 <Icon icon={mdiOpenInNew} />
156 </a>
157 </p>
158 <p>
159 {intl.formatMessage(messages.textExpenses)}
160 <a
161 href="https://opencollective.com/getferdi#section-budget"
162 target="_blank"
163 className="link"
164 rel="noreferrer"
165 >
166 {' '}
167 {intl.formatMessage(messages.textOpenCollective)}
168 <Icon icon={mdiOpenInNew} />
169 </a>
170 </p>
171 <p>
172 {intl.formatMessage(messages.textDonation)}
173 <a
174 href="https://opencollective.com/getferdi#section-contribute"
175 target="_blank"
176 className="link"
177 rel="noreferrer"
178 >
179 {' '}
180 {intl.formatMessage(messages.textOpenCollective)}
181 <Icon icon={mdiOpenInNew} />
182 </a>{' '}
183 {intl.formatMessage(messages.textDonationAnd)}
184 <a
185 href="https://github.com/sponsors/getferdi"
186 target="_blank"
187 className="link"
188 rel="noreferrer"
189 >
190 {' '}
191 {intl.formatMessage(messages.textGitHubSponsors)}
192 <Icon icon={mdiOpenInNew} />
193 </a> 77 </a>
194 </p> 78 </p>
79 <p className="settings__message">{intl.formatMessage(messages.about)}</p>
195 </div> 80 </div>
196 </div> 81 </div>
197 </div> 82 </div>
diff --git a/src/components/ui/FullscreenLoader/index.js b/src/components/ui/FullscreenLoader/index.js
index 39b6c5a4c..d2f4b8321 100644
--- a/src/components/ui/FullscreenLoader/index.js
+++ b/src/components/ui/FullscreenLoader/index.js
@@ -11,7 +11,7 @@ import styles from './styles';
11class FullscreenLoader extends Component { 11class FullscreenLoader extends Component {
12 static propTypes = { 12 static propTypes = {
13 className: PropTypes.string, 13 className: PropTypes.string,
14 title: PropTypes.string.isRequired, 14 title: PropTypes.string,
15 classes: PropTypes.object.isRequired, 15 classes: PropTypes.object.isRequired,
16 theme: PropTypes.object.isRequired, 16 theme: PropTypes.object.isRequired,
17 spinnerColor: PropTypes.string, 17 spinnerColor: PropTypes.string,
@@ -22,6 +22,7 @@ class FullscreenLoader extends Component {
22 className: null, 22 className: null,
23 spinnerColor: null, 23 spinnerColor: null,
24 children: null, 24 children: null,
25 title: null
25 }; 26 };
26 27
27 render() { 28 render() {
diff --git a/src/components/ui/ToggleRaw.js b/src/components/ui/ToggleRaw.js
deleted file mode 100644
index e482b97b4..000000000
--- a/src/components/ui/ToggleRaw.js
+++ /dev/null
@@ -1,76 +0,0 @@
1/**
2 * "Raw" Toggle - for usage without a MobX Form element
3 */
4import { Component } from 'react';
5import PropTypes from 'prop-types';
6import { observer } from 'mobx-react';
7import classnames from 'classnames';
8
9class ToggleRaw extends Component {
10 static propTypes = {
11 onChange: PropTypes.func.isRequired,
12 field: PropTypes.shape({
13 value: PropTypes.bool.isRequired,
14 id: PropTypes.string,
15 name: PropTypes.string,
16 label: PropTypes.string,
17 error: PropTypes.string,
18 }).isRequired,
19 className: PropTypes.string,
20 showLabel: PropTypes.bool,
21 disabled: PropTypes.bool,
22 };
23
24 static defaultProps = {
25 className: '',
26 showLabel: true,
27 disabled: false,
28 };
29
30 onChange(e) {
31 const { onChange } = this.props;
32
33 onChange(e);
34 }
35
36 render() {
37 const { field, className, showLabel, disabled } = this.props;
38
39 return (
40 <div
41 className={classnames([
42 'franz-form__field',
43 'franz-form__toggle-wrapper',
44 'franz-form__toggle-disabled',
45 className,
46 ])}
47 >
48 <label
49 htmlFor={field.id}
50 className={classnames({
51 'franz-form__toggle': true,
52 'is-active': field.value,
53 })}
54 >
55 <div className="franz-form__toggle-button" />
56 <input
57 type="checkbox"
58 id={field.id}
59 name={field.name}
60 value={field.name}
61 checked={field.value}
62 onChange={e => (!disabled ? this.onChange(e) : null)}
63 />
64 </label>
65 {field.error && <div className={field.error}>{field.error}</div>}
66 {field.label && showLabel && (
67 <label className="franz-form__label" htmlFor={field.id}>
68 {field.label}
69 </label>
70 )}
71 </div>
72 );
73 }
74}
75
76export default observer(ToggleRaw);