From 0bf13689d53bd493fb4d0a4213c1801013b5aa8a Mon Sep 17 00:00:00 2001 From: Ricardo Cino Date: Mon, 27 Jun 2022 18:21:31 +0200 Subject: chore: transform containers/settings from js to tsx (#384) --- src/containers/auth/AuthLayoutContainer.tsx | 6 +- src/containers/auth/ChangeServerScreen.tsx | 6 +- src/containers/auth/ImportScreen.tsx | 4 +- src/containers/auth/InviteScreen.tsx | 4 +- src/containers/auth/LockedScreen.tsx | 6 +- src/containers/auth/LoginScreen.tsx | 4 +- src/containers/auth/PasswordScreen.tsx | 4 +- src/containers/auth/SetupAssistantScreen.tsx | 4 +- src/containers/auth/SignupScreen.tsx | 4 +- src/containers/auth/WelcomeScreen.tsx | 4 +- src/containers/layout/AppLayoutContainer.js | 207 ------ src/containers/layout/AppLayoutContainer.tsx | 172 +++++ src/containers/settings/AccountScreen.js | 88 --- src/containers/settings/AccountScreen.tsx | 71 ++ src/containers/settings/EditServiceScreen.js | 472 ------------- src/containers/settings/EditServiceScreen.tsx | 480 +++++++++++++ src/containers/settings/EditSettingsScreen.js | 918 ------------------------- src/containers/settings/EditSettingsScreen.tsx | 906 ++++++++++++++++++++++++ src/containers/settings/EditUserScreen.js | 162 ----- src/containers/settings/EditUserScreen.tsx | 158 +++++ src/containers/settings/InviteScreen.js | 42 -- src/containers/settings/InviteScreen.tsx | 32 + src/containers/settings/RecipesScreen.js | 204 ------ src/containers/settings/RecipesScreen.tsx | 188 +++++ src/containers/settings/ServicesScreen.js | 69 -- src/containers/settings/ServicesScreen.tsx | 53 ++ src/containers/settings/SettingsWindow.js | 59 -- src/containers/settings/SettingsWindow.tsx | 58 ++ src/containers/settings/TeamScreen.js | 57 -- src/containers/settings/TeamScreen.tsx | 48 ++ 30 files changed, 2189 insertions(+), 2301 deletions(-) delete mode 100644 src/containers/layout/AppLayoutContainer.js create mode 100644 src/containers/layout/AppLayoutContainer.tsx delete mode 100644 src/containers/settings/AccountScreen.js create mode 100644 src/containers/settings/AccountScreen.tsx delete mode 100644 src/containers/settings/EditServiceScreen.js create mode 100644 src/containers/settings/EditServiceScreen.tsx delete mode 100644 src/containers/settings/EditSettingsScreen.js create mode 100644 src/containers/settings/EditSettingsScreen.tsx delete mode 100644 src/containers/settings/EditUserScreen.js create mode 100644 src/containers/settings/EditUserScreen.tsx delete mode 100644 src/containers/settings/InviteScreen.js create mode 100644 src/containers/settings/InviteScreen.tsx delete mode 100644 src/containers/settings/RecipesScreen.js create mode 100644 src/containers/settings/RecipesScreen.tsx delete mode 100644 src/containers/settings/ServicesScreen.js create mode 100644 src/containers/settings/ServicesScreen.tsx delete mode 100644 src/containers/settings/SettingsWindow.js create mode 100644 src/containers/settings/SettingsWindow.tsx delete mode 100644 src/containers/settings/TeamScreen.js create mode 100644 src/containers/settings/TeamScreen.tsx (limited to 'src/containers') diff --git a/src/containers/auth/AuthLayoutContainer.tsx b/src/containers/auth/AuthLayoutContainer.tsx index 8d65ec6f4..abe3905e0 100644 --- a/src/containers/auth/AuthLayoutContainer.tsx +++ b/src/containers/auth/AuthLayoutContainer.tsx @@ -2,14 +2,14 @@ import { Component, ReactElement, ReactNode } from 'react'; import { inject, observer } from 'mobx-react'; import { ThemeProvider } from 'react-jss'; -import { DefaultProps } from 'src/@types/ferdium-components.types'; +import { StoresProps } from 'src/@types/ferdium-components.types'; import { Location } from 'mobx-react-router'; import AuthLayout from '../../components/auth/AuthLayout'; import AppLoader from '../../components/ui/AppLoader'; -interface AuthLayoutContainerProps extends DefaultProps { +interface AuthLayoutContainerProps extends StoresProps { location: Location; - children: ReactNode[] | ReactNode; + children: ReactNode; } class AuthLayoutContainer extends Component { diff --git a/src/containers/auth/ChangeServerScreen.tsx b/src/containers/auth/ChangeServerScreen.tsx index 6af87e4a1..6d0feaecb 100644 --- a/src/containers/auth/ChangeServerScreen.tsx +++ b/src/containers/auth/ChangeServerScreen.tsx @@ -1,10 +1,10 @@ import { Component, ReactElement } from 'react'; import { inject, observer } from 'mobx-react'; -import { DefaultProps } from 'src/@types/ferdium-components.types'; +import { StoresProps } from 'src/@types/ferdium-components.types'; import ChangeServer from '../../components/auth/ChangeServer'; -class ChangeServerScreen extends Component { - constructor(props: DefaultProps) { +class ChangeServerScreen extends Component { + constructor(props: StoresProps) { super(props); this.onSubmit = this.onSubmit.bind(this); diff --git a/src/containers/auth/ImportScreen.tsx b/src/containers/auth/ImportScreen.tsx index c128dec14..3522ce0ce 100644 --- a/src/containers/auth/ImportScreen.tsx +++ b/src/containers/auth/ImportScreen.tsx @@ -1,9 +1,9 @@ import { Component, ReactElement } from 'react'; import { inject, observer } from 'mobx-react'; -import { DefaultProps } from 'src/@types/ferdium-components.types'; +import { StoresProps } from 'src/@types/ferdium-components.types'; import Import from '../../components/auth/Import'; -class ImportScreen extends Component { +class ImportScreen extends Component { render(): ReactElement { const { actions, stores } = this.props; diff --git a/src/containers/auth/InviteScreen.tsx b/src/containers/auth/InviteScreen.tsx index a2c684f41..40ebdba8d 100644 --- a/src/containers/auth/InviteScreen.tsx +++ b/src/containers/auth/InviteScreen.tsx @@ -1,9 +1,9 @@ import { Component, ReactElement } from 'react'; import { inject, observer } from 'mobx-react'; -import { DefaultProps } from 'src/@types/ferdium-components.types'; +import { StoresProps } from 'src/@types/ferdium-components.types'; import Invite from '../../components/auth/Invite'; -class InviteScreen extends Component { +class InviteScreen extends Component { render(): ReactElement { const { actions } = this.props; diff --git a/src/containers/auth/LockedScreen.tsx b/src/containers/auth/LockedScreen.tsx index 8e3c1ec49..88f743d02 100644 --- a/src/containers/auth/LockedScreen.tsx +++ b/src/containers/auth/LockedScreen.tsx @@ -1,16 +1,16 @@ import { Component, ReactElement } from 'react'; import { inject, observer } from 'mobx-react'; -import { DefaultProps } from 'src/@types/ferdium-components.types'; +import { StoresProps } from 'src/@types/ferdium-components.types'; import Locked from '../../components/auth/Locked'; import { hash } from '../../helpers/password-helpers'; -class LockedScreen extends Component { +class LockedScreen extends Component { state = { error: false, }; - constructor(props: DefaultProps) { + constructor(props: StoresProps) { super(props); this.onSubmit = this.onSubmit.bind(this); diff --git a/src/containers/auth/LoginScreen.tsx b/src/containers/auth/LoginScreen.tsx index 4c5271fe1..c2efd27eb 100644 --- a/src/containers/auth/LoginScreen.tsx +++ b/src/containers/auth/LoginScreen.tsx @@ -1,9 +1,9 @@ import { Component, ReactElement } from 'react'; import { inject, observer } from 'mobx-react'; -import { DefaultProps, GlobalError } from 'src/@types/ferdium-components.types'; +import { StoresProps, GlobalError } from 'src/@types/ferdium-components.types'; import Login from '../../components/auth/Login'; -interface LoginScreenProps extends DefaultProps { +interface LoginScreenProps extends StoresProps { error: GlobalError; } diff --git a/src/containers/auth/PasswordScreen.tsx b/src/containers/auth/PasswordScreen.tsx index 3176e5a8b..313554802 100644 --- a/src/containers/auth/PasswordScreen.tsx +++ b/src/containers/auth/PasswordScreen.tsx @@ -1,9 +1,9 @@ import { Component, ReactElement } from 'react'; import { inject, observer } from 'mobx-react'; -import { DefaultProps } from 'src/@types/ferdium-components.types'; +import { StoresProps } from 'src/@types/ferdium-components.types'; import Password from '../../components/auth/Password'; -class PasswordScreen extends Component { +class PasswordScreen extends Component { render(): ReactElement { const { actions, stores } = this.props; diff --git a/src/containers/auth/SetupAssistantScreen.tsx b/src/containers/auth/SetupAssistantScreen.tsx index 92f12c0bc..f7a353f26 100644 --- a/src/containers/auth/SetupAssistantScreen.tsx +++ b/src/containers/auth/SetupAssistantScreen.tsx @@ -2,11 +2,11 @@ import { Component, ReactElement } from 'react'; import { inject, observer } from 'mobx-react'; -import { DefaultProps } from 'src/@types/ferdium-components.types'; +import { StoresProps } from 'src/@types/ferdium-components.types'; import { sleep } from '../../helpers/async-helpers'; import SetupAssistant from '../../components/auth/SetupAssistant'; -class SetupAssistantScreen extends Component { +class SetupAssistantScreen extends Component { state = { isSettingUpServices: false, }; diff --git a/src/containers/auth/SignupScreen.tsx b/src/containers/auth/SignupScreen.tsx index 1dac392ef..04b39df67 100644 --- a/src/containers/auth/SignupScreen.tsx +++ b/src/containers/auth/SignupScreen.tsx @@ -1,10 +1,10 @@ import { Component, ReactElement } from 'react'; import { inject, observer } from 'mobx-react'; -import { DefaultProps, GlobalError } from 'src/@types/ferdium-components.types'; +import { StoresProps, GlobalError } from 'src/@types/ferdium-components.types'; import Signup from '../../components/auth/Signup'; -interface SignUpScreenComponents extends DefaultProps { +interface SignUpScreenComponents extends StoresProps { error: GlobalError; } diff --git a/src/containers/auth/WelcomeScreen.tsx b/src/containers/auth/WelcomeScreen.tsx index bbd73f4a2..bbd5b4825 100644 --- a/src/containers/auth/WelcomeScreen.tsx +++ b/src/containers/auth/WelcomeScreen.tsx @@ -1,10 +1,10 @@ import { Component, ReactElement } from 'react'; import { inject, observer } from 'mobx-react'; -import { DefaultProps } from 'src/@types/ferdium-components.types'; +import { StoresProps } from 'src/@types/ferdium-components.types'; import Welcome from '../../components/auth/Welcome'; -class WelcomeScreen extends Component { +class WelcomeScreen extends Component { render(): ReactElement { const { user, recipePreviews } = this.props.stores; diff --git a/src/containers/layout/AppLayoutContainer.js b/src/containers/layout/AppLayoutContainer.js deleted file mode 100644 index 1021ab4fa..000000000 --- a/src/containers/layout/AppLayoutContainer.js +++ /dev/null @@ -1,207 +0,0 @@ -import { Children, Component } from 'react'; -import PropTypes from 'prop-types'; -import { inject, observer } from 'mobx-react'; -import { ThemeProvider } from 'react-jss'; - -import AppStore from '../../stores/AppStore'; -import RecipesStore from '../../stores/RecipesStore'; -import ServicesStore from '../../stores/ServicesStore'; -import FeaturesStore from '../../stores/FeaturesStore'; -import UIStore from '../../stores/UIStore'; -import SettingsStore from '../../stores/SettingsStore'; -import UserStore from '../../stores/UserStore'; -import RequestStore from '../../stores/RequestStore'; -import GlobalErrorStore from '../../stores/GlobalErrorStore'; - -import { oneOrManyChildElements } from '../../prop-types'; -import AppLayout from '../../components/layout/AppLayout'; -import Sidebar from '../../components/layout/Sidebar'; -import Services from '../../components/services/content/Services'; -import AppLoader from '../../components/ui/AppLoader'; - -import { workspaceActions } from '../../features/workspaces/actions'; -import WorkspaceDrawer from '../../features/workspaces/components/WorkspaceDrawer'; -import { workspaceStore } from '../../features/workspaces'; - -class AppLayoutContainer extends Component { - static defaultProps = { - children: null, - }; - - render() { - const { - app, - features, - services, - ui, - settings, - globalError, - requests, - user, - router - } = this.props.stores; - - /* HOTFIX for: - [mobx] Encountered an uncaught exception that was thrown by a reaction or observer component, in: 'Reaction[bound ]' TypeError: Cannot read properties of null (reading 'push') - at RouterStore.push (store.js:25) - at UserStore._requireAuthenticatedUser - */ - if (!user.isLoggedIn) { - router.push('/auth/welcome'); - } - - const { - setActive, - handleIPCMessage, - setWebviewReference, - detachService, - openWindow, - reorder, - reload, - toggleNotifications, - toggleAudio, - toggleDarkMode, - deleteService, - updateService, - hibernate, - awake, - } = this.props.actions.service; - - const { retryRequiredRequests } = this.props.actions.requests; - - const { installUpdate, toggleMuteApp, toggleCollapseMenu } = this.props.actions.app; - - const { openSettings, closeSettings } = this.props.actions.ui; - - const { children } = this.props; - - const isLoadingFeatures = - features.featuresRequest.isExecuting && - !features.featuresRequest.wasExecuted; - - const isLoadingServices = - services.allServicesRequest.isExecuting && - services.allServicesRequest.isExecutingFirstTime; - - const isLoadingSettings = !settings.loaded; - - if ( - isLoadingSettings || - isLoadingFeatures || - isLoadingServices - ) { - return ( - - - - ); - } - - const workspacesDrawer = ( - - workspace - ? workspaceStore.getWorkspaceServices(workspace).map(s => s.name) - : services.all.map(s => s.name) - } - /> - ); - - const sidebar = ( - - ); - - const servicesContainer = ( - - ); - - return ( - - - {Children.count(children) > 0 ? children : null} - - - ); - } -} - -AppLayoutContainer.propTypes = { - stores: PropTypes.shape({ - services: PropTypes.instanceOf(ServicesStore).isRequired, - features: PropTypes.instanceOf(FeaturesStore).isRequired, - recipes: PropTypes.instanceOf(RecipesStore).isRequired, - app: PropTypes.instanceOf(AppStore).isRequired, - ui: PropTypes.instanceOf(UIStore).isRequired, - settings: PropTypes.instanceOf(SettingsStore).isRequired, - user: PropTypes.instanceOf(UserStore).isRequired, - requests: PropTypes.instanceOf(RequestStore).isRequired, - globalError: PropTypes.instanceOf(GlobalErrorStore).isRequired, - }).isRequired, - actions: PropTypes.shape({ - service: PropTypes.instanceOf(ServicesStore).isRequired, - ui: PropTypes.instanceOf(UIStore).isRequired, - app: PropTypes.instanceOf(AppStore).isRequired, - requests: PropTypes.instanceOf(RequestStore).isRequired, - }).isRequired, - // eslint-disable-next-line react/require-default-props - children: oneOrManyChildElements, -}; - -export default inject('stores', 'actions')(observer(AppLayoutContainer)); diff --git a/src/containers/layout/AppLayoutContainer.tsx b/src/containers/layout/AppLayoutContainer.tsx new file mode 100644 index 000000000..c6a9dfb0d --- /dev/null +++ b/src/containers/layout/AppLayoutContainer.tsx @@ -0,0 +1,172 @@ +import { Children, Component, ReactElement, ReactNode } from 'react'; +import { inject, observer } from 'mobx-react'; +import { ThemeProvider } from 'react-jss'; + +import { StoresProps } from 'src/@types/ferdium-components.types'; +import AppLayout from '../../components/layout/AppLayout'; +import Sidebar from '../../components/layout/Sidebar'; +import Services from '../../components/services/content/Services'; +import AppLoader from '../../components/ui/AppLoader'; + +import WorkspaceDrawer from '../../features/workspaces/components/WorkspaceDrawer'; +import { workspaceStore } from '../../features/workspaces'; + +interface AppLayoutContainerProps extends StoresProps { + children: ReactNode; +} + +class AppLayoutContainer extends Component { + render(): ReactElement { + const { + app, + features, + services, + ui, + settings, + globalError, + requests, + user, + router, + } = this.props.stores; + + /* HOTFIX for: + [mobx] Encountered an uncaught exception that was thrown by a reaction or observer component, in: 'Reaction[bound ]' TypeError: Cannot read properties of null (reading 'push') + at RouterStore.push (store.js:25) + at UserStore._requireAuthenticatedUser + */ + if (!user.isLoggedIn) { + router.push('/auth/welcome'); + } + + const { + setActive, + handleIPCMessage, + setWebviewReference, + detachService, + openWindow, + reorder, + reload, + toggleNotifications, + toggleAudio, + toggleDarkMode, + deleteService, + updateService, + hibernate, + awake, + } = this.props.actions.service; + + const { retryRequiredRequests } = this.props.actions.requests; + + const { installUpdate, toggleMuteApp, toggleCollapseMenu } = + this.props.actions.app; + + const { openSettings, closeSettings } = this.props.actions.ui; + + const { children } = this.props; + + const isLoadingFeatures = + features.featuresRequest.isExecuting && + !features.featuresRequest.wasExecuted; + + const isLoadingServices = + services.allServicesRequest.isExecuting && + services.allServicesRequest.isExecutingFirstTime; + + const isLoadingSettings = !settings.loaded; + + if (isLoadingSettings || isLoadingFeatures || isLoadingServices) { + return ( + + + + ); + } + + const workspacesDrawer = ( + + workspace + ? workspaceStore.getWorkspaceServices(workspace).map(s => s.name) + : services.all.map(s => s.name) + } + /> + ); + + const sidebar = ( + + ); + + const servicesContainer = ( + + ); + + return ( + + + {Children.count(children) > 0 ? children : null} + + + ); + } +} + +export default inject('stores', 'actions')(observer(AppLayoutContainer)); diff --git a/src/containers/settings/AccountScreen.js b/src/containers/settings/AccountScreen.js deleted file mode 100644 index aae230577..000000000 --- a/src/containers/settings/AccountScreen.js +++ /dev/null @@ -1,88 +0,0 @@ -import { Component } from 'react'; -import PropTypes from 'prop-types'; -import { inject, observer } from 'mobx-react'; - -import UserStore from '../../stores/UserStore'; -import AppStore from '../../stores/AppStore'; -import FeaturesStore from '../../stores/FeaturesStore'; -import SettingsStore from '../../stores/SettingsStore'; - -import AccountDashboard from '../../components/settings/account/AccountDashboard'; -import ErrorBoundary from '../../components/util/ErrorBoundary'; -import { LIVE_FRANZ_API } from '../../config'; -import { WEBSITE } from '../../environment-remote'; - -class AccountScreen extends Component { - onCloseWindow() { - const { user, features } = this.props.stores; - user.getUserInfoRequest.invalidate({ immediately: true }); - features.featuresRequest.invalidate({ immediately: true }); - } - - reloadData() { - const { user } = this.props.stores; - - user.getUserInfoRequest.reload(); - } - - handleWebsiteLink(route) { - const { actions, stores } = this.props; - - const api = stores.settings.all.app.server; - - const url = - api === LIVE_FRANZ_API - ? stores.user.getAuthURL( - `${WEBSITE}${route}?utm_source=app&utm_medium=account_dashboard`, - ) - : `${api}${route}`; - - actions.app.openExternalUrl({ url }); - } - - render() { - const { user, settings } = this.props.stores; - const { user: userActions } = this.props.actions; - - const isLoadingUserInfo = user.getUserInfoRequest.isExecuting; - - return ( - - this.reloadData()} - onCloseSubscriptionWindow={() => this.onCloseWindow()} - deleteAccount={userActions.delete} - isLoadingDeleteAccount={user.deleteAccountRequest.isExecuting} - isDeleteAccountSuccessful={ - user.deleteAccountRequest.wasExecuted && - !user.deleteAccountRequest.isError - } - openEditAccount={() => this.handleWebsiteLink('/user/profile')} - openInvoices={() => this.handleWebsiteLink('/user/invoices')} - /> - - ); - } -} - -AccountScreen.propTypes = { - stores: PropTypes.shape({ - user: PropTypes.instanceOf(UserStore).isRequired, - features: PropTypes.instanceOf(FeaturesStore).isRequired, - settings: PropTypes.instanceOf(SettingsStore).isRequired, - app: PropTypes.instanceOf(AppStore).isRequired, - }).isRequired, - actions: PropTypes.shape({ - app: PropTypes.instanceOf(AppStore).isRequired, - user: PropTypes.instanceOf(UserStore).isRequired, - }).isRequired, -}; - -export default inject('stores', 'actions')(observer(AccountScreen)); diff --git a/src/containers/settings/AccountScreen.tsx b/src/containers/settings/AccountScreen.tsx new file mode 100644 index 000000000..480b61f6c --- /dev/null +++ b/src/containers/settings/AccountScreen.tsx @@ -0,0 +1,71 @@ +import { Component, ReactElement } from 'react'; +import { inject, observer } from 'mobx-react'; + +import { StoresProps } from 'src/@types/ferdium-components.types'; + +import AccountDashboard from '../../components/settings/account/AccountDashboard'; +import ErrorBoundary from '../../components/util/ErrorBoundary'; +import { LIVE_FRANZ_API } from '../../config'; +import { WEBSITE } from '../../environment-remote'; + +class AccountScreen extends Component { + onCloseWindow(): void { + const { user, features } = this.props.stores; + user.getUserInfoRequest.invalidate({ immediately: true }); + features.featuresRequest.invalidate({ immediately: true }); + } + + reloadData(): void { + const { user } = this.props.stores; + + user.getUserInfoRequest.reload(); + } + + handleWebsiteLink(route: string): void { + const { actions, stores } = this.props; + + const api: string = stores.settings.all.app.server; + + const url: string = + api === LIVE_FRANZ_API + ? stores.user.getAuthURL( + `${WEBSITE}${route}?utm_source=app&utm_medium=account_dashboard`, + ) + : `${api}${route}`; + + actions.app.openExternalUrl({ url }); + } + + render(): ReactElement { + const { user, settings } = this.props.stores; + const { user: userActions } = this.props.actions; + + const isLoadingUserInfo = user.getUserInfoRequest.isExecuting; + + return ( + + this.reloadData()} + onCloseSubscriptionWindow={() => this.onCloseWindow()} + deleteAccount={userActions.delete} + isLoadingDeleteAccount={user.deleteAccountRequest.isExecuting} + isDeleteAccountSuccessful={ + user.deleteAccountRequest.wasExecuted && + !user.deleteAccountRequest.isError + } + openEditAccount={() => this.handleWebsiteLink('/user/profile')} + openInvoices={() => this.handleWebsiteLink('/user/invoices')} + /> + + ); + } +} + +export default inject('stores', 'actions')(observer(AccountScreen)); diff --git a/src/containers/settings/EditServiceScreen.js b/src/containers/settings/EditServiceScreen.js deleted file mode 100644 index 4615a9cdf..000000000 --- a/src/containers/settings/EditServiceScreen.js +++ /dev/null @@ -1,472 +0,0 @@ -import { Component } from 'react'; -import PropTypes from 'prop-types'; -import { inject, observer } from 'mobx-react'; -import { defineMessages, injectIntl } from 'react-intl'; - -import { RouterStore } from 'mobx-react-router'; -import UserStore from '../../stores/UserStore'; -import RecipesStore from '../../stores/RecipesStore'; -import ServicesStore from '../../stores/ServicesStore'; -import SettingsStore from '../../stores/SettingsStore'; -import Form from '../../lib/Form'; - -import ServiceError from '../../components/settings/services/ServiceError'; -import EditServiceForm from '../../components/settings/services/EditServiceForm'; -import ErrorBoundary from '../../components/util/ErrorBoundary'; - -import { required, url, oneRequired } from '../../helpers/validation-helpers'; -import { getSelectOptions } from '../../helpers/i18n-helpers'; - -import { config as proxyFeature } from '../../features/serviceProxy'; - -import { SPELLCHECKER_LOCALES } from '../../i18n/languages'; - -import globalMessages from '../../i18n/globalMessages'; -import { DEFAULT_APP_SETTINGS, DEFAULT_SERVICE_SETTINGS } from '../../config'; - -const messages = defineMessages({ - name: { - id: 'settings.service.form.name', - defaultMessage: 'Name', - }, - enableService: { - id: 'settings.service.form.enableService', - defaultMessage: 'Enable service', - }, - enableHibernation: { - id: 'settings.service.form.enableHibernation', - defaultMessage: 'Enable hibernation', - }, - enableWakeUp: { - id: 'settings.service.form.enableWakeUp', - defaultMessage: 'Enable wake up', - }, - enableNotification: { - id: 'settings.service.form.enableNotification', - defaultMessage: 'Enable notifications', - }, - enableBadge: { - id: 'settings.service.form.enableBadge', - defaultMessage: 'Show unread message badges', - }, - enableAudio: { - id: 'settings.service.form.enableAudio', - defaultMessage: 'Enable audio', - }, - team: { - id: 'settings.service.form.team', - defaultMessage: 'Team', - }, - customUrl: { - id: 'settings.service.form.customUrl', - defaultMessage: 'Custom server', - }, - indirectMessages: { - id: 'settings.service.form.indirectMessages', - defaultMessage: 'Show message badge for all new messages', - }, - icon: { - id: 'settings.service.form.icon', - defaultMessage: 'Custom icon', - }, - enableDarkMode: { - id: 'settings.service.form.enableDarkMode', - defaultMessage: 'Enable Dark Mode', - }, - darkReaderBrightness: { - id: 'settings.service.form.darkReaderBrightness', - defaultMessage: 'Dark Reader Brightness', - }, - darkReaderContrast: { - id: 'settings.service.form.darkReaderContrast', - defaultMessage: 'Dark Reader Contrast', - }, - darkReaderSepia: { - id: 'settings.service.form.darkReaderSepia', - defaultMessage: 'Dark Reader Sepia', - }, - enableProgressbar: { - id: 'settings.service.form.enableProgressbar', - defaultMessage: 'Enable Progress bar', - }, - trapLinkClicks: { - id: 'settings.service.form.trapLinkClicks', - defaultMessage: 'Open URLs within Ferdium', - }, - onlyShowFavoritesInUnreadCount: { - id: 'settings.service.form.onlyShowFavoritesInUnreadCount', - defaultMessage: 'Only show Favorites in unread count', - }, - enableProxy: { - id: 'settings.service.form.proxy.isEnabled', - defaultMessage: 'Use Proxy', - }, - proxyHost: { - id: 'settings.service.form.proxy.host', - defaultMessage: 'Proxy Host/IP', - }, - proxyPort: { - id: 'settings.service.form.proxy.port', - defaultMessage: 'Port', - }, - proxyUser: { - id: 'settings.service.form.proxy.user', - defaultMessage: 'User (optional)', - }, - proxyPassword: { - id: 'settings.service.form.proxy.password', - defaultMessage: 'Password (optional)', - }, -}); - -class EditServiceScreen extends Component { - onSubmit(data) { - const { action } = this.props.router.params; - const { recipes, services } = this.props.stores; - const { createService, updateService } = this.props.actions.service; - data.darkReaderSettings = { - brightness: data.darkReaderBrightness, - contrast: data.darkReaderContrast, - sepia: data.darkReaderSepia, - }; - delete data.darkReaderContrast; - delete data.darkReaderBrightness; - delete data.darkReaderSepia; - - const serviceData = data; - serviceData.isMuted = !serviceData.isMuted; - - if (action === 'edit') { - updateService({ serviceId: services.activeSettings.id, serviceData }); - } else { - createService({ recipeId: recipes.active.id, serviceData }); - } - } - - prepareForm(recipe, service, proxy) { - const { intl } = this.props; - - const { stores, router } = this.props; - - const { action } = router.params; - - let defaultSpellcheckerLanguage = - SPELLCHECKER_LOCALES[stores.settings.app.spellcheckerLanguage]; - - if (stores.settings.app.spellcheckerLanguage === 'automatic') { - defaultSpellcheckerLanguage = intl.formatMessage( - globalMessages.spellcheckerAutomaticDetectionShort, - ); - } - - const spellcheckerLanguage = getSelectOptions({ - locales: SPELLCHECKER_LOCALES, - resetToDefaultText: intl.formatMessage( - globalMessages.spellcheckerSystemDefault, - { default: defaultSpellcheckerLanguage }, - ), - automaticDetectionText: - stores.settings.app.spellcheckerLanguage !== 'automatic' - ? intl.formatMessage(globalMessages.spellcheckerAutomaticDetection) - : '', - }); - - const config = { - fields: { - name: { - label: intl.formatMessage(messages.name), - placeholder: intl.formatMessage(messages.name), - value: service.id ? service.name : recipe.name, - }, - isEnabled: { - label: intl.formatMessage(messages.enableService), - value: service.isEnabled, - default: DEFAULT_SERVICE_SETTINGS.isEnabled, - }, - isHibernationEnabled: { - label: intl.formatMessage(messages.enableHibernation), - value: - action !== 'edit' - ? recipe.autoHibernate - : service.isHibernationEnabled, - default: DEFAULT_SERVICE_SETTINGS.isHibernationEnabled, - }, - isWakeUpEnabled: { - label: intl.formatMessage(messages.enableWakeUp), - value: service.isWakeUpEnabled, - default: DEFAULT_SERVICE_SETTINGS.isWakeUpEnabled, - }, - isNotificationEnabled: { - label: intl.formatMessage(messages.enableNotification), - value: service.isNotificationEnabled, - default: DEFAULT_SERVICE_SETTINGS.isNotificationEnabled, - }, - isBadgeEnabled: { - label: intl.formatMessage(messages.enableBadge), - value: service.isBadgeEnabled, - default: DEFAULT_SERVICE_SETTINGS.isBadgeEnabled, - }, - trapLinkClicks: { - label: intl.formatMessage(messages.trapLinkClicks), - value: service.trapLinkClicks, - default: DEFAULT_SERVICE_SETTINGS.trapLinkClicks, - }, - isMuted: { - label: intl.formatMessage(messages.enableAudio), - value: !service.isMuted, - default: DEFAULT_SERVICE_SETTINGS.isMuted, - }, - customIcon: { - label: intl.formatMessage(messages.icon), - value: service.hasCustomUploadedIcon ? service.icon : false, - default: null, - type: 'file', - }, - isDarkModeEnabled: { - label: intl.formatMessage(messages.enableDarkMode), - value: service.isDarkModeEnabled, - default: stores.settings.app.darkMode, - }, - darkReaderBrightness: { - label: intl.formatMessage(messages.darkReaderBrightness), - value: service.darkReaderSettings - ? service.darkReaderSettings.brightness - : undefined, - default: 100, - }, - darkReaderContrast: { - label: intl.formatMessage(messages.darkReaderContrast), - value: service.darkReaderSettings - ? service.darkReaderSettings.contrast - : undefined, - default: 90, - }, - darkReaderSepia: { - label: intl.formatMessage(messages.darkReaderSepia), - value: service.darkReaderSettings - ? service.darkReaderSettings.sepia - : undefined, - default: 10, - }, - isProgressbarEnabled: { - label: intl.formatMessage(messages.enableProgressbar), - value: service.isProgressbarEnabled, - default: DEFAULT_SERVICE_SETTINGS.isProgressbarEnabled, - }, - spellcheckerLanguage: { - label: intl.formatMessage(globalMessages.spellcheckerLanguage), - value: service.spellcheckerLanguage, - options: spellcheckerLanguage, - disabled: !stores.settings.app.enableSpellchecking, - }, - userAgentPref: { - label: intl.formatMessage(globalMessages.userAgentPref), - placeholder: service.defaultUserAgent, - value: service.userAgentPref ? service.userAgentPref : '', - }, - }, - }; - - if (recipe.hasTeamId) { - Object.assign(config.fields, { - team: { - label: intl.formatMessage(messages.team), - placeholder: intl.formatMessage(messages.team), - value: service.team, - validators: [required], - }, - }); - } - - if (recipe.hasCustomUrl) { - Object.assign(config.fields, { - customUrl: { - label: intl.formatMessage(messages.customUrl), - placeholder: "'http://' or 'https://' or 'file:///'", - value: service.customUrl || recipe.serviceURL, - validators: [required, url], - }, - }); - } - - // More fine grained and use case specific validation rules - if (recipe.hasTeamId && recipe.hasCustomUrl) { - config.fields.team.validators = [oneRequired(['team', 'customUrl'])]; - config.fields.customUrl.validators = [ - url, - oneRequired(['team', 'customUrl']), - ]; - } - - // If a service can be hosted and has a teamId or customUrl - if (recipe.hasHostedOption && (recipe.hasTeamId || recipe.hasCustomUrl)) { - if (config.fields.team) { - config.fields.team.validators = []; - } - if (config.fields.customUrl) { - config.fields.customUrl.validators = [url]; - } - } - - if (recipe.hasIndirectMessages) { - Object.assign(config.fields, { - isIndirectMessageBadgeEnabled: { - label: intl.formatMessage(messages.indirectMessages), - value: service.isIndirectMessageBadgeEnabled, - default: DEFAULT_SERVICE_SETTINGS.hasIndirectMessages, - }, - }); - } - - if (recipe.allowFavoritesDelineationInUnreadCount) { - Object.assign(config.fields, { - onlyShowFavoritesInUnreadCount: { - label: intl.formatMessage(messages.onlyShowFavoritesInUnreadCount), - value: service.onlyShowFavoritesInUnreadCount, - default: DEFAULT_APP_SETTINGS.onlyShowFavoritesInUnreadCount, - }, - }); - } - - if (proxy.isEnabled) { - const serviceProxyConfig = stores.settings.proxy[service.id] || {}; - - Object.assign(config.fields, { - proxy: { - name: 'proxy', - label: 'proxy', - fields: { - isEnabled: { - label: intl.formatMessage(messages.enableProxy), - value: serviceProxyConfig.isEnabled, - default: DEFAULT_APP_SETTINGS.proxyFeatureEnabled, - }, - host: { - label: intl.formatMessage(messages.proxyHost), - value: serviceProxyConfig.host, - default: '', - }, - port: { - label: intl.formatMessage(messages.proxyPort), - value: serviceProxyConfig.port, - default: '', - }, - user: { - label: intl.formatMessage(messages.proxyUser), - value: serviceProxyConfig.user, - default: '', - }, - password: { - label: intl.formatMessage(messages.proxyPassword), - value: serviceProxyConfig.password, - default: '', - type: 'password', - }, - }, - }, - }); - } - - return new Form(config); - } - - deleteService() { - const { deleteService } = this.props.actions.service; - const { action } = this.props.router.params; - - if (action === 'edit') { - const { activeSettings: service } = this.props.stores.services; - deleteService({ - serviceId: service.id, - redirect: '/settings/services', - }); - } - } - - openRecipeFile(file) { - const { openRecipeFile } = this.props.actions.service; - const { action } = this.props.router.params; - - if (action === 'edit') { - const { activeSettings: service } = this.props.stores.services; - openRecipeFile({ - recipe: service.recipe.id, - file, - }); - } - } - - render() { - const { recipes, services, user } = this.props.stores; - const { action } = this.props.router.params; - - let recipe; - let service = {}; - let isLoading = false; - - if (action === 'add') { - recipe = recipes.active; - - // TODO: render error message when recipe is `null` - if (!recipe) { - return ; - } - } else { - service = services.activeSettings; - isLoading = services.allServicesRequest.isExecuting; - - if (!isLoading && service) { - recipe = service.recipe; - } - } - - if (isLoading) { - return
Loading...
; - } - - if (!recipe) { - return
something went wrong
; - } - - const form = this.prepareForm(recipe, service, proxyFeature); - - return ( - - this.onSubmit(d)} - onDelete={() => this.deleteService()} - openRecipeFile={file => this.openRecipeFile(file)} - isProxyFeatureEnabled={proxyFeature.isEnabled} - /> - - ); - } -} - -EditServiceScreen.propTypes = { - stores: PropTypes.shape({ - user: PropTypes.instanceOf(UserStore).isRequired, - recipes: PropTypes.instanceOf(RecipesStore).isRequired, - services: PropTypes.instanceOf(ServicesStore).isRequired, - settings: PropTypes.instanceOf(SettingsStore).isRequired, - }).isRequired, - router: PropTypes.instanceOf(RouterStore).isRequired, - actions: PropTypes.shape({ - service: PropTypes.instanceOf(ServicesStore).isRequired, - }).isRequired, -}; - -export default injectIntl( - inject('stores', 'actions')(observer(EditServiceScreen)), -); diff --git a/src/containers/settings/EditServiceScreen.tsx b/src/containers/settings/EditServiceScreen.tsx new file mode 100644 index 000000000..6545c3d7d --- /dev/null +++ b/src/containers/settings/EditServiceScreen.tsx @@ -0,0 +1,480 @@ +import { Component, ReactElement } from 'react'; +import { inject, observer } from 'mobx-react'; +import { defineMessages, injectIntl } from 'react-intl'; + +import { RouterStore } from 'mobx-react-router'; +import { StoresProps } from 'src/@types/ferdium-components.types'; +import { IRecipe } from 'src/models/Recipe'; +import Service from 'src/models/Service'; +import { FormFields } from 'src/@types/mobx-form.types'; +import Form from '../../lib/Form'; + +import ServiceError from '../../components/settings/services/ServiceError'; +import EditServiceForm from '../../components/settings/services/EditServiceForm'; +import ErrorBoundary from '../../components/util/ErrorBoundary'; + +import { required, url, oneRequired } from '../../helpers/validation-helpers'; +import { getSelectOptions } from '../../helpers/i18n-helpers'; + +import { config as proxyFeature } from '../../features/serviceProxy'; + +import { SPELLCHECKER_LOCALES } from '../../i18n/languages'; + +import globalMessages from '../../i18n/globalMessages'; +import { DEFAULT_APP_SETTINGS, DEFAULT_SERVICE_SETTINGS } from '../../config'; + +const messages = defineMessages({ + name: { + id: 'settings.service.form.name', + defaultMessage: 'Name', + }, + enableService: { + id: 'settings.service.form.enableService', + defaultMessage: 'Enable service', + }, + enableHibernation: { + id: 'settings.service.form.enableHibernation', + defaultMessage: 'Enable hibernation', + }, + enableWakeUp: { + id: 'settings.service.form.enableWakeUp', + defaultMessage: 'Enable wake up', + }, + enableNotification: { + id: 'settings.service.form.enableNotification', + defaultMessage: 'Enable notifications', + }, + enableBadge: { + id: 'settings.service.form.enableBadge', + defaultMessage: 'Show unread message badges', + }, + enableAudio: { + id: 'settings.service.form.enableAudio', + defaultMessage: 'Enable audio', + }, + team: { + id: 'settings.service.form.team', + defaultMessage: 'Team', + }, + customUrl: { + id: 'settings.service.form.customUrl', + defaultMessage: 'Custom server', + }, + indirectMessages: { + id: 'settings.service.form.indirectMessages', + defaultMessage: 'Show message badge for all new messages', + }, + icon: { + id: 'settings.service.form.icon', + defaultMessage: 'Custom icon', + }, + enableDarkMode: { + id: 'settings.service.form.enableDarkMode', + defaultMessage: 'Enable Dark Mode', + }, + darkReaderBrightness: { + id: 'settings.service.form.darkReaderBrightness', + defaultMessage: 'Dark Reader Brightness', + }, + darkReaderContrast: { + id: 'settings.service.form.darkReaderContrast', + defaultMessage: 'Dark Reader Contrast', + }, + darkReaderSepia: { + id: 'settings.service.form.darkReaderSepia', + defaultMessage: 'Dark Reader Sepia', + }, + enableProgressbar: { + id: 'settings.service.form.enableProgressbar', + defaultMessage: 'Enable Progress bar', + }, + trapLinkClicks: { + id: 'settings.service.form.trapLinkClicks', + defaultMessage: 'Open URLs within Ferdium', + }, + onlyShowFavoritesInUnreadCount: { + id: 'settings.service.form.onlyShowFavoritesInUnreadCount', + defaultMessage: 'Only show Favorites in unread count', + }, + enableProxy: { + id: 'settings.service.form.proxy.isEnabled', + defaultMessage: 'Use Proxy', + }, + proxyHost: { + id: 'settings.service.form.proxy.host', + defaultMessage: 'Proxy Host/IP', + }, + proxyPort: { + id: 'settings.service.form.proxy.port', + defaultMessage: 'Port', + }, + proxyUser: { + id: 'settings.service.form.proxy.user', + defaultMessage: 'User (optional)', + }, + proxyPassword: { + id: 'settings.service.form.proxy.password', + defaultMessage: 'Password (optional)', + }, +}); + +interface EditServicesScreenProps extends StoresProps { + router: RouterStore; + intl: any; +} + +class EditServiceScreen extends Component { + onSubmit(data: any) { + // @ts-ignore TODO: This is actually there and we don't have a correct type right now. + const { action } = this.props.router.params; + const { recipes, services } = this.props.stores; + const { createService, updateService } = this.props.actions.service; + data.darkReaderSettings = { + brightness: data.darkReaderBrightness, + contrast: data.darkReaderContrast, + sepia: data.darkReaderSepia, + }; + delete data.darkReaderContrast; + delete data.darkReaderBrightness; + delete data.darkReaderSepia; + + const serviceData = data; + serviceData.isMuted = !serviceData.isMuted; + + if (action === 'edit') { + updateService({ serviceId: services.activeSettings?.id, serviceData }); + } else { + createService({ recipeId: recipes.active?.id, serviceData }); + } + } + + prepareForm(recipe: IRecipe, service: Service | null, proxy: any): Form { + const { intl } = this.props; + + const { stores, router } = this.props; + + // @ts-ignore TODO: This is actually there and we don't have a correct type right now. + const { action } = router.params; + + let defaultSpellcheckerLanguage = + SPELLCHECKER_LOCALES[stores.settings.app.spellcheckerLanguage]; + + if (stores.settings.app.spellcheckerLanguage === 'automatic') { + defaultSpellcheckerLanguage = intl.formatMessage( + globalMessages.spellcheckerAutomaticDetectionShort, + ); + } + + const spellcheckerLanguage = getSelectOptions({ + locales: SPELLCHECKER_LOCALES, + resetToDefaultText: intl.formatMessage( + globalMessages.spellcheckerSystemDefault, + { default: defaultSpellcheckerLanguage }, + ), + automaticDetectionText: + stores.settings.app.spellcheckerLanguage !== 'automatic' + ? intl.formatMessage(globalMessages.spellcheckerAutomaticDetection) + : '', + }); + + const config: FormFields = { + fields: { + name: { + label: intl.formatMessage(messages.name), + placeholder: intl.formatMessage(messages.name), + value: service?.id ? service.name : recipe.name, + }, + isEnabled: { + label: intl.formatMessage(messages.enableService), + value: service?.isEnabled, + default: DEFAULT_SERVICE_SETTINGS.isEnabled, + }, + isHibernationEnabled: { + label: intl.formatMessage(messages.enableHibernation), + value: + action !== 'edit' + ? recipe.autoHibernate + : service?.isHibernationEnabled, + default: DEFAULT_SERVICE_SETTINGS.isHibernationEnabled, + }, + isWakeUpEnabled: { + label: intl.formatMessage(messages.enableWakeUp), + value: service?.isWakeUpEnabled, + default: DEFAULT_SERVICE_SETTINGS.isWakeUpEnabled, + }, + isNotificationEnabled: { + label: intl.formatMessage(messages.enableNotification), + value: service?.isNotificationEnabled, + default: DEFAULT_SERVICE_SETTINGS.isNotificationEnabled, + }, + isBadgeEnabled: { + label: intl.formatMessage(messages.enableBadge), + value: service?.isBadgeEnabled, + default: DEFAULT_SERVICE_SETTINGS.isBadgeEnabled, + }, + trapLinkClicks: { + label: intl.formatMessage(messages.trapLinkClicks), + value: service?.trapLinkClicks, + default: DEFAULT_SERVICE_SETTINGS.trapLinkClicks, + }, + isMuted: { + label: intl.formatMessage(messages.enableAudio), + value: !service?.isMuted, + default: DEFAULT_SERVICE_SETTINGS.isMuted, + }, + customIcon: { + label: intl.formatMessage(messages.icon), + value: service?.hasCustomUploadedIcon ? service?.icon : false, + default: null, + type: 'file', + }, + isDarkModeEnabled: { + label: intl.formatMessage(messages.enableDarkMode), + value: service?.isDarkModeEnabled, + default: stores.settings.app.darkMode, + }, + darkReaderBrightness: { + label: intl.formatMessage(messages.darkReaderBrightness), + value: service?.darkReaderSettings + ? service?.darkReaderSettings.brightness + : undefined, + default: 100, + }, + darkReaderContrast: { + label: intl.formatMessage(messages.darkReaderContrast), + value: service?.darkReaderSettings + ? service?.darkReaderSettings.contrast + : undefined, + default: 90, + }, + darkReaderSepia: { + label: intl.formatMessage(messages.darkReaderSepia), + value: service?.darkReaderSettings + ? service?.darkReaderSettings.sepia + : undefined, + default: 10, + }, + isProgressbarEnabled: { + label: intl.formatMessage(messages.enableProgressbar), + value: service?.isProgressbarEnabled, + default: DEFAULT_SERVICE_SETTINGS.isProgressbarEnabled, + }, + spellcheckerLanguage: { + label: intl.formatMessage(globalMessages.spellcheckerLanguage), + value: service?.spellcheckerLanguage, + options: spellcheckerLanguage, + disabled: !stores.settings.app.enableSpellchecking, + }, + userAgentPref: { + label: intl.formatMessage(globalMessages.userAgentPref), + placeholder: service?.defaultUserAgent, + value: service?.userAgentPref ? service.userAgentPref : '', + }, + }, + }; + + if (recipe.hasTeamId) { + Object.assign(config.fields, { + team: { + label: intl.formatMessage(messages.team), + placeholder: intl.formatMessage(messages.team), + value: service?.team, + validators: [required], + }, + }); + } + + if (recipe.hasCustomUrl) { + Object.assign(config.fields, { + customUrl: { + label: intl.formatMessage(messages.customUrl), + placeholder: "'http://' or 'https://' or 'file:///'", + value: service?.customUrl || recipe.serviceURL, + validators: [required, url], + }, + }); + } + + // More fine grained and use case specific validation rules + if (recipe.hasTeamId && recipe.hasCustomUrl) { + config.fields.team.validators = [oneRequired(['team', 'customUrl'])]; + config.fields.customUrl.validators = [ + url, + oneRequired(['team', 'customUrl']), + ]; + } + + // If a service can be hosted and has a teamId or customUrl + if (recipe.hasHostedOption && (recipe.hasTeamId || recipe.hasCustomUrl)) { + if (config.fields.team) { + config.fields.team.validators = []; + } + if (config.fields.customUrl) { + config.fields.customUrl.validators = [url]; + } + } + + if (recipe.hasIndirectMessages) { + Object.assign(config.fields, { + isIndirectMessageBadgeEnabled: { + label: intl.formatMessage(messages.indirectMessages), + value: service?.isIndirectMessageBadgeEnabled, + default: DEFAULT_SERVICE_SETTINGS.hasIndirectMessages, + }, + }); + } + + if (recipe.allowFavoritesDelineationInUnreadCount) { + Object.assign(config.fields, { + onlyShowFavoritesInUnreadCount: { + label: intl.formatMessage(messages.onlyShowFavoritesInUnreadCount), + value: service?.onlyShowFavoritesInUnreadCount, + default: DEFAULT_APP_SETTINGS.onlyShowFavoritesInUnreadCount, + }, + }); + } + + if (proxy.isEnabled) { + let serviceProxyConfig: { + isEnabled?: boolean; + host?: string; + port?: number; + user?: string; + password?: string; + } = {}; + if (service) { + serviceProxyConfig = stores.settings.proxy[service.id] || {}; + } + + Object.assign(config.fields, { + proxy: { + name: 'proxy', + label: 'proxy', + fields: { + isEnabled: { + label: intl.formatMessage(messages.enableProxy), + value: serviceProxyConfig.isEnabled, + default: DEFAULT_APP_SETTINGS.proxyFeatureEnabled, + }, + host: { + label: intl.formatMessage(messages.proxyHost), + value: serviceProxyConfig.host, + default: '', + }, + port: { + label: intl.formatMessage(messages.proxyPort), + value: serviceProxyConfig.port, + default: '', + }, + user: { + label: intl.formatMessage(messages.proxyUser), + value: serviceProxyConfig.user, + default: '', + }, + password: { + label: intl.formatMessage(messages.proxyPassword), + value: serviceProxyConfig.password, + default: '', + type: 'password', + }, + }, + }, + }); + } + + // @ts-ignore: Remove this ignore once mobx-react-form v4 with typescript + // support has been released. + return new Form(config); + } + + deleteService(): void { + const { deleteService } = this.props.actions.service; + // @ts-ignore TODO: This is actually there and we don't have a correct type right now. + const { action } = this.props.router.params; + + if (action === 'edit') { + const { activeSettings: service } = this.props.stores.services; + deleteService({ + serviceId: service?.id, + redirect: '/settings/services', + }); + } + } + + openRecipeFile(file: any): void { + const { openRecipeFile } = this.props.actions.service; + // @ts-ignore TODO: This is actually there and we don't have a correct type right now. + const { action } = this.props.router.params; + + if (action === 'edit') { + const { activeSettings: service } = this.props.stores.services; + openRecipeFile({ + recipe: service?.recipe.id, + file, + }); + } + } + + render(): ReactElement { + const { recipes, services, user } = this.props.stores; + + // @ts-ignore TODO: This is actually there and we don't have a correct type right now. + const { action } = this.props.router.params; + + let recipe: null | IRecipe = null; + let service: null | Service = null; + let isLoading = false; + + if (action === 'add') { + recipe = recipes.active; + + // TODO: render error message when recipe is `null` + if (!recipe) { + return ; + } + } else { + service = services.activeSettings; + isLoading = services.allServicesRequest.isExecuting; + + if (!isLoading && service) { + recipe = service.recipe; + } + } + + if (isLoading) { + return
Loading...
; + } + + if (!recipe) { + return
something went wrong
; + } + + const form = this.prepareForm(recipe, service, proxyFeature); + + return ( + + this.onSubmit(d)} + onDelete={() => this.deleteService()} + openRecipeFile={file => this.openRecipeFile(file)} + isProxyFeatureEnabled={proxyFeature.isEnabled} + /> + + ); + } +} + +export default injectIntl<'intl', EditServicesScreenProps>( + inject('stores', 'actions')(observer(EditServiceScreen)), +); diff --git a/src/containers/settings/EditSettingsScreen.js b/src/containers/settings/EditSettingsScreen.js deleted file mode 100644 index 867db6f08..000000000 --- a/src/containers/settings/EditSettingsScreen.js +++ /dev/null @@ -1,918 +0,0 @@ -import { ipcRenderer } from 'electron'; -import { Component } from 'react'; -import PropTypes from 'prop-types'; -import { inject, observer } from 'mobx-react'; -import { defineMessages, injectIntl } from 'react-intl'; - -import AppStore from '../../stores/AppStore'; -import SettingsStore from '../../stores/SettingsStore'; -import UserStore from '../../stores/UserStore'; -import TodosStore from '../../features/todos/store'; -import Form from '../../lib/Form'; -import { APP_LOCALES, SPELLCHECKER_LOCALES } from '../../i18n/languages'; -import { - DEFAULT_APP_SETTINGS, - HIBERNATION_STRATEGIES, - SIDEBAR_WIDTH, - SIDEBAR_SERVICES_LOCATION, - ICON_SIZES, - NAVIGATION_BAR_BEHAVIOURS, - SEARCH_ENGINE_NAMES, - TODO_APPS, - DEFAULT_SETTING_KEEP_ALL_WORKSPACES_LOADED, - DEFAULT_IS_FEATURE_ENABLED_BY_USER, - WAKE_UP_STRATEGIES, - WAKE_UP_HIBERNATION_STRATEGIES, - SPLIT_COLUMNS_MIN, - SPLIT_COLUMNS_MAX, -} from '../../config'; -import { isMac } from '../../environment'; - -import { getSelectOptions } from '../../helpers/i18n-helpers'; -import { hash } from '../../helpers/password-helpers'; -import defaultUserAgent from '../../helpers/userAgent-helpers'; - -import EditSettingsForm from '../../components/settings/settings/EditSettingsForm'; -import ErrorBoundary from '../../components/util/ErrorBoundary'; - -import globalMessages from '../../i18n/globalMessages'; -import WorkspacesStore from '../../features/workspaces/store'; -import ServicesStore from '../../stores/ServicesStore'; - -const debug = require('../../preload-safe-debug')('Ferdium:EditSettingsScreen'); - -const messages = defineMessages({ - autoLaunchOnStart: { - id: 'settings.app.form.autoLaunchOnStart', - defaultMessage: 'Launch Ferdium on start', - }, - autoLaunchInBackground: { - id: 'settings.app.form.autoLaunchInBackground', - defaultMessage: 'Open in background', - }, - runInBackground: { - id: 'settings.app.form.runInBackground', - defaultMessage: 'Keep Ferdium in background when closing the window', - }, - startMinimized: { - id: 'settings.app.form.startMinimized', - defaultMessage: 'Start minimized', - }, - confirmOnQuit: { - id: 'settings.app.form.confirmOnQuit', - defaultMessage: 'Confirm when quitting Ferdium', - }, - enableSystemTray: { - id: 'settings.app.form.enableSystemTray', - defaultMessage: 'Always show Ferdium in System Tray', - }, - enableMenuBar: { - id: 'settings.app.form.enableMenuBar', - defaultMessage: 'Always show Ferdium in Menu Bar', - }, - reloadAfterResume: { - id: 'settings.app.form.reloadAfterResume', - defaultMessage: 'Reload Ferdium after system resume', - }, - reloadAfterResumeTime: { - id: 'settings.app.form.reloadAfterResumeTime', - defaultMessage: 'Time to consider the system as idle/suspended (in minutes)', - }, - minimizeToSystemTray: { - id: 'settings.app.form.minimizeToSystemTray', - defaultMessage: 'Minimize Ferdium to system tray', - }, - closeToSystemTray: { - id: 'settings.app.form.closeToSystemTray', - defaultMessage: 'Close Ferdium to system tray', - }, - privateNotifications: { - id: 'settings.app.form.privateNotifications', - defaultMessage: "Don't show message content in notifications", - }, - clipboardNotifications: { - id: 'settings.app.form.clipboardNotifications', - defaultMessage: "Don't show notifications for clipboard events", - }, - notifyTaskBarOnMessage: { - id: 'settings.app.form.notifyTaskBarOnMessage', - defaultMessage: 'Notify TaskBar/Dock on new message', - }, - navigationBarBehaviour: { - id: 'settings.app.form.navigationBarBehaviour', - defaultMessage: 'Navigation bar behaviour', - }, - searchEngine: { - id: 'settings.app.form.searchEngine', - defaultMessage: 'Search engine', - }, - hibernateOnStartup: { - id: 'settings.app.form.hibernateOnStartup', - defaultMessage: 'Keep services in hibernation on startup', - }, - hibernationStrategy: { - id: 'settings.app.form.hibernationStrategy', - defaultMessage: 'Hibernation strategy', - }, - wakeUpStrategy: { - id: 'settings.app.form.wakeUpStrategy', - defaultMessage: 'Wake up strategy', - }, - wakeUpHibernationStrategy: { - id: 'settings.app.form.wakeUpHibernationStrategy', - defaultMessage: 'Hibernation strategy after automatic wake up', - }, - wakeUpHibernationSplay: { - id: 'settings.app.form.wakeUpHibernationSplay', - defaultMessage: 'Splay hibernate/wake cycles to reduce load', - }, - predefinedTodoServer: { - id: 'settings.app.form.predefinedTodoServer', - defaultMessage: 'Todo Server', - }, - customTodoServer: { - id: 'settings.app.form.customTodoServer', - defaultMessage: 'Custom Todo Server', - }, - enableLock: { - id: 'settings.app.form.enableLock', - defaultMessage: 'Enable Password Lock', - }, - lockPassword: { - id: 'settings.app.form.lockPassword', - defaultMessage: 'Password', - }, - useTouchIdToUnlock: { - id: 'settings.app.form.useTouchIdToUnlock', - defaultMessage: 'Allow using TouchID to unlock Ferdium', - }, - inactivityLock: { - id: 'settings.app.form.inactivityLock', - defaultMessage: 'Lock after inactivity', - }, - scheduledDNDEnabled: { - id: 'settings.app.form.scheduledDNDEnabled', - defaultMessage: 'Enable scheduled Do-not-Disturb', - }, - scheduledDNDStart: { - id: 'settings.app.form.scheduledDNDStart', - defaultMessage: 'From', - }, - scheduledDNDEnd: { - id: 'settings.app.form.scheduledDNDEnd', - defaultMessage: 'To', - }, - language: { - id: 'settings.app.form.language', - defaultMessage: 'Language', - }, - darkMode: { - id: 'settings.app.form.darkMode', - defaultMessage: 'Enable Dark Mode', - }, - adaptableDarkMode: { - id: 'settings.app.form.adaptableDarkMode', - defaultMessage: "Synchronize dark mode with my OS's dark mode setting", - }, - universalDarkMode: { - id: 'settings.app.form.universalDarkMode', - defaultMessage: 'Enable universal Dark Mode', - }, - splitMode: { - id: 'settings.app.form.splitMode', - defaultMessage: 'Enable Split View Mode', - }, - splitColumns: { - id: 'settings.app.form.splitColumns', - defaultMessage: 'Number of columns', - }, - serviceRibbonWidth: { - id: 'settings.app.form.serviceRibbonWidth', - defaultMessage: 'Sidebar width', - }, - sidebarServicesLocation: { - id: 'settings.app.form.sidebarServicesLocation', - defaultMessage: 'Sidebar Services Icons Location', - }, - iconSize: { - id: 'settings.app.form.iconSize', - defaultMessage: 'Service icon size', - }, - enableLongPressServiceHint: { - id: 'settings.app.form.enableLongPressServiceHint', - defaultMessage: 'Enable service shortcut hint on long press', - }, - useVerticalStyle: { - id: 'settings.app.form.useVerticalStyle', - defaultMessage: 'Use horizontal style', - }, - hideCollapseButton: { - id: 'settings.app.form.hideCollapseButton', - defaultMessage: 'Hide Collapse button', - }, - hideRecipesButton: { - id: 'settings.app.form.hideRecipesButton', - defaultMessage: 'Hide Recipes button', - }, - hideSplitModeButton: { - id: 'settings.app.form.hideSplitModeButton', - defaultMessage: 'Hide Split Mode button', - }, - useGrayscaleServices: { - id: 'settings.app.form.useGrayscaleServices', - defaultMessage: 'Use grayscale services', - }, - grayscaleServicesDim: { - id: 'settings.app.form.grayscaleServicesDim', - defaultMessage: 'Grayscale dim level', - }, - hideWorkspacesButton: { - id: 'settings.app.form.hideWorkspacesButton', - defaultMessage: 'Hide Workspace Drawer button', - }, - hideNotificationsButton: { - id: 'settings.app.form.hideNotificationsButton', - defaultMessage: 'Hide Notifications & Sound button', - }, - hideSettingsButton: { - id: 'settings.app.form.hideSettingsButton', - defaultMessage: 'Hide Settings button', - }, - alwaysShowWorkspaces: { - id: 'settings.app.form.alwaysShowWorkspaces', - defaultMessage: 'Always show workspace drawer', - }, - accentColor: { - id: 'settings.app.form.accentColor', - defaultMessage: 'Accent color', - }, - progressbarAccentColor: { - id: 'settings.app.form.progressbarAccentColor', - defaultMessage: 'Progressbar Accent color', - }, - showDisabledServices: { - id: 'settings.app.form.showDisabledServices', - defaultMessage: 'Display disabled services tabs', - }, - showServiceName: { - id: 'settings.app.form.showServiceName', - defaultMessage: 'Display service name under the icon', - }, - showMessageBadgeWhenMuted: { - id: 'settings.app.form.showMessagesBadgesWhenMuted', - defaultMessage: 'Show unread message badge when notifications are disabled', - }, - showDragArea: { - id: 'settings.app.form.showDragArea', - defaultMessage: 'Show draggable area on window', - }, - enableSpellchecking: { - id: 'settings.app.form.enableSpellchecking', - defaultMessage: 'Enable spell checking', - }, - enableGPUAcceleration: { - id: 'settings.app.form.enableGPUAcceleration', - defaultMessage: 'Enable GPU Acceleration', - }, - enableGlobalHideShortcut: { - id: 'settings.app.form.enableGlobalHideShortcut', - defaultMessage: 'Enable Global shortcut to hide Ferdium', - }, - beta: { - id: 'settings.app.form.beta', - defaultMessage: 'Include pre-releases', - }, - automaticUpdates: { - id: 'settings.app.form.automaticUpdates', - defaultMessage: 'Enable updates', - }, - enableTodos: { - id: 'settings.app.form.enableTodos', - defaultMessage: 'Enable Ferdium Todos', - }, - keepAllWorkspacesLoaded: { - id: 'settings.app.form.keepAllWorkspacesLoaded', - defaultMessage: 'Keep all workspaces loaded', - }, -}); - -class EditSettingsScreen extends Component { - constructor(props) { - super(props); - - this.state = { - lockedPassword: '', - }; - } - - onSubmit(settingsData) { - const { todos, workspaces } = this.props.stores; - const { - app, - settings, - user, - todos: todosActions, - workspaces: workspaceActions, - } = this.props.actions; - - const useOriginalPassword = settingsData.lockedPassword === ''; - - this.setState({ - lockedPassword: useOriginalPassword ? '' : settingsData.lockedPassword, - }); - - app.launchOnStartup({ - enable: Boolean(settingsData.autoLaunchOnStart), - openInBackground: Boolean(settingsData.autoLaunchInBackground), - }); - - debug(`Updating settings store with data: ${settingsData}`); - - settings.update({ - type: 'app', - // TODO: The conversions might not be necessary once we convert to typescript - data: { - runInBackground: Boolean(settingsData.runInBackground), - enableSystemTray: Boolean(settingsData.enableSystemTray), - reloadAfterResume: Boolean(settingsData.reloadAfterResume), - reloadAfterResumeTime: Number(settingsData.reloadAfterResumeTime), - startMinimized: Boolean(settingsData.startMinimized), - confirmOnQuit: Boolean(settingsData.confirmOnQuit), - minimizeToSystemTray: Boolean(settingsData.minimizeToSystemTray), - closeToSystemTray: Boolean(settingsData.closeToSystemTray), - privateNotifications: Boolean(settingsData.privateNotifications), - clipboardNotifications: Boolean(settingsData.clipboardNotifications), - notifyTaskBarOnMessage: Boolean(settingsData.notifyTaskBarOnMessage), - navigationBarBehaviour: settingsData.navigationBarBehaviour, - searchEngine: settingsData.searchEngine, - hibernateOnStartup: Boolean(settingsData.hibernateOnStartup), - hibernationStrategy: Number(settingsData.hibernationStrategy), - wakeUpStrategy: Number(settingsData.wakeUpStrategy), - wakeUpHibernationStrategy: Number(settingsData.wakeUpHibernationStrategy), - wakeUpHibernationSplay: Boolean(settingsData.wakeUpHibernationSplay), - predefinedTodoServer: settingsData.predefinedTodoServer, - customTodoServer: settingsData.customTodoServer, - lockingFeatureEnabled: Boolean(settingsData.lockingFeatureEnabled), - lockedPassword: useOriginalPassword ? this.props.stores.settings.all.app.lockedPassword : hash(String(settingsData.lockedPassword)), - useTouchIdToUnlock: Boolean(settingsData.useTouchIdToUnlock), - inactivityLock: Number(settingsData.inactivityLock), - scheduledDNDEnabled: Boolean(settingsData.scheduledDNDEnabled), - scheduledDNDStart: settingsData.scheduledDNDStart, - scheduledDNDEnd: settingsData.scheduledDNDEnd, - enableGPUAcceleration: Boolean(settingsData.enableGPUAcceleration), - enableGlobalHideShortcut: Boolean( - settingsData.enableGlobalHideShortcut, - ), - showDisabledServices: Boolean(settingsData.showDisabledServices), - showServiceName: Boolean(settingsData.showServiceName), - darkMode: Boolean(settingsData.darkMode), - adaptableDarkMode: Boolean(settingsData.adaptableDarkMode), - universalDarkMode: Boolean(settingsData.universalDarkMode), - splitMode: Boolean(settingsData.splitMode), - splitColumns: Number(settingsData.splitColumns), - serviceRibbonWidth: Number(settingsData.serviceRibbonWidth), - sidebarServicesLocation: Number(settingsData.sidebarServicesLocation), - iconSize: Number(settingsData.iconSize), - enableLongPressServiceHint: Boolean( - settingsData.enableLongPressServiceHint, - ), - useVerticalStyle: Boolean(settingsData.useVerticalStyle), - hideCollapseButton: Boolean(settingsData.hideCollapseButton), - hideRecipesButton: Boolean(settingsData.hideRecipesButton), - hideSplitModeButton: Boolean(settingsData.hideSplitModeButton), - useGrayscaleServices: Boolean(settingsData.useGrayscaleServices), - grayscaleServicesDim: Number(settingsData.grayscaleServicesDim), - hideWorkspacesButton: Boolean(settingsData.hideWorkspacesButton), - hideNotificationsButton: Boolean(settingsData.hideNotificationsButton), - hideSettingsButton: Boolean(settingsData.hideSettingsButton), - alwaysShowWorkspaces: Boolean(settingsData.alwaysShowWorkspaces), - accentColor: settingsData.accentColor, - progressbarAccentColor: settingsData.progressbarAccentColor, - showMessageBadgeWhenMuted: Boolean( - settingsData.showMessageBadgeWhenMuted, - ), - showDragArea: Boolean(settingsData.showDragArea), - enableSpellchecking: Boolean(settingsData.enableSpellchecking), - spellcheckerLanguage: settingsData.spellcheckerLanguage, - userAgentPref: settingsData.userAgentPref, - beta: Boolean(settingsData.beta), // we need this info in the main process as well - automaticUpdates: Boolean(settingsData.automaticUpdates), // we need this info in the main process as well - locale: settingsData.locale, // we need this info in the main process as well - }, - }); - - user.update({ - userData: { - automaticUpdates: Boolean(settingsData.automaticUpdates), - beta: Boolean(settingsData.beta), - locale: settingsData.locale, - }, - }); - - const { keepAllWorkspacesLoaded } = workspaces.settings; - if ( - Boolean(keepAllWorkspacesLoaded) !== - Boolean(settingsData.keepAllWorkspacesLoaded) - ) { - workspaceActions.toggleKeepAllWorkspacesLoadedSetting(); - } - - if (todos.isFeatureActive) { - const { isFeatureEnabledByUser } = todos.settings; - if ( - Boolean(isFeatureEnabledByUser) !== Boolean(settingsData.enableTodos) - ) { - todosActions.toggleTodosFeatureVisibility(); - } - } - } - - openProcessManager() { - ipcRenderer.send('openProcessManager'); - } - - prepareForm() { - const { app, settings, user, todos, workspaces } = this.props.stores; - const { intl } = this.props; - const { lockedPassword } = this.state; - - const locales = getSelectOptions({ - locales: APP_LOCALES, - }); - - const navigationBarBehaviours = getSelectOptions({ - locales: NAVIGATION_BAR_BEHAVIOURS, - sort: false, - }); - - const searchEngines = getSelectOptions({ - locales: SEARCH_ENGINE_NAMES, - sort: false, - }); - - const hibernationStrategies = getSelectOptions({ - locales: HIBERNATION_STRATEGIES, - sort: false, - }); - - const wakeUpStrategies = getSelectOptions({ - locales: WAKE_UP_STRATEGIES, - sort: false, - }); - - const wakeUpHibernationStrategies = getSelectOptions({ - locales: WAKE_UP_HIBERNATION_STRATEGIES, - sort: false, - }); - - const todoApp = getSelectOptions({ - locales: TODO_APPS, - sort: false, - }); - - const sidebarWidth = getSelectOptions({ - locales: SIDEBAR_WIDTH, - sort: false, - }); - - const sidebarServicesLocation = getSelectOptions({ - locales: SIDEBAR_SERVICES_LOCATION, - sort: false, - }); - - const iconSizes = getSelectOptions({ - locales: ICON_SIZES, - sort: false, - }); - - const spellcheckingLanguages = getSelectOptions({ - locales: SPELLCHECKER_LOCALES, - automaticDetectionText: intl.formatMessage( - globalMessages.spellcheckerAutomaticDetection, - ), - }); - - const config = { - fields: { - autoLaunchOnStart: { - label: intl.formatMessage(messages.autoLaunchOnStart), - value: app.autoLaunchOnStart, - default: DEFAULT_APP_SETTINGS.autoLaunchOnStart, - }, - autoLaunchInBackground: { - label: intl.formatMessage(messages.autoLaunchInBackground), - value: app.launchInBackground, - default: DEFAULT_APP_SETTINGS.autoLaunchInBackground, - }, - runInBackground: { - label: intl.formatMessage(messages.runInBackground), - value: settings.all.app.runInBackground, - default: DEFAULT_APP_SETTINGS.runInBackground, - }, - startMinimized: { - label: intl.formatMessage(messages.startMinimized), - value: settings.all.app.startMinimized, - default: DEFAULT_APP_SETTINGS.startMinimized, - }, - confirmOnQuit: { - label: intl.formatMessage(messages.confirmOnQuit), - value: settings.all.app.confirmOnQuit, - default: DEFAULT_APP_SETTINGS.confirmOnQuit, - }, - enableSystemTray: { - label: intl.formatMessage( - isMac ? messages.enableMenuBar : messages.enableSystemTray, - ), - value: settings.all.app.enableSystemTray, - default: DEFAULT_APP_SETTINGS.enableSystemTray, - }, - reloadAfterResume: { - label: intl.formatMessage(messages.reloadAfterResume), - value: settings.all.app.reloadAfterResume, - default: DEFAULT_APP_SETTINGS.reloadAfterResume, - }, - reloadAfterResumeTime: { - label: intl.formatMessage(messages.reloadAfterResumeTime), - value: settings.all.app.reloadAfterResumeTime, - default: DEFAULT_APP_SETTINGS.reloadAfterResumeTime, - }, - minimizeToSystemTray: { - label: intl.formatMessage(messages.minimizeToSystemTray), - value: settings.all.app.minimizeToSystemTray, - default: DEFAULT_APP_SETTINGS.minimizeToSystemTray, - }, - closeToSystemTray: { - label: intl.formatMessage(messages.closeToSystemTray), - value: settings.all.app.closeToSystemTray, - default: DEFAULT_APP_SETTINGS.closeToSystemTray, - }, - privateNotifications: { - label: intl.formatMessage(messages.privateNotifications), - value: settings.all.app.privateNotifications, - default: DEFAULT_APP_SETTINGS.privateNotifications, - }, - clipboardNotifications: { - label: intl.formatMessage(messages.clipboardNotifications), - value: settings.all.app.clipboardNotifications, - default: DEFAULT_APP_SETTINGS.clipboardNotifications, - }, - notifyTaskBarOnMessage: { - label: intl.formatMessage(messages.notifyTaskBarOnMessage), - value: settings.all.app.notifyTaskBarOnMessage, - default: DEFAULT_APP_SETTINGS.notifyTaskBarOnMessage, - }, - navigationBarBehaviour: { - label: intl.formatMessage(messages.navigationBarBehaviour), - value: settings.all.app.navigationBarBehaviour, - default: DEFAULT_APP_SETTINGS.navigationBarBehaviour, - options: navigationBarBehaviours, - }, - searchEngine: { - label: intl.formatMessage(messages.searchEngine), - value: settings.all.app.searchEngine, - default: DEFAULT_APP_SETTINGS.searchEngine, - options: searchEngines, - }, - hibernateOnStartup: { - label: intl.formatMessage(messages.hibernateOnStartup), - value: settings.all.app.hibernateOnStartup, - default: DEFAULT_APP_SETTINGS.hibernateOnStartup, - }, - hibernationStrategy: { - label: intl.formatMessage(messages.hibernationStrategy), - value: settings.all.app.hibernationStrategy, - options: hibernationStrategies, - default: DEFAULT_APP_SETTINGS.hibernationStrategy, - }, - wakeUpStrategy: { - label: intl.formatMessage(messages.wakeUpStrategy), - value: settings.all.app.wakeUpStrategy, - options: wakeUpStrategies, - default: DEFAULT_APP_SETTINGS.wakeUpStrategy, - }, - wakeUpHibernationStrategy: { - label: intl.formatMessage(messages.wakeUpHibernationStrategy), - value: settings.all.app.wakeUpHibernationStrategy, - options: wakeUpHibernationStrategies, - default: DEFAULT_APP_SETTINGS.wakeUpHibernationStrategy, - }, - wakeUpHibernationSplay: { - label: intl.formatMessage(messages.wakeUpHibernationSplay), - value: settings.all.app.wakeUpHibernationSplay, - default: DEFAULT_APP_SETTINGS.wakeUpHibernationSplay, - }, - predefinedTodoServer: { - label: intl.formatMessage(messages.predefinedTodoServer), - value: settings.all.app.predefinedTodoServer, - default: DEFAULT_APP_SETTINGS.predefinedTodoServer, - options: todoApp, - }, - customTodoServer: { - label: intl.formatMessage(messages.customTodoServer), - value: settings.all.app.customTodoServer, - default: DEFAULT_APP_SETTINGS.customTodoServer, - }, - lockingFeatureEnabled: { - label: intl.formatMessage(messages.enableLock), - value: settings.all.app.lockingFeatureEnabled || false, - default: DEFAULT_APP_SETTINGS.lockingFeatureEnabled, - }, - lockedPassword: { - label: intl.formatMessage(messages.lockPassword), - value: lockedPassword, - default: '', - type: 'password', - }, - useTouchIdToUnlock: { - label: intl.formatMessage(messages.useTouchIdToUnlock), - value: settings.all.app.useTouchIdToUnlock, - default: DEFAULT_APP_SETTINGS.useTouchIdToUnlock, - }, - inactivityLock: { - label: intl.formatMessage(messages.inactivityLock), - value: settings.all.app.inactivityLock, - default: 0, - type: 'number', - }, - scheduledDNDEnabled: { - label: intl.formatMessage(messages.scheduledDNDEnabled), - value: settings.all.app.scheduledDNDEnabled || false, - default: DEFAULT_APP_SETTINGS.scheduledDNDEnabled, - }, - scheduledDNDStart: { - label: intl.formatMessage(messages.scheduledDNDStart), - value: settings.all.app.scheduledDNDStart, - default: '17:00', - type: 'time', - }, - scheduledDNDEnd: { - label: intl.formatMessage(messages.scheduledDNDEnd), - value: settings.all.app.scheduledDNDEnd, - default: '09:00', - type: 'time', - }, - showDisabledServices: { - label: intl.formatMessage(messages.showDisabledServices), - value: settings.all.app.showDisabledServices, - default: DEFAULT_APP_SETTINGS.showDisabledServices, - }, - showServiceName: { - label: intl.formatMessage(messages.showServiceName), - value: settings.all.app.showServiceName, - default: DEFAULT_APP_SETTINGS.showServiceName, - }, - showMessageBadgeWhenMuted: { - label: intl.formatMessage(messages.showMessageBadgeWhenMuted), - value: settings.all.app.showMessageBadgeWhenMuted, - default: DEFAULT_APP_SETTINGS.showMessageBadgeWhenMuted, - }, - showDragArea: { - label: intl.formatMessage(messages.showDragArea), - value: settings.all.app.showDragArea, - default: DEFAULT_APP_SETTINGS.showDragArea, - }, - enableSpellchecking: { - label: intl.formatMessage(messages.enableSpellchecking), - value: settings.all.app.enableSpellchecking, - default: DEFAULT_APP_SETTINGS.enableSpellchecking, - }, - spellcheckerLanguage: { - label: intl.formatMessage(globalMessages.spellcheckerLanguage), - value: settings.all.app.spellcheckerLanguage, - options: spellcheckingLanguages, - default: DEFAULT_APP_SETTINGS.spellcheckerLanguage, - }, - userAgentPref: { - label: intl.formatMessage(globalMessages.userAgentPref), - value: settings.all.app.userAgentPref, - default: DEFAULT_APP_SETTINGS.userAgentPref, - placeholder: defaultUserAgent(), - }, - darkMode: { - label: intl.formatMessage(messages.darkMode), - value: settings.all.app.darkMode, - default: DEFAULT_APP_SETTINGS.darkMode, - }, - adaptableDarkMode: { - label: intl.formatMessage(messages.adaptableDarkMode), - value: settings.all.app.adaptableDarkMode, - default: DEFAULT_APP_SETTINGS.adaptableDarkMode, - }, - universalDarkMode: { - label: intl.formatMessage(messages.universalDarkMode), - value: settings.all.app.universalDarkMode, - default: DEFAULT_APP_SETTINGS.universalDarkMode, - }, - splitMode: { - label: intl.formatMessage(messages.splitMode), - value: settings.all.app.splitMode, - default: DEFAULT_APP_SETTINGS.splitMode, - }, - splitColumns: { - label: `${intl.formatMessage( - messages.splitColumns, - )} (${SPLIT_COLUMNS_MIN}-${SPLIT_COLUMNS_MAX})`, - value: settings.all.app.splitColumns, - default: DEFAULT_APP_SETTINGS.splitColumns, - }, - serviceRibbonWidth: { - label: intl.formatMessage(messages.serviceRibbonWidth), - value: settings.all.app.serviceRibbonWidth, - default: DEFAULT_APP_SETTINGS.serviceRibbonWidth, - options: sidebarWidth, - }, - sidebarServicesLocation: { - label: intl.formatMessage(messages.sidebarServicesLocation), - value: settings.all.app.sidebarServicesLocation, - default: DEFAULT_APP_SETTINGS.sidebarServicesLocation, - options: sidebarServicesLocation, - }, - iconSize: { - label: intl.formatMessage(messages.iconSize), - value: settings.all.app.iconSize, - default: DEFAULT_APP_SETTINGS.iconSize, - options: iconSizes, - }, - enableLongPressServiceHint: { - label: intl.formatMessage(messages.enableLongPressServiceHint), - value: settings.all.app.enableLongPressServiceHint, - default: DEFAULT_APP_SETTINGS.enableLongPressServiceHint, - }, - useVerticalStyle: { - label: intl.formatMessage(messages.useVerticalStyle), - value: settings.all.app.useVerticalStyle, - default: DEFAULT_APP_SETTINGS.useVerticalStyle, - }, - hideCollapseButton: { - label: intl.formatMessage(messages.hideCollapseButton), - value: settings.all.app.hideCollapseButton, - default: DEFAULT_APP_SETTINGS.hideCollapseButton, - }, - hideRecipesButton: { - label: intl.formatMessage(messages.hideRecipesButton), - value: settings.all.app.hideRecipesButton, - default: DEFAULT_APP_SETTINGS.hideRecipesButton, - }, - hideSplitModeButton: { - label: intl.formatMessage(messages.hideSplitModeButton), - value: settings.all.app.hideSplitModeButton, - default: DEFAULT_APP_SETTINGS.hideSplitModeButton, - }, - useGrayscaleServices: { - label: intl.formatMessage(messages.useGrayscaleServices), - value: settings.all.app.useGrayscaleServices, - default: DEFAULT_APP_SETTINGS.useGrayscaleServices, - }, - grayscaleServicesDim: { - label: intl.formatMessage(messages.grayscaleServicesDim), - value: settings.all.app.grayscaleServicesDim, - default: DEFAULT_APP_SETTINGS.grayscaleServicesDim, - }, - hideWorkspacesButton: { - label: intl.formatMessage(messages.hideWorkspacesButton), - value: settings.all.app.hideWorkspacesButton, - default: DEFAULT_APP_SETTINGS.hideWorkspacesButton, - }, - hideNotificationsButton: { - label: intl.formatMessage(messages.hideNotificationsButton), - value: settings.all.app.hideNotificationsButton, - default: DEFAULT_APP_SETTINGS.hideNotificationsButton, - }, - hideSettingsButton: { - label: intl.formatMessage(messages.hideSettingsButton), - value: settings.all.app.hideSettingsButton, - default: DEFAULT_APP_SETTINGS.hideSettingsButton, - }, - alwaysShowWorkspaces: { - label: intl.formatMessage(messages.alwaysShowWorkspaces), - value: settings.all.app.alwaysShowWorkspaces, - default: DEFAULT_APP_SETTINGS.alwaysShowWorkspaces, - }, - accentColor: { - label: intl.formatMessage(messages.accentColor), - value: settings.all.app.accentColor, - default: DEFAULT_APP_SETTINGS.accentColor, - }, - progressbarAccentColor: { - label: intl.formatMessage(messages.progressbarAccentColor), - value: settings.all.app.progressbarAccentColor, - default: DEFAULT_APP_SETTINGS.progressbarAccentColor, - }, - enableGPUAcceleration: { - label: intl.formatMessage(messages.enableGPUAcceleration), - value: settings.all.app.enableGPUAcceleration, - default: DEFAULT_APP_SETTINGS.enableGPUAcceleration, - }, - enableGlobalHideShortcut: { - label: intl.formatMessage(messages.enableGlobalHideShortcut), - value: settings.all.app.enableGlobalHideShortcut, - default: DEFAULT_APP_SETTINGS.enableGlobalHideShortcut, - }, - locale: { - label: intl.formatMessage(messages.language), - value: app.locale, - options: locales, - default: DEFAULT_APP_SETTINGS.locale, - }, - beta: { - label: intl.formatMessage(messages.beta), - value: user.data.beta, - default: DEFAULT_APP_SETTINGS.beta, - }, - automaticUpdates: { - label: intl.formatMessage(messages.automaticUpdates), - value: settings.app.automaticUpdates, - default: DEFAULT_APP_SETTINGS.automaticUpdates, - }, - }, - }; - - if (workspaces.isFeatureActive) { - config.fields.keepAllWorkspacesLoaded = { - label: intl.formatMessage(messages.keepAllWorkspacesLoaded), - value: workspaces.settings.keepAllWorkspacesLoaded, - default: DEFAULT_SETTING_KEEP_ALL_WORKSPACES_LOADED, - }; - } - - if (todos.isFeatureActive) { - config.fields.enableTodos = { - label: intl.formatMessage(messages.enableTodos), - value: todos.settings.isFeatureEnabledByUser, - default: DEFAULT_IS_FEATURE_ENABLED_BY_USER, - }; - } - - return new Form(config); - } - - render() { - const { app, services } = this.props.stores; - const { - updateStatus, - updateStatusTypes, - isClearingAllCache, - lockingFeatureEnabled, - } = app; - const { checkForUpdates, installUpdate, clearAllCache } = - this.props.actions.app; - const form = this.prepareForm(); - - return ( - - this.onSubmit(d)} - getCacheSize={() => app.cacheSize} - isClearingAllCache={isClearingAllCache} - onClearAllCache={clearAllCache} - lockingFeatureEnabled={lockingFeatureEnabled} - automaticUpdates={this.props.stores.settings.app.automaticUpdates} - isDarkmodeEnabled={this.props.stores.settings.app.darkMode} - isAdaptableDarkModeEnabled={ - this.props.stores.settings.app.adaptableDarkMode - } - isUseGrayscaleServicesEnabled={this.props.stores.settings.app.useGrayscaleServices} - isSplitModeEnabled={this.props.stores.settings.app.splitMode} - isTodosActivated={this.props.stores.todos.isFeatureEnabledByUser} - isUsingCustomTodoService={ - this.props.stores.todos.isUsingCustomTodoService - } - openProcessManager={() => this.openProcessManager()} - hasAddedTodosAsService={services.isTodosServiceAdded} - isOnline={app.isOnline} - /> - - ); - } -} - -EditSettingsScreen.propTypes = { - stores: PropTypes.shape({ - app: PropTypes.instanceOf(AppStore).isRequired, - user: PropTypes.instanceOf(UserStore).isRequired, - settings: PropTypes.instanceOf(SettingsStore).isRequired, - services: PropTypes.instanceOf(ServicesStore).isRequired, - todos: PropTypes.instanceOf(TodosStore).isRequired, - workspaces: PropTypes.instanceOf(WorkspacesStore).isRequired, - }).isRequired, - actions: PropTypes.shape({ - app: PropTypes.instanceOf(AppStore).isRequired, - user: PropTypes.instanceOf(UserStore).isRequired, - settings: PropTypes.instanceOf(SettingsStore).isRequired, - todos: PropTypes.instanceOf(TodosStore).isRequired, - workspaces: PropTypes.instanceOf(WorkspacesStore).isRequired, - }).isRequired, -}; - -export default injectIntl( - inject('stores', 'actions')(observer(EditSettingsScreen)), -); diff --git a/src/containers/settings/EditSettingsScreen.tsx b/src/containers/settings/EditSettingsScreen.tsx new file mode 100644 index 000000000..205c71107 --- /dev/null +++ b/src/containers/settings/EditSettingsScreen.tsx @@ -0,0 +1,906 @@ +import { ipcRenderer } from 'electron'; +import { Component, ReactElement } from 'react'; +import { inject, observer } from 'mobx-react'; +import { defineMessages, injectIntl } from 'react-intl'; + +import { FormFields } from 'src/@types/mobx-form.types'; +import { StoresProps } from 'src/@types/ferdium-components.types'; +import Form from '../../lib/Form'; +import { APP_LOCALES, SPELLCHECKER_LOCALES } from '../../i18n/languages'; +import { + DEFAULT_APP_SETTINGS, + HIBERNATION_STRATEGIES, + SIDEBAR_WIDTH, + SIDEBAR_SERVICES_LOCATION, + ICON_SIZES, + NAVIGATION_BAR_BEHAVIOURS, + SEARCH_ENGINE_NAMES, + TODO_APPS, + DEFAULT_SETTING_KEEP_ALL_WORKSPACES_LOADED, + DEFAULT_IS_FEATURE_ENABLED_BY_USER, + WAKE_UP_STRATEGIES, + WAKE_UP_HIBERNATION_STRATEGIES, + SPLIT_COLUMNS_MIN, + SPLIT_COLUMNS_MAX, +} from '../../config'; +import { isMac } from '../../environment'; + +import { getSelectOptions } from '../../helpers/i18n-helpers'; +import { hash } from '../../helpers/password-helpers'; +import defaultUserAgent from '../../helpers/userAgent-helpers'; + +import EditSettingsForm from '../../components/settings/settings/EditSettingsForm'; +import ErrorBoundary from '../../components/util/ErrorBoundary'; + +import globalMessages from '../../i18n/globalMessages'; + +const debug = require('../../preload-safe-debug')('Ferdium:EditSettingsScreen'); + +const messages = defineMessages({ + autoLaunchOnStart: { + id: 'settings.app.form.autoLaunchOnStart', + defaultMessage: 'Launch Ferdium on start', + }, + autoLaunchInBackground: { + id: 'settings.app.form.autoLaunchInBackground', + defaultMessage: 'Open in background', + }, + runInBackground: { + id: 'settings.app.form.runInBackground', + defaultMessage: 'Keep Ferdium in background when closing the window', + }, + startMinimized: { + id: 'settings.app.form.startMinimized', + defaultMessage: 'Start minimized', + }, + confirmOnQuit: { + id: 'settings.app.form.confirmOnQuit', + defaultMessage: 'Confirm when quitting Ferdium', + }, + enableSystemTray: { + id: 'settings.app.form.enableSystemTray', + defaultMessage: 'Always show Ferdium in System Tray', + }, + enableMenuBar: { + id: 'settings.app.form.enableMenuBar', + defaultMessage: 'Always show Ferdium in Menu Bar', + }, + reloadAfterResume: { + id: 'settings.app.form.reloadAfterResume', + defaultMessage: 'Reload Ferdium after system resume', + }, + reloadAfterResumeTime: { + id: 'settings.app.form.reloadAfterResumeTime', + defaultMessage: + 'Time to consider the system as idle/suspended (in minutes)', + }, + minimizeToSystemTray: { + id: 'settings.app.form.minimizeToSystemTray', + defaultMessage: 'Minimize Ferdium to system tray', + }, + closeToSystemTray: { + id: 'settings.app.form.closeToSystemTray', + defaultMessage: 'Close Ferdium to system tray', + }, + privateNotifications: { + id: 'settings.app.form.privateNotifications', + defaultMessage: "Don't show message content in notifications", + }, + clipboardNotifications: { + id: 'settings.app.form.clipboardNotifications', + defaultMessage: "Don't show notifications for clipboard events", + }, + notifyTaskBarOnMessage: { + id: 'settings.app.form.notifyTaskBarOnMessage', + defaultMessage: 'Notify TaskBar/Dock on new message', + }, + navigationBarBehaviour: { + id: 'settings.app.form.navigationBarBehaviour', + defaultMessage: 'Navigation bar behaviour', + }, + searchEngine: { + id: 'settings.app.form.searchEngine', + defaultMessage: 'Search engine', + }, + hibernateOnStartup: { + id: 'settings.app.form.hibernateOnStartup', + defaultMessage: 'Keep services in hibernation on startup', + }, + hibernationStrategy: { + id: 'settings.app.form.hibernationStrategy', + defaultMessage: 'Hibernation strategy', + }, + wakeUpStrategy: { + id: 'settings.app.form.wakeUpStrategy', + defaultMessage: 'Wake up strategy', + }, + wakeUpHibernationStrategy: { + id: 'settings.app.form.wakeUpHibernationStrategy', + defaultMessage: 'Hibernation strategy after automatic wake up', + }, + wakeUpHibernationSplay: { + id: 'settings.app.form.wakeUpHibernationSplay', + defaultMessage: 'Splay hibernate/wake cycles to reduce load', + }, + predefinedTodoServer: { + id: 'settings.app.form.predefinedTodoServer', + defaultMessage: 'Todo Server', + }, + customTodoServer: { + id: 'settings.app.form.customTodoServer', + defaultMessage: 'Custom Todo Server', + }, + enableLock: { + id: 'settings.app.form.enableLock', + defaultMessage: 'Enable Password Lock', + }, + lockPassword: { + id: 'settings.app.form.lockPassword', + defaultMessage: 'Password', + }, + useTouchIdToUnlock: { + id: 'settings.app.form.useTouchIdToUnlock', + defaultMessage: 'Allow using TouchID to unlock Ferdium', + }, + inactivityLock: { + id: 'settings.app.form.inactivityLock', + defaultMessage: 'Lock after inactivity', + }, + scheduledDNDEnabled: { + id: 'settings.app.form.scheduledDNDEnabled', + defaultMessage: 'Enable scheduled Do-not-Disturb', + }, + scheduledDNDStart: { + id: 'settings.app.form.scheduledDNDStart', + defaultMessage: 'From', + }, + scheduledDNDEnd: { + id: 'settings.app.form.scheduledDNDEnd', + defaultMessage: 'To', + }, + language: { + id: 'settings.app.form.language', + defaultMessage: 'Language', + }, + darkMode: { + id: 'settings.app.form.darkMode', + defaultMessage: 'Enable Dark Mode', + }, + adaptableDarkMode: { + id: 'settings.app.form.adaptableDarkMode', + defaultMessage: "Synchronize dark mode with my OS's dark mode setting", + }, + universalDarkMode: { + id: 'settings.app.form.universalDarkMode', + defaultMessage: 'Enable universal Dark Mode', + }, + splitMode: { + id: 'settings.app.form.splitMode', + defaultMessage: 'Enable Split View Mode', + }, + splitColumns: { + id: 'settings.app.form.splitColumns', + defaultMessage: 'Number of columns', + }, + serviceRibbonWidth: { + id: 'settings.app.form.serviceRibbonWidth', + defaultMessage: 'Sidebar width', + }, + sidebarServicesLocation: { + id: 'settings.app.form.sidebarServicesLocation', + defaultMessage: 'Sidebar Services Icons Location', + }, + iconSize: { + id: 'settings.app.form.iconSize', + defaultMessage: 'Service icon size', + }, + enableLongPressServiceHint: { + id: 'settings.app.form.enableLongPressServiceHint', + defaultMessage: 'Enable service shortcut hint on long press', + }, + useVerticalStyle: { + id: 'settings.app.form.useVerticalStyle', + defaultMessage: 'Use horizontal style', + }, + hideCollapseButton: { + id: 'settings.app.form.hideCollapseButton', + defaultMessage: 'Hide Collapse button', + }, + hideRecipesButton: { + id: 'settings.app.form.hideRecipesButton', + defaultMessage: 'Hide Recipes button', + }, + hideSplitModeButton: { + id: 'settings.app.form.hideSplitModeButton', + defaultMessage: 'Hide Split Mode button', + }, + useGrayscaleServices: { + id: 'settings.app.form.useGrayscaleServices', + defaultMessage: 'Use grayscale services', + }, + grayscaleServicesDim: { + id: 'settings.app.form.grayscaleServicesDim', + defaultMessage: 'Grayscale dim level', + }, + hideWorkspacesButton: { + id: 'settings.app.form.hideWorkspacesButton', + defaultMessage: 'Hide Workspace Drawer button', + }, + hideNotificationsButton: { + id: 'settings.app.form.hideNotificationsButton', + defaultMessage: 'Hide Notifications & Sound button', + }, + hideSettingsButton: { + id: 'settings.app.form.hideSettingsButton', + defaultMessage: 'Hide Settings button', + }, + alwaysShowWorkspaces: { + id: 'settings.app.form.alwaysShowWorkspaces', + defaultMessage: 'Always show workspace drawer', + }, + accentColor: { + id: 'settings.app.form.accentColor', + defaultMessage: 'Accent color', + }, + progressbarAccentColor: { + id: 'settings.app.form.progressbarAccentColor', + defaultMessage: 'Progressbar Accent color', + }, + showDisabledServices: { + id: 'settings.app.form.showDisabledServices', + defaultMessage: 'Display disabled services tabs', + }, + showServiceName: { + id: 'settings.app.form.showServiceName', + defaultMessage: 'Display service name under the icon', + }, + showMessageBadgeWhenMuted: { + id: 'settings.app.form.showMessagesBadgesWhenMuted', + defaultMessage: 'Show unread message badge when notifications are disabled', + }, + showDragArea: { + id: 'settings.app.form.showDragArea', + defaultMessage: 'Show draggable area on window', + }, + enableSpellchecking: { + id: 'settings.app.form.enableSpellchecking', + defaultMessage: 'Enable spell checking', + }, + enableGPUAcceleration: { + id: 'settings.app.form.enableGPUAcceleration', + defaultMessage: 'Enable GPU Acceleration', + }, + enableGlobalHideShortcut: { + id: 'settings.app.form.enableGlobalHideShortcut', + defaultMessage: 'Enable Global shortcut to hide Ferdium', + }, + beta: { + id: 'settings.app.form.beta', + defaultMessage: 'Include pre-releases', + }, + automaticUpdates: { + id: 'settings.app.form.automaticUpdates', + defaultMessage: 'Enable updates', + }, + enableTodos: { + id: 'settings.app.form.enableTodos', + defaultMessage: 'Enable Ferdium Todos', + }, + keepAllWorkspacesLoaded: { + id: 'settings.app.form.keepAllWorkspacesLoaded', + defaultMessage: 'Keep all workspaces loaded', + }, +}); + +interface EditSettingsScreenProps extends StoresProps { + intl: any; +} + +class EditSettingsScreen extends Component { + state = { + lockedPassword: '', + }; + + onSubmit(settingsData) { + const { todos, workspaces } = this.props.stores; + const { + app, + settings, + user, + todos: todosActions, + workspaces: workspaceActions, + } = this.props.actions; + + const useOriginalPassword = settingsData.lockedPassword === ''; + + this.setState({ + lockedPassword: useOriginalPassword ? '' : settingsData.lockedPassword, + }); + + app.launchOnStartup({ + enable: Boolean(settingsData.autoLaunchOnStart), + openInBackground: Boolean(settingsData.autoLaunchInBackground), + }); + + debug(`Updating settings store with data: ${settingsData}`); + + settings.update({ + type: 'app', + // TODO: The conversions might not be necessary once we convert to typescript + data: { + runInBackground: Boolean(settingsData.runInBackground), + enableSystemTray: Boolean(settingsData.enableSystemTray), + reloadAfterResume: Boolean(settingsData.reloadAfterResume), + reloadAfterResumeTime: Number(settingsData.reloadAfterResumeTime), + startMinimized: Boolean(settingsData.startMinimized), + confirmOnQuit: Boolean(settingsData.confirmOnQuit), + minimizeToSystemTray: Boolean(settingsData.minimizeToSystemTray), + closeToSystemTray: Boolean(settingsData.closeToSystemTray), + privateNotifications: Boolean(settingsData.privateNotifications), + clipboardNotifications: Boolean(settingsData.clipboardNotifications), + notifyTaskBarOnMessage: Boolean(settingsData.notifyTaskBarOnMessage), + navigationBarBehaviour: settingsData.navigationBarBehaviour, + searchEngine: settingsData.searchEngine, + hibernateOnStartup: Boolean(settingsData.hibernateOnStartup), + hibernationStrategy: Number(settingsData.hibernationStrategy), + wakeUpStrategy: Number(settingsData.wakeUpStrategy), + wakeUpHibernationStrategy: Number( + settingsData.wakeUpHibernationStrategy, + ), + wakeUpHibernationSplay: Boolean(settingsData.wakeUpHibernationSplay), + predefinedTodoServer: settingsData.predefinedTodoServer, + customTodoServer: settingsData.customTodoServer, + lockingFeatureEnabled: Boolean(settingsData.lockingFeatureEnabled), + lockedPassword: useOriginalPassword + ? this.props.stores.settings.all.app.lockedPassword + : hash(String(settingsData.lockedPassword)), + useTouchIdToUnlock: Boolean(settingsData.useTouchIdToUnlock), + inactivityLock: Number(settingsData.inactivityLock), + scheduledDNDEnabled: Boolean(settingsData.scheduledDNDEnabled), + scheduledDNDStart: settingsData.scheduledDNDStart, + scheduledDNDEnd: settingsData.scheduledDNDEnd, + enableGPUAcceleration: Boolean(settingsData.enableGPUAcceleration), + enableGlobalHideShortcut: Boolean( + settingsData.enableGlobalHideShortcut, + ), + showDisabledServices: Boolean(settingsData.showDisabledServices), + showServiceName: Boolean(settingsData.showServiceName), + darkMode: Boolean(settingsData.darkMode), + adaptableDarkMode: Boolean(settingsData.adaptableDarkMode), + universalDarkMode: Boolean(settingsData.universalDarkMode), + splitMode: Boolean(settingsData.splitMode), + splitColumns: Number(settingsData.splitColumns), + serviceRibbonWidth: Number(settingsData.serviceRibbonWidth), + sidebarServicesLocation: Number(settingsData.sidebarServicesLocation), + iconSize: Number(settingsData.iconSize), + enableLongPressServiceHint: Boolean( + settingsData.enableLongPressServiceHint, + ), + useVerticalStyle: Boolean(settingsData.useVerticalStyle), + hideCollapseButton: Boolean(settingsData.hideCollapseButton), + hideRecipesButton: Boolean(settingsData.hideRecipesButton), + hideSplitModeButton: Boolean(settingsData.hideSplitModeButton), + useGrayscaleServices: Boolean(settingsData.useGrayscaleServices), + grayscaleServicesDim: Number(settingsData.grayscaleServicesDim), + hideWorkspacesButton: Boolean(settingsData.hideWorkspacesButton), + hideNotificationsButton: Boolean(settingsData.hideNotificationsButton), + hideSettingsButton: Boolean(settingsData.hideSettingsButton), + alwaysShowWorkspaces: Boolean(settingsData.alwaysShowWorkspaces), + accentColor: settingsData.accentColor, + progressbarAccentColor: settingsData.progressbarAccentColor, + showMessageBadgeWhenMuted: Boolean( + settingsData.showMessageBadgeWhenMuted, + ), + showDragArea: Boolean(settingsData.showDragArea), + enableSpellchecking: Boolean(settingsData.enableSpellchecking), + spellcheckerLanguage: settingsData.spellcheckerLanguage, + userAgentPref: settingsData.userAgentPref, + beta: Boolean(settingsData.beta), // we need this info in the main process as well + automaticUpdates: Boolean(settingsData.automaticUpdates), // we need this info in the main process as well + locale: settingsData.locale, // we need this info in the main process as well + }, + }); + + user.update({ + userData: { + automaticUpdates: Boolean(settingsData.automaticUpdates), + beta: Boolean(settingsData.beta), + locale: settingsData.locale, + }, + }); + + const { keepAllWorkspacesLoaded } = workspaces.settings; + if ( + Boolean(keepAllWorkspacesLoaded) !== + Boolean(settingsData.keepAllWorkspacesLoaded) + ) { + workspaceActions.toggleKeepAllWorkspacesLoadedSetting(); + } + + if (todos.isFeatureActive) { + const { isFeatureEnabledByUser } = todos.settings; + if ( + Boolean(isFeatureEnabledByUser) !== Boolean(settingsData.enableTodos) + ) { + todosActions.toggleTodosFeatureVisibility(); + } + } + } + + openProcessManager() { + ipcRenderer.send('openProcessManager'); + } + + prepareForm() { + const { app, settings, user, todos, workspaces } = this.props.stores; + const { intl } = this.props; + const { lockedPassword } = this.state; + + const locales = getSelectOptions({ + locales: APP_LOCALES, + }); + + const navigationBarBehaviours = getSelectOptions({ + locales: NAVIGATION_BAR_BEHAVIOURS, + sort: false, + }); + + const searchEngines = getSelectOptions({ + locales: SEARCH_ENGINE_NAMES, + sort: false, + }); + + const hibernationStrategies = getSelectOptions({ + locales: HIBERNATION_STRATEGIES, + sort: false, + }); + + const wakeUpStrategies = getSelectOptions({ + locales: WAKE_UP_STRATEGIES, + sort: false, + }); + + const wakeUpHibernationStrategies = getSelectOptions({ + locales: WAKE_UP_HIBERNATION_STRATEGIES, + sort: false, + }); + + const todoApp = getSelectOptions({ + locales: TODO_APPS, + sort: false, + }); + + const sidebarWidth = getSelectOptions({ + locales: SIDEBAR_WIDTH, + sort: false, + }); + + const sidebarServicesLocation = getSelectOptions({ + locales: SIDEBAR_SERVICES_LOCATION, + sort: false, + }); + + const iconSizes = getSelectOptions({ + locales: ICON_SIZES, + sort: false, + }); + + const spellcheckingLanguages = getSelectOptions({ + locales: SPELLCHECKER_LOCALES, + automaticDetectionText: intl.formatMessage( + globalMessages.spellcheckerAutomaticDetection, + ), + }); + + const config: FormFields = { + fields: { + autoLaunchOnStart: { + label: intl.formatMessage(messages.autoLaunchOnStart), + value: app.autoLaunchOnStart, + default: DEFAULT_APP_SETTINGS.autoLaunchOnStart, + }, + autoLaunchInBackground: { + label: intl.formatMessage(messages.autoLaunchInBackground), + value: app.launchInBackground, + default: DEFAULT_APP_SETTINGS.autoLaunchInBackground, + }, + runInBackground: { + label: intl.formatMessage(messages.runInBackground), + value: settings.all.app.runInBackground, + default: DEFAULT_APP_SETTINGS.runInBackground, + }, + startMinimized: { + label: intl.formatMessage(messages.startMinimized), + value: settings.all.app.startMinimized, + default: DEFAULT_APP_SETTINGS.startMinimized, + }, + confirmOnQuit: { + label: intl.formatMessage(messages.confirmOnQuit), + value: settings.all.app.confirmOnQuit, + default: DEFAULT_APP_SETTINGS.confirmOnQuit, + }, + enableSystemTray: { + label: intl.formatMessage( + isMac ? messages.enableMenuBar : messages.enableSystemTray, + ), + value: settings.all.app.enableSystemTray, + default: DEFAULT_APP_SETTINGS.enableSystemTray, + }, + reloadAfterResume: { + label: intl.formatMessage(messages.reloadAfterResume), + value: settings.all.app.reloadAfterResume, + default: DEFAULT_APP_SETTINGS.reloadAfterResume, + }, + reloadAfterResumeTime: { + label: intl.formatMessage(messages.reloadAfterResumeTime), + value: settings.all.app.reloadAfterResumeTime, + default: DEFAULT_APP_SETTINGS.reloadAfterResumeTime, + }, + minimizeToSystemTray: { + label: intl.formatMessage(messages.minimizeToSystemTray), + value: settings.all.app.minimizeToSystemTray, + default: DEFAULT_APP_SETTINGS.minimizeToSystemTray, + }, + closeToSystemTray: { + label: intl.formatMessage(messages.closeToSystemTray), + value: settings.all.app.closeToSystemTray, + default: DEFAULT_APP_SETTINGS.closeToSystemTray, + }, + privateNotifications: { + label: intl.formatMessage(messages.privateNotifications), + value: settings.all.app.privateNotifications, + default: DEFAULT_APP_SETTINGS.privateNotifications, + }, + clipboardNotifications: { + label: intl.formatMessage(messages.clipboardNotifications), + value: settings.all.app.clipboardNotifications, + default: DEFAULT_APP_SETTINGS.clipboardNotifications, + }, + notifyTaskBarOnMessage: { + label: intl.formatMessage(messages.notifyTaskBarOnMessage), + value: settings.all.app.notifyTaskBarOnMessage, + default: DEFAULT_APP_SETTINGS.notifyTaskBarOnMessage, + }, + navigationBarBehaviour: { + label: intl.formatMessage(messages.navigationBarBehaviour), + value: settings.all.app.navigationBarBehaviour, + default: DEFAULT_APP_SETTINGS.navigationBarBehaviour, + options: navigationBarBehaviours, + }, + searchEngine: { + label: intl.formatMessage(messages.searchEngine), + value: settings.all.app.searchEngine, + default: DEFAULT_APP_SETTINGS.searchEngine, + options: searchEngines, + }, + hibernateOnStartup: { + label: intl.formatMessage(messages.hibernateOnStartup), + value: settings.all.app.hibernateOnStartup, + default: DEFAULT_APP_SETTINGS.hibernateOnStartup, + }, + hibernationStrategy: { + label: intl.formatMessage(messages.hibernationStrategy), + value: settings.all.app.hibernationStrategy, + options: hibernationStrategies, + default: DEFAULT_APP_SETTINGS.hibernationStrategy, + }, + wakeUpStrategy: { + label: intl.formatMessage(messages.wakeUpStrategy), + value: settings.all.app.wakeUpStrategy, + options: wakeUpStrategies, + default: DEFAULT_APP_SETTINGS.wakeUpStrategy, + }, + wakeUpHibernationStrategy: { + label: intl.formatMessage(messages.wakeUpHibernationStrategy), + value: settings.all.app.wakeUpHibernationStrategy, + options: wakeUpHibernationStrategies, + default: DEFAULT_APP_SETTINGS.wakeUpHibernationStrategy, + }, + wakeUpHibernationSplay: { + label: intl.formatMessage(messages.wakeUpHibernationSplay), + value: settings.all.app.wakeUpHibernationSplay, + default: DEFAULT_APP_SETTINGS.wakeUpHibernationSplay, + }, + predefinedTodoServer: { + label: intl.formatMessage(messages.predefinedTodoServer), + value: settings.all.app.predefinedTodoServer, + default: DEFAULT_APP_SETTINGS.predefinedTodoServer, + options: todoApp, + }, + customTodoServer: { + label: intl.formatMessage(messages.customTodoServer), + value: settings.all.app.customTodoServer, + default: DEFAULT_APP_SETTINGS.customTodoServer, + }, + lockingFeatureEnabled: { + label: intl.formatMessage(messages.enableLock), + value: settings.all.app.lockingFeatureEnabled || false, + default: DEFAULT_APP_SETTINGS.lockingFeatureEnabled, + }, + lockedPassword: { + label: intl.formatMessage(messages.lockPassword), + value: lockedPassword, + default: '', + type: 'password', + }, + useTouchIdToUnlock: { + label: intl.formatMessage(messages.useTouchIdToUnlock), + value: settings.all.app.useTouchIdToUnlock, + default: DEFAULT_APP_SETTINGS.useTouchIdToUnlock, + }, + inactivityLock: { + label: intl.formatMessage(messages.inactivityLock), + value: settings.all.app.inactivityLock, + default: 0, + type: 'number', + }, + scheduledDNDEnabled: { + label: intl.formatMessage(messages.scheduledDNDEnabled), + value: settings.all.app.scheduledDNDEnabled || false, + default: DEFAULT_APP_SETTINGS.scheduledDNDEnabled, + }, + scheduledDNDStart: { + label: intl.formatMessage(messages.scheduledDNDStart), + value: settings.all.app.scheduledDNDStart, + default: '17:00', + type: 'time', + }, + scheduledDNDEnd: { + label: intl.formatMessage(messages.scheduledDNDEnd), + value: settings.all.app.scheduledDNDEnd, + default: '09:00', + type: 'time', + }, + showDisabledServices: { + label: intl.formatMessage(messages.showDisabledServices), + value: settings.all.app.showDisabledServices, + default: DEFAULT_APP_SETTINGS.showDisabledServices, + }, + showServiceName: { + label: intl.formatMessage(messages.showServiceName), + value: settings.all.app.showServiceName, + default: DEFAULT_APP_SETTINGS.showServiceName, + }, + showMessageBadgeWhenMuted: { + label: intl.formatMessage(messages.showMessageBadgeWhenMuted), + value: settings.all.app.showMessageBadgeWhenMuted, + default: DEFAULT_APP_SETTINGS.showMessageBadgeWhenMuted, + }, + showDragArea: { + label: intl.formatMessage(messages.showDragArea), + value: settings.all.app.showDragArea, + default: DEFAULT_APP_SETTINGS.showDragArea, + }, + enableSpellchecking: { + label: intl.formatMessage(messages.enableSpellchecking), + value: settings.all.app.enableSpellchecking, + default: DEFAULT_APP_SETTINGS.enableSpellchecking, + }, + spellcheckerLanguage: { + label: intl.formatMessage(globalMessages.spellcheckerLanguage), + value: settings.all.app.spellcheckerLanguage, + options: spellcheckingLanguages, + default: DEFAULT_APP_SETTINGS.spellcheckerLanguage, + }, + userAgentPref: { + label: intl.formatMessage(globalMessages.userAgentPref), + value: settings.all.app.userAgentPref, + default: DEFAULT_APP_SETTINGS.userAgentPref, + placeholder: defaultUserAgent(), + }, + darkMode: { + label: intl.formatMessage(messages.darkMode), + value: settings.all.app.darkMode, + default: DEFAULT_APP_SETTINGS.darkMode, + }, + adaptableDarkMode: { + label: intl.formatMessage(messages.adaptableDarkMode), + value: settings.all.app.adaptableDarkMode, + default: DEFAULT_APP_SETTINGS.adaptableDarkMode, + }, + universalDarkMode: { + label: intl.formatMessage(messages.universalDarkMode), + value: settings.all.app.universalDarkMode, + default: DEFAULT_APP_SETTINGS.universalDarkMode, + }, + splitMode: { + label: intl.formatMessage(messages.splitMode), + value: settings.all.app.splitMode, + default: DEFAULT_APP_SETTINGS.splitMode, + }, + splitColumns: { + label: `${intl.formatMessage( + messages.splitColumns, + )} (${SPLIT_COLUMNS_MIN}-${SPLIT_COLUMNS_MAX})`, + value: settings.all.app.splitColumns, + default: DEFAULT_APP_SETTINGS.splitColumns, + }, + serviceRibbonWidth: { + label: intl.formatMessage(messages.serviceRibbonWidth), + value: settings.all.app.serviceRibbonWidth, + default: DEFAULT_APP_SETTINGS.serviceRibbonWidth, + options: sidebarWidth, + }, + sidebarServicesLocation: { + label: intl.formatMessage(messages.sidebarServicesLocation), + value: settings.all.app.sidebarServicesLocation, + default: DEFAULT_APP_SETTINGS.sidebarServicesLocation, + options: sidebarServicesLocation, + }, + iconSize: { + label: intl.formatMessage(messages.iconSize), + value: settings.all.app.iconSize, + default: DEFAULT_APP_SETTINGS.iconSize, + options: iconSizes, + }, + enableLongPressServiceHint: { + label: intl.formatMessage(messages.enableLongPressServiceHint), + value: settings.all.app.enableLongPressServiceHint, + default: DEFAULT_APP_SETTINGS.enableLongPressServiceHint, + }, + useVerticalStyle: { + label: intl.formatMessage(messages.useVerticalStyle), + value: settings.all.app.useVerticalStyle, + default: DEFAULT_APP_SETTINGS.useVerticalStyle, + }, + hideCollapseButton: { + label: intl.formatMessage(messages.hideCollapseButton), + value: settings.all.app.hideCollapseButton, + default: DEFAULT_APP_SETTINGS.hideCollapseButton, + }, + hideRecipesButton: { + label: intl.formatMessage(messages.hideRecipesButton), + value: settings.all.app.hideRecipesButton, + default: DEFAULT_APP_SETTINGS.hideRecipesButton, + }, + hideSplitModeButton: { + label: intl.formatMessage(messages.hideSplitModeButton), + value: settings.all.app.hideSplitModeButton, + default: DEFAULT_APP_SETTINGS.hideSplitModeButton, + }, + useGrayscaleServices: { + label: intl.formatMessage(messages.useGrayscaleServices), + value: settings.all.app.useGrayscaleServices, + default: DEFAULT_APP_SETTINGS.useGrayscaleServices, + }, + grayscaleServicesDim: { + label: intl.formatMessage(messages.grayscaleServicesDim), + value: settings.all.app.grayscaleServicesDim, + default: DEFAULT_APP_SETTINGS.grayscaleServicesDim, + }, + hideWorkspacesButton: { + label: intl.formatMessage(messages.hideWorkspacesButton), + value: settings.all.app.hideWorkspacesButton, + default: DEFAULT_APP_SETTINGS.hideWorkspacesButton, + }, + hideNotificationsButton: { + label: intl.formatMessage(messages.hideNotificationsButton), + value: settings.all.app.hideNotificationsButton, + default: DEFAULT_APP_SETTINGS.hideNotificationsButton, + }, + hideSettingsButton: { + label: intl.formatMessage(messages.hideSettingsButton), + value: settings.all.app.hideSettingsButton, + default: DEFAULT_APP_SETTINGS.hideSettingsButton, + }, + alwaysShowWorkspaces: { + label: intl.formatMessage(messages.alwaysShowWorkspaces), + value: settings.all.app.alwaysShowWorkspaces, + default: DEFAULT_APP_SETTINGS.alwaysShowWorkspaces, + }, + accentColor: { + label: intl.formatMessage(messages.accentColor), + value: settings.all.app.accentColor, + default: DEFAULT_APP_SETTINGS.accentColor, + }, + progressbarAccentColor: { + label: intl.formatMessage(messages.progressbarAccentColor), + value: settings.all.app.progressbarAccentColor, + default: DEFAULT_APP_SETTINGS.progressbarAccentColor, + }, + enableGPUAcceleration: { + label: intl.formatMessage(messages.enableGPUAcceleration), + value: settings.all.app.enableGPUAcceleration, + default: DEFAULT_APP_SETTINGS.enableGPUAcceleration, + }, + enableGlobalHideShortcut: { + label: intl.formatMessage(messages.enableGlobalHideShortcut), + value: settings.all.app.enableGlobalHideShortcut, + default: DEFAULT_APP_SETTINGS.enableGlobalHideShortcut, + }, + locale: { + label: intl.formatMessage(messages.language), + value: app.locale, + options: locales, + default: DEFAULT_APP_SETTINGS.locale, + }, + beta: { + label: intl.formatMessage(messages.beta), + value: user.data.beta, + default: DEFAULT_APP_SETTINGS.beta, + }, + automaticUpdates: { + label: intl.formatMessage(messages.automaticUpdates), + value: settings.app.automaticUpdates, + default: DEFAULT_APP_SETTINGS.automaticUpdates, + }, + }, + }; + + if (workspaces.isFeatureActive) { + config.fields.keepAllWorkspacesLoaded = { + label: intl.formatMessage(messages.keepAllWorkspacesLoaded), + value: workspaces.settings.keepAllWorkspacesLoaded, + default: DEFAULT_SETTING_KEEP_ALL_WORKSPACES_LOADED, + }; + } + + if (todos.isFeatureActive) { + config.fields.enableTodos = { + label: intl.formatMessage(messages.enableTodos), + value: todos.settings.isFeatureEnabledByUser, + default: DEFAULT_IS_FEATURE_ENABLED_BY_USER, + }; + } + + // @ts-ignore: Remove this ignore once mobx-react-form v4 with typescript + // support has been released. + return new Form(config); + } + + render(): ReactElement { + const { app, services } = this.props.stores; + const { + updateStatus, + updateStatusTypes, + isClearingAllCache, + lockingFeatureEnabled, + } = app; + const { checkForUpdates, installUpdate, clearAllCache } = + this.props.actions.app; + const form = this.prepareForm(); + + return ( + + this.onSubmit(d)} + getCacheSize={() => app.cacheSize} + isClearingAllCache={isClearingAllCache} + onClearAllCache={clearAllCache} + lockingFeatureEnabled={lockingFeatureEnabled} + automaticUpdates={this.props.stores.settings.app.automaticUpdates} + isDarkmodeEnabled={this.props.stores.settings.app.darkMode} + isAdaptableDarkModeEnabled={ + this.props.stores.settings.app.adaptableDarkMode + } + isUseGrayscaleServicesEnabled={ + this.props.stores.settings.app.useGrayscaleServices + } + isSplitModeEnabled={this.props.stores.settings.app.splitMode} + isTodosActivated={this.props.stores.todos.isFeatureEnabledByUser} + isUsingCustomTodoService={ + this.props.stores.todos.isUsingCustomTodoService + } + openProcessManager={() => this.openProcessManager()} + hasAddedTodosAsService={services.isTodosServiceAdded} + isOnline={app.isOnline} + /> + + ); + } +} + +export default injectIntl( + inject('stores', 'actions')(observer(EditSettingsScreen)), +); diff --git a/src/containers/settings/EditUserScreen.js b/src/containers/settings/EditUserScreen.js deleted file mode 100644 index baa2d7b71..000000000 --- a/src/containers/settings/EditUserScreen.js +++ /dev/null @@ -1,162 +0,0 @@ -import { Component } from 'react'; -import PropTypes from 'prop-types'; -import { inject, observer } from 'mobx-react'; -import { defineMessages, injectIntl } from 'react-intl'; - -import UserStore from '../../stores/UserStore'; -import Form from '../../lib/Form'; -import EditUserForm from '../../components/settings/user/EditUserForm'; -import ErrorBoundary from '../../components/util/ErrorBoundary'; - -import { required, email, minLength } from '../../helpers/validation-helpers'; - -const messages = defineMessages({ - firstname: { - id: 'settings.user.form.firstname', - defaultMessage: 'First Name', - }, - lastname: { - id: 'settings.user.form.lastname', - defaultMessage: 'Last Name', - }, - email: { - id: 'settings.user.form.email', - defaultMessage: 'Email', - }, - accountTypeLabel: { - id: 'settings.user.form.accountType.label', - defaultMessage: 'Account type', - }, - accountTypeIndividual: { - id: 'settings.user.form.accountType.individual', - defaultMessage: 'Individual', - }, - accountTypeNonProfit: { - id: 'settings.user.form.accountType.non-profit', - defaultMessage: 'Non-Profit', - }, - accountTypeCompany: { - id: 'settings.user.form.accountType.company', - defaultMessage: 'Company', - }, - currentPassword: { - id: 'settings.user.form.currentPassword', - defaultMessage: 'Current password', - }, - newPassword: { - id: 'settings.user.form.newPassword', - defaultMessage: 'New password', - }, -}); - -class EditUserScreen extends Component { - componentWillUnmount() { - this.props.actions.user.resetStatus(); - } - - onSubmit(userData) { - const { update } = this.props.actions.user; - - update({ userData }); - - document.querySelector('#form').scrollIntoView({ behavior: 'smooth' }); - } - - prepareForm(user) { - const { intl } = this.props; - - const config = { - fields: { - firstname: { - label: intl.formatMessage(messages.firstname), - placeholder: intl.formatMessage(messages.firstname), - value: user.firstname, - validators: [required], - }, - lastname: { - label: intl.formatMessage(messages.lastname), - placeholder: intl.formatMessage(messages.lastname), - value: user.lastname, - validators: [required], - }, - email: { - label: intl.formatMessage(messages.email), - placeholder: intl.formatMessage(messages.email), - value: user.email, - validators: [required, email], - }, - accountType: { - value: user.accountType, - validators: [required], - label: intl.formatMessage(messages.accountTypeLabel), - options: [ - { - value: 'individual', - label: intl.formatMessage(messages.accountTypeIndividual), - }, - { - value: 'non-profit', - label: intl.formatMessage(messages.accountTypeNonProfit), - }, - { - value: 'company', - label: intl.formatMessage(messages.accountTypeCompany), - }, - ], - }, - organization: { - label: intl.formatMessage(messages.accountTypeCompany), - placeholder: intl.formatMessage(messages.accountTypeCompany), - value: user.organization, - }, - oldPassword: { - label: intl.formatMessage(messages.currentPassword), - type: 'password', - validators: [minLength(6)], - }, - newPassword: { - label: intl.formatMessage(messages.newPassword), - type: 'password', - validators: [minLength(6)], - }, - }, - }; - - return new Form(config); - } - - render() { - const { user } = this.props.stores; - - if (user.getUserInfoRequest.isExecuting) { - return
Loading...
; - } - - const form = this.prepareForm(user.data); - - return ( - - this.onSubmit(d)} - /> - - ); - } -} - -EditUserScreen.propTypes = { - stores: PropTypes.shape({ - user: PropTypes.instanceOf(UserStore).isRequired, - }).isRequired, - actions: PropTypes.shape({ - user: PropTypes.instanceOf(UserStore).isRequired, - }).isRequired, -}; - -export default injectIntl( - inject('stores', 'actions')(observer(EditUserScreen)), -); diff --git a/src/containers/settings/EditUserScreen.tsx b/src/containers/settings/EditUserScreen.tsx new file mode 100644 index 000000000..6ab288d6f --- /dev/null +++ b/src/containers/settings/EditUserScreen.tsx @@ -0,0 +1,158 @@ +import { Component, ReactElement } from 'react'; +import { inject, observer } from 'mobx-react'; +import { defineMessages, injectIntl } from 'react-intl'; + +import { StoresProps } from 'src/@types/ferdium-components.types'; +import { FormFields } from 'src/@types/mobx-form.types'; +import Form from '../../lib/Form'; +import EditUserForm from '../../components/settings/user/EditUserForm'; +import ErrorBoundary from '../../components/util/ErrorBoundary'; + +import { required, email, minLength } from '../../helpers/validation-helpers'; + +const messages = defineMessages({ + firstname: { + id: 'settings.user.form.firstname', + defaultMessage: 'First Name', + }, + lastname: { + id: 'settings.user.form.lastname', + defaultMessage: 'Last Name', + }, + email: { + id: 'settings.user.form.email', + defaultMessage: 'Email', + }, + accountTypeLabel: { + id: 'settings.user.form.accountType.label', + defaultMessage: 'Account type', + }, + accountTypeIndividual: { + id: 'settings.user.form.accountType.individual', + defaultMessage: 'Individual', + }, + accountTypeNonProfit: { + id: 'settings.user.form.accountType.non-profit', + defaultMessage: 'Non-Profit', + }, + accountTypeCompany: { + id: 'settings.user.form.accountType.company', + defaultMessage: 'Company', + }, + currentPassword: { + id: 'settings.user.form.currentPassword', + defaultMessage: 'Current password', + }, + newPassword: { + id: 'settings.user.form.newPassword', + defaultMessage: 'New password', + }, +}); + +interface EditUserScreenProps extends StoresProps { + intl: any; +} + +class EditUserScreen extends Component { + componentWillUnmount() { + this.props.actions.user.resetStatus(); + } + + onSubmit(userData) { + const { update } = this.props.actions.user; + + update({ userData }); + + document.querySelector('#form')?.scrollIntoView({ behavior: 'smooth' }); + } + + prepareForm(user) { + const { intl } = this.props; + + const config: FormFields = { + fields: { + firstname: { + label: intl.formatMessage(messages.firstname), + placeholder: intl.formatMessage(messages.firstname), + value: user.firstname, + validators: [required], + }, + lastname: { + label: intl.formatMessage(messages.lastname), + placeholder: intl.formatMessage(messages.lastname), + value: user.lastname, + validators: [required], + }, + email: { + label: intl.formatMessage(messages.email), + placeholder: intl.formatMessage(messages.email), + value: user.email, + validators: [required, email], + }, + accountType: { + value: user.accountType, + validators: [required], + label: intl.formatMessage(messages.accountTypeLabel), + options: [ + { + value: 'individual', + label: intl.formatMessage(messages.accountTypeIndividual), + }, + { + value: 'non-profit', + label: intl.formatMessage(messages.accountTypeNonProfit), + }, + { + value: 'company', + label: intl.formatMessage(messages.accountTypeCompany), + }, + ], + }, + organization: { + label: intl.formatMessage(messages.accountTypeCompany), + placeholder: intl.formatMessage(messages.accountTypeCompany), + value: user.organization, + }, + oldPassword: { + label: intl.formatMessage(messages.currentPassword), + type: 'password', + validators: [minLength(6)], + }, + newPassword: { + label: intl.formatMessage(messages.newPassword), + type: 'password', + validators: [minLength(6)], + }, + }, + }; + + // @ts-ignore: Remove this ignore once mobx-react-form v4 with typescript + // support has been released. + return new Form(config); + } + + render(): ReactElement { + const { user } = this.props.stores; + + if (user.getUserInfoRequest.isExecuting) { + return
Loading...
; + } + + const form = this.prepareForm(user.data); + + return ( + + this.onSubmit(d)} + /> + + ); + } +} + +export default injectIntl<'intl', EditUserScreenProps>( + inject('stores', 'actions')(observer(EditUserScreen)), +); diff --git a/src/containers/settings/InviteScreen.js b/src/containers/settings/InviteScreen.js deleted file mode 100644 index 86723554c..000000000 --- a/src/containers/settings/InviteScreen.js +++ /dev/null @@ -1,42 +0,0 @@ -import { Component } from 'react'; -import PropTypes from 'prop-types'; -import { inject, observer } from 'mobx-react'; - -import Invite from '../../components/auth/Invite'; -import ErrorBoundary from '../../components/util/ErrorBoundary'; -import UserStore from '../../stores/UserStore'; - -class InviteScreen extends Component { - componentWillUnmount() { - this.props.stores.user.inviteRequest.reset(); - } - - render() { - const { actions } = this.props; - const { user } = this.props.stores; - - return ( - - - - ); - } -} - -InviteScreen.propTypes = { - actions: PropTypes.shape({ - user: PropTypes.instanceOf(UserStore).isRequired, - }).isRequired, - stores: PropTypes.shape({ - user: PropTypes.instanceOf(UserStore).isRequired, - }).isRequired, -}; - -export default inject('stores', 'actions')(observer(InviteScreen)); diff --git a/src/containers/settings/InviteScreen.tsx b/src/containers/settings/InviteScreen.tsx new file mode 100644 index 000000000..ff192783c --- /dev/null +++ b/src/containers/settings/InviteScreen.tsx @@ -0,0 +1,32 @@ +import { Component, ReactNode } from 'react'; +import { inject, observer } from 'mobx-react'; + +import { StoresProps } from 'src/@types/ferdium-components.types'; +import Invite from '../../components/auth/Invite'; +import ErrorBoundary from '../../components/util/ErrorBoundary'; + +class InviteScreen extends Component { + componentWillUnmount(): void { + this.props.stores.user.inviteRequest.reset(); + } + + render(): ReactNode { + const { actions } = this.props; + const { user } = this.props.stores; + + return ( + + + + ); + } +} + +export default inject('stores', 'actions')(observer(InviteScreen)); diff --git a/src/containers/settings/RecipesScreen.js b/src/containers/settings/RecipesScreen.js deleted file mode 100644 index e8f0a7282..000000000 --- a/src/containers/settings/RecipesScreen.js +++ /dev/null @@ -1,204 +0,0 @@ -import { readJsonSync } from 'fs-extra'; -import { Component } from 'react'; -import PropTypes from 'prop-types'; -import { autorun } from 'mobx'; -import { inject, observer } from 'mobx-react'; - -import RecipePreviewsStore from '../../stores/RecipePreviewsStore'; -import RecipeStore from '../../stores/RecipesStore'; -import ServiceStore from '../../stores/ServicesStore'; -import UserStore from '../../stores/UserStore'; - -import RecipesDashboard from '../../components/settings/recipes/RecipesDashboard'; -import ErrorBoundary from '../../components/util/ErrorBoundary'; -import { CUSTOM_WEBSITE_RECIPE_ID, FRANZ_DEV_DOCS } from '../../config'; -import { userDataRecipesPath } from '../../environment-remote'; -import { asarRecipesPath } from '../../helpers/asar-helpers'; -import { communityRecipesStore } from '../../features/communityRecipes'; -import RecipePreview from '../../models/RecipePreview'; -import AppStore from '../../stores/AppStore'; -import { openPath } from '../../helpers/url-helpers'; - -class RecipesScreen extends Component { - static propTypes = { - params: PropTypes.shape({ - filter: PropTypes.string, - }), - }; - - static defaultProps = { - params: { - filter: null, - }, - }; - - state = { - needle: null, - currentFilter: 'featured', - }; - - autorunDisposer = null; - - customRecipes = []; - - constructor(props) { - super(props); - - this.customRecipes = readJsonSync(asarRecipesPath('all.json')); - } - - componentDidMount() { - this.autorunDisposer = autorun(() => { - const { filter } = this.props.params; - const { currentFilter } = this.state; - - if (filter === 'all' && currentFilter !== 'all') { - this.setState({ currentFilter: 'all' }); - } else if (filter === 'featured' && currentFilter !== 'featured') { - this.setState({ currentFilter: 'featured' }); - } else if (filter === 'dev' && currentFilter !== 'dev') { - this.setState({ currentFilter: 'dev' }); - } - }); - } - - componentWillUnmount() { - this.props.stores.services.resetStatus(); - this.autorunDisposer(); - } - - searchRecipes(needle) { - if (needle === '') { - this.resetSearch(); - } else { - const { search } = this.props.actions.recipePreview; - this.setState({ needle }); - search({ needle }); - } - } - - _sortByName(recipe1, recipe2) { - if (recipe1.name.toLowerCase() < recipe2.name.toLowerCase()) { - return -1; - } - if (recipe1.name.toLowerCase() > recipe2.name.toLowerCase()) { - return 1; - } - return 0; - } - - prepareRecipes(recipes) { - return ( - recipes - // Filter out duplicate recipes - .filter((recipe, index, self) => { - const ids = self.map(rec => rec.id); - return ids.indexOf(recipe.id) === index; - - // Sort alphabetically - }) - .sort(this._sortByName) - ); - } - - // Create an array of RecipePreviews from an array of recipe objects - createPreviews(recipes) { - return recipes.map(recipe => new RecipePreview(recipe)); - } - - resetSearch() { - this.setState({ needle: null }); - } - - render() { - const { recipePreviews, recipes, services } = this.props.stores; - - const { app: appActions, service: serviceActions } = this.props.actions; - - const { filter } = this.props.params; - let recipeFilter; - - if (filter === 'all') { - recipeFilter = this.prepareRecipes([ - ...recipePreviews.all, - ...this.createPreviews(this.customRecipes), - ]); - } else if (filter === 'dev') { - recipeFilter = communityRecipesStore.communityRecipes; - } else { - recipeFilter = recipePreviews.featured; - } - recipeFilter = recipeFilter.sort(this._sortByName); - - const allRecipes = this.state.needle - ? this.prepareRecipes([ - // All search recipes from server - ...recipePreviews.searchResults, - // All search recipes from local recipes - ...this.createPreviews( - this.customRecipes.filter( - service => - service.name - .toLowerCase() - .includes(this.state.needle.toLowerCase()) || - (service.aliases || []).some(alias => - alias.toLowerCase().includes(this.state.needle.toLowerCase()), - ), - ), - ), - ]).sort(this._sortByName) - : recipeFilter; - - const customWebsiteRecipe = recipePreviews.all.find( - service => service.id === CUSTOM_WEBSITE_RECIPE_ID, - ); - - const isLoading = recipePreviews.featuredRecipePreviewsRequest.isExecuting - || recipePreviews.allRecipePreviewsRequest.isExecuting - || recipes.installRecipeRequest.isExecuting - || recipePreviews.searchRecipePreviewsRequest.isExecuting; - - const recipeDirectory = userDataRecipesPath('dev'); - - return ( - - this.searchRecipes(e)} - resetSearch={() => this.resetSearch()} - searchNeedle={this.state.needle} - serviceStatus={services.actionStatus} - recipeFilter={filter} - recipeDirectory={recipeDirectory} - openRecipeDirectory={() => openPath(recipeDirectory)} - openDevDocs={() => - appActions.openExternalUrl({ url: FRANZ_DEV_DOCS }) - } - /> - - ); - } -} - -RecipesScreen.propTypes = { - stores: PropTypes.shape({ - recipePreviews: PropTypes.instanceOf(RecipePreviewsStore).isRequired, - recipes: PropTypes.instanceOf(RecipeStore).isRequired, - services: PropTypes.instanceOf(ServiceStore).isRequired, - user: PropTypes.instanceOf(UserStore).isRequired, - }).isRequired, - actions: PropTypes.shape({ - app: PropTypes.instanceOf(AppStore).isRequired, - service: PropTypes.instanceOf(ServiceStore).isRequired, - recipePreview: PropTypes.shape({ - search: PropTypes.func.isRequired, - }).isRequired, - }).isRequired, -}; - -export default inject('stores', 'actions')(observer(RecipesScreen)); diff --git a/src/containers/settings/RecipesScreen.tsx b/src/containers/settings/RecipesScreen.tsx new file mode 100644 index 000000000..c50ff246e --- /dev/null +++ b/src/containers/settings/RecipesScreen.tsx @@ -0,0 +1,188 @@ +import { readJsonSync } from 'fs-extra'; +import { Component, ReactElement } from 'react'; +import { autorun, IReactionDisposer } from 'mobx'; +import { inject, observer } from 'mobx-react'; + +import { StoresProps } from 'src/@types/ferdium-components.types'; +import Recipe from 'src/models/Recipe'; +import RecipesDashboard from '../../components/settings/recipes/RecipesDashboard'; +import ErrorBoundary from '../../components/util/ErrorBoundary'; +import { CUSTOM_WEBSITE_RECIPE_ID, FRANZ_DEV_DOCS } from '../../config'; +import { userDataRecipesPath } from '../../environment-remote'; +import { asarRecipesPath } from '../../helpers/asar-helpers'; +import { communityRecipesStore } from '../../features/communityRecipes'; +import RecipePreview from '../../models/RecipePreview'; +import { openPath } from '../../helpers/url-helpers'; + +interface RecipesScreenProps extends StoresProps { + params: { + filter?: string | null; + }; +} + +class RecipesScreen extends Component { + state: { + needle: string | null; + currentFilter: string; + } = { + needle: null, + currentFilter: 'featured', + }; + + autorunDisposer: IReactionDisposer | null = null; + + customRecipes: Recipe[] = []; + + constructor(props: RecipesScreenProps) { + super(props); + + this.props.params.filter = this.props.params.filter || null; + + this.customRecipes = readJsonSync(asarRecipesPath('all.json')); + } + + componentDidMount(): void { + this.autorunDisposer = autorun(() => { + const { filter } = this.props.params; + const { currentFilter } = this.state; + + if (filter === 'all' && currentFilter !== 'all') { + this.setState({ currentFilter: 'all' }); + } else if (filter === 'featured' && currentFilter !== 'featured') { + this.setState({ currentFilter: 'featured' }); + } else if (filter === 'dev' && currentFilter !== 'dev') { + this.setState({ currentFilter: 'dev' }); + } + }); + } + + componentWillUnmount(): void { + this.props.stores.services.resetStatus(); + + if (typeof this.autorunDisposer === 'function') { + this.autorunDisposer(); + } + } + + searchRecipes(needle: string | null): void { + if (needle === '') { + this.resetSearch(); + } else { + const { search } = this.props.actions.recipePreview; + this.setState({ needle }); + search({ needle }); + } + } + + _sortByName(recipe1, recipe2): number { + if (recipe1.name.toLowerCase() < recipe2.name.toLowerCase()) { + return -1; + } + if (recipe1.name.toLowerCase() > recipe2.name.toLowerCase()) { + return 1; + } + return 0; + } + + prepareRecipes(recipes: RecipePreview[]): RecipePreview[] { + return ( + recipes + // Filter out duplicate recipes + .filter((recipe, index, self) => { + const ids = self.map(rec => rec.id); + return ids.indexOf(recipe.id) === index; + + // Sort alphabetically + }) + .sort(this._sortByName) + ); + } + + // Create an array of RecipePreviews from an array of recipe objects + createPreviews(recipes: Recipe[]) { + return recipes.map((recipe: any) => new RecipePreview(recipe)); + } + + resetSearch(): void { + this.setState({ needle: null }); + } + + render(): ReactElement { + const { recipePreviews, recipes, services } = this.props.stores; + + const { app: appActions, service: serviceActions } = this.props.actions; + + const { filter } = this.props.params; + let recipeFilter; + + if (filter === 'all') { + recipeFilter = this.prepareRecipes([ + ...recipePreviews.all, + ...this.createPreviews(this.customRecipes), + ]); + } else if (filter === 'dev') { + recipeFilter = communityRecipesStore.communityRecipes; + } else { + recipeFilter = recipePreviews.featured; + } + recipeFilter = recipeFilter.sort(this._sortByName); + + const { needle } = this.state; + const allRecipes = + needle !== null + ? this.prepareRecipes([ + // All search recipes from server + ...recipePreviews.searchResults, + // All search recipes from local recipes + ...this.createPreviews( + this.customRecipes.filter( + (recipe: Recipe) => + recipe.name.toLowerCase().includes(needle.toLowerCase()) || + (recipe.aliases || []).some(alias => + alias.toLowerCase().includes(needle.toLowerCase()), + ), + ), + ), + ]).sort(this._sortByName) + : recipeFilter; + + const customWebsiteRecipe = recipePreviews.all.find( + service => service.id === CUSTOM_WEBSITE_RECIPE_ID, + ); + + const isLoading = + recipePreviews.featuredRecipePreviewsRequest.isExecuting || + recipePreviews.allRecipePreviewsRequest.isExecuting || + recipes.installRecipeRequest.isExecuting || + recipePreviews.searchRecipePreviewsRequest.isExecuting; + + const recipeDirectory = userDataRecipesPath('dev'); + + return ( + + this.searchRecipes(e)} + resetSearch={() => this.resetSearch()} + searchNeedle={this.state.needle} + serviceStatus={services.actionStatus} + recipeFilter={filter} + recipeDirectory={recipeDirectory} + openRecipeDirectory={() => openPath(recipeDirectory)} + openDevDocs={() => + appActions.openExternalUrl({ url: FRANZ_DEV_DOCS }) + } + /> + + ); + } +} + +export default inject('stores', 'actions')(observer(RecipesScreen)); diff --git a/src/containers/settings/ServicesScreen.js b/src/containers/settings/ServicesScreen.js deleted file mode 100644 index 2970b2a54..000000000 --- a/src/containers/settings/ServicesScreen.js +++ /dev/null @@ -1,69 +0,0 @@ -import { Component } from 'react'; -import PropTypes from 'prop-types'; -import { inject, observer } from 'mobx-react'; -import { RouterStore } from 'mobx-react-router'; - -// import RecipePreviewsStore from '../../stores/RecipePreviewsStore'; -import UserStore from '../../stores/UserStore'; -import ServiceStore from '../../stores/ServicesStore'; - -import ServicesDashboard from '../../components/settings/services/ServicesDashboard'; -import ErrorBoundary from '../../components/util/ErrorBoundary'; - -class ServicesScreen extends Component { - componentWillUnmount() { - this.props.actions.service.resetFilter(); - this.props.actions.service.resetStatus(); - } - - deleteService() { - this.props.actions.service.deleteService(); - this.props.stores.services.resetFilter(); - } - - render() { - const { user, services, router } = this.props.stores; - const { toggleService, filter, resetFilter } = this.props.actions.service; - const isLoading = services.allServicesRequest.isExecuting; - - let allServices = services.all; - if (services.filterNeedle !== null) { - allServices = services.filtered; - } - - return ( - - this.deleteService()} - toggleService={toggleService} - isLoading={isLoading} - filterServices={filter} - resetFilter={resetFilter} - goTo={router.push} - servicesRequestFailed={ - services.allServicesRequest.wasExecuted && - services.allServicesRequest.isError - } - retryServicesRequest={() => services.allServicesRequest.reload()} - searchNeedle={services.filterNeedle} - /> - - ); - } -} - -ServicesScreen.propTypes = { - stores: PropTypes.shape({ - user: PropTypes.instanceOf(UserStore).isRequired, - services: PropTypes.instanceOf(ServiceStore).isRequired, - router: PropTypes.instanceOf(RouterStore).isRequired, - }).isRequired, - actions: PropTypes.shape({ - service: PropTypes.instanceOf(ServiceStore).isRequired, - }).isRequired, -}; - -export default inject('stores', 'actions')(observer(ServicesScreen)); diff --git a/src/containers/settings/ServicesScreen.tsx b/src/containers/settings/ServicesScreen.tsx new file mode 100644 index 000000000..615747382 --- /dev/null +++ b/src/containers/settings/ServicesScreen.tsx @@ -0,0 +1,53 @@ +import { Component, ReactElement } from 'react'; +import { inject, observer } from 'mobx-react'; + +import { StoresProps } from 'src/@types/ferdium-components.types'; +import ServicesDashboard from '../../components/settings/services/ServicesDashboard'; +import ErrorBoundary from '../../components/util/ErrorBoundary'; + +class ServicesScreen extends Component { + componentWillUnmount(): void { + this.props.actions.service.resetFilter(); + this.props.actions.service.resetStatus(); + } + + deleteService(): void { + this.props.actions.service.deleteService(); + this.props.actions.service.resetFilter(); + } + + render(): ReactElement { + const { user, services, router } = this.props.stores; + const { toggleService, filter, resetFilter } = this.props.actions.service; + const isLoading = services.allServicesRequest.isExecuting; + + let allServices = services.all; + if (services.filterNeedle !== null) { + allServices = services.filtered; + } + + return ( + + this.deleteService()} + toggleService={toggleService} + isLoading={isLoading} + filterServices={filter} + resetFilter={resetFilter} + goTo={router.push} + servicesRequestFailed={ + services.allServicesRequest.wasExecuted && + services.allServicesRequest.isError + } + retryServicesRequest={() => services.allServicesRequest.reload()} + searchNeedle={services.filterNeedle} + /> + + ); + } +} + +export default inject('stores', 'actions')(observer(ServicesScreen)); diff --git a/src/containers/settings/SettingsWindow.js b/src/containers/settings/SettingsWindow.js deleted file mode 100644 index 0e6ce4df3..000000000 --- a/src/containers/settings/SettingsWindow.js +++ /dev/null @@ -1,59 +0,0 @@ -import { Component } from 'react'; -import ReactDOM from 'react-dom'; -import PropTypes from 'prop-types'; -import { observer, inject } from 'mobx-react'; - -import ServicesStore from '../../stores/ServicesStore'; - -import Layout from '../../components/settings/SettingsLayout'; -import Navigation from '../../components/settings/navigation/SettingsNavigation'; -import ErrorBoundary from '../../components/util/ErrorBoundary'; -import { workspaceStore } from '../../features/workspaces'; -import UIStore from '../../stores/UIStore'; - -class SettingsContainer extends Component { - portalRoot = document.querySelector('#portalContainer'); - - el = document.createElement('div'); - - componentDidMount() { - this.portalRoot.append(this.el); - } - - componentWillUnmount() { - this.el.remove(); - } - - render() { - const { children, stores } = this.props; - const { closeSettings } = this.props.actions.ui; - - const navigation = ( - - ); - - return ReactDOM.createPortal( - - - {children} - - , - this.el, - ); - } -} - -SettingsContainer.propTypes = { - children: PropTypes.element.isRequired, - stores: PropTypes.shape({ - services: PropTypes.instanceOf(ServicesStore).isRequired, - }).isRequired, - actions: PropTypes.shape({ - ui: PropTypes.instanceOf(UIStore).isRequired, - }).isRequired, -}; - -export default inject('stores', 'actions')(observer(SettingsContainer)); diff --git a/src/containers/settings/SettingsWindow.tsx b/src/containers/settings/SettingsWindow.tsx new file mode 100644 index 000000000..fb1e2fd2d --- /dev/null +++ b/src/containers/settings/SettingsWindow.tsx @@ -0,0 +1,58 @@ +import { Component, ReactNode, ReactPortal } from 'react'; +import ReactDOM from 'react-dom'; +import { observer, inject } from 'mobx-react'; + +import { StoresProps } from 'src/@types/ferdium-components.types'; + +import Layout from '../../components/settings/SettingsLayout'; +import Navigation from '../../components/settings/navigation/SettingsNavigation'; +import ErrorBoundary from '../../components/util/ErrorBoundary'; +import { workspaceStore } from '../../features/workspaces'; + +interface SettingsContainerProps extends StoresProps { + children: ReactNode; +} + +class SettingsContainer extends Component { + portalRoot: any; + + el: HTMLDivElement; + + constructor(props: SettingsContainerProps) { + super(props); + + this.portalRoot = document.querySelector('#portalContainer'); + this.el = document.createElement('div'); + } + + componentDidMount(): void { + this.portalRoot.append(this.el); + } + + componentWillUnmount(): void { + this.el.remove(); + } + + render(): ReactPortal { + const { children, stores } = this.props; + const { closeSettings } = this.props.actions.ui; + + const navigation = ( + + ); + + return ReactDOM.createPortal( + + + {children} + + , + this.el, + ); + } +} + +export default inject('stores', 'actions')(observer(SettingsContainer)); diff --git a/src/containers/settings/TeamScreen.js b/src/containers/settings/TeamScreen.js deleted file mode 100644 index 0a8b92b47..000000000 --- a/src/containers/settings/TeamScreen.js +++ /dev/null @@ -1,57 +0,0 @@ -import { Component } from 'react'; -import PropTypes from 'prop-types'; -import { inject, observer } from 'mobx-react'; - -import UserStore from '../../stores/UserStore'; -import AppStore from '../../stores/AppStore'; -import SettingsStore from '../../stores/SettingsStore'; - -import TeamDashboard from '../../components/settings/team/TeamDashboard'; -import ErrorBoundary from '../../components/util/ErrorBoundary'; -import { DEV_API_FRANZ_WEBSITE } from '../../config'; - -class TeamScreen extends Component { - handleWebsiteLink(route) { - const { actions, stores } = this.props; - - const url = `${DEV_API_FRANZ_WEBSITE}/${route}?authToken=${stores.user.authToken}&utm_source=app&utm_medium=account_dashboard`; - - actions.app.openExternalUrl({ url }); - } - - render() { - const { user, settings } = this.props.stores; - - const isLoadingUserInfo = user.getUserInfoRequest.isExecuting; - const { server } = settings.app; - - return ( - - this.reloadData()} - openTeamManagement={() => this.handleWebsiteLink('/user/team')} - server={server} - /> - - ); - } -} - -TeamScreen.propTypes = { - stores: PropTypes.shape({ - user: PropTypes.instanceOf(UserStore).isRequired, - app: PropTypes.instanceOf(AppStore).isRequired, - settings: PropTypes.instanceOf(SettingsStore).isRequired, - }).isRequired, - actions: PropTypes.shape({ - app: PropTypes.instanceOf(AppStore).isRequired, - user: PropTypes.instanceOf(UserStore).isRequired, - }).isRequired, -}; - -export default inject('stores', 'actions')(observer(TeamScreen)); diff --git a/src/containers/settings/TeamScreen.tsx b/src/containers/settings/TeamScreen.tsx new file mode 100644 index 000000000..5e5223f67 --- /dev/null +++ b/src/containers/settings/TeamScreen.tsx @@ -0,0 +1,48 @@ +import { Component, ReactElement } from 'react'; +import { inject, observer } from 'mobx-react'; + +import { StoresProps } from 'src/@types/ferdium-components.types'; + +import TeamDashboard from '../../components/settings/team/TeamDashboard'; +import ErrorBoundary from '../../components/util/ErrorBoundary'; +import { DEV_API_FRANZ_WEBSITE } from '../../config'; + +class TeamScreen extends Component { + handleWebsiteLink(route: string): void { + const { actions, stores } = this.props; + + const url = `${DEV_API_FRANZ_WEBSITE}/${route}?authToken=${stores.user.authToken}&utm_source=app&utm_medium=account_dashboard`; + + actions.app.openExternalUrl({ url }); + } + + reloadData(): void { + const { user } = this.props.stores; + + user.getUserInfoRequest.reload(); + } + + render(): ReactElement { + const { user, settings } = this.props.stores; + + const isLoadingUserInfo = user.getUserInfoRequest.isExecuting; + const { server } = settings.app; + + return ( + + this.reloadData()} + openTeamManagement={() => this.handleWebsiteLink('/user/team')} + server={server} + /> + + ); + } +} + +export default inject('stores', 'actions')(observer(TeamScreen)); -- cgit v1.2.3-54-g00ecf