diff options
author | 2017-10-13 12:29:40 +0200 | |
---|---|---|
committer | 2017-10-13 12:29:40 +0200 | |
commit | 58cda9cc7fb79ca9df6746de7f9662bc08dc156a (patch) | |
tree | 1211600c2a5d3b5f81c435c6896618111a611720 /src/components/services | |
download | ferdium-app-58cda9cc7fb79ca9df6746de7f9662bc08dc156a.tar.gz ferdium-app-58cda9cc7fb79ca9df6746de7f9662bc08dc156a.tar.zst ferdium-app-58cda9cc7fb79ca9df6746de7f9662bc08dc156a.zip |
initial commit
Diffstat (limited to 'src/components/services')
-rw-r--r-- | src/components/services/content/ServiceWebview.js | 73 | ||||
-rw-r--r-- | src/components/services/content/Services.js | 81 | ||||
-rw-r--r-- | src/components/services/tabs/TabBarSortableList.js | 44 | ||||
-rw-r--r-- | src/components/services/tabs/TabItem.js | 136 | ||||
-rw-r--r-- | src/components/services/tabs/Tabbar.js | 77 |
5 files changed, 411 insertions, 0 deletions
diff --git a/src/components/services/content/ServiceWebview.js b/src/components/services/content/ServiceWebview.js new file mode 100644 index 000000000..043ff42ea --- /dev/null +++ b/src/components/services/content/ServiceWebview.js | |||
@@ -0,0 +1,73 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { autorun } from 'mobx'; | ||
4 | import { observer } from 'mobx-react'; | ||
5 | import Webview from 'react-electron-web-view'; | ||
6 | import classnames from 'classnames'; | ||
7 | |||
8 | import ServiceModel from '../../../models/Service'; | ||
9 | |||
10 | @observer | ||
11 | export default class ServiceWebview extends Component { | ||
12 | static propTypes = { | ||
13 | service: PropTypes.instanceOf(ServiceModel).isRequired, | ||
14 | setWebviewReference: PropTypes.func.isRequired, | ||
15 | }; | ||
16 | |||
17 | static defaultProps = { | ||
18 | isActive: false, | ||
19 | }; | ||
20 | |||
21 | state = { | ||
22 | forceRepaint: false, | ||
23 | }; | ||
24 | |||
25 | componentDidMount() { | ||
26 | autorun(() => { | ||
27 | if (this.props.service.isActive) { | ||
28 | this.setState({ forceRepaint: true }); | ||
29 | setTimeout(() => { | ||
30 | this.setState({ forceRepaint: false }); | ||
31 | }, 100); | ||
32 | } | ||
33 | }); | ||
34 | } | ||
35 | |||
36 | webview = null; | ||
37 | |||
38 | render() { | ||
39 | const { | ||
40 | service, | ||
41 | setWebviewReference, | ||
42 | } = this.props; | ||
43 | |||
44 | const webviewClasses = classnames({ | ||
45 | services__webview: true, | ||
46 | 'is-active': service.isActive, | ||
47 | 'services__webview--force-repaint': this.state.forceRepaint, | ||
48 | }); | ||
49 | |||
50 | return ( | ||
51 | <div className={webviewClasses}> | ||
52 | <Webview | ||
53 | ref={(element) => { this.webview = element; }} | ||
54 | |||
55 | autosize | ||
56 | src={service.url} | ||
57 | preload="./webview/plugin.js" | ||
58 | partition={`persist:service-${service.id}`} | ||
59 | |||
60 | onDidAttach={() => setWebviewReference({ | ||
61 | serviceId: service.id, | ||
62 | webview: this.webview.view, | ||
63 | })} | ||
64 | |||
65 | useragent={service.userAgent} | ||
66 | |||
67 | disablewebsecurity | ||
68 | allowpopups | ||
69 | /> | ||
70 | </div> | ||
71 | ); | ||
72 | } | ||
73 | } | ||
diff --git a/src/components/services/content/Services.js b/src/components/services/content/Services.js new file mode 100644 index 000000000..03c68b06f --- /dev/null +++ b/src/components/services/content/Services.js | |||
@@ -0,0 +1,81 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | ||
4 | import { Link } from 'react-router'; | ||
5 | import { defineMessages, intlShape } from 'react-intl'; | ||
6 | |||
7 | import Webview from './ServiceWebview'; | ||
8 | import Appear from '../../ui/effects/Appear'; | ||
9 | |||
10 | const messages = defineMessages({ | ||
11 | welcome: { | ||
12 | id: 'services.welcome', | ||
13 | defaultMessage: '!!!Welcome to Franz', | ||
14 | }, | ||
15 | getStarted: { | ||
16 | id: 'services.getStarted', | ||
17 | defaultMessage: '!!!Get started', | ||
18 | }, | ||
19 | }); | ||
20 | |||
21 | @observer | ||
22 | export default class Services extends Component { | ||
23 | static propTypes = { | ||
24 | services: MobxPropTypes.arrayOrObservableArray.isRequired, | ||
25 | setWebviewReference: PropTypes.func.isRequired, | ||
26 | handleIPCMessage: PropTypes.func.isRequired, | ||
27 | openWindow: PropTypes.func.isRequired, | ||
28 | }; | ||
29 | |||
30 | static defaultProps = { | ||
31 | services: [], | ||
32 | activeService: '', | ||
33 | }; | ||
34 | |||
35 | static contextTypes = { | ||
36 | intl: intlShape, | ||
37 | }; | ||
38 | |||
39 | render() { | ||
40 | const { | ||
41 | services, | ||
42 | handleIPCMessage, | ||
43 | setWebviewReference, | ||
44 | openWindow, | ||
45 | } = this.props; | ||
46 | const { intl } = this.context; | ||
47 | |||
48 | return ( | ||
49 | <div className="services"> | ||
50 | {services.length === 0 && ( | ||
51 | <Appear | ||
52 | timeout={1500} | ||
53 | transitionName="slideUp" | ||
54 | > | ||
55 | <div className="services__no-service"> | ||
56 | <img src="./assets/images/logo.svg" alt="" /> | ||
57 | <h1>{intl.formatMessage(messages.welcome)}</h1> | ||
58 | <Appear | ||
59 | timeout={300} | ||
60 | transitionName="slideUp" | ||
61 | > | ||
62 | <Link to="/settings/recipes" className="button"> | ||
63 | {intl.formatMessage(messages.getStarted)} | ||
64 | </Link> | ||
65 | </Appear> | ||
66 | </div> | ||
67 | </Appear> | ||
68 | )} | ||
69 | {services.map(service => ( | ||
70 | <Webview | ||
71 | key={service.id} | ||
72 | service={service} | ||
73 | handleIPCMessage={handleIPCMessage} | ||
74 | setWebviewReference={setWebviewReference} | ||
75 | openWindow={openWindow} | ||
76 | /> | ||
77 | ))} | ||
78 | </div> | ||
79 | ); | ||
80 | } | ||
81 | } | ||
diff --git a/src/components/services/tabs/TabBarSortableList.js b/src/components/services/tabs/TabBarSortableList.js new file mode 100644 index 000000000..c0a68d1a5 --- /dev/null +++ b/src/components/services/tabs/TabBarSortableList.js | |||
@@ -0,0 +1,44 @@ | |||
1 | import React from 'react'; | ||
2 | import { observer } from 'mobx-react'; | ||
3 | import { SortableContainer } from 'react-sortable-hoc'; | ||
4 | |||
5 | import TabItem from './TabItem'; | ||
6 | import { ctrlKey } from '../../../environment'; | ||
7 | |||
8 | export default SortableContainer(observer(({ | ||
9 | services, | ||
10 | setActive, | ||
11 | reload, | ||
12 | toggleNotifications, | ||
13 | deleteService, | ||
14 | disableService, | ||
15 | openSettings, | ||
16 | }) => ( | ||
17 | <ul | ||
18 | className="tabs" | ||
19 | > | ||
20 | {services.map((service, index) => ( | ||
21 | <TabItem | ||
22 | key={service.id} | ||
23 | clickHandler={() => setActive({ serviceId: service.id })} | ||
24 | service={service} | ||
25 | index={index} | ||
26 | shortcutIndex={index + 1} | ||
27 | reload={() => reload({ serviceId: service.id })} | ||
28 | toggleNotifications={() => toggleNotifications({ serviceId: service.id })} | ||
29 | deleteService={() => deleteService({ serviceId: service.id })} | ||
30 | disableService={() => disableService({ serviceId: service.id })} | ||
31 | openSettings={openSettings} | ||
32 | /> | ||
33 | ))} | ||
34 | <li> | ||
35 | <button | ||
36 | className="sidebar__add-service" | ||
37 | onClick={() => openSettings({ path: 'recipes' })} | ||
38 | data-tip={`Add new service (${ctrlKey}+N)`} | ||
39 | > | ||
40 | <span className="mdi mdi-plus" /> | ||
41 | </button> | ||
42 | </li> | ||
43 | </ul> | ||
44 | ))); | ||
diff --git a/src/components/services/tabs/TabItem.js b/src/components/services/tabs/TabItem.js new file mode 100644 index 000000000..9e03d2e21 --- /dev/null +++ b/src/components/services/tabs/TabItem.js | |||
@@ -0,0 +1,136 @@ | |||
1 | import { remote } from 'electron'; | ||
2 | import React, { Component } from 'react'; | ||
3 | import { defineMessages, intlShape } from 'react-intl'; | ||
4 | import PropTypes from 'prop-types'; | ||
5 | import { observer } from 'mobx-react'; | ||
6 | import classnames from 'classnames'; | ||
7 | import { SortableElement } from 'react-sortable-hoc'; | ||
8 | |||
9 | import ServiceModel from '../../../models/Service'; | ||
10 | import { ctrlKey } from '../../../environment'; | ||
11 | |||
12 | const { Menu } = remote; | ||
13 | |||
14 | const messages = defineMessages({ | ||
15 | reload: { | ||
16 | id: 'tabs.item.reload', | ||
17 | defaultMessage: '!!!Reload', | ||
18 | }, | ||
19 | edit: { | ||
20 | id: 'tabs.item.edit', | ||
21 | defaultMessage: '!!!Edit', | ||
22 | }, | ||
23 | disableNotifications: { | ||
24 | id: 'tabs.item.disableNotifications', | ||
25 | defaultMessage: '!!!Disable notifications', | ||
26 | }, | ||
27 | enableNotifications: { | ||
28 | id: 'tabs.item.enableNotification', | ||
29 | defaultMessage: '!!!Enable notifications', | ||
30 | }, | ||
31 | disableService: { | ||
32 | id: 'tabs.item.disableService', | ||
33 | defaultMessage: '!!!Disable Service', | ||
34 | }, | ||
35 | deleteService: { | ||
36 | id: 'tabs.item.deleteService', | ||
37 | defaultMessage: '!!!Delete Service', | ||
38 | }, | ||
39 | }); | ||
40 | |||
41 | @observer | ||
42 | class TabItem extends Component { | ||
43 | static propTypes = { | ||
44 | service: PropTypes.instanceOf(ServiceModel).isRequired, | ||
45 | clickHandler: PropTypes.func.isRequired, | ||
46 | shortcutIndex: PropTypes.number.isRequired, | ||
47 | reload: PropTypes.func.isRequired, | ||
48 | toggleNotifications: PropTypes.func.isRequired, | ||
49 | openSettings: PropTypes.func.isRequired, | ||
50 | deleteService: PropTypes.func.isRequired, | ||
51 | disableService: PropTypes.func.isRequired, | ||
52 | }; | ||
53 | |||
54 | static contextTypes = { | ||
55 | intl: intlShape, | ||
56 | }; | ||
57 | |||
58 | render() { | ||
59 | const { | ||
60 | service, | ||
61 | clickHandler, | ||
62 | shortcutIndex, | ||
63 | reload, | ||
64 | toggleNotifications, | ||
65 | deleteService, | ||
66 | disableService, | ||
67 | openSettings, | ||
68 | } = this.props; | ||
69 | const { intl } = this.context; | ||
70 | |||
71 | |||
72 | const menuTemplate = [{ | ||
73 | label: service.name || service.recipe.name, | ||
74 | enabled: false, | ||
75 | }, { | ||
76 | type: 'separator', | ||
77 | }, { | ||
78 | label: intl.formatMessage(messages.reload), | ||
79 | click: reload, | ||
80 | }, { | ||
81 | label: intl.formatMessage(messages.edit), | ||
82 | click: () => openSettings({ | ||
83 | path: `services/edit/${service.id}`, | ||
84 | }), | ||
85 | }, { | ||
86 | type: 'separator', | ||
87 | }, { | ||
88 | label: service.isNotificationEnabled | ||
89 | ? intl.formatMessage(messages.disableNotifications) | ||
90 | : intl.formatMessage(messages.enableNotifications), | ||
91 | click: () => toggleNotifications(), | ||
92 | }, { | ||
93 | label: intl.formatMessage(messages.disableService), | ||
94 | click: () => disableService(), | ||
95 | }, { | ||
96 | type: 'separator', | ||
97 | }, { | ||
98 | label: intl.formatMessage(messages.deleteService), | ||
99 | click: () => deleteService(), | ||
100 | }]; | ||
101 | const menu = Menu.buildFromTemplate(menuTemplate); | ||
102 | |||
103 | return ( | ||
104 | <li | ||
105 | className={classnames({ | ||
106 | 'tab-item': true, | ||
107 | 'is-active': service.isActive, | ||
108 | 'has-custom-icon': service.hasCustomIcon, | ||
109 | })} | ||
110 | onClick={clickHandler} | ||
111 | onContextMenu={() => menu.popup(remote.getCurrentWindow())} | ||
112 | data-tip={`${service.name} ${shortcutIndex <= 9 ? `(${ctrlKey}+${shortcutIndex})` : ''}`} | ||
113 | > | ||
114 | <img | ||
115 | src={service.icon} | ||
116 | className="tab-item__icon" | ||
117 | alt="" | ||
118 | /> | ||
119 | {service.unreadDirectMessageCount > 0 && ( | ||
120 | <span className="tab-item__message-count"> | ||
121 | {service.unreadDirectMessageCount} | ||
122 | </span> | ||
123 | )} | ||
124 | {service.unreadIndirectMessageCount > 0 | ||
125 | && service.unreadDirectMessageCount === 0 | ||
126 | && service.isIndirectMessageBadgeEnabled && ( | ||
127 | <span className="tab-item__message-count is-indirect"> | ||
128 | • | ||
129 | </span> | ||
130 | )} | ||
131 | </li> | ||
132 | ); | ||
133 | } | ||
134 | } | ||
135 | |||
136 | export default SortableElement(TabItem); | ||
diff --git a/src/components/services/tabs/Tabbar.js b/src/components/services/tabs/Tabbar.js new file mode 100644 index 000000000..fdb2c0a59 --- /dev/null +++ b/src/components/services/tabs/Tabbar.js | |||
@@ -0,0 +1,77 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | ||
4 | |||
5 | import TabBarSortableList from './TabBarSortableList'; | ||
6 | |||
7 | @observer | ||
8 | export default class TabBar extends Component { | ||
9 | static propTypes = { | ||
10 | services: MobxPropTypes.arrayOrObservableArray.isRequired, | ||
11 | setActive: PropTypes.func.isRequired, | ||
12 | openSettings: PropTypes.func.isRequired, | ||
13 | enableToolTip: PropTypes.func.isRequired, | ||
14 | disableToolTip: PropTypes.func.isRequired, | ||
15 | reorder: PropTypes.func.isRequired, | ||
16 | reload: PropTypes.func.isRequired, | ||
17 | toggleNotifications: PropTypes.func.isRequired, | ||
18 | deleteService: PropTypes.func.isRequired, | ||
19 | updateService: PropTypes.func.isRequired, | ||
20 | } | ||
21 | |||
22 | onSortEnd = ({ oldIndex, newIndex }) => { | ||
23 | const { | ||
24 | enableToolTip, | ||
25 | reorder, | ||
26 | } = this.props; | ||
27 | |||
28 | enableToolTip(); | ||
29 | reorder({ oldIndex, newIndex }); | ||
30 | }; | ||
31 | |||
32 | disableService = ({ serviceId }) => { | ||
33 | const { updateService } = this.props; | ||
34 | |||
35 | if (serviceId) { | ||
36 | updateService({ | ||
37 | serviceId, | ||
38 | serviceData: { | ||
39 | isEnabled: false, | ||
40 | }, | ||
41 | redirect: false, | ||
42 | }); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | render() { | ||
47 | const { | ||
48 | services, | ||
49 | setActive, | ||
50 | openSettings, | ||
51 | disableToolTip, | ||
52 | reload, | ||
53 | toggleNotifications, | ||
54 | deleteService, | ||
55 | } = this.props; | ||
56 | |||
57 | return ( | ||
58 | <div> | ||
59 | <TabBarSortableList | ||
60 | services={services} | ||
61 | setActive={setActive} | ||
62 | onSortEnd={this.onSortEnd} | ||
63 | onSortStart={disableToolTip} | ||
64 | reload={reload} | ||
65 | toggleNotifications={toggleNotifications} | ||
66 | deleteService={deleteService} | ||
67 | disableService={this.disableService} | ||
68 | openSettings={openSettings} | ||
69 | distance={20} | ||
70 | axis="y" | ||
71 | lockAxis="y" | ||
72 | helperClass="is-reordering" | ||
73 | /> | ||
74 | </div> | ||
75 | ); | ||
76 | } | ||
77 | } | ||