feat: offline notice

This commit is contained in:
Thea Schöbl
2023-02-13 12:19:35 +00:00
committed by Rainer Killinger
parent 11d1ac3f7c
commit 9b4caf526f
29 changed files with 548 additions and 106 deletions

177
package-lock.json generated
View File

@@ -28,6 +28,23 @@
"requires": { "requires": {
"@angular-devkit/core": "13.3.9", "@angular-devkit/core": "13.3.9",
"rxjs": "6.6.7" "rxjs": "6.6.7"
},
"dependencies": {
"rxjs": {
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
}
} }
}, },
"@angular-devkit/build-angular": { "@angular-devkit/build-angular": {
@@ -240,6 +257,23 @@
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==",
"dev": true "dev": true
}, },
"rxjs": {
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
}
}
},
"semver": { "semver": {
"version": "7.3.5", "version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
@@ -292,6 +326,23 @@
"requires": { "requires": {
"@angular-devkit/architect": "0.1303.9", "@angular-devkit/architect": "0.1303.9",
"rxjs": "6.6.7" "rxjs": "6.6.7"
},
"dependencies": {
"rxjs": {
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
}
} }
}, },
"@angular-devkit/core": { "@angular-devkit/core": {
@@ -319,6 +370,21 @@
"require-from-string": "^2.0.2", "require-from-string": "^2.0.2",
"uri-js": "^4.2.2" "uri-js": "^4.2.2"
} }
},
"rxjs": {
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
} }
} }
}, },
@@ -340,6 +406,21 @@
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz",
"integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==",
"dev": true "dev": true
},
"rxjs": {
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
} }
} }
}, },
@@ -2594,6 +2675,21 @@
"requires": { "requires": {
"ajv": "^8.0.0" "ajv": "^8.0.0"
} }
},
"rxjs": {
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
} }
} }
}, },
@@ -2970,6 +3066,23 @@
"string-width": "^4.1.0", "string-width": "^4.1.0",
"strip-ansi": "^6.0.0", "strip-ansi": "^6.0.0",
"through": "^2.3.6" "through": "^2.3.6"
},
"dependencies": {
"rxjs": {
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
}
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
}
} }
} }
} }
@@ -3774,6 +3887,23 @@
"yallist": "^4.0.0" "yallist": "^4.0.0"
} }
}, },
"rxjs": {
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
}
}
},
"semver": { "semver": {
"version": "7.3.4", "version": "7.3.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
@@ -3841,6 +3971,23 @@
"yallist": "^4.0.0" "yallist": "^4.0.0"
} }
}, },
"rxjs": {
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
}
}
},
"semver": { "semver": {
"version": "7.3.4", "version": "7.3.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
@@ -15378,18 +15525,11 @@
} }
}, },
"rxjs": { "rxjs": {
"version": "6.6.7", "version": "7.8.0",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz",
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==",
"requires": { "requires": {
"tslib": "^1.9.0" "tslib": "^2.1.0"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
} }
}, },
"rxjs-for-await": { "rxjs-for-await": {
@@ -16651,6 +16791,15 @@
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
"dev": true "dev": true
}, },
"rxjs": {
"version": "6.6.7",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
"integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
}
},
"strip-ansi": { "strip-ansi": {
"version": "5.2.0", "version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
@@ -16741,6 +16890,12 @@
"requires": { "requires": {
"has-flag": "^3.0.0" "has-flag": "^3.0.0"
} }
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"dev": true
} }
} }
}, },

View File

@@ -105,7 +105,7 @@
"ngx-markdown": "13.1.0", "ngx-markdown": "13.1.0",
"ngx-moment": "6.0.2", "ngx-moment": "6.0.2",
"opening_hours": "3.8.0", "opening_hours": "3.8.0",
"rxjs": "6.6.7", "rxjs": "7.8.0",
"swiper": "8.4.5", "swiper": "8.4.5",
"tslib": "2.4.1", "tslib": "2.4.1",
"zone.js": "0.12.0" "zone.js": "0.12.0"

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 StApps * Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.

View File

@@ -19,10 +19,11 @@ import {DefaultAuthService} from './default-auth.service';
import {Browser} from 'ionic-appauth'; import {Browser} from 'ionic-appauth';
import {nowInSeconds, Requestor, StorageBackend} from '@openid/appauth'; import {nowInSeconds, Requestor, StorageBackend} from '@openid/appauth';
import {TranslateService} from '@ngx-translate/core'; import {TranslateService} from '@ngx-translate/core';
import {LoggerConfig, LoggerModule, NGXLogger} from 'ngx-logger'; import {LoggerConfig, LoggerModule, NGXLogger, NgxLoggerLevel} from 'ngx-logger';
import {StAppsWebHttpClient} from '../data/stapps-web-http-client.provider'; import {StAppsWebHttpClient} from '../data/stapps-web-http-client.provider';
import {HttpClientModule} from '@angular/common/http'; import {HttpClientModule} from '@angular/common/http';
import {IonicStorage} from 'ionic-appauth/lib'; import {IonicStorage} from 'ionic-appauth/lib';
import {RouterModule} from '@angular/router';
describe('AuthService', () => { describe('AuthService', () => {
let defaultAuthService: DefaultAuthService; let defaultAuthService: DefaultAuthService;
@@ -34,7 +35,11 @@ describe('AuthService', () => {
storageBackendSpy = jasmine.createSpyObj('StorageBackend', ['getItem']); storageBackendSpy = jasmine.createSpyObj('StorageBackend', ['getItem']);
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [HttpClientModule, LoggerModule], imports: [
HttpClientModule,
LoggerModule.forRoot({level: NgxLoggerLevel.TRACE}),
RouterModule.forRoot([]),
],
providers: [ providers: [
NGXLogger, NGXLogger,
StAppsWebHttpClient, StAppsWebHttpClient,

View File

@@ -1,8 +1,23 @@
/*
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {Requestor} from '@openid/appauth'; import {Requestor} from '@openid/appauth';
import {HttpClient, HttpHeaders} from '@angular/common/http'; import {HttpClient, HttpHeaders} from '@angular/common/http';
import {XhrSettings} from 'ionic-appauth/lib/cordova'; import {XhrSettings} from 'ionic-appauth/lib/cordova';
import {Observable} from 'rxjs'; import {firstValueFrom, Observable} from 'rxjs';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@@ -40,7 +55,7 @@ export class NgHttpService implements Requestor {
break; break;
} }
return observable.toPromise(); return firstValueFrom(observable);
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 StApps * Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -215,14 +215,12 @@ export class ScheduleProvider implements OnDestroy {
if (from || to) { if (from || to) {
const bounds: Bounds<string> = {}; const bounds: Bounds<string> = {};
if (from) { if (from) {
console.log(from);
bounds.lowerBound = { bounds.lowerBound = {
limit: from, limit: from,
mode: 'inclusive', mode: 'inclusive',
}; };
} }
if (to) { if (to) {
console.log(to);
bounds.upperBound = { bounds.upperBound = {
limit: to, limit: to,
mode: 'inclusive', mode: 'inclusive',

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 StApps * Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 StApps * Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 StApps * Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -31,6 +31,8 @@ import {StorageProvider} from '../storage/storage.provider';
import {DataModule} from './data.module'; import {DataModule} from './data.module';
import {DataProvider, DataScope} from './data.provider'; import {DataProvider, DataScope} from './data.provider';
import {StAppsWebHttpClient} from './stapps-web-http-client.provider'; import {StAppsWebHttpClient} from './stapps-web-http-client.provider';
import {LoggerModule, NgxLoggerLevel} from 'ngx-logger';
import {RouterModule} from '@angular/router';
describe('DataProvider', () => { describe('DataProvider', () => {
let dataProvider: DataProvider; let dataProvider: DataProvider;
@@ -82,7 +84,7 @@ describe('DataProvider', () => {
beforeEach(async () => { beforeEach(async () => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [DataModule], imports: [DataModule, LoggerModule.forRoot({level: NgxLoggerLevel.TRACE}), RouterModule.forRoot([])],
providers: [DataProvider, StAppsWebHttpClient], providers: [DataProvider, StAppsWebHttpClient],
}); });
storageProvider = TestBed.inject(StorageProvider); storageProvider = TestBed.inject(StorageProvider);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 StApps * Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -27,6 +27,7 @@ import {DataDetailComponent} from './data-detail.component';
import {By} from '@angular/platform-browser'; import {By} from '@angular/platform-browser';
import {Observable, of} from 'rxjs'; import {Observable, of} from 'rxjs';
import {StorageProvider} from '../../storage/storage.provider'; import {StorageProvider} from '../../storage/storage.provider';
import {LoggerModule, NgxLoggerLevel} from 'ngx-logger';
const translations: any = {data: {detail: {TITLE: 'Foo'}}}; const translations: any = {data: {detail: {TITLE: 'Foo'}}};
@@ -70,6 +71,7 @@ describe('DataDetailComponent', () => {
TranslateModule.forRoot({ TranslateModule.forRoot({
loader: {provide: TranslateLoader, useClass: TranslateFakeLoader}, loader: {provide: TranslateLoader, useClass: TranslateFakeLoader},
}), }),
LoggerModule.forRoot({level: NgxLoggerLevel.TRACE}),
], ],
providers: [ providers: [
{ {

View File

@@ -229,13 +229,7 @@ export class SearchPageComponent implements OnInit, OnDestroy {
})(); })();
} }
} catch (error) { } catch (error) {
const alert: HTMLIonAlertElement = await this.alertController.create({ this.logger.error(error);
buttons: ['Dismiss'],
header: 'Error',
subHeader: (error as Error).message,
});
await alert.present();
} finally { } finally {
this.loading = false; this.loading = false;
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 StApps * Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -15,6 +15,17 @@
import {HttpClient, HttpResponse} from '@angular/common/http'; import {HttpClient, HttpResponse} from '@angular/common/http';
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {HttpClientInterface, HttpClientRequest} from '@openstapps/api/lib/http-client-interface'; import {HttpClientInterface, HttpClientRequest} from '@openstapps/api/lib/http-client-interface';
import {map, retry} from 'rxjs/operators';
import {lastValueFrom, Observable} from 'rxjs';
import {InternetConnectionService} from '../../util/internet-connection.service';
type HttpRequestFunctions = InstanceType<typeof HttpClient>['request'];
type HttpRequestFunction<T extends ReturnType<HttpRequestFunctions>> = Extract<
HttpRequestFunctions,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(...parameters: any[]) => T
>;
type HttpRequestParameters<T extends ReturnType<HttpRequestFunctions>> = Parameters<HttpRequestFunction<T>>;
/** /**
* HttpClient that is based on the Angular HttpClient (@TODO: move it to provider or independent package) * HttpClient that is based on the Angular HttpClient (@TODO: move it to provider or independent package)
@@ -23,9 +34,11 @@ import {HttpClientInterface, HttpClientRequest} from '@openstapps/api/lib/http-c
export class StAppsWebHttpClient implements HttpClientInterface { export class StAppsWebHttpClient implements HttpClientInterface {
/** /**
* *
* @param http TODO
*/ */
constructor(private readonly http: HttpClient) {} constructor(
private readonly http: HttpClient,
private readonly connectionService: InternetConnectionService,
) {}
/** /**
* Make a request * Make a request
@@ -33,42 +46,30 @@ export class StAppsWebHttpClient implements HttpClientInterface {
* @param requestConfig Configuration of the request * @param requestConfig Configuration of the request
*/ */
async request<TYPE_OF_BODY>(requestConfig: HttpClientRequest): Promise<Response<TYPE_OF_BODY>> { async request<TYPE_OF_BODY>(requestConfig: HttpClientRequest): Promise<Response<TYPE_OF_BODY>> {
const options: { const request: HttpRequestParameters<Observable<HttpResponse<TYPE_OF_BODY>>> = [
/** requestConfig.method || 'GET',
* TODO requestConfig.url.toString(),
*/ {
[key: string]: unknown; body: (requestConfig.body || {}) as TYPE_OF_BODY,
/** headers: requestConfig.headers,
* TODO observe: 'response',
*/ responseType: 'json',
observe: 'response'; },
} = { ];
body: {}, // TODO: cache requests by hashing the parameters.
observe: 'response',
responseType: 'json',
};
if (typeof requestConfig.body !== 'undefined') { const response: Observable<Response<TYPE_OF_BODY>> = this.http.request(...request).pipe(
options.body = requestConfig.body; retry(this.connectionService.retryConfig),
} map(
response =>
Object.assign(response, {
statusCode: response.status,
body: response.body || {},
}) as Response<TYPE_OF_BODY>,
),
);
if (typeof requestConfig.headers !== 'undefined') { return lastValueFrom(response);
options.headers = requestConfig.headers;
}
try {
const response: HttpResponse<TYPE_OF_BODY> = await this.http
.request<TYPE_OF_BODY>(requestConfig.method || 'GET', requestConfig.url.toString(), options)
.toPromise();
// eslint-disable-next-line prefer-object-spread
return Object.assign(response, {
statusCode: response.status,
body: response.body || {},
});
} catch (error) {
throw new Error(error as string);
}
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 StApps * Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 StApps * Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -28,6 +28,7 @@ import {Observable, of} from 'rxjs';
import {StorageProvider} from '../../storage/storage.provider'; import {StorageProvider} from '../../storage/storage.provider';
import {IonicModule} from '@ionic/angular'; import {IonicModule} from '@ionic/angular';
import {IonIconModule} from '../../../util/ion-icon/ion-icon.module'; import {IonIconModule} from '../../../util/ion-icon/ion-icon.module';
import {LoggerModule, NgxLoggerLevel} from 'ngx-logger';
const translations: any = {data: {detail: {TITLE: 'Foo'}}}; const translations: any = {data: {detail: {TITLE: 'Foo'}}};
@@ -72,6 +73,7 @@ describe('HebisDetailComponent', () => {
TranslateModule.forRoot({ TranslateModule.forRoot({
loader: {provide: TranslateLoader, useClass: TranslateFakeLoader}, loader: {provide: TranslateLoader, useClass: TranslateFakeLoader},
}), }),
LoggerModule.forRoot({level: NgxLoggerLevel.TRACE}),
], ],
providers: [ providers: [
{ {

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 StApps * Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2019-2021 StApps * Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -20,9 +20,10 @@ import {HttpClientModule} from '@angular/common/http';
import {StorageProvider} from '../storage/storage.provider'; import {StorageProvider} from '../storage/storage.provider';
import {MapModule} from './map.module'; import {MapModule} from './map.module';
import {StorageModule} from '../storage/storage.module'; import {StorageModule} from '../storage/storage.module';
import {LoggerConfig, LoggerModule, NGXLogger} from 'ngx-logger'; import {LoggerModule, NGXLogger, NgxLoggerLevel} from 'ngx-logger';
import {ConfigProvider} from '../config/config.provider'; import {ConfigProvider} from '../config/config.provider';
import {sampleDefaultPolygon} from '../../_helpers/data/sample-configuration'; import {sampleDefaultPolygon} from '../../_helpers/data/sample-configuration';
import {RouterModule} from '@angular/router';
describe('MapProvider', () => { describe('MapProvider', () => {
let provider: MapProvider; let provider: MapProvider;
@@ -31,7 +32,13 @@ describe('MapProvider', () => {
beforeEach(() => { beforeEach(() => {
configProvider = jasmine.createSpyObj('ConfigProvider', ['getValue']); configProvider = jasmine.createSpyObj('ConfigProvider', ['getValue']);
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [MapModule, HttpClientModule, StorageModule, LoggerModule], imports: [
MapModule,
HttpClientModule,
StorageModule,
LoggerModule.forRoot({level: NgxLoggerLevel.TRACE}),
RouterModule.forRoot([]),
],
providers: [ providers: [
{ {
provide: ConfigProvider, provide: ConfigProvider,
@@ -40,7 +47,6 @@ describe('MapProvider', () => {
StAppsWebHttpClient, StAppsWebHttpClient,
StorageProvider, StorageProvider,
NGXLogger, NGXLogger,
LoggerConfig,
], ],
}); });

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 StApps * Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -40,7 +40,7 @@ export class ContextMenuService {
/** /**
* Container for the filter query (SCSearchFilter) * Container for the filter query (SCSearchFilter)
*/ */
filterQuery = new Subject<SCSearchFilter>(); filterQuery = new Subject<SCSearchFilter | undefined>();
/** /**
* Observable filterContext streams * Observable filterContext streams
@@ -65,7 +65,7 @@ export class ContextMenuService {
/** /**
* Container for the sort query * Container for the sort query
*/ */
sortQuery = new Subject<SCSearchSort[]>(); sortQuery = new Subject<SCSearchSort[] | undefined>();
/** /**
* Observable SortContext streams * Observable SortContext streams

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2018, 2019 StApps * Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.

View File

@@ -13,6 +13,7 @@
~ this program. If not, see <https://www.gnu.org/licenses/>. ~ this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
<stapps-offline-notice></stapps-offline-notice>
<ion-split-pane contentId="main" when="lg"> <ion-split-pane contentId="main" when="lg">
<ion-menu menuId="main" contentId="main" type="overlay" side="start" swipe-gesture="false"> <ion-menu menuId="main" contentId="main" type="overlay" side="start" swipe-gesture="false">
<ion-header> <ion-header>

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2022 StApps * Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -21,9 +21,10 @@ import {IonicModule} from '@ionic/angular';
import {IonIconModule} from '../../../util/ion-icon/ion-icon.module'; import {IonIconModule} from '../../../util/ion-icon/ion-icon.module';
import {TranslateModule} from '@ngx-translate/core'; import {TranslateModule} from '@ngx-translate/core';
import {RouterModule} from '@angular/router'; import {RouterModule} from '@angular/router';
import {OfflineNoticeComponent} from './offline-notice.component';
@NgModule({ @NgModule({
declarations: [RootLinkDirective, NavigationComponent, TabsComponent], declarations: [RootLinkDirective, NavigationComponent, TabsComponent, OfflineNoticeComponent],
imports: [CommonModule, IonicModule, IonIconModule, TranslateModule, RouterModule], imports: [CommonModule, IonicModule, IonIconModule, TranslateModule, RouterModule],
exports: [TabsComponent, RootLinkDirective, NavigationComponent], exports: [TabsComponent, RootLinkDirective, NavigationComponent],
}) })

View File

@@ -1,5 +1,5 @@
/*! /*!
* Copyright (C) 2022 StApps * Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free * under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3. * Software Foundation, version 3.
@@ -21,8 +21,14 @@ stapps-navigation-tabs {
} }
} }
stapps-offline-notice.has-error ~ ion-split-pane,
stapps-offline-notice.is-offline ~ ion-split-pane {
margin-top: calc(var(--font-size-md) + 2 * var(--spacing-sm));
}
:host { :host {
ion-split-pane { ion-split-pane {
transition: margin-top 150ms ease;
--side-max-width: 256px; --side-max-width: 256px;
margin-bottom: calc(var(--ion-tabbar-height) + env(safe-area-inset-bottom)); margin-bottom: calc(var(--ion-tabbar-height) + env(safe-area-inset-bottom));

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Component, ElementRef, HostBinding, OnDestroy, ViewChild} from '@angular/core';
import {InternetConnectionService} from '../../../util/internet-connection.service';
import {Subscription} from 'rxjs';
import {Router} from '@angular/router';
import {NGXLogger} from 'ngx-logger';
@Component({
selector: 'stapps-offline-notice',
templateUrl: 'offline-notice.html',
styleUrls: ['offline-notice.scss'],
})
export class OfflineNoticeComponent implements OnDestroy {
@HostBinding('class.is-offline') isOffline = false;
@HostBinding('class.has-error') hasError = false;
@ViewChild('spinIcon', {read: ElementRef}) spinIcon: ElementRef;
readonly subscriptions: Subscription[];
constructor(
readonly offlineProvider: InternetConnectionService,
readonly router: Router,
readonly logger: NGXLogger,
) {
this.subscriptions = [
this.offlineProvider.offline$.subscribe(isOffline => {
this.isOffline = isOffline;
}),
this.offlineProvider.error$.subscribe(hasError => {
this.hasError = hasError;
}),
];
}
retry() {
this.spinIcon.nativeElement.classList.remove('spin');
this.spinIcon.nativeElement.offsetWidth;
this.spinIcon.nativeElement.classList.add('spin');
this.offlineProvider.retry();
}
ngOnDestroy() {
for (const subscription of this.subscriptions) {
subscription.unsubscribe();
}
}
}

View File

@@ -0,0 +1,25 @@
<!--
~ Copyright (C) 2023 StApps
~ This program is free software: you can redistribute it and/or modify it
~ under the terms of the GNU General Public License as published by the Free
~ Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful, but WITHOUT
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
~ more details.
~
~ You should have received a copy of the GNU General Public License along with
~ this program. If not, see <https://www.gnu.org/licenses/>.
-->
<ion-button class="offline-button" color="warning">
<ion-icon slot="start" size="16" weight="800" name="cloud_off"></ion-icon>
<ion-label>{{ 'app.errors.OFFLINE' | translate }}</ion-label>
</ion-button>
<ion-button class="error-button" color="danger" (click)="retry()">
<ion-icon #spinIcon slot="start" size="16" weight="800" name="refresh"></ion-icon>
<ion-label>{{ 'app.errors.CONNECTION_ERROR' | translate }}</ion-label>
</ion-button>
<ion-button class="close" fill="clear" color="light" (click)="offlineProvider.dismissError()"
><ion-icon size="16" weight="800" name="close" slot="icon-only"></ion-icon
></ion-button>

View File

@@ -0,0 +1,77 @@
/*!
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
:host {
display: grid;
$height: calc(var(--font-size-md) + 2 * var(--spacing-sm));
height: $height;
width: 100%;
line-height: var(--font-size-md);
font-size: var(--font-size-md);
font-weight: bold;
transform: translateY(calc(-1 * $height));
transition: all 150ms ease;
&.is-offline,
&.has-error {
transform: translateY(0px);
}
> ion-button {
grid-row: 1;
grid-column: 1;
margin: 0;
--border-radius: 0;
opacity: 0;
--padding-top: 0;
--padding-bottom: 0;
transition: all 150ms ease;
z-index: 0;
&.close {
height: 100%;
margin: 0;
position: absolute;
right: 0;
top: 50%;
bottom: 0;
transform: translateY(-50%);
z-index: 1;
color: var(--ion-color-danger-contrast);
}
}
&.is-offline > .offline-button,
&.has-error > .close,
&.has-error > .error-button {
opacity: 1;
}
}
.spin {
animation: loading 1s ease running 3;
}
@keyframes loading {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

View File

@@ -1,21 +0,0 @@
/*
* Copyright (C) 2020 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Injectable} from '@angular/core';
/**
* MenuService provides bidirectional communication of context menu options and search queries
*/
@Injectable()
export class ScheduleService {}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2023 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {fromEvent, merge, ObservableInput, of, race, RetryConfig, share, Subject, takeUntil} from 'rxjs';
import {Injectable} from '@angular/core';
import {filter, map, startWith, take, tap} from 'rxjs/operators';
import {NGXLogger} from 'ngx-logger';
import {Router} from '@angular/router';
@Injectable({
providedIn: 'root',
})
export class InternetConnectionService {
private readonly manualRetry$ = new Subject<void>();
private readonly abortRetry$ = new Subject<void>();
/**
* Emits whenever the browser goes online or offline.
*/
readonly offline$ = window
? merge(
fromEvent(window, 'online').pipe(map(() => false)),
fromEvent(window, 'offline').pipe(map(() => true)),
).pipe(startWith(!window.navigator.onLine), share())
: of(true);
/**
* Emits whenever http requests should be retried
*
* Also keeps track of when a retry is needed, automatically
* registering itself.
*/
readonly retryConfig: RetryConfig = {
delay: this.doRetry.bind(this),
};
private doRetry(error: unknown, retryCount: number): ObservableInput<unknown> {
return race(
this.offline$.pipe(
tap(it => console.log(it)),
filter(it => !it),
take(1),
),
this.manualRetry$,
).pipe(
tap({
subscribe: () => {
this.errors.add(error);
if (this.errors.size > 0) {
this.error$.next(true);
}
},
next: () => {
this.logger.error(`${retryCount}x`, error);
},
unsubscribe: () => {
this.errors.delete(error);
if (this.errors.size === 0) {
this.error$.next(false);
}
},
}),
takeUntil(
merge(
this.abortRetry$.pipe(tap(() => this.logger.warn('HTTP Request retry aborted manually'))),
this.router.events.pipe(tap(() => this.logger.warn('HTTP Request retry aborted by routing'))),
),
),
);
}
/**
* Emits when there are errors
*/
readonly error$ = new Subject<boolean>();
private readonly errors = new Set<unknown>();
constructor(private readonly logger: NGXLogger, private readonly router: Router) {}
/**
* Retry all failed http requests
*/
retry() {
this.manualRetry$.next();
}
/**
* Abandon all failed http requests
*/
dismissError() {
this.abortRetry$.next();
}
}

View File

@@ -27,7 +27,9 @@
}, },
"errors": { "errors": {
"SERVICE": "Fehler bei Dienstausführung.", "SERVICE": "Fehler bei Dienstausführung.",
"UNKNOWN": "Unbekannter Fehler." "UNKNOWN": "Unbekannter Fehler.",
"OFFLINE": "Keine Internetverbindung",
"CONNECTION_ERROR": "Verbindungsfehler"
} }
}, },
"assessments": { "assessments": {

View File

@@ -27,7 +27,9 @@
}, },
"errors": { "errors": {
"SERVICE": "Service error.", "SERVICE": "Service error.",
"UNKNOWN": "Unknown problem." "UNKNOWN": "Unknown problem.",
"OFFLINE": "No internet connection",
"CONNECTION_ERROR": "Connection error"
} }
}, },
"assessments": { "assessments": {

Binary file not shown.