WiFi Channel Settings.
This commit is contained in:
+1
-1
@@ -646,7 +646,7 @@ const App = withStyles(appStyles)(class extends ResizeResponsiveComponent<AppPro
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<TemporaryDrawer position='left' title="piddle"
|
||||
<TemporaryDrawer position='left' title="PiPedal"
|
||||
is_open={this.state.isDrawerOpen} onClose={() => { this.hideDrawer(); }} >
|
||||
<List subheader={
|
||||
<ListSubheader component="div" id="nested-list-subheader">Banks</ListSubheader>
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import React from 'react';
|
||||
|
||||
import Dialog, {DialogProps} from '@material-ui/core/Dialog';
|
||||
|
||||
|
||||
interface DialogExProps extends DialogProps {
|
||||
tag: string;
|
||||
}
|
||||
|
||||
interface DialogExState {
|
||||
|
||||
}
|
||||
|
||||
class DialogEx extends React.Component<DialogExProps,DialogExState> {
|
||||
constructor(props: DialogExProps)
|
||||
{
|
||||
super(props);
|
||||
this.state = {
|
||||
|
||||
};
|
||||
this.handlePopState = this.handlePopState.bind(this);
|
||||
|
||||
}
|
||||
|
||||
mounted: boolean = false;
|
||||
|
||||
hasHooks: boolean = false;
|
||||
|
||||
stateWasPopped: boolean = false;
|
||||
handlePopState(e: any): any {
|
||||
this.stateWasPopped = true;
|
||||
let shouldClose = (!e.state || !e.state[this.props.tag]);
|
||||
if (shouldClose)
|
||||
{
|
||||
if (this.props.onClose)
|
||||
{
|
||||
this.props.onClose(e,"backdropClick");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateHooks() : void {
|
||||
let wantHooks = this.mounted && this.props.open;
|
||||
if (wantHooks !== this.hasHooks)
|
||||
{
|
||||
this.hasHooks = wantHooks;
|
||||
|
||||
if (this.hasHooks)
|
||||
{
|
||||
this.stateWasPopped = false;
|
||||
window.addEventListener("popstate",this.handlePopState);
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
let state = history.state;
|
||||
if (!state)
|
||||
{
|
||||
state = {};
|
||||
}
|
||||
state[this.props.tag] = true;
|
||||
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
history.pushState(
|
||||
state,
|
||||
"",
|
||||
"#" + this.props.tag
|
||||
);
|
||||
} else {
|
||||
window.removeEventListener("popstate",this.handlePopState);
|
||||
if (!this.stateWasPopped)
|
||||
{
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
history.back();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
onWindowSizeChanged(width: number, height: number): void
|
||||
{
|
||||
this.setState({fullScreen: height < 200})
|
||||
}
|
||||
|
||||
|
||||
componentDidMount()
|
||||
{
|
||||
if (super.componentDidMount)
|
||||
{
|
||||
super.componentDidMount();
|
||||
}
|
||||
this.mounted = true;
|
||||
this.updateHooks();
|
||||
}
|
||||
componentWillUnmount()
|
||||
{
|
||||
if (super.componentWillUnmount)
|
||||
{
|
||||
super.componentWillUnmount();
|
||||
}
|
||||
this.mounted = false;
|
||||
this.updateHooks();
|
||||
}
|
||||
|
||||
componentDidUpdate()
|
||||
{
|
||||
this.updateHooks();
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
let { tag, ...extra} = this.props;
|
||||
return (
|
||||
<Dialog {...extra}>
|
||||
{this.props.children}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default DialogEx;
|
||||
@@ -187,7 +187,7 @@ export const MidiBindingDialog =
|
||||
this.updateHooks();
|
||||
}
|
||||
componentWillUnmount() {
|
||||
super.componentDidMount();
|
||||
super.componentWillUnmount();
|
||||
|
||||
this.mounted = false;
|
||||
this.updateHooks();
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
import React from 'react';
|
||||
import InputAdornment from '@material-ui/core/InputAdornment';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import FormControl from '@material-ui/core/FormControl';
|
||||
import FormHelperText from '@material-ui/core/FormHelperText';
|
||||
import InputLabel from '@material-ui/core/InputLabel';
|
||||
import Input from '@material-ui/core/Input';
|
||||
|
||||
import Visibility from '@material-ui/icons/Visibility';
|
||||
import VisibilityOff from '@material-ui/icons/VisibilityOff';
|
||||
|
||||
|
||||
|
||||
interface NoChangePasswordProps {
|
||||
hasPassword: boolean;
|
||||
label: string;
|
||||
defaultValue: string;
|
||||
onPasswordChange: (text: string) => void;
|
||||
disabled?: boolean;
|
||||
helperText?: string;
|
||||
error?: boolean;
|
||||
};
|
||||
|
||||
interface NoChangePasswordState {
|
||||
focused: boolean;
|
||||
passwordText: string;
|
||||
showPassword: boolean;
|
||||
};
|
||||
|
||||
class NoChangePassword extends React.Component<NoChangePasswordProps, NoChangePasswordState> {
|
||||
refText: React.Ref<HTMLInputElement | HTMLTextAreaElement>;
|
||||
|
||||
constructor(props: NoChangePasswordProps) {
|
||||
super(props);
|
||||
this.refText = React.createRef();
|
||||
this.state = {
|
||||
focused: false,
|
||||
showPassword: false,
|
||||
passwordText: this.props.defaultValue as string
|
||||
};
|
||||
}
|
||||
|
||||
handleShowPassword() {
|
||||
this.setState({
|
||||
showPassword: !this.state.showPassword
|
||||
});
|
||||
}
|
||||
handleChange(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) {
|
||||
this.textChanged = true;
|
||||
this.props.onPasswordChange(e.target.value);
|
||||
}
|
||||
|
||||
textChanged: boolean = false;
|
||||
handleFocus(e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) {
|
||||
console.log("onFocus");
|
||||
if (!this.state.focused) {
|
||||
this.textChanged = false;
|
||||
e.target.value = this.state.passwordText;
|
||||
}
|
||||
e.currentTarget.removeAttribute("readonly");
|
||||
this.setState({ focused: true });
|
||||
}
|
||||
handleBlur(e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) {
|
||||
console.log("onBlur");
|
||||
let text = e.target.value as string;
|
||||
this.setState({ focused: false, passwordText: text });
|
||||
this.props.onPasswordChange(e.target.value);
|
||||
this.textChanged = false;
|
||||
if (text.length === 0 && this.props.hasPassword)
|
||||
{
|
||||
e.target.value = "(Unchanged)";
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
console.log("Render focus: " + this.state.focused)
|
||||
let showUnchanged = (this.state.passwordText.length === 0) && this.props.hasPassword && (!this.state.focused);
|
||||
let thisDefaultValue = showUnchanged ? "(Unchanged)" : this.props.defaultValue;
|
||||
|
||||
let isText = showUnchanged || this.state.showPassword;
|
||||
|
||||
|
||||
return (
|
||||
<FormControl disabled={this.props.disabled} fullWidth error={this.props.error} >
|
||||
<InputLabel htmlFor="standard-adornment-password">{this.props.label}</InputLabel>
|
||||
<Input
|
||||
fullWidth
|
||||
color="primary"
|
||||
spellCheck="false"
|
||||
autoComplete="off"
|
||||
readOnly
|
||||
id="standard-adornment-password"
|
||||
onChange={(e) => this.handleChange(e)}
|
||||
value={thisDefaultValue}
|
||||
onFocus={(e) => this.handleFocus(e)}
|
||||
onBlur={(e) => this.handleBlur(e)}
|
||||
type={isText ? 'text' : 'password'}
|
||||
style={ { color: showUnchanged? '#888': '#000'} }
|
||||
|
||||
endAdornment={
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
aria-label="toggle password visibility"
|
||||
onClick={(e) => this.handleShowPassword()}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
>
|
||||
{this.state.showPassword ? <Visibility /> : <VisibilityOff />}
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
}
|
||||
/>
|
||||
<FormHelperText >{ this.props.helperText}</FormHelperText>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default NoChangePassword;
|
||||
@@ -117,7 +117,7 @@ const pedalBoardStyles = (theme: Theme) => createStyles({
|
||||
marginBottom: (CELL_HEIGHT - FRAME_SIZE) / 2,
|
||||
width: FRAME_SIZE,
|
||||
height: FRAME_SIZE,
|
||||
border: "1pt black solid",
|
||||
border: "1pt #666 solid",
|
||||
borderRadius: 6
|
||||
},
|
||||
pedalIcon: {
|
||||
|
||||
+108
-6
@@ -13,6 +13,8 @@ import JackHostStatus from './JackHostStatus';
|
||||
import JackServerSettings from './JackServerSettings';
|
||||
import MidiBinding from './MidiBinding';
|
||||
import PluginPreset from './PluginPreset';
|
||||
import WifiConfigSettings from './WifiConfigSettings';
|
||||
import WifiChannel from './WifiChannel';
|
||||
|
||||
|
||||
export enum State {
|
||||
@@ -235,6 +237,7 @@ interface ControlChangedBody {
|
||||
|
||||
export interface PiPedalModel {
|
||||
clientId: number;
|
||||
countryCodes: Object;
|
||||
serverVersion?: PiPedalVersion;
|
||||
errorMessage: ObservableProperty<string>;
|
||||
alertMessage: ObservableProperty<string>;
|
||||
@@ -245,6 +248,7 @@ export interface PiPedalModel {
|
||||
jackSettings: ObservableProperty<JackChannelSelection>;
|
||||
banks: ObservableProperty<BankIndex>;
|
||||
jackServerSettings: ObservableProperty<JackServerSettings>;
|
||||
wifiConfigSettings: ObservableProperty<WifiConfigSettings>;
|
||||
|
||||
presets: ObservableProperty<PresetIndex>;
|
||||
|
||||
@@ -320,13 +324,17 @@ export interface PiPedalModel {
|
||||
|
||||
uploadPreset(file: File, uploadAfter: number): Promise<number>;
|
||||
|
||||
setWifiConfigSettings(wifiConfigSettings: WifiConfigSettings): Promise<void>;
|
||||
|
||||
getWifiChannels(countryIso3661: string): Promise<WifiChannel[]>;
|
||||
|
||||
};
|
||||
|
||||
class PiPedalModelImpl implements PiPedalModel {
|
||||
clientId: number = -1;
|
||||
|
||||
serverVersion?: PiPedalVersion;
|
||||
|
||||
countryCodes: Object = {};
|
||||
socketServerUrl: string = "";
|
||||
varServerUrl: string = "";
|
||||
lv2Path: string = "";
|
||||
@@ -346,6 +354,8 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
jackServerSettings: ObservableProperty<JackServerSettings>
|
||||
= new ObservableProperty<JackServerSettings>(new JackServerSettings());
|
||||
|
||||
wifiConfigSettings: ObservableProperty<WifiConfigSettings> = new ObservableProperty<WifiConfigSettings>(new WifiConfigSettings());
|
||||
|
||||
|
||||
presets: ObservableProperty<PresetIndex> = new ObservableProperty<PresetIndex>
|
||||
(
|
||||
@@ -459,6 +469,9 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
} else if (message === "onJackServerSettingsChanged") {
|
||||
let jackServerSettings = new JackServerSettings().deserialize(body);
|
||||
this.jackServerSettings.set(jackServerSettings);
|
||||
} else if (message === "onWifiConfigSettingsChanged") {
|
||||
let wifiConfigSettings = new WifiConfigSettings().deserialize(body);
|
||||
this.wifiConfigSettings.set(wifiConfigSettings);
|
||||
} else if (message === "onBanksChanged") {
|
||||
let banks = new BankIndex().deserialize(body);
|
||||
this.banks.set(banks);
|
||||
@@ -537,6 +550,11 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
.then(data => {
|
||||
this.pedalBoard.set(new PedalBoard().deserialize(data));
|
||||
|
||||
return this.getWebSocket().request<any>("getWifiConfigSettings");
|
||||
})
|
||||
.then(data => {
|
||||
this.wifiConfigSettings.set(new WifiConfigSettings().deserialize(data));
|
||||
|
||||
return this.getWebSocket().request<any>("getJackServerSettings");
|
||||
})
|
||||
.then(data => {
|
||||
@@ -573,7 +591,7 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
})
|
||||
}
|
||||
makeSocketServerUrl(hostName: string, port: number): string {
|
||||
return "ws://" + hostName + ":" + port + "/piddle";
|
||||
return "ws://" + hostName + ":" + port + "/pipedal";
|
||||
|
||||
}
|
||||
makeVarServerUrl(protocol: string, hostName: string, port: number): string {
|
||||
@@ -587,11 +605,12 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
const myRequest = new Request(this.varRequest('config.json'));
|
||||
return fetch(myRequest)
|
||||
.then(
|
||||
response => response.json()
|
||||
|
||||
(response) =>
|
||||
{
|
||||
return response.json();
|
||||
}
|
||||
)
|
||||
.then(data => {
|
||||
console.log("Got config");
|
||||
if (data.max_upload_size)
|
||||
{
|
||||
this.maxUploadSize = data.max_upload_size;
|
||||
@@ -617,6 +636,15 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
return this.webSocket.connect();
|
||||
})
|
||||
.then(() => {
|
||||
const isoRequest = new Request('iso_codes.json');
|
||||
return fetch(isoRequest);
|
||||
})
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((countryCodes) => {
|
||||
this.countryCodes = countryCodes as Object;
|
||||
|
||||
return this.getWebSocket().request<number>("hello");
|
||||
})
|
||||
.then((clientId) => {
|
||||
@@ -652,7 +680,12 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
})
|
||||
.then((data) => {
|
||||
this.presets.set(new PresetIndex().deserialize(data));
|
||||
|
||||
return this.getWebSocket().request<any>("getWifiConfigSettings");
|
||||
})
|
||||
.then(data => {
|
||||
this.wifiConfigSettings.set(new WifiConfigSettings().deserialize(data));
|
||||
|
||||
|
||||
return this.getWebSocket().request<any>("getJackServerSettings");
|
||||
})
|
||||
.then(data => {
|
||||
@@ -1427,6 +1460,75 @@ class PiPedalModelImpl implements PiPedalModel {
|
||||
});
|
||||
return result;
|
||||
}
|
||||
setWifiConfigSettings(wifiConfigSettings: WifiConfigSettings): Promise<void>
|
||||
{
|
||||
let result = new Promise<void>((resolve,reject) =>{
|
||||
let oldSettings = this.wifiConfigSettings.get();
|
||||
wifiConfigSettings = wifiConfigSettings.clone();
|
||||
|
||||
if ((!oldSettings.enable) && (!wifiConfigSettings.enable))
|
||||
{
|
||||
// no effective change.
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
let countryCodeChanged = wifiConfigSettings.countryCode !== oldSettings.countryCode;
|
||||
if (!wifiConfigSettings.enable)
|
||||
{
|
||||
wifiConfigSettings.hasPassword = false;
|
||||
wifiConfigSettings.hotspotName = oldSettings.hotspotName;
|
||||
} else {
|
||||
if (!wifiConfigSettings.hasPassword) {
|
||||
if (wifiConfigSettings.hotspotName === oldSettings.hotspotName && !countryCodeChanged) {
|
||||
// no effective change.
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// save a version for the server (potentially carrying a password)
|
||||
let serverConfigSettings = wifiConfigSettings.clone();
|
||||
|
||||
|
||||
|
||||
// notify the server.
|
||||
let ws = this.webSocket;
|
||||
if (!ws)
|
||||
{
|
||||
reject("Not connected.");
|
||||
return;
|
||||
}
|
||||
ws.request<void>(
|
||||
"setWifiConfigSettings",
|
||||
serverConfigSettings
|
||||
)
|
||||
.then(()=> {
|
||||
resolve();
|
||||
})
|
||||
.catch((err) =>{
|
||||
reject(err);
|
||||
});
|
||||
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
getWifiChannels(countryIso3661: string): Promise<WifiChannel[]>
|
||||
{
|
||||
let result = new Promise<WifiChannel[]>((resolve,reject) => {
|
||||
if (!this.webSocket) {
|
||||
reject("Connection closed.");
|
||||
return;
|
||||
}
|
||||
this.webSocket.request<WifiChannel[]>("getWifiChannels",countryIso3661)
|
||||
.then((data) => {
|
||||
resolve(WifiChannel.deserialize_array(data));
|
||||
})
|
||||
.catch((err)=> reject(err));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -100,12 +100,12 @@ const styles = (theme: Theme) => createStyles({
|
||||
marginRight: 8,
|
||||
marginBottom: 12,
|
||||
position: "relative",
|
||||
paddingLeft: 0,
|
||||
paddingLeft: 3,
|
||||
paddingRight: 0,
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0,
|
||||
border: "2pt #AAA solid",
|
||||
borderRadius: 10,
|
||||
borderRadius: 4,
|
||||
elevation: 12,
|
||||
display: "flex",
|
||||
flexDirection: "row", flexWrap: "wrap",
|
||||
|
||||
@@ -95,7 +95,7 @@ export default class RenameDialog extends ResizeResponsiveComponent<RenameDialog
|
||||
}
|
||||
componentWillUnmount()
|
||||
{
|
||||
super.componentDidMount();
|
||||
super.componentWillUnmount();
|
||||
this.mounted = false;
|
||||
this.updateHooks();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||
import ButtonBase from "@material-ui/core/ButtonBase";
|
||||
import { TransitionProps } from '@material-ui/core/transitions/transition';
|
||||
import Slide from '@material-ui/core/Slide';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import AppBar from '@material-ui/core/AppBar';
|
||||
import Toolbar from '@material-ui/core/Toolbar';
|
||||
import { Theme, withStyles, WithStyles, createStyles } from '@material-ui/core/styles';
|
||||
@@ -18,6 +17,9 @@ import SelectHoverBackground from './SelectHoverBackground';
|
||||
import JackServerSettings from './JackServerSettings';
|
||||
import JackServerSettingsDialog from './JackServerSettingsDialog';
|
||||
import JackHostStatus from './JackHostStatus';
|
||||
import WifiConfigSettings from './WifiConfigSettings';
|
||||
import WifiConfigDialog from './WifiConfigDialog';
|
||||
import DialogEx from './DialogEx'
|
||||
|
||||
|
||||
|
||||
@@ -34,6 +36,9 @@ interface SettingsDialogState {
|
||||
jackServerSettings: JackServerSettings;
|
||||
jackStatus?: JackHostStatus;
|
||||
|
||||
wifiConfigSettings: WifiConfigSettings;
|
||||
|
||||
showWifiConfigDialog: boolean;
|
||||
showInputSelectDialog: boolean;
|
||||
showOutputSelectDialog: boolean;
|
||||
showMidiSelectDialog: boolean;
|
||||
@@ -120,6 +125,8 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
|
||||
jackConfiguration: this.model.jackConfiguration.get(),
|
||||
jackStatus: undefined,
|
||||
jackSettings: this.model.jackSettings.get(),
|
||||
wifiConfigSettings: this.model.wifiConfigSettings.get(),
|
||||
showWifiConfigDialog: false,
|
||||
showInputSelectDialog: false,
|
||||
showOutputSelectDialog: false,
|
||||
showMidiSelectDialog: false,
|
||||
@@ -132,10 +139,28 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
|
||||
this.handleJackConfigurationChanged = this.handleJackConfigurationChanged.bind(this);
|
||||
this.handleJackSettingsChanged = this.handleJackSettingsChanged.bind(this);
|
||||
this.handleJackServerSettingsChanged = this.handleJackServerSettingsChanged.bind(this);
|
||||
this.handleWifiConfigSettingsChanged = this.handleWifiConfigSettingsChanged.bind(this);
|
||||
|
||||
}
|
||||
|
||||
handleApplyWifiConfig(wifiConfigSettings: WifiConfigSettings): void {
|
||||
this.setState({showWifiConfigDialog: false});
|
||||
this.model.setWifiConfigSettings(wifiConfigSettings)
|
||||
.then(() => {
|
||||
|
||||
})
|
||||
.catch((err) => {
|
||||
this.model.showAlert(err);
|
||||
});
|
||||
}
|
||||
|
||||
handleWifiConfigSettingsChanged(): void {
|
||||
this.setState(
|
||||
{
|
||||
wifiConfigSettings: this.model.wifiConfigSettings.get()
|
||||
}
|
||||
)
|
||||
}
|
||||
handleJackSettingsChanged(): void {
|
||||
this.setState({
|
||||
jackSettings: this.model.jackSettings.get()
|
||||
@@ -177,6 +202,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
|
||||
this.model.jackSettings.addOnChangedHandler(this.handleJackSettingsChanged);
|
||||
this.model.jackConfiguration.addOnChangedHandler(this.handleJackConfigurationChanged);
|
||||
this.model.jackServerSettings.addOnChangedHandler(this.handleJackServerSettingsChanged);
|
||||
this.model.wifiConfigSettings.addOnChangedHandler(this.handleWifiConfigSettingsChanged);
|
||||
this.model.getJackStatus()
|
||||
.then((jackStatus) =>
|
||||
this.setState(
|
||||
@@ -193,7 +219,8 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
|
||||
this.handleJackConfigurationChanged();
|
||||
this.handleJackSettingsChanged();
|
||||
this.handleJackServerSettingsChanged();
|
||||
this.timerHandle = setInterval(() => this.tick(), 1000);
|
||||
this.handleWifiConfigSettingsChanged();
|
||||
// xxx UNCOMMENT ME! this.timerHandle = setInterval(() => this.tick(), 1000);
|
||||
} else {
|
||||
if (this.timerHandle) {
|
||||
clearInterval(this.timerHandle);
|
||||
@@ -201,6 +228,8 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
|
||||
this.model.jackConfiguration.removeOnChangedHandler(this.handleJackConfigurationChanged);
|
||||
this.model.jackSettings.removeOnChangedHandler(this.handleJackSettingsChanged);
|
||||
this.model.jackServerSettings.removeOnChangedHandler(this.handleJackServerSettingsChanged);
|
||||
this.model.wifiConfigSettings.removeOnChangedHandler(this.handleWifiConfigSettingsChanged);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,25 +321,33 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
|
||||
if (ports.length === 1) return ports[0];
|
||||
return ports.length + " channels";
|
||||
}
|
||||
|
||||
handleShowWifiConfigDialog() {
|
||||
this.setState({
|
||||
showWifiConfigDialog: true
|
||||
});
|
||||
}
|
||||
handleRestart() {
|
||||
this.setState({ restarting: true });
|
||||
this.model.restart()
|
||||
.then(() => {
|
||||
this.setState({ restarting: true });
|
||||
// this.setState({ restarting: true });
|
||||
})
|
||||
.catch((error) => {
|
||||
this.model.showAlert(error);
|
||||
this.setState({ restarting: false });
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
handleShutdown() {
|
||||
this.setState({ shuttingDown: true });
|
||||
this.model.shutdown()
|
||||
.then(() => {
|
||||
this.setState({ shuttingDown: true });
|
||||
})
|
||||
.catch((error) => {
|
||||
this.model.showAlert(error);
|
||||
this.setState({ shuttingDown: false });
|
||||
});
|
||||
|
||||
}
|
||||
@@ -322,7 +359,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
|
||||
let disableShutdown = this.state.shuttingDown || this.state.restarting;
|
||||
|
||||
return (
|
||||
<Dialog fullScreen open={this.props.open}
|
||||
<DialogEx tag="SettingsDialog" fullScreen open={this.props.open}
|
||||
onClose={() => { this.props.onClose() }} TransitionComponent={Transition}>
|
||||
|
||||
<div style={{ display: "flex", flexDirection: "column", flexWrap: "nowrap", width: "100%", height: "100%", overflow: "hidden" }}>
|
||||
@@ -428,6 +465,18 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
|
||||
<Divider />
|
||||
<div >
|
||||
<Typography className={classes.sectionHead} display="block" variant="caption" color="secondary">SYSTEM</Typography>
|
||||
<ButtonBase className={classes.setting} disabled={!this.state.wifiConfigSettings.valid}
|
||||
onClick={() => this.handleShowWifiConfigDialog()} >
|
||||
<SelectHoverBackground selected={false} showHover={true} />
|
||||
<div style={{ width: "100%" }}>
|
||||
<Typography className={classes.primaryItem} display="block" variant="body2" color="textPrimary" noWrap>
|
||||
Configure Wi-fi Hotspot</Typography>
|
||||
<Typography display="block" variant="caption" noWrap color="textSecondary">
|
||||
{this.state.wifiConfigSettings.getSummaryText()}
|
||||
</Typography>
|
||||
|
||||
</div>
|
||||
</ButtonBase>
|
||||
<ButtonBase className={classes.setting} disabled={!isConfigValid || disableShutdown}
|
||||
onClick={() => this.handleRestart()} >
|
||||
<SelectHoverBackground selected={false} showHover={true} />
|
||||
@@ -452,7 +501,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
|
||||
) : (
|
||||
<Typography className={classes.primaryItem} display="block" variant="body2" noWrap>Shut down</Typography>
|
||||
)
|
||||
}
|
||||
}
|
||||
</div >
|
||||
</ButtonBase>
|
||||
</div>
|
||||
@@ -483,8 +532,12 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
|
||||
|
||||
)
|
||||
}
|
||||
<WifiConfigDialog wifiConfigSettings={this.state.wifiConfigSettings} open={this.state.showWifiConfigDialog}
|
||||
onClose={()=> this.setState({showWifiConfigDialog: false})}
|
||||
onOk={(wifiConfigSettings) => this.handleApplyWifiConfig(wifiConfigSettings)}
|
||||
/>
|
||||
|
||||
</Dialog >
|
||||
</DialogEx >
|
||||
|
||||
);
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ export default class UploadDialog extends ResizeResponsiveComponent<UploadDialog
|
||||
this.updateHooks();
|
||||
}
|
||||
componentWillUnmount() {
|
||||
super.componentDidMount();
|
||||
super.componentWillUnmount();
|
||||
this.mounted = false;
|
||||
this.updateHooks();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
|
||||
export default class WifiChannel {
|
||||
deserialize(input: any): WifiChannel {
|
||||
this.channelId = input.channelId;
|
||||
this.channelName = input.channelName;
|
||||
return this;
|
||||
}
|
||||
static deserialize_array(input: any): WifiChannel[]
|
||||
{
|
||||
let result: WifiChannel[] = [];
|
||||
for (let i = 0; i < input.length; ++i)
|
||||
{
|
||||
result.push(new WifiChannel().deserialize(input[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
channelId: string = "";
|
||||
channelName: string = "";
|
||||
};
|
||||
@@ -0,0 +1,374 @@
|
||||
import React from 'react';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import Switch from '@material-ui/core/Switch';
|
||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
import WifiConfigSettings from './WifiConfigSettings';
|
||||
import NoChangePassword from './NoChangePassword';
|
||||
import DialogEx from './DialogEx'
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import { Theme, withStyles, WithStyles,createStyles } from '@material-ui/core/styles';
|
||||
import Select from '@material-ui/core/Select';
|
||||
import FormControl from '@material-ui/core/FormControl';
|
||||
import InputLabel from '@material-ui/core/InputLabel';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import WifiChannel from './WifiChannel';
|
||||
import {PiPedalModel, PiPedalModelFactory} from './PiPedalModel';
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
pgraph: {
|
||||
paddingBottom: 16
|
||||
}
|
||||
});
|
||||
|
||||
const NBSP = "\u00A0";
|
||||
|
||||
export interface WifiConfigProps extends WithStyles<typeof styles> {
|
||||
open: boolean,
|
||||
wifiConfigSettings: WifiConfigSettings,
|
||||
onOk: (wifiConfigSettings: WifiConfigSettings) => void,
|
||||
onClose: () => void
|
||||
};
|
||||
|
||||
export interface WifiConfigState {
|
||||
fullScreen: boolean;
|
||||
showWifiWarningDialog: boolean;
|
||||
wifiWarningGiven: boolean;
|
||||
enabled: boolean;
|
||||
name: string;
|
||||
newPassword: string;
|
||||
nameError: boolean;
|
||||
nameErrorMessage: string;
|
||||
passwordError: boolean;
|
||||
passwordErrorMessage: string;
|
||||
countryCode: string;
|
||||
channel: string;
|
||||
wifiChannels: WifiChannel[];
|
||||
};
|
||||
|
||||
|
||||
let validNameRegex = /^[a-zA-Z][a-zA-Z0-9_-]*$/
|
||||
function isValidName(text: string)
|
||||
{
|
||||
return validNameRegex.test(text);
|
||||
}
|
||||
|
||||
const WifiConfigDialog = withStyles(styles, { withTheme: true })(
|
||||
class extends ResizeResponsiveComponent<WifiConfigProps, WifiConfigState> {
|
||||
|
||||
refName: React.RefObject<HTMLInputElement>;
|
||||
refPassword: React.RefObject<HTMLInputElement>;
|
||||
model: PiPedalModel;
|
||||
|
||||
constructor(props: WifiConfigProps) {
|
||||
super(props);
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
this.state = {
|
||||
fullScreen: false,
|
||||
enabled: this.props.wifiConfigSettings.enable,
|
||||
name: this.props.wifiConfigSettings.hotspotName,
|
||||
newPassword: "",
|
||||
nameError: false,
|
||||
nameErrorMessage: NBSP,
|
||||
passwordError: false,
|
||||
showWifiWarningDialog: false,
|
||||
wifiWarningGiven: this.props.wifiConfigSettings.wifiWarningGiven,
|
||||
passwordErrorMessage: NBSP,
|
||||
countryCode: this.props.wifiConfigSettings.countryCode,
|
||||
channel: this.props.wifiConfigSettings.channel,
|
||||
wifiChannels: []
|
||||
};
|
||||
this.model.getWifiChannels(this.props.wifiConfigSettings.countryCode)
|
||||
.then((wifiChannels_) => {
|
||||
this.setState({wifiChannels: wifiChannels_});
|
||||
}).catch((err)=> {});
|
||||
|
||||
this.refName = React.createRef<HTMLInputElement>();
|
||||
this.refPassword = React.createRef<HTMLInputElement>();
|
||||
this.handlePopState = this.handlePopState.bind(this);
|
||||
}
|
||||
mounted: boolean = false;
|
||||
|
||||
hasHooks: boolean = false;
|
||||
|
||||
stateWasPopped: boolean = false;
|
||||
|
||||
handleEnableChanged(e: any) {
|
||||
this.setState({ enabled: e.target.checked });
|
||||
}
|
||||
handlePopState(e: any): any {
|
||||
this.stateWasPopped = true;
|
||||
let shouldClose = (!e.state || !e.state.WifiConfig);
|
||||
if (shouldClose) {
|
||||
this.props.onClose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
updateHooks(): void {
|
||||
let wantHooks = this.mounted && this.props.open;
|
||||
if (wantHooks !== this.hasHooks) {
|
||||
this.hasHooks = wantHooks;
|
||||
|
||||
if (this.hasHooks) {
|
||||
this.stateWasPopped = false;
|
||||
window.addEventListener("popstate", this.handlePopState);
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
let state = history.state;
|
||||
if (!state) {
|
||||
state = {};
|
||||
}
|
||||
state.WifiConfig = true;
|
||||
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
history.pushState(
|
||||
state,
|
||||
"Wi-Fi Configuration",
|
||||
"#WifiConfig"
|
||||
);
|
||||
} else {
|
||||
window.removeEventListener("popstate", this.handlePopState);
|
||||
if (!this.stateWasPopped) {
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
history.back();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
onWindowSizeChanged(width: number, height: number): void {
|
||||
this.setState({ fullScreen: height < 200 })
|
||||
}
|
||||
|
||||
|
||||
componentDidMount() {
|
||||
super.componentDidMount();
|
||||
this.mounted = true;
|
||||
this.updateHooks();
|
||||
}
|
||||
componentWillUnmount() {
|
||||
super.componentWillUnmount();
|
||||
this.mounted = false;
|
||||
this.updateHooks();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: WifiConfigProps) {
|
||||
this.updateHooks();
|
||||
|
||||
if (this.props.open && !prevProps.open)
|
||||
{
|
||||
this.setState({
|
||||
enabled: this.props.wifiConfigSettings.enable,
|
||||
name: this.props.wifiConfigSettings.hotspotName,
|
||||
newPassword: "",
|
||||
wifiWarningGiven: this.props.wifiConfigSettings.wifiWarningGiven,
|
||||
countryCode: this.props.wifiConfigSettings.countryCode,
|
||||
channel: this.props.wifiConfigSettings.channel
|
||||
});
|
||||
|
||||
if (this.props.wifiConfigSettings.countryCode !== this.state.countryCode)
|
||||
{
|
||||
this.model.getWifiChannels(this.props.wifiConfigSettings.countryCode)
|
||||
.then((wifiChannels) => {
|
||||
this.setState({wifiChannels: wifiChannels});
|
||||
})
|
||||
.catch((error) => {});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
handleOk(wifiWarningGiven: boolean) {
|
||||
let hasError = false;
|
||||
if (this.state.enabled)
|
||||
{
|
||||
let name = this.state.name;
|
||||
if (name.length === 0) {
|
||||
this.setState({nameError: true, nameErrorMessage: "* Required"});
|
||||
hasError = true;
|
||||
} else {
|
||||
if (!isValidName(name))
|
||||
{
|
||||
this.setState({nameError: true, nameErrorMessage: "Invalid name"});
|
||||
hasError = true;
|
||||
} else if (name.length > 15)
|
||||
{
|
||||
this.setState({nameError: true, nameErrorMessage: "> 15 characters"});
|
||||
hasError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.state.enabled && (!this.props.wifiConfigSettings.hasPassword) && this.state.newPassword.length === 0)
|
||||
{
|
||||
this.setState({passwordError: true, passwordErrorMessage: "* Required"});
|
||||
hasError = true;
|
||||
}
|
||||
if (!hasError)
|
||||
{
|
||||
let wifiConfigSettings = new WifiConfigSettings();
|
||||
wifiConfigSettings.valid = true;
|
||||
|
||||
wifiConfigSettings.enable = this.state.enabled;
|
||||
wifiConfigSettings.hotspotName = this.state.name;
|
||||
if (this.state.newPassword.length === 0 || !this.state.enabled)
|
||||
{
|
||||
wifiConfigSettings.hasPassword = false;
|
||||
} else {
|
||||
wifiConfigSettings.hasPassword = true;
|
||||
wifiConfigSettings.password = this.state.newPassword;
|
||||
}
|
||||
|
||||
if (!this.props.wifiConfigSettings.wifiWarningGiven && !wifiWarningGiven) {
|
||||
this.setState({showWifiWarningDialog: true});
|
||||
} else {
|
||||
wifiConfigSettings.wifiWarningGiven = true;
|
||||
this.props.onOk(wifiConfigSettings);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
handleChannelChange(e: any)
|
||||
{
|
||||
let value = e.target.value as string;
|
||||
this.setState({channel: value });
|
||||
}
|
||||
handleCountryChanged(e: any)
|
||||
{
|
||||
let value = e.target.value as string;
|
||||
|
||||
this.setState({countryCode: value});
|
||||
this.model.getWifiChannels(value)
|
||||
.then(wifiChannels => {
|
||||
this.setState({wifiChannels: wifiChannels});
|
||||
})
|
||||
.catch((error)=> { });
|
||||
}
|
||||
|
||||
render() {
|
||||
let props = this.props;
|
||||
let classes = this.props.classes;
|
||||
let { open, onClose} = props;
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} fullWidth onClose={handleClose} style={{}}
|
||||
fullScreen={this.state.fullScreen}
|
||||
>
|
||||
{ this.state.fullScreen && (
|
||||
<DialogTitle>Wi-fi Hotspot</DialogTitle>
|
||||
)}
|
||||
<DialogContent>
|
||||
|
||||
<FormControlLabel
|
||||
control={(
|
||||
<Switch
|
||||
checked={this.state.enabled}
|
||||
onChange={(e: any) => this.handleEnableChanged(e)}
|
||||
color="secondary"
|
||||
/>
|
||||
)}
|
||||
label="Enable"
|
||||
/>
|
||||
<TextField style={{ marginBottom: 16, marginTop: 16 }}
|
||||
|
||||
autoComplete="off"
|
||||
id="name"
|
||||
spellCheck="false"
|
||||
label="SSID"
|
||||
type="text"
|
||||
fullWidth
|
||||
error={this.state.nameError}
|
||||
helperText={this.state.nameErrorMessage}
|
||||
value={this.state.name}
|
||||
onChange={(e) => this.setState({name: e.target.value, nameError: false, nameErrorMessage: NBSP})}
|
||||
inputRef={this.refName}
|
||||
disabled={!this.state.enabled}
|
||||
/>
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<NoChangePassword
|
||||
onPasswordChange={(text): void => { this.setState({ newPassword: text, passwordError: false, passwordErrorMessage: NBSP }); }}
|
||||
hasPassword={this.props.wifiConfigSettings.hasPassword}
|
||||
label="WEP Passphrase"
|
||||
error={this.state.passwordError}
|
||||
helperText={this.state.passwordErrorMessage}
|
||||
defaultValue={this.state.newPassword}
|
||||
disabled={!this.state.enabled}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ marginBottom: 16}}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="countryCodeSelect">Country</InputLabel>
|
||||
<Select id="countryCodeSelect" value={this.state.countryCode} style={{width: 220}}
|
||||
onChange={(event)=>this.handleCountryChanged(event)} >
|
||||
{Object.entries(this.model.countryCodes).map(([key,value])=> {
|
||||
return (
|
||||
<MenuItem value={key}>{value}</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
</div>
|
||||
<div style={{ marginBottom: 24}}>
|
||||
<FormControl>
|
||||
<InputLabel htmlFor="channelSelect">Channel</InputLabel>
|
||||
<Select id="channelSelect" value={this.state.channel} style={{width: 220}} onChange={(e)=>{
|
||||
this.handleChannelChange(e);
|
||||
}}>
|
||||
{this.state.wifiChannels.map((channel)=> {
|
||||
return (
|
||||
<MenuItem value={channel.channelId}>{channel.channelName}</MenuItem>
|
||||
)
|
||||
})}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
</div>
|
||||
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose} color="primary" style={{ width: 120 }}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={()=> this.handleOk(false)} color="secondary" style={{ width: 120 }} >
|
||||
OK
|
||||
</Button>
|
||||
</DialogActions>
|
||||
<DialogEx open={this.state.showWifiWarningDialog} tag="wifiConfirm" >
|
||||
<DialogContent>
|
||||
<Typography className={classes.pgraph} variant="body2" color="textPrimary">
|
||||
Enabling the Wi-Fi hotspot will disable regular Wi-Fi network access on the PiPedal device. Once
|
||||
enabled, connect to the hotspot and launch http://172.24.1.1 or http://{this.state.name}.local to access the PiPedal web app again.
|
||||
</Typography>
|
||||
<Typography className={classes.pgraph} variant="body2" color="textPrimary" gutterBottom>
|
||||
Are you sure you want to proceed?
|
||||
</Typography>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={()=> this.setState({showWifiWarningDialog: false})} color="primary" style={{ width: 120 }}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={()=> {
|
||||
this.setState({showWifiWarningDialog: false});
|
||||
this.handleOk(true);
|
||||
}} color="secondary" style={{ width: 120 }} >
|
||||
PROCEED
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</DialogEx>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default WifiConfigDialog;
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
|
||||
export default class WifiConfigSettings {
|
||||
deserialize(input: any) : WifiConfigSettings{
|
||||
this.valid = input.valid;
|
||||
this.wifiWarningGiven = input.wifiWarningGiven;
|
||||
this.rebootRequired = input.rebootRequired;
|
||||
this.enable = input.enable;
|
||||
this.hotspotName = input.hotspotName;
|
||||
this.hasPassword = input.hasPassword;
|
||||
this.password = input.password;
|
||||
this.countryCode = input.countryCode;
|
||||
this.channel = input.channel;
|
||||
return this;
|
||||
}
|
||||
clone() : WifiConfigSettings {
|
||||
return this.deserialize(this);
|
||||
}
|
||||
valid: boolean = true;
|
||||
wifiWarningGiven: boolean = false;
|
||||
enable: boolean = true;
|
||||
hasPassword: boolean = false;
|
||||
rebootRequired = false;
|
||||
hotspotName: string = "pipedal";
|
||||
password: string = "";
|
||||
countryCode: string = "US";
|
||||
channel: string = "g6";
|
||||
|
||||
getSummaryText() {
|
||||
let result: string;
|
||||
if (!this.valid) {
|
||||
result = "Not available.";
|
||||
} else if (!this.enable) {
|
||||
result = "Disabled.";
|
||||
} else {
|
||||
result = this.hotspotName;
|
||||
}
|
||||
if (this.rebootRequired)
|
||||
{
|
||||
result += " (Restart required)";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
import { Theme, withStyles, WithStyles,createStyles } from '@material-ui/core/styles';
|
||||
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
});
|
||||
|
||||
|
||||
interface XxxProps extends WithStyles<typeof styles> {
|
||||
theme: Theme,
|
||||
};
|
||||
|
||||
interface XxxState {
|
||||
|
||||
};
|
||||
|
||||
const Xxx = withStyles(styles, { withTheme: true })(
|
||||
|
||||
class extends React.Component<XxxProps, XxxState> {
|
||||
constructor(props: XxxProps)
|
||||
{
|
||||
super(props);
|
||||
this.state = {
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div/>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default Xxx;
|
||||
Reference in New Issue
Block a user