From 912bca432ebf005a1680e9cc28bf7ee92cb0d36b Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Thu, 10 Jan 2019 13:43:45 +0100 Subject: add workspaces menu item in settings dialog --- src/components/settings/navigation/SettingsNavigation.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/components/settings') diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js index 953f702f8..4a80bb126 100644 --- a/src/components/settings/navigation/SettingsNavigation.js +++ b/src/components/settings/navigation/SettingsNavigation.js @@ -14,6 +14,10 @@ const messages = defineMessages({ id: 'settings.navigation.yourServices', defaultMessage: '!!!Your services', }, + yourWorkspaces: { + id: 'settings.navigation.yourWorkspaces', + defaultMessage: '!!!Your workspaces', + }, account: { id: 'settings.navigation.account', defaultMessage: '!!!Account', @@ -63,6 +67,14 @@ export default @inject('stores') @observer class SettingsNavigation extends Comp {' '} {serviceCount} + + {intl.formatMessage(messages.yourWorkspaces)} + {' '} + Date: Mon, 14 Jan 2019 17:11:27 +0100 Subject: basic setup of workspaces settings screen --- src/api/utils/auth.js | 24 ++++++++++ src/app.js | 2 + .../settings/workspaces/WorkspaceItem.js | 41 ++++++++++++++++ .../settings/workspaces/WorkspacesDashboard.js | 56 ++++++++++++++++++++++ src/containers/settings/WorkspacesScreen.js | 29 +++++++++++ src/environment.js | 1 + src/features/workspaces/api.js | 16 ++++--- src/features/workspaces/index.js | 10 ++-- src/features/workspaces/state.js | 12 +++++ src/features/workspaces/store.js | 15 ++++-- src/i18n/locales/en-US.json | 1 + src/models/Workspace.js | 25 ++++++++++ 12 files changed, 216 insertions(+), 16 deletions(-) create mode 100644 src/api/utils/auth.js create mode 100644 src/components/settings/workspaces/WorkspaceItem.js create mode 100644 src/components/settings/workspaces/WorkspacesDashboard.js create mode 100644 src/containers/settings/WorkspacesScreen.js create mode 100644 src/features/workspaces/state.js create mode 100644 src/models/Workspace.js (limited to 'src/components/settings') 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 @@ +import { remote } from 'electron'; +import localStorage from 'mobx-localstorage'; + +const { app } = remote; + +export const prepareAuthRequest = (options, auth = true) => { + const request = Object.assign(options, { + mode: 'cors', + headers: Object.assign({ + 'Content-Type': 'application/json', + 'X-Franz-Source': 'desktop', + 'X-Franz-Version': app.getVersion(), + 'X-Franz-platform': process.platform, + 'X-Franz-Timezone-Offset': new Date().getTimezoneOffset(), + 'X-Franz-System-Locale': app.getLocale(), + }, options.headers), + }); + + if (auth) { + request.headers.Authorization = `Bearer ${localStorage.getItem('authToken')}`; + } + + return request; +}; 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'; import InviteScreen from './containers/auth/InviteScreen'; import AuthLayoutContainer from './containers/auth/AuthLayoutContainer'; import SubscriptionPopupScreen from './containers/subscription/SubscriptionPopupScreen'; +import WorkspacesScreen from './containers/settings/WorkspacesScreen'; // Add Polyfills smoothScroll.polyfill(); @@ -75,6 +76,7 @@ window.addEventListener('load', () => { + 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 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { defineMessages, intlShape } from 'react-intl'; +import { observer } from 'mobx-react'; +import classnames from 'classnames'; +import Workspace from '../../../models/Workspace'; + +// const messages = defineMessages({}); + +@observer +class WorkspaceItem extends Component { + static propTypes = { + workspace: PropTypes.instanceOf(Workspace).isRequired, + }; + + static contextTypes = { + intl: intlShape, + }; + + render() { + const { workspace } = this.props; + // const { intl } = this.context; + + return ( + + console.log('go to workspace', workspace.name)} + > + {workspace.name} + + + ); + } +} + +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 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; +import { defineMessages, intlShape } from 'react-intl'; + +import Loader from '../../ui/Loader'; +import WorkspaceItem from './WorkspaceItem'; + +const messages = defineMessages({ + headline: { + id: 'settings.workspaces.headline', + defaultMessage: '!!!Your workspaces', + }, + noServicesAdded: { + id: 'settings.workspaces.noWorkspacesAdded', + defaultMessage: '!!!You haven\'t added any workspaces yet.', + }, +}); + +@observer +class WorkspacesDashboard extends Component { + static propTypes = { + workspaces: MobxPropTypes.arrayOrObservableArray.isRequired, + isLoading: PropTypes.bool.isRequired, + }; + + static contextTypes = { + intl: intlShape, + }; + + render() { + const { workspaces, isLoading } = this.props; + const { intl } = this.context; + + return ( +
+
+

{intl.formatMessage(messages.headline)}

+
+
+ {isLoading ? ( + + ) : ( + + + {workspaces.map(workspace => )} + +
+ )} +
+
+ ); + } +} + +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 @@ +import React, { Component } from 'react'; +import { observer } from 'mobx-react'; +import { gaPage } from '../../lib/analytics'; +import { state } from '../../features/workspaces/state'; + +import WorkspacesDashboard from '../../components/settings/workspaces/WorkspacesDashboard'; +import ErrorBoundary from '../../components/util/ErrorBoundary'; + +@observer +class WorkspacesScreen extends Component { + static propTypes = {}; + + componentDidMount() { + gaPage('Settings/Workspaces Dashboard'); + } + + render() { + return ( + + + + ); + } +} + +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)) { } export const API = api; +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 @@ -// TODO: use real server instead -const workspaces = [ - { id: 'workspace-1', name: 'Private' }, - { id: 'workspace-2', name: 'Office' }, -]; +import { prepareAuthRequest } from '../../api/utils/auth'; +import { API, API_VERSION } from '../../environment'; export default { - getUserWorkspaces: () => Promise.resolve(workspaces), + getUserWorkspaces: async () => { + const url = `${API}/${API_VERSION}/workspace`; + const request = await window.fetch(url, prepareAuthRequest({ + method: 'GET', + })); + if (!request.ok) throw request; + return request.json(); + }, }; 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 @@ -import { observable, reaction } from 'mobx'; -import { merge } from 'lodash'; +import { reaction } from 'mobx'; import WorkspacesStore from './store'; import api from './api'; +import { state, resetState } from './state'; const debug = require('debug')('Franz:feature:workspaces'); let store = null; -const defaultState = { workspaces: [] }; - -export const state = observable(defaultState); export default function initWorkspaces(stores, actions) { const { features, user } = stores; @@ -27,8 +24,7 @@ export default function initWorkspaces(stores, actions) { debug('Disabling `workspaces` feature'); store.teardown(); store = null; - // Reset state to default - merge(state, defaultState); + resetState(); // Reset state to default } }, { 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 @@ +import { observable } from 'mobx'; + +const defaultState = { + isLoading: false, + workspaces: [], +}; + +export const state = observable(defaultState); + +export function resetState() { + Object.assign(state, defaultState); +} 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 @@ import { observable, reaction } from 'mobx'; import Store from '../../stores/lib/Store'; import CachedRequest from '../../stores/lib/CachedRequest'; +import Workspace from '../../models/Workspace'; const debug = require('debug')('Franz:feature:workspaces'); @@ -18,12 +19,20 @@ export default class WorkspacesStore extends Store { reaction( () => this.allWorkspacesRequest.result, - workspaces => this.setWorkspaces(workspaces), + workspaces => this._setWorkspaces(workspaces), + ); + reaction( + () => this.allWorkspacesRequest.isExecuting, + isExecuting => this._setIsLoading(isExecuting), ); } - setWorkspaces = (workspaces) => { + _setWorkspaces = (workspaces) => { debug('setting user workspaces', workspaces.slice()); - this.state.workspaces = workspaces; + this.state.workspaces = workspaces.map(data => new Workspace(data)); + }; + + _setIsLoading = (isLoading) => { + this.state.isLoading = isLoading; }; } 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 @@ "settings.user.form.accountType.individual": "Individual", "settings.user.form.accountType.non-profit": "Non-Profit", "settings.user.form.accountType.company": "Company", + "settings.workspaces.headline": "Your workspaces", "subscription.type.free": "free", "subscription.type.month": "month", "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 @@ +import { observable } from 'mobx'; + +export default class Workspace { + id = null; + + @observable name = null; + + @observable order = null; + + @observable services = []; + + @observable userId = null; + + constructor(data) { + if (!data.id) { + throw Error('Workspace requires Id'); + } + + this.id = data.id; + this.name = data.name; + this.order = data.order; + this.services = data.services; + this.userId = data.userId; + } +} -- cgit v1.2.3-54-g00ecf From e3a9a93581c52e93ad984c39e80bf3e456797a0b Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Mon, 14 Jan 2019 17:12:22 +0100 Subject: fix eslint error --- src/components/settings/workspaces/WorkspaceItem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/components/settings') diff --git a/src/components/settings/workspaces/WorkspaceItem.js b/src/components/settings/workspaces/WorkspaceItem.js index 82a578ebd..49d67d429 100644 --- a/src/components/settings/workspaces/WorkspaceItem.js +++ b/src/components/settings/workspaces/WorkspaceItem.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { defineMessages, intlShape } from 'react-intl'; +import { intlShape } from 'react-intl'; import { observer } from 'mobx-react'; import classnames from 'classnames'; import Workspace from '../../../models/Workspace'; -- cgit v1.2.3-54-g00ecf From 73fcc05f34d5e707df5abbc744f7fe3313efb226 Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Mon, 14 Jan 2019 17:56:03 +0100 Subject: assign react key prop to workspace items --- src/components/settings/workspaces/WorkspacesDashboard.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'src/components/settings') diff --git a/src/components/settings/workspaces/WorkspacesDashboard.js b/src/components/settings/workspaces/WorkspacesDashboard.js index 830f32f08..c6378ac1e 100644 --- a/src/components/settings/workspaces/WorkspacesDashboard.js +++ b/src/components/settings/workspaces/WorkspacesDashboard.js @@ -43,7 +43,12 @@ class WorkspacesDashboard extends Component { ) : ( - {workspaces.map(workspace => )} + {workspaces.map(workspace => ( + + ))}
)} -- cgit v1.2.3-54-g00ecf From 981679154096517037c6732c9ecca1fe89733d00 Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Mon, 14 Jan 2019 17:56:19 +0100 Subject: add styles for workspace table --- .../settings/workspaces/WorkspaceItem.js | 4 +- .../settings/workspaces/WorkspacesDashboard.js | 2 +- src/styles/main.scss | 1 + src/styles/workspace-table.scss | 53 ++++++++++++++++++++++ 4 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 src/styles/workspace-table.scss (limited to 'src/components/settings') diff --git a/src/components/settings/workspaces/WorkspaceItem.js b/src/components/settings/workspaces/WorkspaceItem.js index 49d67d429..a71e6583d 100644 --- a/src/components/settings/workspaces/WorkspaceItem.js +++ b/src/components/settings/workspaces/WorkspaceItem.js @@ -24,11 +24,11 @@ class WorkspaceItem extends Component { return ( console.log('go to workspace', workspace.name)} > {workspace.name} diff --git a/src/components/settings/workspaces/WorkspacesDashboard.js b/src/components/settings/workspaces/WorkspacesDashboard.js index c6378ac1e..b286adc68 100644 --- a/src/components/settings/workspaces/WorkspacesDashboard.js +++ b/src/components/settings/workspaces/WorkspacesDashboard.js @@ -41,7 +41,7 @@ class WorkspacesDashboard extends Component { {isLoading ? ( ) : ( - +
{workspaces.map(workspace => ( Date: Mon, 14 Jan 2019 19:01:46 +0100 Subject: setup logic to display workspace edit page --- src/actions/index.js | 2 + src/actions/workspace.js | 8 ++++ src/app.js | 2 + .../settings/workspaces/WorkspaceItem.js | 5 +- .../settings/workspaces/WorkspacesDashboard.js | 4 +- src/containers/settings/EditWorkspaceScreen.js | 54 ++++++++++++++++++++++ src/containers/settings/WorkspacesScreen.js | 16 +++++-- src/features/workspaces/state.js | 1 + src/features/workspaces/store.js | 32 ++++++++++++- 9 files changed, 116 insertions(+), 8 deletions(-) create mode 100644 src/actions/workspace.js create mode 100644 src/containers/settings/EditWorkspaceScreen.js (limited to 'src/components/settings') diff --git a/src/actions/index.js b/src/actions/index.js index 59acabb0b..a406af50a 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -11,6 +11,7 @@ import payment from './payment'; import news from './news'; import settings from './settings'; import requests from './requests'; +import workspace from './workspace'; const actions = Object.assign({}, { service, @@ -23,6 +24,7 @@ const actions = Object.assign({}, { news, settings, requests, + workspace, }); export default defineActions(actions, PropTypes.checkPropTypes); diff --git a/src/actions/workspace.js b/src/actions/workspace.js new file mode 100644 index 000000000..ab07a96c0 --- /dev/null +++ b/src/actions/workspace.js @@ -0,0 +1,8 @@ +import PropTypes from 'prop-types'; +import Workspace from '../models/Workspace'; + +export default { + edit: { + workspace: PropTypes.instanceOf(Workspace).isRequired, + }, +}; diff --git a/src/app.js b/src/app.js index ee1b12939..320ba679f 100644 --- a/src/app.js +++ b/src/app.js @@ -40,6 +40,7 @@ import InviteScreen from './containers/auth/InviteScreen'; import AuthLayoutContainer from './containers/auth/AuthLayoutContainer'; import SubscriptionPopupScreen from './containers/subscription/SubscriptionPopupScreen'; import WorkspacesScreen from './containers/settings/WorkspacesScreen'; +import EditWorkspaceScreen from './containers/settings/EditWorkspaceScreen'; // Add Polyfills smoothScroll.polyfill(); @@ -77,6 +78,7 @@ window.addEventListener('load', () => { + diff --git a/src/components/settings/workspaces/WorkspaceItem.js b/src/components/settings/workspaces/WorkspaceItem.js index a71e6583d..088d61433 100644 --- a/src/components/settings/workspaces/WorkspaceItem.js +++ b/src/components/settings/workspaces/WorkspaceItem.js @@ -11,6 +11,7 @@ import Workspace from '../../../models/Workspace'; class WorkspaceItem extends Component { static propTypes = { workspace: PropTypes.instanceOf(Workspace).isRequired, + onItemClick: PropTypes.func.isRequired, }; static contextTypes = { @@ -18,7 +19,7 @@ class WorkspaceItem extends Component { }; render() { - const { workspace } = this.props; + const { workspace, onItemClick } = this.props; // const { intl } = this.context; return ( @@ -29,7 +30,7 @@ class WorkspaceItem extends Component { > diff --git a/src/components/settings/workspaces/WorkspacesDashboard.js b/src/components/settings/workspaces/WorkspacesDashboard.js index b286adc68..a5bb18cb7 100644 --- a/src/components/settings/workspaces/WorkspacesDashboard.js +++ b/src/components/settings/workspaces/WorkspacesDashboard.js @@ -22,6 +22,7 @@ class WorkspacesDashboard extends Component { static propTypes = { workspaces: MobxPropTypes.arrayOrObservableArray.isRequired, isLoading: PropTypes.bool.isRequired, + onWorkspaceClick: PropTypes.func.isRequired, }; static contextTypes = { @@ -29,7 +30,7 @@ class WorkspacesDashboard extends Component { }; render() { - const { workspaces, isLoading } = this.props; + const { workspaces, isLoading, onWorkspaceClick } = this.props; const { intl } = this.context; return ( @@ -47,6 +48,7 @@ class WorkspacesDashboard extends Component { onWorkspaceClick(w)} /> ))} diff --git a/src/containers/settings/EditWorkspaceScreen.js b/src/containers/settings/EditWorkspaceScreen.js new file mode 100644 index 000000000..665b405bd --- /dev/null +++ b/src/containers/settings/EditWorkspaceScreen.js @@ -0,0 +1,54 @@ +import React, { Component } from 'react'; +import { inject, observer } from 'mobx-react'; +import { defineMessages, intlShape } from 'react-intl'; +import Form from '../../lib/Form'; +import ErrorBoundary from '../../components/util/ErrorBoundary'; +import { gaPage } from '../../lib/analytics'; +import { state } from '../../features/workspaces/state'; + +const messages = defineMessages({ + name: { + id: 'settings.workspace.form.name', + defaultMessage: '!!!Name', + }, +}); + +@inject('stores', 'actions') @observer +class EditWorkspaceScreen extends Component { + static contextTypes = { + intl: intlShape, + }; + + componentDidMount() { + gaPage('Settings/Workspace/Edit'); + } + + prepareForm(workspace) { + const { intl } = this.context; + const config = { + fields: { + name: { + label: intl.formatMessage(messages.name), + placeholder: intl.formatMessage(messages.name), + value: workspace.name, + }, + }, + }; + return new Form(config); + } + + render() { + const { workspaceBeingEdited } = state; + if (!workspaceBeingEdited) return null; + + // const form = this.prepareForm(workspaceBeingEdited); + + return ( + +
{workspaceBeingEdited.name}
+
+ ); + } +} + +export default EditWorkspaceScreen; diff --git a/src/containers/settings/WorkspacesScreen.js b/src/containers/settings/WorkspacesScreen.js index e767fdfbe..5e91f7673 100644 --- a/src/containers/settings/WorkspacesScreen.js +++ b/src/containers/settings/WorkspacesScreen.js @@ -1,25 +1,33 @@ import React, { Component } from 'react'; -import { observer } from 'mobx-react'; +import { inject, observer } from 'mobx-react'; +import PropTypes from 'prop-types'; import { gaPage } from '../../lib/analytics'; import { state } from '../../features/workspaces/state'; - import WorkspacesDashboard from '../../components/settings/workspaces/WorkspacesDashboard'; import ErrorBoundary from '../../components/util/ErrorBoundary'; -@observer +@inject('actions') @observer class WorkspacesScreen extends Component { - static propTypes = {}; + static propTypes = { + actions: PropTypes.shape({ + workspace: PropTypes.shape({ + edit: PropTypes.func.isRequired, + }), + }).isRequired, + }; componentDidMount() { gaPage('Settings/Workspaces Dashboard'); } render() { + const { workspace } = this.props.actions; return ( workspace.edit({ workspace: w })} /> ); diff --git a/src/features/workspaces/state.js b/src/features/workspaces/state.js index ed3fe9f00..f938c1470 100644 --- a/src/features/workspaces/state.js +++ b/src/features/workspaces/state.js @@ -3,6 +3,7 @@ import { observable } from 'mobx'; const defaultState = { isLoading: false, workspaces: [], + workspaceBeingEdited: null, }; export const state = observable(defaultState); diff --git a/src/features/workspaces/store.js b/src/features/workspaces/store.js index 2b6d55cc7..aab66708b 100644 --- a/src/features/workspaces/store.js +++ b/src/features/workspaces/store.js @@ -2,6 +2,7 @@ import { observable, reaction } from 'mobx'; import Store from '../../stores/lib/Store'; import CachedRequest from '../../stores/lib/CachedRequest'; import Workspace from '../../models/Workspace'; +import { matchRoute } from '../../helpers/routing-helpers'; const debug = require('debug')('Franz:feature:workspaces'); @@ -14,17 +15,40 @@ export default class WorkspacesStore extends Store { } setup() { - debug('fetching user workspaces'); + debug('fetching workspaces'); this.allWorkspacesRequest.execute(); + /** + * Update the state workspaces array when workspaces request has results. + */ reaction( () => this.allWorkspacesRequest.result, workspaces => this._setWorkspaces(workspaces), ); + /** + * Update the loading state when workspace request is executing. + */ reaction( () => this.allWorkspacesRequest.isExecuting, isExecuting => this._setIsLoading(isExecuting), ); + /** + * Update the state with the workspace to be edited when route matches. + */ + reaction( + () => ({ + pathname: this.stores.router.location.pathname, + workspaces: this.state.workspaces, + }), + ({ pathname }) => { + const match = matchRoute('/settings/workspaces/edit/:id', pathname); + if (match) { + this.state.workspaceBeingEdited = this._getWorkspaceById(match.id); + } + }, + ); + + this.actions.workspace.edit.listen(this._edit); } _setWorkspaces = (workspaces) => { @@ -35,4 +59,10 @@ export default class WorkspacesStore extends Store { _setIsLoading = (isLoading) => { this.state.isLoading = isLoading; }; + + _getWorkspaceById = id => this.state.workspaces.find(w => w.id === id); + + _edit = ({ workspace }) => { + this.stores.router.push(`/settings/workspaces/edit/${workspace.id}`); + } } -- cgit v1.2.3-54-g00ecf From 90399cc608b93cc185b0ee1c9b79e98cfafb8bc1 Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Tue, 12 Feb 2019 14:59:58 +0100 Subject: consolidate workspace feature for further development --- src/actions/index.js | 2 +- src/actions/workspace.js | 8 --- src/app.js | 4 +- .../settings/workspaces/WorkspaceItem.js | 42 ------------ .../settings/workspaces/WorkspacesDashboard.js | 63 ------------------ src/containers/settings/EditWorkspaceScreen.js | 54 ---------------- src/containers/settings/WorkspacesScreen.js | 37 ----------- src/features/workspaces/actions.js | 8 +++ .../workspaces/components/WorkspaceItem.js | 42 ++++++++++++ .../workspaces/components/WorkspacesDashboard.js | 63 ++++++++++++++++++ .../workspaces/containers/EditWorkspaceScreen.js | 75 ++++++++++++++++++++++ .../workspaces/containers/WorkspacesScreen.js | 37 +++++++++++ src/features/workspaces/models/Workspace.js | 25 ++++++++ src/features/workspaces/store.js | 2 +- .../workspaces/styles/workspaces-table.scss | 53 +++++++++++++++ src/i18n/locales/de.json | 3 + src/i18n/locales/en-US.json | 1 + src/models/Workspace.js | 25 -------- src/styles/main.scss | 2 +- src/styles/workspace-table.scss | 53 --------------- 20 files changed, 312 insertions(+), 287 deletions(-) delete mode 100644 src/actions/workspace.js delete mode 100644 src/components/settings/workspaces/WorkspaceItem.js delete mode 100644 src/components/settings/workspaces/WorkspacesDashboard.js delete mode 100644 src/containers/settings/EditWorkspaceScreen.js delete mode 100644 src/containers/settings/WorkspacesScreen.js create mode 100644 src/features/workspaces/actions.js create mode 100644 src/features/workspaces/components/WorkspaceItem.js create mode 100644 src/features/workspaces/components/WorkspacesDashboard.js create mode 100644 src/features/workspaces/containers/EditWorkspaceScreen.js create mode 100644 src/features/workspaces/containers/WorkspacesScreen.js create mode 100644 src/features/workspaces/models/Workspace.js create mode 100644 src/features/workspaces/styles/workspaces-table.scss delete mode 100644 src/models/Workspace.js delete mode 100644 src/styles/workspace-table.scss (limited to 'src/components/settings') diff --git a/src/actions/index.js b/src/actions/index.js index a406af50a..45e6da515 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -11,7 +11,7 @@ import payment from './payment'; import news from './news'; import settings from './settings'; import requests from './requests'; -import workspace from './workspace'; +import workspace from '../features/workspaces/actions'; const actions = Object.assign({}, { service, diff --git a/src/actions/workspace.js b/src/actions/workspace.js deleted file mode 100644 index ab07a96c0..000000000 --- a/src/actions/workspace.js +++ /dev/null @@ -1,8 +0,0 @@ -import PropTypes from 'prop-types'; -import Workspace from '../models/Workspace'; - -export default { - edit: { - workspace: PropTypes.instanceOf(Workspace).isRequired, - }, -}; diff --git a/src/app.js b/src/app.js index 320ba679f..d3b540f62 100644 --- a/src/app.js +++ b/src/app.js @@ -39,8 +39,8 @@ import PricingScreen from './containers/auth/PricingScreen'; import InviteScreen from './containers/auth/InviteScreen'; import AuthLayoutContainer from './containers/auth/AuthLayoutContainer'; import SubscriptionPopupScreen from './containers/subscription/SubscriptionPopupScreen'; -import WorkspacesScreen from './containers/settings/WorkspacesScreen'; -import EditWorkspaceScreen from './containers/settings/EditWorkspaceScreen'; +import WorkspacesScreen from './features/workspaces/containers/WorkspacesScreen'; +import EditWorkspaceScreen from './features/workspaces/containers/EditWorkspaceScreen'; // Add Polyfills smoothScroll.polyfill(); diff --git a/src/components/settings/workspaces/WorkspaceItem.js b/src/components/settings/workspaces/WorkspaceItem.js deleted file mode 100644 index 088d61433..000000000 --- a/src/components/settings/workspaces/WorkspaceItem.js +++ /dev/null @@ -1,42 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { intlShape } from 'react-intl'; -import { observer } from 'mobx-react'; -import classnames from 'classnames'; -import Workspace from '../../../models/Workspace'; - -// const messages = defineMessages({}); - -@observer -class WorkspaceItem extends Component { - static propTypes = { - workspace: PropTypes.instanceOf(Workspace).isRequired, - onItemClick: PropTypes.func.isRequired, - }; - - static contextTypes = { - intl: intlShape, - }; - - render() { - const { workspace, onItemClick } = this.props; - // const { intl } = this.context; - - return ( - - - - ); - } -} - -export default WorkspaceItem; diff --git a/src/components/settings/workspaces/WorkspacesDashboard.js b/src/components/settings/workspaces/WorkspacesDashboard.js deleted file mode 100644 index a5bb18cb7..000000000 --- a/src/components/settings/workspaces/WorkspacesDashboard.js +++ /dev/null @@ -1,63 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; -import { defineMessages, intlShape } from 'react-intl'; - -import Loader from '../../ui/Loader'; -import WorkspaceItem from './WorkspaceItem'; - -const messages = defineMessages({ - headline: { - id: 'settings.workspaces.headline', - defaultMessage: '!!!Your workspaces', - }, - noServicesAdded: { - id: 'settings.workspaces.noWorkspacesAdded', - defaultMessage: '!!!You haven\'t added any workspaces yet.', - }, -}); - -@observer -class WorkspacesDashboard extends Component { - static propTypes = { - workspaces: MobxPropTypes.arrayOrObservableArray.isRequired, - isLoading: PropTypes.bool.isRequired, - onWorkspaceClick: PropTypes.func.isRequired, - }; - - static contextTypes = { - intl: intlShape, - }; - - render() { - const { workspaces, isLoading, onWorkspaceClick } = this.props; - const { intl } = this.context; - - return ( -
-
-

{intl.formatMessage(messages.headline)}

-
-
- {isLoading ? ( - - ) : ( -
console.log('go to workspace', workspace.name)} + onClick={() => onItemClick(workspace)} > {workspace.name}
onItemClick(workspace)} - > - {workspace.name} -
- - {workspaces.map(workspace => ( - onWorkspaceClick(w)} - /> - ))} - -
- )} - - - ); - } -} - -export default WorkspacesDashboard; diff --git a/src/containers/settings/EditWorkspaceScreen.js b/src/containers/settings/EditWorkspaceScreen.js deleted file mode 100644 index 665b405bd..000000000 --- a/src/containers/settings/EditWorkspaceScreen.js +++ /dev/null @@ -1,54 +0,0 @@ -import React, { Component } from 'react'; -import { inject, observer } from 'mobx-react'; -import { defineMessages, intlShape } from 'react-intl'; -import Form from '../../lib/Form'; -import ErrorBoundary from '../../components/util/ErrorBoundary'; -import { gaPage } from '../../lib/analytics'; -import { state } from '../../features/workspaces/state'; - -const messages = defineMessages({ - name: { - id: 'settings.workspace.form.name', - defaultMessage: '!!!Name', - }, -}); - -@inject('stores', 'actions') @observer -class EditWorkspaceScreen extends Component { - static contextTypes = { - intl: intlShape, - }; - - componentDidMount() { - gaPage('Settings/Workspace/Edit'); - } - - prepareForm(workspace) { - const { intl } = this.context; - const config = { - fields: { - name: { - label: intl.formatMessage(messages.name), - placeholder: intl.formatMessage(messages.name), - value: workspace.name, - }, - }, - }; - return new Form(config); - } - - render() { - const { workspaceBeingEdited } = state; - if (!workspaceBeingEdited) return null; - - // const form = this.prepareForm(workspaceBeingEdited); - - return ( - -
{workspaceBeingEdited.name}
-
- ); - } -} - -export default EditWorkspaceScreen; diff --git a/src/containers/settings/WorkspacesScreen.js b/src/containers/settings/WorkspacesScreen.js deleted file mode 100644 index 5e91f7673..000000000 --- a/src/containers/settings/WorkspacesScreen.js +++ /dev/null @@ -1,37 +0,0 @@ -import React, { Component } from 'react'; -import { inject, observer } from 'mobx-react'; -import PropTypes from 'prop-types'; -import { gaPage } from '../../lib/analytics'; -import { state } from '../../features/workspaces/state'; -import WorkspacesDashboard from '../../components/settings/workspaces/WorkspacesDashboard'; -import ErrorBoundary from '../../components/util/ErrorBoundary'; - -@inject('actions') @observer -class WorkspacesScreen extends Component { - static propTypes = { - actions: PropTypes.shape({ - workspace: PropTypes.shape({ - edit: PropTypes.func.isRequired, - }), - }).isRequired, - }; - - componentDidMount() { - gaPage('Settings/Workspaces Dashboard'); - } - - render() { - const { workspace } = this.props.actions; - return ( - - workspace.edit({ workspace: w })} - /> - - ); - } -} - -export default WorkspacesScreen; diff --git a/src/features/workspaces/actions.js b/src/features/workspaces/actions.js new file mode 100644 index 000000000..30866af96 --- /dev/null +++ b/src/features/workspaces/actions.js @@ -0,0 +1,8 @@ +import PropTypes from 'prop-types'; +import Workspace from './models/Workspace'; + +export default { + edit: { + workspace: PropTypes.instanceOf(Workspace).isRequired, + }, +}; diff --git a/src/features/workspaces/components/WorkspaceItem.js b/src/features/workspaces/components/WorkspaceItem.js new file mode 100644 index 000000000..b2c2a4830 --- /dev/null +++ b/src/features/workspaces/components/WorkspaceItem.js @@ -0,0 +1,42 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { intlShape } from 'react-intl'; +import { observer } from 'mobx-react'; +import classnames from 'classnames'; +import Workspace from '../models/Workspace'; + +// const messages = defineMessages({}); + +@observer +class WorkspaceItem extends Component { + static propTypes = { + workspace: PropTypes.instanceOf(Workspace).isRequired, + onItemClick: PropTypes.func.isRequired, + }; + + static contextTypes = { + intl: intlShape, + }; + + render() { + const { workspace, onItemClick } = this.props; + // const { intl } = this.context; + + return ( + + onItemClick(workspace)} + > + {workspace.name} + + + ); + } +} + +export default WorkspaceItem; diff --git a/src/features/workspaces/components/WorkspacesDashboard.js b/src/features/workspaces/components/WorkspacesDashboard.js new file mode 100644 index 000000000..2a8b3a5ee --- /dev/null +++ b/src/features/workspaces/components/WorkspacesDashboard.js @@ -0,0 +1,63 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; +import { defineMessages, intlShape } from 'react-intl'; + +import Loader from '../../../components/ui/Loader'; +import WorkspaceItem from './WorkspaceItem'; + +const messages = defineMessages({ + headline: { + id: 'settings.workspaces.headline', + defaultMessage: '!!!Your workspaces', + }, + noServicesAdded: { + id: 'settings.workspaces.noWorkspacesAdded', + defaultMessage: '!!!You haven\'t added any workspaces yet.', + }, +}); + +@observer +class WorkspacesDashboard extends Component { + static propTypes = { + workspaces: MobxPropTypes.arrayOrObservableArray.isRequired, + isLoading: PropTypes.bool.isRequired, + onWorkspaceClick: PropTypes.func.isRequired, + }; + + static contextTypes = { + intl: intlShape, + }; + + render() { + const { workspaces, isLoading, onWorkspaceClick } = this.props; + const { intl } = this.context; + + return ( +
+
+

{intl.formatMessage(messages.headline)}

+
+
+ {isLoading ? ( + + ) : ( + + + {workspaces.map(workspace => ( + onWorkspaceClick(w)} + /> + ))} + +
+ )} +
+
+ ); + } +} + +export default WorkspacesDashboard; diff --git a/src/features/workspaces/containers/EditWorkspaceScreen.js b/src/features/workspaces/containers/EditWorkspaceScreen.js new file mode 100644 index 000000000..d8c52f586 --- /dev/null +++ b/src/features/workspaces/containers/EditWorkspaceScreen.js @@ -0,0 +1,75 @@ +import React, { Component } from 'react'; +import { inject, observer } from 'mobx-react'; +import { defineMessages, intlShape } from 'react-intl'; +import { Link } from 'react-router'; +import Form from '../../../lib/Form'; +import ErrorBoundary from '../../../components/util/ErrorBoundary'; +import { gaPage } from '../../../lib/analytics'; +import { state } from '../state'; + +const messages = defineMessages({ + name: { + id: 'settings.workspace.form.name', + defaultMessage: '!!!Name', + }, + yourWorkspaces: { + id: 'settings.workspace.form.yourWorkspaces', + defaultMessage: '!!!Your workspaces', + }, +}); + +@inject('stores', 'actions') @observer +class EditWorkspaceScreen extends Component { + static contextTypes = { + intl: intlShape, + }; + + componentDidMount() { + gaPage('Settings/Workspace/Edit'); + } + + prepareForm(workspace) { + const { intl } = this.context; + const config = { + fields: { + name: { + label: intl.formatMessage(messages.name), + placeholder: intl.formatMessage(messages.name), + value: workspace.name, + }, + }, + }; + return new Form(config); + } + + render() { + const { intl } = this.context; + const { workspaceBeingEdited } = state; + if (!workspaceBeingEdited) return null; + + // const form = this.prepareForm(workspaceBeingEdited); + + return ( + +
+
+ + + {intl.formatMessage(messages.yourWorkspaces)} + + + + + {workspaceBeingEdited.name} + +
+
+ test +
+
+
+ ); + } +} + +export default EditWorkspaceScreen; diff --git a/src/features/workspaces/containers/WorkspacesScreen.js b/src/features/workspaces/containers/WorkspacesScreen.js new file mode 100644 index 000000000..f129edec5 --- /dev/null +++ b/src/features/workspaces/containers/WorkspacesScreen.js @@ -0,0 +1,37 @@ +import React, { Component } from 'react'; +import { inject, observer } from 'mobx-react'; +import PropTypes from 'prop-types'; +import { gaPage } from '../../../lib/analytics'; +import { state } from '../state'; +import WorkspacesDashboard from '../components/WorkspacesDashboard'; +import ErrorBoundary from '../../../components/util/ErrorBoundary'; + +@inject('actions') @observer +class WorkspacesScreen extends Component { + static propTypes = { + actions: PropTypes.shape({ + workspace: PropTypes.shape({ + edit: PropTypes.func.isRequired, + }), + }).isRequired, + }; + + componentDidMount() { + gaPage('Settings/Workspaces Dashboard'); + } + + render() { + const { workspace } = this.props.actions; + return ( + + workspace.edit({ workspace: w })} + /> + + ); + } +} + +export default WorkspacesScreen; diff --git a/src/features/workspaces/models/Workspace.js b/src/features/workspaces/models/Workspace.js new file mode 100644 index 000000000..ede2710dc --- /dev/null +++ b/src/features/workspaces/models/Workspace.js @@ -0,0 +1,25 @@ +import { observable } from 'mobx'; + +export default class Workspace { + id = null; + + @observable name = null; + + @observable order = null; + + @observable services = []; + + @observable userId = null; + + constructor(data) { + if (!data.id) { + throw Error('Workspace requires Id'); + } + + this.id = data.id; + this.name = data.name; + this.order = data.order; + this.services = data.services; + this.userId = data.userId; + } +} diff --git a/src/features/workspaces/store.js b/src/features/workspaces/store.js index aab66708b..ea61cec31 100644 --- a/src/features/workspaces/store.js +++ b/src/features/workspaces/store.js @@ -1,7 +1,7 @@ import { observable, reaction } from 'mobx'; import Store from '../../stores/lib/Store'; import CachedRequest from '../../stores/lib/CachedRequest'; -import Workspace from '../../models/Workspace'; +import Workspace from './models/Workspace'; import { matchRoute } from '../../helpers/routing-helpers'; const debug = require('debug')('Franz:feature:workspaces'); diff --git a/src/features/workspaces/styles/workspaces-table.scss b/src/features/workspaces/styles/workspaces-table.scss new file mode 100644 index 000000000..6d0e7b4f5 --- /dev/null +++ b/src/features/workspaces/styles/workspaces-table.scss @@ -0,0 +1,53 @@ +@import '../../../styles/config'; + +.theme__dark .workspace-table { + .workspace-table__column-info .mdi { color: $dark-theme-gray-lightest; } + + .workspace-table__row { + border-bottom: 1px solid $dark-theme-gray-darker; + + &:hover { background: $dark-theme-gray-darker; } + &.workspace-table__row--disabled { color: $dark-theme-gray; } + } +} + +.workspace-table { + width: 100%; + + .workspace-table__toggle { + width: 60px; + + .franz-form__field { + margin-bottom: 0; + } + } + + .workspace-table__column-action { + width: 40px + } + + .workspace-table__column-info { + width: 40px; + + .mdi { + color: $theme-gray-light; + display: block; + font-size: 18px; + } + } + + .workspace-table__row { + border-bottom: 1px solid $theme-gray-lightest; + + &:hover { + cursor: initial; + background: $theme-gray-lightest; + } + + &.workspace-table__row--disabled { + color: $theme-gray-light; + } + } + + td { padding: 10px; } +} diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json index b5abb56d4..5cb8c6bd3 100644 --- a/src/i18n/locales/de.json +++ b/src/i18n/locales/de.json @@ -158,6 +158,7 @@ "settings.navigation.logout" : "Abmelden", "settings.navigation.settings" : "Einstellungen", "settings.navigation.yourServices" : "Deine Dienste", + "settings.navigation.yourWorkspaces": "Deine Workspaces", "settings.recipes.all" : "Alle Dienste", "settings.recipes.dev" : "Entwicklung", "settings.recipes.headline" : "Verfügbare Dienste", @@ -216,6 +217,8 @@ "settings.services.tooltip.isMuted" : "Alle Töne sind deaktiviert", "settings.services.tooltip.notificationsDisabled" : "Benachrichtigungen deaktiviert", "settings.services.updatedInfo" : "Deine Änderungen wurden gespeichert", + "settings.workspaces.headline": "Deine Workspaces", + "settings.workspace.form.yourWorkspaces": "Deine Workspaces", "settings.user.form.accountType.company" : "Firma", "settings.user.form.accountType.individual" : "Einzelperson", "settings.user.form.accountType.label" : "Konto-Typ", diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 1652b5585..9b323e323 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -197,6 +197,7 @@ "settings.user.form.accountType.non-profit": "Non-Profit", "settings.user.form.accountType.company": "Company", "settings.workspaces.headline": "Your workspaces", + "settings.workspace.form.yourWorkspaces": "Your workspaces", "subscription.type.free": "free", "subscription.type.month": "month", "subscription.type.year": "year", diff --git a/src/models/Workspace.js b/src/models/Workspace.js deleted file mode 100644 index ede2710dc..000000000 --- a/src/models/Workspace.js +++ /dev/null @@ -1,25 +0,0 @@ -import { observable } from 'mobx'; - -export default class Workspace { - id = null; - - @observable name = null; - - @observable order = null; - - @observable services = []; - - @observable userId = null; - - constructor(data) { - if (!data.id) { - throw Error('Workspace requires Id'); - } - - this.id = data.id; - this.name = data.name; - this.order = data.order; - this.services = data.services; - this.userId = data.userId; - } -} diff --git a/src/styles/main.scss b/src/styles/main.scss index 30f43532f..a941d89d0 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -30,7 +30,7 @@ $mdi-font-path: '../node_modules/mdi/fonts'; @import './content-tabs.scss'; @import './invite.scss'; @import './title-bar.scss'; -@import './workspace-table.scss'; +@import '../features/workspaces/styles/workspaces-table'; // form @import './input.scss'; diff --git a/src/styles/workspace-table.scss b/src/styles/workspace-table.scss deleted file mode 100644 index 05ebfa629..000000000 --- a/src/styles/workspace-table.scss +++ /dev/null @@ -1,53 +0,0 @@ -@import './config.scss'; - -.theme__dark .workspace-table { - .workspace-table__column-info .mdi { color: $dark-theme-gray-lightest; } - - .workspace-table__row { - border-bottom: 1px solid $dark-theme-gray-darker; - - &:hover { background: $dark-theme-gray-darker; } - &.workspace-table__row--disabled { color: $dark-theme-gray; } - } -} - -.workspace-table { - width: 100%; - - .workspace-table__toggle { - width: 60px; - - .franz-form__field { - margin-bottom: 0; - } - } - - .workspace-table__column-action { - width: 40px - } - - .workspace-table__column-info { - width: 40px; - - .mdi { - color: $theme-gray-light; - display: block; - font-size: 18px; - } - } - - .workspace-table__row { - border-bottom: 1px solid $theme-gray-lightest; - - &:hover { - cursor: initial; - background: $theme-gray-lightest; - } - - &.workspace-table__row--disabled { - color: $theme-gray-light; - } - } - - td { padding: 10px; } -} -- cgit v1.2.3-54-g00ecf From af55b33800eeed36b50b1967da694acbb3991ecb Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Thu, 21 Mar 2019 16:30:13 +0100 Subject: add workspaces count badge in settings navigation --- src/components/settings/navigation/SettingsNavigation.js | 4 +++- src/containers/settings/SettingsWindow.js | 2 ++ src/features/workspaces/components/WorkspaceDrawer.js | 1 - src/i18n/locales/defaultMessages.json | 12 ++++++------ src/i18n/locales/en-US.json | 2 +- .../src/features/workspaces/components/WorkspaceDrawer.json | 12 ++++++------ src/styles/settings.scss | 5 +++-- 7 files changed, 21 insertions(+), 17 deletions(-) (limited to 'src/components/settings') diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js index 4a80bb126..3f570f3b6 100644 --- a/src/components/settings/navigation/SettingsNavigation.js +++ b/src/components/settings/navigation/SettingsNavigation.js @@ -39,6 +39,7 @@ const messages = defineMessages({ export default @inject('stores') @observer class SettingsNavigation extends Component { static propTypes = { serviceCount: PropTypes.number.isRequired, + workspaceCount: PropTypes.number.isRequired, }; static contextTypes = { @@ -46,7 +47,7 @@ export default @inject('stores') @observer class SettingsNavigation extends Comp }; render() { - const { serviceCount } = this.props; + const { serviceCount, workspaceCount } = this.props; const { intl } = this.context; return ( @@ -74,6 +75,7 @@ export default @inject('stores') @observer class SettingsNavigation extends Comp > {intl.formatMessage(messages.yourWorkspaces)} {' '} + {workspaceCount} ); diff --git a/src/features/workspaces/components/WorkspaceDrawer.js b/src/features/workspaces/components/WorkspaceDrawer.js index 5a8d5d854..c18eb0e11 100644 --- a/src/features/workspaces/components/WorkspaceDrawer.js +++ b/src/features/workspaces/components/WorkspaceDrawer.js @@ -9,7 +9,6 @@ import ReactTooltip from 'react-tooltip'; import { workspacesState } from '../state'; import WorkspaceDrawerItem from './WorkspaceDrawerItem'; import { workspaceActions } from '../actions'; -import { ctrlKey } from '../../../environment'; const messages = defineMessages({ headline: { diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json index d4d2a55cc..480f5c6cb 100644 --- a/src/i18n/locales/defaultMessages.json +++ b/src/i18n/locales/defaultMessages.json @@ -3326,39 +3326,39 @@ "defaultMessage": "!!!Workspaces", "end": { "column": 3, - "line": 18 + "line": 17 }, "file": "src/features/workspaces/components/WorkspaceDrawer.js", "id": "workspaceDrawer.headline", "start": { "column": 12, - "line": 15 + "line": 14 } }, { "defaultMessage": "!!!All services", "end": { "column": 3, - "line": 22 + "line": 21 }, "file": "src/features/workspaces/components/WorkspaceDrawer.js", "id": "workspaceDrawer.allServices", "start": { "column": 15, - "line": 19 + "line": 18 } }, { "defaultMessage": "!!!Add workspace", "end": { "column": 3, - "line": 26 + "line": 25 }, "file": "src/features/workspaces/components/WorkspaceDrawer.js", "id": "workspaceDrawer.addWorkspaceTooltip", "start": { "column": 23, - "line": 23 + "line": 22 } } ], diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index f7b5fe2b8..4206d4358 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -305,4 +305,4 @@ "workspaceDrawer.headline": "Workspaces", "workspaceDrawer.item.noServicesAddedYet": "No services added yet", "workspaces.switchingIndicator.switchingTo": "Switching to" -} +} \ No newline at end of file diff --git a/src/i18n/messages/src/features/workspaces/components/WorkspaceDrawer.json b/src/i18n/messages/src/features/workspaces/components/WorkspaceDrawer.json index 7026708e2..4e3237164 100644 --- a/src/i18n/messages/src/features/workspaces/components/WorkspaceDrawer.json +++ b/src/i18n/messages/src/features/workspaces/components/WorkspaceDrawer.json @@ -4,11 +4,11 @@ "defaultMessage": "!!!Workspaces", "file": "src/features/workspaces/components/WorkspaceDrawer.js", "start": { - "line": 15, + "line": 14, "column": 12 }, "end": { - "line": 18, + "line": 17, "column": 3 } }, @@ -17,11 +17,11 @@ "defaultMessage": "!!!All services", "file": "src/features/workspaces/components/WorkspaceDrawer.js", "start": { - "line": 19, + "line": 18, "column": 15 }, "end": { - "line": 22, + "line": 21, "column": 3 } }, @@ -30,11 +30,11 @@ "defaultMessage": "!!!Add workspace", "file": "src/features/workspaces/components/WorkspaceDrawer.js", "start": { - "line": 23, + "line": 22, "column": 23 }, "end": { - "line": 26, + "line": 25, "column": 3 } } diff --git a/src/styles/settings.scss b/src/styles/settings.scss index 750b6bedd..9fde9a7bf 100644 --- a/src/styles/settings.scss +++ b/src/styles/settings.scss @@ -68,7 +68,7 @@ } } - .premium-info { + .premium-info { background: $dark-theme-gray-darker; border: 2px solid $theme-brand-primary; } @@ -414,6 +414,7 @@ .settings-navigation__link { align-items: center; + justify-content: space-between; color: $theme-text-color; display: flex; flex-shrink: 0; @@ -442,8 +443,8 @@ .settings-navigation__expander { flex: 1; } .badge { + display: initial; - margin-left: 5px; transition: background $theme-transition-time, color $theme-transition-time; } -- cgit v1.2.3-54-g00ecf From d05a8efffadd926165d516d6efd8c8b893648ebe Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Tue, 26 Mar 2019 13:47:54 +0100 Subject: hide workspace feature if it is disabled --- src/components/layout/Sidebar.js | 25 ++++++----- .../settings/navigation/SettingsNavigation.js | 21 +++++---- src/config.js | 2 +- src/features/workspaces/store.js | 5 +++ src/i18n/locales/defaultMessages.json | 52 +++++++++++----------- .../messages/src/components/layout/Sidebar.json | 24 +++++----- .../settings/navigation/SettingsNavigation.json | 28 ++++++------ src/lib/Menu.js | 6 ++- src/stores/FeaturesStore.js | 4 +- 9 files changed, 90 insertions(+), 77 deletions(-) (limited to 'src/components/settings') diff --git a/src/components/layout/Sidebar.js b/src/components/layout/Sidebar.js index de379875e..f7bacfe0f 100644 --- a/src/components/layout/Sidebar.js +++ b/src/components/layout/Sidebar.js @@ -6,6 +6,7 @@ import { observer } from 'mobx-react'; import Tabbar from '../services/tabs/Tabbar'; import { ctrlKey } from '../../environment'; +import { workspaceStore } from '../../features/workspaces'; const messages = defineMessages({ settings: { @@ -88,17 +89,19 @@ export default @observer class Sidebar extends Component { enableToolTip={() => this.enableToolTip()} disableToolTip={() => this.disableToolTip()} /> - + {workspaceStore.isFeatureActive ? ( + + ) : null} @@ -73,3 +88,5 @@ PremiumFeatureContainer.wrappedComponent.propTypes = { }).isRequired, }).isRequired, }; + +export default PremiumFeatureContainer; diff --git a/src/components/ui/PremiumFeatureContainer/styles.js b/src/components/ui/PremiumFeatureContainer/styles.js index 81d6666c6..615ed0a79 100644 --- a/src/components/ui/PremiumFeatureContainer/styles.js +++ b/src/components/ui/PremiumFeatureContainer/styles.js @@ -6,6 +6,7 @@ export default theme => ({ padding: 20, 'border-radius': theme.borderRadius, pointerEvents: 'none', + height: 'auto', }, titleContainer: { display: 'flex', @@ -26,7 +27,7 @@ export default theme => ({ content: { opacity: 0.5, 'margin-top': 20, - '& :last-child': { + '& > :last-child': { 'margin-bottom': 0, }, }, diff --git a/src/features/delayApp/index.js b/src/features/delayApp/index.js index abc8274cf..67f0fc5e6 100644 --- a/src/features/delayApp/index.js +++ b/src/features/delayApp/index.js @@ -55,7 +55,7 @@ export default function init(stores) { setVisibility(true); gaPage('/delayApp'); - gaEvent('delayApp', 'show', 'Delay App Feature'); + gaEvent('DelayApp', 'show', 'Delay App Feature'); timeLastDelay = moment(); shownAfterLaunch = true; diff --git a/src/features/utils/FeatureStore.js b/src/features/utils/FeatureStore.js new file mode 100644 index 000000000..66b66a104 --- /dev/null +++ b/src/features/utils/FeatureStore.js @@ -0,0 +1,21 @@ +import Reaction from '../../stores/lib/Reaction'; + +export class FeatureStore { + _actions = null; + + _reactions = null; + + _listenToActions(actions) { + if (this._actions) this._actions.forEach(a => a[0].off(a[1])); + this._actions = []; + actions.forEach(a => this._actions.push(a)); + this._actions.forEach(a => a[0].listen(a[1])); + } + + _startReactions(reactions) { + if (this._reactions) this._reactions.forEach(r => r.stop()); + this._reactions = []; + reactions.forEach(r => this._reactions.push(new Reaction(r))); + this._reactions.forEach(r => r.start()); + } +} diff --git a/src/features/workspaces/components/CreateWorkspaceForm.js b/src/features/workspaces/components/CreateWorkspaceForm.js index a8f07d0d5..0be2d528f 100644 --- a/src/features/workspaces/components/CreateWorkspaceForm.js +++ b/src/features/workspaces/components/CreateWorkspaceForm.js @@ -30,7 +30,6 @@ const styles = () => ({ }, submitButton: { height: 'inherit', - marginTop: '3px', }, }); diff --git a/src/features/workspaces/components/WorkspacesDashboard.js b/src/features/workspaces/components/WorkspacesDashboard.js index 52c3afdcf..1fad1f71d 100644 --- a/src/features/workspaces/components/WorkspacesDashboard.js +++ b/src/features/workspaces/components/WorkspacesDashboard.js @@ -10,6 +10,8 @@ import WorkspaceItem from './WorkspaceItem'; import CreateWorkspaceForm from './CreateWorkspaceForm'; import Request from '../../../stores/lib/Request'; import Appear from '../../../components/ui/effects/Appear'; +import { workspaceStore } from '../index'; +import PremiumFeatureContainer from '../../../components/ui/PremiumFeatureContainer'; const messages = defineMessages({ headline: { @@ -36,6 +38,14 @@ const messages = defineMessages({ id: 'settings.workspaces.deletedInfo', defaultMessage: '!!!Workspace has been deleted', }, + workspaceFeatureInfo: { + id: 'settings.workspaces.workspaceFeatureInfo', + defaultMessage: '!!!Info about workspace feature', + }, + workspaceFeatureHeadline: { + id: 'settings.workspaces.workspaceFeatureHeadline', + defaultMessage: '!!!Less is More: Introducing Franz Workspaces', + }, }); const styles = () => ({ @@ -46,6 +56,12 @@ const styles = () => ({ appear: { height: 'auto', }, + premiumAnnouncement: { + padding: '20px', + backgroundColor: '#3498db', + marginLeft: '-20px', + height: 'auto', + }, }); @injectSheet(styles) @observer @@ -112,14 +128,24 @@ class WorkspacesDashboard extends Component { )} - {/* ===== Create workspace form ===== */} -
- -
- + + {/* ===== Create workspace form ===== */} +
+ +
+
+ {workspaceStore.isUpgradeToPremiumRequired && ( +
+

{intl.formatMessage(messages.workspaceFeatureHeadline)}

+

{intl.formatMessage(messages.workspaceFeatureInfo)}

+
+ )} {getUserWorkspacesRequest.isExecuting ? ( ) : ( diff --git a/src/features/workspaces/index.js b/src/features/workspaces/index.js index 89999ab0f..524a83e3c 100644 --- a/src/features/workspaces/index.js +++ b/src/features/workspaces/index.js @@ -4,11 +4,12 @@ import { resetApiRequests } from './api'; const debug = require('debug')('Franz:feature:workspaces'); -export const GA_CATEGORY_WORKSPACES = 'workspaces'; +export const GA_CATEGORY_WORKSPACES = 'Workspaces'; export const workspaceStore = new WorkspacesStore(); export default function initWorkspaces(stores, actions) { + stores.workspaces = workspaceStore; const { features, user } = stores; // Toggle workspace feature diff --git a/src/features/workspaces/store.js b/src/features/workspaces/store.js index f7df7b29c..62bf3efb4 100644 --- a/src/features/workspaces/store.js +++ b/src/features/workspaces/store.js @@ -3,9 +3,9 @@ import { observable, action, } from 'mobx'; -import Reaction from '../../stores/lib/Reaction'; import { matchRoute } from '../../helpers/routing-helpers'; import { workspaceActions } from './actions'; +import { FeatureStore } from '../utils/FeatureStore'; import { createWorkspaceRequest, deleteWorkspaceRequest, @@ -15,7 +15,11 @@ import { const debug = require('debug')('Franz:feature:workspaces:store'); -export default class WorkspacesStore { +export default class WorkspacesStore extends FeatureStore { + @observable isFeatureEnabled = false; + + @observable isPremiumFeature = true; + @observable isFeatureActive = false; @observable activeWorkspace = null; @@ -33,36 +37,39 @@ export default class WorkspacesStore { return getUserWorkspacesRequest.result || []; } - constructor() { - // Wire-up action handlers - workspaceActions.edit.listen(this._edit); - workspaceActions.create.listen(this._create); - workspaceActions.delete.listen(this._delete); - workspaceActions.update.listen(this._update); - workspaceActions.activate.listen(this._setActiveWorkspace); - workspaceActions.deactivate.listen(this._deactivateActiveWorkspace); - workspaceActions.toggleWorkspaceDrawer.listen(this._toggleWorkspaceDrawer); - workspaceActions.openWorkspaceSettings.listen(this._openWorkspaceSettings); - - // Register and start reactions - this._registerReactions([ - this._updateWorkspaceBeingEdited, - this._updateActiveServiceOnWorkspaceSwitch, - ]); + @computed get isUpgradeToPremiumRequired() { + return this.isFeatureEnabled && !this.isFeatureActive; } start(stores, actions) { debug('WorkspacesStore::start'); this.stores = stores; this.actions = actions; - this._reactions.forEach(r => r.start()); - this.isFeatureActive = true; + + this._listenToActions([ + [workspaceActions.edit, this._edit], + [workspaceActions.create, this._create], + [workspaceActions.delete, this._delete], + [workspaceActions.update, this._update], + [workspaceActions.activate, this._setActiveWorkspace], + [workspaceActions.deactivate, this._deactivateActiveWorkspace], + [workspaceActions.toggleWorkspaceDrawer, this._toggleWorkspaceDrawer], + [workspaceActions.openWorkspaceSettings, this._openWorkspaceSettings], + ]); + + this._startReactions([ + this._setWorkspaceBeingEditedReaction, + this._setActiveServiceOnWorkspaceSwitchReaction, + this._setFeatureEnabledReaction, + this._setIsPremiumFeatureReaction, + ]); + getUserWorkspacesRequest.execute(); + this.isFeatureActive = true; } stop() { debug('WorkspacesStore::stop'); - this._reactions.forEach(r => r.stop()); this.isFeatureActive = false; this.activeWorkspace = null; this.nextWorkspace = null; @@ -85,12 +92,6 @@ export default class WorkspacesStore { // ========== PRIVATE ========= // - _reactions = []; - - _registerReactions(reactions) { - reactions.forEach(r => this._reactions.push(new Reaction(r))); - } - _getWorkspaceById = id => this.workspaces.find(w => w.id === id); // Actions @@ -164,7 +165,17 @@ export default class WorkspacesStore { // Reactions - _updateWorkspaceBeingEdited = () => { + _setFeatureEnabledReaction = () => { + const { isWorkspaceEnabled } = this.stores.features.features; + this.isFeatureEnabled = isWorkspaceEnabled; + }; + + _setIsPremiumFeatureReaction = () => { + const { isWorkspacePremiumFeature } = this.stores.features.features; + this.isPremiumFeature = isWorkspacePremiumFeature; + }; + + _setWorkspaceBeingEditedReaction = () => { const { pathname } = this.stores.router.location; const match = matchRoute('/settings/workspaces/edit/:id', pathname); if (match) { @@ -172,7 +183,7 @@ export default class WorkspacesStore { } }; - _updateActiveServiceOnWorkspaceSwitch = () => { + _setActiveServiceOnWorkspaceSwitchReaction = () => { if (!this.isFeatureActive) return; if (this.activeWorkspace) { const services = this.stores.services.allDisplayed; diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json index 659b1b361..1747e1976 100644 --- a/src/i18n/locales/defaultMessages.json +++ b/src/i18n/locales/defaultMessages.json @@ -2535,13 +2535,13 @@ "defaultMessage": "!!!Upgrade account", "end": { "column": 3, - "line": 17 + "line": 18 }, "file": "src/components/ui/PremiumFeatureContainer/index.js", "id": "premiumFeature.button.upgradeAccount", "start": { "column": 10, - "line": 14 + "line": 15 } } ], @@ -3388,78 +3388,104 @@ "defaultMessage": "!!!Your workspaces", "end": { "column": 3, - "line": 18 + "line": 20 }, "file": "src/features/workspaces/components/WorkspacesDashboard.js", "id": "settings.workspaces.headline", "start": { "column": 12, - "line": 15 + "line": 17 } }, { "defaultMessage": "!!!You haven't added any workspaces yet.", "end": { "column": 3, - "line": 22 + "line": 24 }, "file": "src/features/workspaces/components/WorkspacesDashboard.js", "id": "settings.workspaces.noWorkspacesAdded", "start": { "column": 19, - "line": 19 + "line": 21 } }, { "defaultMessage": "!!!Could not load your workspaces", "end": { "column": 3, - "line": 26 + "line": 28 }, "file": "src/features/workspaces/components/WorkspacesDashboard.js", "id": "settings.workspaces.workspacesRequestFailed", "start": { "column": 27, - "line": 23 + "line": 25 } }, { "defaultMessage": "!!!Try again", "end": { "column": 3, - "line": 30 + "line": 32 }, "file": "src/features/workspaces/components/WorkspacesDashboard.js", "id": "settings.workspaces.tryReloadWorkspaces", "start": { "column": 23, - "line": 27 + "line": 29 } }, { "defaultMessage": "!!!Your changes have been saved", "end": { "column": 3, - "line": 34 + "line": 36 }, "file": "src/features/workspaces/components/WorkspacesDashboard.js", "id": "settings.workspaces.updatedInfo", "start": { "column": 15, - "line": 31 + "line": 33 } }, { "defaultMessage": "!!!Workspace has been deleted", "end": { "column": 3, - "line": 38 + "line": 40 }, "file": "src/features/workspaces/components/WorkspacesDashboard.js", "id": "settings.workspaces.deletedInfo", "start": { "column": 15, - "line": 35 + "line": 37 + } + }, + { + "defaultMessage": "!!!Info about workspace feature", + "end": { + "column": 3, + "line": 44 + }, + "file": "src/features/workspaces/components/WorkspacesDashboard.js", + "id": "settings.workspaces.workspaceFeatureInfo", + "start": { + "column": 24, + "line": 41 + } + }, + { + "defaultMessage": "!!!Less is More: Introducing Franz Workspaces", + "end": { + "column": 3, + "line": 48 + }, + "file": "src/features/workspaces/components/WorkspacesDashboard.js", + "id": "settings.workspaces.workspaceFeatureHeadline", + "start": { + "column": 28, + "line": 45 } } ], diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 5f7254317..987262c35 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -254,6 +254,8 @@ "settings.workspaces.noWorkspacesAdded": "You haven't added any workspaces yet.", "settings.workspaces.tryReloadWorkspaces": "Try again", "settings.workspaces.updatedInfo": "Your changes have been saved", + "settings.workspaces.workspaceFeatureHeadline": "Less is More: Introducing Franz Workspaces", + "settings.workspaces.workspaceFeatureInfo": "Franz Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time. You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.", "settings.workspaces.workspacesRequestFailed": "Could not load your workspaces", "sidebar.addNewService": "Add new service", "sidebar.closeWorkspaceDrawer": "Close workspace drawer", diff --git a/src/i18n/messages/src/components/ui/PremiumFeatureContainer/index.json b/src/i18n/messages/src/components/ui/PremiumFeatureContainer/index.json index 582d546fa..320d3ca3e 100644 --- a/src/i18n/messages/src/components/ui/PremiumFeatureContainer/index.json +++ b/src/i18n/messages/src/components/ui/PremiumFeatureContainer/index.json @@ -4,11 +4,11 @@ "defaultMessage": "!!!Upgrade account", "file": "src/components/ui/PremiumFeatureContainer/index.js", "start": { - "line": 14, + "line": 15, "column": 10 }, "end": { - "line": 17, + "line": 18, "column": 3 } } diff --git a/src/i18n/messages/src/features/workspaces/components/WorkspacesDashboard.json b/src/i18n/messages/src/features/workspaces/components/WorkspacesDashboard.json index a957358c8..ef8f1bebc 100644 --- a/src/i18n/messages/src/features/workspaces/components/WorkspacesDashboard.json +++ b/src/i18n/messages/src/features/workspaces/components/WorkspacesDashboard.json @@ -4,11 +4,11 @@ "defaultMessage": "!!!Your workspaces", "file": "src/features/workspaces/components/WorkspacesDashboard.js", "start": { - "line": 15, + "line": 17, "column": 12 }, "end": { - "line": 18, + "line": 20, "column": 3 } }, @@ -17,11 +17,11 @@ "defaultMessage": "!!!You haven't added any workspaces yet.", "file": "src/features/workspaces/components/WorkspacesDashboard.js", "start": { - "line": 19, + "line": 21, "column": 19 }, "end": { - "line": 22, + "line": 24, "column": 3 } }, @@ -30,11 +30,11 @@ "defaultMessage": "!!!Could not load your workspaces", "file": "src/features/workspaces/components/WorkspacesDashboard.js", "start": { - "line": 23, + "line": 25, "column": 27 }, "end": { - "line": 26, + "line": 28, "column": 3 } }, @@ -43,11 +43,11 @@ "defaultMessage": "!!!Try again", "file": "src/features/workspaces/components/WorkspacesDashboard.js", "start": { - "line": 27, + "line": 29, "column": 23 }, "end": { - "line": 30, + "line": 32, "column": 3 } }, @@ -56,11 +56,11 @@ "defaultMessage": "!!!Your changes have been saved", "file": "src/features/workspaces/components/WorkspacesDashboard.js", "start": { - "line": 31, + "line": 33, "column": 15 }, "end": { - "line": 34, + "line": 36, "column": 3 } }, @@ -69,11 +69,37 @@ "defaultMessage": "!!!Workspace has been deleted", "file": "src/features/workspaces/components/WorkspacesDashboard.js", "start": { - "line": 35, + "line": 37, "column": 15 }, "end": { - "line": 38, + "line": 40, + "column": 3 + } + }, + { + "id": "settings.workspaces.workspaceFeatureInfo", + "defaultMessage": "!!!Info about workspace feature", + "file": "src/features/workspaces/components/WorkspacesDashboard.js", + "start": { + "line": 41, + "column": 24 + }, + "end": { + "line": 44, + "column": 3 + } + }, + { + "id": "settings.workspaces.workspaceFeatureHeadline", + "defaultMessage": "!!!Less is More: Introducing Franz Workspaces", + "file": "src/features/workspaces/components/WorkspacesDashboard.js", + "start": { + "line": 45, + "column": 28 + }, + "end": { + "line": 48, "column": 3 } } diff --git a/src/lib/Menu.js b/src/lib/Menu.js index d19aa9d6e..a4e41c17c 100644 --- a/src/lib/Menu.js +++ b/src/lib/Menu.js @@ -323,7 +323,7 @@ const _templateFactory = intl => [ { label: intl.formatMessage(menuItems.workspaces), submenu: [], - visible: workspaceStore.isFeatureActive, + visible: workspaceStore.isFeatureEnabled, }, { label: intl.formatMessage(menuItems.window), @@ -732,7 +732,7 @@ export default class FranzMenu { tpl[3].submenu = serviceTpl; } - if (workspaceStore.isFeatureActive) { + if (workspaceStore.isFeatureEnabled) { tpl[4].submenu = this.workspacesMenu(); } diff --git a/src/stores/FeaturesStore.js b/src/stores/FeaturesStore.js index 2bda82e17..52e38ad96 100644 --- a/src/stores/FeaturesStore.js +++ b/src/stores/FeaturesStore.js @@ -1,4 +1,4 @@ -import { computed, observable, reaction } from 'mobx'; +import { computed, observable, reaction, runInAction } from 'mobx'; import Store from './lib/Store'; import CachedRequest from './lib/CachedRequest'; @@ -17,8 +17,11 @@ export default class FeaturesStore extends Store { @observable featuresRequest = new CachedRequest(this.api.features, 'features'); + @observable features = Object.assign({}, DEFAULT_FEATURES_CONFIG); + async setup() { this.registerReactions([ + this._updateFeatures, this._monitorLoginStatus.bind(this), ]); @@ -37,13 +40,16 @@ export default class FeaturesStore extends Store { return this.defaultFeaturesRequest.execute().result || DEFAULT_FEATURES_CONFIG; } - @computed get features() { + _updateFeatures = () => { + const features = Object.assign({}, DEFAULT_FEATURES_CONFIG); if (this.stores.user.isLoggedIn) { - return Object.assign({}, DEFAULT_FEATURES_CONFIG, this.featuresRequest.execute().result); + const requestResult = this.featuresRequest.execute().result; + Object.assign(features, requestResult); } - - return DEFAULT_FEATURES_CONFIG; - } + runInAction('FeaturesStore::_updateFeatures', () => { + this.features = features; + }); + }; _monitorLoginStatus() { if (this.stores.user.isLoggedIn) { -- cgit v1.2.3-54-g00ecf From 6b38abc9011648a1f54b1ef3e3fb29d13750750c Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Wed, 3 Apr 2019 16:59:24 +0200 Subject: add workspace premium badge in settings nav --- .../settings/navigation/SettingsNavigation.js | 9 +++++- src/i18n/locales/defaultMessages.json | 32 +++++++++++----------- src/i18n/locales/en-US.json | 2 +- .../settings/navigation/SettingsNavigation.json | 28 +++++++++---------- src/stores/FeaturesStore.js | 7 ++++- src/styles/settings.scss | 10 +++++++ 6 files changed, 55 insertions(+), 33 deletions(-) (limited to 'src/components/settings') diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js index dc3c1d6f1..945285f5a 100644 --- a/src/components/settings/navigation/SettingsNavigation.js +++ b/src/components/settings/navigation/SettingsNavigation.js @@ -2,6 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { defineMessages, intlShape } from 'react-intl'; import { inject, observer } from 'mobx-react'; +import { Icon } from '@meetfranz/ui'; import Link from '../../ui/Link'; import { workspaceStore } from '../../../features/workspaces'; @@ -77,7 +78,13 @@ export default @inject('stores') @observer class SettingsNavigation extends Comp > {intl.formatMessage(messages.yourWorkspaces)} {' '} - {workspaceCount} + {workspaceStore.isPremiumUpgradeRequired ? ( + + + + ) : ( + {workspaceCount} + )} ) : null} Franz Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time.

You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.

", "workspaces.switchingIndicator.switchingTo": "Switching to" -} +} \ No newline at end of file diff --git a/src/i18n/messages/src/components/settings/navigation/SettingsNavigation.json b/src/i18n/messages/src/components/settings/navigation/SettingsNavigation.json index 96a42aa80..de78a71cf 100644 --- a/src/i18n/messages/src/components/settings/navigation/SettingsNavigation.json +++ b/src/i18n/messages/src/components/settings/navigation/SettingsNavigation.json @@ -4,11 +4,11 @@ "defaultMessage": "!!!Available services", "file": "src/components/settings/navigation/SettingsNavigation.js", "start": { - "line": 10, + "line": 11, "column": 21 }, "end": { - "line": 13, + "line": 14, "column": 3 } }, @@ -17,11 +17,11 @@ "defaultMessage": "!!!Your services", "file": "src/components/settings/navigation/SettingsNavigation.js", "start": { - "line": 14, + "line": 15, "column": 16 }, "end": { - "line": 17, + "line": 18, "column": 3 } }, @@ -30,11 +30,11 @@ "defaultMessage": "!!!Your workspaces", "file": "src/components/settings/navigation/SettingsNavigation.js", "start": { - "line": 18, + "line": 19, "column": 18 }, "end": { - "line": 21, + "line": 22, "column": 3 } }, @@ -43,11 +43,11 @@ "defaultMessage": "!!!Account", "file": "src/components/settings/navigation/SettingsNavigation.js", "start": { - "line": 22, + "line": 23, "column": 11 }, "end": { - "line": 25, + "line": 26, "column": 3 } }, @@ -56,11 +56,11 @@ "defaultMessage": "!!!Settings", "file": "src/components/settings/navigation/SettingsNavigation.js", "start": { - "line": 26, + "line": 27, "column": 12 }, "end": { - "line": 29, + "line": 30, "column": 3 } }, @@ -69,11 +69,11 @@ "defaultMessage": "!!!Invite Friends", "file": "src/components/settings/navigation/SettingsNavigation.js", "start": { - "line": 30, + "line": 31, "column": 17 }, "end": { - "line": 33, + "line": 34, "column": 3 } }, @@ -82,11 +82,11 @@ "defaultMessage": "!!!Logout", "file": "src/components/settings/navigation/SettingsNavigation.js", "start": { - "line": 34, + "line": 35, "column": 10 }, "end": { - "line": 37, + "line": 38, "column": 3 } } diff --git a/src/stores/FeaturesStore.js b/src/stores/FeaturesStore.js index 52e38ad96..8fe576813 100644 --- a/src/stores/FeaturesStore.js +++ b/src/stores/FeaturesStore.js @@ -1,4 +1,9 @@ -import { computed, observable, reaction, runInAction } from 'mobx'; +import { + computed, + observable, + reaction, + runInAction, +} from 'mobx'; import Store from './lib/Store'; import CachedRequest from './lib/CachedRequest'; diff --git a/src/styles/settings.scss b/src/styles/settings.scss index 9fde9a7bf..d97d4ac2c 100644 --- a/src/styles/settings.scss +++ b/src/styles/settings.scss @@ -85,6 +85,11 @@ .badge { background: $dark-theme-gray-lighter; color: $dark-theme-gray-smoke; + + &.badge--pro { + background: $theme-brand-primary; + padding: 4px 6px 3px 7px; + } } &:hover { @@ -93,6 +98,11 @@ .badge { background: $dark-theme-gray-lighter; color: $dark-theme-gray-smoke; + + &.badge--pro { + background: $theme-brand-primary; + padding: 4px 6px 3px 7px; + } } } -- cgit v1.2.3-54-g00ecf From 4e5726914a0c32fc445b2aae364baf0d006024a8 Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Wed, 3 Apr 2019 17:17:54 +0200 Subject: fix premium workspace badge in settings menu for light theme --- src/components/settings/navigation/SettingsNavigation.js | 2 +- src/i18n/locales/defaultMessages.json | 2 +- src/i18n/locales/en-US.json | 2 +- src/i18n/messages/src/features/shareFranz/Component.json | 2 +- src/styles/settings.scss | 13 ++++++++++++- 5 files changed, 16 insertions(+), 5 deletions(-) (limited to 'src/components/settings') diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js index 945285f5a..1c51d50d6 100644 --- a/src/components/settings/navigation/SettingsNavigation.js +++ b/src/components/settings/navigation/SettingsNavigation.js @@ -80,7 +80,7 @@ export default @inject('stores') @observer class SettingsNavigation extends Comp {' '} {workspaceStore.isPremiumUpgradeRequired ? ( - + ) : ( {workspaceCount} diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json index 5e3988781..500053377 100644 --- a/src/i18n/locales/defaultMessages.json +++ b/src/i18n/locales/defaultMessages.json @@ -3204,7 +3204,7 @@ } }, { - "defaultMessage": "!!! I've added {count} services to Franz! Get the free app for WhatsApp, Messenger, Slack, Skype and co at www.meetfranz.com /cc @MeetFranz", + "defaultMessage": "!!! I've added {count} services to Franz! Get the free app for WhatsApp, Messenger, Slack, Skype and co at www.meetfranz.com /cc @FranzMessenger", "end": { "column": 3, "line": 42 diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index c36801d4a..9770ff0d5 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -313,4 +313,4 @@ "workspaceDrawer.premiumCtaButtonLabel": "Create your first workspace", "workspaceDrawer.workspaceFeatureInfo": "

Franz Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time.

You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.

", "workspaces.switchingIndicator.switchingTo": "Switching to" -} +} \ No newline at end of file diff --git a/src/i18n/messages/src/features/shareFranz/Component.json b/src/i18n/messages/src/features/shareFranz/Component.json index 0fec9db64..34a43d5a0 100644 --- a/src/i18n/messages/src/features/shareFranz/Component.json +++ b/src/i18n/messages/src/features/shareFranz/Component.json @@ -79,7 +79,7 @@ }, { "id": "feature.shareFranz.shareText.twitter", - "defaultMessage": "!!! I've added {count} services to Franz! Get the free app for WhatsApp, Messenger, Slack, Skype and co at www.meetfranz.com /cc @MeetFranz", + "defaultMessage": "!!! I've added {count} services to Franz! Get the free app for WhatsApp, Messenger, Slack, Skype and co at www.meetfranz.com /cc @FranzMessenger", "file": "src/features/shareFranz/Component.js", "start": { "line": 39, diff --git a/src/styles/settings.scss b/src/styles/settings.scss index d97d4ac2c..b4f013cad 100644 --- a/src/styles/settings.scss +++ b/src/styles/settings.scss @@ -433,10 +433,21 @@ text-decoration: none; transition: background $theme-transition-time, color $theme-transition-time; + .badge--pro { + background: $theme-brand-primary !important; + padding: 4px 6px 3px 7px; + + .badge-icon-pro { + fill: white; + } + } + &:hover { background: darken($theme-gray-lightest, 5%); - .badge { background: #FFF; } + .badge { + background: #FFF; + } } &.is-active { -- cgit v1.2.3-54-g00ecf From 073212bf046b9218f9e3129988b1b63fba5d685d Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Wed, 10 Apr 2019 16:12:38 +0200 Subject: added generic pro badge component for settings nav --- packages/ui/src/badge/ProBadge.tsx | 65 ++++++++++++++++++++++ packages/ui/src/index.ts | 1 + .../settings/navigation/SettingsNavigation.js | 13 +++-- src/features/workspaces/store.js | 12 ++-- src/i18n/locales/defaultMessages.json | 28 +++++----- src/i18n/locales/en-US.json | 2 +- .../settings/navigation/SettingsNavigation.json | 28 +++++----- src/stores/UIStore.js | 9 +-- src/styles/settings.scss | 26 --------- uidev/src/stories/badge.stories.tsx | 12 +++- 10 files changed, 125 insertions(+), 71 deletions(-) create mode 100644 packages/ui/src/badge/ProBadge.tsx (limited to 'src/components/settings') diff --git a/packages/ui/src/badge/ProBadge.tsx b/packages/ui/src/badge/ProBadge.tsx new file mode 100644 index 000000000..eb00e156d --- /dev/null +++ b/packages/ui/src/badge/ProBadge.tsx @@ -0,0 +1,65 @@ +import { Theme } from '@meetfranz/theme'; +import classnames from 'classnames'; +import React, { Component } from 'react'; +import injectStyle from 'react-jss'; + +import { Icon, Badge } from '../'; +import { IWithStyle } from '../typings/generic'; + +interface IProps extends IWithStyle { + badgeClasses?: string; + iconClasses?: string; + inverted?: boolean; +} + +const styles = (theme: Theme) => ({ + badge: { + height: 'auto', + padding: [4, 6, 2, 7], + borderRadius: theme.borderRadiusSmall, + }, + invertedBadge: { + background: theme.styleTypes.primary.contrast, + color: theme.styleTypes.primary.accent, + }, + icon: { + fill: theme.styleTypes.primary.contrast, + }, + invertedIcon: { + fill: theme.styleTypes.primary.accent, + }, +}); + +class ProBadgeComponent extends Component { + render() { + const { + classes, + badgeClasses, + iconClasses, + inverted, + } = this.props; + + return ( + + + + ); + } +} + +export const ProBadge = injectStyle(styles)(ProBadgeComponent); diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 1eeec5144..666495ce9 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -3,3 +3,4 @@ export { Infobox } from './infobox'; export * from './headline'; export { Loader } from './loader'; export { Badge } from './badge'; +export { ProBadge } from './badge/ProBadge'; diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js index 1c51d50d6..993b0a44a 100644 --- a/src/components/settings/navigation/SettingsNavigation.js +++ b/src/components/settings/navigation/SettingsNavigation.js @@ -2,10 +2,11 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { defineMessages, intlShape } from 'react-intl'; import { inject, observer } from 'mobx-react'; -import { Icon } from '@meetfranz/ui'; +import { ProBadge } from '@meetfranz/ui'; import Link from '../../ui/Link'; import { workspaceStore } from '../../../features/workspaces'; +import UIStore from '../../../stores/UIStore'; const messages = defineMessages({ availableServices: { @@ -40,6 +41,9 @@ const messages = defineMessages({ export default @inject('stores') @observer class SettingsNavigation extends Component { static propTypes = { + stores: PropTypes.shape({ + ui: PropTypes.instanceOf(UIStore).isRequired, + }).isRequired, serviceCount: PropTypes.number.isRequired, workspaceCount: PropTypes.number.isRequired, }; @@ -49,7 +53,8 @@ export default @inject('stores') @observer class SettingsNavigation extends Comp }; render() { - const { serviceCount, workspaceCount } = this.props; + const { serviceCount, workspaceCount, stores } = this.props; + const { isDarkThemeActive } = stores.ui; const { intl } = this.context; return ( @@ -79,9 +84,7 @@ export default @inject('stores') @observer class SettingsNavigation extends Comp {intl.formatMessage(messages.yourWorkspaces)} {' '} {workspaceStore.isPremiumUpgradeRequired ? ( - - - + ) : ( {workspaceCount} )} diff --git a/src/features/workspaces/store.js b/src/features/workspaces/store.js index 2e1764f99..ba48022c2 100644 --- a/src/features/workspaces/store.js +++ b/src/features/workspaces/store.js @@ -36,6 +36,8 @@ export default class WorkspacesStore extends FeatureStore { @observable isWorkspaceDrawerOpen = false; + @observable isSettingsRouteActive = null; + @computed get workspaces() { if (!this.isFeatureActive) return []; return getUserWorkspacesRequest.result || []; @@ -104,8 +106,6 @@ export default class WorkspacesStore extends FeatureStore { _wasDrawerOpenBeforeSettingsRoute = null; - _isSettingsRouteActive = null; - _getWorkspaceById = id => this.workspaces.find(w => w.id === id); _updateSettings = (changes) => { @@ -239,17 +239,17 @@ export default class WorkspacesStore extends FeatureStore { _openDrawerWithSettingsReaction = () => { const { router } = this.stores; const isWorkspaceSettingsRoute = router.location.pathname.includes(WORKSPACES_ROUTES.ROOT); - const isSwitchingToSettingsRoute = !this._isSettingsRouteActive && isWorkspaceSettingsRoute; - const isLeavingSettingsRoute = !isWorkspaceSettingsRoute && this._isSettingsRouteActive; + const isSwitchingToSettingsRoute = !this.isSettingsRouteActive && isWorkspaceSettingsRoute; + const isLeavingSettingsRoute = !isWorkspaceSettingsRoute && this.isSettingsRouteActive; if (isSwitchingToSettingsRoute) { - this._isSettingsRouteActive = true; + this.isSettingsRouteActive = true; this._wasDrawerOpenBeforeSettingsRoute = this.isWorkspaceDrawerOpen; if (!this._wasDrawerOpenBeforeSettingsRoute) { workspaceActions.toggleWorkspaceDrawer(); } } else if (isLeavingSettingsRoute) { - this._isSettingsRouteActive = false; + this.isSettingsRouteActive = false; if (!this._wasDrawerOpenBeforeSettingsRoute && this.isWorkspaceDrawerOpen) { workspaceActions.toggleWorkspaceDrawer(); } diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json index f882e6030..791c4dd53 100644 --- a/src/i18n/locales/defaultMessages.json +++ b/src/i18n/locales/defaultMessages.json @@ -1302,91 +1302,91 @@ "defaultMessage": "!!!Available services", "end": { "column": 3, - "line": 14 + "line": 15 }, "file": "src/components/settings/navigation/SettingsNavigation.js", "id": "settings.navigation.availableServices", "start": { "column": 21, - "line": 11 + "line": 12 } }, { "defaultMessage": "!!!Your services", "end": { "column": 3, - "line": 18 + "line": 19 }, "file": "src/components/settings/navigation/SettingsNavigation.js", "id": "settings.navigation.yourServices", "start": { "column": 16, - "line": 15 + "line": 16 } }, { "defaultMessage": "!!!Your workspaces", "end": { "column": 3, - "line": 22 + "line": 23 }, "file": "src/components/settings/navigation/SettingsNavigation.js", "id": "settings.navigation.yourWorkspaces", "start": { "column": 18, - "line": 19 + "line": 20 } }, { "defaultMessage": "!!!Account", "end": { "column": 3, - "line": 26 + "line": 27 }, "file": "src/components/settings/navigation/SettingsNavigation.js", "id": "settings.navigation.account", "start": { "column": 11, - "line": 23 + "line": 24 } }, { "defaultMessage": "!!!Settings", "end": { "column": 3, - "line": 30 + "line": 31 }, "file": "src/components/settings/navigation/SettingsNavigation.js", "id": "settings.navigation.settings", "start": { "column": 12, - "line": 27 + "line": 28 } }, { "defaultMessage": "!!!Invite Friends", "end": { "column": 3, - "line": 34 + "line": 35 }, "file": "src/components/settings/navigation/SettingsNavigation.js", "id": "settings.navigation.inviteFriends", "start": { "column": 17, - "line": 31 + "line": 32 } }, { "defaultMessage": "!!!Logout", "end": { "column": 3, - "line": 38 + "line": 39 }, "file": "src/components/settings/navigation/SettingsNavigation.js", "id": "settings.navigation.logout", "start": { "column": 10, - "line": 35 + "line": 36 } } ], diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index db1d51f3b..6591af2e2 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -315,4 +315,4 @@ "workspaceDrawer.workspaceFeatureInfo": "

Franz Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time.

You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.

", "workspaceDrawer.workspacesSettingsTooltip": "Edit workspaces settings", "workspaces.switchingIndicator.switchingTo": "Switching to" -} +} \ No newline at end of file diff --git a/src/i18n/messages/src/components/settings/navigation/SettingsNavigation.json b/src/i18n/messages/src/components/settings/navigation/SettingsNavigation.json index de78a71cf..77b0ed8a4 100644 --- a/src/i18n/messages/src/components/settings/navigation/SettingsNavigation.json +++ b/src/i18n/messages/src/components/settings/navigation/SettingsNavigation.json @@ -4,11 +4,11 @@ "defaultMessage": "!!!Available services", "file": "src/components/settings/navigation/SettingsNavigation.js", "start": { - "line": 11, + "line": 12, "column": 21 }, "end": { - "line": 14, + "line": 15, "column": 3 } }, @@ -17,11 +17,11 @@ "defaultMessage": "!!!Your services", "file": "src/components/settings/navigation/SettingsNavigation.js", "start": { - "line": 15, + "line": 16, "column": 16 }, "end": { - "line": 18, + "line": 19, "column": 3 } }, @@ -30,11 +30,11 @@ "defaultMessage": "!!!Your workspaces", "file": "src/components/settings/navigation/SettingsNavigation.js", "start": { - "line": 19, + "line": 20, "column": 18 }, "end": { - "line": 22, + "line": 23, "column": 3 } }, @@ -43,11 +43,11 @@ "defaultMessage": "!!!Account", "file": "src/components/settings/navigation/SettingsNavigation.js", "start": { - "line": 23, + "line": 24, "column": 11 }, "end": { - "line": 26, + "line": 27, "column": 3 } }, @@ -56,11 +56,11 @@ "defaultMessage": "!!!Settings", "file": "src/components/settings/navigation/SettingsNavigation.js", "start": { - "line": 27, + "line": 28, "column": 12 }, "end": { - "line": 30, + "line": 31, "column": 3 } }, @@ -69,11 +69,11 @@ "defaultMessage": "!!!Invite Friends", "file": "src/components/settings/navigation/SettingsNavigation.js", "start": { - "line": 31, + "line": 32, "column": 17 }, "end": { - "line": 34, + "line": 35, "column": 3 } }, @@ -82,11 +82,11 @@ "defaultMessage": "!!!Logout", "file": "src/components/settings/navigation/SettingsNavigation.js", "start": { - "line": 35, + "line": 36, "column": 10 }, "end": { - "line": 38, + "line": 39, "column": 3 } } diff --git a/src/stores/UIStore.js b/src/stores/UIStore.js index bb7965a4a..a95a8e1e0 100644 --- a/src/stores/UIStore.js +++ b/src/stores/UIStore.js @@ -21,11 +21,12 @@ export default class UIStore extends Store { return (settings.app.isAppMuted && settings.app.showMessageBadgeWhenMuted) || !settings.isAppMuted; } - @computed get theme() { - if (this.stores.settings.all.app.darkMode) { - return theme('dark'); - } + @computed get isDarkThemeActive() { + return this.stores.settings.all.app.darkMode; + } + @computed get theme() { + if (this.isDarkThemeActive) return theme('dark'); return theme('default'); } diff --git a/src/styles/settings.scss b/src/styles/settings.scss index 6340f2951..dd6f56d2b 100644 --- a/src/styles/settings.scss +++ b/src/styles/settings.scss @@ -85,11 +85,6 @@ .badge { background: $dark-theme-gray-lighter; color: $dark-theme-gray-smoke; - - &.badge--pro { - background: $theme-brand-primary; - padding: 4px 6px 3px 7px; - } } &:hover { @@ -98,11 +93,6 @@ .badge { background: $dark-theme-gray-lighter; color: $dark-theme-gray-smoke; - - &.badge--pro { - background: $theme-brand-primary; - padding: 4px 6px 3px 7px; - } } } @@ -433,15 +423,6 @@ text-decoration: none; transition: background $theme-transition-time, color $theme-transition-time; - .badge--pro { - background: $theme-brand-primary !important; - padding: 4px 6px 3px 7px; - - .badge-icon-pro { - fill: white; - } - } - &:hover { background: darken($theme-gray-lightest, 5%); @@ -454,13 +435,6 @@ background: $theme-brand-primary; color: #FFF; - .badge--pro { - background: white !important; - .badge-icon-pro { - fill: $theme-brand-primary; - } - } - .badge { background: #FFF; color: $theme-brand-primary; diff --git a/uidev/src/stories/badge.stories.tsx b/uidev/src/stories/badge.stories.tsx index 6de2034bf..d7b4d55b5 100644 --- a/uidev/src/stories/badge.stories.tsx +++ b/uidev/src/stories/badge.stories.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { Badge } from '@meetfranz/ui'; +import { Badge, ProBadge } from '@meetfranz/ui'; import { storiesOf } from '../stores/stories'; storiesOf('Badge') @@ -18,4 +18,14 @@ storiesOf('Badge') danger inverted + )) + .add('Pro Badge', () => ( + <> + + + )) + .add('Pro Badge inverted', () => ( + <> + + )); -- cgit v1.2.3-54-g00ecf