aboutsummaryrefslogtreecommitdiffstats
path: root/src/features/quickSwitch/Component.js
diff options
context:
space:
mode:
authorLibravatar muhamedsalih-tw <104364298+muhamedsalih-tw@users.noreply.github.com>2022-10-27 07:13:47 +0530
committerLibravatar GitHub <noreply@github.com>2022-10-27 01:43:47 +0000
commit81c43ecc3d17e0dbf7ad1d949b6d977f2c65bd48 (patch)
treedfa7c08cb54fb81b7d2e788d350de52c2ebd05d2 /src/features/quickSwitch/Component.js
parent6.2.1-nightly.30 [skip ci] (diff)
downloadferdium-app-81c43ecc3d17e0dbf7ad1d949b6d977f2c65bd48.tar.gz
ferdium-app-81c43ecc3d17e0dbf7ad1d949b6d977f2c65bd48.tar.zst
ferdium-app-81c43ecc3d17e0dbf7ad1d949b6d977f2c65bd48.zip
fix: 'failed prop' warning in QuickSwitchModal, SettingsNavigation, SettingsWindow and Recipe component tree (#713)
* chore: turn off eslint rule @typescript-eslint/no-useless-constructor to initialize dynamic props & state Co-authored-by: Muhamed <> Co-authored-by: Vijay A <vraravam@users.noreply.github.com>
Diffstat (limited to 'src/features/quickSwitch/Component.js')
-rw-r--r--src/features/quickSwitch/Component.js357
1 files changed, 0 insertions, 357 deletions
diff --git a/src/features/quickSwitch/Component.js b/src/features/quickSwitch/Component.js
deleted file mode 100644
index 16da22dce..000000000
--- a/src/features/quickSwitch/Component.js
+++ /dev/null
@@ -1,357 +0,0 @@
1import { Component, createRef } from 'react';
2import { getCurrentWindow } from '@electron/remote';
3import PropTypes from 'prop-types';
4import { observer, inject } from 'mobx-react';
5import { reaction } from 'mobx';
6import injectSheet from 'react-jss';
7import { defineMessages, injectIntl } from 'react-intl';
8import { compact, invoke } from 'lodash';
9
10import Input from '../../components/ui/input/index';
11import { H1 } from '../../components/ui/headline';
12import Modal from '../../components/ui/Modal';
13import { state as ModalState } from './store';
14import ServicesStore from '../../stores/ServicesStore';
15
16const messages = defineMessages({
17 title: {
18 id: 'feature.quickSwitch.title',
19 defaultMessage: 'QuickSwitch',
20 },
21 search: {
22 id: 'feature.quickSwitch.search',
23 defaultMessage: 'Search...',
24 },
25 info: {
26 id: 'feature.quickSwitch.info',
27 defaultMessage:
28 'Select a service with TAB, ↑ and ↓. Open a service with ENTER.',
29 },
30});
31
32const styles = theme => ({
33 modal: {
34 width: '80%',
35 maxWidth: 600,
36 background: theme.styleTypes.primary.contrast,
37 paddingTop: 30,
38 },
39 headline: {
40 fontSize: 20,
41 marginBottom: 20,
42 marginTop: -27,
43 },
44 services: {
45 width: '100%',
46 maxHeight: '50vh',
47 overflowX: 'hidden',
48 overflowY: 'auto',
49 },
50 service: {
51 background: theme.styleTypes.primary.contrast,
52 color: theme.colorText,
53 borderRadius: 6,
54 padding: '3px 25px',
55 marginBottom: 10,
56 display: 'flex',
57 alignItems: 'center',
58 '&:last-child': {
59 marginBottom: 0,
60 },
61 '&:hover': {
62 cursor: 'pointer',
63 },
64 },
65 activeService: {
66 background: `${theme.styleTypes.primary.accent} !important`,
67 color: theme.styleTypes.primary.contrast,
68 cursor: 'pointer',
69 },
70 serviceIcon: {
71 width: 50,
72 height: 50,
73 paddingRight: 20,
74 objectFit: 'contain',
75 },
76});
77
78class QuickSwitchModal extends Component {
79 static propTypes = {
80 classes: PropTypes.object.isRequired,
81 };
82
83 state = {
84 selected: 0,
85 search: '',
86 wasPrevVisible: false,
87 };
88
89 ARROW_DOWN = 40;
90
91 ARROW_UP = 38;
92
93 ENTER = 13;
94
95 TAB = 9;
96
97 inputRef = createRef();
98
99 serviceElements = {};
100
101 constructor(props) {
102 super(props);
103
104 this._handleKeyDown = this._handleKeyDown.bind(this);
105 this._handleSearchUpdate = this._handleSearchUpdate.bind(this);
106 this._handleVisibilityChange = this._handleVisibilityChange.bind(this);
107 this.openService = this.openService.bind(this);
108
109 reaction(
110 () => ModalState.isModalVisible,
111 () => {
112 this._handleVisibilityChange();
113 },
114 );
115 }
116
117 // Add global keydown listener when component mounts
118 componentDidMount() {
119 document.addEventListener('keydown', this._handleKeyDown);
120 }
121
122 // Remove global keydown listener when component unmounts
123 componentWillUnmount() {
124 document.removeEventListener('keydown', this._handleKeyDown);
125 }
126
127 // Get currently shown services
128 services() {
129 let services = [];
130 if (
131 this.state.search &&
132 compact(invoke(this.state.search, 'match', /^[\da-z]/i)).length > 0
133 ) {
134 // Apply simple search algorythm to list of all services
135 services = this.props.stores.services.allDisplayed;
136 services = services.filter(
137 service =>
138 service.name.toLowerCase().search(this.state.search.toLowerCase()) !==
139 -1,
140 );
141 } else if (this.props.stores.services.allDisplayed.length > 0) {
142 // Add the currently active service first
143 const currentService = this.props.stores.services.active;
144 if (currentService) {
145 services.push(currentService);
146 }
147
148 // Add last used services to services array
149 for (const service of this.props.stores.services.lastUsedServices) {
150 const tempService = this.props.stores.services.one(service);
151 if (tempService && !services.includes(tempService)) {
152 services.push(tempService);
153 }
154 }
155
156 // Add all other services in the default order
157 for (const service of this.props.stores.services.allDisplayed) {
158 if (!services.includes(service)) {
159 services.push(service);
160 }
161 }
162 }
163
164 return services;
165 }
166
167 openService(index) {
168 // Open service
169 const service = this.services()[index];
170 this.props.actions.service.setActive({ serviceId: service.id });
171
172 // Reset and close modal
173 this.setState({
174 selected: 0,
175 search: '',
176 });
177 this.close();
178 }
179
180 // Change the selected service
181 // factor should be -1 or 1
182 changeSelected(factor) {
183 this.setState(state => {
184 let newSelected = state.selected + factor;
185 const services = this.services().length;
186
187 // Roll around when on edge of list
188 if (state.selected < 1 && factor === -1) {
189 newSelected = services - 1;
190 } else if (state.selected >= services - 1 && factor === 1) {
191 newSelected = 0;
192 }
193
194 // Make sure new selection is visible
195 const serviceElement = this.serviceElements[newSelected];
196 if (serviceElement) {
197 serviceElement.scrollIntoViewIfNeeded(false);
198 }
199
200 return {
201 selected: newSelected,
202 };
203 });
204 }
205
206 // Handle global key presses to change the selection
207 _handleKeyDown(event) {
208 if (ModalState.isModalVisible) {
209 switch (event.keyCode) {
210 case this.ARROW_DOWN:
211 this.changeSelected(1);
212 break;
213 case this.TAB:
214 if (event.shiftKey) {
215 this.changeSelected(-1);
216 } else {
217 this.changeSelected(1);
218 }
219 break;
220 case this.ARROW_UP:
221 this.changeSelected(-1);
222 break;
223 case this.ENTER:
224 this.openService(this.state.selected);
225 break;
226 default:
227 break;
228 }
229 }
230 }
231
232 // Handle update of the search query
233 _handleSearchUpdate(evt) {
234 this.setState({
235 search: evt.target.value,
236 });
237 }
238
239 _handleVisibilityChange() {
240 const { isModalVisible } = ModalState;
241
242 if (isModalVisible && !this.state.wasPrevVisible) {
243 // Set focus back on current window if its in a service
244 // TODO: Find a way to gain back focus
245 getCurrentWindow().blurWebView();
246 getCurrentWindow().webContents.focus();
247
248 // The input "focus" attribute will only work on first modal open
249 // Manually add focus to the input element
250 // Wrapped inside timeout to let the modal render first
251 setTimeout(() => {
252 if (this.inputRef.current) {
253 this.inputRef.current.querySelectorAll('input')[0].focus();
254 }
255 }, 10);
256
257 this.setState({
258 wasPrevVisible: true,
259 });
260 } else if (!isModalVisible && this.state.wasPrevVisible) {
261 // Manually blur focus from the input element to prevent
262 // search query change when modal not visible
263 setTimeout(() => {
264 if (this.inputRef.current) {
265 this.inputRef.current.querySelectorAll('input')[0].blur();
266 }
267 }, 100);
268
269 this.setState({
270 wasPrevVisible: false,
271 });
272 }
273 }
274
275 // Close this modal
276 close() {
277 ModalState.isModalVisible = false;
278 }
279
280 render() {
281 const { isModalVisible } = ModalState;
282
283 const { openService } = this;
284
285 const { classes } = this.props;
286
287 const services = this.services();
288
289 const { intl } = this.props;
290
291 return (
292 <Modal
293 isOpen={isModalVisible}
294 className={`${classes.modal} quick-switch`}
295 shouldCloseOnOverlayClick
296 close={this.close.bind(this)}
297 >
298 <H1 className={classes.headline}>
299 {intl.formatMessage(messages.title)}
300 </H1>
301 <div ref={this.inputRef}>
302 <Input
303 placeholder={intl.formatMessage(messages.search)}
304 focus
305 value={this.state.search}
306 onChange={this._handleSearchUpdate}
307 />
308 </div>
309
310 <div className={classes.services}>
311 {services.map((service, index) => (
312 <div
313 className={`${classes.service} ${
314 this.state.selected === index
315 ? `${classes.activeService} active`
316 : ''
317 } service`}
318 onClick={() => openService(index)}
319 key={service.id}
320 ref={el => {
321 this.serviceElements[index] = el;
322 }}
323 >
324 <img
325 src={service.icon}
326 className={classes.serviceIcon}
327 alt={service.recipe.name}
328 />
329 <div>{service.name}</div>
330 </div>
331 ))}
332 </div>
333
334 <p>
335 <br />
336 {intl.formatMessage(messages.info)}
337 </p>
338 </Modal>
339 );
340 }
341}
342
343QuickSwitchModal.propTypes = {
344 stores: PropTypes.shape({
345 services: PropTypes.instanceOf(ServicesStore).isRequired,
346 }).isRequired,
347 actions: PropTypes.shape({
348 service: PropTypes.instanceOf(ServicesStore).isRequired,
349 }).isRequired,
350 classes: PropTypes.object.isRequired,
351};
352
353export default injectIntl(
354 injectSheet(styles, { injectTheme: true })(
355 inject('stores', 'actions')(observer(QuickSwitchModal)),
356 ),
357);