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/i18n/locales/de.json | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/i18n/locales/de.json') 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", -- cgit v1.2.3-70-g09d2 From a421ba151f40695f54a4890219c5ec8af0a24a45 Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Tue, 19 Feb 2019 15:02:11 +0100 Subject: prepare basic workspace edit form --- .../workspaces/components/EditWorkspaceForm.js | 142 +++++++++++++++++++++ .../workspaces/containers/EditWorkspaceScreen.js | 67 +++------- src/i18n/locales/de.json | 3 + src/i18n/locales/en-US.json | 3 + 4 files changed, 164 insertions(+), 51 deletions(-) create mode 100644 src/features/workspaces/components/EditWorkspaceForm.js (limited to 'src/i18n/locales/de.json') diff --git a/src/features/workspaces/components/EditWorkspaceForm.js b/src/features/workspaces/components/EditWorkspaceForm.js new file mode 100644 index 000000000..9e87b56dd --- /dev/null +++ b/src/features/workspaces/components/EditWorkspaceForm.js @@ -0,0 +1,142 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { observer } from 'mobx-react'; +import { defineMessages, intlShape } from 'react-intl'; +import { Link } from 'react-router'; +import { Input, Button } from '@meetfranz/forms'; + +import Form from '../../../lib/Form'; +import Workspace from '../models/Workspace'; + +const messages = defineMessages({ + buttonDelete: { + id: 'settings.workspace.form.buttonDelete', + defaultMessage: '!!!Delete workspace', + }, + buttonSave: { + id: 'settings.workspace.form.buttonSave', + defaultMessage: '!!!Save workspace', + }, + name: { + id: 'settings.workspace.form.name', + defaultMessage: '!!!Name', + }, + yourWorkspaces: { + id: 'settings.workspace.form.yourWorkspaces', + defaultMessage: '!!!Your workspaces', + }, +}); + +@observer +class EditWorkspaceForm extends Component { + static contextTypes = { + intl: intlShape, + }; + + static propTypes = { + workspace: PropTypes.instanceOf(Workspace).isRequired, + onSave: PropTypes.func.isRequired, + onDelete: PropTypes.func.isRequired, + isSaving: PropTypes.bool.isRequired, + isDeleting: PropTypes.bool.isRequired, + }; + + 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); + } + + submitForm(submitEvent, form) { + submitEvent.preventDefault(); + form.submit({ + onSuccess: async (f) => { + const { onSave } = this.props; + const values = f.values(); + onSave(values); + }, + onError: async () => {}, + }); + } + + render() { + const { intl } = this.context; + const { + workspace, + isDeleting, + isSaving, + onDelete, + } = this.props; + if (!workspace) return null; + + const form = this.prepareForm(workspace); + + return ( +
+
+ + + {intl.formatMessage(messages.yourWorkspaces)} + + + + + {workspace.name} + +
+
+
this.submitForm(e, form)} id="form"> +
+ +
+
+
+
+ {/* ===== Delete Button ===== */} + {isDeleting ? ( +
+
+ ); + } +} + +export default EditWorkspaceForm; diff --git a/src/features/workspaces/containers/EditWorkspaceScreen.js b/src/features/workspaces/containers/EditWorkspaceScreen.js index d8c52f586..ed54b194e 100644 --- a/src/features/workspaces/containers/EditWorkspaceScreen.js +++ b/src/features/workspaces/containers/EditWorkspaceScreen.js @@ -1,72 +1,37 @@ 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', - }, -}); +import EditWorkspaceForm from '../components/EditWorkspaceForm'; @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); - } + onDelete = () => { + console.log('delete workspace'); + }; + + onSave = (values) => { + console.log('save workspace', values); + }; 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 -
-
+
); } diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json index 5cb8c6bd3..ffad8b835 100644 --- a/src/i18n/locales/de.json +++ b/src/i18n/locales/de.json @@ -219,6 +219,9 @@ "settings.services.updatedInfo" : "Deine Änderungen wurden gespeichert", "settings.workspaces.headline": "Deine Workspaces", "settings.workspace.form.yourWorkspaces": "Deine Workspaces", + "settings.workspace.form.name": "Name", + "settings.workspace.form.buttonDelete": "Workspace löschen", + "settings.workspace.form.buttonSave": "Workspace speichern", "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 9b323e323..899c62651 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -198,6 +198,9 @@ "settings.user.form.accountType.company": "Company", "settings.workspaces.headline": "Your workspaces", "settings.workspace.form.yourWorkspaces": "Your workspaces", + "settings.workspace.form.name": "Name", + "settings.workspace.form.buttonDelete": "Delete Workspace", + "settings.workspace.form.buttonSave": "Save Workspace", "subscription.type.free": "free", "subscription.type.month": "month", "subscription.type.year": "year", -- cgit v1.2.3-70-g09d2 From 7e40b08e2f8ab2448dcbe2ce7b4e38ca66764b9c Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Fri, 22 Feb 2019 12:34:27 +0100 Subject: add form for creating workspaces --- .eslintrc | 2 +- src/features/delayApp/Component.js | 2 +- src/features/workspaces/actions.js | 3 + src/features/workspaces/api.js | 9 +++ .../workspaces/components/CreateWorkspaceForm.js | 94 ++++++++++++++++++++++ .../workspaces/components/WorkspacesDashboard.js | 58 ++++++++----- .../workspaces/containers/WorkspacesScreen.js | 1 + src/features/workspaces/store.js | 14 +++- src/i18n/locales/de.json | 6 +- src/i18n/locales/en-US.json | 2 + src/styles/main.scss | 2 + 11 files changed, 170 insertions(+), 23 deletions(-) create mode 100644 src/features/workspaces/components/CreateWorkspaceForm.js (limited to 'src/i18n/locales/de.json') diff --git a/.eslintrc b/.eslintrc index e15148e96..48fb21250 100644 --- a/.eslintrc +++ b/.eslintrc @@ -13,7 +13,7 @@ "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], - "react/forbid-prop-types": 1, + "react/forbid-prop-types": 0, "react/destructuring-assignment": 1, "prefer-destructuring": 1, "no-underscore-dangle": 0, diff --git a/src/features/delayApp/Component.js b/src/features/delayApp/Component.js index ff84510e8..ff0f1f2f8 100644 --- a/src/features/delayApp/Component.js +++ b/src/features/delayApp/Component.js @@ -38,7 +38,7 @@ export default @inject('actions') @injectSheet(styles) @observer class DelayApp state = { countdown: config.delayDuration, - } + }; countdownInterval = null; diff --git a/src/features/workspaces/actions.js b/src/features/workspaces/actions.js index 30866af96..390af0696 100644 --- a/src/features/workspaces/actions.js +++ b/src/features/workspaces/actions.js @@ -5,4 +5,7 @@ export default { edit: { workspace: PropTypes.instanceOf(Workspace).isRequired, }, + create: { + name: PropTypes.string.isRequired, + }, }; diff --git a/src/features/workspaces/api.js b/src/features/workspaces/api.js index 97badbd01..65108a077 100644 --- a/src/features/workspaces/api.js +++ b/src/features/workspaces/api.js @@ -10,4 +10,13 @@ export default { if (!request.ok) throw request; return request.json(); }, + createWorkspace: async (name) => { + const url = `${API}/${API_VERSION}/workspace`; + const request = await window.fetch(url, prepareAuthRequest({ + method: 'POST', + body: JSON.stringify({ name }), + })); + if (!request.ok) throw request; + return request.json(); + }, }; diff --git a/src/features/workspaces/components/CreateWorkspaceForm.js b/src/features/workspaces/components/CreateWorkspaceForm.js new file mode 100644 index 000000000..d440b9bae --- /dev/null +++ b/src/features/workspaces/components/CreateWorkspaceForm.js @@ -0,0 +1,94 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { observer } from 'mobx-react'; +import { defineMessages, intlShape } from 'react-intl'; +import { Input, Button } from '@meetfranz/forms'; +import injectSheet from 'react-jss'; + +import Form from '../../../lib/Form'; + +const messages = defineMessages({ + submitButton: { + id: 'settings.workspace.add.form.submitButton', + defaultMessage: '!!!Save workspace', + }, + name: { + id: 'settings.workspace.add.form.name', + defaultMessage: '!!!Name', + }, +}); + +const styles = () => ({ + form: { + display: 'flex', + }, + input: { + flexGrow: 1, + marginRight: '10px', + }, + submitButton: { + height: 'inherit', + marginTop: '17px', + }, +}); + +@observer @injectSheet(styles) +class CreateWorkspaceForm extends Component { + static contextTypes = { + intl: intlShape, + }; + + static propTypes = { + classes: PropTypes.object.isRequired, + onSubmit: PropTypes.func.isRequired, + }; + + prepareForm() { + const { intl } = this.context; + const config = { + fields: { + name: { + label: intl.formatMessage(messages.name), + placeholder: intl.formatMessage(messages.name), + value: '', + }, + }, + }; + return new Form(config); + } + + submitForm(form) { + form.submit({ + onSuccess: async (f) => { + const { onSubmit } = this.props; + const values = f.values(); + onSubmit(values); + }, + onError: async () => {}, + }); + } + + render() { + const { intl } = this.context; + const { classes } = this.props; + const form = this.prepareForm(); + + return ( +
+ +
+ ); + } +} + +export default CreateWorkspaceForm; diff --git a/src/features/workspaces/components/WorkspacesDashboard.js b/src/features/workspaces/components/WorkspacesDashboard.js index 2a8b3a5ee..917807302 100644 --- a/src/features/workspaces/components/WorkspacesDashboard.js +++ b/src/features/workspaces/components/WorkspacesDashboard.js @@ -2,9 +2,11 @@ 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 injectSheet from 'react-jss'; import Loader from '../../../components/ui/Loader'; import WorkspaceItem from './WorkspaceItem'; +import CreateWorkspaceForm from './CreateWorkspaceForm'; const messages = defineMessages({ headline: { @@ -17,12 +19,21 @@ const messages = defineMessages({ }, }); -@observer +const styles = () => ({ + createForm: { + height: 'auto', + marginBottom: '20px', + }, +}); + +@observer @injectSheet(styles) class WorkspacesDashboard extends Component { static propTypes = { - workspaces: MobxPropTypes.arrayOrObservableArray.isRequired, + classes: PropTypes.object.isRequired, isLoading: PropTypes.bool.isRequired, + onCreateWorkspaceSubmit: PropTypes.func.isRequired, onWorkspaceClick: PropTypes.func.isRequired, + workspaces: MobxPropTypes.arrayOrObservableArray.isRequired, }; static contextTypes = { @@ -30,7 +41,13 @@ class WorkspacesDashboard extends Component { }; render() { - const { workspaces, isLoading, onWorkspaceClick } = this.props; + const { + workspaces, + isLoading, + onCreateWorkspaceSubmit, + onWorkspaceClick, + classes, + } = this.props; const { intl } = this.context; return ( @@ -39,21 +56,26 @@ class WorkspacesDashboard extends Component {

{intl.formatMessage(messages.headline)}

- {isLoading ? ( - - ) : ( - - - {workspaces.map(workspace => ( - onWorkspaceClick(w)} - /> - ))} - -
- )} +
+
+ +
+ {isLoading ? ( + + ) : ( + + + {workspaces.map(workspace => ( + onWorkspaceClick(w)} + /> + ))} + +
+ )} +
); diff --git a/src/features/workspaces/containers/WorkspacesScreen.js b/src/features/workspaces/containers/WorkspacesScreen.js index f129edec5..eb3287952 100644 --- a/src/features/workspaces/containers/WorkspacesScreen.js +++ b/src/features/workspaces/containers/WorkspacesScreen.js @@ -27,6 +27,7 @@ class WorkspacesScreen extends Component { workspace.create(data)} onWorkspaceClick={w => workspace.edit({ workspace: w })} /> diff --git a/src/features/workspaces/store.js b/src/features/workspaces/store.js index ea61cec31..d90f9caac 100644 --- a/src/features/workspaces/store.js +++ b/src/features/workspaces/store.js @@ -49,6 +49,7 @@ export default class WorkspacesStore extends Store { ); this.actions.workspace.edit.listen(this._edit); + this.actions.workspace.create.listen(this._create); } _setWorkspaces = (workspaces) => { @@ -64,5 +65,16 @@ export default class WorkspacesStore extends Store { _edit = ({ workspace }) => { this.stores.router.push(`/settings/workspaces/edit/${workspace.id}`); - } + }; + + _create = async ({ name }) => { + try { + const result = await this.api.createWorkspace(name); + const workspace = new Workspace(result); + this.state.workspaces.push(workspace); + this._edit({ workspace }); + } catch (error) { + throw error; + } + }; } diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json index ffad8b835..b795b90f8 100644 --- a/src/i18n/locales/de.json +++ b/src/i18n/locales/de.json @@ -218,10 +218,12 @@ "settings.services.tooltip.notificationsDisabled" : "Benachrichtigungen deaktiviert", "settings.services.updatedInfo" : "Deine Änderungen wurden gespeichert", "settings.workspaces.headline": "Deine Workspaces", + "settings.workspace.add.form.submitButton": "Create Workspace", + "settings.workspace.add.form.name": "Name", "settings.workspace.form.yourWorkspaces": "Deine Workspaces", "settings.workspace.form.name": "Name", - "settings.workspace.form.buttonDelete": "Workspace löschen", - "settings.workspace.form.buttonSave": "Workspace speichern", + "settings.workspace.form.buttonDelete": "Workspace löschen", + "settings.workspace.form.buttonSave": "Workspace speichern", "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 899c62651..24c96cb26 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -197,6 +197,8 @@ "settings.user.form.accountType.non-profit": "Non-Profit", "settings.user.form.accountType.company": "Company", "settings.workspaces.headline": "Your workspaces", + "settings.workspace.add.form.submitButton": "Create Workspace", + "settings.workspace.add.form.name": "Name", "settings.workspace.form.yourWorkspaces": "Your workspaces", "settings.workspace.form.name": "Name", "settings.workspace.form.buttonDelete": "Delete Workspace", diff --git a/src/styles/main.scss b/src/styles/main.scss index a941d89d0..9ba7f5827 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -30,6 +30,8 @@ $mdi-font-path: '../node_modules/mdi/fonts'; @import './content-tabs.scss'; @import './invite.scss'; @import './title-bar.scss'; + +// Workspaces legacy css @import '../features/workspaces/styles/workspaces-table'; // form -- cgit v1.2.3-70-g09d2 From 18d2b0f988792632d8b9395d06613d08c582ad7b Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Fri, 22 Feb 2019 13:03:40 +0100 Subject: small fixes --- .eslintrc | 1 + src/i18n/locales/de.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src/i18n/locales/de.json') diff --git a/.eslintrc b/.eslintrc index 48fb21250..f1051723d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,6 +2,7 @@ "parser": "babel-eslint", "extends": "eslint-config-airbnb", "rules": { + "consistent-return": 0, "import/extensions": 0, "import/no-extraneous-dependencies": 0, "import/no-unresolved": [2, { diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json index b795b90f8..e1a955176 100644 --- a/src/i18n/locales/de.json +++ b/src/i18n/locales/de.json @@ -218,7 +218,7 @@ "settings.services.tooltip.notificationsDisabled" : "Benachrichtigungen deaktiviert", "settings.services.updatedInfo" : "Deine Änderungen wurden gespeichert", "settings.workspaces.headline": "Deine Workspaces", - "settings.workspace.add.form.submitButton": "Create Workspace", + "settings.workspace.add.form.submitButton": "Workspace erstellen", "settings.workspace.add.form.name": "Name", "settings.workspace.form.yourWorkspaces": "Deine Workspaces", "settings.workspace.form.name": "Name", -- cgit v1.2.3-70-g09d2 From dca7437b45c8eb67692a1df563fb4e969826b1cc Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Tue, 26 Feb 2019 15:29:34 +0100 Subject: finish basic workspace settings --- src/features/workspaces/actions.js | 3 ++ src/features/workspaces/api.js | 11 +++++ .../workspaces/components/EditWorkspaceForm.js | 46 +++++++++++++++++++-- .../workspaces/components/ServiceListItem.js | 48 ++++++++++++++++++++++ .../workspaces/containers/EditWorkspaceScreen.js | 14 ++++++- src/features/workspaces/models/Workspace.js | 2 +- src/features/workspaces/store.js | 12 ++++++ src/i18n/locales/de.json | 1 + src/i18n/locales/en-US.json | 1 + 9 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 src/features/workspaces/components/ServiceListItem.js (limited to 'src/i18n/locales/de.json') diff --git a/src/features/workspaces/actions.js b/src/features/workspaces/actions.js index 83d3447c3..84de2b011 100644 --- a/src/features/workspaces/actions.js +++ b/src/features/workspaces/actions.js @@ -11,4 +11,7 @@ export default { delete: { workspace: PropTypes.instanceOf(Workspace).isRequired, }, + update: { + workspace: PropTypes.instanceOf(Workspace).isRequired, + }, }; diff --git a/src/features/workspaces/api.js b/src/features/workspaces/api.js index fabc12455..733cb5593 100644 --- a/src/features/workspaces/api.js +++ b/src/features/workspaces/api.js @@ -1,3 +1,4 @@ +import { pick } from 'lodash'; import { sendAuthRequest } from '../../api/utils/auth'; import { API, API_VERSION } from '../../environment'; @@ -25,4 +26,14 @@ export default { if (!request.ok) throw request; return request.json(); }, + + updateWorkspace: async (workspace) => { + const url = `${API}/${API_VERSION}/workspace/${workspace.id}`; + const request = await sendAuthRequest(url, { + method: 'PUT', + body: JSON.stringify(pick(workspace, ['name', 'services'])), + }); + if (!request.ok) throw request; + return request.json(); + }, }; diff --git a/src/features/workspaces/components/EditWorkspaceForm.js b/src/features/workspaces/components/EditWorkspaceForm.js index 05ca65403..48090f608 100644 --- a/src/features/workspaces/components/EditWorkspaceForm.js +++ b/src/features/workspaces/components/EditWorkspaceForm.js @@ -7,8 +7,10 @@ import { Input, Button } from '@meetfranz/forms'; import injectSheet from 'react-jss'; import Workspace from '../models/Workspace'; +import Service from '../../../models/Service'; import Form from '../../../lib/Form'; import { required } from '../../../helpers/validation-helpers'; +import ServiceListItem from './ServiceListItem'; const messages = defineMessages({ buttonDelete: { @@ -27,12 +29,19 @@ const messages = defineMessages({ id: 'settings.workspace.form.yourWorkspaces', defaultMessage: '!!!Your workspaces', }, + servicesInWorkspaceHeadline: { + id: 'settings.workspace.form.servicesInWorkspaceHeadline', + defaultMessage: '!!!Services in this Workspace', + }, }); const styles = () => ({ nameInput: { height: 'auto', }, + serviceList: { + height: 'auto', + }, }); @injectSheet(styles) @observer @@ -42,11 +51,13 @@ class EditWorkspaceForm extends Component { }; static propTypes = { - workspace: PropTypes.instanceOf(Workspace).isRequired, - onSave: PropTypes.func.isRequired, - onDelete: PropTypes.func.isRequired, - isSaving: PropTypes.bool.isRequired, + classes: PropTypes.object.isRequired, isDeleting: PropTypes.bool.isRequired, + isSaving: PropTypes.bool.isRequired, + onDelete: PropTypes.func.isRequired, + onSave: PropTypes.func.isRequired, + services: PropTypes.arrayOf(PropTypes.instanceOf(Service)).isRequired, + workspace: PropTypes.instanceOf(Workspace).isRequired, }; form = this.prepareWorkspaceForm(this.props.workspace); @@ -68,6 +79,9 @@ class EditWorkspaceForm extends Component { value: workspace.name, validators: [required], }, + services: { + value: workspace.services.slice(), + }, }, }); } @@ -83,6 +97,17 @@ class EditWorkspaceForm extends Component { }); } + toggleService(service) { + const servicesField = this.form.$('services'); + const serviceIds = servicesField.value; + if (serviceIds.includes(service.id)) { + serviceIds.splice(serviceIds.indexOf(service.id), 1); + } else { + serviceIds.push(service.id); + } + servicesField.set(serviceIds); + } + render() { const { intl } = this.context; const { @@ -91,8 +116,10 @@ class EditWorkspaceForm extends Component { isSaving, onDelete, workspace, + services, } = this.props; const { form } = this; + const workspaceServices = form.$('services').value; return (
@@ -110,6 +137,17 @@ class EditWorkspaceForm extends Component {
+

{intl.formatMessage(messages.servicesInWorkspaceHeadline)}

+
+ {services.map(s => ( + this.toggleService(s)} + /> + ))} +
{/* ===== Delete Button ===== */} diff --git a/src/features/workspaces/components/ServiceListItem.js b/src/features/workspaces/components/ServiceListItem.js new file mode 100644 index 000000000..146cc5a36 --- /dev/null +++ b/src/features/workspaces/components/ServiceListItem.js @@ -0,0 +1,48 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { observer } from 'mobx-react'; +import injectSheet from 'react-jss'; +import { Toggle } from '@meetfranz/forms'; + +import Service from '../../../models/Service'; + +const styles = () => ({ + service: { + height: 'auto', + display: 'flex', + }, + name: { + marginTop: '4px', + }, +}); + +@injectSheet(styles) @observer +class ServiceListItem extends Component { + static propTypes = { + classes: PropTypes.object.isRequired, + isInWorkspace: PropTypes.bool.isRequired, + onToggle: PropTypes.func.isRequired, + service: PropTypes.instanceOf(Service).isRequired, + }; + + render() { + const { + classes, + isInWorkspace, + onToggle, + service, + } = this.props; + + return ( +
+ +
+ ); + } +} + +export default ServiceListItem; diff --git a/src/features/workspaces/containers/EditWorkspaceScreen.js b/src/features/workspaces/containers/EditWorkspaceScreen.js index 17b723303..790b8a0fe 100644 --- a/src/features/workspaces/containers/EditWorkspaceScreen.js +++ b/src/features/workspaces/containers/EditWorkspaceScreen.js @@ -5,6 +5,8 @@ import PropTypes from 'prop-types'; import ErrorBoundary from '../../../components/util/ErrorBoundary'; import EditWorkspaceForm from '../components/EditWorkspaceForm'; import { state } from '../state'; +import ServicesStore from '../../../stores/ServicesStore'; +import Workspace from '../models/Workspace'; @inject('stores', 'actions') @observer class EditWorkspaceScreen extends Component { @@ -14,6 +16,9 @@ class EditWorkspaceScreen extends Component { delete: PropTypes.func.isRequired, }), }).isRequired, + stores: PropTypes.shape({ + services: PropTypes.instanceOf(ServicesStore).isRequired, + }).isRequired, }; onDelete = () => { @@ -24,16 +29,23 @@ class EditWorkspaceScreen extends Component { }; onSave = (values) => { - console.log('save workspace', values); + const { workspaceBeingEdited } = state; + const { actions } = this.props; + const workspace = new Workspace( + Object.assign({}, workspaceBeingEdited, values), + ); + actions.workspace.update({ workspace }); }; render() { const { workspaceBeingEdited } = state; + const { stores } = this.props; if (!workspaceBeingEdited) return null; return ( { @@ -88,4 +89,15 @@ export default class WorkspacesStore extends Store { throw error; } }; + + _update = async ({ workspace }) => { + try { + await this.api.updateWorkspace(workspace); + const localWorkspace = this.state.workspaces.find(ws => ws.id === workspace.id); + Object.assign(localWorkspace, workspace); + this.stores.router.push('/settings/workspaces'); + } catch (error) { + throw error; + } + }; } diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json index e1a955176..4906070a3 100644 --- a/src/i18n/locales/de.json +++ b/src/i18n/locales/de.json @@ -224,6 +224,7 @@ "settings.workspace.form.name": "Name", "settings.workspace.form.buttonDelete": "Workspace löschen", "settings.workspace.form.buttonSave": "Workspace speichern", + "settings.workspace.form.servicesInWorkspaceHeadline": "Services in diesem Workspace", "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 fe16e916f..cd5c417e3 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -207,6 +207,7 @@ "settings.workspace.form.name": "Name", "settings.workspace.form.buttonDelete": "Delete Workspace", "settings.workspace.form.buttonSave": "Save Workspace", + "settings.workspace.form.servicesInWorkspaceHeadline": "Services in this Workspace", "subscription.type.free": "free", "subscription.type.month": "month", "subscription.type.year": "year", -- cgit v1.2.3-70-g09d2 From 739ef2e8a2dec94c3e10c3d26d797fe759fac7aa Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Fri, 1 Mar 2019 14:25:44 +0100 Subject: finish workspaces mvp --- src/actions/index.js | 8 ++- src/actions/lib/actions.js | 29 +++++---- src/components/layout/Sidebar.js | 2 +- src/components/services/tabs/Tabbar.js | 4 +- src/features/workspaces/actions.js | 9 ++- .../workspaces/containers/EditWorkspaceScreen.js | 12 ++-- .../workspaces/containers/WorkspacesScreen.js | 10 +-- src/features/workspaces/index.js | 40 +++++++++++- src/features/workspaces/state.js | 6 +- src/features/workspaces/store.js | 37 +++++++---- src/i18n/locales/de.json | 5 +- src/i18n/locales/en-US.json | 5 +- src/lib/Menu.js | 71 +++++++++++++++++++++- src/stores/ServicesStore.js | 12 ++-- 14 files changed, 193 insertions(+), 57 deletions(-) (limited to 'src/i18n/locales/de.json') diff --git a/src/actions/index.js b/src/actions/index.js index 45e6da515..00f843cd6 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 '../features/workspaces/actions'; +import workspaces from '../features/workspaces/actions'; const actions = Object.assign({}, { service, @@ -24,7 +24,9 @@ const actions = Object.assign({}, { news, settings, requests, - workspace, }); -export default defineActions(actions, PropTypes.checkPropTypes); +export default Object.assign( + defineActions(actions, PropTypes.checkPropTypes), + { workspaces }, +); diff --git a/src/actions/lib/actions.js b/src/actions/lib/actions.js index 499018d70..6571e9441 100644 --- a/src/actions/lib/actions.js +++ b/src/actions/lib/actions.js @@ -1,18 +1,23 @@ +export const createActionsFromDefinitions = (actionDefinitions, validate) => { + const actions = {}; + Object.keys(actionDefinitions).forEach((actionName) => { + const action = (params) => { + const schema = actionDefinitions[actionName]; + validate(schema, params, actionName); + action.notify(params); + }; + actions[actionName] = action; + action.listeners = []; + action.listen = listener => action.listeners.push(listener); + action.notify = params => action.listeners.forEach(listener => listener(params)); + }); + return actions; +}; + export default (definitions, validate) => { const newActions = {}; Object.keys(definitions).forEach((scopeName) => { - newActions[scopeName] = {}; - Object.keys(definitions[scopeName]).forEach((actionName) => { - const action = (params) => { - const schema = definitions[scopeName][actionName]; - validate(schema, params, actionName); - action.notify(params); - }; - newActions[scopeName][actionName] = action; - action.listeners = []; - action.listen = listener => action.listeners.push(listener); - action.notify = params => action.listeners.forEach(listener => listener(params)); - }); + newActions[scopeName] = createActionsFromDefinitions(definitions[scopeName], validate); }); return newActions; }; diff --git a/src/components/layout/Sidebar.js b/src/components/layout/Sidebar.js index 609a3b604..fcc5b0001 100644 --- a/src/components/layout/Sidebar.js +++ b/src/components/layout/Sidebar.js @@ -31,7 +31,7 @@ export default @observer class Sidebar extends Component { openSettings: PropTypes.func.isRequired, toggleMuteApp: PropTypes.func.isRequired, isAppMuted: PropTypes.bool.isRequired, - } + }; static contextTypes = { intl: intlShape, diff --git a/src/components/services/tabs/Tabbar.js b/src/components/services/tabs/Tabbar.js index dd5c2140f..5e8260ad0 100644 --- a/src/components/services/tabs/Tabbar.js +++ b/src/components/services/tabs/Tabbar.js @@ -19,7 +19,7 @@ export default @observer class TabBar extends Component { updateService: PropTypes.func.isRequired, showMessageBadgeWhenMutedSetting: PropTypes.bool.isRequired, showMessageBadgesEvenWhenMuted: PropTypes.bool.isRequired, - } + }; onSortEnd = ({ oldIndex, newIndex }) => { const { @@ -45,7 +45,7 @@ export default @observer class TabBar extends Component { redirect: false, }); } - } + }; disableService({ serviceId }) { this.toggleService({ serviceId, isEnabled: false }); diff --git a/src/features/workspaces/actions.js b/src/features/workspaces/actions.js index 84de2b011..25246de09 100644 --- a/src/features/workspaces/actions.js +++ b/src/features/workspaces/actions.js @@ -1,7 +1,8 @@ import PropTypes from 'prop-types'; import Workspace from './models/Workspace'; +import { createActionsFromDefinitions } from '../../actions/lib/actions'; -export default { +export default createActionsFromDefinitions({ edit: { workspace: PropTypes.instanceOf(Workspace).isRequired, }, @@ -14,4 +15,8 @@ export default { update: { workspace: PropTypes.instanceOf(Workspace).isRequired, }, -}; + activate: { + workspace: PropTypes.instanceOf(Workspace).isRequired, + }, + deactivate: {}, +}, PropTypes.checkPropTypes); diff --git a/src/features/workspaces/containers/EditWorkspaceScreen.js b/src/features/workspaces/containers/EditWorkspaceScreen.js index 790b8a0fe..1b13bc2d4 100644 --- a/src/features/workspaces/containers/EditWorkspaceScreen.js +++ b/src/features/workspaces/containers/EditWorkspaceScreen.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import ErrorBoundary from '../../../components/util/ErrorBoundary'; import EditWorkspaceForm from '../components/EditWorkspaceForm'; -import { state } from '../state'; +import { workspacesState } from '../state'; import ServicesStore from '../../../stores/ServicesStore'; import Workspace from '../models/Workspace'; @@ -22,23 +22,23 @@ class EditWorkspaceScreen extends Component { }; onDelete = () => { - const { workspaceBeingEdited } = state; + const { workspaceBeingEdited } = workspacesState; const { actions } = this.props; if (!workspaceBeingEdited) return null; - actions.workspace.delete({ workspace: workspaceBeingEdited }); + actions.workspaces.delete({ workspace: workspaceBeingEdited }); }; onSave = (values) => { - const { workspaceBeingEdited } = state; + const { workspaceBeingEdited } = workspacesState; const { actions } = this.props; const workspace = new Workspace( Object.assign({}, workspaceBeingEdited, values), ); - actions.workspace.update({ workspace }); + actions.workspaces.update({ workspace }); }; render() { - const { workspaceBeingEdited } = state; + const { workspaceBeingEdited } = workspacesState; const { stores } = this.props; if (!workspaceBeingEdited) return null; return ( diff --git a/src/features/workspaces/containers/WorkspacesScreen.js b/src/features/workspaces/containers/WorkspacesScreen.js index b89cbcf67..94e714255 100644 --- a/src/features/workspaces/containers/WorkspacesScreen.js +++ b/src/features/workspaces/containers/WorkspacesScreen.js @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import { inject, observer } from 'mobx-react'; import PropTypes from 'prop-types'; -import { state } from '../state'; +import { workspacesState } from '../state'; import WorkspacesDashboard from '../components/WorkspacesDashboard'; import ErrorBoundary from '../../../components/util/ErrorBoundary'; @@ -20,10 +20,10 @@ class WorkspacesScreen extends Component { return ( actions.workspace.create(data)} - onWorkspaceClick={w => actions.workspace.edit({ workspace: w })} + workspaces={workspacesState.workspaces} + isLoading={workspacesState.isLoading} + onCreateWorkspaceSubmit={data => actions.workspaces.create(data)} + onWorkspaceClick={w => actions.workspaces.edit({ workspace: w })} /> ); diff --git a/src/features/workspaces/index.js b/src/features/workspaces/index.js index 50ac3b414..8091f49fc 100644 --- a/src/features/workspaces/index.js +++ b/src/features/workspaces/index.js @@ -1,14 +1,28 @@ -import { reaction } from 'mobx'; +import { reaction, runInAction } from 'mobx'; import WorkspacesStore from './store'; import api from './api'; -import { state, resetState } from './state'; +import { workspacesState, resetState } from './state'; const debug = require('debug')('Franz:feature:workspaces'); let store = null; +export const filterServicesByActiveWorkspace = (services) => { + const { isFeatureActive, activeWorkspace } = workspacesState; + if (isFeatureActive && activeWorkspace) { + return services.filter(s => activeWorkspace.services.includes(s.id)); + } + return services; +}; + +export const getActiveWorkspaceServices = (services) => { + return filterServicesByActiveWorkspace(services); +}; + export default function initWorkspaces(stores, actions) { const { features, user } = stores; + + // Toggle workspace feature reaction( () => ( features.features.isWorkspaceEnabled && ( @@ -18,10 +32,12 @@ export default function initWorkspaces(stores, actions) { (isEnabled) => { if (isEnabled) { debug('Initializing `workspaces` feature'); - store = new WorkspacesStore(stores, api, actions, state); + store = new WorkspacesStore(stores, api, actions, workspacesState); store.initialize(); + runInAction(() => { workspacesState.isFeatureActive = true; }); } else if (store) { debug('Disabling `workspaces` feature'); + runInAction(() => { workspacesState.isFeatureActive = false; }); store.teardown(); store = null; resetState(); // Reset state to default @@ -31,4 +47,22 @@ export default function initWorkspaces(stores, actions) { fireImmediately: true, }, ); + + // Update active service on workspace switches + reaction(() => ({ + isFeatureActive: workspacesState.isFeatureActive, + activeWorkspace: workspacesState.activeWorkspace, + }), ({ isFeatureActive, activeWorkspace }) => { + if (!isFeatureActive) return; + if (activeWorkspace) { + const services = stores.services.allDisplayed; + const activeService = services.find(s => s.isActive); + const workspaceServices = filterServicesByActiveWorkspace(services); + const isActiveServiceInWorkspace = workspaceServices.includes(activeService); + if (!isActiveServiceInWorkspace) { + console.log(workspaceServices[0].id); + actions.service.setActive({ serviceId: workspaceServices[0].id }); + } + } + }); } diff --git a/src/features/workspaces/state.js b/src/features/workspaces/state.js index f938c1470..963b96f81 100644 --- a/src/features/workspaces/state.js +++ b/src/features/workspaces/state.js @@ -1,13 +1,15 @@ import { observable } from 'mobx'; const defaultState = { + activeWorkspace: null, isLoading: false, + isFeatureActive: false, workspaces: [], workspaceBeingEdited: null, }; -export const state = observable(defaultState); +export const workspacesState = observable(defaultState); export function resetState() { - Object.assign(state, defaultState); + Object.assign(workspacesState, defaultState); } diff --git a/src/features/workspaces/store.js b/src/features/workspaces/store.js index 5cccb2ab7..a2997a0d2 100644 --- a/src/features/workspaces/store.js +++ b/src/features/workspaces/store.js @@ -1,8 +1,9 @@ -import { observable, reaction } from 'mobx'; +import { observable, reaction, action } 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'; +import workspaceActions from './actions'; const debug = require('debug')('Franz:feature:workspaces'); @@ -48,28 +49,30 @@ export default class WorkspacesStore extends Store { }, ); - this.actions.workspace.edit.listen(this._edit); - this.actions.workspace.create.listen(this._create); - this.actions.workspace.delete.listen(this._delete); - this.actions.workspace.update.listen(this._update); + 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); } - _setWorkspaces = (workspaces) => { + _getWorkspaceById = id => this.state.workspaces.find(w => w.id === id); + + @action _setWorkspaces = (workspaces) => { debug('setting user workspaces', workspaces.slice()); this.state.workspaces = workspaces.map(data => new Workspace(data)); }; - _setIsLoading = (isLoading) => { + @action _setIsLoading = (isLoading) => { this.state.isLoading = isLoading; }; - _getWorkspaceById = id => this.state.workspaces.find(w => w.id === id); - - _edit = ({ workspace }) => { + @action _edit = ({ workspace }) => { this.stores.router.push(`/settings/workspaces/edit/${workspace.id}`); }; - _create = async ({ name }) => { + @action _create = async ({ name }) => { try { const result = await this.api.createWorkspace(name); const workspace = new Workspace(result); @@ -80,7 +83,7 @@ export default class WorkspacesStore extends Store { } }; - _delete = async ({ workspace }) => { + @action _delete = async ({ workspace }) => { try { await this.api.deleteWorkspace(workspace); this.state.workspaces.remove(workspace); @@ -90,7 +93,7 @@ export default class WorkspacesStore extends Store { } }; - _update = async ({ workspace }) => { + @action _update = async ({ workspace }) => { try { await this.api.updateWorkspace(workspace); const localWorkspace = this.state.workspaces.find(ws => ws.id === workspace.id); @@ -100,4 +103,12 @@ export default class WorkspacesStore extends Store { throw error; } }; + + @action _setActiveWorkspace = ({ workspace }) => { + this.state.activeWorkspace = workspace; + }; + + @action _deactivateActiveWorkspace = () => { + this.state.activeWorkspace = null; + }; } diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json index 4906070a3..0c1fb8aa6 100644 --- a/src/i18n/locales/de.json +++ b/src/i18n/locales/de.json @@ -74,6 +74,9 @@ "menu.window" : "Fenster", "menu.window.close" : "Schließen", "menu.window.minimize" : "Minimieren", + "menu.workspaces": "Workspaces", + "menu.workspaces.defaultWorkspace": "All services", + "menu.workspaces.addNewWorkspace": "Add New Workspace", "password.email.label" : "E-Mail Adresse", "password.headline" : "Passwort zurücksetzen", "password.link.login" : "An Deinem Konto anmelden", @@ -224,7 +227,7 @@ "settings.workspace.form.name": "Name", "settings.workspace.form.buttonDelete": "Workspace löschen", "settings.workspace.form.buttonSave": "Workspace speichern", - "settings.workspace.form.servicesInWorkspaceHeadline": "Services in diesem Workspace", + "settings.workspace.form.servicesInWorkspaceHeadline": "Services in diesem Workspace", "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 988ac46f2..2a51662a2 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -276,7 +276,10 @@ "menu.app.hideOthers": "Hide Others", "menu.app.unhide": "Unhide", "menu.app.quit": "Quit", - "menu.services.addNewService": "Add New Service...", + "menu.services.addNewService": "Add New Service", + "menu.workspaces": "Workspaces", + "menu.workspaces.defaultWorkspace": "All services", + "menu.workspaces.addNewWorkspace": "Add New Workspace", "validation.required": "{field} is required", "validation.email": "{field} is not valid", "validation.url": "{field} is not a valid URL", diff --git a/src/lib/Menu.js b/src/lib/Menu.js index c378619ad..1560dd285 100644 --- a/src/lib/Menu.js +++ b/src/lib/Menu.js @@ -3,6 +3,8 @@ import { observable, autorun, computed } from 'mobx'; import { defineMessages } from 'react-intl'; import { isMac, ctrlKey, cmdKey } from '../environment'; +import { workspacesState } from '../features/workspaces/state'; +import workspaceActions from '../features/workspaces/actions'; const { app, Menu, dialog } = remote; @@ -179,6 +181,18 @@ const menuItems = defineMessages({ id: 'menu.services.addNewService', defaultMessage: '!!!Add New Service...', }, + workspaces: { + id: 'menu.workspaces', + defaultMessage: '!!!Workspaces', + }, + defaultWorkspace: { + id: 'menu.workspaces.defaultWorkspace', + defaultMessage: '!!!Default', + }, + addNewWorkspace: { + id: 'menu.workspaces.addNewWorkspace', + defaultMessage: '!!!Add New Workspace...', + }, }); function getActiveWebview() { @@ -265,6 +279,10 @@ const _templateFactory = intl => [ label: intl.formatMessage(menuItems.services), submenu: [], }, + { + label: intl.formatMessage(menuItems.workspaces), + submenu: [], + }, { label: intl.formatMessage(menuItems.window), role: 'window', @@ -499,7 +517,9 @@ export default class FranzMenu { } _build() { - const serviceTpl = Object.assign([], this.serviceTpl); // need to clone object so we don't modify computed (cached) object + // need to clone object so we don't modify computed (cached) object + const serviceTpl = Object.assign([], this.serviceTpl); + const workspacesMenu = Object.assign([], this.workspacesMenu); if (window.franz === undefined) { return; @@ -632,7 +652,7 @@ export default class FranzMenu { }, ); - tpl[4].submenu.unshift(about, { + tpl[5].submenu.unshift(about, { type: 'separator', }); } else { @@ -678,6 +698,8 @@ export default class FranzMenu { tpl[3].submenu = serviceTpl; } + tpl[4].submenu = workspacesMenu; + this.currentTemplate = tpl; const menu = Menu.buildFromTemplate(tpl); Menu.setApplicationMenu(menu); @@ -701,6 +723,51 @@ export default class FranzMenu { return []; } + @computed get workspacesMenu() { + const { workspaces, activeWorkspace } = workspacesState; + const { intl } = window.franz; + const menu = []; + + // Add new workspace item: + menu.push({ + label: intl.formatMessage(menuItems.addNewWorkspace), + accelerator: `${cmdKey}+Shift+N`, + click: () => { + this.actions.ui.openSettings({ path: 'workspaces' }); + }, + enabled: this.stores.user.isLoggedIn, + }, { + type: 'separator', + }); + + // Default workspace + menu.push({ + label: intl.formatMessage(menuItems.defaultWorkspace), + accelerator: `${cmdKey}+Alt+1`, + type: 'radio', + checked: !activeWorkspace, + click: () => { + workspaceActions.deactivate(); + }, + }); + + // Workspace items + if (this.stores.user.isLoggedIn) { + workspaces.forEach((workspace, i) => menu.push({ + label: workspace.name, + accelerator: i < 9 ? `${cmdKey}+Alt+${i + 2}` : null, + type: 'radio', + checked: activeWorkspace ? workspace.id === activeWorkspace.id : false, + click: () => { + workspaceActions.activate({ workspace }); + }, + })); + } + + console.log(menu); + return menu; + } + _getServiceName(service) { if (service.name) { return service.name; diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index c63bef196..a86db8103 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js @@ -2,7 +2,7 @@ import { action, reaction, computed, - observable, + observable, runInAction, } from 'mobx'; import { debounce, remove } from 'lodash'; import ms from 'ms'; @@ -12,6 +12,8 @@ import Request from './lib/Request'; import CachedRequest from './lib/CachedRequest'; import { matchRoute } from '../helpers/routing-helpers'; import { gaEvent } from '../lib/analytics'; +import { workspacesState } from '../features/workspaces/state'; +import { filterServicesByActiveWorkspace, getActiveWorkspaceServices } from '../features/workspaces'; const debug = require('debug')('Franz:ServiceStore'); @@ -98,7 +100,6 @@ export default class ServicesStore extends Store { return observable(services.slice().slice().sort((a, b) => a.order - b.order)); } } - return []; } @@ -107,13 +108,16 @@ export default class ServicesStore extends Store { } @computed get allDisplayed() { - return this.stores.settings.all.app.showDisabledServices ? this.all : this.enabled; + const services = this.stores.settings.all.app.showDisabledServices ? this.all : this.enabled; + return filterServicesByActiveWorkspace(services); } // This is just used to avoid unnecessary rerendering of resource-heavy webviews @computed get allDisplayedUnordered() { + const { showDisabledServices } = this.stores.settings.all.app; const services = this.allServicesRequest.execute().result || []; - return this.stores.settings.all.app.showDisabledServices ? services : services.filter(service => service.isEnabled); + const filteredServices = showDisabledServices ? services : services.filter(service => service.isEnabled); + return getActiveWorkspaceServices(filteredServices); } @computed get filtered() { -- cgit v1.2.3-70-g09d2 From 93137229bf3b04e4589ae315a81ed2de7a171ded Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Thu, 21 Mar 2019 16:35:38 +0100 Subject: fix merge conflicts with latest develop --- CHANGELOG.md | 27 +++++++++++++++++++++ README.md | 5 +--- appveyor.yml | 2 +- package.json | 2 +- packages/forms/package.json | 6 ++--- packages/forms/src/button/index.tsx | 1 + packages/forms/src/input/index.tsx | 9 +++++-- packages/forms/src/label/index.tsx | 2 ++ packages/theme/package.json | 4 +-- packages/theme/src/themes/default/index.ts | 4 ++- packages/typings/package.json | 4 +-- packages/ui/package.json | 6 ++--- packages/ui/src/infobox/index.tsx | 7 +++++- src/i18n/locales/ca.json | 15 +++++------- src/i18n/locales/cs.json | 15 +++++------- src/i18n/locales/de.json | 33 ++++++++++++------------- src/i18n/locales/el.json | 15 +++++------- src/i18n/locales/es.json | 19 ++++++--------- src/i18n/locales/fr.json | 33 ++++++++++++------------- src/i18n/locales/ga.json | 15 +++++------- src/i18n/locales/hr.json | 15 +++++------- src/i18n/locales/hu.json | 15 +++++------- src/i18n/locales/id.json | 15 +++++------- src/i18n/locales/it.json | 15 +++++------- src/i18n/locales/ja.json | 15 +++++------- src/i18n/locales/ka.json | 15 +++++------- src/i18n/locales/nl-BE.json | 15 +++++------- src/i18n/locales/nl.json | 15 +++++------- src/i18n/locales/pl.json | 15 +++++------- src/i18n/locales/pt-BR.json | 33 ++++++++++++------------- src/i18n/locales/pt.json | 15 +++++------- src/i18n/locales/ru.json | 17 ++++++------- src/i18n/locales/sk.json | 15 +++++------- src/i18n/locales/sr.json | 15 +++++------- src/i18n/locales/tr.json | 39 ++++++++++++++---------------- src/i18n/locales/uk.json | 15 +++++------- src/i18n/locales/zh-TW.json | 15 +++++------- uidev/src/stories/button.stories.tsx | 5 ++++ uidev/src/stories/infobox.stories.tsx | 9 +++++++ uidev/src/stories/input.stories.tsx | 8 ++++++ 40 files changed, 267 insertions(+), 278 deletions(-) (limited to 'src/i18n/locales/de.json') diff --git a/CHANGELOG.md b/CHANGELOG.md index 641cd2b6b..998d40f4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,30 @@ +## [5.0.1-beta.1](https://github.com/meetfranz/franz/compare/v5.0.0...v5.0.1-beta.1) (2019-03-18) + + +### General + +* **Translations:** Improved translations. **[A million thanks to the amazing community. 🎉](http://i18n.meetfranz.com/)** + + +### Features + +* **App:** Add security checks for external URLs ([6e5531a](https://github.com/meetfranz/franz/commit/6e5531a)) +* **App:** Update electron to 4.0.8 ([8336d17](https://github.com/meetfranz/franz/commit/8336d17)) +* **Linux:** Add auto updater for Linux AppImage builds ([d641b4e](https://github.com/meetfranz/franz/commit/d641b4e)) +* **Spell check:** Add British English as spell check language ([#1306](https://github.com/meetfranz/franz/issues/1306)) ([67fa325](https://github.com/meetfranz/franz/commit/67fa325)) +* **Windows:** Add option to quit Franz from Taskbar icon ([952fc8b](https://github.com/meetfranz/franz/commit/952fc8b)) + +### Bug Fixes + +* **Linux:** Fix minimized window focusing ([#1304](https://github.com/meetfranz/franz/issues/1304)) ([@skoruppa](https://github.com/skoruppa)) ([5b02c4d](https://github.com/meetfranz/franz/commit/5b02c4d)) +* **Notifications:** Fix notifications & notification click when icon is blob ([03589f6](https://github.com/meetfranz/franz/commit/03589f6)) +* **Service:** Fix service zoom (cmd/ctrl+ & cmd/ctrl-) ([91a0f59](https://github.com/meetfranz/franz/commit/91a0f59)) +* **Service:** Fix shortcut for (un)muting notifications & audio ([1df3342](https://github.com/meetfranz/franz/commit/1df3342)) +* **Windows:** Fix copy & paste in service context menus ([e66fcaa](https://github.com/meetfranz/franz/commit/e66fcaa)), closes [#1316](https://github.com/meetfranz/franz/issues/1316) +* **Windows:** Fix losing window when "Keep Franz in background" is enabled ([78a3722](https://github.com/meetfranz/franz/commit/78a3722)) + + + # [5.0.0](https://github.com/meetfranz/franz/compare/5.0.0-beta.24...5.0.0) (2019-02-15) ### General diff --git a/README.md b/README.md index d44cfaa6c..5397189c6 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,6 @@ -**This repository is only for Franz 5 and later, previous versions are no longer maintained.** ---- - -# Franz 5 (beta) +# Franz 5 [![Build status Windows](https://ci.appveyor.com/api/projects/status/9yman4ye19x4274o/branch/master?svg=true)](https://ci.appveyor.com/project/adlk/franz/branch/master) [![Build Status Mac & Linux](https://travis-ci.com/meetfranz/franz.svg?branch=master)](https://travis-ci.com/meetfranz/franz) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://meetfranz.com/payment.html) diff --git a/appveyor.yml b/appveyor.yml index 70b8d6f1f..0ed8866a3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,7 +6,7 @@ environment: CSC_KEY_PASSWORD: secure: t8ypNTPKTmvRfd3hHA4aMOtC5KOFqOw3AsKhpU7140Q= -version: 5.0.0.{build} +version: 5.0.1.{build} install: - ps: Install-Product node 10 diff --git a/package.json b/package.json index df7fa4191..ec135ca77 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "franz", "productName": "Franz", "appId": "com.meetfranz.franz", - "version": "5.0.0", + "version": "5.0.1-beta.1", "description": "Messaging app for WhatsApp, Slack, Telegram, HipChat, Hangouts and many many more.", "copyright": "adlk x franz - Stefan Malzner", "main": "index.js", diff --git a/packages/forms/package.json b/packages/forms/package.json index e78929777..fe161a282 100644 --- a/packages/forms/package.json +++ b/packages/forms/package.json @@ -1,6 +1,6 @@ { "name": "@meetfranz/forms", - "version": "1.0.9", + "version": "1.0.13", "description": "React form components for Franz", "main": "lib/index.js", "scripts": { @@ -25,7 +25,7 @@ "dependencies": { "@mdi/js": "^3.3.92", "@mdi/react": "^1.1.0", - "@meetfranz/theme": "^1.0.7", + "@meetfranz/theme": "^1.0.11", "react-html-attributes": "^1.4.3", "react-loader": "^2.4.5" }, @@ -35,5 +35,5 @@ "react-dom": "16.7.0", "react-jss": "^8.6.1" }, - "gitHead": "14b151cad6a5a849bb476aaa3fc53bf1eead7f4b" + "gitHead": "27778954921365e4957eae964e28f68690f3825f" } diff --git a/packages/forms/src/button/index.tsx b/packages/forms/src/button/index.tsx index 7a7f83dab..6959cde73 100644 --- a/packages/forms/src/button/index.tsx +++ b/packages/forms/src/button/index.tsx @@ -44,6 +44,7 @@ const styles = (theme: Theme) => ({ width: (props: IProps) => (props.stretch ? '100%' : 'auto') as CSS.WidthProperty, fontSize: theme.uiFontSize, textDecoration: 'none', + height: theme.buttonHeight, '&:hover': { opacity: 0.8, diff --git a/packages/forms/src/input/index.tsx b/packages/forms/src/input/index.tsx index cc3709b1a..f89c91be5 100644 --- a/packages/forms/src/input/index.tsx +++ b/packages/forms/src/input/index.tsx @@ -34,7 +34,7 @@ interface IState { } class InputComponent extends Component { - public static defaultProps = { + static defaultProps = { focus: false, onChange: () => {}, onBlur: () => {}, @@ -109,8 +109,10 @@ class InputComponent extends Component { placeholder, spellCheck, onBlur, - onEnterKey, onFocus, + min, + max, + step, } = this.props; const { @@ -157,6 +159,9 @@ class InputComponent extends Component { onBlur={onBlur} disabled={disabled} onKeyPress={this.onInputKeyPress.bind(this)} + min={min} + max={max} + step={step} /> {suffix && ( diff --git a/packages/forms/src/label/index.tsx b/packages/forms/src/label/index.tsx index 36fcfbedf..590270a06 100644 --- a/packages/forms/src/label/index.tsx +++ b/packages/forms/src/label/index.tsx @@ -26,6 +26,8 @@ class LabelComponent extends Component { htmlFor, } = this.props; + if (!showLabel) return children; + return (