@sumaris-net/ngx-components 18.14.15 → 18.14.17

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.
Files changed (27) hide show
  1. package/doc/changelog.md +462 -128
  2. package/doc/feed/feed-fr.json +8 -6
  3. package/esm2022/public_api.mjs +4 -4
  4. package/esm2022/src/app/shared/pipes/arrays.pipe.mjs +14 -1
  5. package/esm2022/src/app/shared/pipes/pipes.module.mjs +5 -2
  6. package/esm2022/src/app/social/feed/feed.component.mjs +3 -4
  7. package/esm2022/src/app/social/feed/feed.directive.mjs +4 -4
  8. package/esm2022/src/app/social/feed/feed.service.mjs +158 -2
  9. package/esm2022/src/app/social/message/message.form.mjs +4 -3
  10. package/esm2022/src/app/social/message/message.model.mjs +2 -2
  11. package/esm2022/src/environments/environment.mjs +4 -2
  12. package/fesm2022/sumaris-net.ngx-components.mjs +156 -312
  13. package/fesm2022/sumaris-net.ngx-components.mjs.map +1 -1
  14. package/package.json +1 -1
  15. package/public_api.d.ts +0 -3
  16. package/src/app/shared/inputs.d.ts +1 -1
  17. package/src/app/shared/pipes/arrays.pipe.d.ts +5 -0
  18. package/src/app/shared/pipes/pipes.module.d.ts +1 -1
  19. package/src/app/social/feed/feed.directive.d.ts +1 -1
  20. package/src/app/social/feed/feed.service.d.ts +57 -0
  21. package/src/assets/manifest.json +1 -1
  22. package/esm2022/src/app/social/feed/discourse/discourse-feed.service.mjs +0 -170
  23. package/esm2022/src/app/social/feed/discourse/discourse.model.mjs +0 -2
  24. package/esm2022/src/app/social/feed/discourse/discourse.utils.mjs +0 -168
  25. package/src/app/social/feed/discourse/discourse-feed.service.d.ts +0 -56
  26. package/src/app/social/feed/discourse/discourse.model.d.ts +0 -7
  27. package/src/app/social/feed/discourse/discourse.utils.d.ts +0 -44
@@ -1,6 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { InjectionToken, Directive, Pipe, Injectable, EventEmitter, Output, Optional, Inject, inject, NgModule, forwardRef, booleanAttribute, Component, ChangeDetectionStrategy, Input, ViewChildren, HostBinding, HostListener, ElementRef, numberAttribute, ViewChild, ANIMATION_MODULE_TYPE, RendererStyleFlags2, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectorRef, Self, ViewEncapsulation, APP_INITIALIZER, SecurityContext, viewChild, signal, input, model, computed, effect, untracked } from '@angular/core';
3
- import { firstValueFrom, shareReplay, tap, of, timer, Subject, merge, delay, isObservable, from, Subscription, BehaviorSubject, fromEvent, noop as noop$9, Observable, forkJoin, timeout, defer, combineLatest, debounceTime as debounceTime$1, distinctUntilChanged as distinctUntilChanged$1, interval, mergeMap as mergeMap$1, EMPTY, switchMap as switchMap$1 } from 'rxjs';
3
+ import { firstValueFrom, shareReplay, tap, of, timer, Subject, merge, delay, isObservable, from, Subscription, BehaviorSubject, fromEvent, noop as noop$9, Observable, forkJoin, timeout, defer, combineLatest, debounceTime as debounceTime$1, distinctUntilChanged as distinctUntilChanged$1, interval, mergeMap as mergeMap$1, switchMap as switchMap$1, EMPTY } from 'rxjs';
4
4
  import { catchError, map, filter, takeUntil, first, switchMap, tap as tap$1, throttleTime, debounceTime, startWith, distinctUntilChanged, mergeMap, skip, finalize, bufferWhen, distinctUntilKeyChanged, take } from 'rxjs/operators';
5
5
  import * as cloneImported from 'clone';
6
6
  import * as i3$1 from '@angular/common';
@@ -155,8 +155,6 @@ import { Camera, CameraResultType } from '@capacitor/camera';
155
155
  import { Geolocation } from '@capacitor/geolocation';
156
156
  import * as i1$8 from '@e-is/ngx-material-table';
157
157
  import { ValidatorService, TableDataSource, AsyncTableDataSource } from '@e-is/ngx-material-table';
158
- import DiscourseAPI from 'discourse2';
159
- import { Promise as Promise$1 } from '@rx-angular/cdk/zone-less/browser';
160
158
  import 'moment-timezone';
161
159
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
162
160
 
@@ -2464,6 +2462,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
2464
2462
  name: 'splitArrayInChunks',
2465
2463
  }]
2466
2464
  }] });
2465
+ class ArrayMapPipe {
2466
+ transform(value, mapFn, ...otherArguments) {
2467
+ return mapFn.call(this, value, ...otherArguments);
2468
+ }
2469
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ArrayMapPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
2470
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: ArrayMapPipe, name: "arrayMap" });
2471
+ }
2472
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: ArrayMapPipe, decorators: [{
2473
+ type: Pipe,
2474
+ args: [{
2475
+ name: 'arrayMap',
2476
+ }]
2477
+ }] });
2467
2478
 
2468
2479
  class MapGetPipe {
2469
2480
  transform(val, args) {
@@ -5918,6 +5929,7 @@ const components$1 = [
5918
5929
  ArrayDistinctPipe,
5919
5930
  ArrayFindByPropertyPipe,
5920
5931
  SplitArrayInChunksPipe,
5932
+ ArrayMapPipe,
5921
5933
  MapGetPipe,
5922
5934
  MapKeysPipe,
5923
5935
  MapValuesPipe,
@@ -6001,6 +6013,7 @@ class SharedPipesModule {
6001
6013
  ArrayDistinctPipe,
6002
6014
  ArrayFindByPropertyPipe,
6003
6015
  SplitArrayInChunksPipe,
6016
+ ArrayMapPipe,
6004
6017
  MapGetPipe,
6005
6018
  MapKeysPipe,
6006
6019
  MapValuesPipe,
@@ -6080,6 +6093,7 @@ class SharedPipesModule {
6080
6093
  ArrayDistinctPipe,
6081
6094
  ArrayFindByPropertyPipe,
6082
6095
  SplitArrayInChunksPipe,
6096
+ ArrayMapPipe,
6083
6097
  MapGetPipe,
6084
6098
  MapKeysPipe,
6085
6099
  MapValuesPipe,
@@ -12899,8 +12913,10 @@ const environment = Object.freeze({
12899
12913
  jsonFeed: {
12900
12914
  fr: [
12901
12915
  'https://gitlab.ifremer.fr/sih-public/sumaris/ngx-sumaris-components/-/raw/master/doc/feed/feed-fr.json',
12902
- //'https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/1.1/feed-fr-FR.json',
12916
+ // Example with JsonFeed version 1 (and not 1.1)
12903
12917
  //'https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/feed-fr.json',
12918
+ // Example with discourse API :
12919
+ //'https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/1.1/feed-fr-FR.json',
12904
12920
  ],
12905
12921
  en: ['https://gitlab.ifremer.fr/sih-public/sumaris/ngx-sumaris-components/-/raw/master/doc/feed/feed-en.json'],
12906
12922
  },
@@ -33357,169 +33373,151 @@ class JsonFeedUtils {
33357
33373
  }
33358
33374
  }
33359
33375
 
33360
- class DiscourseUtils {
33361
- static _logPrefix = '[discourse-utils]';
33362
- static removeJsonExtension = JsonFeedUtils.removeJsonExtension;
33363
- static isDiscourseObject(json) {
33364
- return this.isDiscourseTopic(json) || this.isDiscourseCategory(json);
33365
- }
33366
- static isDiscourseTopic(json) {
33367
- return (json && !!json.post_stream?.posts && json.fancy_title && json.slug && json.details?.created_by && true) || false;
33376
+ const APP_FEED_SERVICE = new InjectionToken('FeeService');
33377
+ class FeedService extends StartableService {
33378
+ settings;
33379
+ environment;
33380
+ _logPrefix = '[feed-service] ';
33381
+ _state = new RxState();
33382
+ network = inject(NetworkService);
33383
+ locale$ = this._state.select('locale');
33384
+ feedUrls$ = this._state.select('feedUrls');
33385
+ get feedUrls() {
33386
+ return this._state.get('feedUrls');
33368
33387
  }
33369
- static isDiscourseCategory(json) {
33370
- return (json && json.topic_list?.topics && json.posters && true) || false;
33388
+ set feedUrls(urls) {
33389
+ this._state.set('feedUrls', () => urls);
33371
33390
  }
33372
- static getDiscourseBaseUrl(url) {
33373
- return UrlUtils.getRootUrl(url);
33391
+ constructor(settings, environment) {
33392
+ super(settings);
33393
+ this.settings = settings;
33394
+ this.environment = environment;
33395
+ this._state.connect('locale', this.settings.locale$);
33396
+ this._state.connect('feedUrls', this.locale$.pipe(filter(isNotNilOrBlank), map((locale) => (locale && environment.feed?.jsonFeed?.[locale]) ?? [])));
33397
+ // DEBUG
33398
+ this._debug = !environment.production;
33399
+ if (this._debug)
33400
+ console.debug(`${this._logPrefix}created`);
33374
33401
  }
33375
- static isDiscourseCategoryUrl(url) {
33376
- const baseUrl = this.getDiscourseBaseUrl(url);
33377
- return baseUrl && url.substring(baseUrl.length).match(/^\/c\//);
33402
+ async ngOnStart() {
33403
+ await this.settings.ready();
33404
+ return {
33405
+ locale: this.settings.locale,
33406
+ };
33378
33407
  }
33379
- static extractCategoryIdFromUrl(url) {
33380
- const match = this.removeJsonExtension(url).match(/\/(\d+)$/);
33381
- return match ? parseInt(match[1], 10) : null;
33408
+ watchAll(opts) {
33409
+ if (!this.started) {
33410
+ return from(this.start()).pipe(switchMap$1(() => this.watchAll(opts)));
33411
+ }
33412
+ if (isNotEmptyArray(opts?.urls)) {
33413
+ return this.locale$.pipe(mergeMap((locale) => this.loadAll(opts.urls, { locale, ...opts })));
33414
+ }
33415
+ return this.feedUrls$.pipe(filter(isNotEmptyArray), mergeMap((urls) => this.loadAll(urls, opts)));
33382
33416
  }
33383
- static extractCategorySlugFromUrl(url) {
33384
- const match = this.removeJsonExtension(url).match(/\/c\/([\w/-]+)\/\d+$/);
33385
- return match ? match[1] : null;
33417
+ getHomeUrl(feed) {
33418
+ const feedUrl = feed?.feed_url ?? firstArrayValue(this.feedUrls);
33419
+ return feedUrl ? UrlUtils.getRootUrl(feedUrl) : undefined;
33386
33420
  }
33387
- static extractTopicIdFromUrl(url) {
33388
- const match = url.match(/\/(\d+)(?:$|\/)/) || url.match(/\/(\d+)\.json$/);
33389
- return match ? parseInt(match[1], 10) : null;
33421
+ getTagUrl(feed, tag) {
33422
+ if (!feed || !tag)
33423
+ throw new Error("Missing 'feed' or 'tag' argument");
33424
+ const baseUrl = this.getHomeUrl(feed);
33425
+ return feed.tag_template?.replace('{tag}', tag) ?? baseUrl + '/tag/' + tag;
33390
33426
  }
33391
- static getLanguageFromTitle(title) {
33392
- if (!title)
33393
- return '';
33394
- const match = title.match('\\s\\(([a-z]{2}(?:-[A-Z]{2})?)\\)$');
33395
- return match && match[1];
33427
+ async load(url, opts) {
33428
+ const { data } = await this.loadAll([url], opts);
33429
+ return data?.[0];
33396
33430
  }
33397
- static async getFeedsByUrl(url, opts) {
33398
- try {
33399
- // Check a category
33400
- if (DiscourseUtils.isDiscourseCategoryUrl(url)) {
33401
- console.debug(`${this._logPrefix}Detected a Discourse category at: ${url}`);
33402
- return this.getFeedsByCategoryUrl(url, opts);
33431
+ async loadAll(urls, opts) {
33432
+ await this.ready();
33433
+ urls = urls ?? this.feedUrls;
33434
+ opts = {
33435
+ maxAgeInMonths: opts?.maxAgeInMonths ?? this.environment.feed?.maxAgeInMonths,
33436
+ maxContentLength: opts?.maxContentLength ?? this.environment.feed?.maxContentLength,
33437
+ locale: this.settings.locale,
33438
+ depth: 0,
33439
+ ...opts,
33440
+ };
33441
+ const feeds = await Promise.all((urls || []).map(async (url) => {
33442
+ try {
33443
+ // Get JSON
33444
+ const json = await this.network.fetch(url);
33445
+ console.debug(`${this._logPrefix}Loaded JSON from ${url}`, json);
33446
+ // Resolve feed
33447
+ return await this.resolveFeed(json, url, opts);
33403
33448
  }
33404
- // Or a topic
33405
- else {
33406
- const post = await this.getFeedByTopicUrl(url, opts);
33407
- return post ? [post] : [];
33449
+ catch (err) {
33450
+ if (err?.status === 404) {
33451
+ console.error(`${this._logPrefix} Error while fetching feeds at ${url}. 404 (Not found)`);
33452
+ }
33453
+ else {
33454
+ console.error(`${this._logPrefix} Error while fetching feeds at ${url}`, err);
33455
+ }
33456
+ return Promise.resolve([]);
33408
33457
  }
33458
+ }));
33459
+ const data = feeds.flat();
33460
+ // Close opened resources
33461
+ if (opts.depth === 0) {
33462
+ this.onAfterLoadAll();
33409
33463
  }
33410
- catch (err) {
33411
- console.error(`${this._logPrefix}`, err);
33412
- return [];
33413
- }
33464
+ return { data, total: data.length };
33414
33465
  }
33415
- static async getFeedsByCategoryUrl(url, opts) {
33416
- const categoryId = this.extractCategoryIdFromUrl(url);
33417
- const categorySlug = this.extractCategorySlugFromUrl(url);
33418
- const baseUrl = this.getDiscourseBaseUrl(url);
33419
- const api = opts?.api ?? new DiscourseAPI(baseUrl);
33420
- const category = await api.listCategoryTopics({ id: categoryId, slug: categorySlug });
33421
- const topics = (category?.topic_list?.topics || []).filter((topic) => {
33422
- const topicLocale = this.getLanguageFromTitle(topic.fancy_title);
33423
- return (topic.pinned &&
33424
- topic.visible &&
33425
- // Keep same locale, if exists
33426
- (!topicLocale || !opts?.locale || topicLocale === opts.locale));
33427
- });
33428
- const posts = (await Promise$1.all(topics.map(async (t) => {
33429
- const topic = await api.getTopic({ id: t.id.toString() });
33430
- // Ignore 'About' topics
33431
- if (topic.tags.includes('about-category'))
33432
- return undefined;
33433
- const posts = this.filterRecentPosts(topic.post_stream.posts, opts);
33434
- if (isEmptyArray(posts))
33435
- return undefined;
33436
- return this.toFeed(posts, topic, url, opts);
33437
- }))).filter(isNotNil);
33438
- if (isEmptyArray(posts)) {
33439
- console.warn(`${this._logPrefix}No recent post found on category #${categoryId}`);
33440
- }
33441
- return posts;
33466
+ async resolveFeed(json, url, opts) {
33467
+ // Check json is JsonFeed compatible
33468
+ if (JsonFeedUtils.isJsonFeed(json)) {
33469
+ // Migrate from old versions
33470
+ let feed = JsonFeedUtils.migrateFeed(json);
33471
+ // Resolve items (e.g. using feed.next_url)
33472
+ feed = await this.resolveFeedItems(feed, { ...opts, depth: opts?.depth ?? 0 });
33473
+ // Truncate html
33474
+ JsonFeedUtils.truncateFeedItemsHtml(feed, opts);
33475
+ return isNotEmptyArray(feed?.items) ? [feed] : [];
33476
+ }
33477
+ // not resolved (unknown format)
33478
+ return undefined;
33442
33479
  }
33443
- static async getFeedByTopicUrl(url, opts) {
33444
- const topicId = this.extractTopicIdFromUrl(url);
33445
- if (!topicId) {
33446
- console.error(`${this._logPrefix}Invalid URL`);
33447
- return undefined;
33448
- }
33449
- const api = opts?.api ?? new DiscourseAPI(this.getDiscourseBaseUrl(url));
33450
- const topic = await api.getTopic({ id: topicId.toString() });
33451
- // Ignore about category
33452
- if (topic.tags?.includes('about-category'))
33453
- return undefined;
33454
- const posts = this.filterRecentPosts(topic.post_stream.posts, opts);
33455
- if (isEmptyArray(posts)) {
33456
- console.warn(`${this._logPrefix}No recent posts found in topic #${topicId}`);
33457
- return undefined;
33480
+ async resolveFeedItems(json, opts) {
33481
+ if (!json)
33482
+ return json;
33483
+ // Load items
33484
+ if (isEmptyArray(json.items) && isNotNilOrBlank(json.next_url)) {
33485
+ const depth = opts?.depth ?? 0;
33486
+ if (depth >= 3) {
33487
+ console.warn(`${this._logPrefix}Max depth reached (${depth}) for ${json.feed_url}`);
33488
+ return json;
33489
+ }
33490
+ // Load items
33491
+ const feed = await this.load(json.next_url, { ...opts, depth: depth + 1 });
33492
+ if (isEmptyArray(feed?.items)) {
33493
+ return json;
33494
+ }
33495
+ // Merge with the input json
33496
+ return {
33497
+ ...json,
33498
+ title: json.title ?? feed.title,
33499
+ home_page_url: json.home_page_url ?? feed.home_page_url,
33500
+ authors: isNotEmptyArray(json.authors) ? json.authors : feed.authors,
33501
+ items: feed.items,
33502
+ tag_template: json.tag_template ?? feed.tag_template,
33503
+ };
33458
33504
  }
33459
- return this.toFeed(posts, topic, url, opts);
33460
- }
33461
- static filterRecentPosts(posts, opts) {
33462
- const maxAgeInMonths = opts?.maxAgeInMonths ?? JsonFeedUtils.DEFAULT_MAX_POST_AGE_IN_MONTHS;
33463
- if (maxAgeInMonths <= 0)
33464
- return posts; // Accept all
33465
- const minDate = maxAgeInMonths > 0 ? DateUtils.moment().subtract(maxAgeInMonths, 'month').startOf('day').utc() : undefined;
33466
- return (posts || []).filter((post) => post && DateUtils.moment(post.created_at).isSameOrAfter(minDate));
33467
- }
33468
- static toFeed(posts, topic, url, opts) {
33469
- const baseUrl = this.getDiscourseBaseUrl(url);
33470
- const title = this.cleanTitle(topic.fancy_title ?? topic.title);
33471
- const items = (posts || []).map((post) => this.toFeedItem(post, topic, url, opts));
33472
- const firstItem = items?.[0];
33473
- if (firstItem)
33474
- firstItem.title = title;
33475
- const authorName = topic?.details?.created_by && (topic.details.created_by.name || topic.details.created_by.username);
33476
- const authorAvatar = baseUrl + topic.details.created_by.avatar_template.replace('{size}', '48');
33477
- return {
33478
- version: JsonFeedUtils.VERSION_1_1,
33479
- feed_url: url,
33480
- title: this.cleanTitle(topic.fancy_title),
33481
- expired: topic.archived,
33482
- authors: [
33483
- {
33484
- name: authorName,
33485
- avatar: authorAvatar,
33486
- },
33487
- ],
33488
- items,
33489
- tag_template: baseUrl + '/tag/{tag}',
33490
- };
33491
- }
33492
- static toFeedItem(post, topic, url, opts) {
33493
- const baseUrl = this.getDiscourseBaseUrl(url);
33494
- url = post.post_url ? baseUrl + post.post_url : url;
33495
- const id = post.post_url ? baseUrl + post.post_url : post.id;
33496
- const html = JsonFeedUtils.truncateHtml(post.cooked, opts);
33497
- const truncated = html?.length !== post.cooked?.length;
33498
- const authorName = post.name || post.username;
33499
- const authorAvatar = baseUrl + (post.avatar_template.replace('{size}', '48') ?? topic.details.last_poster.avatar_template.replace('{size}', '48'));
33500
- return {
33501
- id,
33502
- url,
33503
- authors: [
33504
- {
33505
- name: authorName,
33506
- avatar: authorAvatar,
33507
- },
33508
- ],
33509
- content_html: html,
33510
- date_published: post.created_at,
33511
- tags: topic.tags,
33512
- truncated,
33513
- };
33505
+ return json;
33514
33506
  }
33515
- static cleanTitle(title) {
33516
- if (!title)
33517
- return undefined;
33518
- return title.replace(/\s\([a-z]{2}(:?-[A-Z]{2})?\)$/, '');
33507
+ onAfterLoadAll() {
33508
+ console.debug(`${this._logPrefix}Feeds loaded`);
33519
33509
  }
33510
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedService, deps: [{ token: LocalSettingsService }, { token: ENVIRONMENT, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
33511
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedService });
33520
33512
  }
33521
-
33522
- const APP_FEED_SERVICE = new InjectionToken('FeeService');
33513
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedService, decorators: [{
33514
+ type: Injectable
33515
+ }], ctorParameters: () => [{ type: LocalSettingsService }, { type: Environment, decorators: [{
33516
+ type: Optional
33517
+ }, {
33518
+ type: Inject,
33519
+ args: [ENVIRONMENT]
33520
+ }] }] });
33523
33521
 
33524
33522
  class FeedDirective {
33525
33523
  element;
@@ -33533,7 +33531,7 @@ class FeedDirective {
33533
33531
  _baseUrl;
33534
33532
  set feedUrl(value) {
33535
33533
  this._feedUrl = value;
33536
- this._baseUrl = value && DiscourseUtils.removeJsonExtension(value);
33534
+ this._baseUrl = value && JsonFeedUtils.removeJsonExtension(value);
33537
33535
  }
33538
33536
  get feedUrl() {
33539
33537
  return this._feedUrl;
@@ -33659,7 +33657,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
33659
33657
  }]
33660
33658
  }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { feedUrl: [{
33661
33659
  type: Input,
33662
- args: ['feed']
33660
+ args: [{ alias: 'feed', required: true }]
33663
33661
  }] } });
33664
33662
 
33665
33663
  class FeedsComponent {
@@ -33741,7 +33739,7 @@ class FeedsComponent {
33741
33739
  this.feeds?.forEach((f) => this.openFeedHome(f));
33742
33740
  return;
33743
33741
  }
33744
- const url = feed.home_page_url ?? DiscourseUtils.removeJsonExtension(feed.feed_url) ?? this.feedService.getHomeUrl();
33742
+ const url = feed.home_page_url ?? JsonFeedUtils.removeJsonExtension(feed.feed_url) ?? this.feedService.getHomeUrl();
33745
33743
  if (!url)
33746
33744
  return;
33747
33745
  return this.platform.open(url);
@@ -33749,7 +33747,7 @@ class FeedsComponent {
33749
33747
  openUrl(url) {
33750
33748
  if (!url)
33751
33749
  return;
33752
- const fixedUrl = DiscourseUtils.removeJsonExtension(url);
33750
+ const fixedUrl = JsonFeedUtils.removeJsonExtension(url);
33753
33751
  return this.platform.open(fixedUrl);
33754
33752
  }
33755
33753
  openTag(feed, tag) {
@@ -44063,7 +44061,7 @@ const MessageTypeList = Object.freeze([
44063
44061
  },
44064
44062
  {
44065
44063
  id: MessageTypes.FEED,
44066
- icon: 'paper',
44064
+ icon: 'megaphone',
44067
44065
  label: 'SOCIAL.MESSAGE.TYPE_ENUM.FEED',
44068
44066
  },
44069
44067
  ]);
@@ -44208,11 +44206,11 @@ class MessageForm extends AppForm {
44208
44206
  this.cd.markForCheck();
44209
44207
  }
44210
44208
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageForm, deps: [{ token: i0.Injector }, { token: i1$3.UntypedFormBuilder }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
44211
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: MessageForm, selector: "app-message-form", inputs: { suggestFn: "suggestFn", subjectMinLength: "subjectMinLength", subjectMaxLength: "subjectMaxLength", bodyMaxLength: "bodyMaxLength", bodyAutoHeight: "bodyAutoHeight", canSelectType: "canSelectType", canRecipientFilter: "canRecipientFilter", recipientFilterCount: "recipientFilterCount" }, usesInheritance: true, ngImport: i0, template: "<form [formGroup]=\"form\" class=\"form-container\">\n <!-- type -->\n <mat-form-field *ngIf=\"canSelectType\">\n <mat-label>{{ 'SOCIAL.MESSAGE.TYPE' | translate }}</mat-label>\n <mat-select formControlName=\"type\">\n <ng-container *ngFor=\"let item of types\">\n <mat-option [value]=\"item.id\" *ngIf=\"item.id !== 'FEED' || canRecipientFilter\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label | translate }}\n </mat-option>\n </ng-container>\n </mat-select>\n </mat-form-field>\n\n <!-- Recipients -->\n <mat-chips-field\n *ngIf=\"(form.controls.type.valueChanges | async) !== 'FEED'; else recipientFilter\"\n formControlName=\"recipients\"\n chipColor=\"accent\"\n [placeholder]=\"'SOCIAL.MESSAGE.RECIPIENTS' | translate\"\n [config]=\"autocompleteFields.recipients\"\n [equals]=\"isSamePerson\"\n ></mat-chips-field>\n <ng-template #recipientFilter>\n <mat-form-field>\n <mat-chip-grid>\n <mat-chip-row>\n {{ 'SOCIAL.MESSAGE.RECIPIENT_FILTER_COUNT' | translate: { count: recipientFilterCount } }}\n </mat-chip-row>\n </mat-chip-grid>\n <input matInput hidden type=\"number\" />\n </mat-form-field>\n </ng-template>\n\n <!-- Subject -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.SUBJECT' | translate }}</mat-label>\n <input matInput type=\"text\" formControlName=\"subject\" autocomplete=\"off\" required />\n <mat-error *ngIf=\"form.controls.subject.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"form.controls.subject.hasError('minlength')\">\n {{ 'ERROR.FIELD_MIN_LENGTH_COMPACT' | translate }}\n </mat-error>\n <mat-error *ngIf=\"form.controls.subject.hasError('maxlength')\">\n {{ 'ERROR.FIELD_MAX_LENGTH_COMPACT' | translate }}\n </mat-error>\n <mat-hint *ngIf=\"subjectMaxLength\" align=\"end\">\n {{\n 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.subject.value?.length || 0, max: subjectMaxLength }\n }}\n </mat-hint>\n </mat-form-field>\n\n <!-- Body -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.BODY_HELP' | translate }}</mat-label>\n <textarea\n matInput\n type=\"text\"\n formControlName=\"body\"\n [class.fixed-height]=\"!bodyAutoHeight\"\n [cdkTextareaAutosize]=\"bodyAutoHeight\"\n (keydown.control.enter)=\"doSubmit($event)\"\n ></textarea>\n <mat-error *ngIf=\"form.controls.body.hasError('maxlength')\">\n {{ 'ERROR.FIELD_MAX_LENGTH' | translate: form.controls.body.errors.maxlength }}\n </mat-error>\n <mat-hint *ngIf=\"bodyMaxLength\" align=\"end\">\n {{ 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.body.value?.length || 0, max: bodyMaxLength } }}\n </mat-hint>\n </mat-form-field>\n</form>\n", styles: ["textarea.fixed-height{height:11.5em}textarea{min-height:11.5em}\n"], dependencies: [{ kind: "directive", type: i3$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2$1.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "directive", type: i1$1.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "directive", type: i1$4.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "component", type: i6$3.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i2.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: MatChipsField, selector: "mat-chips-field", inputs: ["equals", "logPrefix", "formControl", "formControlName", "floatLabel", "placeholder", "suggestFn", "required", "mobile", "readonly", "clearable", "debounceTime", "displaySeparator", "displayWith", "displayAttributes", "displayColumnSizes", "displayColumnNames", "highlightAccent", "showAllOnFocus", "showPanelOnFocus", "autofocus", "config", "i18nPrefix", "noResultMessage", "panelClass", "panelWidth", "disableRipple", "matAutocompletePosition", "itemSize", "fetchMoreThreshold", "suggestLengthThreshold", "showLoadingSpinner", "chipColor", "debug", "applyImplicitValue", "dropButtonTitle", "clearButtonTitle", "trimSearchText", "hideRequiredMarker", "colSizes", "separatorKeysCodes", "appearance", "subscriptSizing", "class", "filter", "tabindex", "items"], outputs: ["click", "blur", "focus", "dropButtonClick", "keydown.escape", "keyup.enter"] }, { kind: "directive", type: AutoResizeDirective, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMaxRows", "cdkAutosizeMinRows"] }, { kind: "component", type: i11$1.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "component", type: i11$1.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "pipe", type: i3$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
44209
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: MessageForm, selector: "app-message-form", inputs: { suggestFn: "suggestFn", subjectMinLength: "subjectMinLength", subjectMaxLength: "subjectMaxLength", bodyMaxLength: "bodyMaxLength", bodyAutoHeight: "bodyAutoHeight", canSelectType: "canSelectType", canRecipientFilter: "canRecipientFilter", recipientFilterCount: "recipientFilterCount" }, usesInheritance: true, ngImport: i0, template: "<form [formGroup]=\"form\" class=\"form-container\">\n <!-- type -->\n <mat-form-field *ngIf=\"canSelectType\">\n <mat-label>{{ 'SOCIAL.MESSAGE.TYPE' | translate }}</mat-label>\n <mat-select formControlName=\"type\">\n @for (item of types; track item.id) {\n @if (canRecipientFilter || item.id !== 'FEED') {\n <mat-option [value]=\"item.id\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label | translate }}\n </mat-option>\n }\n }\n </mat-select>\n </mat-form-field>\n\n <!-- Recipients -->\n @if ((form | formGetValue: 'type') !== 'FEED') {\n <mat-chips-field\n formControlName=\"recipients\"\n chipColor=\"accent\"\n [placeholder]=\"'SOCIAL.MESSAGE.RECIPIENTS' | translate\"\n [config]=\"autocompleteFields.recipients\"\n [equals]=\"isSamePerson\"\n ></mat-chips-field>\n } @else {\n <mat-form-field>\n <input matInput hidden formControlName=\"recipients\" type=\"text\" />\n <mat-chip-grid>\n <mat-chip-row>\n {{ 'SOCIAL.MESSAGE.RECIPIENT_FILTER_COUNT' | translate: { count: recipientFilterCount } }}\n </mat-chip-row>\n </mat-chip-grid>\n </mat-form-field>\n }\n\n <!-- Subject -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.SUBJECT' | translate }}</mat-label>\n <input matInput type=\"text\" formControlName=\"subject\" autocomplete=\"off\" required />\n <mat-error *ngIf=\"form.controls.subject.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"form.controls.subject.hasError('minlength')\">\n {{ 'ERROR.FIELD_MIN_LENGTH_COMPACT' | translate }}\n </mat-error>\n <mat-error *ngIf=\"form.controls.subject.hasError('maxlength')\">\n {{ 'ERROR.FIELD_MAX_LENGTH_COMPACT' | translate }}\n </mat-error>\n <mat-hint *ngIf=\"subjectMaxLength\" align=\"end\">\n {{\n 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.subject.value?.length || 0, max: subjectMaxLength }\n }}\n </mat-hint>\n </mat-form-field>\n\n <!-- Body -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.BODY_HELP' | translate }}</mat-label>\n <textarea\n matInput\n type=\"text\"\n formControlName=\"body\"\n [class.fixed-height]=\"!bodyAutoHeight\"\n [cdkTextareaAutosize]=\"bodyAutoHeight\"\n (keydown.control.enter)=\"doSubmit($event)\"\n ></textarea>\n <mat-error *ngIf=\"form.controls.body.hasError('maxlength')\">\n {{ 'ERROR.FIELD_MAX_LENGTH' | translate: form.controls.body.errors.maxlength }}\n </mat-error>\n <mat-hint *ngIf=\"bodyMaxLength\" align=\"end\">\n {{ 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.body.value?.length || 0, max: bodyMaxLength } }}\n </mat-hint>\n </mat-form-field>\n</form>\n", styles: ["textarea.fixed-height{height:11.5em}textarea{min-height:11.5em}\n"], dependencies: [{ kind: "directive", type: i3$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2$1.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "directive", type: i1$1.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "directive", type: i1$4.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "component", type: i6$3.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i2.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: MatChipsField, selector: "mat-chips-field", inputs: ["equals", "logPrefix", "formControl", "formControlName", "floatLabel", "placeholder", "suggestFn", "required", "mobile", "readonly", "clearable", "debounceTime", "displaySeparator", "displayWith", "displayAttributes", "displayColumnSizes", "displayColumnNames", "highlightAccent", "showAllOnFocus", "showPanelOnFocus", "autofocus", "config", "i18nPrefix", "noResultMessage", "panelClass", "panelWidth", "disableRipple", "matAutocompletePosition", "itemSize", "fetchMoreThreshold", "suggestLengthThreshold", "showLoadingSpinner", "chipColor", "debug", "applyImplicitValue", "dropButtonTitle", "clearButtonTitle", "trimSearchText", "hideRequiredMarker", "colSizes", "separatorKeysCodes", "appearance", "subscriptSizing", "class", "filter", "tabindex", "items"], outputs: ["click", "blur", "focus", "dropButtonClick", "keydown.escape", "keyup.enter"] }, { kind: "directive", type: AutoResizeDirective, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMaxRows", "cdkAutosizeMinRows"] }, { kind: "component", type: i11$1.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "component", type: i11$1.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }, { kind: "pipe", type: FormGetValuePipe, name: "formGetValue" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
44212
44210
  }
44213
44211
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageForm, decorators: [{
44214
44212
  type: Component,
44215
- args: [{ selector: 'app-message-form', changeDetection: ChangeDetectionStrategy.OnPush, template: "<form [formGroup]=\"form\" class=\"form-container\">\n <!-- type -->\n <mat-form-field *ngIf=\"canSelectType\">\n <mat-label>{{ 'SOCIAL.MESSAGE.TYPE' | translate }}</mat-label>\n <mat-select formControlName=\"type\">\n <ng-container *ngFor=\"let item of types\">\n <mat-option [value]=\"item.id\" *ngIf=\"item.id !== 'FEED' || canRecipientFilter\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label | translate }}\n </mat-option>\n </ng-container>\n </mat-select>\n </mat-form-field>\n\n <!-- Recipients -->\n <mat-chips-field\n *ngIf=\"(form.controls.type.valueChanges | async) !== 'FEED'; else recipientFilter\"\n formControlName=\"recipients\"\n chipColor=\"accent\"\n [placeholder]=\"'SOCIAL.MESSAGE.RECIPIENTS' | translate\"\n [config]=\"autocompleteFields.recipients\"\n [equals]=\"isSamePerson\"\n ></mat-chips-field>\n <ng-template #recipientFilter>\n <mat-form-field>\n <mat-chip-grid>\n <mat-chip-row>\n {{ 'SOCIAL.MESSAGE.RECIPIENT_FILTER_COUNT' | translate: { count: recipientFilterCount } }}\n </mat-chip-row>\n </mat-chip-grid>\n <input matInput hidden type=\"number\" />\n </mat-form-field>\n </ng-template>\n\n <!-- Subject -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.SUBJECT' | translate }}</mat-label>\n <input matInput type=\"text\" formControlName=\"subject\" autocomplete=\"off\" required />\n <mat-error *ngIf=\"form.controls.subject.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"form.controls.subject.hasError('minlength')\">\n {{ 'ERROR.FIELD_MIN_LENGTH_COMPACT' | translate }}\n </mat-error>\n <mat-error *ngIf=\"form.controls.subject.hasError('maxlength')\">\n {{ 'ERROR.FIELD_MAX_LENGTH_COMPACT' | translate }}\n </mat-error>\n <mat-hint *ngIf=\"subjectMaxLength\" align=\"end\">\n {{\n 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.subject.value?.length || 0, max: subjectMaxLength }\n }}\n </mat-hint>\n </mat-form-field>\n\n <!-- Body -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.BODY_HELP' | translate }}</mat-label>\n <textarea\n matInput\n type=\"text\"\n formControlName=\"body\"\n [class.fixed-height]=\"!bodyAutoHeight\"\n [cdkTextareaAutosize]=\"bodyAutoHeight\"\n (keydown.control.enter)=\"doSubmit($event)\"\n ></textarea>\n <mat-error *ngIf=\"form.controls.body.hasError('maxlength')\">\n {{ 'ERROR.FIELD_MAX_LENGTH' | translate: form.controls.body.errors.maxlength }}\n </mat-error>\n <mat-hint *ngIf=\"bodyMaxLength\" align=\"end\">\n {{ 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.body.value?.length || 0, max: bodyMaxLength } }}\n </mat-hint>\n </mat-form-field>\n</form>\n", styles: ["textarea.fixed-height{height:11.5em}textarea{min-height:11.5em}\n"] }]
44213
+ args: [{ selector: 'app-message-form', changeDetection: ChangeDetectionStrategy.OnPush, template: "<form [formGroup]=\"form\" class=\"form-container\">\n <!-- type -->\n <mat-form-field *ngIf=\"canSelectType\">\n <mat-label>{{ 'SOCIAL.MESSAGE.TYPE' | translate }}</mat-label>\n <mat-select formControlName=\"type\">\n @for (item of types; track item.id) {\n @if (canRecipientFilter || item.id !== 'FEED') {\n <mat-option [value]=\"item.id\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label | translate }}\n </mat-option>\n }\n }\n </mat-select>\n </mat-form-field>\n\n <!-- Recipients -->\n @if ((form | formGetValue: 'type') !== 'FEED') {\n <mat-chips-field\n formControlName=\"recipients\"\n chipColor=\"accent\"\n [placeholder]=\"'SOCIAL.MESSAGE.RECIPIENTS' | translate\"\n [config]=\"autocompleteFields.recipients\"\n [equals]=\"isSamePerson\"\n ></mat-chips-field>\n } @else {\n <mat-form-field>\n <input matInput hidden formControlName=\"recipients\" type=\"text\" />\n <mat-chip-grid>\n <mat-chip-row>\n {{ 'SOCIAL.MESSAGE.RECIPIENT_FILTER_COUNT' | translate: { count: recipientFilterCount } }}\n </mat-chip-row>\n </mat-chip-grid>\n </mat-form-field>\n }\n\n <!-- Subject -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.SUBJECT' | translate }}</mat-label>\n <input matInput type=\"text\" formControlName=\"subject\" autocomplete=\"off\" required />\n <mat-error *ngIf=\"form.controls.subject.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"form.controls.subject.hasError('minlength')\">\n {{ 'ERROR.FIELD_MIN_LENGTH_COMPACT' | translate }}\n </mat-error>\n <mat-error *ngIf=\"form.controls.subject.hasError('maxlength')\">\n {{ 'ERROR.FIELD_MAX_LENGTH_COMPACT' | translate }}\n </mat-error>\n <mat-hint *ngIf=\"subjectMaxLength\" align=\"end\">\n {{\n 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.subject.value?.length || 0, max: subjectMaxLength }\n }}\n </mat-hint>\n </mat-form-field>\n\n <!-- Body -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.BODY_HELP' | translate }}</mat-label>\n <textarea\n matInput\n type=\"text\"\n formControlName=\"body\"\n [class.fixed-height]=\"!bodyAutoHeight\"\n [cdkTextareaAutosize]=\"bodyAutoHeight\"\n (keydown.control.enter)=\"doSubmit($event)\"\n ></textarea>\n <mat-error *ngIf=\"form.controls.body.hasError('maxlength')\">\n {{ 'ERROR.FIELD_MAX_LENGTH' | translate: form.controls.body.errors.maxlength }}\n </mat-error>\n <mat-hint *ngIf=\"bodyMaxLength\" align=\"end\">\n {{ 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.body.value?.length || 0, max: bodyMaxLength } }}\n </mat-hint>\n </mat-form-field>\n</form>\n", styles: ["textarea.fixed-height{height:11.5em}textarea{min-height:11.5em}\n"] }]
44216
44214
  }], ctorParameters: () => [{ type: i0.Injector }, { type: i1$3.UntypedFormBuilder }, { type: i0.ChangeDetectorRef }], propDecorators: { suggestFn: [{
44217
44215
  type: Input
44218
44216
  }], subjectMinLength: [{
@@ -44861,160 +44859,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
44861
44859
  args: [ENVIRONMENT]
44862
44860
  }] }] });
44863
44861
 
44864
- class DiscourseFeedService extends StartableService {
44865
- settings;
44866
- environment;
44867
- discourseApis = new Map();
44868
- _logPrefix = '[discourse-feed-service] ';
44869
- _state = new RxState();
44870
- network = inject(NetworkService);
44871
- locale$ = this._state.select('locale');
44872
- feedUrls$ = this._state.select('feedUrls');
44873
- get feedUrls() {
44874
- return this._state.get('feedUrls');
44875
- }
44876
- set feedUrls(urls) {
44877
- this._state.set('feedUrls', () => urls);
44878
- }
44879
- constructor(settings, environment) {
44880
- super(settings);
44881
- this.settings = settings;
44882
- this.environment = environment;
44883
- this._debug = !environment.production;
44884
- if (this._debug)
44885
- console.debug('[discourse-feed-service] Creating service');
44886
- this._state.connect('locale', this.settings.locale$);
44887
- this._state.connect('feedUrls', this.locale$.pipe(filter(isNotNilOrBlank), map((locale) => (locale && environment.feed?.jsonFeed?.[locale]) ?? [])));
44888
- }
44889
- async ngOnStart() {
44890
- await this.settings.ready();
44891
- return {
44892
- locale: this.settings.locale,
44893
- };
44894
- }
44895
- watchAll(opts) {
44896
- if (!this.started) {
44897
- return from(this.start()).pipe(switchMap$1(() => this.watchAll(opts)));
44898
- }
44899
- if (isNotEmptyArray(opts?.urls)) {
44900
- return this.locale$.pipe(mergeMap((locale) => this.loadAll(opts.urls, { locale, ...opts })));
44901
- }
44902
- return this.feedUrls$.pipe(filter(isNotEmptyArray), mergeMap((urls) => this.loadAll(urls, opts)));
44903
- }
44904
- getHomeUrl(feed) {
44905
- const feedUrl = feed?.feed_url ?? firstArrayValue(this.feedUrls);
44906
- return feedUrl ? UrlUtils.getRootUrl(feedUrl) : undefined;
44907
- }
44908
- getTagUrl(feed, tag) {
44909
- if (!feed || !tag)
44910
- throw new Error("Missing 'feed' or 'tag' argument");
44911
- const baseUrl = this.getHomeUrl(feed);
44912
- return feed.tag_template?.replace('{tag}', tag) ?? baseUrl + '/tag/' + tag;
44913
- }
44914
- async load(url, opts) {
44915
- const { data } = await this.loadAll([url], opts);
44916
- return data?.[0];
44917
- }
44918
- async loadAll(urls, opts) {
44919
- await this.ready();
44920
- urls = urls ?? this.feedUrls;
44921
- opts = {
44922
- maxAgeInMonths: opts?.maxAgeInMonths ?? this.environment.feed?.maxAgeInMonths,
44923
- maxContentLength: opts?.maxContentLength ?? this.environment.feed?.maxContentLength,
44924
- locale: this.settings.locale,
44925
- depth: 0,
44926
- closeApis: true,
44927
- ...opts,
44928
- };
44929
- const feeds = await Promise$1.all((urls || []).map(async (url) => {
44930
- try {
44931
- // Get JSON
44932
- const json = await this.network.fetch(url);
44933
- console.debug(`${this._logPrefix}Loaded JSON from ${url}`, json);
44934
- if (JsonFeedUtils.isJsonFeed(json)) {
44935
- // Migrate old version
44936
- let feed = JsonFeedUtils.migrateFeed(json);
44937
- // Resolve items (e.g. using feed.next_url)
44938
- feed = await this.resolveFeedItems(feed, { ...opts, depth: opts?.depth ?? 0 });
44939
- // Truncate html
44940
- JsonFeedUtils.truncateFeedItemsHtml(feed, opts);
44941
- return isNotEmptyArray(feed?.items) ? [feed] : [];
44942
- }
44943
- // Get discourse
44944
- if (DiscourseUtils.isDiscourseObject(json)) {
44945
- const api = this.getOrCreateDiscourseApi(url);
44946
- return await DiscourseUtils.getFeedsByUrl(url, { ...opts, api });
44947
- }
44948
- return undefined;
44949
- }
44950
- catch (err) {
44951
- if (err?.status === 404) {
44952
- console.error(`${this._logPrefix} Error while fetching feed at ${url}. 404 (Not found)`);
44953
- }
44954
- else {
44955
- console.error(`${this._logPrefix} Error while fetching feed at ${url}`, err);
44956
- }
44957
- return Promise$1.resolve([]);
44958
- }
44959
- }));
44960
- if (opts?.closeApis !== false) {
44961
- this.closeApis();
44962
- }
44963
- const data = feeds.flat();
44964
- return { data, total: data.length };
44965
- }
44966
- getOrCreateDiscourseApi(url) {
44967
- const baseUrl = DiscourseUtils.getDiscourseBaseUrl(url);
44968
- let api = this.discourseApis.get(baseUrl);
44969
- if (!api) {
44970
- api = new DiscourseAPI(baseUrl);
44971
- this.discourseApis.set(baseUrl, api);
44972
- }
44973
- return api;
44974
- }
44975
- closeApis() {
44976
- this.discourseApis.clear();
44977
- }
44978
- async resolveFeedItems(json, opts) {
44979
- if (!json)
44980
- return json;
44981
- // Load items
44982
- if (isEmptyArray(json.items) && isNotNilOrBlank(json.next_url)) {
44983
- const depth = opts?.depth ?? 0;
44984
- if (depth >= 3) {
44985
- console.warn(`${this._logPrefix}Max depth reached (${depth}) for ${json.feed_url}`);
44986
- return json;
44987
- }
44988
- // Load items
44989
- const feed = await this.load(json.next_url, { ...opts, closeApis: false, depth: depth + 1 });
44990
- if (isEmptyArray(feed?.items)) {
44991
- return json;
44992
- }
44993
- // Merge with the input json
44994
- return {
44995
- ...json,
44996
- title: json.title ?? feed.title,
44997
- home_page_url: json.home_page_url ?? feed.home_page_url,
44998
- authors: isNotEmptyArray(json.authors) ? json.authors : feed.authors,
44999
- items: feed.items,
45000
- tag_template: json.tag_template ?? feed.tag_template,
45001
- };
45002
- }
45003
- return json;
45004
- }
45005
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DiscourseFeedService, deps: [{ token: LocalSettingsService }, { token: ENVIRONMENT, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
45006
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DiscourseFeedService, providedIn: 'root' });
45007
- }
45008
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DiscourseFeedService, decorators: [{
45009
- type: Injectable,
45010
- args: [{ providedIn: 'root' }]
45011
- }], ctorParameters: () => [{ type: LocalSettingsService }, { type: Environment, decorators: [{
45012
- type: Optional
45013
- }, {
45014
- type: Inject,
45015
- args: [ENVIRONMENT]
45016
- }] }] });
45017
-
45018
44862
  const PersonFragments = {
45019
44863
  person: gql$1 `
45020
44864
  fragment PersonFragment on PersonVO {
@@ -49898,5 +49742,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
49898
49742
  * Generated bundle index. Do not edit.
49899
49743
  */
49900
49744
 
49901
- export { APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_CONFIG_OPTIONS, APP_DEBUG_DATA_SERVICE, APP_FEED_SERVICE, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_FRAGMENTS, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_HOME_CONFIG, APP_HOTKEYS_CONFIG, 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_NAMED_FILTER_SERVICE, APP_PROGRESS_BAR_SERVICE, APP_SETTINGS_MENU_ITEMS, APP_STORAGE, APP_STORAGE_EXPLORER_PROTECTED_KEYS, APP_TESTING_PAGES, APP_USER_EVENT_LIST_INFINITE_SCROLL_THRESHOLD, APP_USER_EVENT_SERVICE, APP_USER_SETTINGS_OPTIONS, APP_USER_TOKEN_SCOPES, AboutModal, AbstractNamedFilterService, 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, AppGraphQLModule, AppHomePageModule, AppIconComponent, AppIconModule, AppImageGalleryComponent, AppInMemoryTable, AppInstallUpgradeCard, AppInstallUpgradeCardModule, AppListForm, AppListFormModule, AppLoadingSpinner, AppMarkdownContent, AppMarkdownModal, AppMenuModule, AppNullForm, AppPropertiesForm, AppPropertiesFormModule, AppPropertiesTable, AppPropertiesUtils, AppPropertyUtils, AppRegisterModule, AppResetPasswordModal, AppRowField, AppSelectPeerModule, AppSettingsPageModule, AppTabEditor, AppTabEditorOptions, AppTable, AppTableModule, AppTableUtils, AppTextPopoverModule, AppUpdateOfflineModeCard, AppUpdateOfflineModeCardModule, AppValidatorService, AppendQueryParamsPipePipe, ArrayDistinctPipe, ArrayFilterPipe, ArrayFindByPropertyPipe, ArrayFirstPipe, ArrayFormTestPage, ArrayIncludesPipe, ArrayJoinPipe, ArrayLastPipe, ArrayLengthPipe, ArrayPluckPipe, ArraySortPipe, AsAnyPipe, AsArrayPipe, AsBooleanPipe, AsFloatLabelTypePipe, AsObservablePipe, AudioProvider, AudioTestingModule, AudioTestingPage, AuthGuardService, AutoResizeDirective, AutoTitleDirective, AutocompleteTestPage, AutofocusDirective, BadgeDirective, BadgeNumberPipe, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, BooleanFormatPipe, BooleanTestPage, CORE_CONFIG_OPTIONS, CORE_TESTING_PAGES, CapitalizePipe, CellValueChangeListener, ChangePasswordForm, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigFragments, ConfigService, Configuration, CoreModule, CorePipesModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, 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, DateShortTestPage, DateTestPage, DateTimeTestPage, DateUtils, DebugComponent, Department, DepartmentToStringPipe, DiscourseFeedService, DiscourseUtils, DisplayWithPipe, DragAndDropDirective, DurationPipe, DurationTestPage, ED25519_SEED_LENGTH, EMPTY_PLACEHOLDER_CHAR, EMPTY_PLACEHOLDER_CHAR_REGEXP_GLOBAL, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EntitiesAsyncTableDataSource, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, EnvironmentHttpLoader, EnvironmentLoader, ErrorCodes, EvenPipe, FeedDirective, FeedModule, FeedsComponent, FileResponse, FileService, FileSizePipe, FilesUtils, FirstFalsePipe, FirstPipe, FirstTruePipe, FormArrayHelper, FormArrayTestModule, FormButtonsBarComponent, FormButtonsBarToken, FormErrorPipe, FormErrorTranslatePipe, FormErrorTranslator, FormFieldDefinitionUtils, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetPipe, FormGetValuePipe, GalleryTestPage, GeolocationUtils, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, IMAGE_DEFAULTS, IPosition, ImageAttachment, ImageAttachmentFilter, ImageAttachmentService, ImageGalleryModule, ImageGalleryTestingModule, ImageModule, ImageService, ImagesUtils, InMemoryEntitiesService, 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, 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, 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, MaterialTestingModule, MathAbsPipe, MenuComponent, MenuItem, MenuItems, MenuOptions, MenuService, MenuTestingModule, MenuTestingPage, Message, MessageFilter, MessageForm, MessageModal, MessageModule, MessageService, MessageTypeList, MessageTypes, MimeTypes, ModalToolbarComponent, NETWORK_DEFAULT_CONNECTION_TIMEOUT, NamedFilter, NamedFilterFilter, NamedFilterSelector, NamedFilterSelectorTestingModule, NamedFilterSelectorTestingPage, NavActionsColumnComponent, 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, 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, RegExpUtils, RegisterConfirmPage, RegisterForm, RegisterModal, ResizableComponent, ResizableDirective, ResizableModule, RoundPipe, RxStateComputed, RxStateModule, 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_MATERIAL_TESTING_PAGES, SHARED_STORAGE_TESTING_PAGES, SHARED_TESTING_PAGES, SOCIAL_CONFIG_OPTIONS, SOCIAL_TESTING_PAGES, SPACE_PLACEHOLDER_CHAR, SPACE_PLACEHOLDER_CHAR_REGEXP_GLOBAL, SafeHtmlPipe, SafeStylePipe, SelectPeerModal, SelectionLengthPipe, ServerErrorCodes, SettingsPage, SharedAsyncValidators, SharedBadgeModule, SharedDebugModule, SharedDirectivesModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedMarkdownModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatChipsModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedNamedFilterModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedTestsPage, SharedTextFormModule, SharedToolbarModule, SharedValidators, SocialErrorCodes, SocialModule, SocialModuleOptionsToken, SocialTestingModule, Software, SplitArrayInChunksPipe, StartableService, StatusById, StatusIds, StatusList, StorageDrivers, StorageExplorerComponent, StorageExplorerModule, StorageExplorerTestingModule, StorageExplorerTestingRoutingModule, StorageService, StrIncludesPipe, StrLengthPipe, StrReplacePipe, SubMenuTabDirective, SwipeTestPage, TABLE_SETTINGS_ENUM, TOOLBAR_HEADER_ID, Table2TestPage, TableSelectColumnsComponent, TableTestPage, TableTestingModule, TableValidatorService, TextForm, TextFormTestingPage, TextPopover, TextPopoverTestingModule, TextPopoverTestingPage, ThrottledClickDirective, ToStringPipe, ToastTestingModule, ToastTestingPage, Toasts, TokenScope, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, TreeItemEntityUtils, TruncHtmlPipe, TruncTextPipe, UploadFile, UploadFileComponent, UploadFilePopover, UploadFileTestingModule, UploadFileTestingPage, UriUtils, UrlUtils, UserEventModule, UserEventNotificationIcon, UserEventNotificationList, UserEventNotificationModal, UserEventTestService, UserEventTestingModule, UserEventTestingPage, UserSettings, UserToken, UserTokenTable, UsersPage, ValueFormatPipe, VersionUtils, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayResize, arraySize, asInputElement, booleanToString, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, collectByProperty, compareVersionNumbers, computeDecimalDegrees, computeDecimalPart, copyEntity2Form, createPromiseEvent, createPromiseEventEmitter, 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, firstTrue, firstTruePromise, focusInput, focusNextInput, focusPreviousInput, formatLatLong, formatLatitude, formatLongitude, fromDateISOString, fromScrollEndEvent, fromUnixMsTimestamp, fromUnixTimestamp, getCaretPosition, getColorContrast, getColorShade, getColorTint, getControlFromPath, getFocusableInputElements, getFormErrors, getFormValueFromEntity, getInputRangeFromCaretIndex, getInputSelectionRangesFromMask, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, getRandomImageWithCredit, getUserAgent, hexToRgb, hexToRgbArray, initArrayControlsFromValues, interpolateString, intersectArrays, isAndroid, isBlankString, isCapacitor, isChrome, isControlHasInput, isEdge, isEmptyArray, isEntityService, isFirefox, isIOS, isInputElement, isInstanceOf, isInt, isIpad, isMacOS, isMobile, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilBoolean, isNotNilObject, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, isOnFieldMode, isPrint, isProgressEvent, isPromise, isResponseEvent, isSafari, isSameVersion, isStartableService, isTouchUi, isVersionCompatible, isWindows, joinProperties, joinPropertiesPath, lastArrayValue, logFormErrors, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, maskitoAutoSelectByMaskPattern, maskitoPrefixPlugin, matchMedia, matchUpperCase, mergeLoadResult, mixHex, moveInputCaretToSeparator, newArray, noHtml, noTrailingSlash, notNilOrDefault, nullIfNilOrBlank, nullIfUndefined, numberOrNilAttribute, numberToString, parseLatitudeOrLongitude, propertiesPathComparator, propertyComparator, propertyPathComparator, referentialToString, referentialsToString, remove, removeAll, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, rgbArrayToHex, rgbToHex, round, scrollFactory, selectInputContent, selectInputContentFromEvent, selectInputRange, setCalculatedValue, setControlEnabled, 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, toNotNil, toNumber, trimEmptyToNull, uncapitalizeFirstLetter, undefinedIfNull, underscoreToChangeCase, updateValueAndValidity, waitFor, waitForFalse, waitForTrue, waitIdle, waitWhilePending };
49745
+ export { APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_CONFIG_OPTIONS, APP_DEBUG_DATA_SERVICE, APP_FEED_SERVICE, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_FRAGMENTS, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_HOME_CONFIG, APP_HOTKEYS_CONFIG, 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_NAMED_FILTER_SERVICE, APP_PROGRESS_BAR_SERVICE, APP_SETTINGS_MENU_ITEMS, APP_STORAGE, APP_STORAGE_EXPLORER_PROTECTED_KEYS, APP_TESTING_PAGES, APP_USER_EVENT_LIST_INFINITE_SCROLL_THRESHOLD, APP_USER_EVENT_SERVICE, APP_USER_SETTINGS_OPTIONS, APP_USER_TOKEN_SCOPES, AboutModal, AbstractNamedFilterService, 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, AppGraphQLModule, AppHomePageModule, AppIconComponent, AppIconModule, AppImageGalleryComponent, AppInMemoryTable, AppInstallUpgradeCard, AppInstallUpgradeCardModule, AppListForm, AppListFormModule, AppLoadingSpinner, AppMarkdownContent, AppMarkdownModal, AppMenuModule, AppNullForm, AppPropertiesForm, AppPropertiesFormModule, AppPropertiesTable, AppPropertiesUtils, AppPropertyUtils, AppRegisterModule, AppResetPasswordModal, AppRowField, AppSelectPeerModule, AppSettingsPageModule, AppTabEditor, AppTabEditorOptions, AppTable, AppTableModule, AppTableUtils, AppTextPopoverModule, AppUpdateOfflineModeCard, AppUpdateOfflineModeCardModule, AppValidatorService, AppendQueryParamsPipePipe, ArrayDistinctPipe, ArrayFilterPipe, ArrayFindByPropertyPipe, ArrayFirstPipe, ArrayFormTestPage, ArrayIncludesPipe, ArrayJoinPipe, ArrayLastPipe, ArrayLengthPipe, ArrayMapPipe, ArrayPluckPipe, ArraySortPipe, AsAnyPipe, AsArrayPipe, AsBooleanPipe, AsFloatLabelTypePipe, AsObservablePipe, AudioProvider, AudioTestingModule, AudioTestingPage, AuthGuardService, AutoResizeDirective, AutoTitleDirective, AutocompleteTestPage, AutofocusDirective, BadgeDirective, BadgeNumberPipe, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, BooleanFormatPipe, BooleanTestPage, CORE_CONFIG_OPTIONS, CORE_TESTING_PAGES, CapitalizePipe, CellValueChangeListener, ChangePasswordForm, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigFragments, ConfigService, Configuration, CoreModule, CorePipesModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, 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, 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, EntitiesAsyncTableDataSource, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, EnvironmentHttpLoader, EnvironmentLoader, ErrorCodes, EvenPipe, FeedDirective, FeedModule, FeedService, FeedsComponent, FileResponse, FileService, FileSizePipe, FilesUtils, FirstFalsePipe, FirstPipe, FirstTruePipe, FormArrayHelper, FormArrayTestModule, FormButtonsBarComponent, FormButtonsBarToken, FormErrorPipe, FormErrorTranslatePipe, FormErrorTranslator, FormFieldDefinitionUtils, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetPipe, FormGetValuePipe, GalleryTestPage, GeolocationUtils, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, IMAGE_DEFAULTS, IPosition, ImageAttachment, ImageAttachmentFilter, ImageAttachmentService, ImageGalleryModule, ImageGalleryTestingModule, ImageModule, ImageService, ImagesUtils, InMemoryEntitiesService, 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, 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, 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, MaterialTestingModule, MathAbsPipe, MenuComponent, MenuItem, MenuItems, MenuOptions, MenuService, MenuTestingModule, MenuTestingPage, Message, MessageFilter, MessageForm, MessageModal, MessageModule, MessageService, MessageTypeList, MessageTypes, MimeTypes, ModalToolbarComponent, NETWORK_DEFAULT_CONNECTION_TIMEOUT, NamedFilter, NamedFilterFilter, NamedFilterSelector, NamedFilterSelectorTestingModule, NamedFilterSelectorTestingPage, NavActionsColumnComponent, 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, 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, RegExpUtils, RegisterConfirmPage, RegisterForm, RegisterModal, ResizableComponent, ResizableDirective, ResizableModule, RoundPipe, RxStateComputed, RxStateModule, 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_MATERIAL_TESTING_PAGES, SHARED_STORAGE_TESTING_PAGES, SHARED_TESTING_PAGES, SOCIAL_CONFIG_OPTIONS, SOCIAL_TESTING_PAGES, SPACE_PLACEHOLDER_CHAR, SPACE_PLACEHOLDER_CHAR_REGEXP_GLOBAL, SafeHtmlPipe, SafeStylePipe, SelectPeerModal, SelectionLengthPipe, ServerErrorCodes, SettingsPage, SharedAsyncValidators, SharedBadgeModule, SharedDebugModule, SharedDirectivesModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedMarkdownModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatChipsModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedNamedFilterModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedTestsPage, SharedTextFormModule, SharedToolbarModule, SharedValidators, SocialErrorCodes, SocialModule, SocialModuleOptionsToken, SocialTestingModule, Software, SplitArrayInChunksPipe, StartableService, StatusById, StatusIds, StatusList, StorageDrivers, StorageExplorerComponent, StorageExplorerModule, StorageExplorerTestingModule, StorageExplorerTestingRoutingModule, StorageService, StrIncludesPipe, StrLengthPipe, StrReplacePipe, SubMenuTabDirective, SwipeTestPage, TABLE_SETTINGS_ENUM, TOOLBAR_HEADER_ID, Table2TestPage, TableSelectColumnsComponent, TableTestPage, TableTestingModule, TableValidatorService, TextForm, TextFormTestingPage, TextPopover, TextPopoverTestingModule, TextPopoverTestingPage, ThrottledClickDirective, ToStringPipe, ToastTestingModule, ToastTestingPage, Toasts, TokenScope, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, TreeItemEntityUtils, TruncHtmlPipe, TruncTextPipe, UploadFile, UploadFileComponent, UploadFilePopover, UploadFileTestingModule, UploadFileTestingPage, UriUtils, UrlUtils, UserEventModule, UserEventNotificationIcon, UserEventNotificationList, UserEventNotificationModal, UserEventTestService, UserEventTestingModule, UserEventTestingPage, UserSettings, UserToken, UserTokenTable, UsersPage, ValueFormatPipe, VersionUtils, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayResize, arraySize, asInputElement, booleanToString, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, collectByProperty, compareVersionNumbers, computeDecimalDegrees, computeDecimalPart, copyEntity2Form, createPromiseEvent, createPromiseEventEmitter, 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, firstTrue, firstTruePromise, focusInput, focusNextInput, focusPreviousInput, formatLatLong, formatLatitude, formatLongitude, fromDateISOString, fromScrollEndEvent, fromUnixMsTimestamp, fromUnixTimestamp, getCaretPosition, getColorContrast, getColorShade, getColorTint, getControlFromPath, getFocusableInputElements, getFormErrors, getFormValueFromEntity, getInputRangeFromCaretIndex, getInputSelectionRangesFromMask, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, getRandomImageWithCredit, getUserAgent, hexToRgb, hexToRgbArray, initArrayControlsFromValues, interpolateString, intersectArrays, isAndroid, isBlankString, isCapacitor, isChrome, isControlHasInput, isEdge, isEmptyArray, isEntityService, isFirefox, isIOS, isInputElement, isInstanceOf, isInt, isIpad, isMacOS, isMobile, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilBoolean, isNotNilObject, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, isOnFieldMode, isPrint, isProgressEvent, isPromise, isResponseEvent, isSafari, isSameVersion, isStartableService, isTouchUi, isVersionCompatible, isWindows, joinProperties, joinPropertiesPath, lastArrayValue, logFormErrors, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, maskitoAutoSelectByMaskPattern, maskitoPrefixPlugin, matchMedia, matchUpperCase, mergeLoadResult, mixHex, moveInputCaretToSeparator, newArray, noHtml, noTrailingSlash, notNilOrDefault, nullIfNilOrBlank, nullIfUndefined, numberOrNilAttribute, numberToString, parseLatitudeOrLongitude, propertiesPathComparator, propertyComparator, propertyPathComparator, referentialToString, referentialsToString, remove, removeAll, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, rgbArrayToHex, rgbToHex, round, scrollFactory, selectInputContent, selectInputContentFromEvent, selectInputRange, setCalculatedValue, setControlEnabled, 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, toNotNil, toNumber, trimEmptyToNull, uncapitalizeFirstLetter, undefinedIfNull, underscoreToChangeCase, updateValueAndValidity, waitFor, waitForFalse, waitForTrue, waitIdle, waitWhilePending };
49902
49746
  //# sourceMappingURL=sumaris-net.ngx-components.mjs.map