@sumaris-net/ngx-components 21.4.0 → 21.4.2
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.
- package/doc/changelog.md +6 -0
- package/fesm2022/sumaris-net.ngx-components.mjs +603 -513
- package/fesm2022/sumaris-net.ngx-components.mjs.map +1 -1
- package/package.json +1 -1
- package/src/assets/i18n/en.json +8 -8
- package/src/assets/i18n/fr.json +8 -8
- package/src/assets/manifest.json +1 -1
- package/types/sumaris-net.ngx-components.d.ts +453 -398
|
@@ -33202,268 +33202,137 @@ class CsvUtils {
|
|
|
33202
33202
|
}
|
|
33203
33203
|
}
|
|
33204
33204
|
|
|
33205
|
-
|
|
33206
|
-
|
|
33207
|
-
|
|
33208
|
-
|
|
33209
|
-
|
|
33210
|
-
|
|
33211
|
-
|
|
33212
|
-
|
|
33213
|
-
|
|
33214
|
-
|
|
33215
|
-
medium: [152, 154, 162], // ok
|
|
33216
|
-
dark: [34, 36, 40], // ok
|
|
33217
|
-
red: [255, 0, 0],
|
|
33218
|
-
green: [0, 255, 0],
|
|
33219
|
-
blue: [0, 0, 255],
|
|
33220
|
-
};
|
|
33221
|
-
// Fill a map of Color objects
|
|
33222
|
-
const colorsMap = {};
|
|
33223
|
-
/**
|
|
33224
|
-
* Useful class for color conversion
|
|
33225
|
-
*/
|
|
33226
|
-
// @dynamic
|
|
33227
|
-
class Color {
|
|
33228
|
-
_rgbArray;
|
|
33229
|
-
_opacity;
|
|
33230
|
-
_name;
|
|
33231
|
-
// Helper method, to retrieve a color
|
|
33232
|
-
static get(name) {
|
|
33233
|
-
return colorsMap[name];
|
|
33234
|
-
}
|
|
33235
|
-
static parseRgba(rgba) {
|
|
33236
|
-
if (!rgba || (!rgba.startsWith('rgb(') && !rgba.startsWith('rgba(')))
|
|
33237
|
-
return null;
|
|
33238
|
-
// Parse parts
|
|
33239
|
-
const parts = rgba.replace('rgb(', '').replace('rgba(', '').replace(')', '').split(',');
|
|
33240
|
-
if (parts.length !== 3 && parts.length !== 4)
|
|
33241
|
-
return null;
|
|
33242
|
-
return new Color([+parts[0], +parts[1], +parts[2]], (parts.length === 4 && +parts[3]) || 1, 'custom');
|
|
33243
|
-
}
|
|
33244
|
-
static transparent = function () {
|
|
33245
|
-
return new Color([0, 0, 0], 0, 'translucent');
|
|
33246
|
-
};
|
|
33247
|
-
constructor(_rgbArray, _opacity, _name = 'custom') {
|
|
33248
|
-
this._rgbArray = _rgbArray;
|
|
33249
|
-
this._opacity = _opacity;
|
|
33250
|
-
this._name = _name;
|
|
33251
|
-
}
|
|
33252
|
-
get name() {
|
|
33253
|
-
return this._name;
|
|
33254
|
-
}
|
|
33255
|
-
get opacity() {
|
|
33256
|
-
return isNotNil(this._opacity) ? this._opacity : 1;
|
|
33257
|
-
}
|
|
33258
|
-
get rgb() {
|
|
33259
|
-
return this._rgbArray;
|
|
33260
|
-
}
|
|
33261
|
-
get r() {
|
|
33262
|
-
return this._rgbArray[0];
|
|
33263
|
-
}
|
|
33264
|
-
get g() {
|
|
33265
|
-
return this._rgbArray[1];
|
|
33266
|
-
}
|
|
33267
|
-
get b() {
|
|
33268
|
-
return this._rgbArray[2];
|
|
33205
|
+
// GPL v3 License
|
|
33206
|
+
class FileAttachmentUtils {
|
|
33207
|
+
/**
|
|
33208
|
+
* Get the file name of an attachment (its name, or the last part of its URL)
|
|
33209
|
+
*/
|
|
33210
|
+
static getFilename(attachment) {
|
|
33211
|
+
if (attachment?.name?.trim())
|
|
33212
|
+
return attachment.name.trim();
|
|
33213
|
+
const path = attachment?.url?.split('?')[0];
|
|
33214
|
+
return path?.substring(path.lastIndexOf('/') + 1) || 'attachment';
|
|
33269
33215
|
}
|
|
33270
|
-
|
|
33271
|
-
|
|
33272
|
-
|
|
33273
|
-
|
|
33274
|
-
}
|
|
33275
|
-
return 'rgba(' + this._rgbArray.join(',') + ',' + opacity + ')';
|
|
33216
|
+
/**
|
|
33217
|
+
* Get the Material icon (font ligature) of an attachment
|
|
33218
|
+
*/
|
|
33219
|
+
static getIcon(attachment) {
|
|
33220
|
+
return FileTypeUtils.getIconByFile({ name: FileAttachmentUtils.getFilename(attachment), type: attachment?.mimeType });
|
|
33276
33221
|
}
|
|
33277
33222
|
}
|
|
33223
|
+
|
|
33224
|
+
// GPL v3 License
|
|
33225
|
+
/** Kinds of file that can be previewed (displayed without being downloaded) */
|
|
33226
|
+
const PREVIEW_KINDS = Object.freeze(['image', 'text', 'code']);
|
|
33227
|
+
/** Extra MIME types (or extensions) that can be previewed as text */
|
|
33228
|
+
const PREVIEW_TEXT_MIME_TYPES = /^(text\/(csv|tab-separated-values|markdown|x-markdown)|application\/csv)$/;
|
|
33229
|
+
const PREVIEW_TEXT_EXTENSIONS = Object.freeze(['csv', 'tsv', 'md', 'markdown']);
|
|
33230
|
+
const MARKDOWN_MIME_TYPES = /^text\/(x-)?markdown$/;
|
|
33231
|
+
const MARKDOWN_EXTENSIONS = Object.freeze(['md', 'markdown']);
|
|
33232
|
+
const HTML_MIME_TYPES = /^(text\/html|application\/xhtml\+xml)$/;
|
|
33233
|
+
const HTML_EXTENSIONS = Object.freeze(['html', 'htm', 'xhtml']);
|
|
33278
33234
|
/**
|
|
33279
|
-
*
|
|
33235
|
+
* Download (or read) a remote file, sending the application headers (e.g. authorization) when the file is on the current peer.
|
|
33236
|
+
*
|
|
33237
|
+
* - On Android (Cordova downloader): the file is downloaded by the native download manager (visible in the "Downloads" app);
|
|
33238
|
+
* - Otherwise (web, iOS): the file is fetched as a Blob, then saved using a temporary object URL.
|
|
33280
33239
|
*/
|
|
33281
|
-
|
|
33282
|
-
|
|
33283
|
-
|
|
33284
|
-
options = options || {};
|
|
33285
|
-
return new ColorScale(linearColorGradientWithIntermediate(count, {
|
|
33286
|
-
startColor: options.startColor || undefined,
|
|
33287
|
-
mainColor: options.mainColor || undefined,
|
|
33288
|
-
mainColorIndex: isNotNil(options.mainColorIndex) ? options.mainColorIndex : undefined,
|
|
33289
|
-
endColor: options.endColor || undefined,
|
|
33290
|
-
}), options);
|
|
33291
|
-
};
|
|
33292
|
-
static default() {
|
|
33293
|
-
return ColorScale.custom(25);
|
|
33294
|
-
}
|
|
33240
|
+
class FileDownloadService {
|
|
33241
|
+
network = inject(NetworkService);
|
|
33242
|
+
platform = inject(PlatformService);
|
|
33295
33243
|
/**
|
|
33296
|
-
*
|
|
33297
|
-
|
|
33298
|
-
|
|
33299
|
-
|
|
33300
|
-
return new Array(length || 25).fill(color);
|
|
33301
|
-
}
|
|
33302
|
-
_min;
|
|
33303
|
-
_max;
|
|
33304
|
-
_rangeSize;
|
|
33305
|
-
_legendItems;
|
|
33306
|
-
get min() {
|
|
33307
|
-
return this._min;
|
|
33308
|
-
}
|
|
33309
|
-
get max() {
|
|
33310
|
-
return this._max;
|
|
33244
|
+
* Can the file be previewed (displayed without being downloaded)?
|
|
33245
|
+
*/
|
|
33246
|
+
canPreview(file) {
|
|
33247
|
+
return this.isImage(file) || this.isText(file);
|
|
33311
33248
|
}
|
|
33312
|
-
|
|
33313
|
-
|
|
33314
|
-
// reserved last colors for value > max
|
|
33315
|
-
const nbIntervalBeforeUpper = !options.upperMax ? colorArray.length : colorArray.length - 1;
|
|
33316
|
-
this._min = options.min || 0;
|
|
33317
|
-
this._max = options.max || nbIntervalBeforeUpper;
|
|
33318
|
-
this._rangeSize = Math.round((this._max - this._min) / nbIntervalBeforeUpper);
|
|
33319
|
-
this._legendItems = this.computeLegendItems(colorArray);
|
|
33249
|
+
isImage(file) {
|
|
33250
|
+
return FileTypeUtils.getKindByFile({ name: file?.name, type: file?.mimeType }) === 'image';
|
|
33320
33251
|
}
|
|
33321
|
-
|
|
33322
|
-
|
|
33323
|
-
|
|
33324
|
-
|
|
33252
|
+
isText(file) {
|
|
33253
|
+
const mimeType = file?.mimeType?.trim().toLowerCase();
|
|
33254
|
+
if (mimeType && PREVIEW_TEXT_MIME_TYPES.test(mimeType))
|
|
33255
|
+
return true;
|
|
33256
|
+
if (PREVIEW_TEXT_EXTENSIONS.includes(FileTypeUtils.getExtension(file?.name)))
|
|
33257
|
+
return true;
|
|
33258
|
+
const kind = FileTypeUtils.getKindByFile({ name: file?.name, type: mimeType });
|
|
33259
|
+
return kind !== 'image' && PREVIEW_KINDS.includes(kind);
|
|
33325
33260
|
}
|
|
33326
|
-
|
|
33327
|
-
|
|
33261
|
+
isMarkdown(file) {
|
|
33262
|
+
const mimeType = file?.mimeType?.trim().toLowerCase();
|
|
33263
|
+
return (mimeType && MARKDOWN_MIME_TYPES.test(mimeType)) || MARKDOWN_EXTENSIONS.includes(FileTypeUtils.getExtension(file?.name));
|
|
33328
33264
|
}
|
|
33329
|
-
|
|
33330
|
-
|
|
33331
|
-
|
|
33332
|
-
if (value >= this._max)
|
|
33333
|
-
return this._legendItems[this._legendItems.length - 1];
|
|
33334
|
-
const index = Math.floor((value * (this._legendItems.length - 1)) / this._max);
|
|
33335
|
-
return this._legendItems[index];
|
|
33265
|
+
isHtml(file) {
|
|
33266
|
+
const mimeType = file?.mimeType?.trim().toLowerCase();
|
|
33267
|
+
return (mimeType && HTML_MIME_TYPES.test(mimeType)) || HTML_EXTENSIONS.includes(FileTypeUtils.getExtension(file?.name));
|
|
33336
33268
|
}
|
|
33337
|
-
|
|
33338
|
-
|
|
33269
|
+
/**
|
|
33270
|
+
* Fetch a remote file, as a Blob
|
|
33271
|
+
*/
|
|
33272
|
+
getBlob(url) {
|
|
33273
|
+
return this.network.getBlob(FileDownloadService.normalizeUrl(url));
|
|
33339
33274
|
}
|
|
33340
|
-
|
|
33341
|
-
|
|
33342
|
-
|
|
33343
|
-
|
|
33344
|
-
|
|
33345
|
-
|
|
33346
|
-
|
|
33347
|
-
|
|
33348
|
-
});
|
|
33275
|
+
/**
|
|
33276
|
+
* Remove duplicated slashes in the path of an URL (e.g. 'http://host//api/file' => 'http://host/api/file').
|
|
33277
|
+
* Needed because the server (and its CORS rules) does not match a path with duplicated slashes.
|
|
33278
|
+
*/
|
|
33279
|
+
static normalizeUrl(url) {
|
|
33280
|
+
if (!url)
|
|
33281
|
+
return url;
|
|
33282
|
+
const [path, ...query] = url.split('?');
|
|
33283
|
+
const normalizedPath = path.replace(/([^:\/])\/{2,}/g, '$1/');
|
|
33284
|
+
return [normalizedPath, ...query].join('?');
|
|
33349
33285
|
}
|
|
33350
|
-
|
|
33351
|
-
|
|
33352
|
-
|
|
33353
|
-
|
|
33354
|
-
|
|
33355
|
-
|
|
33356
|
-
* Internal function, that create a colors scale, using iteration
|
|
33357
|
-
*
|
|
33358
|
-
* @param count
|
|
33359
|
-
* @param opacity
|
|
33360
|
-
* @param startColor
|
|
33361
|
-
* @param startState
|
|
33362
|
-
* @returns
|
|
33363
|
-
*/
|
|
33364
|
-
function linearColorGradientWithIntermediate(count, options) {
|
|
33365
|
-
options = options || {};
|
|
33366
|
-
// From [0,1]
|
|
33367
|
-
options.startColor = options.startColor || [255, 255, 190]; // default start = creme
|
|
33368
|
-
options.mainColorIndex = options.mainColorIndex && options.mainColorIndex < count - 1 ? options.mainColorIndex : count - 1;
|
|
33369
|
-
options.endColor = options.endColor || [255, 0, 0]; // default main = red
|
|
33370
|
-
if (!options.mainColor) {
|
|
33371
|
-
return linearColorGradient(count, {
|
|
33372
|
-
startColor: options.startColor,
|
|
33373
|
-
endColor: options.endColor,
|
|
33374
|
-
});
|
|
33286
|
+
/**
|
|
33287
|
+
* Fetch a remote file, as a text (e.g. to preview it)
|
|
33288
|
+
*/
|
|
33289
|
+
async getText(url) {
|
|
33290
|
+
const blob = await this.getBlob(url);
|
|
33291
|
+
return blob.text();
|
|
33375
33292
|
}
|
|
33376
|
-
|
|
33377
|
-
|
|
33378
|
-
|
|
33379
|
-
|
|
33380
|
-
|
|
33381
|
-
|
|
33382
|
-
|
|
33383
|
-
|
|
33384
|
-
|
|
33385
|
-
|
|
33386
|
-
|
|
33293
|
+
/**
|
|
33294
|
+
* Download a remote file
|
|
33295
|
+
*/
|
|
33296
|
+
async download(url, opts) {
|
|
33297
|
+
if (!url)
|
|
33298
|
+
throw new Error("Missing argument 'url'");
|
|
33299
|
+
url = FileDownloadService.normalizeUrl(url);
|
|
33300
|
+
const filename = opts?.filename || this.getFilename(url);
|
|
33301
|
+
// Android: use the native download manager
|
|
33302
|
+
if (this.platform.canDownload) {
|
|
33303
|
+
const headers = Object.entries(this.network.getAppHeaders(url)).map(([header, value]) => ({
|
|
33304
|
+
header,
|
|
33305
|
+
value: Array.isArray(value) ? value.join(', ') : value,
|
|
33387
33306
|
}));
|
|
33307
|
+
await firstValueFrom(this.platform.download({ uri: url, filename, title: filename, mimeType: opts?.mimeType, headers }));
|
|
33308
|
+
return;
|
|
33388
33309
|
}
|
|
33389
|
-
|
|
33390
|
-
|
|
33310
|
+
// Web, iOS: fetch as a Blob (with the authorization headers), then save it
|
|
33311
|
+
const blob = await this.getBlob(url);
|
|
33312
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
33313
|
+
try {
|
|
33314
|
+
FilesUtils.downloadUri(objectUrl, filename);
|
|
33391
33315
|
}
|
|
33392
|
-
|
|
33393
|
-
|
|
33394
|
-
|
|
33395
|
-
options = options || {};
|
|
33396
|
-
// From [0,1]
|
|
33397
|
-
options.startColor = options.startColor || [255, 255, 255]; // default start = white
|
|
33398
|
-
options.endColor = options.endColor || [255, 0, 0]; // default end = red
|
|
33399
|
-
const result = [];
|
|
33400
|
-
const color = options.startColor.slice(); // copy the start color
|
|
33401
|
-
const delta = [
|
|
33402
|
-
Math.round((options.endColor[0] - options.startColor[0]) / count),
|
|
33403
|
-
Math.round((options.endColor[1] - options.startColor[1]) / count),
|
|
33404
|
-
Math.round((options.endColor[2] - options.startColor[2]) / count),
|
|
33405
|
-
];
|
|
33406
|
-
for (let i = 0; i < count - 1; i++) {
|
|
33407
|
-
for (let j = 0; j < 3; j++) {
|
|
33408
|
-
color[j] += delta[j];
|
|
33316
|
+
finally {
|
|
33317
|
+
// Release the object URL, once the download has started
|
|
33318
|
+
setTimeout(() => URL.revokeObjectURL(objectUrl), 1000);
|
|
33409
33319
|
}
|
|
33410
|
-
result.push(color.slice());
|
|
33411
|
-
}
|
|
33412
|
-
// Force last color = end color
|
|
33413
|
-
result.push(options.endColor.slice());
|
|
33414
|
-
// Output as array
|
|
33415
|
-
return result;
|
|
33416
|
-
}
|
|
33417
|
-
|
|
33418
|
-
class DebugComponent {
|
|
33419
|
-
titlePrefix = 'COMMON.DEBUG.TITLE';
|
|
33420
|
-
title = '';
|
|
33421
|
-
enable = null;
|
|
33422
|
-
expanded = false;
|
|
33423
|
-
expansionPanel;
|
|
33424
|
-
constructor(environment) {
|
|
33425
|
-
this.enable = !environment || !environment.production;
|
|
33426
33320
|
}
|
|
33427
|
-
|
|
33428
|
-
|
|
33321
|
+
/* -- protected methods -- */
|
|
33322
|
+
getFilename(url) {
|
|
33323
|
+
try {
|
|
33324
|
+
return FilesUtils.getFilenameFromUri(url.split('?')[0]) || 'download';
|
|
33325
|
+
}
|
|
33326
|
+
catch {
|
|
33327
|
+
return 'download';
|
|
33328
|
+
}
|
|
33429
33329
|
}
|
|
33430
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type:
|
|
33431
|
-
static
|
|
33432
|
-
}
|
|
33433
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: DebugComponent, decorators: [{
|
|
33434
|
-
type: Component,
|
|
33435
|
-
args: [{ selector: 'app-debug', standalone: false, template: "<!-- debug -->\n<ion-item tappable (click)=\"toggle()\" lines=\"none\" color=\"secondary50\">\n <ion-icon slot=\"start\" name=\"bug\"></ion-icon>\n <ion-label color=\"primary\">{{ titlePrefix | translate }} {{ title }}</ion-label>\n <mat-icon slot=\"end\">{{ expansionPanel.expanded ? 'expand_less' : 'expand_more' }}</mat-icon>\n</ion-item>\n<mat-expansion-panel #expansionPanel [(expanded)]=\"expanded\">\n <ion-text color=\"primary100\">\n <div class=\"ion-margin-start\">\n <ng-content></ng-content>\n </div>\n </ion-text>\n</mat-expansion-panel>\n", styles: ["ion-item mat-icon,ion-item ion-icon{color:var(--ion-color-primary)}ion-item{cursor:pointer!important}.mat-expansion-panel{--expansion-panel-padding: 4px;background:var(--ion-color-secondary50)!important}.mat-expansion-panel.mat-expanded{margin-bottom:4px!important}.mat-expansion-panel .mat-expansion-panel-body *{color:var(--ion-color-primary100)}\n"] }]
|
|
33436
|
-
}], ctorParameters: () => [{ type: Environment, decorators: [{
|
|
33437
|
-
type: Inject,
|
|
33438
|
-
args: [ENVIRONMENT]
|
|
33439
|
-
}, {
|
|
33440
|
-
type: Optional
|
|
33441
|
-
}] }], propDecorators: { titlePrefix: [{
|
|
33442
|
-
type: Input
|
|
33443
|
-
}], title: [{
|
|
33444
|
-
type: Input
|
|
33445
|
-
}], enable: [{
|
|
33446
|
-
type: Input
|
|
33447
|
-
}], expanded: [{
|
|
33448
|
-
type: Input
|
|
33449
|
-
}], expansionPanel: [{
|
|
33450
|
-
type: ViewChild,
|
|
33451
|
-
args: ['expansionPanel']
|
|
33452
|
-
}] } });
|
|
33453
|
-
|
|
33454
|
-
class SharedDebugModule {
|
|
33455
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedDebugModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
33456
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.22", ngImport: i0, type: SharedDebugModule, declarations: [DebugComponent], imports: [CommonModule, SharedIonicModule, MatExpansionModule, MatIconModule, TranslatePipe], exports: [DebugComponent] });
|
|
33457
|
-
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedDebugModule, providers: [provideChildTranslateService()], imports: [CommonModule, SharedIonicModule, MatExpansionModule, MatIconModule] });
|
|
33330
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileDownloadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
33331
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileDownloadService, providedIn: 'root' });
|
|
33458
33332
|
}
|
|
33459
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type:
|
|
33460
|
-
type:
|
|
33461
|
-
args: [{
|
|
33462
|
-
imports: [CommonModule, SharedIonicModule, MatExpansionModule, MatIconModule, TranslatePipe],
|
|
33463
|
-
declarations: [DebugComponent],
|
|
33464
|
-
exports: [DebugComponent],
|
|
33465
|
-
providers: [provideChildTranslateService()],
|
|
33466
|
-
}]
|
|
33333
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileDownloadService, decorators: [{
|
|
33334
|
+
type: Injectable,
|
|
33335
|
+
args: [{ providedIn: 'root' }]
|
|
33467
33336
|
}] });
|
|
33468
33337
|
|
|
33469
33338
|
class GitlabUtils {
|
|
@@ -33929,6 +33798,155 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
33929
33798
|
args: ['ready']
|
|
33930
33799
|
}] } });
|
|
33931
33800
|
|
|
33801
|
+
// GPL v3 License
|
|
33802
|
+
/**
|
|
33803
|
+
* Display the content of an attachment (text, markdown, image, HTML), without downloading it
|
|
33804
|
+
*/
|
|
33805
|
+
class FileAttachmentPreviewModal {
|
|
33806
|
+
cd = inject(ChangeDetectorRef);
|
|
33807
|
+
modalCtrl = inject(ModalController);
|
|
33808
|
+
fileDownload = inject(FileDownloadService);
|
|
33809
|
+
mobile = inject(LocalSettingsService).mobile;
|
|
33810
|
+
sanitizer = inject(DomSanitizer);
|
|
33811
|
+
elementRef = inject(ElementRef);
|
|
33812
|
+
attachment;
|
|
33813
|
+
loading = true;
|
|
33814
|
+
error;
|
|
33815
|
+
filename;
|
|
33816
|
+
text;
|
|
33817
|
+
markdown = false;
|
|
33818
|
+
imageUrl;
|
|
33819
|
+
/** HTML content, displayed in a sandboxed iframe (scripts allowed, but isolated from the application: no same origin) */
|
|
33820
|
+
html;
|
|
33821
|
+
/** Display the modal in a large size */
|
|
33822
|
+
maximized = false;
|
|
33823
|
+
async ngOnInit() {
|
|
33824
|
+
this.filename = FileAttachmentUtils.getFilename(this.attachment);
|
|
33825
|
+
const file = { name: this.filename, mimeType: this.attachment?.mimeType };
|
|
33826
|
+
try {
|
|
33827
|
+
if (this.fileDownload.isImage(file)) {
|
|
33828
|
+
const blob = await this.fileDownload.getBlob(this.attachment.url);
|
|
33829
|
+
this.imageUrl = URL.createObjectURL(blob);
|
|
33830
|
+
}
|
|
33831
|
+
else if (this.fileDownload.isHtml(file)) {
|
|
33832
|
+
const content = await this.fileDownload.getText(this.attachment.url);
|
|
33833
|
+
// Safe: the content is only rendered inside a sandboxed iframe (see template)
|
|
33834
|
+
this.html = this.sanitizer.bypassSecurityTrustHtml(content);
|
|
33835
|
+
}
|
|
33836
|
+
else {
|
|
33837
|
+
this.markdown = this.fileDownload.isMarkdown(file);
|
|
33838
|
+
this.text = await this.fileDownload.getText(this.attachment.url);
|
|
33839
|
+
}
|
|
33840
|
+
}
|
|
33841
|
+
catch (err) {
|
|
33842
|
+
console.error(`[file-attachment] Cannot load attachment {${this.attachment?.url}}`, err);
|
|
33843
|
+
this.error = 'FILE.ATTACHMENT.PREVIEW_ERROR';
|
|
33844
|
+
}
|
|
33845
|
+
finally {
|
|
33846
|
+
this.loading = false;
|
|
33847
|
+
this.cd.markForCheck();
|
|
33848
|
+
}
|
|
33849
|
+
}
|
|
33850
|
+
ngOnDestroy() {
|
|
33851
|
+
if (this.imageUrl)
|
|
33852
|
+
URL.revokeObjectURL(this.imageUrl);
|
|
33853
|
+
}
|
|
33854
|
+
async download() {
|
|
33855
|
+
await this.fileDownload.download(this.attachment.url, { filename: this.filename, mimeType: this.attachment.mimeType });
|
|
33856
|
+
}
|
|
33857
|
+
/** Display the modal in a large size (to restore the size: close then reopen the attachment) */
|
|
33858
|
+
maximize() {
|
|
33859
|
+
this.maximized = true;
|
|
33860
|
+
this.elementRef.nativeElement.closest('ion-modal')?.classList.add('modal-large');
|
|
33861
|
+
this.cd.markForCheck();
|
|
33862
|
+
}
|
|
33863
|
+
close() {
|
|
33864
|
+
return this.modalCtrl.dismiss();
|
|
33865
|
+
}
|
|
33866
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileAttachmentPreviewModal, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
33867
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: FileAttachmentPreviewModal, isStandalone: false, selector: "app-file-attachment-preview-modal", inputs: { attachment: "attachment" }, ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ filename }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button\n class=\"download-button\"\n (click)=\"download()\"\n [title]=\"'FILE.ATTACHMENT.BTN_DOWNLOAD' | translate\"\n >\n <mat-icon slot=\"icon-only\">download</mat-icon>\n </ion-button>\n @if (!mobile && !maximized) {\n <ion-button\n class=\"maximize-button\"\n (click)=\"maximize()\"\n [title]=\"'FILE.ATTACHMENT.BTN_MAXIMIZE' | translate\"\n >\n <mat-icon slot=\"icon-only\">fullscreen</mat-icon>\n </ion-button>\n }\n <ion-button class=\"close-button\" (click)=\"close()\" [title]=\"'COMMON.BTN_CLOSE' | translate\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n @if (loading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else if (error) {\n <ion-text color=\"danger\" class=\"preview-error\">{{ error | translate }}</ion-text>\n } @else if (imageUrl) {\n <img class=\"preview-image\" [src]=\"imageUrl\" [alt]=\"filename\" />\n } @else if (html) {\n <!-- sandboxed: scripts allowed, but no access to the application (no same origin) -->\n <iframe class=\"preview-html\" sandbox=\"allow-scripts\" [srcdoc]=\"html\" [title]=\"filename\"></iframe>\n } @else if (markdown) {\n <markdown class=\"preview-markdown\" [data]=\"text\"></markdown>\n } @else {\n <pre class=\"preview-text\">{{ text }}</pre>\n }\n</ion-content>\n", styles: [".preview-image{display:block;max-width:100%;margin:0 auto}.preview-text{white-space:pre-wrap;word-break:break-word;font-size:.9em}.preview-html{display:block;width:100%;height:calc(100% - 4px);min-height:60vh;border:none}\n"], dependencies: [{ kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$2.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i4$3.MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }, { kind: "directive", type: MarkdownDirective, selector: "markdown,[markdown]" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
33868
|
+
}
|
|
33869
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileAttachmentPreviewModal, decorators: [{
|
|
33870
|
+
type: Component,
|
|
33871
|
+
args: [{ selector: 'app-file-attachment-preview-modal', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ filename }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button\n class=\"download-button\"\n (click)=\"download()\"\n [title]=\"'FILE.ATTACHMENT.BTN_DOWNLOAD' | translate\"\n >\n <mat-icon slot=\"icon-only\">download</mat-icon>\n </ion-button>\n @if (!mobile && !maximized) {\n <ion-button\n class=\"maximize-button\"\n (click)=\"maximize()\"\n [title]=\"'FILE.ATTACHMENT.BTN_MAXIMIZE' | translate\"\n >\n <mat-icon slot=\"icon-only\">fullscreen</mat-icon>\n </ion-button>\n }\n <ion-button class=\"close-button\" (click)=\"close()\" [title]=\"'COMMON.BTN_CLOSE' | translate\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n @if (loading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else if (error) {\n <ion-text color=\"danger\" class=\"preview-error\">{{ error | translate }}</ion-text>\n } @else if (imageUrl) {\n <img class=\"preview-image\" [src]=\"imageUrl\" [alt]=\"filename\" />\n } @else if (html) {\n <!-- sandboxed: scripts allowed, but no access to the application (no same origin) -->\n <iframe class=\"preview-html\" sandbox=\"allow-scripts\" [srcdoc]=\"html\" [title]=\"filename\"></iframe>\n } @else if (markdown) {\n <markdown class=\"preview-markdown\" [data]=\"text\"></markdown>\n } @else {\n <pre class=\"preview-text\">{{ text }}</pre>\n }\n</ion-content>\n", styles: [".preview-image{display:block;max-width:100%;margin:0 auto}.preview-text{white-space:pre-wrap;word-break:break-word;font-size:.9em}.preview-html{display:block;width:100%;height:calc(100% - 4px);min-height:60vh;border:none}\n"] }]
|
|
33872
|
+
}], propDecorators: { attachment: [{
|
|
33873
|
+
type: Input
|
|
33874
|
+
}] } });
|
|
33875
|
+
|
|
33876
|
+
// GPL v3 License
|
|
33877
|
+
/**
|
|
33878
|
+
* Display an attachment as a compact badge (chip): icon, file name, and size (in the tooltip).
|
|
33879
|
+
*
|
|
33880
|
+
* - Click on the badge: display the file content in a modal (text, markdown, image, HTML), or download it if not previewable;
|
|
33881
|
+
* - Download part (right side, visible on hover - always visible on mobile): download the file.
|
|
33882
|
+
*/
|
|
33883
|
+
class FileAttachmentComponent {
|
|
33884
|
+
modalCtrl = inject(ModalController);
|
|
33885
|
+
toastCtrl = inject(ToastController);
|
|
33886
|
+
translate = inject(TranslateService);
|
|
33887
|
+
fileDownload = inject(FileDownloadService);
|
|
33888
|
+
getFilename = FileAttachmentUtils.getFilename;
|
|
33889
|
+
getIcon = FileAttachmentUtils.getIcon;
|
|
33890
|
+
canPreview = (attachment) => this.fileDownload.canPreview({ name: FileAttachmentUtils.getFilename(attachment), mimeType: attachment?.mimeType });
|
|
33891
|
+
attachment;
|
|
33892
|
+
/** Bigger badge (touch), with the download part always visible. Default: the mobile local settings */
|
|
33893
|
+
mobile = inject(LocalSettingsService).mobile;
|
|
33894
|
+
/**
|
|
33895
|
+
* Open the attachment: display its content, if possible (text, markdown, image...), or download it
|
|
33896
|
+
*/
|
|
33897
|
+
async open(event) {
|
|
33898
|
+
event?.preventDefault();
|
|
33899
|
+
event?.stopPropagation();
|
|
33900
|
+
const attachment = this.attachment;
|
|
33901
|
+
if (!attachment?.url)
|
|
33902
|
+
return;
|
|
33903
|
+
if (!this.canPreview(attachment)) {
|
|
33904
|
+
return this.download(event);
|
|
33905
|
+
}
|
|
33906
|
+
const hasTopModal = !!(await this.modalCtrl.getTop());
|
|
33907
|
+
const modal = await this.modalCtrl.create({
|
|
33908
|
+
component: FileAttachmentPreviewModal,
|
|
33909
|
+
componentProps: { attachment },
|
|
33910
|
+
cssClass: hasTopModal ? 'stack-modal' : undefined,
|
|
33911
|
+
});
|
|
33912
|
+
await modal.present();
|
|
33913
|
+
}
|
|
33914
|
+
/**
|
|
33915
|
+
* Download the attachment (using the native download manager on Android)
|
|
33916
|
+
*/
|
|
33917
|
+
async download(event) {
|
|
33918
|
+
event?.preventDefault();
|
|
33919
|
+
event?.stopPropagation();
|
|
33920
|
+
const attachment = this.attachment;
|
|
33921
|
+
if (!attachment?.url)
|
|
33922
|
+
return;
|
|
33923
|
+
try {
|
|
33924
|
+
await this.fileDownload.download(attachment.url, {
|
|
33925
|
+
filename: FileAttachmentUtils.getFilename(attachment),
|
|
33926
|
+
mimeType: attachment.mimeType,
|
|
33927
|
+
});
|
|
33928
|
+
}
|
|
33929
|
+
catch (err) {
|
|
33930
|
+
console.error(`[file-attachment] Cannot download attachment {${attachment.url}}`, err);
|
|
33931
|
+
await Toasts.show(this.toastCtrl, this.translate, { type: 'error', message: 'FILE.ATTACHMENT.DOWNLOAD_ERROR' });
|
|
33932
|
+
}
|
|
33933
|
+
}
|
|
33934
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileAttachmentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
33935
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: FileAttachmentComponent, isStandalone: false, selector: "app-file-attachment", inputs: { attachment: "attachment", mobile: "mobile" }, host: { properties: { "class.mobile": "this.mobile" } }, ngImport: i0, template: "@let filename = attachment | map: getFilename;\n@let previewable = attachment | map: canPreview;\n@let action = (previewable ? 'FILE.ATTACHMENT.BTN_PREVIEW' : 'FILE.ATTACHMENT.BTN_DOWNLOAD') | translate;\n<ion-chip\n class=\"file-attachment\"\n [class.has-download]=\"previewable\"\n [outline]=\"true\"\n [title]=\"filename + (attachment?.size ? ' (' + (attachment.size | fileSize) + ')' : '') + ' - ' + action\"\n (click)=\"open($event)\"\n tappable\n>\n <mat-icon class=\"attachment-icon\">{{ attachment | map: getIcon }}</mat-icon>\n <ion-label class=\"attachment-name\">{{ filename }}</ion-label>\n @if (previewable) {\n <!-- download part (separated, visible on hover): preview is the default action -->\n <span class=\"attachment-download\" role=\"button\" [title]=\"'FILE.ATTACHMENT.BTN_DOWNLOAD' | translate\" (click)=\"download($event)\">\n <mat-icon>download</mat-icon>\n </span>\n }\n</ion-chip>\n", styles: [":host{display:inline-flex;max-width:100%;--file-attachment-height: 22px;--file-attachment-icon-size: 14px;--file-attachment-padding: 6px;--file-attachment-font-size: 12px}:host(.mobile){--file-attachment-height: 32px;--file-attachment-icon-size: 18px;--file-attachment-padding: 10px;--file-attachment-font-size: 14px}.file-attachment{max-width:100%;height:var(--file-attachment-height);min-height:var(--file-attachment-height);margin:0;padding:0 var(--file-attachment-padding);font-size:var(--file-attachment-font-size);border-width:1px;overflow:hidden;color:var(--ion-text-color, #000);border-color:rgba(var(--ion-text-color-rgb, 0, 0, 0),.4)}.file-attachment mat-icon{flex-shrink:0;width:var(--file-attachment-icon-size);height:var(--file-attachment-icon-size);font-size:var(--file-attachment-icon-size);line-height:var(--file-attachment-icon-size)}.file-attachment .attachment-icon{margin-inline-end:4px}.file-attachment .attachment-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-attachment .attachment-download{display:flex;align-items:center;align-self:stretch;flex-shrink:0;overflow:hidden;max-width:0;margin-inline-start:0;padding:0;border-inline-start:0 solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.3);opacity:0;transition:max-width .15s ease-in-out,opacity .15s ease-in-out,background-color .15s ease-in-out}.file-attachment.has-download:hover{padding-inline-end:0}.file-attachment:hover .attachment-download{max-width:calc(var(--file-attachment-icon-size) + 2 * var(--file-attachment-padding) + 1px);margin-inline-start:var(--file-attachment-padding);padding:0 var(--file-attachment-padding);border-inline-start-width:1px;opacity:1}.file-attachment .attachment-download:hover{color:var(--ion-color-primary);background-color:rgba(var(--ion-color-primary-rgb, 0, 0, 0),.12)}:host(.mobile) .file-attachment.has-download{padding-inline-end:0}:host(.mobile) .file-attachment .attachment-download{max-width:calc(var(--file-attachment-icon-size) + 2 * var(--file-attachment-padding) + 1px);margin-inline-start:var(--file-attachment-padding);padding:0 var(--file-attachment-padding);border-inline-start-width:1px;opacity:1}\n"], dependencies: [{ kind: "component", type: i2$2.IonChip, selector: "ion-chip", inputs: ["color", "disabled", "mode", "outline"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: FileSizePipe, name: "fileSize" }, { kind: "pipe", type: MapPipe, name: "map" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
33936
|
+
}
|
|
33937
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileAttachmentComponent, decorators: [{
|
|
33938
|
+
type: Component,
|
|
33939
|
+
args: [{ selector: 'app-file-attachment', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "@let filename = attachment | map: getFilename;\n@let previewable = attachment | map: canPreview;\n@let action = (previewable ? 'FILE.ATTACHMENT.BTN_PREVIEW' : 'FILE.ATTACHMENT.BTN_DOWNLOAD') | translate;\n<ion-chip\n class=\"file-attachment\"\n [class.has-download]=\"previewable\"\n [outline]=\"true\"\n [title]=\"filename + (attachment?.size ? ' (' + (attachment.size | fileSize) + ')' : '') + ' - ' + action\"\n (click)=\"open($event)\"\n tappable\n>\n <mat-icon class=\"attachment-icon\">{{ attachment | map: getIcon }}</mat-icon>\n <ion-label class=\"attachment-name\">{{ filename }}</ion-label>\n @if (previewable) {\n <!-- download part (separated, visible on hover): preview is the default action -->\n <span class=\"attachment-download\" role=\"button\" [title]=\"'FILE.ATTACHMENT.BTN_DOWNLOAD' | translate\" (click)=\"download($event)\">\n <mat-icon>download</mat-icon>\n </span>\n }\n</ion-chip>\n", styles: [":host{display:inline-flex;max-width:100%;--file-attachment-height: 22px;--file-attachment-icon-size: 14px;--file-attachment-padding: 6px;--file-attachment-font-size: 12px}:host(.mobile){--file-attachment-height: 32px;--file-attachment-icon-size: 18px;--file-attachment-padding: 10px;--file-attachment-font-size: 14px}.file-attachment{max-width:100%;height:var(--file-attachment-height);min-height:var(--file-attachment-height);margin:0;padding:0 var(--file-attachment-padding);font-size:var(--file-attachment-font-size);border-width:1px;overflow:hidden;color:var(--ion-text-color, #000);border-color:rgba(var(--ion-text-color-rgb, 0, 0, 0),.4)}.file-attachment mat-icon{flex-shrink:0;width:var(--file-attachment-icon-size);height:var(--file-attachment-icon-size);font-size:var(--file-attachment-icon-size);line-height:var(--file-attachment-icon-size)}.file-attachment .attachment-icon{margin-inline-end:4px}.file-attachment .attachment-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-attachment .attachment-download{display:flex;align-items:center;align-self:stretch;flex-shrink:0;overflow:hidden;max-width:0;margin-inline-start:0;padding:0;border-inline-start:0 solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.3);opacity:0;transition:max-width .15s ease-in-out,opacity .15s ease-in-out,background-color .15s ease-in-out}.file-attachment.has-download:hover{padding-inline-end:0}.file-attachment:hover .attachment-download{max-width:calc(var(--file-attachment-icon-size) + 2 * var(--file-attachment-padding) + 1px);margin-inline-start:var(--file-attachment-padding);padding:0 var(--file-attachment-padding);border-inline-start-width:1px;opacity:1}.file-attachment .attachment-download:hover{color:var(--ion-color-primary);background-color:rgba(var(--ion-color-primary-rgb, 0, 0, 0),.12)}:host(.mobile) .file-attachment.has-download{padding-inline-end:0}:host(.mobile) .file-attachment .attachment-download{max-width:calc(var(--file-attachment-icon-size) + 2 * var(--file-attachment-padding) + 1px);margin-inline-start:var(--file-attachment-padding);padding:0 var(--file-attachment-padding);border-inline-start-width:1px;opacity:1}\n"] }]
|
|
33940
|
+
}], propDecorators: { attachment: [{
|
|
33941
|
+
type: Input,
|
|
33942
|
+
args: [{ required: true }]
|
|
33943
|
+
}], mobile: [{
|
|
33944
|
+
type: HostBinding,
|
|
33945
|
+
args: ['class.mobile']
|
|
33946
|
+
}, {
|
|
33947
|
+
type: Input
|
|
33948
|
+
}] } });
|
|
33949
|
+
|
|
33932
33950
|
const PRINT_LOADING_STORAGE_KEY_PREFIX = 'print-loading-';
|
|
33933
33951
|
const PRINT_ID_QUERY_PARAM = 'print-id';
|
|
33934
33952
|
class PrintService {
|
|
@@ -34223,6 +34241,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
34223
34241
|
args: [DOCUMENT]
|
|
34224
34242
|
}] }] });
|
|
34225
34243
|
|
|
34244
|
+
class DebugComponent {
|
|
34245
|
+
titlePrefix = 'COMMON.DEBUG.TITLE';
|
|
34246
|
+
title = '';
|
|
34247
|
+
enable = null;
|
|
34248
|
+
expanded = false;
|
|
34249
|
+
expansionPanel;
|
|
34250
|
+
constructor(environment) {
|
|
34251
|
+
this.enable = !environment || !environment.production;
|
|
34252
|
+
}
|
|
34253
|
+
toggle() {
|
|
34254
|
+
this.expansionPanel.toggle();
|
|
34255
|
+
}
|
|
34256
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: DebugComponent, deps: [{ token: ENVIRONMENT, optional: true }], target: i0.ɵɵFactoryTarget.Component });
|
|
34257
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.22", type: DebugComponent, isStandalone: false, selector: "app-debug", inputs: { titlePrefix: "titlePrefix", title: "title", enable: "enable", expanded: "expanded" }, viewQueries: [{ propertyName: "expansionPanel", first: true, predicate: ["expansionPanel"], descendants: true }], ngImport: i0, template: "<!-- debug -->\n<ion-item tappable (click)=\"toggle()\" lines=\"none\" color=\"secondary50\">\n <ion-icon slot=\"start\" name=\"bug\"></ion-icon>\n <ion-label color=\"primary\">{{ titlePrefix | translate }} {{ title }}</ion-label>\n <mat-icon slot=\"end\">{{ expansionPanel.expanded ? 'expand_less' : 'expand_more' }}</mat-icon>\n</ion-item>\n<mat-expansion-panel #expansionPanel [(expanded)]=\"expanded\">\n <ion-text color=\"primary100\">\n <div class=\"ion-margin-start\">\n <ng-content></ng-content>\n </div>\n </ion-text>\n</mat-expansion-panel>\n", styles: ["ion-item mat-icon,ion-item ion-icon{color:var(--ion-color-primary)}ion-item{cursor:pointer!important}.mat-expansion-panel{--expansion-panel-padding: 4px;background:var(--ion-color-secondary50)!important}.mat-expansion-panel.mat-expanded{margin-bottom:4px!important}.mat-expansion-panel .mat-expansion-panel-body *{color:var(--ion-color-primary100)}\n"], dependencies: [{ kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i11$2.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }] });
|
|
34258
|
+
}
|
|
34259
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: DebugComponent, decorators: [{
|
|
34260
|
+
type: Component,
|
|
34261
|
+
args: [{ selector: 'app-debug', standalone: false, template: "<!-- debug -->\n<ion-item tappable (click)=\"toggle()\" lines=\"none\" color=\"secondary50\">\n <ion-icon slot=\"start\" name=\"bug\"></ion-icon>\n <ion-label color=\"primary\">{{ titlePrefix | translate }} {{ title }}</ion-label>\n <mat-icon slot=\"end\">{{ expansionPanel.expanded ? 'expand_less' : 'expand_more' }}</mat-icon>\n</ion-item>\n<mat-expansion-panel #expansionPanel [(expanded)]=\"expanded\">\n <ion-text color=\"primary100\">\n <div class=\"ion-margin-start\">\n <ng-content></ng-content>\n </div>\n </ion-text>\n</mat-expansion-panel>\n", styles: ["ion-item mat-icon,ion-item ion-icon{color:var(--ion-color-primary)}ion-item{cursor:pointer!important}.mat-expansion-panel{--expansion-panel-padding: 4px;background:var(--ion-color-secondary50)!important}.mat-expansion-panel.mat-expanded{margin-bottom:4px!important}.mat-expansion-panel .mat-expansion-panel-body *{color:var(--ion-color-primary100)}\n"] }]
|
|
34262
|
+
}], ctorParameters: () => [{ type: Environment, decorators: [{
|
|
34263
|
+
type: Inject,
|
|
34264
|
+
args: [ENVIRONMENT]
|
|
34265
|
+
}, {
|
|
34266
|
+
type: Optional
|
|
34267
|
+
}] }], propDecorators: { titlePrefix: [{
|
|
34268
|
+
type: Input
|
|
34269
|
+
}], title: [{
|
|
34270
|
+
type: Input
|
|
34271
|
+
}], enable: [{
|
|
34272
|
+
type: Input
|
|
34273
|
+
}], expanded: [{
|
|
34274
|
+
type: Input
|
|
34275
|
+
}], expansionPanel: [{
|
|
34276
|
+
type: ViewChild,
|
|
34277
|
+
args: ['expansionPanel']
|
|
34278
|
+
}] } });
|
|
34279
|
+
|
|
34226
34280
|
class AppMarkdownContent {
|
|
34227
34281
|
cd;
|
|
34228
34282
|
element;
|
|
@@ -34612,6 +34666,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
34612
34666
|
args: ['content']
|
|
34613
34667
|
}] } });
|
|
34614
34668
|
|
|
34669
|
+
class SharedDebugModule {
|
|
34670
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedDebugModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
34671
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.22", ngImport: i0, type: SharedDebugModule, declarations: [DebugComponent], imports: [CommonModule, SharedIonicModule, MatExpansionModule, MatIconModule, TranslatePipe], exports: [DebugComponent] });
|
|
34672
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedDebugModule, providers: [provideChildTranslateService()], imports: [CommonModule, SharedIonicModule, MatExpansionModule, MatIconModule] });
|
|
34673
|
+
}
|
|
34674
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedDebugModule, decorators: [{
|
|
34675
|
+
type: NgModule,
|
|
34676
|
+
args: [{
|
|
34677
|
+
imports: [CommonModule, SharedIonicModule, MatExpansionModule, MatIconModule, TranslatePipe],
|
|
34678
|
+
declarations: [DebugComponent],
|
|
34679
|
+
exports: [DebugComponent],
|
|
34680
|
+
providers: [provideChildTranslateService()],
|
|
34681
|
+
}]
|
|
34682
|
+
}] });
|
|
34683
|
+
|
|
34615
34684
|
// function that returns `MarkedOptions` with renderer override
|
|
34616
34685
|
class SharedMarkdownModule {
|
|
34617
34686
|
static forRoot() {
|
|
@@ -34664,29 +34733,258 @@ class SharedMarkdownModule {
|
|
|
34664
34733
|
SharedToolbarModule,
|
|
34665
34734
|
SharedDebugModule, MarkdownModule] });
|
|
34666
34735
|
}
|
|
34667
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedMarkdownModule, decorators: [{
|
|
34668
|
-
type: NgModule,
|
|
34669
|
-
args: [{
|
|
34670
|
-
imports: [
|
|
34671
|
-
CommonModule,
|
|
34672
|
-
SharedIonicModule,
|
|
34673
|
-
TranslatePipe,
|
|
34674
|
-
MarkdownModule.forChild(),
|
|
34675
|
-
// App module
|
|
34676
|
-
SharedToolbarModule,
|
|
34677
|
-
SharedDebugModule,
|
|
34678
|
-
],
|
|
34679
|
-
declarations: [
|
|
34680
|
-
// Component
|
|
34681
|
-
AppMarkdownContent,
|
|
34682
|
-
AppMarkdownModal,
|
|
34683
|
-
// Directive
|
|
34684
|
-
MarkdownDirective,
|
|
34685
|
-
],
|
|
34686
|
-
exports: [MarkdownModule, AppMarkdownContent, MarkdownDirective],
|
|
34687
|
-
providers: [provideChildTranslateService()],
|
|
34688
|
-
}]
|
|
34689
|
-
}] });
|
|
34736
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedMarkdownModule, decorators: [{
|
|
34737
|
+
type: NgModule,
|
|
34738
|
+
args: [{
|
|
34739
|
+
imports: [
|
|
34740
|
+
CommonModule,
|
|
34741
|
+
SharedIonicModule,
|
|
34742
|
+
TranslatePipe,
|
|
34743
|
+
MarkdownModule.forChild(),
|
|
34744
|
+
// App module
|
|
34745
|
+
SharedToolbarModule,
|
|
34746
|
+
SharedDebugModule,
|
|
34747
|
+
],
|
|
34748
|
+
declarations: [
|
|
34749
|
+
// Component
|
|
34750
|
+
AppMarkdownContent,
|
|
34751
|
+
AppMarkdownModal,
|
|
34752
|
+
// Directive
|
|
34753
|
+
MarkdownDirective,
|
|
34754
|
+
],
|
|
34755
|
+
exports: [MarkdownModule, AppMarkdownContent, MarkdownDirective],
|
|
34756
|
+
providers: [provideChildTranslateService()],
|
|
34757
|
+
}]
|
|
34758
|
+
}] });
|
|
34759
|
+
|
|
34760
|
+
// GPL v3 License
|
|
34761
|
+
class SharedFileAttachmentModule {
|
|
34762
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedFileAttachmentModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
34763
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.22", ngImport: i0, type: SharedFileAttachmentModule, declarations: [FileAttachmentComponent, FileAttachmentPreviewModal], imports: [CommonModule, SharedIonicModule, SharedMaterialModule, SharedPipesModule, SharedMarkdownModule, TranslatePipe], exports: [FileAttachmentComponent, FileAttachmentPreviewModal] });
|
|
34764
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedFileAttachmentModule, providers: [provideChildTranslateService()], imports: [CommonModule, SharedIonicModule, SharedMaterialModule, SharedPipesModule, SharedMarkdownModule] });
|
|
34765
|
+
}
|
|
34766
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: SharedFileAttachmentModule, decorators: [{
|
|
34767
|
+
type: NgModule,
|
|
34768
|
+
args: [{
|
|
34769
|
+
imports: [CommonModule, SharedIonicModule, SharedMaterialModule, SharedPipesModule, SharedMarkdownModule, TranslatePipe],
|
|
34770
|
+
declarations: [FileAttachmentComponent, FileAttachmentPreviewModal],
|
|
34771
|
+
exports: [FileAttachmentComponent, FileAttachmentPreviewModal],
|
|
34772
|
+
providers: [provideChildTranslateService()],
|
|
34773
|
+
}]
|
|
34774
|
+
}] });
|
|
34775
|
+
|
|
34776
|
+
/**
|
|
34777
|
+
* Define here theme colors
|
|
34778
|
+
*/
|
|
34779
|
+
const rgbArrayMap = {
|
|
34780
|
+
white: [255, 255, 255],
|
|
34781
|
+
primary: [20, 67, 145], // ok
|
|
34782
|
+
secondary: [117, 196, 253], // ok
|
|
34783
|
+
tertiary: [91, 94, 244], // ok
|
|
34784
|
+
danger: [245, 61, 61], // ok
|
|
34785
|
+
light: [244, 245, 248], // ok
|
|
34786
|
+
medium: [152, 154, 162], // ok
|
|
34787
|
+
dark: [34, 36, 40], // ok
|
|
34788
|
+
red: [255, 0, 0],
|
|
34789
|
+
green: [0, 255, 0],
|
|
34790
|
+
blue: [0, 0, 255],
|
|
34791
|
+
};
|
|
34792
|
+
// Fill a map of Color objects
|
|
34793
|
+
const colorsMap = {};
|
|
34794
|
+
/**
|
|
34795
|
+
* Useful class for color conversion
|
|
34796
|
+
*/
|
|
34797
|
+
// @dynamic
|
|
34798
|
+
class Color {
|
|
34799
|
+
_rgbArray;
|
|
34800
|
+
_opacity;
|
|
34801
|
+
_name;
|
|
34802
|
+
// Helper method, to retrieve a color
|
|
34803
|
+
static get(name) {
|
|
34804
|
+
return colorsMap[name];
|
|
34805
|
+
}
|
|
34806
|
+
static parseRgba(rgba) {
|
|
34807
|
+
if (!rgba || (!rgba.startsWith('rgb(') && !rgba.startsWith('rgba(')))
|
|
34808
|
+
return null;
|
|
34809
|
+
// Parse parts
|
|
34810
|
+
const parts = rgba.replace('rgb(', '').replace('rgba(', '').replace(')', '').split(',');
|
|
34811
|
+
if (parts.length !== 3 && parts.length !== 4)
|
|
34812
|
+
return null;
|
|
34813
|
+
return new Color([+parts[0], +parts[1], +parts[2]], (parts.length === 4 && +parts[3]) || 1, 'custom');
|
|
34814
|
+
}
|
|
34815
|
+
static transparent = function () {
|
|
34816
|
+
return new Color([0, 0, 0], 0, 'translucent');
|
|
34817
|
+
};
|
|
34818
|
+
constructor(_rgbArray, _opacity, _name = 'custom') {
|
|
34819
|
+
this._rgbArray = _rgbArray;
|
|
34820
|
+
this._opacity = _opacity;
|
|
34821
|
+
this._name = _name;
|
|
34822
|
+
}
|
|
34823
|
+
get name() {
|
|
34824
|
+
return this._name;
|
|
34825
|
+
}
|
|
34826
|
+
get opacity() {
|
|
34827
|
+
return isNotNil(this._opacity) ? this._opacity : 1;
|
|
34828
|
+
}
|
|
34829
|
+
get rgb() {
|
|
34830
|
+
return this._rgbArray;
|
|
34831
|
+
}
|
|
34832
|
+
get r() {
|
|
34833
|
+
return this._rgbArray[0];
|
|
34834
|
+
}
|
|
34835
|
+
get g() {
|
|
34836
|
+
return this._rgbArray[1];
|
|
34837
|
+
}
|
|
34838
|
+
get b() {
|
|
34839
|
+
return this._rgbArray[2];
|
|
34840
|
+
}
|
|
34841
|
+
rgba(opacity) {
|
|
34842
|
+
opacity = isNotNil(opacity) ? opacity : this._opacity;
|
|
34843
|
+
if (isNil(opacity) || opacity < 0 || opacity > 1) {
|
|
34844
|
+
return 'rgb(' + this._rgbArray.join(',') + ')';
|
|
34845
|
+
}
|
|
34846
|
+
return 'rgba(' + this._rgbArray.join(',') + ',' + opacity + ')';
|
|
34847
|
+
}
|
|
34848
|
+
}
|
|
34849
|
+
/**
|
|
34850
|
+
* Helper class for colors scale
|
|
34851
|
+
*/
|
|
34852
|
+
// @dynamic
|
|
34853
|
+
class ColorScale {
|
|
34854
|
+
static custom = (count, options) => {
|
|
34855
|
+
options = options || {};
|
|
34856
|
+
return new ColorScale(linearColorGradientWithIntermediate(count, {
|
|
34857
|
+
startColor: options.startColor || undefined,
|
|
34858
|
+
mainColor: options.mainColor || undefined,
|
|
34859
|
+
mainColorIndex: isNotNil(options.mainColorIndex) ? options.mainColorIndex : undefined,
|
|
34860
|
+
endColor: options.endColor || undefined,
|
|
34861
|
+
}), options);
|
|
34862
|
+
};
|
|
34863
|
+
static default() {
|
|
34864
|
+
return ColorScale.custom(25);
|
|
34865
|
+
}
|
|
34866
|
+
/**
|
|
34867
|
+
* Create an array with the given color
|
|
34868
|
+
**/
|
|
34869
|
+
static fix(length, colorName) {
|
|
34870
|
+
const color = Color.get(colorName || 'primary').rgba(0.5);
|
|
34871
|
+
return new Array(length || 25).fill(color);
|
|
34872
|
+
}
|
|
34873
|
+
_min;
|
|
34874
|
+
_max;
|
|
34875
|
+
_rangeSize;
|
|
34876
|
+
_legendItems;
|
|
34877
|
+
get min() {
|
|
34878
|
+
return this._min;
|
|
34879
|
+
}
|
|
34880
|
+
get max() {
|
|
34881
|
+
return this._max;
|
|
34882
|
+
}
|
|
34883
|
+
constructor(colorArray, options) {
|
|
34884
|
+
options = options || {};
|
|
34885
|
+
// reserved last colors for value > max
|
|
34886
|
+
const nbIntervalBeforeUpper = !options.upperMax ? colorArray.length : colorArray.length - 1;
|
|
34887
|
+
this._min = options.min || 0;
|
|
34888
|
+
this._max = options.max || nbIntervalBeforeUpper;
|
|
34889
|
+
this._rangeSize = Math.round((this._max - this._min) / nbIntervalBeforeUpper);
|
|
34890
|
+
this._legendItems = this.computeLegendItems(colorArray);
|
|
34891
|
+
}
|
|
34892
|
+
get legend() {
|
|
34893
|
+
return {
|
|
34894
|
+
items: this._legendItems,
|
|
34895
|
+
};
|
|
34896
|
+
}
|
|
34897
|
+
getValueColor(value) {
|
|
34898
|
+
return this.getLegendForValue(value).color;
|
|
34899
|
+
}
|
|
34900
|
+
getLegendForValue(value) {
|
|
34901
|
+
if (value <= this._min)
|
|
34902
|
+
return this._legendItems[0];
|
|
34903
|
+
if (value >= this._max)
|
|
34904
|
+
return this._legendItems[this._legendItems.length - 1];
|
|
34905
|
+
const index = Math.floor((value * (this._legendItems.length - 1)) / this._max);
|
|
34906
|
+
return this._legendItems[index];
|
|
34907
|
+
}
|
|
34908
|
+
getLegendAtIndex(index) {
|
|
34909
|
+
return this._legendItems[index];
|
|
34910
|
+
}
|
|
34911
|
+
computeLegendItems(colorArray) {
|
|
34912
|
+
return colorArray.map((color, index) => {
|
|
34913
|
+
const start = index * this._rangeSize;
|
|
34914
|
+
const end = start + this._rangeSize;
|
|
34915
|
+
return {
|
|
34916
|
+
color: new Color(color),
|
|
34917
|
+
label: end < this._max ? `${start.toLocaleString()} - ${end.toLocaleString()}` : ` >= ${start}`,
|
|
34918
|
+
};
|
|
34919
|
+
});
|
|
34920
|
+
}
|
|
34921
|
+
}
|
|
34922
|
+
// Fill colorsMap
|
|
34923
|
+
Object.getOwnPropertyNames(rgbArrayMap).forEach((key) => {
|
|
34924
|
+
colorsMap[key] = new Color(rgbArrayMap[key], 1, key);
|
|
34925
|
+
});
|
|
34926
|
+
/**
|
|
34927
|
+
* Internal function, that create a colors scale, using iteration
|
|
34928
|
+
*
|
|
34929
|
+
* @param count
|
|
34930
|
+
* @param opacity
|
|
34931
|
+
* @param startColor
|
|
34932
|
+
* @param startState
|
|
34933
|
+
* @returns
|
|
34934
|
+
*/
|
|
34935
|
+
function linearColorGradientWithIntermediate(count, options) {
|
|
34936
|
+
options = options || {};
|
|
34937
|
+
// From [0,1]
|
|
34938
|
+
options.startColor = options.startColor || [255, 255, 190]; // default start = creme
|
|
34939
|
+
options.mainColorIndex = options.mainColorIndex && options.mainColorIndex < count - 1 ? options.mainColorIndex : count - 1;
|
|
34940
|
+
options.endColor = options.endColor || [255, 0, 0]; // default main = red
|
|
34941
|
+
if (!options.mainColor) {
|
|
34942
|
+
return linearColorGradient(count, {
|
|
34943
|
+
startColor: options.startColor,
|
|
34944
|
+
endColor: options.endColor,
|
|
34945
|
+
});
|
|
34946
|
+
}
|
|
34947
|
+
else {
|
|
34948
|
+
// Step 1: startColor -> mainColor
|
|
34949
|
+
const result = linearColorGradient(options.mainColorIndex + 1, {
|
|
34950
|
+
startColor: options.startColor,
|
|
34951
|
+
endColor: options.mainColor,
|
|
34952
|
+
});
|
|
34953
|
+
// Step 2: mainColor -> endColor
|
|
34954
|
+
if (options.mainColorIndex < count - 1) {
|
|
34955
|
+
return result.concat(linearColorGradient(count - options.mainColorIndex, {
|
|
34956
|
+
startColor: options.mainColor,
|
|
34957
|
+
endColor: options.endColor,
|
|
34958
|
+
}));
|
|
34959
|
+
}
|
|
34960
|
+
else {
|
|
34961
|
+
return result;
|
|
34962
|
+
}
|
|
34963
|
+
}
|
|
34964
|
+
}
|
|
34965
|
+
function linearColorGradient(count, options) {
|
|
34966
|
+
options = options || {};
|
|
34967
|
+
// From [0,1]
|
|
34968
|
+
options.startColor = options.startColor || [255, 255, 255]; // default start = white
|
|
34969
|
+
options.endColor = options.endColor || [255, 0, 0]; // default end = red
|
|
34970
|
+
const result = [];
|
|
34971
|
+
const color = options.startColor.slice(); // copy the start color
|
|
34972
|
+
const delta = [
|
|
34973
|
+
Math.round((options.endColor[0] - options.startColor[0]) / count),
|
|
34974
|
+
Math.round((options.endColor[1] - options.startColor[1]) / count),
|
|
34975
|
+
Math.round((options.endColor[2] - options.startColor[2]) / count),
|
|
34976
|
+
];
|
|
34977
|
+
for (let i = 0; i < count - 1; i++) {
|
|
34978
|
+
for (let j = 0; j < 3; j++) {
|
|
34979
|
+
color[j] += delta[j];
|
|
34980
|
+
}
|
|
34981
|
+
result.push(color.slice());
|
|
34982
|
+
}
|
|
34983
|
+
// Force last color = end color
|
|
34984
|
+
result.push(options.endColor.slice());
|
|
34985
|
+
// Output as array
|
|
34986
|
+
return result;
|
|
34987
|
+
}
|
|
34690
34988
|
|
|
34691
34989
|
const APP_ABOUT_DEVELOPERS = new InjectionToken('aboutDevelopers');
|
|
34692
34990
|
const APP_ABOUT_PARTNERS = new InjectionToken('aboutPartners');
|
|
@@ -39081,6 +39379,21 @@ let MessageAttachment = class MessageAttachment extends Entity {
|
|
|
39081
39379
|
MessageAttachment = __decorate([
|
|
39082
39380
|
EntityClass({ typename: 'FileAttachmentVO' })
|
|
39083
39381
|
], MessageAttachment);
|
|
39382
|
+
class MessageAttachmentUtils {
|
|
39383
|
+
/**
|
|
39384
|
+
* Convert a message attachment into a generic file attachment (e.g. to display it with `app-file-attachment`)
|
|
39385
|
+
*/
|
|
39386
|
+
static toFileAttachment(attachment) {
|
|
39387
|
+
if (!attachment)
|
|
39388
|
+
return undefined;
|
|
39389
|
+
return {
|
|
39390
|
+
url: attachment.url,
|
|
39391
|
+
name: attachment.name || attachment.comments?.trim() || attachment.path,
|
|
39392
|
+
mimeType: attachment.contentType,
|
|
39393
|
+
size: attachment.size,
|
|
39394
|
+
};
|
|
39395
|
+
}
|
|
39396
|
+
}
|
|
39084
39397
|
const MessageTypes = {
|
|
39085
39398
|
INBOX_MESSAGE: 'INBOX_MESSAGE',
|
|
39086
39399
|
EMAIL: 'EMAIL',
|
|
@@ -39819,120 +40132,6 @@ function providePersonService() {
|
|
|
39819
40132
|
};
|
|
39820
40133
|
}
|
|
39821
40134
|
|
|
39822
|
-
// GPL v3 License
|
|
39823
|
-
/** Kinds of file that can be previewed (displayed without being downloaded) */
|
|
39824
|
-
const PREVIEW_KINDS = Object.freeze(['image', 'text', 'code']);
|
|
39825
|
-
/** Extra MIME types (or extensions) that can be previewed as text */
|
|
39826
|
-
const PREVIEW_TEXT_MIME_TYPES = /^(text\/(csv|tab-separated-values|markdown|x-markdown)|application\/csv)$/;
|
|
39827
|
-
const PREVIEW_TEXT_EXTENSIONS = Object.freeze(['csv', 'tsv', 'md', 'markdown']);
|
|
39828
|
-
const MARKDOWN_MIME_TYPES = /^text\/(x-)?markdown$/;
|
|
39829
|
-
const MARKDOWN_EXTENSIONS = Object.freeze(['md', 'markdown']);
|
|
39830
|
-
const HTML_MIME_TYPES = /^(text\/html|application\/xhtml\+xml)$/;
|
|
39831
|
-
const HTML_EXTENSIONS = Object.freeze(['html', 'htm', 'xhtml']);
|
|
39832
|
-
/**
|
|
39833
|
-
* Download (or read) a remote file, sending the application headers (e.g. authorization) when the file is on the current peer.
|
|
39834
|
-
*
|
|
39835
|
-
* - On Android (Cordova downloader): the file is downloaded by the native download manager (visible in the "Downloads" app);
|
|
39836
|
-
* - Otherwise (web, iOS): the file is fetched as a Blob, then saved using a temporary object URL.
|
|
39837
|
-
*/
|
|
39838
|
-
class FileDownloadService {
|
|
39839
|
-
network = inject(NetworkService);
|
|
39840
|
-
platform = inject(PlatformService);
|
|
39841
|
-
/**
|
|
39842
|
-
* Can the file be previewed (displayed without being downloaded)?
|
|
39843
|
-
*/
|
|
39844
|
-
canPreview(file) {
|
|
39845
|
-
return this.isImage(file) || this.isText(file);
|
|
39846
|
-
}
|
|
39847
|
-
isImage(file) {
|
|
39848
|
-
return FileTypeUtils.getKindByFile({ name: file?.name, type: file?.mimeType }) === 'image';
|
|
39849
|
-
}
|
|
39850
|
-
isText(file) {
|
|
39851
|
-
const mimeType = file?.mimeType?.trim().toLowerCase();
|
|
39852
|
-
if (mimeType && PREVIEW_TEXT_MIME_TYPES.test(mimeType))
|
|
39853
|
-
return true;
|
|
39854
|
-
if (PREVIEW_TEXT_EXTENSIONS.includes(FileTypeUtils.getExtension(file?.name)))
|
|
39855
|
-
return true;
|
|
39856
|
-
const kind = FileTypeUtils.getKindByFile({ name: file?.name, type: mimeType });
|
|
39857
|
-
return kind !== 'image' && PREVIEW_KINDS.includes(kind);
|
|
39858
|
-
}
|
|
39859
|
-
isMarkdown(file) {
|
|
39860
|
-
const mimeType = file?.mimeType?.trim().toLowerCase();
|
|
39861
|
-
return (mimeType && MARKDOWN_MIME_TYPES.test(mimeType)) || MARKDOWN_EXTENSIONS.includes(FileTypeUtils.getExtension(file?.name));
|
|
39862
|
-
}
|
|
39863
|
-
isHtml(file) {
|
|
39864
|
-
const mimeType = file?.mimeType?.trim().toLowerCase();
|
|
39865
|
-
return (mimeType && HTML_MIME_TYPES.test(mimeType)) || HTML_EXTENSIONS.includes(FileTypeUtils.getExtension(file?.name));
|
|
39866
|
-
}
|
|
39867
|
-
/**
|
|
39868
|
-
* Fetch a remote file, as a Blob
|
|
39869
|
-
*/
|
|
39870
|
-
getBlob(url) {
|
|
39871
|
-
return this.network.getBlob(FileDownloadService.normalizeUrl(url));
|
|
39872
|
-
}
|
|
39873
|
-
/**
|
|
39874
|
-
* Remove duplicated slashes in the path of an URL (e.g. 'http://host//api/file' => 'http://host/api/file').
|
|
39875
|
-
* Needed because the server (and its CORS rules) does not match a path with duplicated slashes.
|
|
39876
|
-
*/
|
|
39877
|
-
static normalizeUrl(url) {
|
|
39878
|
-
if (!url)
|
|
39879
|
-
return url;
|
|
39880
|
-
const [path, ...query] = url.split('?');
|
|
39881
|
-
const normalizedPath = path.replace(/([^:\/])\/{2,}/g, '$1/');
|
|
39882
|
-
return [normalizedPath, ...query].join('?');
|
|
39883
|
-
}
|
|
39884
|
-
/**
|
|
39885
|
-
* Fetch a remote file, as a text (e.g. to preview it)
|
|
39886
|
-
*/
|
|
39887
|
-
async getText(url) {
|
|
39888
|
-
const blob = await this.getBlob(url);
|
|
39889
|
-
return blob.text();
|
|
39890
|
-
}
|
|
39891
|
-
/**
|
|
39892
|
-
* Download a remote file
|
|
39893
|
-
*/
|
|
39894
|
-
async download(url, opts) {
|
|
39895
|
-
if (!url)
|
|
39896
|
-
throw new Error("Missing argument 'url'");
|
|
39897
|
-
url = FileDownloadService.normalizeUrl(url);
|
|
39898
|
-
const filename = opts?.filename || this.getFilename(url);
|
|
39899
|
-
// Android: use the native download manager
|
|
39900
|
-
if (this.platform.canDownload) {
|
|
39901
|
-
const headers = Object.entries(this.network.getAppHeaders(url)).map(([header, value]) => ({
|
|
39902
|
-
header,
|
|
39903
|
-
value: Array.isArray(value) ? value.join(', ') : value,
|
|
39904
|
-
}));
|
|
39905
|
-
await firstValueFrom(this.platform.download({ uri: url, filename, title: filename, mimeType: opts?.mimeType, headers }));
|
|
39906
|
-
return;
|
|
39907
|
-
}
|
|
39908
|
-
// Web, iOS: fetch as a Blob (with the authorization headers), then save it
|
|
39909
|
-
const blob = await this.getBlob(url);
|
|
39910
|
-
const objectUrl = URL.createObjectURL(blob);
|
|
39911
|
-
try {
|
|
39912
|
-
FilesUtils.downloadUri(objectUrl, filename);
|
|
39913
|
-
}
|
|
39914
|
-
finally {
|
|
39915
|
-
// Release the object URL, once the download has started
|
|
39916
|
-
setTimeout(() => URL.revokeObjectURL(objectUrl), 1000);
|
|
39917
|
-
}
|
|
39918
|
-
}
|
|
39919
|
-
/* -- protected methods -- */
|
|
39920
|
-
getFilename(url) {
|
|
39921
|
-
try {
|
|
39922
|
-
return FilesUtils.getFilenameFromUri(url.split('?')[0]) || 'download';
|
|
39923
|
-
}
|
|
39924
|
-
catch {
|
|
39925
|
-
return 'download';
|
|
39926
|
-
}
|
|
39927
|
-
}
|
|
39928
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileDownloadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
39929
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileDownloadService, providedIn: 'root' });
|
|
39930
|
-
}
|
|
39931
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FileDownloadService, decorators: [{
|
|
39932
|
-
type: Injectable,
|
|
39933
|
-
args: [{ providedIn: 'root' }]
|
|
39934
|
-
}] });
|
|
39935
|
-
|
|
39936
40135
|
// GPL v3 License
|
|
39937
40136
|
const APP_FILE_TRANSFER_SERVICE = new InjectionToken('FileTransferService');
|
|
39938
40137
|
|
|
@@ -40406,97 +40605,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
40406
40605
|
}] }] });
|
|
40407
40606
|
|
|
40408
40607
|
class FeedAttachmentUtils {
|
|
40608
|
+
/**
|
|
40609
|
+
* Convert a feed attachment (JSON Feed format) into a generic file attachment
|
|
40610
|
+
*/
|
|
40611
|
+
static toFileAttachment(attachment) {
|
|
40612
|
+
if (!attachment)
|
|
40613
|
+
return undefined;
|
|
40614
|
+
return {
|
|
40615
|
+
url: attachment.url,
|
|
40616
|
+
name: attachment.title?.trim() || undefined,
|
|
40617
|
+
mimeType: attachment.mime_type,
|
|
40618
|
+
size: attachment.size_in_bytes,
|
|
40619
|
+
};
|
|
40620
|
+
}
|
|
40409
40621
|
/**
|
|
40410
40622
|
* Get the file name of an attachment (its title, or the last part of its URL)
|
|
40411
40623
|
*/
|
|
40412
40624
|
static getFilename(attachment) {
|
|
40413
|
-
|
|
40414
|
-
return attachment.title.trim();
|
|
40415
|
-
const path = attachment?.url?.split('?')[0];
|
|
40416
|
-
return path?.substring(path.lastIndexOf('/') + 1) || 'attachment';
|
|
40625
|
+
return FileAttachmentUtils.getFilename(FeedAttachmentUtils.toFileAttachment(attachment));
|
|
40417
40626
|
}
|
|
40418
40627
|
/**
|
|
40419
40628
|
* Get the Material icon (font ligature) of an attachment
|
|
40420
40629
|
*/
|
|
40421
40630
|
static getIcon(attachment) {
|
|
40422
|
-
return
|
|
40423
|
-
}
|
|
40424
|
-
}
|
|
40425
|
-
|
|
40426
|
-
// GPL v3 License
|
|
40427
|
-
/**
|
|
40428
|
-
* Display the content of a feed attachment (text, markdown, image), without downloading it
|
|
40429
|
-
*/
|
|
40430
|
-
class FeedAttachmentPreviewModal {
|
|
40431
|
-
cd = inject(ChangeDetectorRef);
|
|
40432
|
-
modalCtrl = inject(ModalController);
|
|
40433
|
-
fileDownload = inject(FileDownloadService);
|
|
40434
|
-
mobile = inject(LocalSettingsService).mobile;
|
|
40435
|
-
sanitizer = inject(DomSanitizer);
|
|
40436
|
-
elementRef = inject(ElementRef);
|
|
40437
|
-
attachment;
|
|
40438
|
-
loading = true;
|
|
40439
|
-
error;
|
|
40440
|
-
filename;
|
|
40441
|
-
text;
|
|
40442
|
-
markdown = false;
|
|
40443
|
-
imageUrl;
|
|
40444
|
-
/** HTML content, displayed in a sandboxed iframe (scripts allowed, but isolated from the application: no same origin) */
|
|
40445
|
-
html;
|
|
40446
|
-
/** Display the modal in a large size (same as the feed details modal) */
|
|
40447
|
-
maximized = false;
|
|
40448
|
-
async ngOnInit() {
|
|
40449
|
-
this.filename = FeedAttachmentUtils.getFilename(this.attachment);
|
|
40450
|
-
const file = { name: this.filename, mimeType: this.attachment?.mime_type };
|
|
40451
|
-
try {
|
|
40452
|
-
if (this.fileDownload.isImage(file)) {
|
|
40453
|
-
const blob = await this.fileDownload.getBlob(this.attachment.url);
|
|
40454
|
-
this.imageUrl = URL.createObjectURL(blob);
|
|
40455
|
-
}
|
|
40456
|
-
else if (this.fileDownload.isHtml(file)) {
|
|
40457
|
-
const content = await this.fileDownload.getText(this.attachment.url);
|
|
40458
|
-
// Safe: the content is only rendered inside a sandboxed iframe (see template)
|
|
40459
|
-
this.html = this.sanitizer.bypassSecurityTrustHtml(content);
|
|
40460
|
-
}
|
|
40461
|
-
else {
|
|
40462
|
-
this.markdown = this.fileDownload.isMarkdown(file);
|
|
40463
|
-
this.text = await this.fileDownload.getText(this.attachment.url);
|
|
40464
|
-
}
|
|
40465
|
-
}
|
|
40466
|
-
catch (err) {
|
|
40467
|
-
console.error(`[feed-attachment] Cannot load attachment {${this.attachment?.url}}`, err);
|
|
40468
|
-
this.error = 'SOCIAL.FEED.ATTACHMENTS.PREVIEW_ERROR';
|
|
40469
|
-
}
|
|
40470
|
-
finally {
|
|
40471
|
-
this.loading = false;
|
|
40472
|
-
this.cd.markForCheck();
|
|
40473
|
-
}
|
|
40474
|
-
}
|
|
40475
|
-
ngOnDestroy() {
|
|
40476
|
-
if (this.imageUrl)
|
|
40477
|
-
URL.revokeObjectURL(this.imageUrl);
|
|
40478
|
-
}
|
|
40479
|
-
async download() {
|
|
40480
|
-
await this.fileDownload.download(this.attachment.url, { filename: this.filename, mimeType: this.attachment.mime_type });
|
|
40481
|
-
}
|
|
40482
|
-
/** Display the modal in a large size (to restore the size: close then reopen the attachment) */
|
|
40483
|
-
maximize() {
|
|
40484
|
-
this.maximized = true;
|
|
40485
|
-
this.elementRef.nativeElement.closest('ion-modal')?.classList.add('modal-large');
|
|
40486
|
-
this.cd.markForCheck();
|
|
40487
|
-
}
|
|
40488
|
-
close() {
|
|
40489
|
-
return this.modalCtrl.dismiss();
|
|
40631
|
+
return FileAttachmentUtils.getIcon(FeedAttachmentUtils.toFileAttachment(attachment));
|
|
40490
40632
|
}
|
|
40491
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FeedAttachmentPreviewModal, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
40492
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: FeedAttachmentPreviewModal, isStandalone: false, selector: "app-feed-attachment-preview-modal", inputs: { attachment: "attachment" }, ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ filename }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button\n class=\"download-button\"\n (click)=\"download()\"\n [title]=\"'SOCIAL.FEED.ATTACHMENTS.BTN_DOWNLOAD' | translate\"\n >\n <mat-icon slot=\"icon-only\">download</mat-icon>\n </ion-button>\n @if (!mobile && !maximized) {\n <ion-button\n class=\"maximize-button\"\n (click)=\"maximize()\"\n [title]=\"'SOCIAL.FEED.ATTACHMENTS.BTN_MAXIMIZE' | translate\"\n >\n <mat-icon slot=\"icon-only\">fullscreen</mat-icon>\n </ion-button>\n }\n <ion-button class=\"close-button\" (click)=\"close()\" [title]=\"'COMMON.BTN_CLOSE' | translate\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n @if (loading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else if (error) {\n <ion-text color=\"danger\" class=\"preview-error\">{{ error | translate }}</ion-text>\n } @else if (imageUrl) {\n <img class=\"preview-image\" [src]=\"imageUrl\" [alt]=\"filename\" />\n } @else if (html) {\n <!-- sandboxed: scripts allowed, but no access to the application (no same origin) -->\n <iframe class=\"preview-html\" sandbox=\"allow-scripts\" [srcdoc]=\"html\" [title]=\"filename\"></iframe>\n } @else if (markdown) {\n <markdown class=\"preview-markdown\" [data]=\"text\"></markdown>\n } @else {\n <pre class=\"preview-text\">{{ text }}</pre>\n }\n</ion-content>\n", styles: [".preview-image{display:block;max-width:100%;margin:0 auto}.preview-text{white-space:pre-wrap;word-break:break-word;font-size:.9em}.preview-html{display:block;width:100%;height:calc(100% - 4px);min-height:60vh;border:none}\n"], dependencies: [{ kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$2.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: i4$3.MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: MarkdownDirective, selector: "markdown,[markdown]" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
40493
40633
|
}
|
|
40494
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FeedAttachmentPreviewModal, decorators: [{
|
|
40495
|
-
type: Component,
|
|
40496
|
-
args: [{ selector: 'app-feed-attachment-preview-modal', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ filename }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button\n class=\"download-button\"\n (click)=\"download()\"\n [title]=\"'SOCIAL.FEED.ATTACHMENTS.BTN_DOWNLOAD' | translate\"\n >\n <mat-icon slot=\"icon-only\">download</mat-icon>\n </ion-button>\n @if (!mobile && !maximized) {\n <ion-button\n class=\"maximize-button\"\n (click)=\"maximize()\"\n [title]=\"'SOCIAL.FEED.ATTACHMENTS.BTN_MAXIMIZE' | translate\"\n >\n <mat-icon slot=\"icon-only\">fullscreen</mat-icon>\n </ion-button>\n }\n <ion-button class=\"close-button\" (click)=\"close()\" [title]=\"'COMMON.BTN_CLOSE' | translate\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n @if (loading) {\n <ion-spinner name=\"crescent\"></ion-spinner>\n } @else if (error) {\n <ion-text color=\"danger\" class=\"preview-error\">{{ error | translate }}</ion-text>\n } @else if (imageUrl) {\n <img class=\"preview-image\" [src]=\"imageUrl\" [alt]=\"filename\" />\n } @else if (html) {\n <!-- sandboxed: scripts allowed, but no access to the application (no same origin) -->\n <iframe class=\"preview-html\" sandbox=\"allow-scripts\" [srcdoc]=\"html\" [title]=\"filename\"></iframe>\n } @else if (markdown) {\n <markdown class=\"preview-markdown\" [data]=\"text\"></markdown>\n } @else {\n <pre class=\"preview-text\">{{ text }}</pre>\n }\n</ion-content>\n", styles: [".preview-image{display:block;max-width:100%;margin:0 auto}.preview-text{white-space:pre-wrap;word-break:break-word;font-size:.9em}.preview-html{display:block;width:100%;height:calc(100% - 4px);min-height:60vh;border:none}\n"] }]
|
|
40497
|
-
}], propDecorators: { attachment: [{
|
|
40498
|
-
type: Input
|
|
40499
|
-
}] } });
|
|
40500
40634
|
|
|
40501
40635
|
class FeedDirective {
|
|
40502
40636
|
element;
|
|
@@ -40650,9 +40784,6 @@ class FeedsComponent {
|
|
|
40650
40784
|
platform = inject(PlatformService);
|
|
40651
40785
|
networkService = inject(NetworkService);
|
|
40652
40786
|
router = inject(Router);
|
|
40653
|
-
fileDownload = inject(FileDownloadService);
|
|
40654
|
-
modalCtrl = inject(ModalController);
|
|
40655
|
-
toastCtrl = inject(ToastController);
|
|
40656
40787
|
version;
|
|
40657
40788
|
modalItemId;
|
|
40658
40789
|
onRefresh = new EventEmitter();
|
|
@@ -40876,47 +41007,7 @@ class FeedsComponent {
|
|
|
40876
41007
|
getVisibleEntries(feeds, filterItem) {
|
|
40877
41008
|
return (feeds || []).flatMap((feed) => (feed?.items || []).filter((item) => !filterItem || filterItem(item)).map((item) => ({ feed, item })));
|
|
40878
41009
|
}
|
|
40879
|
-
|
|
40880
|
-
getAttachmentIcon = FeedAttachmentUtils.getIcon;
|
|
40881
|
-
canPreviewAttachment = (attachment) => this.fileDownload.canPreview({ name: FeedAttachmentUtils.getFilename(attachment), mimeType: attachment?.mime_type });
|
|
40882
|
-
/**
|
|
40883
|
-
* Open an attachment: display its content, if possible (text, markdown, image...), or download it
|
|
40884
|
-
*/
|
|
40885
|
-
async openAttachment(event, attachment) {
|
|
40886
|
-
event?.preventDefault();
|
|
40887
|
-
event?.stopPropagation();
|
|
40888
|
-
if (!attachment?.url)
|
|
40889
|
-
return;
|
|
40890
|
-
if (!this.canPreviewAttachment(attachment)) {
|
|
40891
|
-
return this.downloadAttachment(event, attachment);
|
|
40892
|
-
}
|
|
40893
|
-
const hasTopModal = !!(await this.modalCtrl.getTop());
|
|
40894
|
-
const modal = await this.modalCtrl.create({
|
|
40895
|
-
component: FeedAttachmentPreviewModal,
|
|
40896
|
-
componentProps: { attachment },
|
|
40897
|
-
cssClass: hasTopModal ? 'stack-modal' : undefined,
|
|
40898
|
-
});
|
|
40899
|
-
await modal.present();
|
|
40900
|
-
}
|
|
40901
|
-
/**
|
|
40902
|
-
* Download an attachment (using the native download manager on Android)
|
|
40903
|
-
*/
|
|
40904
|
-
async downloadAttachment(event, attachment) {
|
|
40905
|
-
event?.preventDefault();
|
|
40906
|
-
event?.stopPropagation();
|
|
40907
|
-
if (!attachment?.url)
|
|
40908
|
-
return;
|
|
40909
|
-
try {
|
|
40910
|
-
await this.fileDownload.download(attachment.url, {
|
|
40911
|
-
filename: FeedAttachmentUtils.getFilename(attachment),
|
|
40912
|
-
mimeType: attachment.mime_type,
|
|
40913
|
-
});
|
|
40914
|
-
}
|
|
40915
|
-
catch (err) {
|
|
40916
|
-
console.error(`[feed-component] Cannot download attachment {${attachment.url}}`, err);
|
|
40917
|
-
await Toasts.show(this.toastCtrl, this.translate, { type: 'error', message: 'SOCIAL.FEED.ATTACHMENTS.DOWNLOAD_ERROR' });
|
|
40918
|
-
}
|
|
40919
|
-
}
|
|
41010
|
+
toFileAttachment = FeedAttachmentUtils.toFileAttachment;
|
|
40920
41011
|
getFeedHomeUrl(feed) {
|
|
40921
41012
|
return feed.home_page_url ?? JsonFeedUtils.removeJsonExtension(feed.feed_url) ?? this.feedService.getHomeUrl();
|
|
40922
41013
|
}
|
|
@@ -40982,11 +41073,11 @@ class FeedsComponent {
|
|
|
40982
41073
|
}
|
|
40983
41074
|
}
|
|
40984
41075
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FeedsComponent, deps: [{ token: LocalSettingsService }, { token: ENVIRONMENT }, { token: APP_FEED_SERVICE, optional: true }, { token: AccountService }, { token: i2$2.AlertController }], target: i0.ɵɵFactoryTarget.Component });
|
|
40985
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: FeedsComponent, isStandalone: false, selector: "app-feed", inputs: { debug: "debug", mobile: "mobile", showHeader: "showHeader", showReadMoreButton: "showReadMoreButton", headerColor: "headerColor", cardColor: "cardColor", shape: "shape", class: "class", itemId: "itemId", filterItem: "filterItem", feeds: "feeds", urls: "urls", maxAgeInMonths: "maxAgeInMonths", maxContentLength: "maxContentLength" }, outputs: { editItem: "editItem", deleteItem: "deleteItem" }, host: { properties: { "class": "this.hostClass" } }, providers: [RxState], viewQueries: [{ propertyName: "modal", first: true, predicate: ["modal"], descendants: true }, { propertyName: "feedContent", first: true, predicate: ["feedContent"], descendants: true }, { propertyName: "feedItems", first: true, predicate: ["feedItems"], descendants: true }], ngImport: i0, template: "<!-- debug -->\n@if (debug) {\n <app-debug [title]=\"'Feed'\">\n <p>\n hasFeeds?: {{ hasFeeds$ | async }}\n <br />\n urls: {{ urls$ | async | json }}\n <br />\n shape: {{ shape }}\n </p>\n </app-debug>\n}\n\n@let feeds = feeds$ | async;\n@let userId = userId$ | async;\n\n@if (feeds | isNotEmptyArray) {\n <!-- top header -->\n @if (showHeader && (feeds | arrayFirst); as feed) {\n <ion-item lines=\"none\" [color]=\"headerColor\" class=\"feed-header shape-{{ shape }}\">\n <ion-icon slot=\"start\" name=\"megaphone\"></ion-icon>\n <ion-label>\n <b>{{ feed.title || ('SOCIAL.FEED.NEWS' | translate) }}</b>\n </ion-label>\n <ion-button slot=\"end\" fill=\"clear\" (click)=\"openFeedHome()\" shape=\"\">\n <ion-text>{{ 'SOCIAL.FEED.SHOW_ALL_FEED' | translate }}</ion-text>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n </ion-item>\n }\n\n <!-- scrollable (without visible scrollbar), with a progressive fade at the bottom, when more items are below -->\n <div\n #feedContent\n class=\"feed-content shape-{{ shape }} ion-no-padding\"\n [class.has-header]=\"showHeader\"\n (scroll)=\"updateOverflow()\"\n >\n <div #feedItems class=\"feed-items\">\n <!-- visible items, of all feeds (first/last are computed on the visible items, e.g. when filtered by itemId) -->\n @let entries = feeds | map: getVisibleEntries : filterItem;\n @for (entry of entries; track entry.feed.feed_url + '#' + entry.item.id; let first = $first; let last = $last) {\n @let feed = entry.feed;\n @let item = entry.item;\n <ion-card\n [class.first]=\"first\"\n [class.last]=\"last\"\n [color]=\"cardColor !== 'light-transparent' ? cardColor : undefined\"\n class=\"feed-item-card\"\n >\n <ion-card-header>\n <ion-card-subtitle style=\"vertical-align: middle\">\n <!-- Authors -->\n @for (author of item.authors || feed.authors; track author) {\n @if (author.name || author.avatar) {\n <ion-chip (click)=\"openUrl(author.url)\" tappable>\n @if (author.avatar) {\n <ion-avatar>\n <ion-img [src]=\"author.avatar\" [alt]=\"author.name\"></ion-img>\n </ion-avatar>\n }\n @if (author.name) {\n <ion-label class=\"author\">{{ author.name }}</ion-label>\n }\n </ion-chip>\n }\n }\n <ion-note class=\"ion-float-end\">\n <small>{{ item.date_published | dateFromNow }}</small>\n </ion-note>\n </ion-card-subtitle>\n\n <!-- title -->\n <ion-card-title (click)=\"openFeedItem(item, feed)\" tappable>{{ item?.title }}</ion-card-title>\n\n <!-- tags -->\n @let tags = item | map: getTags;\n @if (tags | isNotEmptyArray) {\n <ion-text class=\"tags\">\n @for (tag of tags; track tag; let last = $last) {\n <a (click)=\"openTag(feed, tag)\" tappable>\n <ion-text>#{{ tag }}</ion-text>\n </a>\n @if (!last) {\n \n }\n }\n </ion-text>\n }\n </ion-card-header>\n\n <!-- Feed content -->\n <ion-card-content>\n <ion-text [feed]=\"item.url || feed.feed_url\">\n @if (item.content_html) {\n <p [innerHTML]=\"item.content_html\"></p>\n } @else if (item.content_text) {\n <p>\n <markdown [data]=\"item.content_text\" emoji></markdown>\n </p>\n }\n </ion-text>\n </ion-card-content>\n\n <!-- Attachments -->\n @if (item.attachments | isNotEmptyArray) {\n <div class=\"feed-attachments\" [class.mobile]=\"mobile\">\n @for (attachment of item.attachments; track attachment.url) {\n @let filename = attachment | map: getAttachmentFilename;\n @let canPreview = attachment | map: canPreviewAttachment;\n @let action =\n (canPreview ? 'SOCIAL.FEED.ATTACHMENTS.BTN_PREVIEW' : 'SOCIAL.FEED.ATTACHMENTS.BTN_DOWNLOAD')\n | translate;\n <ion-chip\n class=\"feed-attachment\"\n [class.has-download]=\"canPreview\"\n [outline]=\"true\"\n [title]=\"\n filename +\n (attachment.size_in_bytes ? ' (' + (attachment.size_in_bytes | fileSize) + ')' : '') +\n ' - ' +\n action\n \"\n (click)=\"openAttachment($event, attachment)\"\n tappable\n >\n <mat-icon class=\"attachment-icon\">{{ attachment | map: getAttachmentIcon }}</mat-icon>\n <ion-label class=\"attachment-name\">{{ filename }}</ion-label>\n @if (canPreview) {\n <!-- download part (separated, visible on hover): preview is the default action -->\n <span\n class=\"attachment-download\"\n role=\"button\"\n [title]=\"'SOCIAL.FEED.ATTACHMENTS.BTN_DOWNLOAD' | translate\"\n (click)=\"downloadAttachment($event, attachment)\"\n >\n <mat-icon>download</mat-icon>\n </span>\n }\n </ion-chip>\n }\n </div>\n }\n\n @let editable = canEditItem(item, userId, feed);\n @if (editable || showReadMoreButton) {\n @if (editable) {\n <!-- Delete button (visible hover)-->\n <button\n mat-icon-button\n (click)=\"onDeleteItem($event, item)\"\n class=\"visible-hover ion-float-start\"\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n <!-- Edit button (visible hover) -->\n <ion-button (click)=\"onEditItem($event, item)\" class=\"visible-hover ion-float-start\" fill=\"clear\">\n <mat-icon slot=\"start\">edit</mat-icon>\n <!-- <ion-icon name=\"pencil\" slot=\"start\"></ion-icon>-->\n <ion-text>{{ 'COMMON.BTN_EDIT' | translate }}</ion-text>\n </ion-button>\n }\n @if (showReadMoreButton) {\n <ion-button (click)=\"openFeedItem(item, feed)\" class=\"ion-float-end\" fill=\"clear\">\n <ion-label>{{ (item.truncated ? 'SOCIAL.FEED.READ_MORE' : 'COMMON.BTN_SHOW') | translate }}</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n }\n }\n </ion-card>\n }\n </div>\n </div>\n}\n\n<!-- Details modal -->\n<ion-modal #modal [showBackdrop]=\"false\" class=\"stack-modal\" [class.modal-large]=\"!mobile\">\n <ng-template>\n <ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ 'SOCIAL.FEED.NEWS' | translate }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"modal.dismiss()\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content>\n <div class=\"ion-padding\">\n <app-feed\n [urls]=\"urls\"\n [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [maxContentLength]=\"-1\"\n [maxAgeInMonths]=\"-1\"\n [itemId]=\"modalItemId\"\n cardColor=\"light\"\n [debug]=\"debug\"\n ></app-feed>\n </div>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [":host{display:flex;flex-direction:column;min-height:0;height:calc(100% - 10px);max-height:fit-content;overflow:hidden;--feed-header-height: 48px;-webkit-box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f;-webkit-margin-start:10px;margin-inline-start:10px;-webkit-margin-end:10px;margin-inline-end:10px;margin-top:0;margin-bottom:10px;--feed-border-radius: 4px;border-radius:var(--feed-border-radius)}:host.shape-round{--feed-border-radius: 12px}:host{--ion-card-background: rgba(var(--ion-background-color-rgb), .6)}ion-button{text-transform:unset;--color: rgba(var(--ion-color-contrast-rgb), .7)}ion-item.feed-header{flex:none;height:var(--feed-header-height);margin:0;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);border-radius:var(--feed-border-radius) var(--feed-border-radius) 0 0}ion-item.feed-header ion-icon[slot=start]{-webkit-margin-end:8px;margin-inline-end:8px}.feed-content{flex:1 1 auto;min-height:0;overflow-y:auto;--margin-bottom: 8px;--feed-fade-height: 64px;scrollbar-width:none}.feed-content::-webkit-scrollbar{display:none}.feed-content ion-card.feed-item-card{--top-radius: 4px;--bottom-radius: 4px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin:0 0 var(--margin-bottom) 0;border-radius:var(--top-radius) var(--top-radius) var(--bottom-radius) var(--bottom-radius);--ion-card-color-contrast-rgb: var(--ion-color-contrast-rgb, var(--ion-color-dark-rgb, 0, 0, 0));--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card.first{--top-radius: 0}.feed-content ion-card.feed-item-card.last{--margin-bottom: 0;--bottom-radius: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-title{--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip{--background: transparent;--border-color: transparent;--border-width: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-avatar{--color: rgba(var(--ion-card-color-contrast-rgb), .6);border:1px solid var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-label{--color: rgba(var(--ion-card-color-contrast-rgb), .8);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-note{--color: rgba(var(--ion-card-color-contrast-rgb), .6);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header .tags{display:inline}.feed-content ion-card.feed-item-card ion-card-header .tags a{color:rgba(var(--ion-card-color-contrast-rgb),.6)!important}.feed-content ion-card.feed-item-card ion-card-content ion-text ::ng-deep img{max-width:100%;height:auto!important}.feed-content ion-card.feed-item-card .visible-hover{opacity:0;transition:opacity .2s ease-in-out}.feed-content ion-card.feed-item-card:hover .visible-hover{opacity:1}.feed-attachments{display:flex;flex-wrap:wrap;gap:4px;padding:0 16px 8px}.feed-attachments .feed-attachment{--feed-attachment-height: 22px;--feed-attachment-icon-size: 14px;--feed-attachment-padding: 6px;max-width:100%;height:var(--feed-attachment-height);min-height:var(--feed-attachment-height);margin:0;padding:0 var(--feed-attachment-padding);font-size:12px;border-width:1px;overflow:hidden;color:var(--ion-text-color, #000);border-color:rgba(var(--ion-text-color-rgb, 0, 0, 0),.4)}.feed-attachments .feed-attachment mat-icon{flex-shrink:0;width:var(--feed-attachment-icon-size);height:var(--feed-attachment-icon-size);font-size:var(--feed-attachment-icon-size);line-height:var(--feed-attachment-icon-size)}.feed-attachments .feed-attachment .attachment-icon{margin-inline-end:4px}.feed-attachments .feed-attachment .attachment-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.feed-attachments .feed-attachment .attachment-download{display:flex;align-items:center;align-self:stretch;flex-shrink:0;overflow:hidden;max-width:0;margin-inline-start:0;padding:0;border-inline-start:0 solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.3);opacity:0;transition:max-width .15s ease-in-out,opacity .15s ease-in-out,background-color .15s ease-in-out}.feed-attachments .feed-attachment.has-download:hover{padding-inline-end:0}.feed-attachments .feed-attachment:hover .attachment-download{max-width:calc(var(--feed-attachment-icon-size) + 2 * var(--feed-attachment-padding) + 1px);margin-inline-start:var(--feed-attachment-padding);padding:0 var(--feed-attachment-padding);border-inline-start-width:1px;opacity:1}.feed-attachments .feed-attachment .attachment-download:hover{color:var(--ion-color-primary);background-color:rgba(var(--ion-color-primary-rgb, 0, 0, 0),.12)}.feed-attachments.mobile .feed-attachment{--feed-attachment-height: 32px;--feed-attachment-icon-size: 18px;--feed-attachment-padding: 10px;font-size:14px}.feed-attachments.mobile .feed-attachment.has-download{padding-inline-end:0}.feed-attachments.mobile .feed-attachment .attachment-download{max-width:calc(var(--feed-attachment-icon-size) + 2 * var(--feed-attachment-padding) + 1px);margin-inline-start:var(--feed-attachment-padding);padding:0 var(--feed-attachment-padding);border-inline-start-width:1px;opacity:1}\n"], dependencies: [{ kind: "component", type: i2$2.IonAvatar, selector: "ion-avatar" }, { kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: i2$2.IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: i2$2.IonCardSubtitle, selector: "ion-card-subtitle", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonChip, selector: "ion-chip", inputs: ["color", "disabled", "mode", "outline"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonImg, selector: "ion-img", inputs: ["alt", "src"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonModal, selector: "ion-modal" }, { kind: "component", type: i2$2.IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$2.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: i4$3.MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: DebugComponent, selector: "app-debug", inputs: ["titlePrefix", "title", "enable", "expanded"] }, { kind: "directive", type: MarkdownDirective, selector: "markdown,[markdown]" }, { kind: "component", type: FeedsComponent, selector: "app-feed", inputs: ["debug", "mobile", "showHeader", "showReadMoreButton", "headerColor", "cardColor", "shape", "class", "itemId", "filterItem", "feeds", "urls", "maxAgeInMonths", "maxContentLength"], outputs: ["editItem", "deleteItem"] }, { kind: "directive", type: FeedDirective, selector: "feed,[feed]", inputs: ["feed"] }, { kind: "pipe", type: i2$3.AsyncPipe, name: "async" }, { kind: "pipe", type: i2$3.JsonPipe, name: "json" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: DateFromNowPipe, name: "dateFromNow" }, { kind: "pipe", type: FileSizePipe, name: "fileSize" }, { kind: "pipe", type: NotEmptyArrayPipe, name: "isNotEmptyArray" }, { kind: "pipe", type: ArrayFirstPipe, name: "arrayFirst" }, { kind: "pipe", type: MapPipe, name: "map" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
41076
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: FeedsComponent, isStandalone: false, selector: "app-feed", inputs: { debug: "debug", mobile: "mobile", showHeader: "showHeader", showReadMoreButton: "showReadMoreButton", headerColor: "headerColor", cardColor: "cardColor", shape: "shape", class: "class", itemId: "itemId", filterItem: "filterItem", feeds: "feeds", urls: "urls", maxAgeInMonths: "maxAgeInMonths", maxContentLength: "maxContentLength" }, outputs: { editItem: "editItem", deleteItem: "deleteItem" }, host: { properties: { "class": "this.hostClass" } }, providers: [RxState], viewQueries: [{ propertyName: "modal", first: true, predicate: ["modal"], descendants: true }, { propertyName: "feedContent", first: true, predicate: ["feedContent"], descendants: true }, { propertyName: "feedItems", first: true, predicate: ["feedItems"], descendants: true }], ngImport: i0, template: "<!-- debug -->\n@if (debug) {\n <app-debug [title]=\"'Feed'\">\n <p>\n hasFeeds?: {{ hasFeeds$ | async }}\n <br />\n urls: {{ urls$ | async | json }}\n <br />\n shape: {{ shape }}\n </p>\n </app-debug>\n}\n\n@let feeds = feeds$ | async;\n@let userId = userId$ | async;\n\n@if (feeds | isNotEmptyArray) {\n <!-- top header -->\n @if (showHeader && (feeds | arrayFirst); as feed) {\n <ion-item lines=\"none\" [color]=\"headerColor\" class=\"feed-header shape-{{ shape }}\">\n <ion-icon slot=\"start\" name=\"megaphone\"></ion-icon>\n <ion-label>\n <b>{{ feed.title || ('SOCIAL.FEED.NEWS' | translate) }}</b>\n </ion-label>\n <ion-button slot=\"end\" fill=\"clear\" (click)=\"openFeedHome()\" shape=\"\">\n <ion-text>{{ 'SOCIAL.FEED.SHOW_ALL_FEED' | translate }}</ion-text>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n </ion-item>\n }\n\n <!-- scrollable (without visible scrollbar), with a progressive fade at the bottom, when more items are below -->\n <div\n #feedContent\n class=\"feed-content shape-{{ shape }} ion-no-padding\"\n [class.has-header]=\"showHeader\"\n (scroll)=\"updateOverflow()\"\n >\n <div #feedItems class=\"feed-items\">\n <!-- visible items, of all feeds (first/last are computed on the visible items, e.g. when filtered by itemId) -->\n @let entries = feeds | map: getVisibleEntries : filterItem;\n @for (entry of entries; track entry.feed.feed_url + '#' + entry.item.id; let first = $first; let last = $last) {\n @let feed = entry.feed;\n @let item = entry.item;\n <ion-card\n [class.first]=\"first\"\n [class.last]=\"last\"\n [color]=\"cardColor !== 'light-transparent' ? cardColor : undefined\"\n class=\"feed-item-card\"\n >\n <ion-card-header>\n <ion-card-subtitle style=\"vertical-align: middle\">\n <!-- Authors -->\n @for (author of item.authors || feed.authors; track author) {\n @if (author.name || author.avatar) {\n <ion-chip (click)=\"openUrl(author.url)\" tappable>\n @if (author.avatar) {\n <ion-avatar>\n <ion-img [src]=\"author.avatar\" [alt]=\"author.name\"></ion-img>\n </ion-avatar>\n }\n @if (author.name) {\n <ion-label class=\"author\">{{ author.name }}</ion-label>\n }\n </ion-chip>\n }\n }\n <ion-note class=\"ion-float-end\">\n <small>{{ item.date_published | dateFromNow }}</small>\n </ion-note>\n </ion-card-subtitle>\n\n <!-- title -->\n <ion-card-title (click)=\"openFeedItem(item, feed)\" tappable>{{ item?.title }}</ion-card-title>\n\n <!-- tags -->\n @let tags = item | map: getTags;\n @if (tags | isNotEmptyArray) {\n <ion-text class=\"tags\">\n @for (tag of tags; track tag; let last = $last) {\n <a (click)=\"openTag(feed, tag)\" tappable>\n <ion-text>#{{ tag }}</ion-text>\n </a>\n @if (!last) {\n \n }\n }\n </ion-text>\n }\n </ion-card-header>\n\n <!-- Feed content -->\n <ion-card-content>\n <ion-text [feed]=\"item.url || feed.feed_url\">\n @if (item.content_html) {\n <p [innerHTML]=\"item.content_html\"></p>\n } @else if (item.content_text) {\n <p>\n <markdown [data]=\"item.content_text\" emoji></markdown>\n </p>\n }\n </ion-text>\n </ion-card-content>\n\n <!-- Attachments -->\n @if (item.attachments | isNotEmptyArray) {\n <div class=\"feed-attachments\">\n @for (attachment of item.attachments; track attachment.url) {\n <app-file-attachment class=\"feed-attachment\" [attachment]=\"attachment | map: toFileAttachment\" [mobile]=\"mobile\"></app-file-attachment>\n }\n </div>\n }\n\n @let editable = canEditItem(item, userId, feed);\n @if (editable || showReadMoreButton) {\n @if (editable) {\n <!-- Delete button (visible hover)-->\n <button\n mat-icon-button\n (click)=\"onDeleteItem($event, item)\"\n class=\"visible-hover ion-float-start\"\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n <!-- Edit button (visible hover) -->\n <ion-button (click)=\"onEditItem($event, item)\" class=\"visible-hover ion-float-start\" fill=\"clear\">\n <mat-icon slot=\"start\">edit</mat-icon>\n <!-- <ion-icon name=\"pencil\" slot=\"start\"></ion-icon>-->\n <ion-text>{{ 'COMMON.BTN_EDIT' | translate }}</ion-text>\n </ion-button>\n }\n @if (showReadMoreButton) {\n <ion-button (click)=\"openFeedItem(item, feed)\" class=\"ion-float-end\" fill=\"clear\">\n <ion-label>{{ (item.truncated ? 'SOCIAL.FEED.READ_MORE' : 'COMMON.BTN_SHOW') | translate }}</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n }\n }\n </ion-card>\n }\n </div>\n </div>\n}\n\n<!-- Details modal -->\n<ion-modal #modal [showBackdrop]=\"false\" class=\"stack-modal\" [class.modal-large]=\"!mobile\">\n <ng-template>\n <ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ 'SOCIAL.FEED.NEWS' | translate }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"modal.dismiss()\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content>\n <div class=\"ion-padding\">\n <app-feed\n [urls]=\"urls\"\n [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [maxContentLength]=\"-1\"\n [maxAgeInMonths]=\"-1\"\n [itemId]=\"modalItemId\"\n cardColor=\"light\"\n [debug]=\"debug\"\n ></app-feed>\n </div>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [":host{display:flex;flex-direction:column;min-height:0;height:calc(100% - 10px);max-height:fit-content;overflow:hidden;--feed-header-height: 48px;-webkit-box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f;-webkit-margin-start:10px;margin-inline-start:10px;-webkit-margin-end:10px;margin-inline-end:10px;margin-top:0;margin-bottom:10px;--feed-border-radius: 4px;border-radius:var(--feed-border-radius)}:host.shape-round{--feed-border-radius: 12px}:host{--ion-card-background: rgba(var(--ion-background-color-rgb), .6)}ion-button{text-transform:unset;--color: rgba(var(--ion-color-contrast-rgb), .7)}ion-item.feed-header{flex:none;height:var(--feed-header-height);margin:0;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);border-radius:var(--feed-border-radius) var(--feed-border-radius) 0 0}ion-item.feed-header ion-icon[slot=start]{-webkit-margin-end:8px;margin-inline-end:8px}.feed-content{flex:1 1 auto;min-height:0;overflow-y:auto;--margin-bottom: 8px;--feed-fade-height: 64px;scrollbar-width:none}.feed-content::-webkit-scrollbar{display:none}.feed-content ion-card.feed-item-card{--top-radius: 4px;--bottom-radius: 4px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin:0 0 var(--margin-bottom) 0;border-radius:var(--top-radius) var(--top-radius) var(--bottom-radius) var(--bottom-radius);--ion-card-color-contrast-rgb: var(--ion-color-contrast-rgb, var(--ion-color-dark-rgb, 0, 0, 0));--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card.first{--top-radius: 0}.feed-content ion-card.feed-item-card.last{--margin-bottom: 0;--bottom-radius: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-title{--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip{--background: transparent;--border-color: transparent;--border-width: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-avatar{--color: rgba(var(--ion-card-color-contrast-rgb), .6);border:1px solid var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-label{--color: rgba(var(--ion-card-color-contrast-rgb), .8);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-note{--color: rgba(var(--ion-card-color-contrast-rgb), .6);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header .tags{display:inline}.feed-content ion-card.feed-item-card ion-card-header .tags a{color:rgba(var(--ion-card-color-contrast-rgb),.6)!important}.feed-content ion-card.feed-item-card ion-card-content ion-text ::ng-deep img{max-width:100%;height:auto!important}.feed-content ion-card.feed-item-card .visible-hover{opacity:0;transition:opacity .2s ease-in-out}.feed-content ion-card.feed-item-card:hover .visible-hover{opacity:1}.feed-attachments{display:flex;flex-wrap:wrap;gap:4px;padding:0 16px 8px}\n"], dependencies: [{ kind: "component", type: i2$2.IonAvatar, selector: "ion-avatar" }, { kind: "component", type: i2$2.IonButton, selector: "ion-button", inputs: ["buttonType", "color", "disabled", "download", "expand", "fill", "form", "href", "mode", "rel", "routerAnimation", "routerDirection", "shape", "size", "strong", "target", "type"] }, { kind: "component", type: i2$2.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$2.IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: i2$2.IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: i2$2.IonCardSubtitle, selector: "ion-card-subtitle", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonChip, selector: "ion-chip", inputs: ["color", "disabled", "mode", "outline"] }, { kind: "component", type: i2$2.IonContent, selector: "ion-content", inputs: ["color", "fixedSlotPlacement", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$2.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$2.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$2.IonImg, selector: "ion-img", inputs: ["alt", "src"] }, { kind: "component", type: i2$2.IonItem, selector: "ion-item", inputs: ["button", "color", "detail", "detailIcon", "disabled", "download", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$2.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$2.IonModal, selector: "ion-modal" }, { kind: "component", type: i2$2.IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$2.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$2.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: i4$3.MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }, { kind: "component", type: i6$2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: DebugComponent, selector: "app-debug", inputs: ["titlePrefix", "title", "enable", "expanded"] }, { kind: "directive", type: MarkdownDirective, selector: "markdown,[markdown]" }, { kind: "component", type: FileAttachmentComponent, selector: "app-file-attachment", inputs: ["attachment", "mobile"] }, { kind: "component", type: FeedsComponent, selector: "app-feed", inputs: ["debug", "mobile", "showHeader", "showReadMoreButton", "headerColor", "cardColor", "shape", "class", "itemId", "filterItem", "feeds", "urls", "maxAgeInMonths", "maxContentLength"], outputs: ["editItem", "deleteItem"] }, { kind: "directive", type: FeedDirective, selector: "feed,[feed]", inputs: ["feed"] }, { kind: "pipe", type: i2$3.AsyncPipe, name: "async" }, { kind: "pipe", type: i2$3.JsonPipe, name: "json" }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }, { kind: "pipe", type: DateFromNowPipe, name: "dateFromNow" }, { kind: "pipe", type: NotEmptyArrayPipe, name: "isNotEmptyArray" }, { kind: "pipe", type: ArrayFirstPipe, name: "arrayFirst" }, { kind: "pipe", type: MapPipe, name: "map" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
40986
41077
|
}
|
|
40987
41078
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FeedsComponent, decorators: [{
|
|
40988
41079
|
type: Component,
|
|
40989
|
-
args: [{ selector: 'app-feed', providers: [RxState], changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<!-- debug -->\n@if (debug) {\n <app-debug [title]=\"'Feed'\">\n <p>\n hasFeeds?: {{ hasFeeds$ | async }}\n <br />\n urls: {{ urls$ | async | json }}\n <br />\n shape: {{ shape }}\n </p>\n </app-debug>\n}\n\n@let feeds = feeds$ | async;\n@let userId = userId$ | async;\n\n@if (feeds | isNotEmptyArray) {\n <!-- top header -->\n @if (showHeader && (feeds | arrayFirst); as feed) {\n <ion-item lines=\"none\" [color]=\"headerColor\" class=\"feed-header shape-{{ shape }}\">\n <ion-icon slot=\"start\" name=\"megaphone\"></ion-icon>\n <ion-label>\n <b>{{ feed.title || ('SOCIAL.FEED.NEWS' | translate) }}</b>\n </ion-label>\n <ion-button slot=\"end\" fill=\"clear\" (click)=\"openFeedHome()\" shape=\"\">\n <ion-text>{{ 'SOCIAL.FEED.SHOW_ALL_FEED' | translate }}</ion-text>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n </ion-item>\n }\n\n <!-- scrollable (without visible scrollbar), with a progressive fade at the bottom, when more items are below -->\n <div\n #feedContent\n class=\"feed-content shape-{{ shape }} ion-no-padding\"\n [class.has-header]=\"showHeader\"\n (scroll)=\"updateOverflow()\"\n >\n <div #feedItems class=\"feed-items\">\n <!-- visible items, of all feeds (first/last are computed on the visible items, e.g. when filtered by itemId) -->\n @let entries = feeds | map: getVisibleEntries : filterItem;\n @for (entry of entries; track entry.feed.feed_url + '#' + entry.item.id; let first = $first; let last = $last) {\n @let feed = entry.feed;\n @let item = entry.item;\n <ion-card\n [class.first]=\"first\"\n [class.last]=\"last\"\n [color]=\"cardColor !== 'light-transparent' ? cardColor : undefined\"\n class=\"feed-item-card\"\n >\n <ion-card-header>\n <ion-card-subtitle style=\"vertical-align: middle\">\n <!-- Authors -->\n @for (author of item.authors || feed.authors; track author) {\n @if (author.name || author.avatar) {\n <ion-chip (click)=\"openUrl(author.url)\" tappable>\n @if (author.avatar) {\n <ion-avatar>\n <ion-img [src]=\"author.avatar\" [alt]=\"author.name\"></ion-img>\n </ion-avatar>\n }\n @if (author.name) {\n <ion-label class=\"author\">{{ author.name }}</ion-label>\n }\n </ion-chip>\n }\n }\n <ion-note class=\"ion-float-end\">\n <small>{{ item.date_published | dateFromNow }}</small>\n </ion-note>\n </ion-card-subtitle>\n\n <!-- title -->\n <ion-card-title (click)=\"openFeedItem(item, feed)\" tappable>{{ item?.title }}</ion-card-title>\n\n <!-- tags -->\n @let tags = item | map: getTags;\n @if (tags | isNotEmptyArray) {\n <ion-text class=\"tags\">\n @for (tag of tags; track tag; let last = $last) {\n <a (click)=\"openTag(feed, tag)\" tappable>\n <ion-text>#{{ tag }}</ion-text>\n </a>\n @if (!last) {\n \n }\n }\n </ion-text>\n }\n </ion-card-header>\n\n <!-- Feed content -->\n <ion-card-content>\n <ion-text [feed]=\"item.url || feed.feed_url\">\n @if (item.content_html) {\n <p [innerHTML]=\"item.content_html\"></p>\n } @else if (item.content_text) {\n <p>\n <markdown [data]=\"item.content_text\" emoji></markdown>\n </p>\n }\n </ion-text>\n </ion-card-content>\n\n <!-- Attachments -->\n @if (item.attachments | isNotEmptyArray) {\n <div class=\"feed-attachments\" [class.mobile]=\"mobile\">\n @for (attachment of item.attachments; track attachment.url) {\n @let filename = attachment | map: getAttachmentFilename;\n @let canPreview = attachment | map: canPreviewAttachment;\n @let action =\n (canPreview ? 'SOCIAL.FEED.ATTACHMENTS.BTN_PREVIEW' : 'SOCIAL.FEED.ATTACHMENTS.BTN_DOWNLOAD')\n | translate;\n <ion-chip\n class=\"feed-attachment\"\n [class.has-download]=\"canPreview\"\n [outline]=\"true\"\n [title]=\"\n filename +\n (attachment.size_in_bytes ? ' (' + (attachment.size_in_bytes | fileSize) + ')' : '') +\n ' - ' +\n action\n \"\n (click)=\"openAttachment($event, attachment)\"\n tappable\n >\n <mat-icon class=\"attachment-icon\">{{ attachment | map: getAttachmentIcon }}</mat-icon>\n <ion-label class=\"attachment-name\">{{ filename }}</ion-label>\n @if (canPreview) {\n <!-- download part (separated, visible on hover): preview is the default action -->\n <span\n class=\"attachment-download\"\n role=\"button\"\n [title]=\"'SOCIAL.FEED.ATTACHMENTS.BTN_DOWNLOAD' | translate\"\n (click)=\"downloadAttachment($event, attachment)\"\n >\n <mat-icon>download</mat-icon>\n </span>\n }\n </ion-chip>\n }\n </div>\n }\n\n @let editable = canEditItem(item, userId, feed);\n @if (editable || showReadMoreButton) {\n @if (editable) {\n <!-- Delete button (visible hover)-->\n <button\n mat-icon-button\n (click)=\"onDeleteItem($event, item)\"\n class=\"visible-hover ion-float-start\"\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n <!-- Edit button (visible hover) -->\n <ion-button (click)=\"onEditItem($event, item)\" class=\"visible-hover ion-float-start\" fill=\"clear\">\n <mat-icon slot=\"start\">edit</mat-icon>\n <!-- <ion-icon name=\"pencil\" slot=\"start\"></ion-icon>-->\n <ion-text>{{ 'COMMON.BTN_EDIT' | translate }}</ion-text>\n </ion-button>\n }\n @if (showReadMoreButton) {\n <ion-button (click)=\"openFeedItem(item, feed)\" class=\"ion-float-end\" fill=\"clear\">\n <ion-label>{{ (item.truncated ? 'SOCIAL.FEED.READ_MORE' : 'COMMON.BTN_SHOW') | translate }}</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n }\n }\n </ion-card>\n }\n </div>\n </div>\n}\n\n<!-- Details modal -->\n<ion-modal #modal [showBackdrop]=\"false\" class=\"stack-modal\" [class.modal-large]=\"!mobile\">\n <ng-template>\n <ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ 'SOCIAL.FEED.NEWS' | translate }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"modal.dismiss()\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content>\n <div class=\"ion-padding\">\n <app-feed\n [urls]=\"urls\"\n [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [maxContentLength]=\"-1\"\n [maxAgeInMonths]=\"-1\"\n [itemId]=\"modalItemId\"\n cardColor=\"light\"\n [debug]=\"debug\"\n ></app-feed>\n </div>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [":host{display:flex;flex-direction:column;min-height:0;height:calc(100% - 10px);max-height:fit-content;overflow:hidden;--feed-header-height: 48px;-webkit-box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f;-webkit-margin-start:10px;margin-inline-start:10px;-webkit-margin-end:10px;margin-inline-end:10px;margin-top:0;margin-bottom:10px;--feed-border-radius: 4px;border-radius:var(--feed-border-radius)}:host.shape-round{--feed-border-radius: 12px}:host{--ion-card-background: rgba(var(--ion-background-color-rgb), .6)}ion-button{text-transform:unset;--color: rgba(var(--ion-color-contrast-rgb), .7)}ion-item.feed-header{flex:none;height:var(--feed-header-height);margin:0;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);border-radius:var(--feed-border-radius) var(--feed-border-radius) 0 0}ion-item.feed-header ion-icon[slot=start]{-webkit-margin-end:8px;margin-inline-end:8px}.feed-content{flex:1 1 auto;min-height:0;overflow-y:auto;--margin-bottom: 8px;--feed-fade-height: 64px;scrollbar-width:none}.feed-content::-webkit-scrollbar{display:none}.feed-content ion-card.feed-item-card{--top-radius: 4px;--bottom-radius: 4px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin:0 0 var(--margin-bottom) 0;border-radius:var(--top-radius) var(--top-radius) var(--bottom-radius) var(--bottom-radius);--ion-card-color-contrast-rgb: var(--ion-color-contrast-rgb, var(--ion-color-dark-rgb, 0, 0, 0));--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card.first{--top-radius: 0}.feed-content ion-card.feed-item-card.last{--margin-bottom: 0;--bottom-radius: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-title{--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip{--background: transparent;--border-color: transparent;--border-width: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-avatar{--color: rgba(var(--ion-card-color-contrast-rgb), .6);border:1px solid var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-label{--color: rgba(var(--ion-card-color-contrast-rgb), .8);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-note{--color: rgba(var(--ion-card-color-contrast-rgb), .6);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header .tags{display:inline}.feed-content ion-card.feed-item-card ion-card-header .tags a{color:rgba(var(--ion-card-color-contrast-rgb),.6)!important}.feed-content ion-card.feed-item-card ion-card-content ion-text ::ng-deep img{max-width:100%;height:auto!important}.feed-content ion-card.feed-item-card .visible-hover{opacity:0;transition:opacity .2s ease-in-out}.feed-content ion-card.feed-item-card:hover .visible-hover{opacity:1}.feed-attachments{display:flex;flex-wrap:wrap;gap:4px;padding:0 16px 8px}.feed-attachments .feed-attachment{--feed-attachment-height: 22px;--feed-attachment-icon-size: 14px;--feed-attachment-padding: 6px;max-width:100%;height:var(--feed-attachment-height);min-height:var(--feed-attachment-height);margin:0;padding:0 var(--feed-attachment-padding);font-size:12px;border-width:1px;overflow:hidden;color:var(--ion-text-color, #000);border-color:rgba(var(--ion-text-color-rgb, 0, 0, 0),.4)}.feed-attachments .feed-attachment mat-icon{flex-shrink:0;width:var(--feed-attachment-icon-size);height:var(--feed-attachment-icon-size);font-size:var(--feed-attachment-icon-size);line-height:var(--feed-attachment-icon-size)}.feed-attachments .feed-attachment .attachment-icon{margin-inline-end:4px}.feed-attachments .feed-attachment .attachment-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.feed-attachments .feed-attachment .attachment-download{display:flex;align-items:center;align-self:stretch;flex-shrink:0;overflow:hidden;max-width:0;margin-inline-start:0;padding:0;border-inline-start:0 solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.3);opacity:0;transition:max-width .15s ease-in-out,opacity .15s ease-in-out,background-color .15s ease-in-out}.feed-attachments .feed-attachment.has-download:hover{padding-inline-end:0}.feed-attachments .feed-attachment:hover .attachment-download{max-width:calc(var(--feed-attachment-icon-size) + 2 * var(--feed-attachment-padding) + 1px);margin-inline-start:var(--feed-attachment-padding);padding:0 var(--feed-attachment-padding);border-inline-start-width:1px;opacity:1}.feed-attachments .feed-attachment .attachment-download:hover{color:var(--ion-color-primary);background-color:rgba(var(--ion-color-primary-rgb, 0, 0, 0),.12)}.feed-attachments.mobile .feed-attachment{--feed-attachment-height: 32px;--feed-attachment-icon-size: 18px;--feed-attachment-padding: 10px;font-size:14px}.feed-attachments.mobile .feed-attachment.has-download{padding-inline-end:0}.feed-attachments.mobile .feed-attachment .attachment-download{max-width:calc(var(--feed-attachment-icon-size) + 2 * var(--feed-attachment-padding) + 1px);margin-inline-start:var(--feed-attachment-padding);padding:0 var(--feed-attachment-padding);border-inline-start-width:1px;opacity:1}\n"] }]
|
|
41080
|
+
args: [{ selector: 'app-feed', providers: [RxState], changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<!-- debug -->\n@if (debug) {\n <app-debug [title]=\"'Feed'\">\n <p>\n hasFeeds?: {{ hasFeeds$ | async }}\n <br />\n urls: {{ urls$ | async | json }}\n <br />\n shape: {{ shape }}\n </p>\n </app-debug>\n}\n\n@let feeds = feeds$ | async;\n@let userId = userId$ | async;\n\n@if (feeds | isNotEmptyArray) {\n <!-- top header -->\n @if (showHeader && (feeds | arrayFirst); as feed) {\n <ion-item lines=\"none\" [color]=\"headerColor\" class=\"feed-header shape-{{ shape }}\">\n <ion-icon slot=\"start\" name=\"megaphone\"></ion-icon>\n <ion-label>\n <b>{{ feed.title || ('SOCIAL.FEED.NEWS' | translate) }}</b>\n </ion-label>\n <ion-button slot=\"end\" fill=\"clear\" (click)=\"openFeedHome()\" shape=\"\">\n <ion-text>{{ 'SOCIAL.FEED.SHOW_ALL_FEED' | translate }}</ion-text>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n </ion-item>\n }\n\n <!-- scrollable (without visible scrollbar), with a progressive fade at the bottom, when more items are below -->\n <div\n #feedContent\n class=\"feed-content shape-{{ shape }} ion-no-padding\"\n [class.has-header]=\"showHeader\"\n (scroll)=\"updateOverflow()\"\n >\n <div #feedItems class=\"feed-items\">\n <!-- visible items, of all feeds (first/last are computed on the visible items, e.g. when filtered by itemId) -->\n @let entries = feeds | map: getVisibleEntries : filterItem;\n @for (entry of entries; track entry.feed.feed_url + '#' + entry.item.id; let first = $first; let last = $last) {\n @let feed = entry.feed;\n @let item = entry.item;\n <ion-card\n [class.first]=\"first\"\n [class.last]=\"last\"\n [color]=\"cardColor !== 'light-transparent' ? cardColor : undefined\"\n class=\"feed-item-card\"\n >\n <ion-card-header>\n <ion-card-subtitle style=\"vertical-align: middle\">\n <!-- Authors -->\n @for (author of item.authors || feed.authors; track author) {\n @if (author.name || author.avatar) {\n <ion-chip (click)=\"openUrl(author.url)\" tappable>\n @if (author.avatar) {\n <ion-avatar>\n <ion-img [src]=\"author.avatar\" [alt]=\"author.name\"></ion-img>\n </ion-avatar>\n }\n @if (author.name) {\n <ion-label class=\"author\">{{ author.name }}</ion-label>\n }\n </ion-chip>\n }\n }\n <ion-note class=\"ion-float-end\">\n <small>{{ item.date_published | dateFromNow }}</small>\n </ion-note>\n </ion-card-subtitle>\n\n <!-- title -->\n <ion-card-title (click)=\"openFeedItem(item, feed)\" tappable>{{ item?.title }}</ion-card-title>\n\n <!-- tags -->\n @let tags = item | map: getTags;\n @if (tags | isNotEmptyArray) {\n <ion-text class=\"tags\">\n @for (tag of tags; track tag; let last = $last) {\n <a (click)=\"openTag(feed, tag)\" tappable>\n <ion-text>#{{ tag }}</ion-text>\n </a>\n @if (!last) {\n \n }\n }\n </ion-text>\n }\n </ion-card-header>\n\n <!-- Feed content -->\n <ion-card-content>\n <ion-text [feed]=\"item.url || feed.feed_url\">\n @if (item.content_html) {\n <p [innerHTML]=\"item.content_html\"></p>\n } @else if (item.content_text) {\n <p>\n <markdown [data]=\"item.content_text\" emoji></markdown>\n </p>\n }\n </ion-text>\n </ion-card-content>\n\n <!-- Attachments -->\n @if (item.attachments | isNotEmptyArray) {\n <div class=\"feed-attachments\">\n @for (attachment of item.attachments; track attachment.url) {\n <app-file-attachment class=\"feed-attachment\" [attachment]=\"attachment | map: toFileAttachment\" [mobile]=\"mobile\"></app-file-attachment>\n }\n </div>\n }\n\n @let editable = canEditItem(item, userId, feed);\n @if (editable || showReadMoreButton) {\n @if (editable) {\n <!-- Delete button (visible hover)-->\n <button\n mat-icon-button\n (click)=\"onDeleteItem($event, item)\"\n class=\"visible-hover ion-float-start\"\n [title]=\"!showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n [matTooltip]=\"showTooltip ? ('COMMON.BTN_DELETE' | translate) : ''\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n <!-- Edit button (visible hover) -->\n <ion-button (click)=\"onEditItem($event, item)\" class=\"visible-hover ion-float-start\" fill=\"clear\">\n <mat-icon slot=\"start\">edit</mat-icon>\n <!-- <ion-icon name=\"pencil\" slot=\"start\"></ion-icon>-->\n <ion-text>{{ 'COMMON.BTN_EDIT' | translate }}</ion-text>\n </ion-button>\n }\n @if (showReadMoreButton) {\n <ion-button (click)=\"openFeedItem(item, feed)\" class=\"ion-float-end\" fill=\"clear\">\n <ion-label>{{ (item.truncated ? 'SOCIAL.FEED.READ_MORE' : 'COMMON.BTN_SHOW') | translate }}</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n }\n }\n </ion-card>\n }\n </div>\n </div>\n}\n\n<!-- Details modal -->\n<ion-modal #modal [showBackdrop]=\"false\" class=\"stack-modal\" [class.modal-large]=\"!mobile\">\n <ng-template>\n <ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ 'SOCIAL.FEED.NEWS' | translate }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"modal.dismiss()\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content>\n <div class=\"ion-padding\">\n <app-feed\n [urls]=\"urls\"\n [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [maxContentLength]=\"-1\"\n [maxAgeInMonths]=\"-1\"\n [itemId]=\"modalItemId\"\n cardColor=\"light\"\n [debug]=\"debug\"\n ></app-feed>\n </div>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [":host{display:flex;flex-direction:column;min-height:0;height:calc(100% - 10px);max-height:fit-content;overflow:hidden;--feed-header-height: 48px;-webkit-box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);box-shadow:0 3px 1px -2px #0003,0 2px 2px #00000024,0 1px 5px #0000001f;-webkit-margin-start:10px;margin-inline-start:10px;-webkit-margin-end:10px;margin-inline-end:10px;margin-top:0;margin-bottom:10px;--feed-border-radius: 4px;border-radius:var(--feed-border-radius)}:host.shape-round{--feed-border-radius: 12px}:host{--ion-card-background: rgba(var(--ion-background-color-rgb), .6)}ion-button{text-transform:unset;--color: rgba(var(--ion-color-contrast-rgb), .7)}ion-item.feed-header{flex:none;height:var(--feed-header-height);margin:0;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);border-radius:var(--feed-border-radius) var(--feed-border-radius) 0 0}ion-item.feed-header ion-icon[slot=start]{-webkit-margin-end:8px;margin-inline-end:8px}.feed-content{flex:1 1 auto;min-height:0;overflow-y:auto;--margin-bottom: 8px;--feed-fade-height: 64px;scrollbar-width:none}.feed-content::-webkit-scrollbar{display:none}.feed-content ion-card.feed-item-card{--top-radius: 4px;--bottom-radius: 4px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin:0 0 var(--margin-bottom) 0;border-radius:var(--top-radius) var(--top-radius) var(--bottom-radius) var(--bottom-radius);--ion-card-color-contrast-rgb: var(--ion-color-contrast-rgb, var(--ion-color-dark-rgb, 0, 0, 0));--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card.first{--top-radius: 0}.feed-content ion-card.feed-item-card.last{--margin-bottom: 0;--bottom-radius: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-title{--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip{--background: transparent;--border-color: transparent;--border-width: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-avatar{--color: rgba(var(--ion-card-color-contrast-rgb), .6);border:1px solid var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-label{--color: rgba(var(--ion-card-color-contrast-rgb), .8);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-note{--color: rgba(var(--ion-card-color-contrast-rgb), .6);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header .tags{display:inline}.feed-content ion-card.feed-item-card ion-card-header .tags a{color:rgba(var(--ion-card-color-contrast-rgb),.6)!important}.feed-content ion-card.feed-item-card ion-card-content ion-text ::ng-deep img{max-width:100%;height:auto!important}.feed-content ion-card.feed-item-card .visible-hover{opacity:0;transition:opacity .2s ease-in-out}.feed-content ion-card.feed-item-card:hover .visible-hover{opacity:1}.feed-attachments{display:flex;flex-wrap:wrap;gap:4px;padding:0 16px 8px}\n"] }]
|
|
40990
41081
|
}], ctorParameters: () => [{ type: LocalSettingsService }, { type: Environment, decorators: [{
|
|
40991
41082
|
type: Inject,
|
|
40992
41083
|
args: [ENVIRONMENT]
|
|
@@ -45061,16 +45152,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
45061
45152
|
|
|
45062
45153
|
class FeedModule {
|
|
45063
45154
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FeedModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
45064
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.22", ngImport: i0, type: FeedModule, declarations: [FeedsComponent, FeedPage, FeedDirective
|
|
45065
|
-
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FeedModule, providers: [provideChildTranslateService()], imports: [SharedModule, SharedDebugModule, SharedMarkdownModule, MessageModule] });
|
|
45155
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.22", ngImport: i0, type: FeedModule, declarations: [FeedsComponent, FeedPage, FeedDirective], imports: [SharedModule, SharedDebugModule, SharedMarkdownModule, SharedFileAttachmentModule, MessageModule], exports: [FeedsComponent, FeedPage, FeedDirective, SharedFileAttachmentModule] });
|
|
45156
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FeedModule, providers: [provideChildTranslateService()], imports: [SharedModule, SharedDebugModule, SharedMarkdownModule, SharedFileAttachmentModule, MessageModule, SharedFileAttachmentModule] });
|
|
45066
45157
|
}
|
|
45067
45158
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: FeedModule, decorators: [{
|
|
45068
45159
|
type: NgModule,
|
|
45069
45160
|
args: [{
|
|
45070
45161
|
// MessageModule: the feed service opens the message modal (to edit a feed item)
|
|
45071
|
-
imports: [SharedModule, SharedDebugModule, SharedMarkdownModule, MessageModule],
|
|
45072
|
-
declarations: [FeedsComponent, FeedPage, FeedDirective
|
|
45073
|
-
exports: [FeedsComponent, FeedPage, FeedDirective,
|
|
45162
|
+
imports: [SharedModule, SharedDebugModule, SharedMarkdownModule, SharedFileAttachmentModule, MessageModule],
|
|
45163
|
+
declarations: [FeedsComponent, FeedPage, FeedDirective],
|
|
45164
|
+
exports: [FeedsComponent, FeedPage, FeedDirective, SharedFileAttachmentModule],
|
|
45074
45165
|
providers: [provideChildTranslateService()],
|
|
45075
45166
|
}]
|
|
45076
45167
|
}] });
|
|
@@ -48638,10 +48729,9 @@ class AppAsyncTable {
|
|
|
48638
48729
|
// Make sure row will be cancelled, and NOT deleted
|
|
48639
48730
|
if (row.id === -1 || !row.editing)
|
|
48640
48731
|
throw new Error('Row cannot be canceling, but only deleting');
|
|
48641
|
-
|
|
48642
|
-
const confirmed = !opts || opts.interactive !== false;
|
|
48732
|
+
const askConfirmation = !opts || opts.interactive !== false;
|
|
48643
48733
|
// Ask user confirmation, if cancel
|
|
48644
|
-
if (
|
|
48734
|
+
if (askConfirmation && row.dirty && (this.confirmBeforeCancel || this.onBeforeCancelRows.observed)) {
|
|
48645
48735
|
event?.stopPropagation();
|
|
48646
48736
|
if (!(await this.canCancelRows([row], opts))) {
|
|
48647
48737
|
return;
|
|
@@ -58766,5 +58856,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
|
|
|
58766
58856
|
* Generated bundle index. Do not edit.
|
|
58767
58857
|
*/
|
|
58768
58858
|
|
|
58769
|
-
export { ADMIN_ICONS, APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_ACCOUNT_SERVICE, APP_ACCOUNT_SERVICE_OPTIONS, APP_CELL_SELECTION_SERVICE_CONFIG_TOKEN, APP_CELL_SELECTION_SERVICE_TOKEN, APP_CONFIG_OPTIONS, APP_DEBUG_DATA_SERVICE, APP_FEED_SERVICE, APP_FILE_TRANSFER_SERVICE, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_FRAGMENTS, APP_GRAPHQL_SERVICE, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_HOME_CONFIG, APP_HOME_TOOLBAR_BUTTONS, APP_HOTKEYS_CONFIG, APP_INPUT_FIELD, APP_JOB_PROGRESSION_SERVICE, APP_LOCALES, APP_LOCAL_SETTINGS, APP_LOCAL_SETTINGS_OPTIONS, APP_LOCAL_STORAGE_TYPE_POLICIES, APP_LOGGING_SERVICE, APP_MENU_ITEMS, APP_MENU_OPTIONS, APP_MESSAGE_SERVICE_OPTIONS, APP_NAMED_FILTER_SERVICE, APP_PERSON_SERVICE, APP_PERSON_SERVICE_OPTIONS, APP_PROGRESS_BAR_SERVICE, APP_SETTINGS_MENU_ITEMS, APP_SHOW_TOOLTIP, APP_STORAGE, APP_STORAGE_EXPLORER_PROTECTED_KEYS, APP_TABLE_OPTIONS, APP_TESTING_PAGES, APP_USER_EVENT_LIST_INFINITE_SCROLL_THRESHOLD, APP_USER_EVENT_SERVICE, APP_USER_SETTINGS_OPTIONS, APP_USER_TOKEN_SCOPES, AboutModal, AbstractNamedFilterService, AbstractPersonService, AbstractSelectionModelPipe, AbstractTableSelectionPipe, AbstractUserEventService, Account, AccountPage, AccountService, AccountToStringPipe, AccountUtils, ActionsColumnComponent, AdminModule, AdminRoutingModule, AdminUsersModule, Alerts, AndroidOsEnvironment, AppAboutModalModule, AppAccountModule, AppAsyncTable, AppAuthForm, AppAuthModal, AppAuthModule, AppChangePasswordModule, AppChangePasswordPage, AppEditor, AppEditorOptions, AppEntityEditor, AppEntityEditorModal, AppEntityEditorModalOptions, AppEntityFormModule, AppForm, AppFormArray, AppFormButtonsBarModule, AppFormContainer, AppFormField, AppFormModule, AppFormProvider, AppFormUtils, AppGestureConfig, AppHomePageModule, AppIconComponent, AppIconModule, AppIconSelectorField, AppIconSelectorModal, AppIconSelectorModule, AppImageGalleryComponent, AppImageGallerySlideshowComponent, AppInMemoryTable, AppInstallUpgradeCard, AppInstallUpgradeCardModule, AppListForm, AppListFormModule, AppLoadingSpinner, AppMarkdownContent, AppMarkdownModal, AppMenuModule, AppNullForm, AppPropertiesForm, AppPropertiesFormModule, AppPropertiesTable, AppPropertiesUtils, AppPropertyUtils, AppRegisterModule, AppResetPasswordModal, AppRowField, AppSelectPeerModule, AppSelectUsersModal, AppSettingsPageModule, AppTabEditor, AppTabEditorOptions, AppTable, AppTableModule, AppTableUtils, AppTextFormModule, AppTextPopoverModule, AppUpdateOfflineModeCard, AppUpdateOfflineModeCardModule, AppValidatorService, AppWebSocket, AppendQueryParamsPipePipe, ArrayDistinctPipe, ArrayFilterPipe, ArrayFindByPropertyPipe, ArrayFindPipe, ArrayFirstPipe, ArrayFormTestPage, ArrayIncludesPipe, ArrayJoinPipe, ArrayLastPipe, ArrayLengthPipe, ArrayMapPipe, ArrayPluckPipe, ArraySlicePipe, ArraySortPipe, AsAnyPipe, AsArrayPipe, AsBooleanPipe, AsFloatLabelTypePipe, AsObservablePipe, AudioProvider, AudioTestingModule, AudioTestingPage, AuthGuardService, AutoResizeDirective, AutoTitleDirective, AutoTooltipDirective, AutocompleteTestPage, AutofocusDirective, BadgeDirective, BadgeNumberPipe, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, BooleanFormatPipe, BooleanTestPage, CORE_CONFIG_OPTIONS, CORE_ICONS, CORE_TESTING_PAGES, CapitalizePipe, CellIdentifierDirective, CellSelectionDirective, CellSelectionService, CellValueChangeListener, ChangeCaseToUnderscorePipe, ChangePasswordForm, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigFragments, ConfigService, Configuration, CoreModule, CorePipesModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, DATE_ISO_PATTERNS, DATE_MATCH_REGEXP, DATE_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_JOIN_ARRAY_VALUES_SEPARATOR, DEFAULT_JOIN_PROPERTIES_SEPARATOR, DEFAULT_MENU_SHOW_WHEN, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DateDiffDurationPipe, DateFormatPipe, DateFormatService, DateFromNowPipe, DateFromPipe, DateShortTestPage, DateTestPage, DateTimeTestPage, DateUtils, DebugComponent, Department, DepartmentToStringPipe, DisplayWithPipe, DragAndDropDirective, DurationPipe, DurationTestPage, ED25519_SEED_LENGTH, EMPTY_PLACEHOLDER_CHAR, EMPTY_PLACEHOLDER_CHAR_REGEXP_GLOBAL, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EmptyMissingTranslationHandler, EntitiesAsyncTableDataSource, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, EnvironmentHttpLoader, EnvironmentLoader, ErrorCodes, EvenPipe, FILE_KIND_ICONS, FeedAttachmentPreviewModal, FeedAttachmentUtils, FeedDirective, FeedModule, FeedPage, FeedService, FeedsComponent, FileDownloadService, FileResponse, FileService, FileSizePipe, FileTypeUtils, FilesUtils, FirstFalsePipe, FirstPipe, FirstTruePipe, FormArrayAtControlPipe, FormArrayAtGroupPipe, FormArrayHelper, FormArrayTestModule, FormButtonsBarComponent, FormButtonsBarToken, FormErrorPipe, FormErrorTranslatePipe, FormErrorTranslator, FormFieldDefinitionUtils, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetNamePipe, FormGetPipe, FormGetValuePipe, GalleryTestPage, GeolocationUtils, GitlabUtils, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, HttpUtils, IMAGE_DEFAULTS, ION_ICONS, IPosition, IconSelectorTestPage, IconSelectorTestingModule, ImageAttachment, ImageAttachmentFilter, ImageAttachmentService, ImageGalleryModule, ImageGalleryTestingModule, ImageModule, ImageService, ImageSizes, ImagesUtils, InMemoryEntitiesService, InputFieldTestPage, IsAllSelectedPipe, IsEmptySelectionPipe, IsLoginAccountPipe, IsMultipleSelectionPipe, IsNilOrBlankPipe, IsNilOrNaNPipe, IsNilPipe, IsNotAllSelectedPipe, IsNotEmptySelectionPipe, IsNotNilOrBlankPipe, IsNotNilOrNaNPipe, IsNotNilPipe, IsOnDeskPipe, IsOnFieldPipe, IsSelectedPipe, IsSingleSelectionPipe, IsValidDatePipe, JobModule, JobProgression, JobProgressionComponent, JobProgressionIcon, JobProgressionList, JobProgressionService, JobProgressionTestService, JobProgressionTestingPage, JobTestingModule, JobUtils, JsonFeedUtils, JsonUtils, KEYBOARD_HIDE_DELAY_MS, LAT_LONG_PATTERNS, LAT_LONG_PATTERN_MAX_DECIMALS, LAT_LONG_VALUE_MAX_DECIMALS, LatLongFormatPipe, LatLongTestPage, LatitudeFormatPipe, ListFormTestPage, ListFormTestingModule, LocalSettingsService, LogLevel, LogUtils, Logger, LoggingService, LoggingServiceModule, LongitudeFormatPipe, MASKS, MASK_RANGES, MAT_FORM_FIELD_DEFAULT_APPEARANCE, MAT_FORM_FIELD_DEFAULT_SUBSCRIPT_SIZING, MINIFY_ENTITY_FOR_LOCAL_STORAGE, MINIFY_ENTITY_FOR_POD, MOMENT_NO_TIME_PROPERTY, MapGetPipe, MapKeysPipe, MapPipe, MapToPipe, MapValuesPipe, MarkdownDirective, MarkdownService, MarkdownTestPage, MarkdownTestingModule, MarkdownUtils, MaskitoPlaceholderPipe, MaskitoTestPage, MatAutocompleteConfigHolder, MatAutocompleteField, MatAutocompleteFieldUtils, MatBadgeTestPage, MatBooleanField, MatChipsField, MatColorPipe, MatCommonTestPage, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatLatLongFieldInput, MatPaginatorI18n, MatStepperI18n, MatSwipeField, MaterialAutocompleteFooterDirective, MaterialAutocompleteHeaderDirective, MaterialTestingModule, MathAbsPipe, MenuComponent, MenuItem, MenuItems, MenuOptions, MenuService, MenuTestingModule, MenuTestingPage, Message, MessageAttachment, MessageFilter, MessageForm, MessageModal, MessageModule, MessageService, MessageTypeList, MessageTypes, MimeTypes, ModalToolbarComponent, NETWORK_DEFAULT_CONNECTION_TIMEOUT, NamedFilter, NamedFilterFilter, NamedFilterSelector, NamedFilterSelectorTestingModule, NamedFilterSelectorTestingPage, NativeWebSocket, NavActionsColumnComponent, NestedTableTestPage, NetworkService, NetworkUtils, NewTokenForm, NewTokenModal, NgInitDirective, NgVarDirective, NoHtmlPipe, NotEmptyArrayPipe, NumberFormatPipe, ObservableTestPage, OddPipe, OtherMenuTestingPage, PEER_URL_REGEXP, PLUS_PLACEHOLDER_CHAR_REGEXP_GLOBAL, PRINT_ID_QUERY_PARAM, PRINT_LOADING_STORAGE_KEY_PREFIX, PRIORITIZED_AUTHORITIES, PUBKEY_REGEXP, Peer, Person, PersonFilter, PersonFilterAdditionalFields, PersonFragments, PersonService, PersonToStringPipe, PersonUtils, PersonValidatorService, PlatformService, PrintService, ProgressBarService, ProgressInterceptor, PropertiesFormTestPage, PropertiesFormTestingModule, PropertyEntity, PropertyEntityFilter, PropertyEntityValidator, PropertyFormatPipe, PropertyGetPipe, RESERVED_END_COLUMNS, RESERVED_START_COLUMNS, Referential, ReferentialFilter, ReferentialRef, ReferentialToStringPipe, ReferentialUtils, ReferentialValidatorService, ReferentialsToStringPipe, RegExpUtils, RegisterConfirmPage, RegisterForm, RegisterModal, ResizableComponent, ResizableDirective, ResizableModule, RoundPipe, RxStateComputed, RxStateModule, RxStateOutput, RxStateProperty, RxStateRegister, RxStateSelect, SCRYPT_PARAMS, SETTINGS_COMPACT_ROWS, SETTINGS_DISPLAY_COLUMNS, SETTINGS_FILTER, SETTINGS_PAGE_SIZE, SETTINGS_SORTED_COLUMN, SETTINGS_STORAGE_KEY, SETTINGS_TRANSIENT_PROPERTIES, SHARED_ICONS, SHARED_IONIC_COMPONENTS, SHARED_MATERIAL_TESTING_PAGES, SHARED_STORAGE_TESTING_PAGES, SHARED_TESTING_PAGES, SOCIAL_CONFIG_OPTIONS, SOCIAL_ICONS, SOCIAL_TESTING_PAGES, SPACE_PLACEHOLDER_CHAR, SPACE_PLACEHOLDER_CHAR_REGEXP_GLOBAL, STARTUP_DATA_STORAGE_KEY, SafeHtmlPipe, SafeStylePipe, SelectPeerModal, SelectionLengthPipe, ServerErrorCodes, SettingsPage, SharedAsyncValidators, SharedBadgeModule, SharedDebugModule, SharedDirectivesModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedIonicModule, SharedMarkdownModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatChipsModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedNamedFilterModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedTestsPage, SharedToolbarModule, SharedValidators, SocialErrorCodes, SocialModule, SocialModuleOptionsToken, SocialTestingModule, Software, SplitArrayInChunksPipe, StartableService, StartupService, StatusById, StatusIds, StatusList, StorageDrivers, StorageExplorerComponent, StorageExplorerModule, StorageExplorerTestingModule, StorageExplorerTestingRoutingModule, StorageService, StorageServiceWrapper, StrIncludesPipe, StrLengthPipe, StrReplacePipe, SubMenuTabDirective, SwipeTestPage, TABLE_SETTINGS_ENUM, TOOLBAR_HEADER_ID, TRACKED_QUERIES_STORAGE_KEY, Table2TestPage, TableSelectColumnsComponent, TableTestPage, TableTestingModule, TableValidatorService, TextForm, TextFormTestingModule, TextFormTestingPage, TextPopover, TextPopoverTestingModule, TextPopoverTestingPage, ThrottledClickDirective, TimeFormatPipe, ToStringPipe, ToastTestingModule, ToastTestingPage, Toasts, TokenScope, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, TreeItemEntityUtils, TruncHtmlPipe, TruncTextPipe, TruncateHtmlPipe, USER_CONTROLLER, UploadFile, UploadFileComponent, UploadFilePopover, UploadFileTestingModule, UploadFileTestingPage, UriUtils, UrlUtils, UserController, UserEventModule, UserEventNotificationIcon, UserEventNotificationList, UserEventNotificationModal, UserEventTestService, UserEventTestingModule, UserEventTestingPage, UserSettings, UserToken, UserTokenTable, UsersPage, ValueFormatPipe, VersionUtils, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayResize, arraySize, asInputElement, assignSkipUndefined, base64ArrayBuffer, booleanToString, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, collectByProperty, collectByPropertyPath, compareValues, compareValuesDesc, compareVersionNumbers, composeComparators, computeDecimalDegrees, computeDecimalPart, copyEntity2Form, createApolloClientOptions, createAppStartupInitializer, createPromiseEvent, createPromiseEventEmitter, createTestingImageDataUrl, createTrackerLink, decorateWithTakeUntil, deepMergeSkipUndefined, departmentToString, departmentsToString, disableAndClearControl, disableAndClearControls, disableControl, disableControls, emitPromiseEvent, enableControl, enableControls, enableRxStateProdMode, entityToString, equals, equalsOrNil, escapeRegExp, expansionAnimation, fadeInAnimation, fadeInOutAnimation, fadeInSlowAnimation, filterFalse, filterFormErrors, filterFormErrorsByPath, filterFormErrorsByPrefix, filterNotNil, filterNumberInput, filterTrue, findParentWithClass, firstArrayValue, firstFalse, firstFalsePromise, firstNotNil, firstNotNilPromise, firstPromise, firstTrue, firstTruePromise, focusInput, focusNextInput, focusPreviousInput, formatLatLong, formatLatitude, formatLongitude, fromDateISOString, fromScrollEndEvent, fromUnixMsTimestamp, fromUnixTimestamp, getCaretPosition, getColorContrast, getColorLuminance, getColorShade, getColorTint, getControlFromPath, getFocusableInputElements, getFormControlName, getFormErrors, getFormValueFromEntity, getInputRangeFromCaretIndex, getInputSelectionRangesFromMask, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, getRandomImageWithCredit, getUserAgent, hasFocusWithin, hexToRgb, hexToRgbArray, initArrayControlsFromValues, initializeSharedModule, interpolateString, intersectArrays, isAndroid, isAppInputField, isAppTextInputField, isBlankString, isCapacitor, isChrome, isControlHasInput, isEdge, isEmptyArray, isEntityService, isFirefox, isFocusableElement, isIOS, isInputElement, isInstanceOf, isInt, isIpad, isLightColor, isMacOS, isMobile, isMutationOperation, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilBoolean, isNotNilObject, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, isOnFieldMode, isPrint, isProgressEvent, isPromise, isResponseEvent, isSafari, isSameVersion, isStartableService, isSubscriptionOperation, isTouchUi, isValidVersion, isVersionCompatible, isWebAnimationsSupported, isWindows, joinProperties, joinPropertiesPath, lastArrayValue, logFormErrors, loggerLink, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, maskitoAutoSelectByMaskPattern, maskitoPrefixPlugin, matchMedia, matchUpperCase, mergeLoadResult, mergeObjectsWithoutUndefined, mixHex, moveInputCaretToSeparator, newArray, noHtml, noTrailingSlash, notNilOrDefault, nullIfNilOrBlank, nullIfUndefined, numberOrNilAttribute, numberToString, parseBooleanText, parseLatLongText, parseLatitudeOrLongitude, parseVersion, propertiesPathComparator, propertyComparator, propertyPathComparator, provideAccountService, provideAllIonicIcons, provideIonicIcons, provideIonicIconsInitializer, providePersonService, referentialToString, referentialsToString, registerIonicIcons, remove, removeAll, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, restoreTrackedQueries, rgbArrayToHex, rgbToHex, round, scrollFactory, selectInputContent, selectInputContentFromEvent, selectInputRange, setCalculatedValue, setControlEnabled, setControlRequired, setControlsEnabled, setFormErrors, setPropertyByPath, setTabIndex, sleep, slideDownAnimation, slideInAnimation, slideInOutAnimation, slideUpDownAnimation, sort, splitArrayInChunks, splitById, splitByProperty, splitDegreesToDDArray, splitDegreesToDDMMArray, splitDegreesToDDMMSSArray, startsWithUpperCase, suggestFromArray, suggestFromStringArray, tabindexComparator, testUserAgent, toBoolean, toDateISOString, toDuration, toFloat, toInt, toLoadData, toLoadResult, toNotNil, toNumber, trimEmptyToNull, truncateHtml, typeTextIntoInput, uncapitalizeFirstLetter, undefinedIfNull, underscoreToChangeCase, updateValueAndValidity, waitFor, waitForFalse, waitForTrue, waitIdle, waitWhilePending };
|
|
58859
|
+
export { ADMIN_ICONS, APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_ACCOUNT_SERVICE, APP_ACCOUNT_SERVICE_OPTIONS, APP_CELL_SELECTION_SERVICE_CONFIG_TOKEN, APP_CELL_SELECTION_SERVICE_TOKEN, APP_CONFIG_OPTIONS, APP_DEBUG_DATA_SERVICE, APP_FEED_SERVICE, APP_FILE_TRANSFER_SERVICE, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_FRAGMENTS, APP_GRAPHQL_SERVICE, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_HOME_CONFIG, APP_HOME_TOOLBAR_BUTTONS, APP_HOTKEYS_CONFIG, APP_INPUT_FIELD, APP_JOB_PROGRESSION_SERVICE, APP_LOCALES, APP_LOCAL_SETTINGS, APP_LOCAL_SETTINGS_OPTIONS, APP_LOCAL_STORAGE_TYPE_POLICIES, APP_LOGGING_SERVICE, APP_MENU_ITEMS, APP_MENU_OPTIONS, APP_MESSAGE_SERVICE_OPTIONS, APP_NAMED_FILTER_SERVICE, APP_PERSON_SERVICE, APP_PERSON_SERVICE_OPTIONS, APP_PROGRESS_BAR_SERVICE, APP_SETTINGS_MENU_ITEMS, APP_SHOW_TOOLTIP, APP_STORAGE, APP_STORAGE_EXPLORER_PROTECTED_KEYS, APP_TABLE_OPTIONS, APP_TESTING_PAGES, APP_USER_EVENT_LIST_INFINITE_SCROLL_THRESHOLD, APP_USER_EVENT_SERVICE, APP_USER_SETTINGS_OPTIONS, APP_USER_TOKEN_SCOPES, AboutModal, AbstractNamedFilterService, AbstractPersonService, AbstractSelectionModelPipe, AbstractTableSelectionPipe, AbstractUserEventService, Account, AccountPage, AccountService, AccountToStringPipe, AccountUtils, ActionsColumnComponent, AdminModule, AdminRoutingModule, AdminUsersModule, Alerts, AndroidOsEnvironment, AppAboutModalModule, AppAccountModule, AppAsyncTable, AppAuthForm, AppAuthModal, AppAuthModule, AppChangePasswordModule, AppChangePasswordPage, AppEditor, AppEditorOptions, AppEntityEditor, AppEntityEditorModal, AppEntityEditorModalOptions, AppEntityFormModule, AppForm, AppFormArray, AppFormButtonsBarModule, AppFormContainer, AppFormField, AppFormModule, AppFormProvider, AppFormUtils, AppGestureConfig, AppHomePageModule, AppIconComponent, AppIconModule, AppIconSelectorField, AppIconSelectorModal, AppIconSelectorModule, AppImageGalleryComponent, AppImageGallerySlideshowComponent, AppInMemoryTable, AppInstallUpgradeCard, AppInstallUpgradeCardModule, AppListForm, AppListFormModule, AppLoadingSpinner, AppMarkdownContent, AppMarkdownModal, AppMenuModule, AppNullForm, AppPropertiesForm, AppPropertiesFormModule, AppPropertiesTable, AppPropertiesUtils, AppPropertyUtils, AppRegisterModule, AppResetPasswordModal, AppRowField, AppSelectPeerModule, AppSelectUsersModal, AppSettingsPageModule, AppTabEditor, AppTabEditorOptions, AppTable, AppTableModule, AppTableUtils, AppTextFormModule, AppTextPopoverModule, AppUpdateOfflineModeCard, AppUpdateOfflineModeCardModule, AppValidatorService, AppWebSocket, AppendQueryParamsPipePipe, ArrayDistinctPipe, ArrayFilterPipe, ArrayFindByPropertyPipe, ArrayFindPipe, ArrayFirstPipe, ArrayFormTestPage, ArrayIncludesPipe, ArrayJoinPipe, ArrayLastPipe, ArrayLengthPipe, ArrayMapPipe, ArrayPluckPipe, ArraySlicePipe, ArraySortPipe, AsAnyPipe, AsArrayPipe, AsBooleanPipe, AsFloatLabelTypePipe, AsObservablePipe, AudioProvider, AudioTestingModule, AudioTestingPage, AuthGuardService, AutoResizeDirective, AutoTitleDirective, AutoTooltipDirective, AutocompleteTestPage, AutofocusDirective, BadgeDirective, BadgeNumberPipe, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, BooleanFormatPipe, BooleanTestPage, CORE_CONFIG_OPTIONS, CORE_ICONS, CORE_TESTING_PAGES, CapitalizePipe, CellIdentifierDirective, CellSelectionDirective, CellSelectionService, CellValueChangeListener, ChangeCaseToUnderscorePipe, ChangePasswordForm, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigFragments, ConfigService, Configuration, CoreModule, CorePipesModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, DATE_ISO_PATTERNS, DATE_MATCH_REGEXP, DATE_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_JOIN_ARRAY_VALUES_SEPARATOR, DEFAULT_JOIN_PROPERTIES_SEPARATOR, DEFAULT_MENU_SHOW_WHEN, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DateDiffDurationPipe, DateFormatPipe, DateFormatService, DateFromNowPipe, DateFromPipe, DateShortTestPage, DateTestPage, DateTimeTestPage, DateUtils, DebugComponent, Department, DepartmentToStringPipe, DisplayWithPipe, DragAndDropDirective, DurationPipe, DurationTestPage, ED25519_SEED_LENGTH, EMPTY_PLACEHOLDER_CHAR, EMPTY_PLACEHOLDER_CHAR_REGEXP_GLOBAL, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EmptyMissingTranslationHandler, EntitiesAsyncTableDataSource, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, EnvironmentHttpLoader, EnvironmentLoader, ErrorCodes, EvenPipe, FILE_KIND_ICONS, FeedAttachmentUtils, FeedDirective, FeedModule, FeedPage, FeedService, FeedsComponent, FileAttachmentComponent, FileAttachmentPreviewModal, FileAttachmentUtils, FileDownloadService, FileResponse, FileService, FileSizePipe, FileTypeUtils, FilesUtils, FirstFalsePipe, FirstPipe, FirstTruePipe, FormArrayAtControlPipe, FormArrayAtGroupPipe, FormArrayHelper, FormArrayTestModule, FormButtonsBarComponent, FormButtonsBarToken, FormErrorPipe, FormErrorTranslatePipe, FormErrorTranslator, FormFieldDefinitionUtils, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetNamePipe, FormGetPipe, FormGetValuePipe, GalleryTestPage, GeolocationUtils, GitlabUtils, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, HttpUtils, IMAGE_DEFAULTS, ION_ICONS, IPosition, IconSelectorTestPage, IconSelectorTestingModule, ImageAttachment, ImageAttachmentFilter, ImageAttachmentService, ImageGalleryModule, ImageGalleryTestingModule, ImageModule, ImageService, ImageSizes, ImagesUtils, InMemoryEntitiesService, InputFieldTestPage, IsAllSelectedPipe, IsEmptySelectionPipe, IsLoginAccountPipe, IsMultipleSelectionPipe, IsNilOrBlankPipe, IsNilOrNaNPipe, IsNilPipe, IsNotAllSelectedPipe, IsNotEmptySelectionPipe, IsNotNilOrBlankPipe, IsNotNilOrNaNPipe, IsNotNilPipe, IsOnDeskPipe, IsOnFieldPipe, IsSelectedPipe, IsSingleSelectionPipe, IsValidDatePipe, JobModule, JobProgression, JobProgressionComponent, JobProgressionIcon, JobProgressionList, JobProgressionService, JobProgressionTestService, JobProgressionTestingPage, JobTestingModule, JobUtils, JsonFeedUtils, JsonUtils, KEYBOARD_HIDE_DELAY_MS, LAT_LONG_PATTERNS, LAT_LONG_PATTERN_MAX_DECIMALS, LAT_LONG_VALUE_MAX_DECIMALS, LatLongFormatPipe, LatLongTestPage, LatitudeFormatPipe, ListFormTestPage, ListFormTestingModule, LocalSettingsService, LogLevel, LogUtils, Logger, LoggingService, LoggingServiceModule, LongitudeFormatPipe, MASKS, MASK_RANGES, MAT_FORM_FIELD_DEFAULT_APPEARANCE, MAT_FORM_FIELD_DEFAULT_SUBSCRIPT_SIZING, MINIFY_ENTITY_FOR_LOCAL_STORAGE, MINIFY_ENTITY_FOR_POD, MOMENT_NO_TIME_PROPERTY, MapGetPipe, MapKeysPipe, MapPipe, MapToPipe, MapValuesPipe, MarkdownDirective, MarkdownService, MarkdownTestPage, MarkdownTestingModule, MarkdownUtils, MaskitoPlaceholderPipe, MaskitoTestPage, MatAutocompleteConfigHolder, MatAutocompleteField, MatAutocompleteFieldUtils, MatBadgeTestPage, MatBooleanField, MatChipsField, MatColorPipe, MatCommonTestPage, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatLatLongFieldInput, MatPaginatorI18n, MatStepperI18n, MatSwipeField, MaterialAutocompleteFooterDirective, MaterialAutocompleteHeaderDirective, MaterialTestingModule, MathAbsPipe, MenuComponent, MenuItem, MenuItems, MenuOptions, MenuService, MenuTestingModule, MenuTestingPage, Message, MessageAttachment, MessageAttachmentUtils, MessageFilter, MessageForm, MessageModal, MessageModule, MessageService, MessageTypeList, MessageTypes, MimeTypes, ModalToolbarComponent, NETWORK_DEFAULT_CONNECTION_TIMEOUT, NamedFilter, NamedFilterFilter, NamedFilterSelector, NamedFilterSelectorTestingModule, NamedFilterSelectorTestingPage, NativeWebSocket, NavActionsColumnComponent, NestedTableTestPage, NetworkService, NetworkUtils, NewTokenForm, NewTokenModal, NgInitDirective, NgVarDirective, NoHtmlPipe, NotEmptyArrayPipe, NumberFormatPipe, ObservableTestPage, OddPipe, OtherMenuTestingPage, PEER_URL_REGEXP, PLUS_PLACEHOLDER_CHAR_REGEXP_GLOBAL, PRINT_ID_QUERY_PARAM, PRINT_LOADING_STORAGE_KEY_PREFIX, PRIORITIZED_AUTHORITIES, PUBKEY_REGEXP, Peer, Person, PersonFilter, PersonFilterAdditionalFields, PersonFragments, PersonService, PersonToStringPipe, PersonUtils, PersonValidatorService, PlatformService, PrintService, ProgressBarService, ProgressInterceptor, PropertiesFormTestPage, PropertiesFormTestingModule, PropertyEntity, PropertyEntityFilter, PropertyEntityValidator, PropertyFormatPipe, PropertyGetPipe, RESERVED_END_COLUMNS, RESERVED_START_COLUMNS, Referential, ReferentialFilter, ReferentialRef, ReferentialToStringPipe, ReferentialUtils, ReferentialValidatorService, ReferentialsToStringPipe, RegExpUtils, RegisterConfirmPage, RegisterForm, RegisterModal, ResizableComponent, ResizableDirective, ResizableModule, RoundPipe, RxStateComputed, RxStateModule, RxStateOutput, RxStateProperty, RxStateRegister, RxStateSelect, SCRYPT_PARAMS, SETTINGS_COMPACT_ROWS, SETTINGS_DISPLAY_COLUMNS, SETTINGS_FILTER, SETTINGS_PAGE_SIZE, SETTINGS_SORTED_COLUMN, SETTINGS_STORAGE_KEY, SETTINGS_TRANSIENT_PROPERTIES, SHARED_ICONS, SHARED_IONIC_COMPONENTS, SHARED_MATERIAL_TESTING_PAGES, SHARED_STORAGE_TESTING_PAGES, SHARED_TESTING_PAGES, SOCIAL_CONFIG_OPTIONS, SOCIAL_ICONS, SOCIAL_TESTING_PAGES, SPACE_PLACEHOLDER_CHAR, SPACE_PLACEHOLDER_CHAR_REGEXP_GLOBAL, STARTUP_DATA_STORAGE_KEY, SafeHtmlPipe, SafeStylePipe, SelectPeerModal, SelectionLengthPipe, ServerErrorCodes, SettingsPage, SharedAsyncValidators, SharedBadgeModule, SharedDebugModule, SharedDirectivesModule, SharedFileAttachmentModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedIonicModule, SharedMarkdownModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatChipsModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedNamedFilterModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedTestsPage, SharedToolbarModule, SharedValidators, SocialErrorCodes, SocialModule, SocialModuleOptionsToken, SocialTestingModule, Software, SplitArrayInChunksPipe, StartableService, StartupService, StatusById, StatusIds, StatusList, StorageDrivers, StorageExplorerComponent, StorageExplorerModule, StorageExplorerTestingModule, StorageExplorerTestingRoutingModule, StorageService, StorageServiceWrapper, StrIncludesPipe, StrLengthPipe, StrReplacePipe, SubMenuTabDirective, SwipeTestPage, TABLE_SETTINGS_ENUM, TOOLBAR_HEADER_ID, TRACKED_QUERIES_STORAGE_KEY, Table2TestPage, TableSelectColumnsComponent, TableTestPage, TableTestingModule, TableValidatorService, TextForm, TextFormTestingModule, TextFormTestingPage, TextPopover, TextPopoverTestingModule, TextPopoverTestingPage, ThrottledClickDirective, TimeFormatPipe, ToStringPipe, ToastTestingModule, ToastTestingPage, Toasts, TokenScope, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, TreeItemEntityUtils, TruncHtmlPipe, TruncTextPipe, TruncateHtmlPipe, USER_CONTROLLER, UploadFile, UploadFileComponent, UploadFilePopover, UploadFileTestingModule, UploadFileTestingPage, UriUtils, UrlUtils, UserController, UserEventModule, UserEventNotificationIcon, UserEventNotificationList, UserEventNotificationModal, UserEventTestService, UserEventTestingModule, UserEventTestingPage, UserSettings, UserToken, UserTokenTable, UsersPage, ValueFormatPipe, VersionUtils, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayResize, arraySize, asInputElement, assignSkipUndefined, base64ArrayBuffer, booleanToString, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, collectByProperty, collectByPropertyPath, compareValues, compareValuesDesc, compareVersionNumbers, composeComparators, computeDecimalDegrees, computeDecimalPart, copyEntity2Form, createApolloClientOptions, createAppStartupInitializer, createPromiseEvent, createPromiseEventEmitter, createTestingImageDataUrl, createTrackerLink, decorateWithTakeUntil, deepMergeSkipUndefined, departmentToString, departmentsToString, disableAndClearControl, disableAndClearControls, disableControl, disableControls, emitPromiseEvent, enableControl, enableControls, enableRxStateProdMode, entityToString, equals, equalsOrNil, escapeRegExp, expansionAnimation, fadeInAnimation, fadeInOutAnimation, fadeInSlowAnimation, filterFalse, filterFormErrors, filterFormErrorsByPath, filterFormErrorsByPrefix, filterNotNil, filterNumberInput, filterTrue, findParentWithClass, firstArrayValue, firstFalse, firstFalsePromise, firstNotNil, firstNotNilPromise, firstPromise, firstTrue, firstTruePromise, focusInput, focusNextInput, focusPreviousInput, formatLatLong, formatLatitude, formatLongitude, fromDateISOString, fromScrollEndEvent, fromUnixMsTimestamp, fromUnixTimestamp, getCaretPosition, getColorContrast, getColorLuminance, getColorShade, getColorTint, getControlFromPath, getFocusableInputElements, getFormControlName, getFormErrors, getFormValueFromEntity, getInputRangeFromCaretIndex, getInputSelectionRangesFromMask, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, getRandomImageWithCredit, getUserAgent, hasFocusWithin, hexToRgb, hexToRgbArray, initArrayControlsFromValues, initializeSharedModule, interpolateString, intersectArrays, isAndroid, isAppInputField, isAppTextInputField, isBlankString, isCapacitor, isChrome, isControlHasInput, isEdge, isEmptyArray, isEntityService, isFirefox, isFocusableElement, isIOS, isInputElement, isInstanceOf, isInt, isIpad, isLightColor, isMacOS, isMobile, isMutationOperation, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilBoolean, isNotNilObject, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, isOnFieldMode, isPrint, isProgressEvent, isPromise, isResponseEvent, isSafari, isSameVersion, isStartableService, isSubscriptionOperation, isTouchUi, isValidVersion, isVersionCompatible, isWebAnimationsSupported, isWindows, joinProperties, joinPropertiesPath, lastArrayValue, logFormErrors, loggerLink, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, maskitoAutoSelectByMaskPattern, maskitoPrefixPlugin, matchMedia, matchUpperCase, mergeLoadResult, mergeObjectsWithoutUndefined, mixHex, moveInputCaretToSeparator, newArray, noHtml, noTrailingSlash, notNilOrDefault, nullIfNilOrBlank, nullIfUndefined, numberOrNilAttribute, numberToString, parseBooleanText, parseLatLongText, parseLatitudeOrLongitude, parseVersion, propertiesPathComparator, propertyComparator, propertyPathComparator, provideAccountService, provideAllIonicIcons, provideIonicIcons, provideIonicIconsInitializer, providePersonService, referentialToString, referentialsToString, registerIonicIcons, remove, removeAll, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, restoreTrackedQueries, rgbArrayToHex, rgbToHex, round, scrollFactory, selectInputContent, selectInputContentFromEvent, selectInputRange, setCalculatedValue, setControlEnabled, setControlRequired, setControlsEnabled, setFormErrors, setPropertyByPath, setTabIndex, sleep, slideDownAnimation, slideInAnimation, slideInOutAnimation, slideUpDownAnimation, sort, splitArrayInChunks, splitById, splitByProperty, splitDegreesToDDArray, splitDegreesToDDMMArray, splitDegreesToDDMMSSArray, startsWithUpperCase, suggestFromArray, suggestFromStringArray, tabindexComparator, testUserAgent, toBoolean, toDateISOString, toDuration, toFloat, toInt, toLoadData, toLoadResult, toNotNil, toNumber, trimEmptyToNull, truncateHtml, typeTextIntoInput, uncapitalizeFirstLetter, undefinedIfNull, underscoreToChangeCase, updateValueAndValidity, waitFor, waitForFalse, waitForTrue, waitIdle, waitWhilePending };
|
|
58770
58860
|
//# sourceMappingURL=sumaris-net.ngx-components.mjs.map
|