@sumaris-net/ngx-components 0.25.1 → 0.25.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6944,6 +6944,9 @@ class StartableService {
6944
6944
  get started() {
6945
6945
  return this._started;
6946
6946
  }
6947
+ get starting() {
6948
+ return !!this._startPromise;
6949
+ }
6947
6950
  ready() {
6948
6951
  if (this._started)
6949
6952
  return Promise.resolve(this._data);
@@ -8165,6 +8168,19 @@ let Peer = Peer_1 = class Peer extends Entity {
8165
8168
  path: noTrailingSlash(url.pathname)
8166
8169
  });
8167
8170
  }
8171
+ static path(peer, ...paths) {
8172
+ if (!peer)
8173
+ throw new Error('Missing required argument \'peer\'!');
8174
+ // Remove starting slashes
8175
+ paths = (paths || []).map(path => {
8176
+ if (path.startsWith('./'))
8177
+ return path.substring(2);
8178
+ if (path.startsWith('/'))
8179
+ return path.substring(1);
8180
+ return path;
8181
+ }).filter(isNotNilOrBlank);
8182
+ return [noTrailingSlash(Peer_1.fromObject(peer).url)].concat(...paths).join('/');
8183
+ }
8168
8184
  asObject(options) {
8169
8185
  return super.asObject(options);
8170
8186
  }
@@ -9593,7 +9609,7 @@ class NetworkService extends StartableService {
9593
9609
  addListener(name, callback) {
9594
9610
  this._listeners[name] = this._listeners[name] || [];
9595
9611
  this._listeners[name].push(callback);
9596
- // When unsubcribe, remove from the listener
9612
+ // When unsubscribe, remove from the listener
9597
9613
  return new Subscription(() => {
9598
9614
  const index = this._listeners[name].indexOf(callback);
9599
9615
  if (index !== -1) {
@@ -11035,7 +11051,7 @@ function restoreTrackedQueries(opts) {
11035
11051
  const APP_GRAPHQL_TYPE_POLICIES = new InjectionToken('graphqlTypePolicies');
11036
11052
  class GraphqlService extends StartableService {
11037
11053
  constructor(platform, apollo, httpLink, network, storage, cryptoService, environment, typePolicies) {
11038
- super(network); // Wait network
11054
+ super(platform); // Wait network
11039
11055
  this.platform = platform;
11040
11056
  this.apollo = apollo;
11041
11057
  this.httpLink = httpLink;
@@ -11050,13 +11066,6 @@ class GraphqlService extends StartableService {
11050
11066
  this.customErrors = {};
11051
11067
  this._debug = !environment.production;
11052
11068
  this._defaultFetchPolicy = environment.apolloFetchPolicy;
11053
- // Restart if network restart
11054
- this.network.on('start', () => this.restart());
11055
- // Clear cache
11056
- this.network.on('resetCache', () => __awaiter(this, void 0, void 0, function* () {
11057
- yield this.ready();
11058
- yield this.clearCache();
11059
- }));
11060
11069
  // Listen network status
11061
11070
  this._networkStatusChanged$ = network.onNetworkStatusChanges
11062
11071
  .pipe(filter(isNotNil), distinctUntilChanged());
@@ -11461,10 +11470,11 @@ class GraphqlService extends StartableService {
11461
11470
  }
11462
11471
  clearCache(client) {
11463
11472
  return __awaiter(this, void 0, void 0, function* () {
11464
- client = (client || this.apollo.client);
11473
+ client = (client || this.client);
11465
11474
  if (client) {
11466
- const now = this._debug && Date.now();
11467
11475
  console.info('[graphql] Clearing Apollo client\'s cache... ');
11476
+ const now = this._debug && Date.now();
11477
+ // Clearing the cache
11468
11478
  yield client.cache.reset();
11469
11479
  if (this._debug)
11470
11480
  console.debug(`[graphql] Apollo client's cache cleared, in ${Date.now() - now}ms`);
@@ -11477,6 +11487,7 @@ class GraphqlService extends StartableService {
11477
11487
  /* -- protected methods -- */
11478
11488
  ngOnStart() {
11479
11489
  return __awaiter(this, void 0, void 0, function* () {
11490
+ yield this.network.ready();
11480
11491
  console.info('[graphql] Starting graphql...');
11481
11492
  const mobile = this.platform.is('mobile') || this.platform.is('mobileweb');
11482
11493
  const enableTrackMutationQueries = !mobile;
@@ -11612,6 +11623,10 @@ class GraphqlService extends StartableService {
11612
11623
  console.error('[graphql] Failed to restore tracked queries from storage: ' + (err && err.message || err), err);
11613
11624
  }
11614
11625
  }
11626
+ // Listen for network restart
11627
+ this._subscription.add(this.network.on('start', () => this.restart()));
11628
+ // Listen for clear cache request, from network
11629
+ this._subscription.add(this.network.on('resetCache', () => this.clearCache()));
11615
11630
  console.info('[graphql] Starting graphql [OK]');
11616
11631
  return client;
11617
11632
  });
@@ -13644,6 +13659,7 @@ class PlatformService {
13644
13659
  this.browser = browser;
13645
13660
  this.downloader = downloader;
13646
13661
  this._started = false;
13662
+ this._downloadingPromise = new Map();
13647
13663
  this._debug = !environment.production;
13648
13664
  if (this._debug)
13649
13665
  console.debug('[platform] Creating service');
@@ -13669,7 +13685,7 @@ class PlatformService {
13669
13685
  return this._android && this._cordova;
13670
13686
  }
13671
13687
  get canDownload() {
13672
- return !!this.downloader;
13688
+ return this._android && !!this.downloader;
13673
13689
  }
13674
13690
  width() {
13675
13691
  return this.platform.width();
@@ -13712,7 +13728,7 @@ class PlatformService {
13712
13728
  .then(() => {
13713
13729
  this._started = true;
13714
13730
  this._startPromise = undefined;
13715
- console.info(`[platform] Starting platform [OK] {mobile: ${this._mobile}, touchUi: ${this.touchUi}, downloader: ${this.canDownload} in ${Date.now() - now}ms`);
13731
+ console.info(`[platform] Starting platform [OK] {mobile: ${this._mobile}, touchUi: ${this.touchUi}, canDownload: ${this.canDownload}} in ${Date.now() - now}ms`);
13716
13732
  // Update cache configuration when network changed
13717
13733
  this.networkService.onNetworkStatusChanges.subscribe((type) => this.configureCache(type !== 'none'));
13718
13734
  // Update authentication type
@@ -13759,26 +13775,44 @@ class PlatformService {
13759
13775
  window.open(url, target, features, replace);
13760
13776
  }
13761
13777
  }
13778
+ getDownloadingPromise(uri) {
13779
+ return __awaiter(this, void 0, void 0, function* () {
13780
+ return this._downloadingPromise.get(uri);
13781
+ });
13782
+ }
13762
13783
  download(request) {
13763
13784
  return __awaiter(this, void 0, void 0, function* () {
13764
13785
  if (!request || !request.uri)
13765
13786
  throw new Error('Missing argument \'request\' or \'request.uri\'');
13766
- if (this.downloader) {
13767
- request = Object.assign(Object.assign({ visibleInDownloadsUi: true, notificationVisibility: NotificationVisibility.VisibleNotifyCompleted, title: request.filename || '' }, request), { destinationInExternalFilesDir: Object.assign({ dirType: 'Downloads', subPath: request.filename || request.title || '' }, request.destinationInExternalFilesDir) });
13787
+ if (this._android && this.downloader) {
13788
+ // Check if not already downloading file
13789
+ let promise = this._downloadingPromise.get(request.uri);
13790
+ if (promise)
13791
+ return promise;
13792
+ request = Object.assign({ visibleInDownloadsUi: true, notificationVisibility: NotificationVisibility.VisibleNotifyCompleted, title: request.title || request.filename }, request);
13768
13793
  try {
13769
- const location = yield this.downloader.download(request);
13794
+ console.debug('[platform] Downloading, using request: ' + JSON.stringify(request));
13795
+ // Start download
13796
+ promise = this.downloader.download(request);
13797
+ // Remember this request uri
13798
+ this._downloadingPromise.set(request.uri, promise);
13799
+ const location = yield promise;
13770
13800
  console.info('[platform] File successfully downloaded at:' + location);
13771
13801
  return location;
13772
13802
  }
13773
13803
  catch (err) {
13774
- console.error('[platform] Error while downloading: ' + (err && err.message || err), err);
13804
+ console.error('[platform] Unable to download: ' + (err && err.message || err));
13775
13805
  throw err;
13776
13806
  }
13807
+ finally {
13808
+ // Forget the request
13809
+ this._downloadingPromise.delete(request.uri);
13810
+ }
13777
13811
  }
13778
- // Fallback (no Cordova)
13779
- // Web mode: open URI using the browser
13812
+ // Fallback (if not Android and Cordova): opening the URI using the browser
13813
+ // TODO: find a way to download under iOS (see https://www.c-sharpcorner.com/article/how-to-download-a-file-using-file-transfer-plugin-in-ionic-3/)
13780
13814
  else {
13781
- console.warn('[platform] Cannot use Cordova downloader: using browser open()');
13815
+ console.warn('[platform] Cannot use Android downloader: using browser open()');
13782
13816
  this.open(request.uri, '_system', 'location=no', true);
13783
13817
  return undefined;
13784
13818
  }
@@ -18446,9 +18480,12 @@ const MimeTypes = Object.freeze({
18446
18480
  ANDROID_APK: 'application/vnd.android.package-archive'
18447
18481
  });
18448
18482
  class AppInstallUpgradeCard {
18449
- constructor(modalCtrl, configService, cd, platform, network, environment) {
18483
+ constructor(modalCtrl, configService, toastController, alertController, translate, cd, platform, network, environment) {
18450
18484
  this.modalCtrl = modalCtrl;
18451
18485
  this.configService = configService;
18486
+ this.toastController = toastController;
18487
+ this.alertController = alertController;
18488
+ this.translate = translate;
18452
18489
  this.cd = cd;
18453
18490
  this.platform = platform;
18454
18491
  this.network = network;
@@ -18477,18 +18514,8 @@ class AppInstallUpgradeCard {
18477
18514
  this.offline = this.network.offline;
18478
18515
  // Listen pod config
18479
18516
  this._subscription.add(this.configService.config
18480
- .subscribe(config => {
18481
- console.info('[install] Checking if upgrade or install is need...');
18482
- const installLinks = this.getAllInstallLinks(config);
18483
- // Check for upgrade
18484
- this.updateLinks = this.getCompatibleUpgradeLinks(installLinks, config);
18485
- // Check for install links (if no upgrade need)
18486
- this.installLinks = !this.updateLinks && this.getCompatibleInstallLinks(installLinks);
18487
- setTimeout(() => {
18488
- this.loading = false;
18489
- this.markForCheck();
18490
- }, 2000); // Add a delay, for animation
18491
- }));
18517
+ .pipe(debounceTime(1000), filter(() => this.network.online))
18518
+ .subscribe(config => this.checkNeedInstallOrUpdate(config)));
18492
18519
  // Listen network changes
18493
18520
  this._subscription.add(this.network.onNetworkStatusChanges
18494
18521
  .pipe(
@@ -18496,42 +18523,50 @@ class AppInstallUpgradeCard {
18496
18523
  //tap(() => this.waitingNetwork = false),
18497
18524
  map(connectionType => connectionType === 'none'), distinctUntilChanged())
18498
18525
  .subscribe(offline => {
18499
- this.offline = offline;
18500
- this.markForCheck();
18526
+ if (this.offline !== offline) {
18527
+ this.offline = offline;
18528
+ this.markForCheck();
18529
+ }
18501
18530
  }));
18502
18531
  });
18503
18532
  }
18504
18533
  ngOnDestroy() {
18505
18534
  this._subscription.unsubscribe();
18535
+ this._subscription = null;
18536
+ this.onUpdateOfflineModeClick.complete();
18537
+ this.installLinks = null;
18538
+ this.upgradeLinks = null;
18506
18539
  }
18507
- downloadUsingPlatform(event, link) {
18540
+ download(event, link) {
18508
18541
  return __awaiter(this, void 0, void 0, function* () {
18509
18542
  if (!link || !link.url) {
18510
- console.info('[install-upgrade] Invalid link: ' + JSON.stringify(link));
18543
+ console.error('[install-upgrade-card] Missing required argument \'link.url\'');
18511
18544
  return; // Skip
18512
18545
  }
18513
- console.info('[install-upgrade] User ask download of: ' + link.url);
18514
- this.platform.download({
18546
+ const job = this.platform.download({
18515
18547
  uri: link.url,
18516
- filename: link.downloadFilename,
18548
+ title: link.title,
18549
+ filename: link.filename,
18517
18550
  mimeType: link.mimeType
18518
- }).then(location => {
18519
- console.info('[install-upgrade] File downloaded at: ' + location);
18520
- // TODO: display a info toast ?
18521
18551
  });
18552
+ return this.listenDownloadPromise(link, job);
18522
18553
  });
18523
18554
  }
18524
18555
  tryOnline() {
18525
- this.waitingNetwork = true;
18526
- this.markForCheck();
18527
- this.network.tryOnline({
18528
- showLoadingToast: false,
18529
- showOnlineToast: true,
18530
- showOfflineToast: false
18531
- })
18532
- .then(() => {
18533
- this.waitingNetwork = false;
18556
+ return __awaiter(this, void 0, void 0, function* () {
18557
+ this.waitingNetwork = true;
18534
18558
  this.markForCheck();
18559
+ try {
18560
+ yield this.network.tryOnline({
18561
+ showLoadingToast: false,
18562
+ showOnlineToast: true,
18563
+ showOfflineToast: false
18564
+ });
18565
+ }
18566
+ finally {
18567
+ this.waitingNetwork = false;
18568
+ this.markForCheck();
18569
+ }
18535
18570
  });
18536
18571
  }
18537
18572
  getPlatformName(platform) {
@@ -18548,6 +18583,32 @@ class AppInstallUpgradeCard {
18548
18583
  return value;
18549
18584
  }
18550
18585
  /* -- private method -- */
18586
+ checkNeedInstallOrUpdate(config) {
18587
+ return __awaiter(this, void 0, void 0, function* () {
18588
+ console.info('[install] Check if need to install or upgrade...');
18589
+ try {
18590
+ const links = this.getLinks(config);
18591
+ // Check for upgrade
18592
+ this.upgradeLinks = this.getCompatibleUpgradeLinks(links, config);
18593
+ // Check for install links (if no upgrade need)
18594
+ this.installLinks = !this.upgradeLinks && this.getCompatibleInstallLinks(links);
18595
+ yield sleep(1000); // Add a delay, for animation
18596
+ }
18597
+ finally {
18598
+ this.loading = false;
18599
+ this.markForCheck();
18600
+ }
18601
+ // Listening downloading files
18602
+ (this.upgradeLinks || []).forEach(link => {
18603
+ const promise = this.platform.getDownloadingPromise(link.url);
18604
+ // Listening download promise, if exists
18605
+ if (promise) {
18606
+ console.info(`[install-upgrade-card] Platform is already downloading this file \'${link.url}\': listening promise...`);
18607
+ this.listenDownloadPromise(link, promise);
18608
+ }
18609
+ });
18610
+ });
18611
+ }
18551
18612
  getCompatibleInstallLinks(installLinks) {
18552
18613
  // Cordova already running: not need to install
18553
18614
  if (this.platform.is('cordova'))
@@ -18576,7 +18637,7 @@ class AppInstallUpgradeCard {
18576
18637
  });
18577
18638
  return isNotEmptyArray(upgradeLinks) ? upgradeLinks : undefined;
18578
18639
  }
18579
- getAllInstallLinks(config) {
18640
+ getLinks(config) {
18580
18641
  const result = [];
18581
18642
  // Android
18582
18643
  {
@@ -18584,16 +18645,20 @@ class AppInstallUpgradeCard {
18584
18645
  let url = config.getProperty(CORE_CONFIG_OPTIONS.ANDROID_INSTALL_URL);
18585
18646
  if (isNilOrBlank(url))
18586
18647
  url = this.environment.defaultAndroidInstallUrl || null;
18648
+ // Resolve relative URL
18649
+ const peer = this.network.peer;
18650
+ if (peer && (url.startsWith('./') || url.startsWith('/'))) {
18651
+ url = Peer.path(peer, url);
18652
+ }
18587
18653
  const minVersion = config.getProperty(CORE_CONFIG_OPTIONS.APP_MIN_VERSION);
18588
18654
  // Compute App name
18589
18655
  const name = isNotNilOrBlank(url) && config.label || this.environment.defaultAppName || 'SUMARiS';
18590
18656
  if (url) {
18591
- let downloadFilename;
18592
- let version;
18657
+ let title;
18593
18658
  let mimeType;
18594
18659
  // Get file name
18595
- const filename = this.getFilename(url);
18596
- version = minVersion || 'latest';
18660
+ let filename = this.getFilename(url);
18661
+ let version = minVersion || 'latest';
18597
18662
  // OK, this is a downloadable APK file (e.g. NOT a link to a playstore)
18598
18663
  if (filename === null || filename === void 0 ? void 0 : filename.endsWith('.apk')) {
18599
18664
  // Define mime type
@@ -18602,18 +18667,18 @@ class AppInstallUpgradeCard {
18602
18667
  const versionMatches = /-v([1-9][0-9]*\.[0-9]+\.(:?(:?alpha|beta|rc)?[0-9]*))/i.exec(filename);
18603
18668
  version = versionMatches && versionMatches[1] || version;
18604
18669
  // Compute a new file name, with the version
18605
- if (isNotNilOrBlank(name)) {
18606
- downloadFilename = `${name}-v${version}.apk`.toLowerCase();
18670
+ if (isNotNilOrBlank(name) && version !== 'latest') {
18671
+ filename = `${name}-v${version}.apk`.toLowerCase();
18672
+ title = `${name} v${version}`;
18607
18673
  }
18608
18674
  else {
18609
- downloadFilename = filename;
18610
18675
  // Replace 'latest' with the app version, if present
18611
- if (downloadFilename.indexOf('latest')) {
18612
- downloadFilename = downloadFilename.replace('latest', version);
18676
+ if (filename.indexOf('latest')) {
18677
+ filename = filename.replace('latest', version);
18613
18678
  }
18614
18679
  }
18615
18680
  }
18616
- result.push({ name, url, platform: 'android', version, downloadFilename, mimeType });
18681
+ result.push({ name, url, platform: 'android', version, filename, title, mimeType });
18617
18682
  }
18618
18683
  }
18619
18684
  // iOS - TODO
@@ -18637,11 +18702,73 @@ class AppInstallUpgradeCard {
18637
18702
  markForCheck() {
18638
18703
  this.cd.markForCheck();
18639
18704
  }
18705
+ showToast(opts) {
18706
+ return __awaiter(this, void 0, void 0, function* () {
18707
+ yield Toasts.show(this.toastController, this.translate, opts);
18708
+ });
18709
+ }
18710
+ listenDownloadPromise(link, promise) {
18711
+ return __awaiter(this, void 0, void 0, function* () {
18712
+ if (!link || !promise)
18713
+ return; // Skip
18714
+ try {
18715
+ // Mark link as loading
18716
+ if (!link.downloading) {
18717
+ link.downloading = true;
18718
+ this.markForCheck();
18719
+ }
18720
+ const location = yield promise;
18721
+ console.info('[install-upgrade-card] File downloaded at: ' + location);
18722
+ }
18723
+ // Failed: display a toast
18724
+ catch (err) {
18725
+ console.error('[install-upgrade] Download failed: ' + (err && err.message || err));
18726
+ this.showDownloadFailedToast(err);
18727
+ }
18728
+ finally {
18729
+ link.downloading = false;
18730
+ this.markForCheck();
18731
+ }
18732
+ return this.showDownloadCompleteDialog();
18733
+ });
18734
+ }
18735
+ showDownloadFailedToast(err) {
18736
+ return this.showToast({
18737
+ message: 'ERROR.DOWNLOAD_FAILED',
18738
+ messageParams: { error: err },
18739
+ type: 'error',
18740
+ showCloseButton: true
18741
+ });
18742
+ }
18743
+ showDownloadCompleteDialog() {
18744
+ return __awaiter(this, void 0, void 0, function* () {
18745
+ if (!this._subscription)
18746
+ return; // Skip if component has been destroyed
18747
+ const translations = yield this.translate.get([
18748
+ 'INFO.ALERT_HEADER',
18749
+ 'INFO.DOWNLOAD_UPDATE_SUCCEED',
18750
+ 'COMMON.BTN_CLOSE'
18751
+ ]).toPromise();
18752
+ const alert = yield this.alertController.create({
18753
+ header: translations['INFO.ALERT_HEADER'],
18754
+ message: translations['INFO.DOWNLOAD_UPDATE_SUCCEED'],
18755
+ buttons: [
18756
+ {
18757
+ text: translations['COMMON.BTN_CLOSE'],
18758
+ role: 'cancel',
18759
+ cssClass: 'secondary'
18760
+ }
18761
+ ]
18762
+ });
18763
+ yield alert.present();
18764
+ yield alert.onDidDismiss();
18765
+ });
18766
+ }
18640
18767
  }
18641
18768
  AppInstallUpgradeCard.decorators = [
18642
18769
  { type: Component, args: [{
18643
18770
  selector: 'app-install-upgrade-card',
18644
- template: "\n\n<!-- Offline mode card-->\n<ion-card *ngIf=\"showOfflineWarning && (!loading && offline || waitingNetwork)\"\n color=\"accent\"\n class=\"main ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <ion-text class=\"ion-text-wrap\" *ngIf=\"!waitingNetwork; else waitingNetworkText\">\n <h4>\n <b *ngIf=\"isLogin; else notLogin\" [innerHTML]=\"'NETWORK.INFO.OFFLINE_OR_UNAUTHORIZED'| translate\"></b>\n <ng-template #notLogin>\n <b [innerHTML]=\"'NETWORK.INFO.OFFLINE'| translate\"></b>\n </ng-template>\n </h4>\n <h3>\n <small [innerHTML]=\"'NETWORK.INFO.OFFLINE_HELP'|translate\"></small>\n </h3>\n </ion-text>\n <ng-template #waitingNetworkText>\n <ion-text class=\"ion-text-wrap\" [innerHTML]=\"'NETWORK.INFO.RETRY_TO_CONNECT'| translate\"></ion-text>\n </ng-template>\n </ion-col>\n <ion-col size=\"auto\">\n\n <!-- Retry button -->\n <ion-button *ngIf=\"!waitingNetwork; else waitingSpinner\" color=\"tertiary\" class=\"ion-float-end\"\n (click)=\"tryOnline()\">\n <span translate>NETWORK.BTN_CHECK_ALIVE</span>\n </ion-button>\n\n <!-- Waiting spinner -->\n <ng-template #waitingSpinner>\n <ion-spinner *ngIf=\"waitingNetwork\" color=\"light\"></ion-spinner>\n </ng-template>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Upgrade links -->\n<ion-card *ngIf=\"showUpgradeWarning && !loading && !offline && updateLinks\"\n color=\"accent\"\n class=\"ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row *ngFor=\"let link of updateLinks; last as last\">\n <ion-col>\n <ion-text class=\"ion-text-wrap\">\n <h3>\n <span *ngIf=\"link.version\" [innerHTML]=\"'INFO.UPDATE_APP_TO_VERSION'| translate: link\"></span>\n <span *ngIf=\"!link.version\" [innerHTML]=\"'INFO.UPDATE_APP'| translate: link\"></span>\n </h3>\n <h4>\n <small *ngIf=\"last\" [innerHTML]=\"'INFO.UPDATE_APP_HELP'|translate\"></small>\n </h4>\n </ion-text>\n </ion-col>\n\n <ion-col size=\"auto\">\n <ng-container *ngTemplateOutlet=\"downloadButton; context: { $implicit: link }\"></ng-container>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Install links -->\n<ion-card *ngIf=\"showInstallButton && !loading && !offline && installLinks\"\n color=\"secondary\"\n class=\"ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row *ngFor=\"let link of installLinks; last as last\">\n <ion-col>\n <ion-text class=\"ion-text-wrap\">\n <h3 [innerHTML]=\"'INFO.DOWNLOAD_APP_TITLE'| translate: {name: link.name, platform: getPlatformName(link.platform) }\"></h3>\n <h4><small *ngIf=\"last\" [innerHTML]=\"'INFO.DOWNLOAD_APP_HELP'|translate\"></small></h4>\n </ion-text>\n </ion-col>\n <ion-col size=\"auto\">\n <ng-container *ngTemplateOutlet=\"downloadButton; context: { $implicit: link }\"></ng-container>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Update offline feature card-->\n<ion-card *ngIf=\"showUpdateOfflineFeature && !loading && !offline\"\n color=\"accent\"\n class=\"main ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <ion-text class=\"ion-text-wrap\">\n <h4>\n <b [innerHTML]=\"'NETWORK.INFO.UPDATE_OFFLINE_MODE'| translate\"></b>\n </h4>\n <h3>\n <small [innerHTML]=\"'NETWORK.INFO.UPDATE_OFFLINE_MODE_HELP'|translate\"></small>\n </h3>\n </ion-text>\n </ion-col>\n <ion-col size=\"auto\">\n\n <!-- Retry button -->\n <ion-button color=\"tertiary\" class=\"ion-float-end\"\n (click)=\"onUpdateOfflineModeClick.emit($event)\">\n <span translate>NETWORK.BTN_UPDATE</span>\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Display a download button, depending on the link URL, and the device platform -->\n<ng-template #downloadButton let-link>\n\n <ng-container *ngIf=\"link.downloadFilename; else redirectButton\">\n\n <!-- Download using platform -->\n <ion-button *ngIf=\"platform.canDownload; else webDownloadButton\"\n (click)=\"downloadUsingPlatform($event, asLink(link))\"\n color=\"tertiary\" class=\"ion-float-end\">\n <ion-icon slot=\"start\" name=\"download\"></ion-icon>\n <ion-label translate>COMMON.BTN_DOWNLOAD</ion-label>\n </ion-button>\n\n <!-- Web download button -->\n <ng-template #webDownloadButton>\n <ion-button [download]=\"link.downloadFilename\"\n [href]=\"link.url\"\n color=\"tertiary\" class=\"ion-float-end\" >\n <ion-label translate>COMMON.BTN_DOWNLOAD</ion-label>\n </ion-button>\n </ng-template>\n </ng-container>\n\n <!-- Web redirect button -->\n <ng-template #redirectButton>\n <ion-button [href]=\"link.url\" rel=\"external\" color=\"tertiary\" class=\"ion-float-end\" target=\"_blank\">\n <ion-label translate>COMMON.BTN_SHOW_MORE</ion-label>\n </ion-button>\n </ng-template>\n</ng-template>\n",
18771
+ template: "\n\n<!-- Offline mode card-->\n<ion-card *ngIf=\"showOfflineWarning && (!loading && offline || waitingNetwork)\"\n color=\"accent\"\n class=\"main ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <ion-text class=\"ion-text-wrap\" *ngIf=\"!waitingNetwork; else waitingNetworkText\">\n <h4>\n <b *ngIf=\"isLogin; else notLogin\" [innerHTML]=\"'NETWORK.INFO.OFFLINE_OR_UNAUTHORIZED'| translate\"></b>\n <ng-template #notLogin>\n <b [innerHTML]=\"'NETWORK.INFO.OFFLINE'| translate\"></b>\n </ng-template>\n </h4>\n <h3>\n <small [innerHTML]=\"'NETWORK.INFO.OFFLINE_HELP'|translate\"></small>\n </h3>\n </ion-text>\n <ng-template #waitingNetworkText>\n <ion-text class=\"ion-text-wrap\" [innerHTML]=\"'NETWORK.INFO.RETRY_TO_CONNECT'| translate\"></ion-text>\n </ng-template>\n </ion-col>\n <ion-col size=\"auto\">\n\n <!-- Retry button -->\n <ion-button *ngIf=\"!waitingNetwork; else waitingSpinner\" color=\"tertiary\" class=\"ion-float-end\"\n (click)=\"tryOnline()\">\n <span translate>NETWORK.BTN_CHECK_ALIVE</span>\n </ion-button>\n\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Upgrade links -->\n<ion-card *ngIf=\"showUpgradeWarning && !loading && !offline && upgradeLinks\"\n color=\"accent\"\n class=\"ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row *ngFor=\"let link of upgradeLinks; last as last\">\n <ion-col>\n <ion-text class=\"ion-text-wrap\">\n <h3>\n <span *ngIf=\"link.version\" [innerHTML]=\"'INFO.UPDATE_APP_TO_VERSION'| translate: link\"></span>\n <span *ngIf=\"!link.version\" [innerHTML]=\"'INFO.UPDATE_APP'| translate: link\"></span>\n </h3>\n <h4>\n <small *ngIf=\"last\" [innerHTML]=\"'INFO.UPDATE_APP_HELP'|translate\"></small>\n </h4>\n </ion-text>\n </ion-col>\n\n <ion-col size=\"auto\">\n <ng-container *ngTemplateOutlet=\"downloadButton; context: { $implicit: link }\"></ng-container>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Install links -->\n<ion-card *ngIf=\"showInstallButton && !loading && !offline && installLinks\"\n color=\"secondary\"\n class=\"ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row *ngFor=\"let link of installLinks; last as last\">\n <ion-col>\n <ion-text class=\"ion-text-wrap\">\n <h3 [innerHTML]=\"'INFO.DOWNLOAD_APP_TITLE'| translate: {name: link.name, platform: getPlatformName(link.platform) }\"></h3>\n <h4><small *ngIf=\"last\" [innerHTML]=\"'INFO.DOWNLOAD_APP_HELP'|translate\"></small></h4>\n </ion-text>\n </ion-col>\n <ion-col size=\"auto\">\n <ng-container *ngTemplateOutlet=\"downloadButton; context: { $implicit: link }\"></ng-container>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Update offline feature card-->\n<ion-card *ngIf=\"showUpdateOfflineFeature && !loading && !offline\"\n color=\"accent\"\n class=\"main ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row>\n <ion-col>\n <ion-text class=\"ion-text-wrap\">\n <h4>\n <b [innerHTML]=\"'NETWORK.INFO.UPDATE_OFFLINE_MODE'| translate\"></b>\n </h4>\n <h3>\n <small [innerHTML]=\"'NETWORK.INFO.UPDATE_OFFLINE_MODE_HELP'|translate\"></small>\n </h3>\n </ion-text>\n </ion-col>\n <ion-col size=\"auto\">\n\n <!-- Retry button -->\n <ion-button color=\"tertiary\" class=\"ion-float-end\"\n (click)=\"onUpdateOfflineModeClick.emit($event)\">\n <span translate>NETWORK.BTN_UPDATE</span>\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n</ion-card>\n\n<!-- Display a download button, depending on the link URL, and the device platform -->\n<ng-template #downloadButton let-link>\n\n <ng-container *ngIf=\"link.downloadFilename; else redirectButton\">\n\n <ng-container *ngIf=\"platform.canDownload; else webDownloadButton\">\n <!-- Download using platform -->\n <ion-button (click)=\"download($event, asLink(link))\"\n [disabled]=\"link.downloading\"\n color=\"tertiary\" class=\"ion-float-end\">\n <ion-icon slot=\"start\" *ngIf=\"!link.downloading\" name=\"download\"></ion-icon>\n <ion-spinner slot=\"start\" class=\"ion-no-padding\" *ngIf=\"link.downloading\"></ion-spinner>\n <ion-label translate>COMMON.BTN_DOWNLOAD</ion-label>\n </ion-button>\n </ng-container>\n\n <!-- Web download button -->\n <ng-template #webDownloadButton>\n <ion-button [download]=\"link.downloadFilename\"\n [href]=\"link.url\"\n color=\"tertiary\" class=\"ion-float-end\" >\n <ion-label translate>COMMON.BTN_DOWNLOAD</ion-label>\n </ion-button>\n </ng-template>\n </ng-container>\n\n <!-- Web redirect button -->\n <ng-template #redirectButton>\n <ion-button [href]=\"link.url\" rel=\"external\" color=\"tertiary\" class=\"ion-float-end\" target=\"_blank\">\n <ion-label translate>COMMON.BTN_SHOW_MORE</ion-label>\n </ion-button>\n </ng-template>\n</ng-template>\n\n\n<!-- Waiting spinner -->\n<ng-template #waitingSpinner>\n <ion-spinner color=\"light\"></ion-spinner>\n</ng-template>\n",
18645
18772
  changeDetection: ChangeDetectionStrategy.OnPush,
18646
18773
  animations: [slideUpDownAnimation],
18647
18774
  styles: ["ion-text small{font-size:85%}"]
@@ -18650,6 +18777,9 @@ AppInstallUpgradeCard.decorators = [
18650
18777
  AppInstallUpgradeCard.ctorParameters = () => [
18651
18778
  { type: ModalController },
18652
18779
  { type: ConfigService },
18780
+ { type: ToastController },
18781
+ { type: AlertController },
18782
+ { type: TranslateService },
18653
18783
  { type: ChangeDetectorRef },
18654
18784
  { type: PlatformService },
18655
18785
  { type: NetworkService },