aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Dominik Guzei <dominik.guzei@gmail.com>2019-01-14 17:11:27 +0100
committerLibravatar Dominik Guzei <dominik.guzei@gmail.com>2019-02-12 13:39:03 +0100
commit84755ddf8b8fb015ee2bfd70e9c4aa50d256f9d0 (patch)
treee680bd3e1aab13fa1e413ca54f2f6d7a27c35b20 /src
parentadd workspaces menu item in settings dialog (diff)
downloadferdium-app-84755ddf8b8fb015ee2bfd70e9c4aa50d256f9d0.tar.gz
ferdium-app-84755ddf8b8fb015ee2bfd70e9c4aa50d256f9d0.tar.zst
ferdium-app-84755ddf8b8fb015ee2bfd70e9c4aa50d256f9d0.zip
basic setup of workspaces settings screen
Diffstat (limited to 'src')
-rw-r--r--src/api/utils/auth.js24
-rw-r--r--src/app.js2
-rw-r--r--src/components/settings/workspaces/WorkspaceItem.js41
-rw-r--r--src/components/settings/workspaces/WorkspacesDashboard.js56
-rw-r--r--src/containers/settings/WorkspacesScreen.js29
-rw-r--r--src/environment.js1
-rw-r--r--src/features/workspaces/api.js16
-rw-r--r--src/features/workspaces/index.js10
-rw-r--r--src/features/workspaces/state.js12
-rw-r--r--src/features/workspaces/store.js15
-rw-r--r--src/i18n/locales/en-US.json1
-rw-r--r--src/models/Workspace.js25
12 files changed, 216 insertions, 16 deletions
diff --git a/src/api/utils/auth.js b/src/api/utils/auth.js
new file mode 100644
index 000000000..47ac94c19
--- /dev/null
+++ b/src/api/utils/auth.js
@@ -0,0 +1,24 @@
1import { remote } from 'electron';
2import localStorage from 'mobx-localstorage';
3
4const { app } = remote;
5
6export const prepareAuthRequest = (options, auth = true) => {
7 const request = Object.assign(options, {
8 mode: 'cors',
9 headers: Object.assign({
10 'Content-Type': 'application/json',
11 'X-Franz-Source': 'desktop',
12 'X-Franz-Version': app.getVersion(),
13 'X-Franz-platform': process.platform,
14 'X-Franz-Timezone-Offset': new Date().getTimezoneOffset(),
15 'X-Franz-System-Locale': app.getLocale(),
16 }, options.headers),
17 });
18
19 if (auth) {
20 request.headers.Authorization = `Bearer ${localStorage.getItem('authToken')}`;
21 }
22
23 return request;
24};
diff --git a/src/app.js b/src/app.js
index 6660feb46..ee1b12939 100644
--- a/src/app.js
+++ b/src/app.js
@@ -39,6 +39,7 @@ import PricingScreen from './containers/auth/PricingScreen';
39import InviteScreen from './containers/auth/InviteScreen'; 39import InviteScreen from './containers/auth/InviteScreen';
40import AuthLayoutContainer from './containers/auth/AuthLayoutContainer'; 40import AuthLayoutContainer from './containers/auth/AuthLayoutContainer';
41import SubscriptionPopupScreen from './containers/subscription/SubscriptionPopupScreen'; 41import SubscriptionPopupScreen from './containers/subscription/SubscriptionPopupScreen';
42import WorkspacesScreen from './containers/settings/WorkspacesScreen';
42 43
43// Add Polyfills 44// Add Polyfills
44smoothScroll.polyfill(); 45smoothScroll.polyfill();
@@ -75,6 +76,7 @@ window.addEventListener('load', () => {
75 <Route path="/settings/recipes/:filter" component={RecipesScreen} /> 76 <Route path="/settings/recipes/:filter" component={RecipesScreen} />
76 <Route path="/settings/services" component={ServicesScreen} /> 77 <Route path="/settings/services" component={ServicesScreen} />
77 <Route path="/settings/services/:action/:id" component={EditServiceScreen} /> 78 <Route path="/settings/services/:action/:id" component={EditServiceScreen} />
79 <Route path="/settings/workspaces" component={WorkspacesScreen} />
78 <Route path="/settings/user" component={AccountScreen} /> 80 <Route path="/settings/user" component={AccountScreen} />
79 <Route path="/settings/user/edit" component={EditUserScreen} /> 81 <Route path="/settings/user/edit" component={EditUserScreen} />
80 <Route path="/settings/app" component={EditSettingsScreen} /> 82 <Route path="/settings/app" component={EditSettingsScreen} />
diff --git a/src/components/settings/workspaces/WorkspaceItem.js b/src/components/settings/workspaces/WorkspaceItem.js
new file mode 100644
index 000000000..82a578ebd
--- /dev/null
+++ b/src/components/settings/workspaces/WorkspaceItem.js
@@ -0,0 +1,41 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { defineMessages, intlShape } from 'react-intl';
4import { observer } from 'mobx-react';
5import classnames from 'classnames';
6import Workspace from '../../../models/Workspace';
7
8// const messages = defineMessages({});
9
10@observer
11class WorkspaceItem extends Component {
12 static propTypes = {
13 workspace: PropTypes.instanceOf(Workspace).isRequired,
14 };
15
16 static contextTypes = {
17 intl: intlShape,
18 };
19
20 render() {
21 const { workspace } = this.props;
22 // const { intl } = this.context;
23
24 return (
25 <tr
26 className={classnames({
27 'service-table__row': true,
28 })}
29 >
30 <td
31 className="service-table__column-name"
32 onClick={() => console.log('go to workspace', workspace.name)}
33 >
34 {workspace.name}
35 </td>
36 </tr>
37 );
38 }
39}
40
41export default WorkspaceItem;
diff --git a/src/components/settings/workspaces/WorkspacesDashboard.js b/src/components/settings/workspaces/WorkspacesDashboard.js
new file mode 100644
index 000000000..830f32f08
--- /dev/null
+++ b/src/components/settings/workspaces/WorkspacesDashboard.js
@@ -0,0 +1,56 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { observer, PropTypes as MobxPropTypes } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl';
5
6import Loader from '../../ui/Loader';
7import WorkspaceItem from './WorkspaceItem';
8
9const messages = defineMessages({
10 headline: {
11 id: 'settings.workspaces.headline',
12 defaultMessage: '!!!Your workspaces',
13 },
14 noServicesAdded: {
15 id: 'settings.workspaces.noWorkspacesAdded',
16 defaultMessage: '!!!You haven\'t added any workspaces yet.',
17 },
18});
19
20@observer
21class WorkspacesDashboard extends Component {
22 static propTypes = {
23 workspaces: MobxPropTypes.arrayOrObservableArray.isRequired,
24 isLoading: PropTypes.bool.isRequired,
25 };
26
27 static contextTypes = {
28 intl: intlShape,
29 };
30
31 render() {
32 const { workspaces, isLoading } = this.props;
33 const { intl } = this.context;
34
35 return (
36 <div className="settings__main">
37 <div className="settings__header">
38 <h1>{intl.formatMessage(messages.headline)}</h1>
39 </div>
40 <div className="settings__body">
41 {isLoading ? (
42 <Loader />
43 ) : (
44 <table className="service-table">
45 <tbody>
46 {workspaces.map(workspace => <WorkspaceItem workspace={workspace} />)}
47 </tbody>
48 </table>
49 )}
50 </div>
51 </div>
52 );
53 }
54}
55
56export default WorkspacesDashboard;
diff --git a/src/containers/settings/WorkspacesScreen.js b/src/containers/settings/WorkspacesScreen.js
new file mode 100644
index 000000000..e767fdfbe
--- /dev/null
+++ b/src/containers/settings/WorkspacesScreen.js
@@ -0,0 +1,29 @@
1import React, { Component } from 'react';
2import { observer } from 'mobx-react';
3import { gaPage } from '../../lib/analytics';
4import { state } from '../../features/workspaces/state';
5
6import WorkspacesDashboard from '../../components/settings/workspaces/WorkspacesDashboard';
7import ErrorBoundary from '../../components/util/ErrorBoundary';
8
9@observer
10class WorkspacesScreen extends Component {
11 static propTypes = {};
12
13 componentDidMount() {
14 gaPage('Settings/Workspaces Dashboard');
15 }
16
17 render() {
18 return (
19 <ErrorBoundary>
20 <WorkspacesDashboard
21 workspaces={state.workspaces}
22 isLoading={state.isLoading}
23 />
24 </ErrorBoundary>
25 );
26 }
27}
28
29export default WorkspacesScreen;
diff --git a/src/environment.js b/src/environment.js
index 73b1c7ab2..d67fd6adb 100644
--- a/src/environment.js
+++ b/src/environment.js
@@ -28,3 +28,4 @@ if (!isDevMode || (isDevMode && useLiveAPI)) {
28} 28}
29 29
30export const API = api; 30export const API = api;
31export const API_VERSION = 'v1';
diff --git a/src/features/workspaces/api.js b/src/features/workspaces/api.js
index 1ee2440fe..97badbd01 100644
--- a/src/features/workspaces/api.js
+++ b/src/features/workspaces/api.js
@@ -1,9 +1,13 @@
1// TODO: use real server instead 1import { prepareAuthRequest } from '../../api/utils/auth';
2const workspaces = [ 2import { API, API_VERSION } from '../../environment';
3 { id: 'workspace-1', name: 'Private' },
4 { id: 'workspace-2', name: 'Office' },
5];
6 3
7export default { 4export default {
8 getUserWorkspaces: () => Promise.resolve(workspaces), 5 getUserWorkspaces: async () => {
6 const url = `${API}/${API_VERSION}/workspace`;
7 const request = await window.fetch(url, prepareAuthRequest({
8 method: 'GET',
9 }));
10 if (!request.ok) throw request;
11 return request.json();
12 },
9}; 13};
diff --git a/src/features/workspaces/index.js b/src/features/workspaces/index.js
index b4cfd3c2d..50ac3b414 100644
--- a/src/features/workspaces/index.js
+++ b/src/features/workspaces/index.js
@@ -1,14 +1,11 @@
1import { observable, reaction } from 'mobx'; 1import { reaction } from 'mobx';
2import { merge } from 'lodash';
3import WorkspacesStore from './store'; 2import WorkspacesStore from './store';
4import api from './api'; 3import api from './api';
4import { state, resetState } from './state';
5 5
6const debug = require('debug')('Franz:feature:workspaces'); 6const debug = require('debug')('Franz:feature:workspaces');
7 7
8let store = null; 8let store = null;
9const defaultState = { workspaces: [] };
10
11export const state = observable(defaultState);
12 9
13export default function initWorkspaces(stores, actions) { 10export default function initWorkspaces(stores, actions) {
14 const { features, user } = stores; 11 const { features, user } = stores;
@@ -27,8 +24,7 @@ export default function initWorkspaces(stores, actions) {
27 debug('Disabling `workspaces` feature'); 24 debug('Disabling `workspaces` feature');
28 store.teardown(); 25 store.teardown();
29 store = null; 26 store = null;
30 // Reset state to default 27 resetState(); // Reset state to default
31 merge(state, defaultState);
32 } 28 }
33 }, 29 },
34 { 30 {
diff --git a/src/features/workspaces/state.js b/src/features/workspaces/state.js
new file mode 100644
index 000000000..ed3fe9f00
--- /dev/null
+++ b/src/features/workspaces/state.js
@@ -0,0 +1,12 @@
1import { observable } from 'mobx';
2
3const defaultState = {
4 isLoading: false,
5 workspaces: [],
6};
7
8export const state = observable(defaultState);
9
10export function resetState() {
11 Object.assign(state, defaultState);
12}
diff --git a/src/features/workspaces/store.js b/src/features/workspaces/store.js
index 4b4e729ed..2b6d55cc7 100644
--- a/src/features/workspaces/store.js
+++ b/src/features/workspaces/store.js
@@ -1,6 +1,7 @@
1import { observable, reaction } from 'mobx'; 1import { observable, reaction } from 'mobx';
2import Store from '../../stores/lib/Store'; 2import Store from '../../stores/lib/Store';
3import CachedRequest from '../../stores/lib/CachedRequest'; 3import CachedRequest from '../../stores/lib/CachedRequest';
4import Workspace from '../../models/Workspace';
4 5
5const debug = require('debug')('Franz:feature:workspaces'); 6const debug = require('debug')('Franz:feature:workspaces');
6 7
@@ -18,12 +19,20 @@ export default class WorkspacesStore extends Store {
18 19
19 reaction( 20 reaction(
20 () => this.allWorkspacesRequest.result, 21 () => this.allWorkspacesRequest.result,
21 workspaces => this.setWorkspaces(workspaces), 22 workspaces => this._setWorkspaces(workspaces),
23 );
24 reaction(
25 () => this.allWorkspacesRequest.isExecuting,
26 isExecuting => this._setIsLoading(isExecuting),
22 ); 27 );
23 } 28 }
24 29
25 setWorkspaces = (workspaces) => { 30 _setWorkspaces = (workspaces) => {
26 debug('setting user workspaces', workspaces.slice()); 31 debug('setting user workspaces', workspaces.slice());
27 this.state.workspaces = workspaces; 32 this.state.workspaces = workspaces.map(data => new Workspace(data));
33 };
34
35 _setIsLoading = (isLoading) => {
36 this.state.isLoading = isLoading;
28 }; 37 };
29} 38}
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 1b869aff1..1652b5585 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -196,6 +196,7 @@
196 "settings.user.form.accountType.individual": "Individual", 196 "settings.user.form.accountType.individual": "Individual",
197 "settings.user.form.accountType.non-profit": "Non-Profit", 197 "settings.user.form.accountType.non-profit": "Non-Profit",
198 "settings.user.form.accountType.company": "Company", 198 "settings.user.form.accountType.company": "Company",
199 "settings.workspaces.headline": "Your workspaces",
199 "subscription.type.free": "free", 200 "subscription.type.free": "free",
200 "subscription.type.month": "month", 201 "subscription.type.month": "month",
201 "subscription.type.year": "year", 202 "subscription.type.year": "year",
diff --git a/src/models/Workspace.js b/src/models/Workspace.js
new file mode 100644
index 000000000..ede2710dc
--- /dev/null
+++ b/src/models/Workspace.js
@@ -0,0 +1,25 @@
1import { observable } from 'mobx';
2
3export default class Workspace {
4 id = null;
5
6 @observable name = null;
7
8 @observable order = null;
9
10 @observable services = [];
11
12 @observable userId = null;
13
14 constructor(data) {
15 if (!data.id) {
16 throw Error('Workspace requires Id');
17 }
18
19 this.id = data.id;
20 this.name = data.name;
21 this.order = data.order;
22 this.services = data.services;
23 this.userId = data.userId;
24 }
25}