mirror of
https://github.com/sensebox/blockly-app
synced 2025-10-20 23:33:53 +02:00
UX improvements
This commit is contained in:
parent
8af35cd30c
commit
4e14d01112
5 changed files with 149 additions and 51 deletions
|
@ -13,12 +13,14 @@
|
|||
},
|
||||
"OPENSOURCE": {
|
||||
"TITLE": "Open Source",
|
||||
"TEXT": "This app is free & libre open source software. To may find the source code & issue tracker on GitHub:"
|
||||
"TEXT": "This app is free & libre open source software. You may find the source code & issue tracker on GitHub:"
|
||||
},
|
||||
"HINT": "<em>Hint:</em> A <a href=\"https://blockly.sensebox.de\">web version of Blockly for senseBox</a> is available as well!"
|
||||
},
|
||||
"BLOCKLY": {
|
||||
"TITLE": "Blockly for senseBox"
|
||||
"TITLE": "Blockly for senseBox",
|
||||
"BTN_CODE": "Code View",
|
||||
"BTN_OTA": "OTA Programmer"
|
||||
},
|
||||
"OTAWIZ": {
|
||||
"TITLE": "Over The Air Programmer",
|
||||
|
@ -27,19 +29,27 @@
|
|||
"BTN_BACK": "Back",
|
||||
"INTRO": {
|
||||
"TITLE": "Welcome",
|
||||
"TEXT": "To <b>program your senseBox</b> wirelessly, please follow these steps. We will help you connect to your senseBox via <b>WiFi</b>.",
|
||||
"TEXT": "To transfer your code to your senseBox over the air (OTA), please follow these steps.",
|
||||
"STEPS": "First, please make sure that your senseBox...",
|
||||
"STEP1": "has the <b>WiFi shield</b> plugged in",
|
||||
"STEP1": "has the <b>WiFi Bee</b> plugged in XBee Slot 1",
|
||||
"STEP2": "has the initial <b>OTA sketch</b> installed",
|
||||
"STEP3": "is running"
|
||||
"STEP3": "is powered"
|
||||
},
|
||||
"OTAMODE": {
|
||||
"TITLE": "Enable OTA Mode",
|
||||
"TEXT": "To transfer your code, the senseBox must be in OTA mode.<br/>This is indicated by the green blinking LED next to the red reset button.",
|
||||
"STEPS": "To enable OTA mode...",
|
||||
"STEP1": "press & hold the switch button (gray or blue)",
|
||||
"STEP2": "press the red reset button shortly",
|
||||
"STEP3": "release the switch button after one second"
|
||||
},
|
||||
"COMPILATION": {
|
||||
"COMPILING": {
|
||||
"TITLE": "Compiling your sketch..."
|
||||
},
|
||||
"GO_ONLINE": {
|
||||
"TITLE": "Compiling your sketch...",
|
||||
"TEXT": "For compilation, you need to connect to the internet. Please enable a connection."
|
||||
"TITLE": "You are offline.",
|
||||
"TEXT": "For compilation you need to connect to the internet. Please enable a connection."
|
||||
},
|
||||
"DONE": {
|
||||
"TITLE": "Sketch successfully compiled."
|
||||
|
@ -51,11 +61,13 @@
|
|||
"WIFI": {
|
||||
"MANUAL": {
|
||||
"TITLE": "Connect to your senseBox",
|
||||
"TEXT": "Your senseBox should have opened the a WiFi network. Because we can not do this automatically on your platform, please connect to it manually."
|
||||
"TEXT": "Your senseBox should have created a WiFi network. Because we can not do this automatically on your platform, please connect to it manually."
|
||||
},
|
||||
"AUTO": {
|
||||
"TITLE": "Select your senseBox",
|
||||
"TEXT": "In the list, all running senseBoxes with OTA available are shown. If you don't see yours, please make sure that GPS is enabled.",
|
||||
"TEXT1": "All powered senseBoxes around you with OTA mode enabled are listed here.",
|
||||
"TEXT2": "Please check the last digits of MAC-Adress on your WiFi Bee to identify your senseBox. You can find it printed on the bottom of the WiFi Bee.",
|
||||
"TEXT3": "If you can't find your senseBox, please make sure that your WiFi Bee is mounted correctly, OTA mode is enabled and GPS is enabled on this device.",
|
||||
"AVAILABLE": "Available senseBox WiFis",
|
||||
"SCANNING": "searching...",
|
||||
"CONNECTING": "connecting...",
|
||||
|
|
|
@ -14,12 +14,28 @@
|
|||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-fab top right edge style="right: 4vh">
|
||||
<button ion-fab large (click)="launchOtaWizard()" color="light"><ion-icon name="wifi"></ion-icon></button>
|
||||
<ion-fab top right edge style="right: 2vh">
|
||||
<button
|
||||
ion-fab
|
||||
large
|
||||
color ="light"
|
||||
[title]="'BLOCKLY.BTN_OTA' | translate"
|
||||
(click)="launchOtaWizard();"
|
||||
>
|
||||
<ion-icon name="wifi"></ion-icon>
|
||||
</button>
|
||||
</ion-fab>
|
||||
|
||||
<ion-fab top right edge style="right: calc(4vh + 60px)">
|
||||
<button ion-fab mini (click)="toggleView();" color ="light"><ion-icon name="code"></ion-icon></button>
|
||||
<ion-fab top right edge style="right: calc(2vh + 60px)">
|
||||
<button
|
||||
ion-fab
|
||||
mini
|
||||
color ="light"
|
||||
[title]="'BLOCKLY.BTN_CODE' | translate"
|
||||
(click)="toggleView();"
|
||||
>
|
||||
<ion-icon name="code"></ion-icon>
|
||||
</button>
|
||||
</ion-fab>
|
||||
|
||||
<iframe #blocklyFrame scrolling="no" src='assets/blockly.html'></iframe>
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
</ion-navbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-content padding>
|
||||
<ion-slides #slides pager (ionSlideDidChange)="onSlideChange()">
|
||||
|
||||
<!-- intro -->
|
||||
<ion-slide>
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-row align-items-center>
|
||||
<ion-col col-12 col-md-6>
|
||||
<ion-icon name="wifi" style="font-size: 160px"></ion-icon>
|
||||
<h2 translate>OTAWIZ.INTRO.TITLE</h2>
|
||||
|
@ -36,11 +36,38 @@
|
|||
</ion-grid>
|
||||
</ion-slide>
|
||||
|
||||
<!-- compilation waiting screen -->
|
||||
<!-- OTA mode guide -->
|
||||
<ion-slide>
|
||||
<ion-grid>
|
||||
<ion-row align-items-center>
|
||||
<ion-col col-12 col-md-6>
|
||||
<h2 translate>OTAWIZ.OTAMODE.TITLE</h2>
|
||||
<p [innerHTML]="'OTAWIZ.OTAMODE.TEXT' | translate"></p>
|
||||
<p translate>OTAWIZ.OTAMODE.STEPS</p>
|
||||
<ol style="text-align: left">
|
||||
<li [innerHTML]="'OTAWIZ.OTAMODE.STEP1' | translate"></li>
|
||||
<li [innerHTML]="'OTAWIZ.OTAMODE.STEP2' | translate"></li>
|
||||
<li [innerHTML]="'OTAWIZ.OTAMODE.STEP3' | translate"></li>
|
||||
</ol>
|
||||
</ion-col>
|
||||
|
||||
<ion-col col-12 col-md-6>
|
||||
<!-- @TODO: diagram! -->
|
||||
<button ion-button large clear icon-end color="primary" (click)="slides.slideNext()">
|
||||
{{ 'OTAWIZ.BTN_NEXT' | translate }}
|
||||
<ion-icon name="arrow-forward"></ion-icon>
|
||||
</button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-slide>
|
||||
|
||||
<!-- compilation waiting screen -->
|
||||
<ion-slide *ngIf="!slideIsHidden(slideCompilation)">
|
||||
<ng-container *ngIf="state.compilation == 'compiling'">
|
||||
<h2 translate>OTAWIZ.COMPILATION.COMPILING.TITLE</h2>
|
||||
<ion-spinner *ngIf="slides.getActiveIndex() == 1" item-start name="dots"></ion-spinner>
|
||||
<!-- getActiveIndex() check because animated icons use loads of CPU, even when not visible! -->
|
||||
<ion-spinner *ngIf="currentSlide == slideCompilation" item-start name="dots"></ion-spinner>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="state.compilation == 'go-online'">
|
||||
|
@ -78,7 +105,11 @@
|
|||
<ion-col col-12 col-md-6>
|
||||
<ion-icon name="wifi" style="font-size: 160px"></ion-icon>
|
||||
<h2 translate>OTAWIZ.WIFI.AUTO.TITLE</h2>
|
||||
<p translate>OTAWIZ.WIFI.AUTO.TEXT</p>
|
||||
<ul style="text-align: left">
|
||||
<li translate>OTAWIZ.WIFI.AUTO.TEXT1</li>
|
||||
<li translate>OTAWIZ.WIFI.AUTO.TEXT2</li>
|
||||
<li translate>OTAWIZ.WIFI.AUTO.TEXT3</li>
|
||||
</ul>
|
||||
</ion-col>
|
||||
|
||||
<ion-col col-12 col-md-6>
|
||||
|
@ -89,12 +120,12 @@
|
|||
|
||||
<ion-list id="wifi-list">
|
||||
<ion-item *ngIf="state.wifiSelection == 'scanning'">
|
||||
<ion-spinner *ngIf="slides.getActiveIndex() == 2" item-start name="dots"></ion-spinner>
|
||||
<ion-spinner *ngIf="currentSlide == slideWifi" item-start name="dots"></ion-spinner>
|
||||
{{ 'OTAWIZ.WIFI.AUTO.SCANNING' | translate }}
|
||||
</ion-item>
|
||||
|
||||
<ion-item *ngIf="state.wifiSelection == 'connecting'">
|
||||
<ion-spinner *ngIf="slides.getActiveIndex() == 2" item-start name="dots"></ion-spinner>
|
||||
<ion-spinner *ngIf="currentSlide == slideWifi" item-start name="dots"></ion-spinner>
|
||||
{{ 'OTAWIZ.WIFI.AUTO.CONNECTING' | translate }}
|
||||
</ion-item>
|
||||
|
||||
|
@ -122,7 +153,7 @@
|
|||
<ion-slide>
|
||||
<ng-container *ngIf="state.upload == 'uploading'">
|
||||
<h2 translate>OTAWIZ.UPLOAD.UPLOADING</h2>
|
||||
<ion-spinner *ngIf="slides.getActiveIndex() == 3" item-start name="dots"></ion-spinner>
|
||||
<ion-spinner *ngIf="currentSlide == slideUpload" item-start name="dots"></ion-spinner>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="state.upload == 'done'">
|
||||
|
|
|
@ -20,10 +20,10 @@ page-ota-wizard {
|
|||
}
|
||||
}
|
||||
|
||||
p, ul {
|
||||
p, ul, ol {
|
||||
padding: 0 20px;
|
||||
font-size: 16px;
|
||||
line-height: 1.4;
|
||||
line-height: 1.5;
|
||||
color: #60646B;
|
||||
|
||||
b {
|
||||
|
@ -32,8 +32,13 @@ page-ota-wizard {
|
|||
}
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin-left: 4%;
|
||||
li {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
}
|
||||
ul {
|
||||
margin-left: 3%;
|
||||
list-style-type: disc;
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +59,5 @@ page-ota-wizard {
|
|||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
OnDestroy,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
ChangeDetectorRef,
|
||||
} from '@angular/core'
|
||||
import {
|
||||
IonicPage,
|
||||
|
@ -23,14 +24,8 @@ import { CompilerProvider } from '../../providers/compiler/compiler';
|
|||
})
|
||||
export class OtaWizardPage implements OnInit, OnDestroy {
|
||||
@ViewChild(Slides) slides: Slides
|
||||
onlineSub: Subscription
|
||||
offlineSub: Subscription
|
||||
|
||||
sketch = ''
|
||||
availableSenseboxes: string[] = [] // list of SSIDs
|
||||
compiledSketch: ArrayBuffer = undefined
|
||||
errorMsg = ''
|
||||
|
||||
state: OtaState = {
|
||||
isOnline: false,
|
||||
compilation: 'compiling',
|
||||
|
@ -38,12 +33,24 @@ export class OtaWizardPage implements OnInit, OnDestroy {
|
|||
upload: 'uploading',
|
||||
}
|
||||
|
||||
// for unified slide index access in the template
|
||||
slideCompilation = OtaSlides.Compilation
|
||||
slideWifi = OtaSlides.WifiSelection
|
||||
slideUpload = OtaSlides.Upload
|
||||
|
||||
private onlineSub: Subscription
|
||||
private offlineSub: Subscription
|
||||
private sketch = ''
|
||||
private compiledSketch: ArrayBuffer = undefined
|
||||
private hiddenSlides: OtaSlides[] = []
|
||||
|
||||
constructor(
|
||||
private network: Network,
|
||||
private otaWifi: OtaWifiProvider,
|
||||
private navCtrl: NavController,
|
||||
private webcompiler: CompilerProvider,
|
||||
private changedetect: ChangeDetectorRef,
|
||||
navParams : NavParams,
|
||||
private compilerprovider:CompilerProvider
|
||||
) {
|
||||
// for OTA to work, the new sketch has to include the OTA logic as well.
|
||||
// to ensure that, we're prepending it here to the sketch.
|
||||
|
@ -61,6 +68,14 @@ export class OtaWizardPage implements OnInit, OnDestroy {
|
|||
ngOnInit() {
|
||||
// try to start compilation already in the background
|
||||
this.compileSketch()
|
||||
.then(() => this.hideSlide(OtaSlides.Compilation))
|
||||
|
||||
if (this.otaWifi.strategy === WifiStrategy.Automatic) {
|
||||
this.otaWifi.findSenseboxes(true)
|
||||
.then(ssids => this.availableSenseboxes = ssids)
|
||||
} else {
|
||||
this.state.wifiSelection = 'manual'
|
||||
}
|
||||
|
||||
this.state.isOnline = this.network.type !== 'none'
|
||||
|
||||
|
@ -81,7 +96,7 @@ export class OtaWizardPage implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
onWifiRefresh () {
|
||||
this.handleWifiSelection()
|
||||
this.handleWifiSelection(true)
|
||||
}
|
||||
|
||||
onClose () {
|
||||
|
@ -90,8 +105,9 @@ export class OtaWizardPage implements OnInit, OnDestroy {
|
|||
|
||||
// call logic for each slide
|
||||
onSlideChange () {
|
||||
switch (this.slides.getActiveIndex()) {
|
||||
switch (this.currentSlide) {
|
||||
case OtaSlides.Intro:
|
||||
case OtaSlides.Intro2:
|
||||
this.slides.lockSwipeToNext(false)
|
||||
break
|
||||
|
||||
|
@ -112,6 +128,23 @@ export class OtaWizardPage implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
get currentSlide (): OtaSlides {
|
||||
const current = this.slides.getActiveIndex()
|
||||
const hiddenOffset = this.hiddenSlides.filter(slide => slide <= current).length
|
||||
return current + hiddenOffset
|
||||
}
|
||||
|
||||
slideIsHidden (slide: OtaSlides): boolean {
|
||||
return this.hiddenSlides.indexOf(slide) !== -1
|
||||
}
|
||||
|
||||
private hideSlide (slide: OtaSlides) {
|
||||
if (this.currentSlide === slide) return
|
||||
if (this.slideIsHidden(slide)) return
|
||||
this.hiddenSlides.push(slide)
|
||||
this.slides.update()
|
||||
}
|
||||
|
||||
async connectToSensebox (ssid: string) {
|
||||
this.state.wifiSelection = 'connecting'
|
||||
try {
|
||||
|
@ -126,10 +159,7 @@ export class OtaWizardPage implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
private handleCompilation () {
|
||||
// skip compilation slide if already compiled
|
||||
this.slides.lockSwipeToNext(!this.compiledSketch)
|
||||
if (this.compiledSketch)
|
||||
return this.slides.slideNext(0)
|
||||
|
||||
// need to go online for compilation. compilation is retriggered via this.onlineSub
|
||||
if (!this.state.isOnline) {
|
||||
|
@ -145,20 +175,25 @@ export class OtaWizardPage implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
private async handleWifiSelection () {
|
||||
private async handleWifiSelection (force = false) {
|
||||
if (this.otaWifi.strategy === WifiStrategy.Automatic) {
|
||||
this.slides.lockSwipeToNext(true)
|
||||
|
||||
if (this.otaWifi.strategy == WifiStrategy.Manual) {
|
||||
this.state.wifiSelection = 'manual'
|
||||
this.slides.lockSwipeToNext(false)
|
||||
} else {
|
||||
this.state.wifiSelection = 'scanning'
|
||||
// skip scan when boxes where already found from the scan on startup
|
||||
if (!force && this.availableSenseboxes.length)
|
||||
return this.state.wifiSelection = 'select'
|
||||
|
||||
try {
|
||||
this.state.wifiSelection = 'scanning'
|
||||
// force update of view, as setting subproperties of this.state is not detected automatically :/
|
||||
this.changedetect.detectChanges()
|
||||
this.availableSenseboxes = await this.otaWifi.findSenseboxes(true)
|
||||
this.state.wifiSelection = 'select'
|
||||
this.changedetect.detectChanges()
|
||||
} catch (err) {
|
||||
this.state.wifiSelection = 'error'
|
||||
this.errorMsg = err.message
|
||||
this.state.wifiSelection = 'error'
|
||||
this.changedetect.detectChanges()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +216,7 @@ export class OtaWizardPage implements OnInit, OnDestroy {
|
|||
private async compileSketch () {
|
||||
this.state.compilation = 'compiling'
|
||||
try {
|
||||
this.compiledSketch = await this.compilerprovider.callcompiler(this.sketch)
|
||||
this.compiledSketch = await this.webcompiler.compile(this.sketch)
|
||||
this.state.compilation = 'done'
|
||||
this.slides.lockSwipeToNext(false)
|
||||
} catch (err) {
|
||||
|
@ -206,7 +241,8 @@ type OtaState = {
|
|||
// names for the slide indices for easier access
|
||||
enum OtaSlides {
|
||||
Intro = 0,
|
||||
Compilation = 1,
|
||||
WifiSelection = 2,
|
||||
Upload = 3,
|
||||
Intro2 = 1,
|
||||
Compilation = 2,
|
||||
WifiSelection = 3,
|
||||
Upload = 4,
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue