diff --git a/src/components/ewt-list-item.ts b/src/components/ewt-list-item.ts new file mode 100644 index 0000000..6494d8e --- /dev/null +++ b/src/components/ewt-list-item.ts @@ -0,0 +1,14 @@ +import { ListItemBase } from "@material/mwc-list/mwc-list-item-base"; +import { styles } from "@material/mwc-list/mwc-list-item.css"; + +declare global { + interface HTMLElementTagNameMap { + "ewt-list-item": EwtListItem; + } +} + +export class EwtListItem extends ListItemBase { + static override styles = [styles]; +} + +customElements.define("ewt-list-item", EwtListItem); diff --git a/src/components/ewt-select.ts b/src/components/ewt-select.ts new file mode 100644 index 0000000..25bd95d --- /dev/null +++ b/src/components/ewt-select.ts @@ -0,0 +1,14 @@ +import { SelectBase } from "@material/mwc-select/mwc-select-base"; +import { styles } from "@material/mwc-select/mwc-select.css"; + +declare global { + interface HTMLElementTagNameMap { + "ewt-select": EwtSelect; + } +} + +export class EwtSelect extends SelectBase { + static override styles = [styles]; +} + +customElements.define("ewt-select", EwtSelect); diff --git a/src/install-dialog.ts b/src/install-dialog.ts index 3dca2f1..b4a2170 100644 --- a/src/install-dialog.ts +++ b/src/install-dialog.ts @@ -7,12 +7,14 @@ import "./components/ewt-dialog"; import "./components/ewt-formfield"; import "./components/ewt-icon-button"; import "./components/ewt-textfield"; +import type { EwtTextfield } from "./components/ewt-textfield"; +import "./components/ewt-select"; +import "./components/ewt-list-item"; import "./pages/ewt-page-progress"; import "./pages/ewt-page-message"; import { chipIcon, closeIcon, firmwareIcon } from "./components/svg"; -import type { EwtTextfield } from "./components/ewt-textfield"; import { Logger, Manifest, FlashStateType, FlashState } from "./const.js"; -import { ImprovSerial } from "improv-wifi-serial-sdk/dist/serial"; +import { ImprovSerial, Ssid } from "improv-wifi-serial-sdk/dist/serial"; import { ImprovSerialCurrentState, ImprovSerialErrorState, @@ -61,6 +63,13 @@ class EwtInstallDialog extends LitElement { @state() private _busy = false; + // undefined = not loaded + // null = not available + @state() private _ssids?: Ssid[] | null; + + // -1 = custom + @state() private _selectedSsid = -1; + protected render() { if (!this.port) { return html``; @@ -296,7 +305,15 @@ class EwtInstallDialog extends LitElement { let hideActions = false; if (this._busy) { - return [heading, this._renderProgress("Trying to connect"), true]; + return [ + heading, + this._renderProgress( + this._ssids === undefined + ? "Scanning for networks" + : "Trying to connect" + ), + true, + ]; } if ( @@ -356,7 +373,6 @@ class EwtInstallDialog extends LitElement { label="Skip" @click=${() => { this._state = "DASHBOARD"; - this._installState = undefined; }} > @@ -392,7 +408,46 @@ class EwtInstallDialog extends LitElement { to connect to. ${error ? html`

${error}

` : ""} - + ${this._ssids !== null + ? html` + { + const index = ev.detail.index; + // The "Join Other" item is always the last item. + this._selectedSsid = + index === this._ssids!.length ? -1 : index; + }} + @closed=${(ev: Event) => ev.stopPropagation()} + > + ${this._ssids!.map( + (info, idx) => html` + + ${info.name} + + ` + )} + + Join other… + + + ` + : ""} + ${ + // Show input box if command not supported or "Join Other" selected + this._selectedSsid === -1 + ? html` + + ` + : "" + } { - this._installState = undefined; this._state = "DASHBOARD"; }} > @@ -565,7 +619,6 @@ class EwtInstallDialog extends LitElement { @click=${async () => { this._initialize(); this._state = "DASHBOARD"; - this._installState = undefined; }} > `; @@ -622,9 +675,31 @@ class EwtInstallDialog extends LitElement { if (this._state !== "ERROR") { this._error = undefined; } - if (this._state !== "PROVISION") { + // Scan for SSIDs on provision + if (this._state === "PROVISION") { + this._ssids = undefined; + this._busy = true; + this._client!.scan().then( + (ssids) => { + this._busy = false; + this._ssids = ssids; + this._selectedSsid = ssids.length ? 0 : -1; + }, + () => { + this._busy = false; + this._ssids = null; + this._selectedSsid = -1; + } + ); + } else { + // Reset this value if we leave provisioning. this._provisionForce = false; } + + if (this._state === "INSTALL") { + this._installConfirmed = false; + this._installState = undefined; + } } protected override firstUpdated(changedProps: PropertyValues) { @@ -635,20 +710,29 @@ class EwtInstallDialog extends LitElement { protected override updated(changedProps: PropertyValues) { super.updated(changedProps); - if (!changedProps.has("_state")) { + if (changedProps.has("_state")) { + this.setAttribute("state", this._state); + } + + if (this._state !== "PROVISION") { return; } - this.setAttribute("state", this._state); + if (changedProps.has("_selectedSsid") && this._selectedSsid === -1) { + // If we pick "Join other", select SSID input. + this._focusFormElement("ewt-textfield[name=ssid]"); + } else if (changedProps.has("_ssids")) { + // Form is shown when SSIDs are loaded/marked not supported + this._focusFormElement(); + } + } - if (this._state === "PROVISION") { - const textfield = this.shadowRoot!.querySelector("ewt-textfield"); - if (textfield) { - textfield.updateComplete.then(() => textfield.focus()); - } - } else if (this._state === "INSTALL") { - this._installConfirmed = false; - this._installState = undefined; + private _focusFormElement(selector = "ewt-textfield, ewt-select") { + const formEl = this.shadowRoot!.querySelector( + selector + ) as LitElement | null; + if (formEl) { + formEl.updateComplete.then(() => setTimeout(() => formEl.focus(), 100)); } } @@ -738,9 +822,14 @@ class EwtInstallDialog extends LitElement { this._busy = true; this._wasProvisioned = this._client!.state === ImprovSerialCurrentState.PROVISIONED; - const ssid = ( - this.shadowRoot!.querySelector("ewt-textfield[name=ssid]") as EwtTextfield - ).value; + const ssid = + this._selectedSsid === -1 + ? ( + this.shadowRoot!.querySelector( + "ewt-textfield[name=ssid]" + ) as EwtTextfield + ).value + : this._ssids![this._selectedSsid].name; const password = ( this.shadowRoot!.querySelector( "ewt-textfield[name=password]" @@ -810,7 +899,8 @@ class EwtInstallDialog extends LitElement { width: 20px; margin-right: 8px; } - ewt-textfield { + ewt-textfield, + ewt-select { display: block; margin-top: 16px; }