aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Dominik Guzei <dominik.guzei@gmail.com>2019-03-21 15:04:31 +0100
committerLibravatar Dominik Guzei <dominik.guzei@gmail.com>2019-03-21 15:04:31 +0100
commita9734f24bf15ab322c8244fbb8e86c37caf30f4a (patch)
treeace7e455e817df831bf571aa4851a99b0e37e145 /src
parentadd workspace drawer toggle menu item and shortcut (diff)
downloadferdium-app-a9734f24bf15ab322c8244fbb8e86c37caf30f4a.tar.gz
ferdium-app-a9734f24bf15ab322c8244fbb8e86c37caf30f4a.tar.zst
ferdium-app-a9734f24bf15ab322c8244fbb8e86c37caf30f4a.zip
improve workspace switching ux
Diffstat (limited to 'src')
-rw-r--r--src/components/layout/AppLayout.js3
-rw-r--r--src/components/ui/FullscreenLoader/index.js4
-rw-r--r--src/components/ui/WebviewLoader/index.js2
-rw-r--r--src/features/workspaces/components/WorkspaceDrawer.js7
-rw-r--r--src/features/workspaces/components/WorkspaceSwitchingIndicator.js56
-rw-r--r--src/features/workspaces/components/WorkspacesDashboard.js4
-rw-r--r--src/features/workspaces/containers/WorkspacesScreen.js2
-rw-r--r--src/features/workspaces/index.js11
-rw-r--r--src/features/workspaces/state.js4
-rw-r--r--src/features/workspaces/store.js25
-rw-r--r--src/i18n/messages/src/components/layout/AppLayout.json24
-rw-r--r--src/styles/layout.scss1
12 files changed, 112 insertions, 31 deletions
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js
index e06192f87..284a2523a 100644
--- a/src/components/layout/AppLayout.js
+++ b/src/components/layout/AppLayout.js
@@ -15,6 +15,8 @@ import ErrorBoundary from '../util/ErrorBoundary';
15 15
16import { isWindows } from '../../environment'; 16import { isWindows } from '../../environment';
17import { workspacesState } from '../../features/workspaces/state'; 17import { workspacesState } from '../../features/workspaces/state';
18import FullscreenLoader from '../ui/FullscreenLoader';
19import WorkspaceSwitchingIndicator from '../../features/workspaces/components/WorkspaceSwitchingIndicator';
18 20
19function createMarkup(HTMLString) { 21function createMarkup(HTMLString) {
20 return { __html: HTMLString }; 22 return { __html: HTMLString };
@@ -123,6 +125,7 @@ class AppLayout extends Component {
123 {workspacesDrawer} 125 {workspacesDrawer}
124 {sidebar} 126 {sidebar}
125 <div className="app__service"> 127 <div className="app__service">
128 <WorkspaceSwitchingIndicator />
126 {news.length > 0 && news.map(item => ( 129 {news.length > 0 && news.map(item => (
127 <InfoBar 130 <InfoBar
128 key={item.id} 131 key={item.id}
diff --git a/src/components/ui/FullscreenLoader/index.js b/src/components/ui/FullscreenLoader/index.js
index 6ecf4d395..06dab1eb6 100644
--- a/src/components/ui/FullscreenLoader/index.js
+++ b/src/components/ui/FullscreenLoader/index.js
@@ -16,13 +16,13 @@ export default @observer @withTheme @injectSheet(styles) class FullscreenLoader
16 theme: PropTypes.object.isRequired, 16 theme: PropTypes.object.isRequired,
17 spinnerColor: PropTypes.string, 17 spinnerColor: PropTypes.string,
18 children: PropTypes.node, 18 children: PropTypes.node,
19 } 19 };
20 20
21 static defaultProps = { 21 static defaultProps = {
22 className: null, 22 className: null,
23 spinnerColor: null, 23 spinnerColor: null,
24 children: null, 24 children: null,
25 } 25 };
26 26
27 render() { 27 render() {
28 const { 28 const {
diff --git a/src/components/ui/WebviewLoader/index.js b/src/components/ui/WebviewLoader/index.js
index 3a3dbbe49..d200b8193 100644
--- a/src/components/ui/WebviewLoader/index.js
+++ b/src/components/ui/WebviewLoader/index.js
@@ -11,7 +11,7 @@ export default @observer @injectSheet(styles) class WebviewLoader extends Compon
11 static propTypes = { 11 static propTypes = {
12 name: PropTypes.string.isRequired, 12 name: PropTypes.string.isRequired,
13 classes: PropTypes.object.isRequired, 13 classes: PropTypes.object.isRequired,
14 } 14 };
15 15
16 render() { 16 render() {
17 const { classes, name } = this.props; 17 const { classes, name } = this.props;
diff --git a/src/features/workspaces/components/WorkspaceDrawer.js b/src/features/workspaces/components/WorkspaceDrawer.js
index 27bf08361..c9c4d3bc9 100644
--- a/src/features/workspaces/components/WorkspaceDrawer.js
+++ b/src/features/workspaces/components/WorkspaceDrawer.js
@@ -54,7 +54,8 @@ class WorkspaceDrawer extends Component {
54 getServicesForWorkspace, 54 getServicesForWorkspace,
55 } = this.props; 55 } = this.props;
56 const { intl } = this.context; 56 const { intl } = this.context;
57 57 const { activeWorkspace, isSwitchingWorkspace, nextWorkspace } = workspacesState;
58 const actualWorkspace = isSwitchingWorkspace ? nextWorkspace : activeWorkspace;
58 return ( 59 return (
59 <div className={classes.drawer}> 60 <div className={classes.drawer}>
60 <H1 className={classes.headline}> 61 <H1 className={classes.headline}>
@@ -74,13 +75,13 @@ class WorkspaceDrawer extends Component {
74 name={intl.formatMessage(messages.allServices)} 75 name={intl.formatMessage(messages.allServices)}
75 onClick={() => workspaceActions.deactivate()} 76 onClick={() => workspaceActions.deactivate()}
76 services={getServicesForWorkspace(null)} 77 services={getServicesForWorkspace(null)}
77 isActive={workspacesState.activeWorkspace == null} 78 isActive={actualWorkspace == null}
78 /> 79 />
79 {workspacesState.workspaces.map(workspace => ( 80 {workspacesState.workspaces.map(workspace => (
80 <WorkspaceDrawerItem 81 <WorkspaceDrawerItem
81 key={workspace.id} 82 key={workspace.id}
82 name={workspace.name} 83 name={workspace.name}
83 isActive={workspacesState.activeWorkspace === workspace} 84 isActive={actualWorkspace === workspace}
84 onClick={() => workspaceActions.activate({ workspace })} 85 onClick={() => workspaceActions.activate({ workspace })}
85 services={getServicesForWorkspace(workspace)} 86 services={getServicesForWorkspace(workspace)}
86 /> 87 />
diff --git a/src/features/workspaces/components/WorkspaceSwitchingIndicator.js b/src/features/workspaces/components/WorkspaceSwitchingIndicator.js
new file mode 100644
index 000000000..4a279afaf
--- /dev/null
+++ b/src/features/workspaces/components/WorkspaceSwitchingIndicator.js
@@ -0,0 +1,56 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { observer } from 'mobx-react';
4import injectSheet from 'react-jss';
5import { workspacesState } from '../state';
6import LoaderComponent from '../../../components/ui/Loader';
7
8const styles = () => ({
9 wrapper: {
10 display: 'flex',
11 alignItems: 'flex-start',
12 position: 'absolute',
13 width: '100%',
14 marginTop: '20px',
15 },
16 component: {
17 background: 'rgba(20, 20, 20, 0.4)',
18 padding: 20,
19 width: 'auto',
20 height: 'auto',
21 margin: [0, 'auto'],
22 borderRadius: 6,
23 alignItems: 'flex-start',
24 zIndex: 200,
25 },
26 name: {
27 fontSize: 35,
28 marginBottom: '10px',
29 },
30});
31
32@injectSheet(styles) @observer
33class WorkspaceSwitchingIndicator extends Component {
34 static propTypes = {
35 classes: PropTypes.object.isRequired,
36 };
37
38 render() {
39 const { classes } = this.props;
40 const { isSwitchingWorkspace, nextWorkspace } = workspacesState;
41 if (!isSwitchingWorkspace) return null;
42 const nextWorkspaceName = nextWorkspace ? nextWorkspace.name : 'All services';
43 return (
44 <div className={classes.wrapper}>
45 <div className={classes.component}>
46 <h1 className={classes.name}>
47 {`Switching to ${nextWorkspaceName}`}
48 </h1>
49 <LoaderComponent />
50 </div>
51 </div>
52 );
53 }
54}
55
56export default WorkspaceSwitchingIndicator;
diff --git a/src/features/workspaces/components/WorkspacesDashboard.js b/src/features/workspaces/components/WorkspacesDashboard.js
index 917807302..df35b3590 100644
--- a/src/features/workspaces/components/WorkspacesDashboard.js
+++ b/src/features/workspaces/components/WorkspacesDashboard.js
@@ -30,7 +30,7 @@ const styles = () => ({
30class WorkspacesDashboard extends Component { 30class WorkspacesDashboard extends Component {
31 static propTypes = { 31 static propTypes = {
32 classes: PropTypes.object.isRequired, 32 classes: PropTypes.object.isRequired,
33 isLoading: PropTypes.bool.isRequired, 33 isLoadingWorkspaces: PropTypes.bool.isRequired,
34 onCreateWorkspaceSubmit: PropTypes.func.isRequired, 34 onCreateWorkspaceSubmit: PropTypes.func.isRequired,
35 onWorkspaceClick: PropTypes.func.isRequired, 35 onWorkspaceClick: PropTypes.func.isRequired,
36 workspaces: MobxPropTypes.arrayOrObservableArray.isRequired, 36 workspaces: MobxPropTypes.arrayOrObservableArray.isRequired,
@@ -60,7 +60,7 @@ class WorkspacesDashboard extends Component {
60 <div className={classes.createForm}> 60 <div className={classes.createForm}>
61 <CreateWorkspaceForm onSubmit={onCreateWorkspaceSubmit} /> 61 <CreateWorkspaceForm onSubmit={onCreateWorkspaceSubmit} />
62 </div> 62 </div>
63 {isLoading ? ( 63 {isLoadingWorkspaces ? (
64 <Loader /> 64 <Loader />
65 ) : ( 65 ) : (
66 <table className="workspace-table"> 66 <table className="workspace-table">
diff --git a/src/features/workspaces/containers/WorkspacesScreen.js b/src/features/workspaces/containers/WorkspacesScreen.js
index 94e714255..99241210e 100644
--- a/src/features/workspaces/containers/WorkspacesScreen.js
+++ b/src/features/workspaces/containers/WorkspacesScreen.js
@@ -21,7 +21,7 @@ class WorkspacesScreen extends Component {
21 <ErrorBoundary> 21 <ErrorBoundary>
22 <WorkspacesDashboard 22 <WorkspacesDashboard
23 workspaces={workspacesState.workspaces} 23 workspaces={workspacesState.workspaces}
24 isLoading={workspacesState.isLoading} 24 isLoading={workspacesState.isLoadingWorkspaces}
25 onCreateWorkspaceSubmit={data => actions.workspaces.create(data)} 25 onCreateWorkspaceSubmit={data => actions.workspaces.create(data)}
26 onWorkspaceClick={w => actions.workspaces.edit({ workspace: w })} 26 onWorkspaceClick={w => actions.workspaces.edit({ workspace: w })}
27 /> 27 />
diff --git a/src/features/workspaces/index.js b/src/features/workspaces/index.js
index 26cadea64..1644c0e2f 100644
--- a/src/features/workspaces/index.js
+++ b/src/features/workspaces/index.js
@@ -8,10 +8,13 @@ const debug = require('debug')('Franz:feature:workspaces');
8let store = null; 8let store = null;
9 9
10export const filterServicesByActiveWorkspace = (services) => { 10export const filterServicesByActiveWorkspace = (services) => {
11 const { isFeatureActive, activeWorkspace } = workspacesState; 11 const {
12 if (isFeatureActive && activeWorkspace) { 12 activeWorkspace,
13 return services.filter(s => activeWorkspace.services.includes(s.id)); 13 isFeatureActive,
14 } 14 } = workspacesState;
15
16 if (!isFeatureActive) return services;
17 if (activeWorkspace) return services.filter(s => activeWorkspace.services.includes(s.id));
15 return services; 18 return services;
16}; 19};
17 20
diff --git a/src/features/workspaces/state.js b/src/features/workspaces/state.js
index 68a7d8d91..c916480c0 100644
--- a/src/features/workspaces/state.js
+++ b/src/features/workspaces/state.js
@@ -2,8 +2,10 @@ import { observable } from 'mobx';
2 2
3const defaultState = { 3const defaultState = {
4 activeWorkspace: null, 4 activeWorkspace: null,
5 isLoading: false, 5 nextWorkspace: null,
6 isLoadingWorkspaces: false,
6 isFeatureActive: false, 7 isFeatureActive: false,
8 isSwitchingWorkspace: false,
7 isWorkspaceDrawerOpen: false, 9 isWorkspaceDrawerOpen: false,
8 workspaces: [], 10 workspaces: [],
9 workspaceBeingEdited: null, 11 workspaceBeingEdited: null,
diff --git a/src/features/workspaces/store.js b/src/features/workspaces/store.js
index 1b57ba2da..f6b9b2ff4 100644
--- a/src/features/workspaces/store.js
+++ b/src/features/workspaces/store.js
@@ -31,7 +31,7 @@ export default class WorkspacesStore extends Store {
31 */ 31 */
32 reaction( 32 reaction(
33 () => this.allWorkspacesRequest.isExecuting, 33 () => this.allWorkspacesRequest.isExecuting,
34 isExecuting => this._setIsLoading(isExecuting), 34 isExecuting => this._setIsLoadingWorkspaces(isExecuting),
35 ); 35 );
36 /** 36 /**
37 * Update the state with the workspace to be edited when route matches. 37 * Update the state with the workspace to be edited when route matches.
@@ -66,8 +66,8 @@ export default class WorkspacesStore extends Store {
66 this.state.workspaces = workspaces.map(data => new Workspace(data)); 66 this.state.workspaces = workspaces.map(data => new Workspace(data));
67 }; 67 };
68 68
69 @action _setIsLoading = (isLoading) => { 69 @action _setIsLoadingWorkspaces = (isLoading) => {
70 this.state.isLoading = isLoading; 70 this.state.isLoadingWorkspaces = isLoading;
71 }; 71 };
72 72
73 @action _edit = ({ workspace }) => { 73 @action _edit = ({ workspace }) => {
@@ -107,11 +107,26 @@ export default class WorkspacesStore extends Store {
107 }; 107 };
108 108
109 @action _setActiveWorkspace = ({ workspace }) => { 109 @action _setActiveWorkspace = ({ workspace }) => {
110 this.state.activeWorkspace = workspace; 110 Object.assign(this.state, {
111 isSwitchingWorkspace: true,
112 nextWorkspace: workspace,
113 });
114 setTimeout(() => { this.state.activeWorkspace = workspace; }, 100);
115 setTimeout(() => {
116 Object.assign(this.state, {
117 isSwitchingWorkspace: false,
118 nextWorkspace: null,
119 });
120 }, 1000);
111 }; 121 };
112 122
113 @action _deactivateActiveWorkspace = () => { 123 @action _deactivateActiveWorkspace = () => {
114 this.state.activeWorkspace = null; 124 Object.assign(this.state, {
125 isSwitchingWorkspace: true,
126 nextWorkspace: null,
127 });
128 setTimeout(() => { this.state.activeWorkspace = null; }, 100);
129 setTimeout(() => { this.state.isSwitchingWorkspace = false; }, 1000);
115 }; 130 };
116 131
117 @action _toggleWorkspaceDrawer = () => { 132 @action _toggleWorkspaceDrawer = () => {
diff --git a/src/i18n/messages/src/components/layout/AppLayout.json b/src/i18n/messages/src/components/layout/AppLayout.json
index 85d3e8696..4dd354afc 100644
--- a/src/i18n/messages/src/components/layout/AppLayout.json
+++ b/src/i18n/messages/src/components/layout/AppLayout.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Your services have been updated.", 4 "defaultMessage": "!!!Your services have been updated.",
5 "file": "src/components/layout/AppLayout.js", 5 "file": "src/components/layout/AppLayout.js",
6 "start": { 6 "start": {
7 "line": 24, 7 "line": 26,
8 "column": 19 8 "column": 19
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 27, 11 "line": 29,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,11 @@
17 "defaultMessage": "!!!A new update for Franz is available.", 17 "defaultMessage": "!!!A new update for Franz is available.",
18 "file": "src/components/layout/AppLayout.js", 18 "file": "src/components/layout/AppLayout.js",
19 "start": { 19 "start": {
20 "line": 28, 20 "line": 30,
21 "column": 19 21 "column": 19
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 31, 24 "line": 33,
25 "column": 3 25 "column": 3
26 } 26 }
27 }, 27 },
@@ -30,11 +30,11 @@
30 "defaultMessage": "!!!Reload services", 30 "defaultMessage": "!!!Reload services",
31 "file": "src/components/layout/AppLayout.js", 31 "file": "src/components/layout/AppLayout.js",
32 "start": { 32 "start": {
33 "line": 32, 33 "line": 34,
34 "column": 24 34 "column": 24
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 35, 37 "line": 37,
38 "column": 3 38 "column": 3
39 } 39 }
40 }, 40 },
@@ -43,11 +43,11 @@
43 "defaultMessage": "!!!Changelog", 43 "defaultMessage": "!!!Changelog",
44 "file": "src/components/layout/AppLayout.js", 44 "file": "src/components/layout/AppLayout.js",
45 "start": { 45 "start": {
46 "line": 36, 46 "line": 38,
47 "column": 13 47 "column": 13
48 }, 48 },
49 "end": { 49 "end": {
50 "line": 39, 50 "line": 41,
51 "column": 3 51 "column": 3
52 } 52 }
53 }, 53 },
@@ -56,11 +56,11 @@
56 "defaultMessage": "!!!Restart & install update", 56 "defaultMessage": "!!!Restart & install update",
57 "file": "src/components/layout/AppLayout.js", 57 "file": "src/components/layout/AppLayout.js",
58 "start": { 58 "start": {
59 "line": 40, 59 "line": 42,
60 "column": 23 60 "column": 23
61 }, 61 },
62 "end": { 62 "end": {
63 "line": 43, 63 "line": 45,
64 "column": 3 64 "column": 3
65 } 65 }
66 }, 66 },
@@ -69,11 +69,11 @@
69 "defaultMessage": "!!!Could not load services and user information", 69 "defaultMessage": "!!!Could not load services and user information",
70 "file": "src/components/layout/AppLayout.js", 70 "file": "src/components/layout/AppLayout.js",
71 "start": { 71 "start": {
72 "line": 44, 72 "line": 46,
73 "column": 26 73 "column": 26
74 }, 74 },
75 "end": { 75 "end": {
76 "line": 47, 76 "line": 49,
77 "column": 3 77 "column": 3
78 } 78 }
79 } 79 }
diff --git a/src/styles/layout.scss b/src/styles/layout.scss
index 78e9e68f1..e858b7904 100644
--- a/src/styles/layout.scss
+++ b/src/styles/layout.scss
@@ -39,6 +39,7 @@ html { overflow: hidden; }
39 .app__content { display: flex; } 39 .app__content { display: flex; }
40 40
41 .app__service { 41 .app__service {
42 position: relative;
42 display: flex; 43 display: flex;
43 flex: 1; 44 flex: 1;
44 flex-direction: column; 45 flex-direction: column;