aboutsummaryrefslogtreecommitdiffstats
path: root/src/features/todos/components/TodosWebview.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/features/todos/components/TodosWebview.js')
-rw-r--r--src/features/todos/components/TodosWebview.js300
1 files changed, 300 insertions, 0 deletions
diff --git a/src/features/todos/components/TodosWebview.js b/src/features/todos/components/TodosWebview.js
new file mode 100644
index 000000000..c06183e37
--- /dev/null
+++ b/src/features/todos/components/TodosWebview.js
@@ -0,0 +1,300 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { observer } from 'mobx-react';
4import injectSheet from 'react-jss';
5import Webview from 'react-electron-web-view';
6import { Icon } from '@meetfranz/ui';
7import { defineMessages, intlShape } from 'react-intl';
8
9import { mdiChevronRight, mdiCheckAll } from '@mdi/js';
10import * as environment from '../../../environment';
11import Appear from '../../../components/ui/effects/Appear';
12import UpgradeButton from '../../../components/ui/UpgradeButton';
13
14const OPEN_TODOS_BUTTON_SIZE = 45;
15const CLOSE_TODOS_BUTTON_SIZE = 35;
16
17const messages = defineMessages({
18 premiumInfo: {
19 id: 'feature.todos.premium.info',
20 defaultMessage: '!!!Franz Todos are available to premium users now!',
21 },
22 upgradeCTA: {
23 id: 'feature.todos.premium.upgrade',
24 defaultMessage: '!!!Upgrade Account',
25 },
26 rolloutInfo: {
27 id: 'feature.todos.premium.rollout',
28 defaultMessage: '!!!Everyone else will have to wait a little longer.',
29 },
30});
31
32const styles = theme => ({
33 root: {
34 background: theme.colorBackground,
35 position: 'relative',
36 borderLeft: [1, 'solid', theme.todos.todosLayer.borderLeftColor],
37 zIndex: 300,
38
39 transform: ({ isVisible, width }) => `translateX(${isVisible ? 0 : width}px)`,
40
41 '&:hover $closeTodosButton': {
42 opacity: 1,
43 },
44 '& webview': {
45 height: '100%',
46 },
47 },
48 resizeHandler: {
49 position: 'absolute',
50 left: 0,
51 marginLeft: -5,
52 width: 10,
53 zIndex: 400,
54 cursor: 'col-resize',
55 },
56 dragIndicator: {
57 position: 'absolute',
58 left: 0,
59 width: 5,
60 zIndex: 400,
61 background: theme.todos.dragIndicator.background,
62
63 },
64 openTodosButton: {
65 width: OPEN_TODOS_BUTTON_SIZE,
66 height: OPEN_TODOS_BUTTON_SIZE,
67 background: theme.todos.toggleButton.background,
68 position: 'absolute',
69 bottom: 120,
70 right: props => (props.width + (props.isVisible ? -OPEN_TODOS_BUTTON_SIZE / 2 : 0)),
71 borderRadius: OPEN_TODOS_BUTTON_SIZE / 2,
72 opacity: props => (props.isVisible ? 0 : 1),
73 transition: 'right 0.5s',
74 zIndex: 600,
75 display: 'flex',
76 alignItems: 'center',
77 justifyContent: 'center',
78 boxShadow: [0, 0, 10, theme.todos.toggleButton.shadowColor],
79
80 borderTopRightRadius: props => (props.isVisible ? null : 0),
81 borderBottomRightRadius: props => (props.isVisible ? null : 0),
82
83 '& svg': {
84 fill: theme.todos.toggleButton.textColor,
85 transition: 'all 0.5s',
86 },
87 },
88 closeTodosButton: {
89 width: CLOSE_TODOS_BUTTON_SIZE,
90 height: CLOSE_TODOS_BUTTON_SIZE,
91 background: theme.todos.toggleButton.background,
92 position: 'absolute',
93 bottom: 120,
94 right: ({ width }) => (width + -CLOSE_TODOS_BUTTON_SIZE / 2),
95 borderRadius: CLOSE_TODOS_BUTTON_SIZE / 2,
96 opacity: ({ isTodosIncludedInCurrentPlan }) => (!isTodosIncludedInCurrentPlan ? 1 : 0),
97 transition: 'opacity 0.5s',
98 zIndex: 600,
99 display: 'flex',
100 alignItems: 'center',
101 justifyContent: 'center',
102 boxShadow: [0, 0, 10, theme.todos.toggleButton.shadowColor],
103
104 '& svg': {
105 fill: theme.todos.toggleButton.textColor,
106 },
107 },
108 premiumContainer: {
109 display: 'flex',
110 flexDirection: 'column',
111 justifyContent: 'center',
112 alignItems: 'center',
113 width: '80%',
114 maxWidth: 300,
115 margin: [0, 'auto'],
116 textAlign: 'center',
117 },
118 premiumIcon: {
119 marginBottom: 40,
120 background: theme.styleTypes.primary.accent,
121 fill: theme.styleTypes.primary.contrast,
122 padding: 10,
123 borderRadius: 10,
124 },
125 premiumCTA: {
126 marginTop: 40,
127 },
128});
129
130@injectSheet(styles) @observer
131class TodosWebview extends Component {
132 static propTypes = {
133 classes: PropTypes.object.isRequired,
134 isVisible: PropTypes.bool.isRequired,
135 togglePanel: PropTypes.func.isRequired,
136 handleClientMessage: PropTypes.func.isRequired,
137 setTodosWebview: PropTypes.func.isRequired,
138 resize: PropTypes.func.isRequired,
139 width: PropTypes.number.isRequired,
140 minWidth: PropTypes.number.isRequired,
141 isTodosIncludedInCurrentPlan: PropTypes.bool.isRequired,
142 };
143
144 state = {
145 isDragging: false,
146 width: 300,
147 };
148
149 static contextTypes = {
150 intl: intlShape,
151 };
152
153 componentWillMount() {
154 const { width } = this.props;
155
156 this.setState({
157 width,
158 });
159 }
160
161 componentDidMount() {
162 this.node.addEventListener('mousemove', this.resizePanel.bind(this));
163 this.node.addEventListener('mouseup', this.stopResize.bind(this));
164 this.node.addEventListener('mouseleave', this.stopResize.bind(this));
165 }
166
167 startResize = (event) => {
168 this.setState({
169 isDragging: true,
170 initialPos: event.clientX,
171 delta: 0,
172 });
173 };
174
175 resizePanel(e) {
176 const { minWidth } = this.props;
177
178 const {
179 isDragging,
180 initialPos,
181 } = this.state;
182
183 if (isDragging && Math.abs(e.clientX - window.innerWidth) > minWidth) {
184 const delta = e.clientX - initialPos;
185
186 this.setState({
187 delta,
188 });
189 }
190 }
191
192 stopResize() {
193 const {
194 resize,
195 minWidth,
196 } = this.props;
197
198 const {
199 isDragging,
200 delta,
201 width,
202 } = this.state;
203
204 if (isDragging) {
205 let newWidth = width + (delta < 0 ? Math.abs(delta) : -Math.abs(delta));
206
207 if (newWidth < minWidth) {
208 newWidth = minWidth;
209 }
210
211 this.setState({
212 isDragging: false,
213 delta: 0,
214 width: newWidth,
215 });
216
217 resize(newWidth);
218 }
219 }
220
221 startListeningToIpcMessages() {
222 const { handleClientMessage } = this.props;
223 if (!this.webview) return;
224 this.webview.addEventListener('ipc-message', e => handleClientMessage(e.args[0]));
225 }
226
227 render() {
228 const {
229 classes,
230 isVisible,
231 togglePanel,
232 isTodosIncludedInCurrentPlan,
233 } = this.props;
234
235 const {
236 width,
237 delta,
238 isDragging,
239 } = this.state;
240
241 const { intl } = this.context;
242
243 return (
244 <div
245 className={classes.root}
246 style={{ width: isVisible ? width : 0 }}
247 onMouseUp={() => this.stopResize()}
248 ref={(node) => { this.node = node; }}
249 >
250 <button
251 onClick={() => togglePanel()}
252 className={isVisible ? classes.closeTodosButton : classes.openTodosButton}
253 type="button"
254 >
255 <Icon icon={isVisible ? mdiChevronRight : mdiCheckAll} size={2} />
256 </button>
257 <div
258 className={classes.resizeHandler}
259 style={Object.assign({ left: delta }, isDragging ? { width: 600, marginLeft: -200 } : {})} // This hack is required as resizing with webviews beneath behaves quite bad
260 onMouseDown={e => this.startResize(e)}
261 />
262 {isDragging && (
263 <div
264 className={classes.dragIndicator}
265 style={{ left: delta }} // This hack is required as resizing with webviews beneath behaves quite bad
266 />
267 )}
268 {isTodosIncludedInCurrentPlan ? (
269 <Webview
270 className={classes.webview}
271 onDidAttach={() => {
272 const { setTodosWebview } = this.props;
273 setTodosWebview(this.webview);
274 this.startListeningToIpcMessages();
275 }}
276 partition="persist:todos"
277 preload="./features/todos/preload.js"
278 ref={(webview) => { this.webview = webview ? webview.view : null; }}
279 src={environment.TODOS_FRONTEND}
280 />
281 ) : (
282 <Appear>
283 <div className={classes.premiumContainer}>
284 <Icon icon={mdiCheckAll} className={classes.premiumIcon} size={4} />
285 <p>{intl.formatMessage(messages.premiumInfo)}</p>
286 <p>{intl.formatMessage(messages.rolloutInfo)}</p>
287 <UpgradeButton
288 className={classes.premiumCTA}
289 gaEventInfo={{ category: 'Todos', event: 'upgrade' }}
290 short
291 />
292 </div>
293 </Appear>
294 )}
295 </div>
296 );
297 }
298}
299
300export default TodosWebview;