1
0
Fork 0
mirror of https://github.com/sensebox/blockly-app synced 2025-10-20 23:33:53 +02:00

UX improvements

This commit is contained in:
Norwin 2019-02-17 17:37:35 +01:00
parent 8af35cd30c
commit 4e14d01112
5 changed files with 149 additions and 51 deletions

View file

@ -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 &amp; 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...",

View file

@ -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>

View file

@ -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'">

View file

@ -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;
}
}
}
}

View file

@ -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,
}