From 58cda9cc7fb79ca9df6746de7f9662bc08dc156a Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Fri, 13 Oct 2017 12:29:40 +0200 Subject: initial commit --- src/containers/settings/AccountScreen.js | 114 ++++++++++++++ src/containers/settings/EditServiceScreen.js | 208 ++++++++++++++++++++++++++ src/containers/settings/EditSettingsScreen.js | 167 +++++++++++++++++++++ src/containers/settings/EditUserScreen.js | 165 ++++++++++++++++++++ src/containers/settings/RecipesScreen.js | 126 ++++++++++++++++ src/containers/settings/ServicesScreen.js | 75 ++++++++++ src/containers/settings/SettingsWindow.js | 43 ++++++ 7 files changed, 898 insertions(+) create mode 100644 src/containers/settings/AccountScreen.js create mode 100644 src/containers/settings/EditServiceScreen.js create mode 100644 src/containers/settings/EditSettingsScreen.js create mode 100644 src/containers/settings/EditUserScreen.js create mode 100644 src/containers/settings/RecipesScreen.js create mode 100644 src/containers/settings/ServicesScreen.js create mode 100644 src/containers/settings/SettingsWindow.js (limited to 'src/containers/settings') diff --git a/src/containers/settings/AccountScreen.js b/src/containers/settings/AccountScreen.js new file mode 100644 index 000000000..a1ac8bda3 --- /dev/null +++ b/src/containers/settings/AccountScreen.js @@ -0,0 +1,114 @@ +import { remote } from 'electron'; +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { inject, observer } from 'mobx-react'; + +import PaymentStore from '../../stores/PaymentStore'; +import UserStore from '../../stores/UserStore'; +import AppStore from '../../stores/AppStore'; +import { gaPage } from '../../lib/analytics'; + +import AccountDashboard from '../../components/settings/account/AccountDashboard'; + +const { BrowserWindow } = remote; + +@inject('stores', 'actions') @observer +export default class AccountScreen extends Component { + componentDidMount() { + gaPage('Settings/Account Dashboard'); + } + + onCloseWindow() { + const { user, payment } = this.props.stores; + user.getUserInfoRequest.invalidate({ immediately: true }); + payment.ordersDataRequest.invalidate({ immediately: true }); + } + + reloadData() { + const { user, payment } = this.props.stores; + + user.getUserInfoRequest.reload(); + payment.ordersDataRequest.reload(); + payment.plansRequest.reload(); + } + + stopMiner() { + const { update } = this.props.actions.user; + + update({ userData: { + isMiner: false, + } }); + } + + async handlePaymentDashboard() { + const { actions, stores } = this.props; + + actions.payment.createDashboardUrl(); + + const dashboard = await stores.payment.createDashboardUrlRequest; + + if (dashboard.url) { + const paymentWindow = new BrowserWindow({ + title: '🔒 Franz Subscription Dashboard', + parent: remote.getCurrentWindow(), + modal: false, + width: 900, + minWidth: 600, + webPreferences: { + nodeIntegration: false, + }, + }); + paymentWindow.loadURL(dashboard.url); + + paymentWindow.on('closed', () => { + this.onCloseWindow(); + }); + } + } + + render() { + const { user, payment, app } = this.props.stores; + const { openExternalUrl } = this.props.actions.app; + + const isLoadingUserInfo = user.getUserInfoRequest.isExecuting; + const isLoadingOrdersInfo = payment.ordersDataRequest.isExecuting; + const isLoadingPlans = payment.plansRequest.isExecuting; + + return ( + this.reloadData()} + isCreatingPaymentDashboardUrl={payment.createDashboardUrlRequest.isExecuting} + openDashboard={price => this.handlePaymentDashboard(price)} + openExternalUrl={url => openExternalUrl({ url })} + onCloseSubscriptionWindow={() => this.onCloseWindow()} + stopMiner={() => this.stopMiner()} + /> + ); + } +} + +AccountScreen.wrappedComponent.propTypes = { + stores: PropTypes.shape({ + user: PropTypes.instanceOf(UserStore).isRequired, + payment: PropTypes.instanceOf(PaymentStore).isRequired, + app: PropTypes.instanceOf(AppStore).isRequired, + }).isRequired, + actions: PropTypes.shape({ + payment: PropTypes.shape({ + createDashboardUrl: PropTypes.func.isRequired, + }).isRequired, + app: PropTypes.shape({ + openExternalUrl: PropTypes.func.isRequired, + }).isRequired, + user: PropTypes.shape({ + update: PropTypes.func.isRequired, + }).isRequired, + }).isRequired, +}; diff --git a/src/containers/settings/EditServiceScreen.js b/src/containers/settings/EditServiceScreen.js new file mode 100644 index 000000000..6c614b941 --- /dev/null +++ b/src/containers/settings/EditServiceScreen.js @@ -0,0 +1,208 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { inject, observer } from 'mobx-react'; +import { defineMessages, intlShape } from 'react-intl'; + +import UserStore from '../../stores/UserStore'; +import RecipesStore from '../../stores/RecipesStore'; +import ServicesStore from '../../stores/ServicesStore'; +import Form from '../../lib/Form'; +import { gaPage } from '../../lib/analytics'; + + +import ServiceError from '../../components/settings/services/ServiceError'; +import EditServiceForm from '../../components/settings/services/EditServiceForm'; +import { required, url, oneRequired } from '../../helpers/validation-helpers'; + +const messages = defineMessages({ + name: { + id: 'settings.service.form.name', + defaultMessage: '!!!Name', + }, + enableService: { + id: 'settings.service.form.enableService', + defaultMessage: '!!!Enable service', + }, + enableNotification: { + id: 'settings.service.form.enableNotification', + defaultMessage: '!!!Enable Notifications', + }, + 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', + }, +}); + +@inject('stores', 'actions') @observer +export default class EditServiceScreen extends Component { + static contextTypes = { + intl: intlShape, + }; + + componentDidMount() { + gaPage('Settings/Service/Edit'); + } + + onSubmit(serviceData) { + const { action } = this.props.router.params; + const { recipes, services } = this.props.stores; + const { createService, updateService } = this.props.actions.service; + + if (action === 'edit') { + updateService({ serviceId: services.activeSettings.id, serviceData }); + } else { + createService({ recipeId: recipes.active.id, serviceData }); + } + } + + prepareForm(recipe, service) { + const { intl } = this.context; + 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: true, + }, + isNotificationEnabled: { + label: intl.formatMessage(messages.enableNotification), + value: service.isNotificationEnabled, + default: true, + }, + }, + }; + + if (recipe.hasTeamId) { + Object.assign(config.fields, { + team: { + label: intl.formatMessage(messages.team), + placeholder: intl.formatMessage(messages.team), + value: service.team, + validate: [required], + }, + }); + } + + if (recipe.hasCustomUrl) { + Object.assign(config.fields, { + customUrl: { + label: intl.formatMessage(messages.customUrl), + placeholder: 'https://', + value: service.customUrl, + validate: [required, url], + }, + }); + } + + if (recipe.hasTeamId && recipe.hasCustomUrl) { + config.fields.team.validate = [oneRequired(['team', 'customUrl'])]; + config.fields.customUrl.validate = [url, oneRequired(['team', 'customUrl'])]; + } + + if (recipe.hasIndirectMessages) { + Object.assign(config.fields, { + isIndirectMessageBadgeEnabled: { + label: intl.formatMessage(messages.indirectMessages), + value: service.isIndirectMessageBadgeEnabled, + default: true, + }, + }); + } + + 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', + }); + } + } + + 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...
); + } + + const form = this.prepareForm(recipe, service); + + return ( + this.onSubmit(d)} + onDelete={() => this.deleteService()} + /> + ); + } +} + +EditServiceScreen.wrappedComponent.propTypes = { + stores: PropTypes.shape({ + user: PropTypes.instanceOf(UserStore).isRequired, + recipes: PropTypes.instanceOf(RecipesStore).isRequired, + services: PropTypes.instanceOf(ServicesStore).isRequired, + }).isRequired, + router: PropTypes.shape({ + params: PropTypes.shape({ + action: PropTypes.string.isRequired, + }).isRequired, + }).isRequired, + actions: PropTypes.shape({ + service: PropTypes.shape({ + createService: PropTypes.func.isRequired, + updateService: PropTypes.func.isRequired, + deleteService: PropTypes.func.isRequired, + }).isRequired, + }).isRequired, +}; diff --git a/src/containers/settings/EditSettingsScreen.js b/src/containers/settings/EditSettingsScreen.js new file mode 100644 index 000000000..0e17cafce --- /dev/null +++ b/src/containers/settings/EditSettingsScreen.js @@ -0,0 +1,167 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { inject, observer } from 'mobx-react'; +import { defineMessages, intlShape } from 'react-intl'; + +import AppStore from '../../stores/AppStore'; +import SettingsStore from '../../stores/SettingsStore'; +import UserStore from '../../stores/UserStore'; +import Form from '../../lib/Form'; +import languages from '../../i18n/languages'; +import { gaPage } from '../../lib/analytics'; + + +import EditSettingsForm from '../../components/settings/settings/EditSettingsForm'; + +const messages = defineMessages({ + autoLaunchOnStart: { + id: 'settings.app.form.autoLaunchOnStart', + defaultMessage: '!!!Launch Franz on start', + }, + autoLaunchInBackground: { + id: 'settings.app.form.autoLaunchInBackground', + defaultMessage: '!!!Open in background', + }, + runInBackground: { + id: 'settings.app.form.runInBackground', + defaultMessage: '!!!Keep Franz in background when closing the window', + }, + minimizeToSystemTray: { + id: 'settings.app.form.minimizeToSystemTray', + defaultMessage: '!!!Minimize Franz to system tray', + }, + language: { + id: 'settings.app.form.language', + defaultMessage: '!!!Language', + }, + beta: { + id: 'settings.app.form.beta', + defaultMessage: '!!!Include beta versions', + }, +}); + +@inject('stores', 'actions') @observer +export default class EditSettingsScreen extends Component { + static contextTypes = { + intl: intlShape, + }; + + componentDidMount() { + gaPage('Settings/App'); + } + + onSubmit(settingsData) { + const { app, settings, user } = this.props.actions; + + app.launchOnStartup({ + enable: settingsData.autoLaunchOnStart, + openInBackground: settingsData.autoLaunchInBackground, + }); + + settings.update({ + settings: { + runInBackground: settingsData.runInBackground, + minimizeToSystemTray: settingsData.minimizeToSystemTray, + locale: settingsData.locale, + beta: settingsData.beta, + }, + }); + + user.update({ + userData: { + beta: settingsData.beta, + }, + }); + } + + prepareForm() { + const { app, settings, user } = this.props.stores; + const { intl } = this.context; + + const options = []; + Object.keys(languages).forEach((key) => { + options.push({ + value: key, + label: languages[key], + }); + }); + + const config = { + fields: { + autoLaunchOnStart: { + label: intl.formatMessage(messages.autoLaunchOnStart), + value: app.autoLaunchOnStart, + default: true, + }, + autoLaunchInBackground: { + label: intl.formatMessage(messages.autoLaunchInBackground), + value: app.launchInBackground, + default: false, + }, + runInBackground: { + label: intl.formatMessage(messages.runInBackground), + value: settings.all.runInBackground, + default: true, + }, + minimizeToSystemTray: { + label: intl.formatMessage(messages.minimizeToSystemTray), + value: settings.all.minimizeToSystemTray, + default: false, + }, + locale: { + label: intl.formatMessage(messages.language), + value: app.locale, + options, + default: 'en-US', + }, + beta: { + label: intl.formatMessage(messages.beta), + value: user.data.beta, + default: false, + }, + }, + }; + + return new Form(config); + } + + render() { + const { updateStatus, updateStatusTypes } = this.props.stores.app; + const { checkForUpdates, installUpdate } = this.props.actions.app; + const form = this.prepareForm(); + + return ( + this.onSubmit(d)} + /> + ); + } +} + +EditSettingsScreen.wrappedComponent.propTypes = { + stores: PropTypes.shape({ + app: PropTypes.instanceOf(AppStore).isRequired, + user: PropTypes.instanceOf(UserStore).isRequired, + settings: PropTypes.instanceOf(SettingsStore).isRequired, + }).isRequired, + actions: PropTypes.shape({ + app: PropTypes.shape({ + launchOnStartup: PropTypes.func.isRequired, + checkForUpdates: PropTypes.func.isRequired, + installUpdate: PropTypes.func.isRequired, + }).isRequired, + settings: PropTypes.shape({ + update: PropTypes.func.isRequired, + }).isRequired, + user: PropTypes.shape({ + update: PropTypes.func.isRequired, + }).isRequired, + }).isRequired, +}; diff --git a/src/containers/settings/EditUserScreen.js b/src/containers/settings/EditUserScreen.js new file mode 100644 index 000000000..fb5c5db89 --- /dev/null +++ b/src/containers/settings/EditUserScreen.js @@ -0,0 +1,165 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { inject, observer } from 'mobx-react'; +import { defineMessages, intlShape } from 'react-intl'; + +import UserStore from '../../stores/UserStore'; +import Form from '../../lib/Form'; +import EditUserForm from '../../components/settings/user/EditUserForm'; +import { required, email, minLength } from '../../helpers/validation-helpers'; +import { gaPage } from '../../lib/analytics'; + +const messages = defineMessages({ + firstname: { + id: 'settings.user.form.firstname', + defaultMessage: '!!!Firstname', + }, + lastname: { + id: 'settings.user.form.lastname', + defaultMessage: '!!!Lastname', + }, + email: { + id: 'settings.user.form.email', + defaultMessage: '!!!Email', + }, + accountType: { + label: { + id: 'settings.user.form.accountType.label', + defaultMessage: '!!!Account type', + }, + individual: { + id: 'settings.user.form.accountType.individual', + defaultMessage: '!!!Individual', + }, + nonProfit: { + id: 'settings.user.form.accountType.non-profit', + defaultMessage: '!!!Non-Profit', + }, + company: { + 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', + }, +}); + +@inject('stores', 'actions') @observer +export default class EditUserScreen extends Component { + static contextTypes = { + intl: intlShape, + }; + + componentDidMount() { + gaPage('Settings/Account/Edit'); + } + + 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.context; + + const config = { + fields: { + firstname: { + label: intl.formatMessage(messages.firstname), + placeholder: intl.formatMessage(messages.firstname), + value: user.firstname, + validate: [required], + }, + lastname: { + label: intl.formatMessage(messages.lastname), + placeholder: intl.formatMessage(messages.lastname), + value: user.lastname, + validate: [required], + }, + email: { + label: intl.formatMessage(messages.email), + placeholder: intl.formatMessage(messages.email), + value: user.email, + validate: [required, email], + }, + accountType: { + value: user.accountType, + validate: [required], + label: intl.formatMessage(messages.accountType.label), + options: [{ + value: 'individual', + label: intl.formatMessage(messages.accountType.individual), + }, { + value: 'non-profit', + label: intl.formatMessage(messages.accountType.nonProfit), + }, { + value: 'company', + label: intl.formatMessage(messages.accountType.company), + }], + }, + organization: { + label: intl.formatMessage(messages.accountType.company), + placeholder: intl.formatMessage(messages.accountType.company), + value: user.organization, + }, + oldPassword: { + label: intl.formatMessage(messages.currentPassword), + type: 'password', + validate: [minLength(6)], + }, + newPassword: { + label: intl.formatMessage(messages.newPassword), + type: 'password', + validate: [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.wrappedComponent.propTypes = { + stores: PropTypes.shape({ + user: PropTypes.instanceOf(UserStore).isRequired, + }).isRequired, + actions: PropTypes.shape({ + user: PropTypes.shape({ + update: PropTypes.func.isRequired, + resetStatus: PropTypes.func.isRequired, + }).isRequired, + }).isRequired, +}; diff --git a/src/containers/settings/RecipesScreen.js b/src/containers/settings/RecipesScreen.js new file mode 100644 index 000000000..65341e9e3 --- /dev/null +++ b/src/containers/settings/RecipesScreen.js @@ -0,0 +1,126 @@ +import React, { 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 { gaPage } from '../../lib/analytics'; + +import RecipesDashboard from '../../components/settings/recipes/RecipesDashboard'; + +@inject('stores', 'actions') @observer +export default class RecipesScreen extends Component { + static propTypes = { + params: PropTypes.shape({ + filter: PropTypes.string, + }).isRequired, + }; + + static defaultProps = { + params: { + filter: null, + }, + }; + + state = { + needle: null, + currentFilter: 'featured', + }; + + componentDidMount() { + gaPage('Settings/Recipe Dashboard/Featured'); + + autorun(() => { + const { filter } = this.props.params; + const { currentFilter } = this.state; + + if (filter === 'all' && currentFilter !== 'all') { + gaPage('Settings/Recipe Dashboard/All'); + this.setState({ currentFilter: 'all' }); + } else if (filter === 'featured' && currentFilter !== 'featured') { + gaPage('Settings/Recipe Dashboard/Featured'); + this.setState({ currentFilter: 'featured' }); + } else if (filter === 'dev' && currentFilter !== 'dev') { + gaPage('Settings/Recipe Dashboard/Dev'); + this.setState({ currentFilter: 'dev' }); + } + }); + } + + componentWillUnmount() { + this.props.stores.services.resetStatus(); + } + + searchRecipes(needle) { + if (needle === '') { + this.resetSearch(); + } else { + const { search } = this.props.actions.recipePreview; + this.setState({ needle }); + search({ needle }); + } + } + + resetSearch() { + this.setState({ needle: null }); + } + + render() { + const { recipePreviews, recipes, services, user } = this.props.stores; + const { showAddServiceInterface } = this.props.actions.service; + + const { filter } = this.props.params; + let recipeFilter; + + if (filter === 'all') { + recipeFilter = recipePreviews.all; + } else if (filter === 'dev') { + recipeFilter = recipePreviews.dev; + } else { + recipeFilter = recipePreviews.featured; + } + + const allRecipes = this.state.needle ? recipePreviews.searchResults : recipeFilter; + + const isLoading = recipePreviews.featuredRecipePreviewsRequest.isExecuting + || recipePreviews.allRecipePreviewsRequest.isExecuting + || recipes.installRecipeRequest.isExecuting + || recipePreviews.searchRecipePreviewsRequest.isExecuting; + + return ( + this.searchRecipes(e)} + resetSearch={() => this.resetSearch()} + searchNeedle={this.state.needle} + serviceStatus={services.actionStatus} + devRecipesCount={recipePreviews.dev.length} + /> + ); + } +} + +RecipesScreen.wrappedComponent.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({ + service: PropTypes.shape({ + showAddServiceInterface: PropTypes.func.isRequired, + }).isRequired, + recipePreview: PropTypes.shape({ + search: PropTypes.func.isRequired, + }).isRequired, + }).isRequired, +}; diff --git a/src/containers/settings/ServicesScreen.js b/src/containers/settings/ServicesScreen.js new file mode 100644 index 000000000..d0580041f --- /dev/null +++ b/src/containers/settings/ServicesScreen.js @@ -0,0 +1,75 @@ +import React, { 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 { gaPage } from '../../lib/analytics'; + +import ServicesDashboard from '../../components/settings/services/ServicesDashboard'; + +@inject('stores', 'actions') @observer +export default class ServicesScreen extends Component { + componentDidMount() { + gaPage('Settings/Service Dashboard'); + } + + componentWillUnmount() { + this.props.actions.service.resetFilter(); + } + + 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()} + /> + ); + } +} + +ServicesScreen.wrappedComponent.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.shape({ + showAddServiceInterface: PropTypes.func.isRequired, + deleteService: PropTypes.func.isRequired, + toggleService: PropTypes.func.isRequired, + filter: PropTypes.func.isRequired, + resetFilter: PropTypes.func.isRequired, + }).isRequired, + }).isRequired, +}; diff --git a/src/containers/settings/SettingsWindow.js b/src/containers/settings/SettingsWindow.js new file mode 100644 index 000000000..13ca96f72 --- /dev/null +++ b/src/containers/settings/SettingsWindow.js @@ -0,0 +1,43 @@ +import React, { Component } from 'react'; +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'; + +@inject('stores', 'actions') @observer +export default class SettingsContainer extends Component { + render() { + const { children, stores } = this.props; + const { closeSettings } = this.props.actions.ui; + + const navigation = ( + + ); + + return ( + + {children} + + ); + } +} + +SettingsContainer.wrappedComponent.propTypes = { + children: PropTypes.element.isRequired, + stores: PropTypes.shape({ + services: PropTypes.instanceOf(ServicesStore).isRequired, + }).isRequired, + actions: PropTypes.shape({ + ui: PropTypes.shape({ + closeSettings: PropTypes.func.isRequired, + }), + }).isRequired, +}; -- cgit v1.2.3-54-g00ecf