Wifi optional (#2249)

* begin work

* allow enable and disable wifi

* nice styling

* done except for popover not dismissing

* update wifi.ts

* address comments
This commit is contained in:
Matt Hill
2023-05-09 07:40:56 -06:00
committed by Aiden McClelland
parent f6c09109ba
commit 9499ea8ca9
11 changed files with 422 additions and 433 deletions

View File

@@ -2,7 +2,7 @@ import { NgModule } from '@angular/core'
import { CommonModule } from '@angular/common'
import { IonicModule } from '@ionic/angular'
import { RouterModule, Routes } from '@angular/router'
import { WifiPage } from './wifi.page'
import { WifiPage, ToWifiIconPipe } from './wifi.page'
import { SharedPipesModule } from '@start9labs/shared'
const routes: Routes = [
@@ -19,6 +19,6 @@ const routes: Routes = [
RouterModule.forChild(routes),
SharedPipesModule,
],
declarations: [WifiPage],
declarations: [WifiPage, ToWifiIconPipe],
})
export class WifiPageModule {}

View File

@@ -4,171 +4,156 @@
<ion-back-button defaultHref="system"></ion-back-button>
</ion-buttons>
<ion-title>WiFi Settings</ion-title>
<ion-buttons slot="end">
<ion-button (click)="getWifi()">
Refresh
<ion-icon slot="end" name="refresh"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding-top with-widgets">
<ion-item-group>
<!-- always -->
<ion-item>
<ion-label>
<h2>
Adding WiFi credentials to your StartOS allows you to remove the
Ethernet cable and move the device anywhere you want. StartOS will
automatically connect to available networks.
<a
href="https://docs.start9.com/latest/user-manual/wifi"
target="_blank"
rel="noreferrer"
<ion-content class="with-widgets">
<div class="smaller" *tuiLet="enabled$ | async as enabled">
<ion-item-group>
<ion-item>
<ion-label>
<h2>
Adding WiFi credentials to your Embassy allows you to remove the
Ethernet cable and move the device anywhere you want. Embassy will
automatically connect to available networks.
<a
href="https://docs.start9.com/latest/user-manual/wifi"
target="_blank"
rel="noreferrer"
>
View instructions
</a>
</h2>
</ion-label>
</ion-item>
<ion-item>
<ion-label><b>Wi-Fi</b></ion-label>
<ion-toggle
slot="end"
[checked]="enabled"
(ionChange)="toggleWifi($any($event))"
></ion-toggle>
</ion-item>
</ion-item-group>
<ng-container *ngIf="enabled">
<ng-container *ngIf="wifi$ | async as wifi; else loading">
<!-- known networks -->
<ion-item-divider *ngIf="!(wifi.ssids | empty)">
Known Networks
</ion-item-divider>
<ion-item-group>
<ion-item *ngFor="let ssid of wifi.ssids | keyvalue">
<ion-label>
<h2>{{ ssid.key }}</h2>
<p *ngIf="ssid.key === wifi.connected" class="inline">
<ion-icon
color="success"
name="ellipse"
style="padding-right: 4px"
></ion-icon>
<ion-text color="dark"><b>Connected</b></ion-text>
</p>
</ion-label>
<div slot="end" class="inline slot-end">
<img [src]="ssid.value | toWifiIcon" style="max-width: 32px" />
<ion-button
[id]="ssid.key"
style="--padding-start: 6px; --padding-end: 4px"
fill="clear"
>
<ion-icon
slot="icon-only"
name="ellipsis-horizontal-circle"
></ion-icon>
</ion-button>
</div>
<ion-popover [trigger]="ssid.key" triggerAction="click">
<ng-template>
<ion-content>
<ion-item
button
*ngIf="ssid.key !== wifi.connected"
(click)="connect(ssid.key)"
>
<ion-label>Connect</ion-label>
</ion-item>
<ion-item
button
lines="none"
(click)="forget(ssid.key, wifi)"
>
<ion-label>Forget this network</ion-label>
</ion-item>
</ion-content>
</ng-template>
</ion-popover>
</ion-item>
</ion-item-group>
<!-- other networks -->
<ion-item-divider>Other Networks</ion-item-divider>
<ion-item-group>
<ng-container *ngFor="let avWifi of wifi['available-wifi']">
<ion-item *ngIf="avWifi.ssid">
<ion-label>{{ avWifi.ssid }}</ion-label>
<div slot="end" class="inline slot-end">
<ion-button
color="dark"
class="connect-button"
(click)="presentModalAdd(avWifi)"
>
Connect
</ion-button>
<ion-icon
style="margin-right: 12px"
[name]="avWifi.security.length ? 'lock-closed-outline' : 'lock-open-outline'"
></ion-icon>
<img
[src]="avWifi.strength | toWifiIcon"
style="max-width: 32px"
/>
</div>
</ion-item>
</ng-container>
</ion-item-group>
<div class="ion-text-end ion-padding-top">
<ion-button
(click)="presentModalAddOther(wifi)"
color="dark"
strong
size="small"
style="font-size: 12px"
>
View instructions
</a>
</h2>
</ion-label>
</ion-item>
<ion-item-divider>Country</ion-item-divider>
<!-- not loading -->
<ion-item
button
detail="false"
(click)="presentAlertCountry()"
[disabled]="loading"
>
<ion-icon slot="start" name="earth-outline" size="large"></ion-icon>
<ion-label *ngIf="wifi.country">
{{ wifi.country }} - {{ this.countries[wifi.country] }}
</ion-label>
<ion-label *ngIf="!wifi.country">Select Country</ion-label>
</ion-item>
<!-- loading -->
<ng-container *ngIf="loading">
<ion-item-divider>Saved Networks</ion-item-divider>
<ion-item *ngFor="let entry of ['', '']" class="skeleton-parts">
<ion-button slot="start" fill="clear">
<ion-skeleton-text
animated
style="width: 30px; height: 30px; border-radius: 0"
></ion-skeleton-text>
</ion-button>
<ion-label>
<ion-skeleton-text animated style="width: 18%"></ion-skeleton-text>
</ion-label>
</ion-item>
<ion-item-divider>Available Networks</ion-item-divider>
<ion-item *ngFor="let entry of ['', '']" class="skeleton-parts">
<ion-button slot="start" fill="clear">
<ion-skeleton-text
animated
style="width: 30px; height: 30px; border-radius: 0"
></ion-skeleton-text>
</ion-button>
<ion-label>
<ion-skeleton-text animated style="width: 18%"></ion-skeleton-text>
</ion-label>
</ion-item>
</ng-container>
<!-- not loading -->
<ng-container *ngIf="!loading && wifi.country">
<ion-item-divider *ngIf="!(wifi.ssids | empty)">
Saved Networks
</ion-item-divider>
<ion-item
button
detail="false"
*ngFor="let ssid of wifi.ssids | keyvalue"
(click)="presentAction(ssid.key)"
>
<div
*ngIf="ssid.key !== wifi.connected"
slot="start"
style="padding-right: 32px"
></div>
<ion-icon
*ngIf="ssid.key === wifi.connected"
slot="start"
size="large"
name="checkmark"
color="success"
></ion-icon>
<ion-label>{{ ssid.key }}</ion-label>
<img
*ngIf="ssid.value > 0 && ssid.value < 5"
slot="end"
src="assets/img/icons/wifi-1.png"
style="max-width: 32px"
/>
<img
*ngIf="ssid.value >= 5 && ssid.value < 50"
slot="end"
src="assets/img/icons/wifi-1.png"
style="max-width: 32px"
/>
<img
*ngIf="ssid.value >= 50 && ssid.value < 90"
slot="end"
src="assets/img/icons/wifi-2.png"
style="max-width: 32px"
/>
<img
*ngIf="ssid.value >= 90"
slot="end"
src="assets/img/icons/wifi-3.png"
style="max-width: 32px"
/>
</ion-item>
<ion-item-divider>Available Networks</ion-item-divider>
<ng-container *ngFor="let avWifi of wifi['available-wifi']">
<ion-item
button
detail="false"
(click)="presentModalAdd(avWifi.ssid, !!avWifi.security.length)"
*ngIf="avWifi.ssid"
>
<ion-label>{{ avWifi.ssid }}</ion-label>
<img
*ngIf="avWifi.strength < 5"
slot="end"
src="assets/img/icons/wifi-1.png"
style="max-width: 32px"
/>
<img
*ngIf="avWifi.strength >= 5 && avWifi.strength < 50"
slot="end"
src="assets/img/icons/wifi-1.png"
style="max-width: 32px"
/>
<img
*ngIf="avWifi.strength >= 50 && avWifi.strength < 90"
slot="end"
src="assets/img/icons/wifi-2.png"
style="max-width: 32px"
/>
<img
*ngIf="avWifi.strength >= 90"
slot="end"
src="assets/img/icons/wifi-3.png"
style="max-width: 32px"
/>
</ion-item>
Other...
</ion-button>
</div>
</ng-container>
<ion-item button detail="false" (click)="presentModalAdd()">
<ion-icon slot="start" name="add" color="dark"></ion-icon>
<ion-label>
<b>Join Another Network</b>
</ion-label>
</ion-item>
<!-- loading -->
<ng-template #loading>
<ion-item-divider>Known Networks</ion-item-divider>
<ion-item-group>
<ion-item *ngFor="let entry of ['', '']" class="skeleton-parts">
<ion-label>
<ion-skeleton-text
animated
style="width: 25%"
></ion-skeleton-text>
</ion-label>
</ion-item>
</ion-item-group>
<ion-item-divider>Other Networks</ion-item-divider>
<ion-item-group>
<ion-item *ngFor="let entry of ['', '', '']" class="skeleton-parts">
<ion-label>
<ion-skeleton-text
animated
style="width: 25%"
></ion-skeleton-text>
</ion-label>
</ion-item>
</ion-item-group>
</ng-template>
</ng-container>
</ion-item-group>
</div>
</ion-content>

View File

@@ -1,7 +1,41 @@
.smaller {
padding: 32px;
max-width: 800px;
}
.no-padding {
padding-right: 0;
}
.skeleton-parts {
ion-button::part(native) {
padding-inline-start: 0;
padding-inline-end: 0;
};
padding-bottom: 6px;
}
.connect-button {
font-size: 10px;
font-weight: bold;
margin-right: 12px;
}
.slot-end {
margin-left: 4px;
}
ion-item-divider {
text-transform: unset;
padding-bottom: 12px;
padding-left: 0;
}
ion-item-group {
background-color: #1e2024;
border: 1px solid #717171;
border-radius: 6px;
}
ion-item {
--background: #1e2024;
}

View File

@@ -1,20 +1,30 @@
import { Component } from '@angular/core'
import {
ActionSheetController,
AlertController,
ToastController,
} from '@ionic/angular'
import { AlertInput } from '@ionic/core'
import { ToastController } from '@ionic/angular'
import { TuiDialogOptions } from '@taiga-ui/core'
import { ToggleCustomEvent } from '@ionic/core'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { ActionSheetButton } from '@ionic/core'
import { ValueSpecObject } from 'start-sdk/lib/config/configTypes'
import { RR } from 'src/app/services/api/api.types'
import { AvailableWifi, RR } from 'src/app/services/api/api.types'
import { pauseFor, ErrorToastService } from '@start9labs/shared'
import { ConfigService } from 'src/app/services/config.service'
import { FormDialogService } from 'src/app/services/form-dialog.service'
import { FormContext, FormPage } from 'src/app/modals/form/form.page'
import { LoadingService } from 'src/app/modals/loading/loading.service'
import { PatchDB } from 'patch-db-client'
import { DataModel } from 'src/app/services/patch-db/data-model'
import { ConnectionService } from 'src/app/services/connection.service'
import { Pipe, PipeTransform } from '@angular/core'
import {
BehaviorSubject,
catchError,
distinctUntilChanged,
filter,
from,
merge,
Observable,
Subject,
switchMap,
tap,
} from 'rxjs'
import { wifiSpec } from './wifiSpec'
interface WiFiForm {
ssid: string
@@ -27,101 +37,106 @@ interface WiFiForm {
styleUrls: ['wifi.page.scss'],
})
export class WifiPage {
loading = true
wifi: RR.GetWifiRes = {} as any
countries = require('../../../util/countries.json') as {
[key: string]: string
}
readonly connected$ = this.connectionService.connected$.pipe(filter(Boolean))
readonly enabled$ = this.patch.watch$('server-info', 'wifi-enabled').pipe(
distinctUntilChanged(),
tap(enabled => {
if (enabled) this.trigger$.next('')
}),
)
readonly trigger$ = new BehaviorSubject('')
readonly localChanges$ = new Subject<RR.GetWifiRes>()
readonly wifi$ = merge(
this.trigger$.pipe(switchMap(() => this.getWifi$())),
this.localChanges$,
)
constructor(
private readonly api: ApiService,
private readonly toastCtrl: ToastController,
private readonly alertCtrl: AlertController,
private readonly loader: LoadingService,
private readonly formDialog: FormDialogService,
private readonly errToast: ErrorToastService,
private readonly actionCtrl: ActionSheetController,
private readonly config: ConfigService,
private readonly patch: PatchDB<DataModel>,
private readonly connectionService: ConnectionService,
) {}
async ngOnInit() {
await this.getWifi()
}
async toggleWifi(e: ToggleCustomEvent): Promise<void> {
const enable = e.detail.checked
const loader = this.loader
.open(enable ? 'Enabling Wifi' : 'Disabling WiFi')
.subscribe()
async getWifi(timeout: number = 0): Promise<void> {
this.loading = true
try {
this.wifi = await this.api.getWifi({}, timeout)
if (!this.wifi.country) {
await this.presentAlertCountry()
}
await this.api.enableWifi({ enable })
} catch (e: any) {
this.errToast.present(e)
} finally {
this.loading = false
loader.unsubscribe()
}
}
async presentAlertCountry(): Promise<void> {
if (!this.config.isLan) {
const alert = await this.alertCtrl.create({
header: 'Cannot Complete Action',
message:
'You must be connected to your server via LAN to change the country.',
buttons: [
{
text: 'OK',
role: 'cancel',
},
],
cssClass: 'enter-click',
})
await alert.present()
return
async connect(ssid: string): Promise<void> {
const loader = this.loader
.open('Connecting. This could take a while...')
.subscribe()
try {
await this.api.connectWifi({ ssid })
await this.confirmWifi(ssid)
} catch (e: any) {
this.errToast.present(e)
} finally {
loader.unsubscribe()
}
const inputs: AlertInput[] = Object.entries(this.countries).map(
([country, fullName]) => {
return {
name: fullName,
type: 'radio',
label: `${country} - ${fullName}`,
value: country,
checked: country === this.wifi.country,
}
},
)
const alert = await this.alertCtrl.create({
header: 'Select Country',
subHeader:
'Warning: Changing the country will delete all saved networks from StartOS.',
inputs,
buttons: [
{
text: 'Cancel',
role: 'cancel',
},
{
text: 'Save',
handler: (country: string) => this.setCountry(country),
},
],
cssClass: 'enter-click select-warning',
})
await alert.present()
}
presentModalAdd(ssid?: string, needsPW: boolean = true) {
const { name, spec } = getWifiValueSpec(ssid, needsPW)
async forget(ssid: string, wifi: RR.GetWifiRes): Promise<void> {
const loader = this.loader.open('Deleting...').subscribe()
try {
await this.api.deleteWifi({ ssid })
delete wifi.ssids[ssid]
this.localChanges$.next(wifi)
this.trigger$.next('')
} catch (e: any) {
this.errToast.present(e)
} finally {
loader.unsubscribe()
}
}
async presentModalAdd(network: AvailableWifi) {
if (!network.security.length) {
this.connect(network.ssid)
} else {
const options: Partial<TuiDialogOptions<FormContext<WiFiForm>>> = {
label: 'Password Needed',
data: {
spec: wifiSpec.spec,
buttons: [
{
text: 'Connect',
handler: async ({ ssid, password }) =>
this.saveAndConnect(ssid, password),
},
],
},
}
this.formDialog.open(FormPage, options)
}
}
presentModalAddOther(wifi: RR.GetWifiRes) {
const options: Partial<TuiDialogOptions<FormContext<WiFiForm>>> = {
label: name,
label: wifiSpec.name,
data: {
spec,
spec: wifiSpec.spec,
buttons: [
{
text: 'Save for Later',
handler: async ({ ssid, password }) => this.save(ssid, password),
handler: async ({ ssid, password }) =>
this.save(ssid, password, wifi),
},
{
text: 'Save and Connect',
@@ -131,107 +146,36 @@ export class WifiPage {
],
},
}
this.formDialog.open(FormPage, options)
}
async presentAction(ssid: string) {
const buttons: ActionSheetButton[] = [
{
text: 'Forget',
icon: 'trash',
role: 'destructive',
handler: () => {
this.delete(ssid)
},
},
]
if (ssid !== this.wifi.connected) {
buttons.unshift({
text: 'Connect',
icon: 'wifi',
handler: () => {
this.connect(ssid)
},
})
}
const action = await this.actionCtrl.create({
header: ssid,
subHeader: 'Manage network',
mode: 'ios',
buttons,
})
await action.present()
private getWifi$(): Observable<RR.GetWifiRes> {
return from(this.api.getWifi({}, 10000)).pipe(
catchError((e: any) => {
this.errToast.present(e)
return []
}),
)
}
private async setCountry(country: string): Promise<void> {
const loader = this.loader.open('Setting country...').subscribe()
try {
await this.api.setWifiCountry({ country })
await this.getWifi()
this.wifi.country = country
} catch (e: any) {
this.errToast.present(e)
} finally {
loader.unsubscribe()
}
}
private async confirmWifi(
ssid: string,
deleteOnFailure = false,
): Promise<void> {
const maxAttempts = 5
let attempts = 0
while (attempts < maxAttempts) {
if (attempts > maxAttempts) {
this.presentToastFail()
if (deleteOnFailure) {
delete this.wifi.ssids[ssid]
}
break
}
try {
const start = new Date().valueOf()
await this.getWifi()
const end = new Date().valueOf()
if (this.wifi.connected === ssid) {
this.presentAlertSuccess(ssid)
break
} else {
attempts++
const diff = end - start
// depending on the response time, wait a min of 1000 ms, and a max of 4000 ms in between retries. Both 1000 and 4000 are arbitrary
await pauseFor(Math.max(1000, 4000 - diff))
}
} catch (e) {
attempts++
console.warn(e)
}
}
}
private async presentAlertSuccess(ssid: string): Promise<void> {
const alert = await this.alertCtrl.create({
header: `Connected to "${ssid}"`,
message:
'Note. It may take several minutes to an hour for StartOS to reconnect over Tor.',
private async presentToastSuccess(): Promise<void> {
const toast = await this.toastCtrl.create({
header: 'Connection successful!',
position: 'bottom',
duration: 4000,
buttons: [
{
text: 'Ok',
role: 'cancel',
cssClass: 'enter-click',
side: 'start',
icon: 'close',
handler: () => {
return true
},
},
],
cssClass: 'success-toast',
})
await alert.present()
await toast.present()
}
private async presentToastFail(): Promise<void> {
@@ -255,36 +199,11 @@ export class WifiPage {
await toast.present()
}
private async connect(ssid: string): Promise<void> {
const loader = this.loader
.open('Connecting. This could take a while...')
.subscribe()
try {
await this.api.connectWifi({ ssid })
await this.confirmWifi(ssid)
} catch (e: any) {
this.errToast.present(e)
} finally {
loader.unsubscribe()
}
}
private async delete(ssid: string): Promise<void> {
const loader = this.loader.open('Deleting...').subscribe()
try {
await this.api.deleteWifi({ ssid })
await this.getWifi()
delete this.wifi.ssids[ssid]
} catch (e: any) {
this.errToast.present(e)
} finally {
loader.unsubscribe()
}
}
private async save(ssid: string, password: string): Promise<boolean> {
private async save(
ssid: string,
password: string,
wifi: RR.GetWifiRes,
): Promise<boolean> {
const loader = this.loader.open('Saving...').subscribe()
try {
@@ -294,7 +213,9 @@ export class WifiPage {
priority: 0,
connect: false,
})
await this.getWifi()
wifi.ssids[ssid] = 0
this.localChanges$.next(wifi)
this.trigger$.next('')
return true
} catch (e: any) {
this.errToast.present(e)
@@ -319,7 +240,7 @@ export class WifiPage {
priority: 0,
connect: true,
})
await this.confirmWifi(ssid, true)
await this.confirmWifi(ssid)
return true
} catch (e: any) {
this.errToast.present(e)
@@ -328,52 +249,52 @@ export class WifiPage {
loader.unsubscribe()
}
}
}
function getWifiValueSpec(
ssid?: string,
needsPW: boolean = true,
): ValueSpecObject {
return {
type: 'object',
name: 'WiFi Credentials',
description:
'Enter the network SSID and password. You can connect now or save the network for later.',
warning: null,
spec: {
ssid: {
type: 'text',
name: 'Network SSID',
description: null,
inputmode: 'text',
placeholder: null,
patterns: [],
minLength: null,
maxLength: null,
required: true,
masked: false,
default: ssid || null,
warning: null,
},
password: {
type: 'text',
name: 'Password',
description: null,
inputmode: 'text',
placeholder: null,
required: needsPW,
masked: true,
minLength: null,
maxLength: null,
patterns: [
{
regex: '^.{8,}$',
description: 'Must be longer than 8 characters',
},
],
default: null,
warning: null,
},
},
private async confirmWifi(ssid: string): Promise<void> {
const maxAttempts = 5
let attempts = 0
while (true) {
if (attempts > maxAttempts) {
this.presentToastFail()
break
}
try {
const start = new Date().valueOf()
const newWifi = await this.api.getWifi({}, 10000)
const end = new Date().valueOf()
if (newWifi.connected === ssid) {
this.localChanges$.next(newWifi)
this.presentToastSuccess()
break
} else {
attempts++
const diff = end - start
// depending on the response time, wait a min of 1000 ms, and a max of 4000 ms in between retries. Both 1000 and 4000 are arbitrary
await pauseFor(Math.max(1000, 4000 - diff))
}
} catch (e) {
attempts++
console.warn(e)
}
}
}
}
@Pipe({
name: 'toWifiIcon',
})
export class ToWifiIconPipe implements PipeTransform {
transform(signal: number): string {
if (signal >= 0 && signal < 5) {
return 'assets/img/icons/wifi-0.png'
} else if (signal >= 5 && signal < 50) {
return 'assets/img/icons/wifi-1.png'
} else if (signal >= 50 && signal < 90) {
return 'assets/img/icons/wifi-2.png'
} else {
return 'assets/img/icons/wifi-3.png'
}
}
}

View File

@@ -0,0 +1,44 @@
import { ValueSpecObject } from 'start-sdk/lib/config/configTypes'
export const wifiSpec: ValueSpecObject = {
type: 'object',
name: 'WiFi Credentials',
description:
'Enter the network SSID and password. You can connect now or save the network for later.',
warning: null,
spec: {
ssid: {
type: 'text',
minLength: null,
maxLength: null,
patterns: [],
name: 'Network SSID',
description: null,
inputmode: 'text',
placeholder: null,
required: true,
masked: false,
default: null,
warning: null,
},
password: {
type: 'text',
minLength: null,
maxLength: null,
patterns: [
{
regex: '^.{8,}$',
description: 'Must be longer than 8 characters',
},
],
name: 'Password',
description: null,
inputmode: 'text',
placeholder: null,
required: true,
masked: true,
default: null,
warning: null,
},
},
}

View File

@@ -104,9 +104,6 @@ export module RR {
// wifi
export type SetWifiCountryReq = { country: string }
export type SetWifiCountryRes = null
export type GetWifiReq = {}
export type GetWifiRes = {
ssids: {
@@ -127,6 +124,9 @@ export module RR {
}
export type AddWifiRes = null
export type EnableWifiReq = { enable: boolean } // wifi.enable
export type EnableWifiRes = null
export type ConnectWifiReq = { ssid: string } // wifi.connect
export type ConnectWifiRes = null

View File

@@ -147,15 +147,13 @@ export abstract class ApiService {
// wifi
abstract enableWifi(params: RR.EnableWifiReq): Promise<RR.EnableWifiRes>
abstract getWifi(
params: RR.GetWifiReq,
timeout: number,
): Promise<RR.GetWifiRes>
abstract setWifiCountry(
params: RR.SetWifiCountryReq,
): Promise<RR.SetWifiCountryRes>
abstract addWifi(params: RR.AddWifiReq): Promise<RR.AddWifiRes>
abstract connectWifi(params: RR.ConnectWifiReq): Promise<RR.ConnectWifiRes>

View File

@@ -266,6 +266,10 @@ export class LiveApiService extends ApiService {
// wifi
async enableWifi(params: RR.EnableWifiReq): Promise<RR.EnableWifiRes> {
return this.rpcRequest({ method: 'wifi.enable', params })
}
async getWifi(
params: RR.GetWifiReq,
timeout?: number,
@@ -273,12 +277,6 @@ export class LiveApiService extends ApiService {
return this.rpcRequest({ method: 'wifi.get', params, timeout })
}
async setWifiCountry(
params: RR.SetWifiCountryReq,
): Promise<RR.SetWifiCountryRes> {
return this.rpcRequest({ method: 'wifi.country.set', params })
}
async addWifi(params: RR.AddWifiReq): Promise<RR.AddWifiRes> {
return this.rpcRequest({ method: 'wifi.add', params })
}

View File

@@ -418,18 +418,23 @@ export class MockApiService extends ApiService {
// wifi
async enableWifi(params: RR.EnableWifiReq): Promise<RR.EnableWifiRes> {
await pauseFor(2000)
const patch = [
{
op: PatchOp.REPLACE,
path: '/server-info/wifi-enabled',
value: params.enable,
},
]
return this.withRevision(patch, null)
}
async getWifi(params: RR.GetWifiReq): Promise<RR.GetWifiRes> {
await pauseFor(2000)
return Mock.Wifi
}
async setWifiCountry(
params: RR.SetWifiCountryReq,
): Promise<RR.SetWifiCountryRes> {
await pauseFor(2000)
return null
}
async addWifi(params: RR.AddWifiReq): Promise<RR.AddWifiRes> {
await pauseFor(2000)
return null

View File

@@ -41,7 +41,8 @@ export const mockPatchData: DataModel = {
},
'server-info': {
id: 'abcdefgh',
version: '0.3.4.3',
version: '0.3.4',
country: 'us',
'last-backup': new Date(new Date().valueOf() - 604800001).toISOString(),
'lan-address': 'https://adjective-noun.local',
'tor-address': 'http://myveryownspecialtoraddress.onion',
@@ -56,6 +57,7 @@ export const mockPatchData: DataModel = {
},
},
'last-wifi-region': null,
'wifi-enabled': false,
'unread-notification-count': 4,
// password is asdfasdf
'password-hash':

View File

@@ -64,11 +64,13 @@ export interface DevProjectData {
export interface ServerInfo {
id: string
version: string
country: string
'last-backup': string | null
'lan-address': Url
'tor-address': Url
'ip-info': IpInfo
'last-wifi-region': string | null
'wifi-enabled': boolean
'unread-notification-count': number
'status-info': ServerStatusInfo
'eos-version-compat': string