Skip to content

Commit

Permalink
settings: add new hubName setting
Browse files Browse the repository at this point in the history
This setting will be used to customize the hub name when flashing
firmware. This commit just implements the setting.

Some changes needed to be made to the existing settings infrastructure
since previously it only allowed boolean settings.
  • Loading branch information
dlech committed Nov 20, 2021
1 parent ad6566b commit 86c0e93
Show file tree
Hide file tree
Showing 13 changed files with 423 additions and 66 deletions.
4 changes: 2 additions & 2 deletions src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import SplitterLayout from 'react-splitter-layout';
import Editor from '../editor/Editor';
import { RootState } from '../reducers';
import { toggleBoolean } from '../settings/actions';
import { SettingId } from '../settings/defaults';
import { BooleanSettingId } from '../settings/defaults';
import StatusBar from '../status-bar/StatusBar';
import Terminal from '../terminal/Terminal';
import Toolbar from '../toolbar/Toolbar';
Expand Down Expand Up @@ -142,7 +142,7 @@ function App(): JSX.Element {
e.key == 'd'
) {
e.preventDefault();
dispatch(toggleBoolean(SettingId.ShowDocs));
dispatch(toggleBoolean(BooleanSettingId.ShowDocs));
}
});

Expand Down
4 changes: 2 additions & 2 deletions src/editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { IDisposable } from 'xterm';
import { compile } from '../mpy/actions';
import { RootState } from '../reducers';
import { toggleBoolean } from '../settings/actions';
import { SettingId } from '../settings/defaults';
import { BooleanSettingId } from '../settings/defaults';
import { IContextMenuTarget, handleContextMenu } from '../utils/IContextMenuTarget';
import { isMacOS } from '../utils/os';
import { setEditSession, storageChanged } from './actions';
Expand Down Expand Up @@ -254,7 +254,7 @@ const mapDispatchToProps: DispatchProps = {
// REVISIT: the options here might need to be changed - hopefully there is
// one setting that works for all hub types for cases where we aren't connected.
onCheck: (script) => compile(script, []),
onToggleDocs: () => toggleBoolean(SettingId.ShowDocs),
onToggleDocs: () => toggleBoolean(BooleanSettingId.ShowDocs),
};

export default connect(
Expand Down
4 changes: 4 additions & 0 deletions src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,7 @@ a.#{$ns}-button {
::-webkit-scrollbar-thumb:hover {
background: $pt-icon-color-hover;
}

.#{$ns}-input-group .#{$ns}-icon {
margin: 7px;
}
62 changes: 56 additions & 6 deletions src/settings/SettingsDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ import {
Button,
ButtonGroup,
Classes,
ControlGroup,
Drawer,
FormGroup,
Hotkey,
Hotkeys,
HotkeysTarget,
Icon,
InputGroup,
Intent,
Label,
Position,
Switch,
Tooltip,
Expand All @@ -32,10 +37,11 @@ import { RootState } from '../reducers';
import ExternalLinkIcon from '../utils/ExternalLinkIcon';
import { BeforeInstallPromptEvent } from '../utils/dom';
import { isMacOS } from '../utils/os';
import { setBoolean, toggleBoolean } from './actions';
import { SettingId } from './defaults';
import { setBoolean, setString, toggleBoolean } from './actions';
import { BooleanSettingId, StringSettingId } from './defaults';
import { SettingsStringId } from './i18n';
import en from './i18n.en.json';
import './settings.scss';

type StateProps = {
showDocs: boolean;
Expand All @@ -47,6 +53,8 @@ type StateProps = {
beforeInstallPrompt: BeforeInstallPromptEvent | null;
promptingInstall: boolean;
readyForOfflineUse: boolean;
hubName: string;
isHubNameValid: boolean;
};

type DispatchProps = {
Expand All @@ -57,6 +65,7 @@ type DispatchProps = {
onCheckForUpdate: (registration: ServiceWorkerRegistration) => void;
onReload: (registration: ServiceWorkerRegistration) => void;
onInstallPrompt: (event: BeforeInstallPromptEvent) => void;
onHubNameChange: React.FormEventHandler<HTMLInputElement>;
};

type OwnProps = {
Expand Down Expand Up @@ -92,6 +101,9 @@ class SettingsDrawer extends React.PureComponent<SettingsProps> {
isOpen,
onClose,
i18n,
hubName,
isHubNameValid,
onHubNameChange,
} = this.props;
return (
<Drawer
Expand Down Expand Up @@ -180,6 +192,40 @@ class SettingsDrawer extends React.PureComponent<SettingsProps> {
}
/>
</Tooltip>
<ControlGroup>
<Label className={Classes.INLINE}>
{i18n.translate(
SettingsStringId.FirmwareHubNameLabel,
)}
<InputGroup
value={hubName}
onChange={onHubNameChange}
className="pb-hub-name-input"
intent={
isHubNameValid ? Intent.NONE : Intent.DANGER
}
placeholder="Pybricks Hub"
rightElement={
isHubNameValid ? undefined : (
<Tooltip
content={i18n.translate(
SettingsStringId.FirmwareHubNameErrorTooltip,
)}
boundary="window"
position={Position.BOTTOM}
targetTagName="div"
>
<Icon
icon="error"
intent={Intent.DANGER}
itemType="div"
/>
</Tooltip>
)
}
/>
</Label>
</ControlGroup>
</FormGroup>
<FormGroup label={i18n.translate(SettingsStringId.HelpTitle)}>
<ButtonGroup
Expand Down Expand Up @@ -328,17 +374,21 @@ const mapStateToProps = (state: RootState): StateProps => ({
beforeInstallPrompt: state.app.beforeInstallPrompt,
promptingInstall: state.app.promptingInstall,
readyForOfflineUse: state.app.readyForOfflineUse,
hubName: state.settings.hubName,
isHubNameValid: state.settings.isHubNameValid,
});

const mapDispatchToProps: DispatchProps = {
onShowDocsChanged: (checked) => setBoolean(SettingId.ShowDocs, checked),
onDarkModeChanged: (checked) => setBoolean(SettingId.DarkMode, checked),
onShowDocsChanged: (checked) => setBoolean(BooleanSettingId.ShowDocs, checked),
onDarkModeChanged: (checked) => setBoolean(BooleanSettingId.DarkMode, checked),
onFlashCurrentProgramChanged: (checked) =>
setBoolean(SettingId.FlashCurrentProgram, checked),
onToggleDocs: () => toggleBoolean(SettingId.ShowDocs),
setBoolean(BooleanSettingId.FlashCurrentProgram, checked),
onToggleDocs: () => toggleBoolean(BooleanSettingId.ShowDocs),
onCheckForUpdate: checkForUpdate,
onReload: reload,
onInstallPrompt: installPrompt,
onHubNameChange: (event) =>
setString(StringSettingId.HubName, event.currentTarget.value),
};

export default connect(
Expand Down
73 changes: 60 additions & 13 deletions src/settings/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,72 +2,119 @@
// Copyright (c) 2021 The Pybricks Authors

import { Action } from 'redux';
import { SettingId } from './defaults';
import { BooleanSettingId, StringSettingId } from './defaults';

/** Actions related to settings. */
export enum SettingsActionType {
SetBoolean = 'settings.action.setBoolean',
ToggleBoolean = 'settings.action.toggleBoolean',
DidFailToSetBoolean = 'settings.action.didFailToSetBoolean',
DidBooleanChange = 'settings.action.didBooleanChange',
SetString = 'settings.action.setString',
DidFailToSetString = 'settings.action.didFailToSetString',
DidStringChange = 'settings.action.didStringChange',
}

type SettingInfo<T> = {
type SettingInfo<TId, TState> = {
/** The ID of the setting. */
id: SettingId;
id: TId;
/** The new state for the setting. */
newState: T;
newState: TState;
};

/** Action to set/store a setting. */
export type SettingsSetBooleanAction = Action<SettingsActionType.SetBoolean> &
SettingInfo<boolean>;
SettingInfo<BooleanSettingId, boolean>;

/** Creates an action to set/store a setting. */
export function setBoolean(id: SettingId, newState: boolean): SettingsSetBooleanAction {
export function setBoolean(
id: BooleanSettingId,
newState: boolean,
): SettingsSetBooleanAction {
return { type: SettingsActionType.SetBoolean, id, newState };
}

/** Action to toggle a setting. */
export type SettingsToggleBooleanAction = Action<SettingsActionType.ToggleBoolean> & {
id: SettingId;
id: BooleanSettingId;
};

/** Creates an action to toggle a setting. */
export function toggleBoolean(id: SettingId): SettingsToggleBooleanAction {
export function toggleBoolean(id: BooleanSettingId): SettingsToggleBooleanAction {
return { type: SettingsActionType.ToggleBoolean, id };
}

/** Action that indicates setting/storing a setting failed. */
export type SettingsDidFailToSetBooleanAction =
Action<SettingsActionType.DidFailToSetBoolean> & {
id: SettingId;
id: BooleanSettingId;
err: Error;
};

/** Creates an action indicating that setting/storing a setting failed. */
export function didFailToSetBoolean(
id: SettingId,
id: BooleanSettingId,
err: Error,
): SettingsDidFailToSetBooleanAction {
return { type: SettingsActionType.DidFailToSetBoolean, id, err };
}

/** Action that indicates a stored boolean setting value changed. */
export type SettingsDidBooleanChangeAction =
Action<SettingsActionType.DidBooleanChange> & SettingInfo<boolean>;
Action<SettingsActionType.DidBooleanChange> &
SettingInfo<BooleanSettingId, boolean>;

/** Creates an action that indicates a stored boolean setting value changed. */
export function didBooleanChange(
id: SettingId,
id: BooleanSettingId,
newState: boolean,
): SettingsDidBooleanChangeAction {
return { type: SettingsActionType.DidBooleanChange, id, newState };
}

/** Action to set/store a setting. */
export type SettingsSetStringAction = Action<SettingsActionType.SetString> &
SettingInfo<StringSettingId, string>;

/** Creates an action to set/store a setting. */
export function setString(
id: StringSettingId,
newState: string,
): SettingsSetStringAction {
return { type: SettingsActionType.SetString, id, newState };
}

/** Action that indicates setting/storing a setting failed. */
export type SettingsDidFailToSetStringAction =
Action<SettingsActionType.DidFailToSetString> & {
id: StringSettingId;
err: Error;
};

/** Creates an action indicating that setting/storing a setting failed. */
export function didFailToSetString(
id: StringSettingId,
err: Error,
): SettingsDidFailToSetStringAction {
return { type: SettingsActionType.DidFailToSetString, id, err };
}

export type SettingsDidStringChangeAction = Action<SettingsActionType.DidStringChange> &
SettingInfo<StringSettingId, string>;

export function didStringChange(
id: StringSettingId,
newState: string,
): SettingsDidStringChangeAction {
return { type: SettingsActionType.DidStringChange, id, newState };
}

/** Common type for all settings actions. */
export type SettingsAction =
| SettingsSetBooleanAction
| SettingsToggleBooleanAction
| SettingsDidFailToSetBooleanAction
| SettingsDidBooleanChangeAction;
| SettingsDidBooleanChangeAction
| SettingsSetStringAction
| SettingsDidFailToSetStringAction
| SettingsDidStringChangeAction;
26 changes: 20 additions & 6 deletions src/settings/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,36 @@

import { prefersDarkMode } from '../utils/os';

export enum SettingId {
export enum BooleanSettingId {
ShowDocs = 'showDocs',
DarkMode = 'darkMode',
FlashCurrentProgram = 'flashCurrentProgram',
}

export function getDefaultBooleanValue(id: SettingId): boolean {
export function getDefaultBooleanValue(id: BooleanSettingId): boolean {
switch (id) {
case SettingId.ShowDocs:
case BooleanSettingId.ShowDocs:
return window.innerWidth >= 1024;
case SettingId.DarkMode:
case BooleanSettingId.DarkMode:
return prefersDarkMode();
case SettingId.FlashCurrentProgram:
case BooleanSettingId.FlashCurrentProgram:
return false;
// istanbul ignore next: it is a programmer error if we hit this
default:
throw Error(`Bad setting id: ${id}`);
throw Error(`Bad BooleanSettingId: ${id}`);
}
}

export enum StringSettingId {
HubName = 'hubName',
}

export function getDefaultStringValue(id: StringSettingId): string {
switch (id) {
case StringSettingId.HubName:
return ''; // empty string will result in 'Pybricks Hub'
// istanbul ignore next: it is a programmer error if we hit this
default:
throw Error(`Bad StringSettingId: ${id}`);
}
}
6 changes: 6 additions & 0 deletions src/settings/i18n.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
"flash-current-program": {
"label": "Include current program",
"tooltip": "Select to include your program when installing the firmware."
},
"hub-name": {
"label": "Hub name",
"error": {
"tooltip": "The name is too long."
}
}
},
"help": {
Expand Down
2 changes: 2 additions & 0 deletions src/settings/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export enum SettingsStringId {
FirmwareTitle = 'settings.firmware.title',
FirmwareCurrentProgramLabel = 'settings.firmware.flash-current-program.label',
FirmwareCurrentProgramTooltip = 'settings.firmware.flash-current-program.tooltip',
FirmwareHubNameLabel = 'settings.firmware.hub-name.label',
FirmwareHubNameErrorTooltip = 'settings.firmware.hub-name.error.tooltip',
HelpTitle = 'settings.help.title',
HelpProjectsLabel = 'settings.help.projects.label',
HelpSupportLabel = 'settings.help.support.label',
Expand Down
Loading

0 comments on commit 86c0e93

Please sign in to comment.