Skip to content

Commit 68b1f8d

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
Implemented the Network tab.
Signed-off-by: Akos Kitta <[email protected]>
1 parent e957ac4 commit 68b1f8d

File tree

7 files changed

+322
-17
lines changed

7 files changed

+322
-17
lines changed

Diff for: arduino-ide-extension/package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"@types/ncp": "^2.0.4",
4242
"@types/ps-tree": "^1.1.0",
4343
"@types/react-select": "^3.0.0",
44+
"@types/react-tabs": "^2.3.2",
4445
"@types/sinon": "^7.5.2",
4546
"@types/temp": "^0.8.34",
4647
"@types/which": "^1.3.1",
@@ -56,7 +57,9 @@
5657
"ncp": "^2.0.0",
5758
"p-queue": "^5.0.0",
5859
"ps-tree": "^1.2.0",
60+
"react-disable": "^0.1.0",
5961
"react-select": "^3.0.4",
62+
"react-tabs": "^3.1.2",
6063
"semver": "^7.3.2",
6164
"string-natural-compare": "^2.0.3",
6265
"temp": "^0.9.1",
@@ -120,7 +123,7 @@
120123
],
121124
"arduino": {
122125
"cli": {
123-
"version": "0.15.0-rc1"
126+
"version": "20210203"
124127
}
125128
}
126129
}

Diff for: arduino-ide-extension/src/browser/settings.tsx

+193-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import * as React from 'react';
22
import { injectable, inject, postConstruct } from 'inversify';
33
import { Widget } from '@phosphor/widgets';
44
import { Message } from '@phosphor/messaging';
5+
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
6+
import 'react-tabs/style/react-tabs.css';
7+
import { Disable } from 'react-disable';
58
import URI from '@theia/core/lib/common/uri';
69
import { Emitter } from '@theia/core/lib/common/event';
710
import { Deferred } from '@theia/core/lib/common/promise-util';
@@ -15,7 +18,7 @@ import { DisposableCollection } from '@theia/core/lib/common/disposable';
1518
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
1619
import { AbstractDialog, DialogProps, PreferenceService, PreferenceScope, DialogError, ReactWidget } from '@theia/core/lib/browser';
1720
import { Index } from '../common/types';
18-
import { ConfigService, FileSystemExt } from '../common/protocol';
21+
import { ConfigService, FileSystemExt, Network, ProxySettings } from '../common/protocol';
1922

2023
export interface Settings extends Index {
2124
editorFontSize: number; // `editor.fontSize`
@@ -32,6 +35,7 @@ export interface Settings extends Index {
3235

3336
sketchbookPath: string; // CLI
3437
additionalUrls: string[]; // CLI
38+
network: Network; // CLI
3539
}
3640
export namespace Settings {
3741

@@ -40,7 +44,6 @@ export namespace Settings {
4044
}
4145

4246
}
43-
export type SettingsKey = keyof Settings;
4447

4548
@injectable()
4649
export class SettingsService {
@@ -101,7 +104,7 @@ export class SettingsService {
101104
this.preferenceService.get<boolean>('arduino.language.log', true),
102105
this.configService.getConfiguration()
103106
]);
104-
const { additionalUrls, sketchDirUri } = cliConfig;
107+
const { additionalUrls, sketchDirUri, network } = cliConfig;
105108
const sketchbookPath = await this.fileService.fsPath(new URI(sketchDirUri));
106109
return {
107110
editorFontSize,
@@ -115,7 +118,8 @@ export class SettingsService {
115118
verifyAfterUpload,
116119
enableLsLogs,
117120
additionalUrls,
118-
sketchbookPath
121+
sketchbookPath,
122+
network
119123
};
120124
}
121125

@@ -175,14 +179,16 @@ export class SettingsService {
175179
verifyAfterUpload,
176180
enableLsLogs,
177181
sketchbookPath,
178-
additionalUrls
182+
additionalUrls,
183+
network
179184
} = this._settings;
180185
const [config, sketchDirUri] = await Promise.all([
181186
this.configService.getConfiguration(),
182187
this.fileSystemExt.getUri(sketchbookPath)
183188
]);
184189
(config as any).additionalUrls = additionalUrls;
185190
(config as any).sketchDirUri = sketchDirUri;
191+
(config as any).network = network;
186192

187193
await Promise.all([
188194
this.preferenceService.set('editor.fontSize', editorFontSize, PreferenceScope.User),
@@ -230,6 +236,21 @@ export class SettingsComponent extends React.Component<SettingsComponent.Props,
230236
if (!this.state) {
231237
return <div />;
232238
}
239+
return <Tabs>
240+
<TabList>
241+
<Tab>Settings</Tab>
242+
<Tab>Network</Tab>
243+
</TabList>
244+
<TabPanel>
245+
{this.renderSettings()}
246+
</TabPanel>
247+
<TabPanel>
248+
{this.renderNetwork()}
249+
</TabPanel>
250+
</Tabs>;
251+
}
252+
253+
protected renderSettings(): React.ReactNode {
233254
return <div className='content noselect'>
234255
Sketchbook location:
235256
<div className='flex-line'>
@@ -343,12 +364,106 @@ export class SettingsComponent extends React.Component<SettingsComponent.Props,
343364
</div>;
344365
}
345366

367+
protected renderNetwork(): React.ReactNode {
368+
return <div className='content noselect'>
369+
<form>
370+
<label className='flex-line'>
371+
<input
372+
type='radio'
373+
checked={this.state.network === 'none'}
374+
onChange={this.noProxyDidChange} />
375+
No proxy
376+
</label>
377+
<label className='flex-line'>
378+
<input
379+
type='radio'
380+
checked={this.state.network !== 'none'}
381+
onChange={this.manualProxyDidChange} />
382+
Manual proxy configuration
383+
</label>
384+
</form>
385+
{this.renderProxySettings()}
386+
</div>;
387+
}
388+
389+
protected renderProxySettings(): React.ReactNode {
390+
const disabled = this.state.network === 'none';
391+
return <Disable disabled={disabled}>
392+
<div className='proxy-settings' aria-disabled={disabled}>
393+
<form className='flex-line'>
394+
<input
395+
type='radio'
396+
checked={this.state.network === 'none' ? true : this.state.network.protocol === 'http'}
397+
onChange={this.httpProtocolDidChange} />
398+
HTTP
399+
<label className='flex-line'>
400+
<input
401+
type='radio'
402+
checked={this.state.network === 'none' ? false : this.state.network.protocol !== 'http'}
403+
onChange={this.socksProtocolDidChange} />
404+
SOCKS
405+
</label>
406+
</form>
407+
<div className='flex-line proxy-settings'>
408+
<div className='column'>
409+
<div className='flex-line'>Host name:</div>
410+
<div className='flex-line'>Port number:</div>
411+
<div className='flex-line'>Username:</div>
412+
<div className='flex-line'>Password:</div>
413+
</div>
414+
<div className='column stretch'>
415+
<div className='flex-line'>
416+
<input
417+
className='theia-input stretch with-margin'
418+
type='text'
419+
value={this.state.network === 'none' ? '' : this.state.network.hostname}
420+
onChange={this.hostnameDidChange} />
421+
</div>
422+
<div className='flex-line'>
423+
<input
424+
className='theia-input small with-margin'
425+
type='number'
426+
pattern='[0-9]'
427+
value={this.state.network === 'none' ? '' : this.state.network.port}
428+
onKeyDown={this.numbersOnlyKeyDown}
429+
onChange={this.portDidChange} />
430+
</div>
431+
<div className='flex-line'>
432+
<input
433+
className='theia-input stretch with-margin'
434+
type='text'
435+
value={this.state.network === 'none' ? '' : this.state.network.username}
436+
onChange={this.usernameDidChange} />
437+
</div>
438+
<div className='flex-line'>
439+
<input
440+
className='theia-input stretch with-margin'
441+
type='password'
442+
value={this.state.network === 'none' ? '' : this.state.network.password}
443+
onChange={this.passwordDidChange} />
444+
</div>
445+
</div>
446+
</div>
447+
</div>
448+
</Disable>;
449+
}
450+
451+
private isControlKey(event: React.KeyboardEvent<HTMLInputElement>): boolean {
452+
return !!event.key && ['tab', 'delete', 'backspace', 'arrowleft', 'arrowright'].some(key => event.key.toLocaleLowerCase() === key);
453+
}
454+
346455
protected noopKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
456+
if (this.isControlKey(event)) {
457+
return;
458+
}
347459
event.nativeEvent.preventDefault();
348460
event.nativeEvent.returnValue = false;
349461
}
350462

351463
protected numbersOnlyKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
464+
if (this.isControlKey(event)) {
465+
return;
466+
}
352467
const key = Number(event.key)
353468
if (isNaN(key) || event.key === null || event.key === ' ') {
354469
event.nativeEvent.preventDefault();
@@ -444,6 +559,79 @@ export class SettingsComponent extends React.Component<SettingsComponent.Props,
444559
}
445560
};
446561

562+
protected noProxyDidChange = (event: React.ChangeEvent<HTMLInputElement>) => {
563+
if (event.target.checked) {
564+
this.setState({ network: 'none' });
565+
} else {
566+
this.setState({ network: Network.Default() });
567+
}
568+
};
569+
570+
protected manualProxyDidChange = (event: React.ChangeEvent<HTMLInputElement>) => {
571+
if (event.target.checked) {
572+
this.setState({ network: Network.Default() });
573+
} else {
574+
this.setState({ network: 'none' });
575+
}
576+
};
577+
578+
protected httpProtocolDidChange = (event: React.ChangeEvent<HTMLInputElement>) => {
579+
if (this.state.network !== 'none') {
580+
const network = this.cloneProxySettings;
581+
network.protocol = event.target.checked ? 'http' : 'socks';
582+
this.setState({ network });
583+
}
584+
};
585+
586+
protected socksProtocolDidChange = (event: React.ChangeEvent<HTMLInputElement>) => {
587+
if (this.state.network !== 'none') {
588+
const network = this.cloneProxySettings;
589+
network.protocol = event.target.checked ? 'socks' : 'http';
590+
this.setState({ network });
591+
}
592+
};
593+
594+
protected hostnameDidChange = (event: React.ChangeEvent<HTMLInputElement>) => {
595+
if (this.state.network !== 'none') {
596+
const network = this.cloneProxySettings;
597+
network.hostname = event.target.value;
598+
this.setState({ network });
599+
}
600+
};
601+
602+
protected portDidChange = (event: React.ChangeEvent<HTMLInputElement>) => {
603+
if (this.state.network !== 'none') {
604+
const network = this.cloneProxySettings;
605+
network.port = event.target.value;
606+
this.setState({ network });
607+
}
608+
};
609+
610+
protected usernameDidChange = (event: React.ChangeEvent<HTMLInputElement>) => {
611+
if (this.state.network !== 'none') {
612+
const network = this.cloneProxySettings;
613+
network.username = event.target.value;
614+
this.setState({ network });
615+
}
616+
};
617+
618+
protected passwordDidChange = (event: React.ChangeEvent<HTMLInputElement>) => {
619+
if (this.state.network !== 'none') {
620+
const network = this.cloneProxySettings;
621+
network.password = event.target.value;
622+
this.setState({ network });
623+
}
624+
};
625+
626+
private get cloneProxySettings(): ProxySettings {
627+
const { network } = this.state;
628+
if (network === 'none') {
629+
throw new Error('Must be called when proxy is enabled.');
630+
}
631+
const copyNetwork = deepClone(network);
632+
return copyNetwork;
633+
}
634+
447635
}
448636
export namespace SettingsComponent {
449637
export interface Props {

Diff for: arduino-ide-extension/src/browser/style/settings-dialog.css

+18-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
.arduino-settings-dialog .content {
66
padding: 5px;
7+
height: 250px;
78
}
89

910
.arduino-settings-dialog .flex-line {
@@ -25,19 +26,32 @@
2526
vertical-align: middle;
2627
}
2728

29+
.arduino-settings-dialog .stretch {
30+
width: 100% !important;
31+
}
32+
2833
.arduino-settings-dialog .flex-line .theia-button.shrink {
2934
min-width: unset;
3035
}
3136

32-
.arduino-settings-dialog .theia-input.stretch {
33-
width: 100% !important;
37+
.arduino-settings-dialog .proxy-settings {
38+
margin: 5px;
39+
}
40+
41+
.arduino-settings-dialog input[type="radio"] {
42+
margin: 3px !important;
3443
}
3544

3645
.arduino-settings-dialog .theia-input.small {
37-
max-width: 40px;
38-
width: 40px;
46+
max-width: 50px;
47+
width: 50px;
3948
}
4049

4150
.additional-urls-dialog .link:hover {
4251
color: var(--theia-textLink-activeForeground);
4352
}
53+
54+
.arduino-settings-dialog .react-tabs__tab-list {
55+
display: flex;
56+
justify-content: center;
57+
}

0 commit comments

Comments
 (0)