@sumaris-net/ngx-components 1.19.4 → 1.19.5-rc3

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.
@@ -9231,7 +9231,8 @@
9231
9231
  return ToolbarToken;
9232
9232
  }());
9233
9233
  var ToolbarComponent = /** @class */ (function () {
9234
- function ToolbarComponent(router, routerOutlet, cd, progressBarService) {
9234
+ function ToolbarComponent(route, router, routerOutlet, cd, progressBarService) {
9235
+ this.route = route;
9235
9236
  this.router = router;
9236
9237
  this.routerOutlet = routerOutlet;
9237
9238
  this.cd = cd;
@@ -9334,33 +9335,47 @@
9334
9335
  };
9335
9336
  ToolbarComponent.prototype.goBack = function () {
9336
9337
  return __awaiter(this, void 0, void 0, function () {
9338
+ var replaceUrl;
9337
9339
  return __generator(this, function (_a) {
9338
9340
  switch (_a.label) {
9339
9341
  case 0:
9340
9342
  console.debug('[toolbar] calling goBack()');
9341
- if (!this._backHref) return [3 /*break*/, 2];
9342
- return [4 /*yield*/, this.router.navigateByUrl(this._backHref)];
9343
+ if (!this._backHref) return [3 /*break*/, 5];
9344
+ replaceUrl = !this.routerOutlet.canGoBack();
9345
+ if (!this._backHref.startsWith('.')) return [3 /*break*/, 2];
9346
+ return [4 /*yield*/, this.router.navigate([this._backHref], { relativeTo: this.route, replaceUrl: replaceUrl })];
9343
9347
  case 1:
9344
9348
  _a.sent();
9345
- return [3 /*break*/, 7];
9346
- case 2:
9347
- if (!this.routerOutlet.canGoBack()) return [3 /*break*/, 4];
9348
- return [4 /*yield*/, this.routerOutlet.pop()];
9349
+ return [3 /*break*/, 4];
9350
+ case 2: return [4 /*yield*/, this.router.navigateByUrl(this._backHref, { replaceUrl: replaceUrl })];
9349
9351
  case 3:
9350
9352
  _a.sent();
9351
- return [3 /*break*/, 7];
9352
- case 4:
9353
- if (!this._defaultBackHref) return [3 /*break*/, 6];
9354
- return [4 /*yield*/, this.router.navigateByUrl(this._defaultBackHref, {
9355
- replaceUrl: true
9356
- })];
9353
+ _a.label = 4;
9354
+ case 4: return [3 /*break*/, 13];
9357
9355
  case 5:
9358
- _a.sent();
9359
- return [3 /*break*/, 7];
9356
+ if (!this.routerOutlet.canGoBack()) return [3 /*break*/, 7];
9357
+ return [4 /*yield*/, this.routerOutlet.pop()];
9360
9358
  case 6:
9359
+ _a.sent();
9360
+ return [3 /*break*/, 13];
9361
+ case 7:
9362
+ if (!this._defaultBackHref) return [3 /*break*/, 12];
9363
+ if (!this._backHref.startsWith('.')) return [3 /*break*/, 9];
9364
+ return [4 /*yield*/, this.router.navigate([this._backHref], { relativeTo: this.route, replaceUrl: true })];
9365
+ case 8:
9366
+ _a.sent();
9367
+ return [3 /*break*/, 11];
9368
+ case 9: return [4 /*yield*/, this.router.navigateByUrl(this._defaultBackHref, {
9369
+ replaceUrl: true
9370
+ })];
9371
+ case 10:
9372
+ _a.sent();
9373
+ _a.label = 11;
9374
+ case 11: return [3 /*break*/, 13];
9375
+ case 12:
9361
9376
  console.error('[toolbar] Cannot go back. Missing attribute \'defaultBackHref\' or \'backHref\'');
9362
- _a.label = 7;
9363
- case 7: return [2 /*return*/];
9377
+ _a.label = 13;
9378
+ case 13: return [2 /*return*/];
9364
9379
  }
9365
9380
  });
9366
9381
  });
@@ -9420,7 +9435,7 @@
9420
9435
  ToolbarComponent.decorators = [
9421
9436
  { type: i0.Component, args: [{
9422
9437
  selector: 'app-toolbar',
9423
- template: "<ion-header [class]=\"class\">\n <ion-toolbar [color]=\"color\">\n\n <ion-buttons slot=\"start\">\n\n <!-- back button -->\n <ion-button class=\"back-button\"\n *ngIf=\"canGoBack\"\n (click)=\"doBackClick($event)\"\n routerDirection=\"back\" >\n <ion-icon slot=\"icon-only\" name=\"arrow-back\"></ion-icon>\n </ion-button>\n\n <!-- show menu button -->\n <ion-menu-toggle [hidden]=\"canGoBack || !canShowMenu\">\n <ion-button>\n <ion-icon slot=\"icon-only\" name=\"menu\"></ion-icon>\n </ion-button>\n </ion-menu-toggle>\n\n <ng-content select=\"[slot=start]\"></ng-content>\n </ion-buttons>\n\n <ion-title [innerHTML]=\"title\"></ion-title>\n\n <ng-content select=\"ion-title\"></ng-content>\n\n <ng-content select=\"ion-buttons[slot=end]\"></ng-content>\n\n <ion-buttons slot=\"end\" collapse=\"true\">\n\n <ng-content select=\"[slot=end]\"></ng-content>\n\n <!-- search bar -->\n <ion-searchbar #searchbar animated\n type=\"search\"\n *ngIf=\"hasSearch\"\n [class.cdk-visually-hidden]=\"!showSearchBar\"\n show-cancel-button=\"always\"\n (ionChange)=\"onSearch.emit($event)\"\n (ionCancel)=\"toggleSearchBar()\">\n </ion-searchbar>\n\n <!-- search button -->\n <ion-button (click)=\"toggleSearchBar()\"\n *ngIf=\"hasSearch && !showSearchBar\">\n <ion-icon slot=\"icon-only\" name=\"search\"></ion-icon>\n </ion-button>\n\n <!-- close button -->\n <ion-button *ngIf=\"hasClose\"\n (tap)=\"tapClose($event)\"\n class=\"visible-xs visible-sm visible-mobile\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n\n <!-- validate button -->\n <ion-button *ngIf=\"hasValidate\"\n (tap)=\"tapValidate($event)\"\n class=\"visible-xs visible-sm visible-mobile\">\n <ion-icon slot=\"icon-only\" name=\"save\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n\n <!-- progress bar -->\n <mat-progress-bar [color]=\"color\" [mode]=\"progressBarMode$ | async\">\n </mat-progress-bar>\n</ion-header>\n",
9438
+ template: "<ion-header [class]=\"class\">\n <ion-toolbar [color]=\"color\">\n\n <ion-buttons slot=\"start\">\n\n <!-- back button -->\n <ion-button class=\"back-button\"\n *ngIf=\"canGoBack\"\n (click)=\"doBackClick($event)\"\n routerDirection=\"back\" >\n <ion-icon slot=\"icon-only\" name=\"arrow-back\"></ion-icon>\n </ion-button>\n\n <!-- show menu button -->\n <ion-button *ngIf=\"!canGoBack && canShowMenu\">\n <ion-icon slot=\"icon-only\" name=\"menu\"></ion-icon>\n </ion-button>\n<!--\n <ion-menu-toggle [hidden]=\"canGoBack || !canShowMenu\">\n </ion-menu-toggle>\n-->\n\n <ng-content select=\"[slot=start]\"></ng-content>\n </ion-buttons>\n\n <ion-title [innerHTML]=\"title\"></ion-title>\n\n <ng-content select=\"ion-title\"></ng-content>\n\n <ng-content select=\"ion-buttons[slot=end]\"></ng-content>\n\n <ion-buttons slot=\"end\" collapse=\"true\">\n\n <ng-content select=\"[slot=end]\"></ng-content>\n\n <!-- search bar -->\n <ion-searchbar #searchbar animated\n type=\"search\"\n *ngIf=\"hasSearch\"\n [class.cdk-visually-hidden]=\"!showSearchBar\"\n show-cancel-button=\"always\"\n (ionChange)=\"onSearch.emit($event)\"\n (ionCancel)=\"toggleSearchBar()\">\n </ion-searchbar>\n\n <!-- search button -->\n <ion-button (click)=\"toggleSearchBar()\"\n *ngIf=\"hasSearch && !showSearchBar\">\n <ion-icon slot=\"icon-only\" name=\"search\"></ion-icon>\n </ion-button>\n\n <!-- close button -->\n <ion-button *ngIf=\"hasClose\"\n (tap)=\"tapClose($event)\"\n class=\"visible-xs visible-sm visible-mobile\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n\n <!-- validate button -->\n <ion-button *ngIf=\"hasValidate\"\n (tap)=\"tapValidate($event)\"\n class=\"visible-xs visible-sm visible-mobile\">\n <ion-icon slot=\"icon-only\" name=\"save\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n\n <!-- progress bar -->\n <mat-progress-bar [color]=\"color\" [mode]=\"progressBarMode$ | async\">\n </mat-progress-bar>\n</ion-header>\n",
9424
9439
  providers: [
9425
9440
  { provide: ToolbarToken, useExisting: ToolbarComponent }
9426
9441
  ],
@@ -9429,6 +9444,7 @@
9429
9444
  },] }
9430
9445
  ];
9431
9446
  ToolbarComponent.ctorParameters = function () { return [
9447
+ { type: i3.ActivatedRoute },
9432
9448
  { type: i3.Router },
9433
9449
  { type: i1$5.IonRouterOutlet },
9434
9450
  { type: i0.ChangeDetectorRef },
@@ -17888,6 +17904,252 @@
17888
17904
  ]; };
17889
17905
  var templateObject_1$1, templateObject_2$1, templateObject_3$1, templateObject_4$1, templateObject_5$1;
17890
17906
 
17907
+ var UploadFile = /** @class */ (function (_super) {
17908
+ __extends(UploadFile, _super);
17909
+ function UploadFile() {
17910
+ return _super !== null && _super.apply(this, arguments) || this;
17911
+ }
17912
+ return UploadFile;
17913
+ }(File));
17914
+ var FileResponse = /** @class */ (function () {
17915
+ function FileResponse(init) {
17916
+ this.type = i2$1.HttpEventType.Response;
17917
+ this.body = isNotNil(init === null || init === void 0 ? void 0 : init.body) ? init.body : null;
17918
+ this.status = (init === null || init === void 0 ? void 0 : init.status) || 0;
17919
+ this.statusText = (init === null || init === void 0 ? void 0 : init.statusText) || 'OK';
17920
+ }
17921
+ return FileResponse;
17922
+ }());
17923
+ function isProgressEvent(event) {
17924
+ return event && (event.type === i2$1.HttpEventType.UploadProgress
17925
+ || event.type === i2$1.HttpEventType.DownloadProgress);
17926
+ }
17927
+ function isResponseEvent(event) {
17928
+ return event && event.type === i2$1.HttpEventType.Response;
17929
+ }
17930
+
17931
+ var UploadFilePopover = /** @class */ (function () {
17932
+ function UploadFilePopover(popoverController, translate, cd) {
17933
+ this.popoverController = popoverController;
17934
+ this.translate = translate;
17935
+ this.cd = cd;
17936
+ this.uniqueFile = false;
17937
+ this.instantUpload = false;
17938
+ this.importing = false;
17939
+ }
17940
+ Object.defineProperty(UploadFilePopover.prototype, "files", {
17941
+ get: function () {
17942
+ return this.uploader.files;
17943
+ },
17944
+ enumerable: false,
17945
+ configurable: true
17946
+ });
17947
+ Object.defineProperty(UploadFilePopover.prototype, "disabled", {
17948
+ get: function () {
17949
+ return this.importing || isEmptyArray(this.files);
17950
+ },
17951
+ enumerable: false,
17952
+ configurable: true
17953
+ });
17954
+ Object.defineProperty(UploadFilePopover.prototype, "valid", {
17955
+ get: function () {
17956
+ return isNotEmptyArray(this.files);
17957
+ },
17958
+ enumerable: false,
17959
+ configurable: true
17960
+ });
17961
+ UploadFilePopover.prototype.onValidate = function (event) {
17962
+ return __awaiter(this, void 0, void 0, function () {
17963
+ var files, errors, err_1;
17964
+ return __generator(this, function (_a) {
17965
+ switch (_a.label) {
17966
+ case 0:
17967
+ event === null || event === void 0 ? void 0 : event.stopPropagation();
17968
+ // Avoid multiple call
17969
+ if (this.importing)
17970
+ return [2 /*return*/];
17971
+ console.debug('[upload-file-popover] Validate form: will upload all files...');
17972
+ _a.label = 1;
17973
+ case 1:
17974
+ _a.trys.push([1, , 8, 9]);
17975
+ this.importing = true;
17976
+ this.resetError();
17977
+ _a.label = 2;
17978
+ case 2:
17979
+ _a.trys.push([2, 6, , 7]);
17980
+ // Wait processing files finished
17981
+ return [4 /*yield*/, this.uploader.waitIdle()];
17982
+ case 3:
17983
+ // Wait processing files finished
17984
+ _a.sent();
17985
+ // upload all files
17986
+ return [4 /*yield*/, this.uploader.uploadFiles()];
17987
+ case 4:
17988
+ // upload all files
17989
+ _a.sent();
17990
+ files = (this.uploader.processedFiles || [])
17991
+ .filter(function (file) { return !file.deleting; });
17992
+ // Nothing to upload: close popover
17993
+ if (isEmptyArray(files))
17994
+ return [2 /*return*/, this.cancel()];
17995
+ errors = files.map(function (file) { return file.error; }).filter(isNotNilOrBlank);
17996
+ // If error: stop
17997
+ if (errors.length) {
17998
+ errors.forEach(function (error) { return console.error(error); });
17999
+ throw { message: 'FILE.UPLOAD.ERROR' };
18000
+ }
18001
+ // return files
18002
+ return [4 /*yield*/, this.popoverController.dismiss(files)];
18003
+ case 5:
18004
+ // return files
18005
+ _a.sent();
18006
+ return [3 /*break*/, 7];
18007
+ case 6:
18008
+ err_1 = _a.sent();
18009
+ this.error = (err_1 && err_1.message) || err_1;
18010
+ return [3 /*break*/, 7];
18011
+ case 7: return [3 /*break*/, 9];
18012
+ case 8:
18013
+ this.importing = false;
18014
+ this.cd.markForCheck();
18015
+ return [7 /*endfinally*/];
18016
+ case 9: return [2 /*return*/];
18017
+ }
18018
+ });
18019
+ });
18020
+ };
18021
+ UploadFilePopover.prototype.cancel = function () {
18022
+ return this.popoverController.dismiss();
18023
+ };
18024
+ UploadFilePopover.prototype.resetError = function () {
18025
+ if (this.error) {
18026
+ this.error = null;
18027
+ this.cd.markForCheck();
18028
+ }
18029
+ };
18030
+ return UploadFilePopover;
18031
+ }());
18032
+ UploadFilePopover.decorators = [
18033
+ { type: i0.Component, args: [{
18034
+ selector: 'app-upload-file-popover',
18035
+ template: "\n<ion-content class=\"ion-no-padding\" scrollY=\"false\">\n <div class=\"modal-content\">\n <app-upload-file\n #uploader\n [uploadFn]=\"uploadFn\"\n [deleteFn]=\"deleteFn\"\n [fileExtension]=\"fileExtension\"\n [instantUpload]=\"instantUpload\"\n [uniqueFile]=\"uniqueFile\"\n [maxParallelUpload]=\"maxParallelUpload\"\n ></app-upload-file>\n </div>\n</ion-content>\n\n<ion-footer>\n <ion-toolbar>\n <ion-row class=\"ion-no-padding\" nowrap>\n <ion-col>\n <div *ngIf=\"importing\" class=\"importing-info\">\n <ion-text color=\"medium\"><small translate>FILE.UPLOAD.IMPORTING</small></ion-text>\n &nbsp;<ion-spinner name=\"dots\" color=\"accent\"></ion-spinner>\n </div>\n <ion-item *ngIf=\"error\" lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"ion-text-wrap\" [innerHTML]=\"error | translate\"></ion-label>\n </ion-item>\n </ion-col>\n <ion-col size=\"auto\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"cancel()\">\n <ion-label translate>COMMON.BTN_CANCEL</ion-label>\n </ion-button>\n <ion-button\n [fill]=\"disabled ? 'clear' : 'solid'\"\n (click)=\"onValidate($event)\"\n (keyup.enter)=\"onValidate($event)\"\n color=\"tertiary\"\n [disabled]=\"disabled\"\n >\n <ion-label translate>COMMON.BTN_IMPORT</ion-label>\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-toolbar>\n</ion-footer>\n",
18036
+ encapsulation: i0.ViewEncapsulation.None,
18037
+ changeDetection: i0.ChangeDetectionStrategy.OnPush,
18038
+ styles: [".importing-info{-webkit-padding-start:var(--ion-padding);padding-inline-start:var(--ion-padding);display:inline-flex;flex-wrap:nowrap;padding-top:8px}"]
18039
+ },] }
18040
+ ];
18041
+ UploadFilePopover.ctorParameters = function () { return [
18042
+ { type: i1$5.PopoverController },
18043
+ { type: i1$2.TranslateService },
18044
+ { type: i0.ChangeDetectorRef }
18045
+ ]; };
18046
+ UploadFilePopover.propDecorators = {
18047
+ uploader: [{ type: i0.ViewChild, args: ['uploader', { static: true },] }],
18048
+ fileExtension: [{ type: i0.Input }],
18049
+ title: [{ type: i0.Input }],
18050
+ uniqueFile: [{ type: i0.Input }],
18051
+ instantUpload: [{ type: i0.Input }],
18052
+ uploadFn: [{ type: i0.Input }],
18053
+ deleteFn: [{ type: i0.Input }],
18054
+ maxParallelUpload: [{ type: i0.Input }]
18055
+ };
18056
+
18057
+ var MimeTypes = Object.freeze({
18058
+ ANDROID_APK: 'application/vnd.android.package-archive'
18059
+ });
18060
+ var FilesUtils = /** @class */ (function () {
18061
+ function FilesUtils() {
18062
+ }
18063
+ FilesUtils.showUploadPopover = function (popoverController, event, opts) {
18064
+ return __awaiter(this, void 0, void 0, function () {
18065
+ var modal, _a, data, role;
18066
+ return __generator(this, function (_b) {
18067
+ switch (_b.label) {
18068
+ case 0: return [4 /*yield*/, popoverController.create({
18069
+ component: UploadFilePopover,
18070
+ componentProps: opts,
18071
+ backdropDismiss: false,
18072
+ keyboardClose: false,
18073
+ event: event,
18074
+ translucent: true,
18075
+ cssClass: 'popover-large',
18076
+ })];
18077
+ case 1:
18078
+ modal = _b.sent();
18079
+ return [4 /*yield*/, modal.present()];
18080
+ case 2:
18081
+ _b.sent();
18082
+ return [4 /*yield*/, modal.onDidDismiss()];
18083
+ case 3:
18084
+ _a = _b.sent(), data = _a.data, role = _a.role;
18085
+ return [2 /*return*/, {
18086
+ role: role,
18087
+ data: data ? data : undefined
18088
+ }];
18089
+ }
18090
+ });
18091
+ });
18092
+ };
18093
+ FilesUtils.readAsText = function (file, encoding) {
18094
+ var $progress = new rxjs.Subject();
18095
+ var reader = new FileReader();
18096
+ encoding = (encoding || 'UTF-8').toLowerCase();
18097
+ // Listen progress
18098
+ reader.onprogress = function (e) {
18099
+ var total = e.total || 1;
18100
+ var loaded = e.total || 0;
18101
+ if (loaded < total)
18102
+ $progress.next({ type: i2$1.HttpEventType.UploadProgress, loaded: loaded, total: total });
18103
+ };
18104
+ // Listen end
18105
+ reader.onloadend = function (e) {
18106
+ var body = reader.result;
18107
+ if (typeof body === 'string') {
18108
+ $progress.next(new FileResponse({ body: body }));
18109
+ $progress.complete();
18110
+ }
18111
+ else {
18112
+ console.error("Cannot read file '" + file.name + "' as text");
18113
+ $progress.error("Cannot read file as text");
18114
+ }
18115
+ };
18116
+ // Listen error
18117
+ reader.onerror = function (event) {
18118
+ console.error('[files] Error while reading file:', event);
18119
+ $progress.error(event);
18120
+ };
18121
+ // Start reading the file
18122
+ reader.readAsText(file, encoding);
18123
+ return $progress.asObservable();
18124
+ };
18125
+ return FilesUtils;
18126
+ }());
18127
+ var UriUtils = /** @class */ (function () {
18128
+ function UriUtils() {
18129
+ }
18130
+ /**
18131
+ * Extract the filename, from an uri (using the last slash)
18132
+ * @param uri
18133
+ */
18134
+ UriUtils.getFilename = function (uri) {
18135
+ if (!uri)
18136
+ return uri;
18137
+ var filename = uri.trim();
18138
+ // Get last part (or all string, if no '/')
18139
+ var lastSlashIndex = filename.lastIndexOf('/');
18140
+ if (lastSlashIndex !== -1 && lastSlashIndex !== uri.length - 1) {
18141
+ filename = filename.substring(lastSlashIndex + 1);
18142
+ }
18143
+ // Remove query params
18144
+ var queryParamIndex = filename.indexOf('?');
18145
+ if (queryParamIndex !== -1) {
18146
+ filename = filename.substring(0, queryParamIndex);
18147
+ }
18148
+ return filename;
18149
+ };
18150
+ return UriUtils;
18151
+ }());
18152
+
17891
18153
  var moment$4 = momentImported__namespace;
17892
18154
  var AndroidOsEnvironment = Object.freeze({
17893
18155
  DIRECTORY_DOWNLOADS: 'Download',
@@ -18083,34 +18345,48 @@
18083
18345
  };
18084
18346
  PlatformService.prototype.download = function (request) {
18085
18347
  var _this = this;
18348
+ var _a, _b;
18086
18349
  if (!request || !request.uri)
18087
18350
  throw new Error('Missing argument \'request\' or \'request.uri\'');
18088
18351
  if (this._android && this.downloader) {
18089
18352
  // Check if not already downloading file
18090
- var location$ = this._downloadingJobs.get(request.uri);
18091
- if (location$)
18092
- return location$;
18093
- request = Object.assign(Object.assign({ visibleInDownloadsUi: true, notificationVisibility: ngx$6.NotificationVisibility.VisibleNotifyCompleted, title: request.title || request.filename }, request), { destinationInExternalPublicDir: Object.assign({ dirType: AndroidOsEnvironment.DIRECTORY_DOWNLOADS, subPath: request.filename || request.title || '' }, request.destinationInExternalPublicDir) });
18094
- console.debug('[platform] Downloading, using request: ' + JSON.stringify(request));
18353
+ var job$ = this._downloadingJobs.get(request.uri);
18354
+ if (job$)
18355
+ return job$;
18356
+ // Compute subPath (final filename)
18357
+ var subPath = ((_a = request.destinationInExternalPublicDir) === null || _a === void 0 ? void 0 : _a.subPath) || ((_b = request.destinationInExternalFilesDir) === null || _b === void 0 ? void 0 : _b.subPath)
18358
+ || request.filename || UriUtils.getFilename(request.uri)
18359
+ || request.title || 'download';
18360
+ subPath = subPath.toLowerCase()
18361
+ // Remove spaces
18362
+ .replace(/\s+/g, '-');
18363
+ // Force APK to finish with '.apk'
18364
+ if (request.mimeType === MimeTypes.ANDROID_APK && !subPath.toLowerCase().endsWith('.apk')) {
18365
+ subPath += '.apk';
18366
+ }
18367
+ var downloadRequest_1 = Object.assign(Object.assign({ visibleInDownloadsUi: true, notificationVisibility: ngx$6.NotificationVisibility.VisibleNotifyCompleted }, request), { filename: undefined, destinationInExternalPublicDir: !request.destinationInExternalFilesDir
18368
+ ? Object.assign(Object.assign({ dirType: AndroidOsEnvironment.DIRECTORY_DOWNLOADS }, request.destinationInExternalPublicDir), { subPath: subPath }) : undefined, destinationInExternalFilesDir: request.destinationInExternalFilesDir
18369
+ ? Object.assign(Object.assign({ dirType: AndroidOsEnvironment.DIRECTORY_DOWNLOADS }, request.destinationInExternalFilesDir), { subPath: subPath }) : undefined });
18095
18370
  // Start download
18096
- location$ = rxjs.from(this.downloader.download(request));
18371
+ console.debug('[platform] Downloading, using request: ' + JSON.stringify(downloadRequest_1));
18372
+ job$ = rxjs.from(this.downloader.download(downloadRequest_1));
18097
18373
  // Remember this request uri
18098
- this._downloadingJobs.set(request.uri, location$);
18099
- return location$
18374
+ this._downloadingJobs.set(downloadRequest_1.uri, job$);
18375
+ return job$
18100
18376
  .pipe(operators.tap(function (location) {
18101
18377
  console.info('[platform] File successfully downloaded at:' + location);
18102
- // Forget the request
18103
- _this._downloadingJobs.delete(request.uri);
18378
+ // Forget job
18379
+ _this._downloadingJobs.delete(downloadRequest_1.uri);
18104
18380
  return location;
18105
18381
  }), operators.catchError(function (err) {
18106
18382
  var _a;
18107
18383
  console.error('[platform] Unable to download: ' + (err && err.message || err));
18108
18384
  // Forget the request
18109
- _this._downloadingJobs.delete(request.uri);
18385
+ _this._downloadingJobs.delete(downloadRequest_1.uri);
18110
18386
  // Retry with another location - issue in older Android version
18111
- if (((_a = request.destinationInExternalPublicDir) === null || _a === void 0 ? void 0 : _a.dirType) === AndroidOsEnvironment.DIRECTORY_DOWNLOADS) {
18112
- request.destinationInExternalPublicDir.dirType = AndroidOsEnvironment.DIRECTORY_DOWNLOADS_OLD;
18113
- return _this.download(request);
18387
+ if (((_a = downloadRequest_1.destinationInExternalPublicDir) === null || _a === void 0 ? void 0 : _a.dirType) === AndroidOsEnvironment.DIRECTORY_DOWNLOADS) {
18388
+ downloadRequest_1.destinationInExternalPublicDir.dirType = AndroidOsEnvironment.DIRECTORY_DOWNLOADS_OLD;
18389
+ return _this.download(downloadRequest_1);
18114
18390
  }
18115
18391
  throw err;
18116
18392
  }));
@@ -18526,30 +18802,6 @@
18526
18802
  { type: i1$2.TranslateService }
18527
18803
  ]; };
18528
18804
 
18529
- var UploadFile = /** @class */ (function (_super) {
18530
- __extends(UploadFile, _super);
18531
- function UploadFile() {
18532
- return _super !== null && _super.apply(this, arguments) || this;
18533
- }
18534
- return UploadFile;
18535
- }(File));
18536
- var FileResponse = /** @class */ (function () {
18537
- function FileResponse(init) {
18538
- this.type = i2$1.HttpEventType.Response;
18539
- this.body = isNotNil(init === null || init === void 0 ? void 0 : init.body) ? init.body : null;
18540
- this.status = (init === null || init === void 0 ? void 0 : init.status) || 0;
18541
- this.statusText = (init === null || init === void 0 ? void 0 : init.statusText) || 'OK';
18542
- }
18543
- return FileResponse;
18544
- }());
18545
- function isProgressEvent(event) {
18546
- return event && (event.type === i2$1.HttpEventType.UploadProgress
18547
- || event.type === i2$1.HttpEventType.DownloadProgress);
18548
- }
18549
- function isResponseEvent(event) {
18550
- return event && event.type === i2$1.HttpEventType.Response;
18551
- }
18552
-
18553
18805
  var UploadFileComponent = /** @class */ (function () {
18554
18806
  function UploadFileComponent(cd, translate) {
18555
18807
  this.cd = cd;
@@ -18778,132 +19030,6 @@
18778
19030
  maxParallelUpload: [{ type: i0.Input }]
18779
19031
  };
18780
19032
 
18781
- var UploadFilePopover = /** @class */ (function () {
18782
- function UploadFilePopover(popoverController, translate, cd) {
18783
- this.popoverController = popoverController;
18784
- this.translate = translate;
18785
- this.cd = cd;
18786
- this.uniqueFile = false;
18787
- this.instantUpload = false;
18788
- this.importing = false;
18789
- }
18790
- Object.defineProperty(UploadFilePopover.prototype, "files", {
18791
- get: function () {
18792
- return this.uploader.files;
18793
- },
18794
- enumerable: false,
18795
- configurable: true
18796
- });
18797
- Object.defineProperty(UploadFilePopover.prototype, "disabled", {
18798
- get: function () {
18799
- return this.importing || isEmptyArray(this.files);
18800
- },
18801
- enumerable: false,
18802
- configurable: true
18803
- });
18804
- Object.defineProperty(UploadFilePopover.prototype, "valid", {
18805
- get: function () {
18806
- return isNotEmptyArray(this.files);
18807
- },
18808
- enumerable: false,
18809
- configurable: true
18810
- });
18811
- UploadFilePopover.prototype.onValidate = function (event) {
18812
- return __awaiter(this, void 0, void 0, function () {
18813
- var files, errors, err_1;
18814
- return __generator(this, function (_a) {
18815
- switch (_a.label) {
18816
- case 0:
18817
- event === null || event === void 0 ? void 0 : event.stopPropagation();
18818
- // Avoid multiple call
18819
- if (this.importing)
18820
- return [2 /*return*/];
18821
- console.debug('[upload-file-popover] Validate form: will upload all files...');
18822
- _a.label = 1;
18823
- case 1:
18824
- _a.trys.push([1, , 8, 9]);
18825
- this.importing = true;
18826
- this.resetError();
18827
- _a.label = 2;
18828
- case 2:
18829
- _a.trys.push([2, 6, , 7]);
18830
- // Wait processing files finished
18831
- return [4 /*yield*/, this.uploader.waitIdle()];
18832
- case 3:
18833
- // Wait processing files finished
18834
- _a.sent();
18835
- // upload all files
18836
- return [4 /*yield*/, this.uploader.uploadFiles()];
18837
- case 4:
18838
- // upload all files
18839
- _a.sent();
18840
- files = (this.uploader.processedFiles || [])
18841
- .filter(function (file) { return !file.deleting; });
18842
- // Nothing to upload: close popover
18843
- if (isEmptyArray(files))
18844
- return [2 /*return*/, this.cancel()];
18845
- errors = files.map(function (file) { return file.error; }).filter(isNotNilOrBlank);
18846
- // If error: stop
18847
- if (errors.length) {
18848
- errors.forEach(function (error) { return console.error(error); });
18849
- throw { message: 'FILE.UPLOAD.ERROR' };
18850
- }
18851
- // return files
18852
- return [4 /*yield*/, this.popoverController.dismiss(files)];
18853
- case 5:
18854
- // return files
18855
- _a.sent();
18856
- return [3 /*break*/, 7];
18857
- case 6:
18858
- err_1 = _a.sent();
18859
- this.error = (err_1 && err_1.message) || err_1;
18860
- return [3 /*break*/, 7];
18861
- case 7: return [3 /*break*/, 9];
18862
- case 8:
18863
- this.importing = false;
18864
- this.cd.markForCheck();
18865
- return [7 /*endfinally*/];
18866
- case 9: return [2 /*return*/];
18867
- }
18868
- });
18869
- });
18870
- };
18871
- UploadFilePopover.prototype.cancel = function () {
18872
- return this.popoverController.dismiss();
18873
- };
18874
- UploadFilePopover.prototype.resetError = function () {
18875
- if (this.error) {
18876
- this.error = null;
18877
- this.cd.markForCheck();
18878
- }
18879
- };
18880
- return UploadFilePopover;
18881
- }());
18882
- UploadFilePopover.decorators = [
18883
- { type: i0.Component, args: [{
18884
- selector: 'app-upload-file-popover',
18885
- template: "\n<ion-content class=\"ion-no-padding\" scrollY=\"false\">\n <div class=\"modal-content\">\n <app-upload-file\n #uploader\n [uploadFn]=\"uploadFn\"\n [deleteFn]=\"deleteFn\"\n [fileExtension]=\"fileExtension\"\n [instantUpload]=\"instantUpload\"\n [uniqueFile]=\"uniqueFile\"\n [maxParallelUpload]=\"maxParallelUpload\"\n ></app-upload-file>\n </div>\n</ion-content>\n\n<ion-footer>\n <ion-toolbar>\n <ion-row class=\"ion-no-padding\" nowrap>\n <ion-col>\n <div *ngIf=\"importing\" class=\"importing-info\">\n <ion-text color=\"medium\"><small translate>FILE.UPLOAD.IMPORTING</small></ion-text>\n &nbsp;<ion-spinner name=\"dots\" color=\"accent\"></ion-spinner>\n </div>\n <ion-item *ngIf=\"error\" lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"ion-text-wrap\" [innerHTML]=\"error | translate\"></ion-label>\n </ion-item>\n </ion-col>\n <ion-col size=\"auto\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"cancel()\">\n <ion-label translate>COMMON.BTN_CANCEL</ion-label>\n </ion-button>\n <ion-button\n [fill]=\"disabled ? 'clear' : 'solid'\"\n (click)=\"onValidate($event)\"\n (keyup.enter)=\"onValidate($event)\"\n color=\"tertiary\"\n [disabled]=\"disabled\"\n >\n <ion-label translate>COMMON.BTN_IMPORT</ion-label>\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-toolbar>\n</ion-footer>\n",
18886
- encapsulation: i0.ViewEncapsulation.None,
18887
- changeDetection: i0.ChangeDetectionStrategy.OnPush,
18888
- styles: [".importing-info{-webkit-padding-start:var(--ion-padding);padding-inline-start:var(--ion-padding);display:inline-flex;flex-wrap:nowrap;padding-top:8px}"]
18889
- },] }
18890
- ];
18891
- UploadFilePopover.ctorParameters = function () { return [
18892
- { type: i1$5.PopoverController },
18893
- { type: i1$2.TranslateService },
18894
- { type: i0.ChangeDetectorRef }
18895
- ]; };
18896
- UploadFilePopover.propDecorators = {
18897
- uploader: [{ type: i0.ViewChild, args: ['uploader', { static: true },] }],
18898
- fileExtension: [{ type: i0.Input }],
18899
- title: [{ type: i0.Input }],
18900
- uniqueFile: [{ type: i0.Input }],
18901
- instantUpload: [{ type: i0.Input }],
18902
- uploadFn: [{ type: i0.Input }],
18903
- deleteFn: [{ type: i0.Input }],
18904
- maxParallelUpload: [{ type: i0.Input }]
18905
- };
18906
-
18907
19033
  var APP_TESTING_PAGES = new i0.InjectionToken('testingPages');
18908
19034
  var SharedTestsPage = /** @class */ (function () {
18909
19035
  function SharedTestsPage(route, router, translate, pages) {
@@ -20498,74 +20624,6 @@
20498
20624
  return JobUtils;
20499
20625
  }());
20500
20626
 
20501
- var FilesUtils = /** @class */ (function () {
20502
- function FilesUtils() {
20503
- }
20504
- FilesUtils.showUploadPopover = function (popoverController, event, opts) {
20505
- return __awaiter(this, void 0, void 0, function () {
20506
- var modal, _a, data, role;
20507
- return __generator(this, function (_b) {
20508
- switch (_b.label) {
20509
- case 0: return [4 /*yield*/, popoverController.create({
20510
- component: UploadFilePopover,
20511
- componentProps: opts,
20512
- backdropDismiss: false,
20513
- keyboardClose: false,
20514
- event: event,
20515
- translucent: true,
20516
- cssClass: 'popover-large',
20517
- })];
20518
- case 1:
20519
- modal = _b.sent();
20520
- return [4 /*yield*/, modal.present()];
20521
- case 2:
20522
- _b.sent();
20523
- return [4 /*yield*/, modal.onDidDismiss()];
20524
- case 3:
20525
- _a = _b.sent(), data = _a.data, role = _a.role;
20526
- return [2 /*return*/, {
20527
- role: role,
20528
- data: data ? data : undefined
20529
- }];
20530
- }
20531
- });
20532
- });
20533
- };
20534
- FilesUtils.readAsText = function (file, encoding) {
20535
- var $progress = new rxjs.Subject();
20536
- var reader = new FileReader();
20537
- encoding = (encoding || 'UTF-8').toLowerCase();
20538
- // Listen progress
20539
- reader.onprogress = function (e) {
20540
- var total = e.total || 1;
20541
- var loaded = e.total || 0;
20542
- if (loaded < total)
20543
- $progress.next({ type: i2$1.HttpEventType.UploadProgress, loaded: loaded, total: total });
20544
- };
20545
- // Listen end
20546
- reader.onloadend = function (e) {
20547
- var body = reader.result;
20548
- if (typeof body === 'string') {
20549
- $progress.next(new FileResponse({ body: body }));
20550
- $progress.complete();
20551
- }
20552
- else {
20553
- console.error("Cannot read file '" + file.name + "' as text");
20554
- $progress.error("Cannot read file as text");
20555
- }
20556
- };
20557
- // Listen error
20558
- reader.onerror = function (event) {
20559
- console.error('[files] Error while reading file:', event);
20560
- $progress.error(event);
20561
- };
20562
- // Start reading the file
20563
- reader.readAsText(file, encoding);
20564
- return $progress.asObservable();
20565
- };
20566
- return FilesUtils;
20567
- }());
20568
-
20569
20627
  /**
20570
20628
  * Define here theme colors
20571
20629
  */
@@ -23215,9 +23273,6 @@
23215
23273
  { type: i0.Injectable, args: [{ providedIn: 'root' },] }
23216
23274
  ];
23217
23275
 
23218
- var MimeTypes = Object.freeze({
23219
- ANDROID_APK: 'application/vnd.android.package-archive'
23220
- });
23221
23276
  var AppInstallUpgradeCard = /** @class */ (function () {
23222
23277
  function AppInstallUpgradeCard(modalCtrl, configService, toastController, alertController, translate, cd, platform, network, environment) {
23223
23278
  this.modalCtrl = modalCtrl;
@@ -23230,7 +23285,6 @@
23230
23285
  this.network = network;
23231
23286
  this.environment = environment;
23232
23287
  this._subscription = new rxjs.Subscription();
23233
- this._showUpdateOfflineFeature = false;
23234
23288
  this.loading = true;
23235
23289
  this.hasDownloader = false;
23236
23290
  this.downloading = false;
@@ -23238,21 +23292,7 @@
23238
23292
  this.showUpgradeWarning = true;
23239
23293
  this.showOfflineWarning = true;
23240
23294
  this.showInstallButton = false;
23241
- this.onUpdateOfflineModeClick = new i0.EventEmitter();
23242
23295
  }
23243
- Object.defineProperty(AppInstallUpgradeCard.prototype, "showUpdateOfflineFeature", {
23244
- get: function () {
23245
- return this._showUpdateOfflineFeature;
23246
- },
23247
- set: function (value) {
23248
- if (value === this._showUpdateOfflineFeature)
23249
- return; // Skip
23250
- this._showUpdateOfflineFeature = value;
23251
- this.markForCheck();
23252
- },
23253
- enumerable: false,
23254
- configurable: true
23255
- });
23256
23296
  AppInstallUpgradeCard.prototype.ngOnInit = function () {
23257
23297
  var _this = this;
23258
23298
  this.offline = this.network.offline;
@@ -23276,7 +23316,6 @@
23276
23316
  AppInstallUpgradeCard.prototype.ngOnDestroy = function () {
23277
23317
  this._subscription.unsubscribe();
23278
23318
  this._subscription = null;
23279
- this.onUpdateOfflineModeClick.complete();
23280
23319
  this.installLinks = null;
23281
23320
  this.upgradeLinks = null;
23282
23321
  };
@@ -23464,10 +23503,10 @@
23464
23503
  var title = void 0;
23465
23504
  var mimeType = void 0;
23466
23505
  // Get file name
23467
- var filename = this.getFilename(url);
23506
+ var filename = UriUtils.getFilename(url);
23468
23507
  var version = minVersion || 'latest';
23469
23508
  // OK, this is a downloadable APK file (e.g. NOT a link to a playstore)
23470
- if (filename === null || filename === void 0 ? void 0 : filename.endsWith('.apk')) {
23509
+ if (filename === null || filename === void 0 ? void 0 : filename.toLowerCase().endsWith('.apk')) {
23471
23510
  // Define mime type
23472
23511
  mimeType = MimeTypes.ANDROID_APK;
23473
23512
  // Get the file version (if any)
@@ -23478,11 +23517,9 @@
23478
23517
  filename = (name + "-v" + version + ".apk").toLowerCase();
23479
23518
  title = name + " v" + version;
23480
23519
  }
23481
- else {
23482
- // Replace 'latest' with the app version, if present
23483
- if (filename.indexOf('latest')) {
23484
- filename = filename.replace('latest', version);
23485
- }
23520
+ // Replace 'latest' with the app version, if present in the filename
23521
+ else if (filename.indexOf('latest') && version !== 'latest') {
23522
+ filename = filename.replace('latest', version);
23486
23523
  }
23487
23524
  }
23488
23525
  result.push({ name: name, url: url, platform: 'android', version: version, filename: filename, title: title, mimeType: mimeType });
@@ -23494,18 +23531,6 @@
23494
23531
  //}
23495
23532
  return result;
23496
23533
  };
23497
- AppInstallUpgradeCard.prototype.getFilename = function (url) {
23498
- if (!url)
23499
- return;
23500
- // Get last part (or all string, if no '/')
23501
- var filename = url.substring(url.lastIndexOf('/') + 1);
23502
- var queryParamIndex = filename.indexOf('?');
23503
- if (queryParamIndex !== -1) {
23504
- // Remove query params
23505
- return filename.substring(0, queryParamIndex);
23506
- }
23507
- return filename;
23508
- };
23509
23534
  AppInstallUpgradeCard.prototype.listenDownloadJobs = function () {
23510
23535
  var _this = this;
23511
23536
  // Listening downloading promise, if exists
@@ -23637,7 +23662,7 @@
23637
23662
  AppInstallUpgradeCard.decorators = [
23638
23663
  { type: i0.Component, args: [{
23639
23664
  selector: 'app-install-upgrade-card',
23640
- 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.filename; else redirectButton\">\n\n <ng-container *ngIf=\"hasDownloader; else webDownloadButton\">\n <!-- Download using platform -->\n <ion-button (click)=\"download($event, asLink(link))\"\n *ngIf=\"!link.location; else openButton\"\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\n <!-- Open APK button -->\n <ng-template #openButton>\n <ion-button *ngIf=\"link.location\"\n (click)=\"openFile($event, link)\"\n color=\"tertiary\" class=\"ion-float-end\" >\n <ion-label translate>COMMON.BTN_INSTALL</ion-label>\n </ion-button>\n </ng-template>\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",
23665
+ 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<!-- 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.filename; else redirectButton\">\n\n <ng-container *ngIf=\"hasDownloader; else webDownloadButton\">\n <!-- Download using platform -->\n <ion-button (click)=\"download($event, asLink(link))\"\n *ngIf=\"!link.location; else openButton\"\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\n <!-- Open APK button -->\n <ng-template #openButton>\n <ion-button *ngIf=\"link.location\"\n (click)=\"openFile($event, link)\"\n color=\"tertiary\" class=\"ion-float-end\" >\n <ion-label translate>COMMON.BTN_INSTALL</ion-label>\n </ion-button>\n </ng-template>\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",
23641
23666
  changeDetection: i0.ChangeDetectionStrategy.OnPush,
23642
23667
  animations: [slideUpDownAnimation],
23643
23668
  styles: ["ion-text small{font-size:85%}"]
@@ -23658,9 +23683,7 @@
23658
23683
  isLogin: [{ type: i0.Input }],
23659
23684
  showUpgradeWarning: [{ type: i0.Input }],
23660
23685
  showOfflineWarning: [{ type: i0.Input }],
23661
- showInstallButton: [{ type: i0.Input }],
23662
- showUpdateOfflineFeature: [{ type: i0.Input }],
23663
- onUpdateOfflineModeClick: [{ type: i0.Output }]
23686
+ showInstallButton: [{ type: i0.Input }]
23664
23687
  };
23665
23688
 
23666
23689
  var IsLoginAccountPipe = /** @class */ (function () {
@@ -24198,6 +24221,31 @@
24198
24221
  ref: [{ type: i0.Input }]
24199
24222
  };
24200
24223
 
24224
+ var AppUpdateOfflineModeCard = /** @class */ (function () {
24225
+ function AppUpdateOfflineModeCard() {
24226
+ this.onUpdateClick = new i0.EventEmitter();
24227
+ }
24228
+ AppUpdateOfflineModeCard.prototype.ngOnDestroy = function () {
24229
+ this.onUpdateClick.complete();
24230
+ };
24231
+ return AppUpdateOfflineModeCard;
24232
+ }());
24233
+ AppUpdateOfflineModeCard.decorators = [
24234
+ { type: i0.Component, args: [{
24235
+ selector: 'app-update-offline-mode-card',
24236
+ template: "\n<!-- Update offline feature card-->\n<ion-card color=\"accent\"\n class=\"main ion-no-margin\"\n @slideUpDownAnimation>\n <ion-card-content class=\"ion-no-padding\">\n <ion-grid>\n <ion-row *ngIf=\"!progressionValue; else progression\">\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\n <!-- Update button -->\n <ion-col size=\"auto\">\n <ion-button color=\"tertiary\" class=\"ion-float-end\"\n (click)=\"onUpdateClick.emit($event)\">\n <span translate>NETWORK.BTN_UPDATE</span>\n </ion-button>\n </ion-col>\n </ion-row>\n\n <ng-template #progression>\n <ion-row>\n <ion-col>\n <ion-text class=\"ion-text-wrap\" [innerHTML]=\"progressionMessage\"></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <ion-progress-bar [value]=\"progressionValue / 100\"></ion-progress-bar>\n </ion-col>\n </ion-row>\n </ng-template>\n </ion-grid>\n\n\n </ion-card-content>\n</ion-card>\n",
24237
+ changeDetection: i0.ChangeDetectionStrategy.OnPush,
24238
+ animations: [slideUpDownAnimation],
24239
+ styles: ["ion-text small{font-size:85%}"]
24240
+ },] }
24241
+ ];
24242
+ AppUpdateOfflineModeCard.ctorParameters = function () { return []; };
24243
+ AppUpdateOfflineModeCard.propDecorators = {
24244
+ progressionMessage: [{ type: i0.Input }],
24245
+ progressionValue: [{ type: i0.Input }],
24246
+ onUpdateClick: [{ type: i0.Output }]
24247
+ };
24248
+
24201
24249
  var CoreModule = /** @class */ (function () {
24202
24250
  function CoreModule() {
24203
24251
  }
@@ -24247,6 +24295,7 @@
24247
24295
  // Network
24248
24296
  SelectPeerModal,
24249
24297
  AppInstallUpgradeCard,
24298
+ AppUpdateOfflineModeCard,
24250
24299
  // Other components
24251
24300
  TableSelectColumnsComponent,
24252
24301
  ActionsColumnComponent,
@@ -24282,6 +24331,7 @@
24282
24331
  AppPropertiesForm,
24283
24332
  AppListForm,
24284
24333
  AppInstallUpgradeCard,
24334
+ AppUpdateOfflineModeCard,
24285
24335
  DepartmentToStringPipe,
24286
24336
  AppIconComponent,
24287
24337
  TextPopover,
@@ -25266,7 +25316,7 @@
25266
25316
  updateDatasource: { get: function () { return _super_1.prototype.updateDatasource; } }
25267
25317
  });
25268
25318
  return __awaiter(this, void 0, void 0, function () {
25269
- var res, existingData;
25319
+ var fetchMoreFn, res, existingData;
25270
25320
  return __generator(this, function (_a) {
25271
25321
  switch (_a.label) {
25272
25322
  case 0:
@@ -25276,9 +25326,9 @@
25276
25326
  console.warn('Cannot fetch more because still editing row');
25277
25327
  return [2 /*return*/];
25278
25328
  }
25279
- // Fetch next page
25280
- this._fetchMoreFn = null; // Avoid multiple call
25281
- return [4 /*yield*/, this._fetchMoreFn()];
25329
+ fetchMoreFn = this._fetchMoreFn;
25330
+ this._fetchMoreFn = null;
25331
+ return [4 /*yield*/, fetchMoreFn()];
25282
25332
  case 1:
25283
25333
  res = _a.sent();
25284
25334
  // Skip if empty (no more data)
@@ -31974,6 +32024,7 @@
31974
32024
  exports.MessageService = MessageService;
31975
32025
  exports.MessageTypeList = MessageTypeList;
31976
32026
  exports.MessageTypes = MessageTypes;
32027
+ exports.MimeTypes = MimeTypes;
31977
32028
  exports.ModalToolbarComponent = ModalToolbarComponent;
31978
32029
  exports.NetworkService = NetworkService;
31979
32030
  exports.NgInitDirective = NgInitDirective;
@@ -32058,6 +32109,7 @@
32058
32109
  exports.UploadFilePopover = UploadFilePopover;
32059
32110
  exports.UploadFileTestingModule = UploadFileTestingModule;
32060
32111
  exports.UploadFileTestingPage = UploadFileTestingPage;
32112
+ exports.UriUtils = UriUtils;
32061
32113
  exports.UserEventFragments = UserEventFragments;
32062
32114
  exports.UserEventService = UserEventService;
32063
32115
  exports.UserEventTypes = UserEventTypes;
@@ -32219,12 +32271,13 @@
32219
32271
  exports.ɵf = RegisterModal;
32220
32272
  exports.ɵg = UserSettingsValidatorService;
32221
32273
  exports.ɵh = LocalSettingsValidatorService;
32222
- exports.ɵi = AppIconComponent;
32223
- exports.ɵj = DateTestPage;
32224
- exports.ɵk = NumpadTestPage;
32225
- exports.ɵl = MatBadgeIconTestPage;
32226
- exports.ɵm = ToastTestingModule;
32227
- exports.ɵn = ToastTestingPage;
32274
+ exports.ɵi = AppUpdateOfflineModeCard;
32275
+ exports.ɵj = AppIconComponent;
32276
+ exports.ɵk = DateTestPage;
32277
+ exports.ɵl = NumpadTestPage;
32278
+ exports.ɵm = MatBadgeIconTestPage;
32279
+ exports.ɵn = ToastTestingModule;
32280
+ exports.ɵo = ToastTestingPage;
32228
32281
 
32229
32282
  Object.defineProperty(exports, '__esModule', { value: true });
32230
32283