Commit 00df96d4 authored by Konstantin Schulz's avatar Konstantin Schulz
Browse files

introducing RxJS with its Observable/Subject syntax to make the access to...

introducing RxJS with its Observable/Subject syntax to make the access to remote data (incl. cache) more uniform and consistent
parent 71c4de41
{
"name": "mc_frontend",
"version": "1.4.8",
"version": "1.5.0",
"author": "Ionic Framework",
"homepage": "https://ionicframework.com/",
"scripts": {
......
......@@ -8,7 +8,7 @@
</div>
</ion-buttons>
<ion-spinner *ngIf="HelperService.isLoading"></ion-spinner>
<ion-title>{{ corpusService.currentAuthor?.name }}</ion-title>
<ion-title *ngIf="HelperService.applicationState | async as state">{{ state.currentSetup.currentAuthor?.name }}</ion-title>
<ion-buttons slot="end">
<ion-menu-button autoHide="false"></ion-menu-button>
</ion-buttons>
......@@ -17,9 +17,8 @@
<ion-content padding>
<ion-list>
<ion-item *ngFor="let corpus of corpusService.currentAuthor?.corpora">
<ion-list *ngIf="HelperService.applicationState | async as state">
<ion-item *ngFor="let corpus of state.currentSetup.currentAuthor?.corpora">
<button (click)="showPossibleReferences(corpus)">
<span>{{corpus.title}}</span>
</button>
......
......@@ -5,25 +5,23 @@ import {TranslateService} from '@ngx-translate/core';
import {HelperService} from '../helper.service';
import {CorpusService} from 'src/app/corpus.service';
import {HttpClient} from '@angular/common/http';
import {TextRange} from 'src/app/models/textRange';
@Component({
selector: 'app-author-detail',
templateUrl: './author-detail.page.html',
styleUrls: ['./author-detail.page.scss'],
selector: 'app-author-detail',
templateUrl: './author-detail.page.html',
styleUrls: ['./author-detail.page.scss'],
})
export class AuthorDetailPage {
HelperService = HelperService;
HelperService = HelperService;
constructor(public navCtrl: NavController,
public corpusService: CorpusService,
public translate: TranslateService,
public http: HttpClient) {
}
constructor(public navCtrl: NavController,
public corpusService: CorpusService,
public translate: TranslateService,
public http: HttpClient) {
}
showPossibleReferences(corpus: CorpusMC) {
this.corpusService.currentCorpus = corpus;
this.corpusService.currentTextRange = new TextRange({start: ['', '', ''], end: ['', '', '']});
HelperService.goToTextRangePage(this.navCtrl).then();
}
showPossibleReferences(corpus: CorpusMC) {
this.corpusService.saveNewCorpus(corpus);
HelperService.goToTextRangePage(this.navCtrl).then();
}
}
......@@ -20,20 +20,29 @@
<ion-row>
<ion-col>
<b style="padding: 0.5em;">{{ 'MOST_RECENT_SETUP' | translate }}:</b>
</ion-col>
</ion-row>
<ion-row *ngIf="HelperService.mostRecentSetup?.currentCorpus && HelperService.mostRecentSetup?.currentUrn">
<ion-col>
<a (click)="restoreLastSetup()">{{ [HelperService.mostRecentSetup.currentCorpus.author,
HelperService.mostRecentSetup.currentCorpus.title,
HelperService.mostRecentSetup.currentUrn?.split(":").slice(-1)[0]].join(", ") }}</a>
</ion-col>
</ion-row>
<div *ngIf="HelperService.applicationState | async as state">
<ion-row
*ngIf="state.mostRecentSetup && state.mostRecentSetup.currentCorpus && state.mostRecentSetup.currentUrn; else noEntryFound">
<ion-col>
<a (click)="restoreLastSetup()">
{{ [state.mostRecentSetup.currentCorpus.author, state.mostRecentSetup.currentCorpus.title,
state.mostRecentSetup.currentUrn?.split(":").slice(-1)[0]].join(", ") }}
</a>
</ion-col>
</ion-row>
<ng-template #noEntryFound>
<ion-row>
<ion-col>{{ "NO_ENTRY_FOUND" | translate }}</ion-col>
</ion-row>
</ng-template>
</div>
<ion-row>
<ion-col>
<label>
<input type="checkbox" [(ngModel)]="showOnlyTreebanks" (change)="toggleTreebankAuthors()" />
<input type="checkbox" [(ngModel)]="showOnlyTreebanks" (change)="toggleTreebankAuthors()"/>
<span class="checkbox">{{ 'AUTHOR_SHOW_ONLY_TREEBANKS' | translate}}</span>
</label>
</ion-col>
......@@ -43,7 +52,7 @@
<label>
<ion-icon name="search" class="search"></ion-icon>
<input type="search" (ngModelChange)="getAuthors($event.toString())"
placeholder="{{ 'AUTHOR_SEARCH' | translate }}" [(ngModel)]="currentSearchValue" />
placeholder="{{ 'AUTHOR_SEARCH' | translate }}" [(ngModel)]="currentSearchValue"/>
</label>
</ion-col>
</ion-row>
......@@ -51,7 +60,7 @@
<ion-col style="display: inline-grid">
<h2>{{'AUTHOR' | translate}}</h2>
<ion-grid *ngIf="corpusService.availableAuthors.length > 0; else loading" class="author"
style="text-align: left">
style="text-align: left">
<ion-row *ngFor="let author of authorsDisplayed">
<ion-col>
<a href="javascript:" (click)="showCorpora(author)">{{author.name}}</a>
......
......@@ -6,6 +6,9 @@ import {CorpusService} from 'src/app/corpus.service';
import {HttpClient} from '@angular/common/http';
import {HelperService} from '../helper.service';
import {ExerciseService} from '../exercise.service';
import {ApplicationState} from '../models/applicationState';
import {Observable} from 'rxjs';
import {take} from 'rxjs/operators';
/**
* Generated class for the AuthorPage page.
......@@ -71,9 +74,11 @@ export class AuthorPage {
showCorpora(author: Author) {
this.corpusService.currentAuthor = author;
HelperService.mostRecentSetup.currentAuthor = author;
this.helperService.saveMostRecentSetup().then();
this.navCtrl.navigateForward('/author-detail').then();
HelperService.applicationState.pipe(take(1)).subscribe((as: ApplicationState) => {
as.currentSetup.currentAuthor = author;
this.helperService.saveApplicationState(as).then();
this.navCtrl.navigateForward('/author-detail').then();
});
}
toggleTreebankAuthors() {
......
......@@ -15,7 +15,7 @@ import {
DependencyValue,
ExerciseType,
ExerciseTypeTranslation,
InstructionsTranslation, MoodleExerciseType,
InstructionsTranslation,
PartOfSpeechTranslation,
PartOfSpeechValue,
Phenomenon
......@@ -27,7 +27,10 @@ import {Exercise} from 'src/app/models/exercise';
import {Feedback} from 'src/app/models/feedback';
import {PhenomenonMap, PhenomenonMapContent} from 'src/app/models/phenomenonMap';
import {FrequencyItem} from 'src/app/models/frequencyItem';
import {ExerciseMC} from 'src/app/models/exerciseMC';
import {BehaviorSubject, ReplaySubject} from 'rxjs';
import {ApplicationState} from './models/applicationState';
import {take} from 'rxjs/operators';
import {TextData} from './models/textData';
@Injectable({
providedIn: 'root'
......@@ -40,9 +43,11 @@ export class CorpusService {
public citationsUnavailableString: string;
public corporaUnavailableString: string;
public currentAuthor: Author;
public currentCorpus: CorpusMC;
public currentCorpus: ReplaySubject<CorpusMC>;
private currentCorpusCache: CorpusMC;
public currentText = '';
public currentTextRange: TextRange = new TextRange({start: ['', '', ''], end: ['', '', '']});
public currentTextRange: ReplaySubject<TextRange>;
private currentTextRangeCache: TextRange = new TextRange({start: ['', '', ''], end: ['', '', '']});
public currentUrn: string;
public exercise: Exercise = new Exercise({
type: ExerciseType.cloze,
......@@ -71,16 +76,18 @@ export class CorpusService {
public helperService: HelperService,
) {
this.isMostRecentSetupLoaded = false;
this.helperService.initApplicationState();
this.initCurrentCorpus();
this.initCurrentTextRange();
HelperService.waitForConfig().then(() => {
this.checkForUpdates().then(() => {
this.checkForUpdates().finally(() => {
this.checkAnnisResponse().then(() => {
this.restoreLastCorpus().then(() => {
this.isMostRecentSetupLoaded = true;
}, () => {
});
}, () => {
this.isMostRecentSetupLoaded = true;
});
}, () => {
});
});
this.initPhenomenonMap();
......@@ -122,12 +129,16 @@ export class CorpusService {
if (this.annisResponse) {
return outerResolve();
}
this.helperService.loadMostRecentSetup().then(() => {
this.annisResponse = HelperService.mostRecentSetup.annisResponse;
this.currentAuthor = HelperService.mostRecentSetup.currentAuthor;
this.currentUrn = HelperService.mostRecentSetup.currentUrn;
this.currentCorpus = HelperService.mostRecentSetup.currentCorpus;
return outerResolve();
HelperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
if (state.mostRecentSetup) {
this.annisResponse = state.mostRecentSetup.annisResponse;
this.currentAuthor = state.mostRecentSetup.currentAuthor;
this.currentUrn = state.mostRecentSetup.currentUrn;
this.currentCorpusCache = state.mostRecentSetup.currentCorpus;
return outerResolve();
} else {
return outerReject();
}
}, () => {
return outerReject();
});
......@@ -212,8 +223,10 @@ export class CorpusService {
HelperService.makeGetRequest(this.http, this.toastCtrl, url, params).then((fis: FrequencyItem[]) => {
HelperService.isLoading = false;
this.annisResponse.frequency_analysis = fis;
HelperService.mostRecentSetup.annisResponse = this.annisResponse;
this.helperService.saveMostRecentSetup().then();
HelperService.applicationState.pipe(take(1)).subscribe((as: ApplicationState) => {
as.mostRecentSetup.annisResponse = this.annisResponse;
this.helperService.saveApplicationState(as).then();
});
return resolve();
}, () => {
return reject();
......@@ -281,6 +294,27 @@ export class CorpusService {
this.translate.get('LINK_COPIED').subscribe(value => this.shareLinkCopiedString = value);
}
initCurrentCorpus(): void {
this.currentCorpus = new ReplaySubject<CorpusMC>(1);
if (!this.currentCorpusCache) {
HelperService.applicationState.subscribe((as: ApplicationState) => {
const textData: TextData = as.currentSetup ? as.currentSetup : as.mostRecentSetup;
this.currentCorpusCache = textData ? textData.currentCorpus : null;
this.currentCorpus.next(this.currentCorpusCache);
});
} else {
this.currentCorpus.next(this.currentCorpusCache);
}
}
initCurrentTextRange(): void {
this.currentTextRange = new ReplaySubject<TextRange>(1);
HelperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
this.currentTextRangeCache = state.currentSetup.currentTextRange;
this.currentTextRange.next(this.currentTextRangeCache);
});
}
initPhenomenonMap() {
// map the different phenomena to their respective Enum for processing and display/translation
Object.keys(Phenomenon).forEach((key) => {
......@@ -336,9 +370,11 @@ export class CorpusService {
});
this.annisResponse = ar;
if (saveToCache) {
HelperService.mostRecentSetup.currentUrn = this.currentUrn;
HelperService.mostRecentSetup.annisResponse = ar;
this.helperService.saveMostRecentSetup().then();
HelperService.applicationState.pipe(take(1)).subscribe((as: ApplicationState) => {
as.currentSetup.currentUrn = this.currentUrn;
as.currentSetup.annisResponse = ar;
this.helperService.saveApplicationState(as).then();
});
}
}
......@@ -404,28 +440,56 @@ export class CorpusService {
restoreLastCorpus() {
return new Promise((resolve, reject) => {
this.annisResponse = HelperService.mostRecentSetup.annisResponse;
this.currentUrn = HelperService.mostRecentSetup.currentUrn;
this.currentCorpus = HelperService.mostRecentSetup.currentCorpus;
this.currentTextRange = HelperService.mostRecentSetup.currentTextRange;
this.isTextRangeCorrect = true;
if (this.annisResponse && this.annisResponse.nodes.length) {
this.processAnnisResponse(this.annisResponse, false);
}
// check if the data is already present
if (this.currentText) {
return resolve();
}
const saveToCache: boolean = !HelperService.mostRecentSetup.annisResponse ||
!HelperService.mostRecentSetup.annisResponse.nodes.length;
this.getText(saveToCache).then(() => {
return resolve();
}, () => {
return reject();
HelperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
this.annisResponse = state.mostRecentSetup.annisResponse;
this.currentUrn = state.mostRecentSetup.currentUrn;
this.currentCorpusCache = state.mostRecentSetup.currentCorpus;
this.currentTextRangeCache = state.mostRecentSetup.currentTextRange;
this.isTextRangeCorrect = true;
if (this.annisResponse && this.annisResponse.nodes.length) {
this.processAnnisResponse(this.annisResponse, false);
return resolve();
} else if (this.currentText) {
// check if the data is already present
return resolve();
} else {
const saveToCache: boolean = !state.mostRecentSetup.annisResponse || !state.mostRecentSetup.annisResponse.nodes.length;
this.getText(saveToCache).then(() => {
return resolve();
}, () => {
return reject();
});
}
});
});
}
saveNewCorpus(corpus: CorpusMC): void {
this.currentCorpusCache = corpus;
this.currentCorpus.next(this.currentCorpusCache);
this.setCurrentTextRange(-1, null, new TextRange({start: ['', '', ''], end: ['', '', '']}));
HelperService.applicationState.pipe(take(1)).subscribe((state: ApplicationState) => {
state.currentSetup.currentCorpus = corpus;
HelperService.applicationState.next(state);
});
}
setCurrentTextRange(inputId: number = -1, newValue: string = null, tr: TextRange = null): void {
if (tr) {
this.currentTextRangeCache = tr;
} else if (inputId >= 0) {
const isStart: boolean = inputId < 4;
const targetInputId = `input${inputId}`;
if (newValue === null) {
newValue = document.querySelector<HTMLInputElement>(`#${targetInputId}`).value;
}
const trIdx: number = inputId - (isStart ? 1 : 4);
const relevantTextRangePart: string[] = isStart ? this.currentTextRangeCache.start : this.currentTextRangeCache.end;
relevantTextRangePart[trIdx] = newValue;
}
this.currentTextRange.next(this.currentTextRangeCache);
}
updateBaseWord(query: QueryMC, queryIndex: number) {
if (!this.annisResponse || !this.annisResponse.frequency_analysis || !this.annisResponse.frequency_analysis.length) {
return;
......
......@@ -60,24 +60,22 @@
</ion-col>
<ion-col>
<ng-container
*ngIf="[ExerciseType.matching].indexOf(corpusService.exercise.type) === -1; else matching">
*ngIf="corpusService.exercise.type === ExerciseType.matching; else notMatching">
<label>
<h2 class="label">{{ 'QUERY_VALUE' | translate }}</h2>
<select [(ngModel)]="query.values" name="queryValue" multiple
size="{{Math.min(corpusService.getSortedQueryValues(query, i).length, 20)}}">
<select [(ngModel)]="query.values[0]" name="queryValue"
(change)="corpusService.updateBaseWord(query, i)">
<option *ngFor="let key of corpusService.getSortedQueryValues(query, i)"
[value]=key>{{ getDisplayValue(query, key, i) }}
</option>
</select>
</label>
</ng-container>
<ng-template #matching>
<ng-template #notMatching>
<label>
<h2 class="label">
{{ 'QUERY_VALUE' | translate }}
</h2>
<select [(ngModel)]="query.values[0]" name="queryValue"
(change)="corpusService.updateBaseWord(query, i)">
<h2 class="label">{{ 'QUERY_VALUE' | translate }}</h2>
<select [(ngModel)]="query.values" name="queryValue" multiple
size="{{Math.min(corpusService.getSortedQueryValues(query, i).length, 20)}}">
<option *ngFor="let key of corpusService.getSortedQueryValues(query, i)"
[value]=key>{{ getDisplayValue(query, key, i) }}
</option>
......@@ -116,8 +114,7 @@
<ion-col>
<label>
<input [(ngModel)]="corpusService.exercise.instructionsTranslation"
type="text"
name="instructionsTranslation"/>
type="text" name="instructionsTranslation"/>
</label>
</ion-col>
</ion-row>
......
......@@ -17,6 +17,10 @@ import {CorpusService} from 'src/app/corpus.service';
import {QueryMC} from 'src/app/models/queryMC';
import {PhenomenonMapContent} from 'src/app/models/phenomenonMap';
import {FrequencyItem} from 'src/app/models/frequencyItem';
import {CorpusMC} from '../models/corpusMC';
import {ApplicationState} from '../models/applicationState';
import {take} from 'rxjs/operators';
import {TextRange} from '../models/textRange';
@Component({
selector: 'app-exercise-parameters',
......@@ -107,20 +111,22 @@ export class ExerciseParametersPage implements OnInit {
const values: string[] = this.corpusService.exercise.queryItems[0].values as string[];
instructions += ` [${values.map(x => pmc.translationValues[x]).join(', ')}]`;
}
// TODO: change the corpus title to something meaningful, e.g. concatenate user ID and wanted exercise title
const workTitle: string = this.corpusService.currentCorpus.title + ', ' +
this.corpusService.currentTextRange.start.filter(x => x).join('.') + '-' +
this.corpusService.currentTextRange.end.filter(x => x).join('.');
formData.append('work_title', workTitle);
formData.append('type', MoodleExerciseType[this.corpusService.exercise.type]);
formData.append('type_translation', this.corpusService.exercise.typeTranslation);
formData.append('instructions', instructions);
formData.append('correct_feedback', this.corpusService.exercise.feedback.correct);
formData.append('partially_correct_feedback', this.corpusService.exercise.feedback.partiallyCorrect);
formData.append('incorrect_feedback', this.corpusService.exercise.feedback.incorrect);
formData.append('general_feedback', this.corpusService.exercise.feedback.general);
formData.append('work_author', this.corpusService.currentCorpus.author);
this.getH5Pexercise(formData);
this.corpusService.currentCorpus.pipe(take(1)).subscribe((cc: CorpusMC) => {
this.corpusService.currentTextRange.pipe(take(1)).subscribe((tr: TextRange) => {
// TODO: change the corpus title to something meaningful, e.g. concatenate user ID and wanted exercise title
const workTitle: string = cc.title + ', ' + tr.start.filter(x => x).join('.') + '-' + tr.end.filter(x => x).join('.');
formData.append('work_title', workTitle);
formData.append('type', MoodleExerciseType[this.corpusService.exercise.type]);
formData.append('type_translation', this.corpusService.exercise.typeTranslation);
formData.append('instructions', instructions);
formData.append('correct_feedback', this.corpusService.exercise.feedback.correct);
formData.append('partially_correct_feedback', this.corpusService.exercise.feedback.partiallyCorrect);
formData.append('incorrect_feedback', this.corpusService.exercise.feedback.incorrect);
formData.append('general_feedback', this.corpusService.exercise.feedback.general);
formData.append('work_author', cc.author);
this.getH5Pexercise(formData);
});
});
}
getH5Pexercise(formData: FormData) {
......@@ -131,12 +137,14 @@ export class ExerciseParametersPage implements OnInit {
HelperService.isLoading = false;
// save the old frequency analysis in case we want to change the exercise parameters at a later time
ar.frequency_analysis = this.corpusService.annisResponse.frequency_analysis;
HelperService.mostRecentSetup.annisResponse = ar;
this.helperService.saveMostRecentSetup().then();
this.corpusService.annisResponse.exercise_id = ar.exercise_id;
this.corpusService.annisResponse.uri = ar.uri;
this.corpusService.annisResponse.solutions = ar.solutions;
HelperService.goToPreviewPage(this.navCtrl).then();
HelperService.applicationState.pipe(take(1)).subscribe((as: ApplicationState) => {
as.mostRecentSetup.annisResponse = ar;
this.helperService.saveApplicationState(as).then();
this.corpusService.annisResponse.exercise_id = ar.exercise_id;
this.corpusService.annisResponse.uri = ar.uri;
this.corpusService.annisResponse.solutions = ar.solutions;
HelperService.goToPreviewPage(this.navCtrl).then();
});
}, async (error: HttpErrorResponse) => {
HelperService.isLoading = false;
HelperService.currentError = error;
......
......@@ -16,7 +16,6 @@ window.onresize = () => {
})
export class ExerciseService {
public excludeOOV = false;
public exerciseTypePath: string;
public fillBlanksString = 'fill_blanks';
public h5pIframeString = '#h5p-iframe-1';
public kwicGraphs: string;
......
......@@ -9,6 +9,8 @@ import {HttpClient, HttpParams} from '@angular/common/http';
import {AnnisResponse} from 'src/app/models/annisResponse';
import {ExerciseType, MoodleExerciseType} from 'src/app/models/enum';
import {CorpusService} from 'src/app/corpus.service';
import {ApplicationState} from '../models/applicationState';
import {take} from 'rxjs/operators';
@Component({
selector: 'app-exercise',
......@@ -38,18 +40,20 @@ export class ExercisePage implements OnInit {
let url: string = HelperService.config['backendBaseUrl'] + HelperService.config['backendApiExercisePath'];
const httpParams: HttpParams = new HttpParams().set('eid', params['eid']);
HelperService.makeGetRequest(this.http, this.toastCtrl, url, httpParams).then((ar: AnnisResponse) => {
HelperService.mostRecentSetup.annisResponse = ar;
this.helperService.saveMostRecentSetup().then();
this.corpusService.annisResponse = ar;
const met: MoodleExerciseType = MoodleExerciseType[ar.exercise_type];
this.corpusService.exercise.type = ExerciseType[met.toString()];
// this will be called via GET request from the h5p standalone javascript library
url = `${HelperService.config['backendBaseUrl']}${HelperService.config['backendApiH5pPath']}` +
`?eid=${this.corpusService.annisResponse.exercise_id}&lang=${this.translateService.currentLang}`;
window.localStorage.setItem(HelperService.config['localStorageKeyH5P'], url);
const exerciseTypePath: string = this.corpusService.exercise.type === ExerciseType.markWords ?
'mark_words' : 'drag_text';
this.exerciseService.initH5P(exerciseTypePath);
HelperService.applicationState.pipe(take(1)).subscribe((as: ApplicationState) => {
as.mostRecentSetup.annisResponse = ar;
this.helperService.saveApplicationState(as).then();
this.corpusService.annisResponse = ar;
const met: MoodleExerciseType = MoodleExerciseType[ar.exercise_type];
this.corpusService.exercise.type = ExerciseType[met.toString()];
// this will be called via GET request from the h5p standalone javascript library
url = `${HelperService.config['backendBaseUrl']}${HelperService.config['backendApiH5pPath']}` +
`?eid=${this.corpusService.annisResponse.exercise_id}&lang=${this.translateService.currentLang}`;
window.localStorage.setItem(HelperService.config['localStorageKeyH5P'], url);
const exerciseTypePath: string = this.corpusService.exercise.type === ExerciseType.markWords ?
'mark_words' : 'drag_text';
this.exerciseService.initH5P(exerciseTypePath);
});
}, () => {
});
} else {
......
......@@ -8,12 +8,16 @@ import {CaseValue, DependencyValue, PartOfSpeechValue} from 'src/app/models/enum
import {TranslateService} from '@ngx-translate/core';
import {Storage} from '@ionic/storage';
import {Language} from 'src/app/models/language';
import {ReplaySubject} from 'rxjs';
import {TextData} from './models/textData';
@Injectable({
providedIn: 'root'
})
export class HelperService {
public static applicationState: ReplaySubject<ApplicationState> = null;
private static applicationStateCache: ApplicationState = null;
public static baseUrl: string = location.protocol.concat('//').concat(window.location.host) +
window.location.pathname.split('/').slice(0, -1).join('/');
public static caseMap: { [rawValue: string]: CaseValue } = {
......@@ -81,7 +85,6 @@ export class HelperService {
shortcut: 'en'
}), new Language({name: 'Deutsch', shortcut: 'de'})];
public static menuId = 'menu1';
public static mostRecentSetup: ApplicationState = null;
public static partOfSpeechMap: { [rawValue: string]: PartOfSpeechValue } = {
ADJ: PartOfSpeechValue.adjective,
ADP: PartOfSpeechValue.preposition,
......@@ -142,7 +145,7 @@ export class HelperService {
}
static goToAuthorDetailPage(navCtrl: NavController): Promise<boolean> {
return navCtrl.navigateForward('/author-detail');
return navCtrl.navigateForward('/author-detail');
}
static goToDocExercisesPage(navCtrl: NavController): Promise<boolean> {
......@@ -235,7 +238,10 @@ export class HelperService {
}, 0);
return resolve(result);
}, async (error: HttpErrorResponse) => {
HelperService.isLoading = false;
// dirty hack to avoid ExpressionChangedAfterItHasBeenCheckedError
setTimeout(() => {
HelperService.isLoading = false;
}, 0);
HelperService.currentError = error;
const toast = await toastCtrl.create({
message: HelperService.generalErrorAlertMessage,
......@@ -274,6 +280,24 @@ export class HelperService {
});
}
initApplicationState(): void {
HelperService.applicationState = new ReplaySubject<ApplicationState>(1);
if (!HelperService.applicationStateCache) {
this.storage.get(HelperService.config['localStorageKeyApplicationState']).then((jsonString: string) => {
HelperService.applicationStateCache = new ApplicationState({
currentSetup: new TextData()
});
if (jsonString) {
const parsedJson: object = JSON.parse(jsonString);
HelperService.applicationStateCache = parsedJson as ApplicationState;
}
HelperService.applicationState.next(HelperService.applicationStateCache);