diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/api/utils/auth.js | 24 | ||||
-rw-r--r-- | src/app.js | 2 | ||||
-rw-r--r-- | src/components/settings/workspaces/WorkspaceItem.js | 41 | ||||
-rw-r--r-- | src/components/settings/workspaces/WorkspacesDashboard.js | 56 | ||||
-rw-r--r-- | src/containers/settings/WorkspacesScreen.js | 29 | ||||
-rw-r--r-- | src/environment.js | 1 | ||||
-rw-r--r-- | src/features/workspaces/api.js | 16 | ||||
-rw-r--r-- | src/features/workspaces/index.js | 10 | ||||
-rw-r--r-- | src/features/workspaces/state.js | 12 | ||||
-rw-r--r-- | src/features/workspaces/store.js | 15 | ||||
-rw-r--r-- | src/i18n/locales/en-US.json | 1 | ||||
-rw-r--r-- | src/models/Workspace.js | 25 |
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 @@ | |||
1 | import { remote } from 'electron'; | ||
2 | import localStorage from 'mobx-localstorage'; | ||
3 | |||
4 | const { app } = remote; | ||
5 | |||
6 | export 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'; | |||
39 | import InviteScreen from './containers/auth/InviteScreen'; | 39 | import InviteScreen from './containers/auth/InviteScreen'; |
40 | import AuthLayoutContainer from './containers/auth/AuthLayoutContainer'; | 40 | import AuthLayoutContainer from './containers/auth/AuthLayoutContainer'; |
41 | import SubscriptionPopupScreen from './containers/subscription/SubscriptionPopupScreen'; | 41 | import SubscriptionPopupScreen from './containers/subscription/SubscriptionPopupScreen'; |
42 | import WorkspacesScreen from './containers/settings/WorkspacesScreen'; | ||
42 | 43 | ||
43 | // Add Polyfills | 44 | // Add Polyfills |
44 | smoothScroll.polyfill(); | 45 | smoothScroll.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 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { defineMessages, intlShape } from 'react-intl'; | ||
4 | import { observer } from 'mobx-react'; | ||
5 | import classnames from 'classnames'; | ||
6 | import Workspace from '../../../models/Workspace'; | ||
7 | |||
8 | // const messages = defineMessages({}); | ||
9 | |||
10 | @observer | ||
11 | class 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 | |||
41 | export 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 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | ||
4 | import { defineMessages, intlShape } from 'react-intl'; | ||
5 | |||
6 | import Loader from '../../ui/Loader'; | ||
7 | import WorkspaceItem from './WorkspaceItem'; | ||
8 | |||
9 | const 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 | ||
21 | class 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 | |||
56 | export 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 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import { observer } from 'mobx-react'; | ||
3 | import { gaPage } from '../../lib/analytics'; | ||
4 | import { state } from '../../features/workspaces/state'; | ||
5 | |||
6 | import WorkspacesDashboard from '../../components/settings/workspaces/WorkspacesDashboard'; | ||
7 | import ErrorBoundary from '../../components/util/ErrorBoundary'; | ||
8 | |||
9 | @observer | ||
10 | class 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 | |||
29 | export 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 | ||
30 | export const API = api; | 30 | export const API = api; |
31 | export 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 | 1 | import { prepareAuthRequest } from '../../api/utils/auth'; |
2 | const workspaces = [ | 2 | import { API, API_VERSION } from '../../environment'; |
3 | { id: 'workspace-1', name: 'Private' }, | ||
4 | { id: 'workspace-2', name: 'Office' }, | ||
5 | ]; | ||
6 | 3 | ||
7 | export default { | 4 | export 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 @@ | |||
1 | import { observable, reaction } from 'mobx'; | 1 | import { reaction } from 'mobx'; |
2 | import { merge } from 'lodash'; | ||
3 | import WorkspacesStore from './store'; | 2 | import WorkspacesStore from './store'; |
4 | import api from './api'; | 3 | import api from './api'; |
4 | import { state, resetState } from './state'; | ||
5 | 5 | ||
6 | const debug = require('debug')('Franz:feature:workspaces'); | 6 | const debug = require('debug')('Franz:feature:workspaces'); |
7 | 7 | ||
8 | let store = null; | 8 | let store = null; |
9 | const defaultState = { workspaces: [] }; | ||
10 | |||
11 | export const state = observable(defaultState); | ||
12 | 9 | ||
13 | export default function initWorkspaces(stores, actions) { | 10 | export 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 @@ | |||
1 | import { observable } from 'mobx'; | ||
2 | |||
3 | const defaultState = { | ||
4 | isLoading: false, | ||
5 | workspaces: [], | ||
6 | }; | ||
7 | |||
8 | export const state = observable(defaultState); | ||
9 | |||
10 | export 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 @@ | |||
1 | import { observable, reaction } from 'mobx'; | 1 | import { observable, reaction } from 'mobx'; |
2 | import Store from '../../stores/lib/Store'; | 2 | import Store from '../../stores/lib/Store'; |
3 | import CachedRequest from '../../stores/lib/CachedRequest'; | 3 | import CachedRequest from '../../stores/lib/CachedRequest'; |
4 | import Workspace from '../../models/Workspace'; | ||
4 | 5 | ||
5 | const debug = require('debug')('Franz:feature:workspaces'); | 6 | const 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 @@ | |||
1 | import { observable } from 'mobx'; | ||
2 | |||
3 | export 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 | } | ||