diff options
author | Dominik Guzei <dominik.guzei@gmail.com> | 2019-04-11 12:00:07 +0200 |
---|---|---|
committer | Dominik Guzei <dominik.guzei@gmail.com> | 2019-04-11 12:00:07 +0200 |
commit | e43d6bd80b3d76aae627fa8174eea98c14016549 (patch) | |
tree | de1d0a3ba10100ce68d0cb1ed618095e362fe573 /src/components | |
parent | implements basic release announcement feature (diff) | |
parent | handle deleted services that are attached to workspaces (diff) | |
download | ferdium-app-e43d6bd80b3d76aae627fa8174eea98c14016549.tar.gz ferdium-app-e43d6bd80b3d76aae627fa8174eea98c14016549.tar.zst ferdium-app-e43d6bd80b3d76aae627fa8174eea98c14016549.zip |
merge-in workspace feature
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/layout/AppLayout.js | 26 | ||||
-rw-r--r-- | src/components/layout/Sidebar.js | 49 | ||||
-rw-r--r-- | src/components/services/content/ServiceView.js | 5 | ||||
-rw-r--r-- | src/components/services/tabs/Tabbar.js | 4 | ||||
-rw-r--r-- | src/components/settings/navigation/SettingsNavigation.js | 29 | ||||
-rw-r--r-- | src/components/settings/services/EditServiceForm.js | 10 | ||||
-rw-r--r-- | src/components/settings/services/ServicesDashboard.js | 2 | ||||
-rw-r--r-- | src/components/settings/settings/EditSettingsForm.js | 1 | ||||
-rw-r--r-- | src/components/ui/AppLoader/index.js | 4 | ||||
-rw-r--r-- | src/components/ui/FullscreenLoader/index.js | 4 | ||||
-rw-r--r-- | src/components/ui/Infobox.js | 17 | ||||
-rw-r--r-- | src/components/ui/PremiumFeatureContainer/index.js | 21 | ||||
-rw-r--r-- | src/components/ui/PremiumFeatureContainer/styles.js | 5 | ||||
-rw-r--r-- | src/components/ui/ServiceIcon.js | 67 | ||||
-rw-r--r-- | src/components/ui/WebviewLoader/index.js | 18 |
15 files changed, 236 insertions, 26 deletions
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js index 2bda91f73..985475c8d 100644 --- a/src/components/layout/AppLayout.js +++ b/src/components/layout/AppLayout.js | |||
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; | |||
3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | 3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; |
4 | import { defineMessages, intlShape } from 'react-intl'; | 4 | import { defineMessages, intlShape } from 'react-intl'; |
5 | import { TitleBar } from 'electron-react-titlebar'; | 5 | import { TitleBar } from 'electron-react-titlebar'; |
6 | import injectSheet from 'react-jss'; | ||
6 | 7 | ||
7 | import InfoBar from '../ui/InfoBar'; | 8 | import InfoBar from '../ui/InfoBar'; |
8 | import { Component as DelayApp } from '../../features/delayApp'; | 9 | import { Component as DelayApp } from '../../features/delayApp'; |
@@ -14,6 +15,8 @@ import ErrorBoundary from '../util/ErrorBoundary'; | |||
14 | 15 | ||
15 | import { isWindows } from '../../environment'; | 16 | import { isWindows } from '../../environment'; |
16 | import AnnouncementScreen from '../../features/announcements/Component'; | 17 | import AnnouncementScreen from '../../features/announcements/Component'; |
18 | import WorkspaceSwitchingIndicator from '../../features/workspaces/components/WorkspaceSwitchingIndicator'; | ||
19 | import { workspaceStore } from '../../features/workspaces'; | ||
17 | 20 | ||
18 | function createMarkup(HTMLString) { | 21 | function createMarkup(HTMLString) { |
19 | return { __html: HTMLString }; | 22 | return { __html: HTMLString }; |
@@ -46,10 +49,23 @@ const messages = defineMessages({ | |||
46 | }, | 49 | }, |
47 | }); | 50 | }); |
48 | 51 | ||
49 | export default @observer class AppLayout extends Component { | 52 | const styles = theme => ({ |
53 | appContent: { | ||
54 | width: `calc(100% + ${theme.workspaces.drawer.width}px)`, | ||
55 | transition: 'transform 0.5s ease', | ||
56 | transform() { | ||
57 | return workspaceStore.isWorkspaceDrawerOpen ? 'translateX(0)' : `translateX(-${theme.workspaces.drawer.width}px)`; | ||
58 | }, | ||
59 | }, | ||
60 | }); | ||
61 | |||
62 | @injectSheet(styles) @observer | ||
63 | class AppLayout extends Component { | ||
50 | static propTypes = { | 64 | static propTypes = { |
65 | classes: PropTypes.object.isRequired, | ||
51 | isFullScreen: PropTypes.bool.isRequired, | 66 | isFullScreen: PropTypes.bool.isRequired, |
52 | sidebar: PropTypes.element.isRequired, | 67 | sidebar: PropTypes.element.isRequired, |
68 | workspacesDrawer: PropTypes.element.isRequired, | ||
53 | services: PropTypes.element.isRequired, | 69 | services: PropTypes.element.isRequired, |
54 | children: PropTypes.element, | 70 | children: PropTypes.element, |
55 | news: MobxPropTypes.arrayOrObservableArray.isRequired, | 71 | news: MobxPropTypes.arrayOrObservableArray.isRequired, |
@@ -78,7 +94,9 @@ export default @observer class AppLayout extends Component { | |||
78 | 94 | ||
79 | render() { | 95 | render() { |
80 | const { | 96 | const { |
97 | classes, | ||
81 | isFullScreen, | 98 | isFullScreen, |
99 | workspacesDrawer, | ||
82 | sidebar, | 100 | sidebar, |
83 | services, | 101 | services, |
84 | children, | 102 | children, |
@@ -105,9 +123,11 @@ export default @observer class AppLayout extends Component { | |||
105 | <div className={(darkMode ? 'theme__dark' : '')}> | 123 | <div className={(darkMode ? 'theme__dark' : '')}> |
106 | <div className="app"> | 124 | <div className="app"> |
107 | {isWindows && !isFullScreen && <TitleBar menu={window.franz.menu.template} icon="assets/images/logo.svg" />} | 125 | {isWindows && !isFullScreen && <TitleBar menu={window.franz.menu.template} icon="assets/images/logo.svg" />} |
108 | <div className="app__content"> | 126 | <div className={`app__content ${classes.appContent}`}> |
127 | {workspacesDrawer} | ||
109 | {sidebar} | 128 | {sidebar} |
110 | <div className="app__service"> | 129 | <div className="app__service"> |
130 | <WorkspaceSwitchingIndicator /> | ||
111 | {news.length > 0 && news.map(item => ( | 131 | {news.length > 0 && news.map(item => ( |
112 | <InfoBar | 132 | <InfoBar |
113 | key={item.id} | 133 | key={item.id} |
@@ -180,3 +200,5 @@ export default @observer class AppLayout extends Component { | |||
180 | ); | 200 | ); |
181 | } | 201 | } |
182 | } | 202 | } |
203 | |||
204 | export default AppLayout; | ||
diff --git a/src/components/layout/Sidebar.js b/src/components/layout/Sidebar.js index 609a3b604..36c1f2e39 100644 --- a/src/components/layout/Sidebar.js +++ b/src/components/layout/Sidebar.js | |||
@@ -6,6 +6,8 @@ import { observer } from 'mobx-react'; | |||
6 | 6 | ||
7 | import Tabbar from '../services/tabs/Tabbar'; | 7 | import Tabbar from '../services/tabs/Tabbar'; |
8 | import { ctrlKey } from '../../environment'; | 8 | import { ctrlKey } from '../../environment'; |
9 | import { GA_CATEGORY_WORKSPACES, workspaceStore } from '../../features/workspaces'; | ||
10 | import { gaEvent } from '../../lib/analytics'; | ||
9 | 11 | ||
10 | const messages = defineMessages({ | 12 | const messages = defineMessages({ |
11 | settings: { | 13 | settings: { |
@@ -24,6 +26,14 @@ const messages = defineMessages({ | |||
24 | id: 'sidebar.unmuteApp', | 26 | id: 'sidebar.unmuteApp', |
25 | defaultMessage: '!!!Enable notifications & audio', | 27 | defaultMessage: '!!!Enable notifications & audio', |
26 | }, | 28 | }, |
29 | openWorkspaceDrawer: { | ||
30 | id: 'sidebar.openWorkspaceDrawer', | ||
31 | defaultMessage: '!!!Open workspace drawer', | ||
32 | }, | ||
33 | closeWorkspaceDrawer: { | ||
34 | id: 'sidebar.closeWorkspaceDrawer', | ||
35 | defaultMessage: '!!!Close workspace drawer', | ||
36 | }, | ||
27 | }); | 37 | }); |
28 | 38 | ||
29 | export default @observer class Sidebar extends Component { | 39 | export default @observer class Sidebar extends Component { |
@@ -31,7 +41,9 @@ export default @observer class Sidebar extends Component { | |||
31 | openSettings: PropTypes.func.isRequired, | 41 | openSettings: PropTypes.func.isRequired, |
32 | toggleMuteApp: PropTypes.func.isRequired, | 42 | toggleMuteApp: PropTypes.func.isRequired, |
33 | isAppMuted: PropTypes.bool.isRequired, | 43 | isAppMuted: PropTypes.bool.isRequired, |
34 | } | 44 | isWorkspaceDrawerOpen: PropTypes.bool.isRequired, |
45 | toggleWorkspaceDrawer: PropTypes.func.isRequired, | ||
46 | }; | ||
35 | 47 | ||
36 | static contextTypes = { | 48 | static contextTypes = { |
37 | intl: intlShape, | 49 | intl: intlShape, |
@@ -53,9 +65,23 @@ export default @observer class Sidebar extends Component { | |||
53 | this.setState({ tooltipEnabled: false }); | 65 | this.setState({ tooltipEnabled: false }); |
54 | } | 66 | } |
55 | 67 | ||
68 | updateToolTip() { | ||
69 | this.disableToolTip(); | ||
70 | setTimeout(this.enableToolTip.bind(this)); | ||
71 | } | ||
72 | |||
56 | render() { | 73 | render() { |
57 | const { openSettings, toggleMuteApp, isAppMuted } = this.props; | 74 | const { |
75 | openSettings, | ||
76 | toggleMuteApp, | ||
77 | isAppMuted, | ||
78 | isWorkspaceDrawerOpen, | ||
79 | toggleWorkspaceDrawer, | ||
80 | } = this.props; | ||
58 | const { intl } = this.context; | 81 | const { intl } = this.context; |
82 | const workspaceToggleMessage = ( | ||
83 | isWorkspaceDrawerOpen ? messages.closeWorkspaceDrawer : messages.openWorkspaceDrawer | ||
84 | ); | ||
59 | 85 | ||
60 | return ( | 86 | return ( |
61 | <div className="sidebar"> | 87 | <div className="sidebar"> |
@@ -64,9 +90,26 @@ export default @observer class Sidebar extends Component { | |||
64 | enableToolTip={() => this.enableToolTip()} | 90 | enableToolTip={() => this.enableToolTip()} |
65 | disableToolTip={() => this.disableToolTip()} | 91 | disableToolTip={() => this.disableToolTip()} |
66 | /> | 92 | /> |
93 | {workspaceStore.isFeatureEnabled ? ( | ||
94 | <button | ||
95 | type="button" | ||
96 | onClick={() => { | ||
97 | toggleWorkspaceDrawer(); | ||
98 | this.updateToolTip(); | ||
99 | gaEvent(GA_CATEGORY_WORKSPACES, 'toggleDrawer', 'sidebar'); | ||
100 | }} | ||
101 | className={`sidebar__button sidebar__button--workspaces ${isWorkspaceDrawerOpen ? 'is-active' : ''}`} | ||
102 | data-tip={`${intl.formatMessage(workspaceToggleMessage)} (${ctrlKey}+D)`} | ||
103 | > | ||
104 | <i className="mdi mdi-view-grid" /> | ||
105 | </button> | ||
106 | ) : null} | ||
67 | <button | 107 | <button |
68 | type="button" | 108 | type="button" |
69 | onClick={toggleMuteApp} | 109 | onClick={() => { |
110 | toggleMuteApp(); | ||
111 | this.updateToolTip(); | ||
112 | }} | ||
70 | className={`sidebar__button sidebar__button--audio ${isAppMuted ? 'is-muted' : ''}`} | 113 | className={`sidebar__button sidebar__button--audio ${isAppMuted ? 'is-muted' : ''}`} |
71 | data-tip={`${intl.formatMessage(isAppMuted ? messages.unmute : messages.mute)} (${ctrlKey}+Shift+M)`} | 114 | data-tip={`${intl.formatMessage(isAppMuted ? messages.unmute : messages.mute)} (${ctrlKey}+Shift+M)`} |
72 | > | 115 | > |
diff --git a/src/components/services/content/ServiceView.js b/src/components/services/content/ServiceView.js index 5afc54f9d..13148b9b3 100644 --- a/src/components/services/content/ServiceView.js +++ b/src/components/services/content/ServiceView.js | |||
@@ -35,11 +35,13 @@ export default @observer class ServiceView extends Component { | |||
35 | 35 | ||
36 | autorunDisposer = null; | 36 | autorunDisposer = null; |
37 | 37 | ||
38 | forceRepaintTimeout = null; | ||
39 | |||
38 | componentDidMount() { | 40 | componentDidMount() { |
39 | this.autorunDisposer = autorun(() => { | 41 | this.autorunDisposer = autorun(() => { |
40 | if (this.props.service.isActive) { | 42 | if (this.props.service.isActive) { |
41 | this.setState({ forceRepaint: true }); | 43 | this.setState({ forceRepaint: true }); |
42 | setTimeout(() => { | 44 | this.forceRepaintTimeout = setTimeout(() => { |
43 | this.setState({ forceRepaint: false }); | 45 | this.setState({ forceRepaint: false }); |
44 | }, 100); | 46 | }, 100); |
45 | } | 47 | } |
@@ -48,6 +50,7 @@ export default @observer class ServiceView extends Component { | |||
48 | 50 | ||
49 | componentWillUnmount() { | 51 | componentWillUnmount() { |
50 | this.autorunDisposer(); | 52 | this.autorunDisposer(); |
53 | clearTimeout(this.forceRepaintTimeout); | ||
51 | } | 54 | } |
52 | 55 | ||
53 | updateTargetUrl = (event) => { | 56 | updateTargetUrl = (event) => { |
diff --git a/src/components/services/tabs/Tabbar.js b/src/components/services/tabs/Tabbar.js index dd5c2140f..5e8260ad0 100644 --- a/src/components/services/tabs/Tabbar.js +++ b/src/components/services/tabs/Tabbar.js | |||
@@ -19,7 +19,7 @@ export default @observer class TabBar extends Component { | |||
19 | updateService: PropTypes.func.isRequired, | 19 | updateService: PropTypes.func.isRequired, |
20 | showMessageBadgeWhenMutedSetting: PropTypes.bool.isRequired, | 20 | showMessageBadgeWhenMutedSetting: PropTypes.bool.isRequired, |
21 | showMessageBadgesEvenWhenMuted: PropTypes.bool.isRequired, | 21 | showMessageBadgesEvenWhenMuted: PropTypes.bool.isRequired, |
22 | } | 22 | }; |
23 | 23 | ||
24 | onSortEnd = ({ oldIndex, newIndex }) => { | 24 | onSortEnd = ({ oldIndex, newIndex }) => { |
25 | const { | 25 | const { |
@@ -45,7 +45,7 @@ export default @observer class TabBar extends Component { | |||
45 | redirect: false, | 45 | redirect: false, |
46 | }); | 46 | }); |
47 | } | 47 | } |
48 | } | 48 | }; |
49 | 49 | ||
50 | disableService({ serviceId }) { | 50 | disableService({ serviceId }) { |
51 | this.toggleService({ serviceId, isEnabled: false }); | 51 | this.toggleService({ serviceId, isEnabled: false }); |
diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js index 953f702f8..993b0a44a 100644 --- a/src/components/settings/navigation/SettingsNavigation.js +++ b/src/components/settings/navigation/SettingsNavigation.js | |||
@@ -2,8 +2,11 @@ import React, { Component } from 'react'; | |||
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { defineMessages, intlShape } from 'react-intl'; | 3 | import { defineMessages, intlShape } from 'react-intl'; |
4 | import { inject, observer } from 'mobx-react'; | 4 | import { inject, observer } from 'mobx-react'; |
5 | import { ProBadge } from '@meetfranz/ui'; | ||
5 | 6 | ||
6 | import Link from '../../ui/Link'; | 7 | import Link from '../../ui/Link'; |
8 | import { workspaceStore } from '../../../features/workspaces'; | ||
9 | import UIStore from '../../../stores/UIStore'; | ||
7 | 10 | ||
8 | const messages = defineMessages({ | 11 | const messages = defineMessages({ |
9 | availableServices: { | 12 | availableServices: { |
@@ -14,6 +17,10 @@ const messages = defineMessages({ | |||
14 | id: 'settings.navigation.yourServices', | 17 | id: 'settings.navigation.yourServices', |
15 | defaultMessage: '!!!Your services', | 18 | defaultMessage: '!!!Your services', |
16 | }, | 19 | }, |
20 | yourWorkspaces: { | ||
21 | id: 'settings.navigation.yourWorkspaces', | ||
22 | defaultMessage: '!!!Your workspaces', | ||
23 | }, | ||
17 | account: { | 24 | account: { |
18 | id: 'settings.navigation.account', | 25 | id: 'settings.navigation.account', |
19 | defaultMessage: '!!!Account', | 26 | defaultMessage: '!!!Account', |
@@ -34,7 +41,11 @@ const messages = defineMessages({ | |||
34 | 41 | ||
35 | export default @inject('stores') @observer class SettingsNavigation extends Component { | 42 | export default @inject('stores') @observer class SettingsNavigation extends Component { |
36 | static propTypes = { | 43 | static propTypes = { |
44 | stores: PropTypes.shape({ | ||
45 | ui: PropTypes.instanceOf(UIStore).isRequired, | ||
46 | }).isRequired, | ||
37 | serviceCount: PropTypes.number.isRequired, | 47 | serviceCount: PropTypes.number.isRequired, |
48 | workspaceCount: PropTypes.number.isRequired, | ||
38 | }; | 49 | }; |
39 | 50 | ||
40 | static contextTypes = { | 51 | static contextTypes = { |
@@ -42,7 +53,8 @@ export default @inject('stores') @observer class SettingsNavigation extends Comp | |||
42 | }; | 53 | }; |
43 | 54 | ||
44 | render() { | 55 | render() { |
45 | const { serviceCount } = this.props; | 56 | const { serviceCount, workspaceCount, stores } = this.props; |
57 | const { isDarkThemeActive } = stores.ui; | ||
46 | const { intl } = this.context; | 58 | const { intl } = this.context; |
47 | 59 | ||
48 | return ( | 60 | return ( |
@@ -63,6 +75,21 @@ export default @inject('stores') @observer class SettingsNavigation extends Comp | |||
63 | {' '} | 75 | {' '} |
64 | <span className="badge">{serviceCount}</span> | 76 | <span className="badge">{serviceCount}</span> |
65 | </Link> | 77 | </Link> |
78 | {workspaceStore.isFeatureEnabled ? ( | ||
79 | <Link | ||
80 | to="/settings/workspaces" | ||
81 | className="settings-navigation__link" | ||
82 | activeClassName="is-active" | ||
83 | > | ||
84 | {intl.formatMessage(messages.yourWorkspaces)} | ||
85 | {' '} | ||
86 | {workspaceStore.isPremiumUpgradeRequired ? ( | ||
87 | <ProBadge inverted={!isDarkThemeActive && workspaceStore.isSettingsRouteActive} /> | ||
88 | ) : ( | ||
89 | <span className="badge">{workspaceCount}</span> | ||
90 | )} | ||
91 | </Link> | ||
92 | ) : null} | ||
66 | <Link | 93 | <Link |
67 | to="/settings/user" | 94 | to="/settings/user" |
68 | className="settings-navigation__link" | 95 | className="settings-navigation__link" |
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js index 21616b5de..4ba2eb844 100644 --- a/src/components/settings/services/EditServiceForm.js +++ b/src/components/settings/services/EditServiceForm.js | |||
@@ -341,14 +341,20 @@ export default @observer class EditServiceForm extends Component { | |||
341 | </div> | 341 | </div> |
342 | </div> | 342 | </div> |
343 | 343 | ||
344 | <PremiumFeatureContainer condition={isSpellcheckerPremiumFeature}> | 344 | <PremiumFeatureContainer |
345 | condition={isSpellcheckerPremiumFeature} | ||
346 | gaEventInfo={{ category: 'User', event: 'upgrade', label: 'spellchecker' }} | ||
347 | > | ||
345 | <div className="settings__settings-group"> | 348 | <div className="settings__settings-group"> |
346 | <Select field={form.$('spellcheckerLanguage')} /> | 349 | <Select field={form.$('spellcheckerLanguage')} /> |
347 | </div> | 350 | </div> |
348 | </PremiumFeatureContainer> | 351 | </PremiumFeatureContainer> |
349 | 352 | ||
350 | {isProxyFeatureEnabled && ( | 353 | {isProxyFeatureEnabled && ( |
351 | <PremiumFeatureContainer condition={isProxyPremiumFeature}> | 354 | <PremiumFeatureContainer |
355 | condition={isProxyPremiumFeature} | ||
356 | gaEventInfo={{ category: 'User', event: 'upgrade', label: 'proxy' }} | ||
357 | > | ||
352 | <div className="settings__settings-group"> | 358 | <div className="settings__settings-group"> |
353 | <h3> | 359 | <h3> |
354 | {intl.formatMessage(messages.headlineProxy)} | 360 | {intl.formatMessage(messages.headlineProxy)} |
diff --git a/src/components/settings/services/ServicesDashboard.js b/src/components/settings/services/ServicesDashboard.js index a12df7372..53bae12df 100644 --- a/src/components/settings/services/ServicesDashboard.js +++ b/src/components/settings/services/ServicesDashboard.js | |||
@@ -65,7 +65,7 @@ export default @observer class ServicesDashboard extends Component { | |||
65 | 65 | ||
66 | static defaultProps = { | 66 | static defaultProps = { |
67 | searchNeedle: '', | 67 | searchNeedle: '', |
68 | } | 68 | }; |
69 | 69 | ||
70 | static contextTypes = { | 70 | static contextTypes = { |
71 | intl: intlShape, | 71 | intl: intlShape, |
diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js index a92e559f3..8429d0ecb 100644 --- a/src/components/settings/settings/EditSettingsForm.js +++ b/src/components/settings/settings/EditSettingsForm.js | |||
@@ -170,6 +170,7 @@ export default @observer class EditSettingsForm extends Component { | |||
170 | <Select field={form.$('locale')} showLabel={false} /> | 170 | <Select field={form.$('locale')} showLabel={false} /> |
171 | <PremiumFeatureContainer | 171 | <PremiumFeatureContainer |
172 | condition={isSpellcheckerPremiumFeature} | 172 | condition={isSpellcheckerPremiumFeature} |
173 | gaEventInfo={{ category: 'User', event: 'upgrade', label: 'spellchecker' }} | ||
173 | > | 174 | > |
174 | <Fragment> | 175 | <Fragment> |
175 | <Toggle | 176 | <Toggle |
diff --git a/src/components/ui/AppLoader/index.js b/src/components/ui/AppLoader/index.js index 61053f6d1..b0c7fed7b 100644 --- a/src/components/ui/AppLoader/index.js +++ b/src/components/ui/AppLoader/index.js | |||
@@ -23,11 +23,11 @@ export default @injectSheet(styles) @withTheme class AppLoader extends Component | |||
23 | static propTypes = { | 23 | static propTypes = { |
24 | classes: PropTypes.object.isRequired, | 24 | classes: PropTypes.object.isRequired, |
25 | theme: PropTypes.object.isRequired, | 25 | theme: PropTypes.object.isRequired, |
26 | } | 26 | }; |
27 | 27 | ||
28 | state = { | 28 | state = { |
29 | step: 0, | 29 | step: 0, |
30 | } | 30 | }; |
31 | 31 | ||
32 | interval = null; | 32 | interval = null; |
33 | 33 | ||
diff --git a/src/components/ui/FullscreenLoader/index.js b/src/components/ui/FullscreenLoader/index.js index 6ecf4d395..06dab1eb6 100644 --- a/src/components/ui/FullscreenLoader/index.js +++ b/src/components/ui/FullscreenLoader/index.js | |||
@@ -16,13 +16,13 @@ export default @observer @withTheme @injectSheet(styles) class FullscreenLoader | |||
16 | theme: PropTypes.object.isRequired, | 16 | theme: PropTypes.object.isRequired, |
17 | spinnerColor: PropTypes.string, | 17 | spinnerColor: PropTypes.string, |
18 | children: PropTypes.node, | 18 | children: PropTypes.node, |
19 | } | 19 | }; |
20 | 20 | ||
21 | static defaultProps = { | 21 | static defaultProps = { |
22 | className: null, | 22 | className: null, |
23 | spinnerColor: null, | 23 | spinnerColor: null, |
24 | children: null, | 24 | children: null, |
25 | } | 25 | }; |
26 | 26 | ||
27 | render() { | 27 | render() { |
28 | const { | 28 | const { |
diff --git a/src/components/ui/Infobox.js b/src/components/ui/Infobox.js index a33c6474a..0917ee9f0 100644 --- a/src/components/ui/Infobox.js +++ b/src/components/ui/Infobox.js | |||
@@ -13,6 +13,8 @@ export default @observer class Infobox extends Component { | |||
13 | ctaLabel: PropTypes.string, | 13 | ctaLabel: PropTypes.string, |
14 | ctaLoading: PropTypes.bool, | 14 | ctaLoading: PropTypes.bool, |
15 | dismissable: PropTypes.bool, | 15 | dismissable: PropTypes.bool, |
16 | onDismiss: PropTypes.func, | ||
17 | onSeen: PropTypes.func, | ||
16 | }; | 18 | }; |
17 | 19 | ||
18 | static defaultProps = { | 20 | static defaultProps = { |
@@ -22,12 +24,19 @@ export default @observer class Infobox extends Component { | |||
22 | ctaOnClick: () => null, | 24 | ctaOnClick: () => null, |
23 | ctaLabel: '', | 25 | ctaLabel: '', |
24 | ctaLoading: false, | 26 | ctaLoading: false, |
27 | onDismiss: () => null, | ||
28 | onSeen: () => null, | ||
25 | }; | 29 | }; |
26 | 30 | ||
27 | state = { | 31 | state = { |
28 | dismissed: false, | 32 | dismissed: false, |
29 | }; | 33 | }; |
30 | 34 | ||
35 | componentDidMount() { | ||
36 | const { onSeen } = this.props; | ||
37 | if (onSeen) onSeen(); | ||
38 | } | ||
39 | |||
31 | render() { | 40 | render() { |
32 | const { | 41 | const { |
33 | children, | 42 | children, |
@@ -37,6 +46,7 @@ export default @observer class Infobox extends Component { | |||
37 | ctaLoading, | 46 | ctaLoading, |
38 | ctaOnClick, | 47 | ctaOnClick, |
39 | dismissable, | 48 | dismissable, |
49 | onDismiss, | ||
40 | } = this.props; | 50 | } = this.props; |
41 | 51 | ||
42 | if (this.state.dismissed) { | 52 | if (this.state.dismissed) { |
@@ -76,9 +86,10 @@ export default @observer class Infobox extends Component { | |||
76 | {dismissable && ( | 86 | {dismissable && ( |
77 | <button | 87 | <button |
78 | type="button" | 88 | type="button" |
79 | onClick={() => this.setState({ | 89 | onClick={() => { |
80 | dismissed: true, | 90 | this.setState({ dismissed: true }); |
81 | })} | 91 | if (onDismiss) onDismiss(); |
92 | }} | ||
82 | className="infobox__delete mdi mdi-close" | 93 | className="infobox__delete mdi mdi-close" |
83 | /> | 94 | /> |
84 | )} | 95 | )} |
diff --git a/src/components/ui/PremiumFeatureContainer/index.js b/src/components/ui/PremiumFeatureContainer/index.js index 67cd6af0b..3c1e0fac3 100644 --- a/src/components/ui/PremiumFeatureContainer/index.js +++ b/src/components/ui/PremiumFeatureContainer/index.js | |||
@@ -9,6 +9,7 @@ import { oneOrManyChildElements } from '../../../prop-types'; | |||
9 | import UserStore from '../../../stores/UserStore'; | 9 | import UserStore from '../../../stores/UserStore'; |
10 | 10 | ||
11 | import styles from './styles'; | 11 | import styles from './styles'; |
12 | import { gaEvent } from '../../../lib/analytics'; | ||
12 | 13 | ||
13 | const messages = defineMessages({ | 14 | const messages = defineMessages({ |
14 | action: { | 15 | action: { |
@@ -17,14 +18,21 @@ const messages = defineMessages({ | |||
17 | }, | 18 | }, |
18 | }); | 19 | }); |
19 | 20 | ||
20 | export default @inject('stores', 'actions') @injectSheet(styles) @observer class PremiumFeatureContainer extends Component { | 21 | @inject('stores', 'actions') @injectSheet(styles) @observer |
22 | class PremiumFeatureContainer extends Component { | ||
21 | static propTypes = { | 23 | static propTypes = { |
22 | classes: PropTypes.object.isRequired, | 24 | classes: PropTypes.object.isRequired, |
23 | condition: PropTypes.bool, | 25 | condition: PropTypes.bool, |
26 | gaEventInfo: PropTypes.shape({ | ||
27 | category: PropTypes.string.isRequired, | ||
28 | event: PropTypes.string.isRequired, | ||
29 | label: PropTypes.string, | ||
30 | }), | ||
24 | }; | 31 | }; |
25 | 32 | ||
26 | static defaultProps = { | 33 | static defaultProps = { |
27 | condition: true, | 34 | condition: true, |
35 | gaEventInfo: null, | ||
28 | }; | 36 | }; |
29 | 37 | ||
30 | static contextTypes = { | 38 | static contextTypes = { |
@@ -38,6 +46,7 @@ export default @inject('stores', 'actions') @injectSheet(styles) @observer class | |||
38 | actions, | 46 | actions, |
39 | condition, | 47 | condition, |
40 | stores, | 48 | stores, |
49 | gaEventInfo, | ||
41 | } = this.props; | 50 | } = this.props; |
42 | 51 | ||
43 | const { intl } = this.context; | 52 | const { intl } = this.context; |
@@ -49,7 +58,13 @@ export default @inject('stores', 'actions') @injectSheet(styles) @observer class | |||
49 | <button | 58 | <button |
50 | className={classes.actionButton} | 59 | className={classes.actionButton} |
51 | type="button" | 60 | type="button" |
52 | onClick={() => actions.ui.openSettings({ path: 'user' })} | 61 | onClick={() => { |
62 | actions.ui.openSettings({ path: 'user' }); | ||
63 | if (gaEventInfo) { | ||
64 | const { category, event, label } = gaEventInfo; | ||
65 | gaEvent(category, event, label); | ||
66 | } | ||
67 | }} | ||
53 | > | 68 | > |
54 | {intl.formatMessage(messages.action)} | 69 | {intl.formatMessage(messages.action)} |
55 | </button> | 70 | </button> |
@@ -73,3 +88,5 @@ PremiumFeatureContainer.wrappedComponent.propTypes = { | |||
73 | }).isRequired, | 88 | }).isRequired, |
74 | }).isRequired, | 89 | }).isRequired, |
75 | }; | 90 | }; |
91 | |||
92 | export default PremiumFeatureContainer; | ||
diff --git a/src/components/ui/PremiumFeatureContainer/styles.js b/src/components/ui/PremiumFeatureContainer/styles.js index 81d6666c6..41881e044 100644 --- a/src/components/ui/PremiumFeatureContainer/styles.js +++ b/src/components/ui/PremiumFeatureContainer/styles.js | |||
@@ -6,6 +6,7 @@ export default theme => ({ | |||
6 | padding: 20, | 6 | padding: 20, |
7 | 'border-radius': theme.borderRadius, | 7 | 'border-radius': theme.borderRadius, |
8 | pointerEvents: 'none', | 8 | pointerEvents: 'none', |
9 | height: 'auto', | ||
9 | }, | 10 | }, |
10 | titleContainer: { | 11 | titleContainer: { |
11 | display: 'flex', | 12 | display: 'flex', |
@@ -19,14 +20,14 @@ export default theme => ({ | |||
19 | color: theme.colorSubscriptionContainerActionButtonColor, | 20 | color: theme.colorSubscriptionContainerActionButtonColor, |
20 | 'margin-left': 'auto', | 21 | 'margin-left': 'auto', |
21 | 'border-radius': theme.borderRadiusSmall, | 22 | 'border-radius': theme.borderRadiusSmall, |
22 | padding: [2, 4], | 23 | padding: [4, 8], |
23 | 'font-size': 12, | 24 | 'font-size': 12, |
24 | pointerEvents: 'initial', | 25 | pointerEvents: 'initial', |
25 | }, | 26 | }, |
26 | content: { | 27 | content: { |
27 | opacity: 0.5, | 28 | opacity: 0.5, |
28 | 'margin-top': 20, | 29 | 'margin-top': 20, |
29 | '& :last-child': { | 30 | '& > :last-child': { |
30 | 'margin-bottom': 0, | 31 | 'margin-bottom': 0, |
31 | }, | 32 | }, |
32 | }, | 33 | }, |
diff --git a/src/components/ui/ServiceIcon.js b/src/components/ui/ServiceIcon.js new file mode 100644 index 000000000..0b9155a4e --- /dev/null +++ b/src/components/ui/ServiceIcon.js | |||
@@ -0,0 +1,67 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | ||
4 | import injectSheet from 'react-jss'; | ||
5 | import classnames from 'classnames'; | ||
6 | |||
7 | import ServiceModel from '../../models/Service'; | ||
8 | |||
9 | const styles = theme => ({ | ||
10 | root: { | ||
11 | height: 'auto', | ||
12 | }, | ||
13 | icon: { | ||
14 | width: theme.serviceIcon.width, | ||
15 | }, | ||
16 | isCustomIcon: { | ||
17 | width: theme.serviceIcon.isCustom.width, | ||
18 | border: theme.serviceIcon.isCustom.border, | ||
19 | borderRadius: theme.serviceIcon.isCustom.borderRadius, | ||
20 | }, | ||
21 | isDisabled: { | ||
22 | filter: 'grayscale(100%)', | ||
23 | opacity: '.5', | ||
24 | }, | ||
25 | }); | ||
26 | |||
27 | @injectSheet(styles) @observer | ||
28 | class ServiceIcon extends Component { | ||
29 | static propTypes = { | ||
30 | classes: PropTypes.object.isRequired, | ||
31 | service: PropTypes.instanceOf(ServiceModel).isRequired, | ||
32 | className: PropTypes.string, | ||
33 | }; | ||
34 | |||
35 | static defaultProps = { | ||
36 | className: '', | ||
37 | }; | ||
38 | |||
39 | render() { | ||
40 | const { | ||
41 | classes, | ||
42 | className, | ||
43 | service, | ||
44 | } = this.props; | ||
45 | |||
46 | return ( | ||
47 | <div | ||
48 | className={classnames([ | ||
49 | classes.root, | ||
50 | className, | ||
51 | ])} | ||
52 | > | ||
53 | <img | ||
54 | src={service.icon} | ||
55 | className={classnames([ | ||
56 | classes.icon, | ||
57 | service.isEnabled ? null : classes.isDisabled, | ||
58 | service.hasCustomIcon ? classes.isCustomIcon : null, | ||
59 | ])} | ||
60 | alt="" | ||
61 | /> | ||
62 | </div> | ||
63 | ); | ||
64 | } | ||
65 | } | ||
66 | |||
67 | export default ServiceIcon; | ||
diff --git a/src/components/ui/WebviewLoader/index.js b/src/components/ui/WebviewLoader/index.js index 3a3dbbe49..58b6b6f1b 100644 --- a/src/components/ui/WebviewLoader/index.js +++ b/src/components/ui/WebviewLoader/index.js | |||
@@ -2,23 +2,35 @@ import React, { Component } from 'react'; | |||
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import injectSheet from 'react-jss'; | 4 | import injectSheet from 'react-jss'; |
5 | import { defineMessages, intlShape } from 'react-intl'; | ||
5 | 6 | ||
6 | import FullscreenLoader from '../FullscreenLoader'; | 7 | import FullscreenLoader from '../FullscreenLoader'; |
7 | |||
8 | import styles from './styles'; | 8 | import styles from './styles'; |
9 | 9 | ||
10 | const messages = defineMessages({ | ||
11 | loading: { | ||
12 | id: 'service.webviewLoader.loading', | ||
13 | defaultMessage: '!!!Loading', | ||
14 | }, | ||
15 | }); | ||
16 | |||
10 | export default @observer @injectSheet(styles) class WebviewLoader extends Component { | 17 | export default @observer @injectSheet(styles) class WebviewLoader extends Component { |
11 | static propTypes = { | 18 | static propTypes = { |
12 | name: PropTypes.string.isRequired, | 19 | name: PropTypes.string.isRequired, |
13 | classes: PropTypes.object.isRequired, | 20 | classes: PropTypes.object.isRequired, |
14 | } | 21 | }; |
22 | |||
23 | static contextTypes = { | ||
24 | intl: intlShape, | ||
25 | }; | ||
15 | 26 | ||
16 | render() { | 27 | render() { |
17 | const { classes, name } = this.props; | 28 | const { classes, name } = this.props; |
29 | const { intl } = this.context; | ||
18 | return ( | 30 | return ( |
19 | <FullscreenLoader | 31 | <FullscreenLoader |
20 | className={classes.component} | 32 | className={classes.component} |
21 | title={`Loading ${name}`} | 33 | title={`${intl.formatMessage(messages.loading)} ${name}`} |
22 | /> | 34 | /> |
23 | ); | 35 | ); |
24 | } | 36 | } |