@sumaris-net/ngx-components 18.13.0 → 18.14.1
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 +4 -0
- package/doc/feed/feed-en.json +26 -0
- package/doc/feed/feed-fr.json +26 -0
- package/esm2022/public_api.mjs +9 -1
- package/esm2022/src/app/core/home/home.mjs +13 -12
- package/esm2022/src/app/core/home/home.module.mjs +7 -3
- package/esm2022/src/app/core/services/local-settings.service.mjs +3 -2
- package/esm2022/src/app/core/services/network.service.mjs +12 -1
- package/esm2022/src/app/social/feed/discourse/discourse-feed.service.mjs +179 -0
- package/esm2022/src/app/social/feed/discourse/discourse.model.mjs +2 -0
- package/esm2022/src/app/social/feed/discourse/discourse.utils.mjs +168 -0
- package/esm2022/src/app/social/feed/feed.component.mjs +121 -0
- package/esm2022/src/app/social/feed/feed.directive.mjs +151 -0
- package/esm2022/src/app/social/feed/feed.model.mjs +68 -0
- package/esm2022/src/app/social/feed/feed.module.mjs +22 -0
- package/esm2022/src/app/social/feed/feed.service.mjs +3 -0
- package/esm2022/src/environments/environment.class.mjs +2 -1
- package/esm2022/src/environments/environment.mjs +10 -1
- package/fesm2022/sumaris-net.ngx-components.mjs +681 -8
- package/fesm2022/sumaris-net.ngx-components.mjs.map +1 -1
- package/package.json +1 -1
- package/public_api.d.ts +8 -0
- package/src/app/core/home/home.d.ts +1 -1
- package/src/app/core/home/home.module.d.ts +2 -1
- package/src/app/core/services/local-settings.service.d.ts +1 -0
- package/src/app/core/services/network.service.d.ts +1 -0
- package/src/app/social/feed/discourse/discourse-feed.service.d.ts +55 -0
- package/src/app/social/feed/discourse/discourse.model.d.ts +7 -0
- package/src/app/social/feed/discourse/discourse.utils.d.ts +44 -0
- package/src/app/social/feed/feed.component.d.ts +39 -0
- package/src/app/social/feed/feed.directive.d.ts +32 -0
- package/src/app/social/feed/feed.model.d.ts +69 -0
- package/src/app/social/feed/feed.module.d.ts +11 -0
- package/src/app/social/feed/feed.service.d.ts +13 -0
- package/src/assets/i18n/en-US.json +5 -1
- package/src/assets/i18n/en.json +5 -0
- package/src/assets/i18n/fr.json +5 -0
- package/src/environments/environment.class.d.ts +8 -0
|
@@ -4,7 +4,7 @@ import { firstValueFrom, shareReplay, tap, of, timer, Subject, merge, delay, isO
|
|
|
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';
|
|
7
|
-
import { CommonModule, DOCUMENT, LocationStrategy, Location, AsyncPipe, JsonPipe } from '@angular/common';
|
|
7
|
+
import { CommonModule, DOCUMENT, LocationStrategy, NgOptimizedImage, Location, AsyncPipe, JsonPipe } from '@angular/common';
|
|
8
8
|
import { CdkTableModule } from '@angular/cdk/table';
|
|
9
9
|
import * as i1$5 from '@angular/cdk/a11y';
|
|
10
10
|
import { A11yModule, FocusMonitor } from '@angular/cdk/a11y';
|
|
@@ -155,6 +155,8 @@ 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';
|
|
158
160
|
import 'moment-timezone';
|
|
159
161
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
160
162
|
|
|
@@ -220,6 +222,7 @@ class Environment {
|
|
|
220
222
|
// Account
|
|
221
223
|
account;
|
|
222
224
|
entityEditor;
|
|
225
|
+
feed;
|
|
223
226
|
}
|
|
224
227
|
|
|
225
228
|
const DATE_ISO_PATTERN = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
|
|
@@ -12892,6 +12895,15 @@ const environment = Object.freeze({
|
|
|
12892
12895
|
enableListenChanges: true,
|
|
12893
12896
|
listenIntervalInSeconds: 60,
|
|
12894
12897
|
},
|
|
12898
|
+
feed: {
|
|
12899
|
+
jsonFeed: {
|
|
12900
|
+
fr: ['https://gitlab.ifremer.fr/sih-public/sumaris/ngx-sumaris-components/-/raw/master/doc/feed/feed-fr.json'],
|
|
12901
|
+
en: ['https://gitlab.ifremer.fr/sih-public/sumaris/ngx-sumaris-components/-/raw/master/doc/feed/feed-en.json'],
|
|
12902
|
+
},
|
|
12903
|
+
maxContentLength: 1000,
|
|
12904
|
+
maxAgeInMonths: -1,
|
|
12905
|
+
maxCount: 3,
|
|
12906
|
+
},
|
|
12895
12907
|
});
|
|
12896
12908
|
|
|
12897
12909
|
class EntityClasses {
|
|
@@ -13243,6 +13255,7 @@ class LocalSettingsService extends StartableService {
|
|
|
13243
13255
|
defaultSettings;
|
|
13244
13256
|
onChange = new Subject();
|
|
13245
13257
|
darkMode$ = merge(this.onChange, this.startSubject).pipe(distinctUntilChanged(), map((data) => data?.darkMode || false));
|
|
13258
|
+
locale$ = merge(this.onChange, this.startSubject).pipe(startWith(this.settings), distinctUntilChanged(), map((data) => data?.locale || this.environment?.defaultLocale));
|
|
13246
13259
|
_optionDefs;
|
|
13247
13260
|
_serializeAsString;
|
|
13248
13261
|
_$persist;
|
|
@@ -19765,6 +19778,17 @@ class NetworkService extends StartableObservableService {
|
|
|
19765
19778
|
console.debug(`[network] All cache cleared, in ${Date.now() - now}ms`);
|
|
19766
19779
|
});
|
|
19767
19780
|
}
|
|
19781
|
+
async fetch(url) {
|
|
19782
|
+
if (!url)
|
|
19783
|
+
throw new Error('Missing url');
|
|
19784
|
+
try {
|
|
19785
|
+
return firstValueFrom(this.http.get(url));
|
|
19786
|
+
}
|
|
19787
|
+
catch (err) {
|
|
19788
|
+
console.error(`[network] Error fetching data from ${url}:`, err);
|
|
19789
|
+
return null;
|
|
19790
|
+
}
|
|
19791
|
+
}
|
|
19768
19792
|
/* -- protected functions -- */
|
|
19769
19793
|
async ngOnStart(peer) {
|
|
19770
19794
|
console.info('[network] Starting network...');
|
|
@@ -33304,6 +33328,477 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
33304
33328
|
args: ['showList']
|
|
33305
33329
|
}] } });
|
|
33306
33330
|
|
|
33331
|
+
class JsonFeedUtils {
|
|
33332
|
+
static DEFAULT_MAX_POST_AGE_IN_MONTHS = 3;
|
|
33333
|
+
static VERSION_1 = 'https://jsonfeed.org/version/1';
|
|
33334
|
+
static VERSION_1_1 = 'https://jsonfeed.org/version/1.1';
|
|
33335
|
+
static ACCEPTED_VERSIONS = [this.VERSION_1, this.VERSION_1_1];
|
|
33336
|
+
static removeJsonExtension(url) {
|
|
33337
|
+
if (!url)
|
|
33338
|
+
return url;
|
|
33339
|
+
return url.replace(/\.json$/, '');
|
|
33340
|
+
}
|
|
33341
|
+
static isJsonFeed(json) {
|
|
33342
|
+
return (json && this.ACCEPTED_VERSIONS.includes(json.version) && isNotEmptyArray(json.items)) || isNotNilOrBlank(json.next_url);
|
|
33343
|
+
}
|
|
33344
|
+
static isJsonFeedItem(json) {
|
|
33345
|
+
return json && isNotEmptyArray(json.id) && (isNotNilOrBlank(json.content_text) || isNotNilOrBlank(json.content_html));
|
|
33346
|
+
}
|
|
33347
|
+
static truncateFeedItemsHtml(feed, opts) {
|
|
33348
|
+
if (!feed?.items)
|
|
33349
|
+
return feed;
|
|
33350
|
+
feed.items.forEach((item) => {
|
|
33351
|
+
// Truncate items html
|
|
33352
|
+
if (item.content_html) {
|
|
33353
|
+
const html = this.truncateHtml(item.content_html, opts);
|
|
33354
|
+
item.truncated = html.length !== item.content_html.length;
|
|
33355
|
+
item.content_html = html;
|
|
33356
|
+
}
|
|
33357
|
+
});
|
|
33358
|
+
return feed;
|
|
33359
|
+
}
|
|
33360
|
+
static truncateHtml(html, opts) {
|
|
33361
|
+
if (!html)
|
|
33362
|
+
return html;
|
|
33363
|
+
const maxContentLength = opts?.maxContentLength ?? 0;
|
|
33364
|
+
if (maxContentLength > 0 && html?.length > maxContentLength) {
|
|
33365
|
+
const endIndex = Math.max(html.lastIndexOf(' ', maxContentLength), html.lastIndexOf('<', maxContentLength));
|
|
33366
|
+
return html.substring(0, endIndex) + ' (...)';
|
|
33367
|
+
}
|
|
33368
|
+
return html;
|
|
33369
|
+
}
|
|
33370
|
+
/**
|
|
33371
|
+
* Migrate from old version 1.0 to 1.1
|
|
33372
|
+
* @param feed
|
|
33373
|
+
*/
|
|
33374
|
+
static migrateFeed(feed) {
|
|
33375
|
+
if (!feed)
|
|
33376
|
+
return feed;
|
|
33377
|
+
// Migrate from old version 1.0 to 1.1
|
|
33378
|
+
if (feed.version === JsonFeedUtils.VERSION_1) {
|
|
33379
|
+
// Migrate feed author
|
|
33380
|
+
if (feed.author && !feed.authors) {
|
|
33381
|
+
feed.authors = [feed.author];
|
|
33382
|
+
delete feed.author;
|
|
33383
|
+
}
|
|
33384
|
+
// Migrate items author
|
|
33385
|
+
feed.items?.forEach((item) => {
|
|
33386
|
+
if (item.author && !item.authors) {
|
|
33387
|
+
item.authors = [item.author];
|
|
33388
|
+
delete item.author;
|
|
33389
|
+
}
|
|
33390
|
+
});
|
|
33391
|
+
// Change version
|
|
33392
|
+
feed.version = JsonFeedUtils.VERSION_1_1;
|
|
33393
|
+
}
|
|
33394
|
+
return feed;
|
|
33395
|
+
}
|
|
33396
|
+
}
|
|
33397
|
+
|
|
33398
|
+
class DiscourseUtils {
|
|
33399
|
+
static _logPrefix = '[discourse-utils]';
|
|
33400
|
+
static removeJsonExtension = JsonFeedUtils.removeJsonExtension;
|
|
33401
|
+
static isDiscourseObject(json) {
|
|
33402
|
+
return this.isDiscourseTopic(json) || this.isDiscourseCategory(json);
|
|
33403
|
+
}
|
|
33404
|
+
static isDiscourseTopic(json) {
|
|
33405
|
+
return (json && !!json.post_stream?.posts && json.fancy_title && json.slug && json.details?.created_by && true) || false;
|
|
33406
|
+
}
|
|
33407
|
+
static isDiscourseCategory(json) {
|
|
33408
|
+
return (json && json.topic_list?.topics && json.posters && true) || false;
|
|
33409
|
+
}
|
|
33410
|
+
static getDiscourseBaseUrl(url) {
|
|
33411
|
+
return UrlUtils.getRootUrl(url);
|
|
33412
|
+
}
|
|
33413
|
+
static isDiscourseCategoryUrl(url) {
|
|
33414
|
+
const baseUrl = this.getDiscourseBaseUrl(url);
|
|
33415
|
+
return baseUrl && url.substring(baseUrl.length).match(/^\/c\//);
|
|
33416
|
+
}
|
|
33417
|
+
static extractCategoryIdFromUrl(url) {
|
|
33418
|
+
const match = this.removeJsonExtension(url).match(/\/(\d+)$/);
|
|
33419
|
+
return match ? parseInt(match[1], 10) : null;
|
|
33420
|
+
}
|
|
33421
|
+
static extractCategorySlugFromUrl(url) {
|
|
33422
|
+
const match = this.removeJsonExtension(url).match(/\/c\/([\w/-]+)\/\d+$/);
|
|
33423
|
+
return match ? match[1] : null;
|
|
33424
|
+
}
|
|
33425
|
+
static extractTopicIdFromUrl(url) {
|
|
33426
|
+
const match = url.match(/\/(\d+)(?:$|\/)/) || url.match(/\/(\d+)\.json$/);
|
|
33427
|
+
return match ? parseInt(match[1], 10) : null;
|
|
33428
|
+
}
|
|
33429
|
+
static getLanguageFromTitle(title) {
|
|
33430
|
+
if (!title)
|
|
33431
|
+
return '';
|
|
33432
|
+
const match = title.match('\\s\\(([a-z]{2}(?:-[A-Z]{2})?)\\)$');
|
|
33433
|
+
return match && match[1];
|
|
33434
|
+
}
|
|
33435
|
+
static async getFeedsByUrl(url, opts) {
|
|
33436
|
+
try {
|
|
33437
|
+
// Check a category
|
|
33438
|
+
if (DiscourseUtils.isDiscourseCategoryUrl(url)) {
|
|
33439
|
+
console.debug(`${this._logPrefix}Detected a Discourse category at: ${url}`);
|
|
33440
|
+
return this.getFeedsByCategoryUrl(url, opts);
|
|
33441
|
+
}
|
|
33442
|
+
// Or a topic
|
|
33443
|
+
else {
|
|
33444
|
+
const post = await this.getFeedByTopicUrl(url, opts);
|
|
33445
|
+
return post ? [post] : [];
|
|
33446
|
+
}
|
|
33447
|
+
}
|
|
33448
|
+
catch (err) {
|
|
33449
|
+
console.error(`${this._logPrefix}`, err);
|
|
33450
|
+
return [];
|
|
33451
|
+
}
|
|
33452
|
+
}
|
|
33453
|
+
static async getFeedsByCategoryUrl(url, opts) {
|
|
33454
|
+
const categoryId = this.extractCategoryIdFromUrl(url);
|
|
33455
|
+
const categorySlug = this.extractCategorySlugFromUrl(url);
|
|
33456
|
+
const baseUrl = this.getDiscourseBaseUrl(url);
|
|
33457
|
+
const api = opts?.api ?? new DiscourseAPI(baseUrl);
|
|
33458
|
+
const category = await api.listCategoryTopics({ id: categoryId, slug: categorySlug });
|
|
33459
|
+
const topics = (category?.topic_list?.topics || []).filter((topic) => {
|
|
33460
|
+
const topicLocale = this.getLanguageFromTitle(topic.fancy_title);
|
|
33461
|
+
return (topic.pinned &&
|
|
33462
|
+
topic.visible &&
|
|
33463
|
+
// Keep same locale, if exists
|
|
33464
|
+
(!topicLocale || !opts?.locale || topicLocale === opts.locale));
|
|
33465
|
+
});
|
|
33466
|
+
const posts = (await Promise$1.all(topics.map(async (t) => {
|
|
33467
|
+
const topic = await api.getTopic({ id: t.id.toString() });
|
|
33468
|
+
// Ignore 'About' topics
|
|
33469
|
+
if (topic.tags.includes('about-category'))
|
|
33470
|
+
return undefined;
|
|
33471
|
+
const posts = this.filterRecentPosts(topic.post_stream.posts, opts);
|
|
33472
|
+
if (isEmptyArray(posts))
|
|
33473
|
+
return undefined;
|
|
33474
|
+
return this.toFeed(posts, topic, url, opts);
|
|
33475
|
+
}))).filter(isNotNil);
|
|
33476
|
+
if (isEmptyArray(posts)) {
|
|
33477
|
+
console.warn(`${this._logPrefix}No recent post found on category #${categoryId}`);
|
|
33478
|
+
}
|
|
33479
|
+
return posts;
|
|
33480
|
+
}
|
|
33481
|
+
static async getFeedByTopicUrl(url, opts) {
|
|
33482
|
+
const topicId = this.extractTopicIdFromUrl(url);
|
|
33483
|
+
if (!topicId) {
|
|
33484
|
+
console.error(`${this._logPrefix}Invalid URL`);
|
|
33485
|
+
return undefined;
|
|
33486
|
+
}
|
|
33487
|
+
const api = opts?.api ?? new DiscourseAPI(this.getDiscourseBaseUrl(url));
|
|
33488
|
+
const topic = await api.getTopic({ id: topicId.toString() });
|
|
33489
|
+
// Ignore about category
|
|
33490
|
+
if (topic.tags?.includes('about-category'))
|
|
33491
|
+
return undefined;
|
|
33492
|
+
const posts = this.filterRecentPosts(topic.post_stream.posts, opts);
|
|
33493
|
+
if (isEmptyArray(posts)) {
|
|
33494
|
+
console.warn(`${this._logPrefix}No recent posts found in topic #${topicId}`);
|
|
33495
|
+
return undefined;
|
|
33496
|
+
}
|
|
33497
|
+
return this.toFeed(posts, topic, url, opts);
|
|
33498
|
+
}
|
|
33499
|
+
static filterRecentPosts(posts, opts) {
|
|
33500
|
+
const maxAgeInMonths = opts?.maxAgeInMonths ?? JsonFeedUtils.DEFAULT_MAX_POST_AGE_IN_MONTHS;
|
|
33501
|
+
if (maxAgeInMonths <= 0)
|
|
33502
|
+
return posts; // Accept all
|
|
33503
|
+
const minDate = maxAgeInMonths > 0 ? DateUtils.moment().subtract(maxAgeInMonths, 'month').startOf('day').utc() : undefined;
|
|
33504
|
+
return (posts || []).filter((post) => post && DateUtils.moment(post.created_at).isSameOrAfter(minDate));
|
|
33505
|
+
}
|
|
33506
|
+
static toFeed(posts, topic, url, opts) {
|
|
33507
|
+
const baseUrl = this.getDiscourseBaseUrl(url);
|
|
33508
|
+
const title = this.cleanTitle(topic.fancy_title ?? topic.title);
|
|
33509
|
+
const items = (posts || []).map((post) => this.toFeedItem(post, topic, url, opts));
|
|
33510
|
+
const firstItem = items?.[0];
|
|
33511
|
+
if (firstItem)
|
|
33512
|
+
firstItem.title = title;
|
|
33513
|
+
const authorName = topic?.details?.created_by && (topic.details.created_by.name || topic.details.created_by.username);
|
|
33514
|
+
const authorAvatar = baseUrl + topic.details.created_by.avatar_template.replace('{size}', '48');
|
|
33515
|
+
return {
|
|
33516
|
+
version: JsonFeedUtils.VERSION_1_1,
|
|
33517
|
+
feed_url: url,
|
|
33518
|
+
title: this.cleanTitle(topic.fancy_title),
|
|
33519
|
+
expired: topic.archived,
|
|
33520
|
+
authors: [
|
|
33521
|
+
{
|
|
33522
|
+
name: authorName,
|
|
33523
|
+
avatar: authorAvatar,
|
|
33524
|
+
},
|
|
33525
|
+
],
|
|
33526
|
+
items,
|
|
33527
|
+
tag_template: baseUrl + '/tag/{tag}',
|
|
33528
|
+
};
|
|
33529
|
+
}
|
|
33530
|
+
static toFeedItem(post, topic, url, opts) {
|
|
33531
|
+
const baseUrl = this.getDiscourseBaseUrl(url);
|
|
33532
|
+
url = post.post_url ? baseUrl + post.post_url : url;
|
|
33533
|
+
const id = post.post_url ? baseUrl + post.post_url : post.id;
|
|
33534
|
+
const html = JsonFeedUtils.truncateHtml(post.cooked, opts);
|
|
33535
|
+
const truncated = html?.length !== post.cooked?.length;
|
|
33536
|
+
const authorName = post.name || post.username;
|
|
33537
|
+
const authorAvatar = baseUrl + (post.avatar_template.replace('{size}', '48') ?? topic.details.last_poster.avatar_template.replace('{size}', '48'));
|
|
33538
|
+
return {
|
|
33539
|
+
id,
|
|
33540
|
+
url,
|
|
33541
|
+
authors: [
|
|
33542
|
+
{
|
|
33543
|
+
name: authorName,
|
|
33544
|
+
avatar: authorAvatar,
|
|
33545
|
+
},
|
|
33546
|
+
],
|
|
33547
|
+
content_html: html,
|
|
33548
|
+
date_published: post.created_at,
|
|
33549
|
+
tags: topic.tags,
|
|
33550
|
+
truncated,
|
|
33551
|
+
};
|
|
33552
|
+
}
|
|
33553
|
+
static cleanTitle(title) {
|
|
33554
|
+
if (!title)
|
|
33555
|
+
return undefined;
|
|
33556
|
+
return title.replace(/\s\([a-z]{2}(:?-[A-Z]{2})?\)$/, '');
|
|
33557
|
+
}
|
|
33558
|
+
}
|
|
33559
|
+
|
|
33560
|
+
const APP_FEED_SERVICE = new InjectionToken('FeeService');
|
|
33561
|
+
|
|
33562
|
+
class FeedDirective {
|
|
33563
|
+
element;
|
|
33564
|
+
_subscription = new Subscription();
|
|
33565
|
+
platform = inject(PlatformService);
|
|
33566
|
+
locationStrategy = inject(LocationStrategy);
|
|
33567
|
+
navController = inject(NavController);
|
|
33568
|
+
router = inject(Router);
|
|
33569
|
+
route = inject(ActivatedRoute);
|
|
33570
|
+
_feedUrl;
|
|
33571
|
+
_baseUrl;
|
|
33572
|
+
set feedUrl(value) {
|
|
33573
|
+
this._feedUrl = value;
|
|
33574
|
+
this._baseUrl = value && DiscourseUtils.removeJsonExtension(value);
|
|
33575
|
+
}
|
|
33576
|
+
get feedUrl() {
|
|
33577
|
+
return this._feedUrl;
|
|
33578
|
+
}
|
|
33579
|
+
constructor(element) {
|
|
33580
|
+
this.element = element;
|
|
33581
|
+
// DEBUG
|
|
33582
|
+
//console.debug('[feed-directive] Creating feed directive');
|
|
33583
|
+
}
|
|
33584
|
+
ngAfterViewInit() {
|
|
33585
|
+
console.debug('[feed-directive] Processing anchors');
|
|
33586
|
+
// Listening click events
|
|
33587
|
+
this._subscription.add(this.listenClickEvents(this.element.nativeElement));
|
|
33588
|
+
}
|
|
33589
|
+
ngOnDestroy() {
|
|
33590
|
+
this._subscription.unsubscribe();
|
|
33591
|
+
}
|
|
33592
|
+
listenClickEvents(nativeElement) {
|
|
33593
|
+
console.debug('[feed-directive] Start listening click events...');
|
|
33594
|
+
const subscription = new Subscription();
|
|
33595
|
+
const listener = (event) => this.click(event, nativeElement);
|
|
33596
|
+
const links = nativeElement.querySelectorAll('a');
|
|
33597
|
+
links.forEach((link) => {
|
|
33598
|
+
// DEBUG
|
|
33599
|
+
//console.debug('[feed-directive] Adding click listener to', link);
|
|
33600
|
+
link.addEventListener('click', listener);
|
|
33601
|
+
subscription.add(() => link.removeEventListener('click', listener));
|
|
33602
|
+
});
|
|
33603
|
+
// DEBUG
|
|
33604
|
+
subscription.add(() => console.debug('[feed-directive] Stop listening click events'));
|
|
33605
|
+
return subscription;
|
|
33606
|
+
}
|
|
33607
|
+
async click(event, containerElement) {
|
|
33608
|
+
console.debug('[feed-directive] Processing click event...', event);
|
|
33609
|
+
if (!(event.target instanceof HTMLAnchorElement) && !(event.target?.['tagName'] === 'A')) {
|
|
33610
|
+
console.warn('[feed-directive] Invalid click event target. Should an anchor <a>', event.target);
|
|
33611
|
+
return;
|
|
33612
|
+
}
|
|
33613
|
+
const element = event.target;
|
|
33614
|
+
let href = element.getAttribute('href');
|
|
33615
|
+
const fragment = UrlUtils.getFragment(href);
|
|
33616
|
+
// Fragment
|
|
33617
|
+
if (href?.startsWith('#') && isNotNilOrBlank(fragment)) {
|
|
33618
|
+
event.preventDefault();
|
|
33619
|
+
this.scrollToAnchor(containerElement, fragment);
|
|
33620
|
+
return true;
|
|
33621
|
+
}
|
|
33622
|
+
const routerLink = element.getAttribute('routerLink');
|
|
33623
|
+
if (isNotNilOrBlank(routerLink)) {
|
|
33624
|
+
event.preventDefault();
|
|
33625
|
+
return this.navController.navigateForward(routerLink);
|
|
33626
|
+
}
|
|
33627
|
+
// Resolve relative URL, if baseUrl has been set
|
|
33628
|
+
if (this._baseUrl && UrlUtils.isRelativeUrl(href)) {
|
|
33629
|
+
// Resolve URL, then open using the platform
|
|
33630
|
+
href = UrlUtils.resolveRelativeUrl(this._baseUrl, href);
|
|
33631
|
+
}
|
|
33632
|
+
// Resolve internal URL as an app route
|
|
33633
|
+
if (UrlUtils.isInternalUrl(href)) {
|
|
33634
|
+
const routePath = this.normalizeUrl(href.startsWith('/') ? href : `/${href}`);
|
|
33635
|
+
const urlTree = this.getUrlTree(routePath);
|
|
33636
|
+
event.preventDefault();
|
|
33637
|
+
this.router.navigated = false;
|
|
33638
|
+
// Opening route
|
|
33639
|
+
return this.navController.navigateForward(urlTree);
|
|
33640
|
+
}
|
|
33641
|
+
// External URL: open using the platform
|
|
33642
|
+
event.preventDefault();
|
|
33643
|
+
await this.platform.open(href);
|
|
33644
|
+
return true;
|
|
33645
|
+
}
|
|
33646
|
+
scrollToAnchor(containerElement, anchorId) {
|
|
33647
|
+
if (!containerElement || !anchorId) {
|
|
33648
|
+
console.warn("Missing 'containerElement' or 'fragment' argument.");
|
|
33649
|
+
return;
|
|
33650
|
+
}
|
|
33651
|
+
console.debug('[feed-directive-anchor] Scrolling to anchor #' + anchorId);
|
|
33652
|
+
// Find the target element with the specified fragment ID
|
|
33653
|
+
const targetElement = containerElement.querySelector(`#${anchorId}`);
|
|
33654
|
+
if (targetElement) {
|
|
33655
|
+
// Scroll smoothly to the target element
|
|
33656
|
+
// eslint-disable-next-line @rx-angular/prefer-no-layout-sensitive-apis
|
|
33657
|
+
targetElement.scrollIntoView({
|
|
33658
|
+
behavior: 'smooth', // Smooth scrolling behavior
|
|
33659
|
+
block: 'start', // Align the target element to the top of the container
|
|
33660
|
+
});
|
|
33661
|
+
}
|
|
33662
|
+
else {
|
|
33663
|
+
console.warn(`No element found with the ID "${anchorId}" inside the container.`);
|
|
33664
|
+
}
|
|
33665
|
+
}
|
|
33666
|
+
/**
|
|
33667
|
+
* Transform a relative URL to its absolute representation according to current router state.
|
|
33668
|
+
* @param url Relative URL path.
|
|
33669
|
+
* @return Absolute URL based on the current route.
|
|
33670
|
+
*/
|
|
33671
|
+
normalizeUrl(url) {
|
|
33672
|
+
if (UrlUtils.isExternalUrl(url)) {
|
|
33673
|
+
return url;
|
|
33674
|
+
}
|
|
33675
|
+
if (this._baseUrl && UrlUtils.isRelativeUrl(url)) {
|
|
33676
|
+
return UrlUtils.resolveRelativeUrl(this._baseUrl, url);
|
|
33677
|
+
}
|
|
33678
|
+
const urlTree = this.getUrlTree(url);
|
|
33679
|
+
const serializedUrl = this.router.serializeUrl(urlTree);
|
|
33680
|
+
return this.locationStrategy.prepareExternalUrl(serializedUrl);
|
|
33681
|
+
}
|
|
33682
|
+
getUrlTree(url) {
|
|
33683
|
+
url = UrlUtils.normalizeUrl(url);
|
|
33684
|
+
const urlPath = UrlUtils.stripFragmentAndQuery(url) || UrlUtils.stripFragmentAndQuery(this.router.url);
|
|
33685
|
+
const parsedUrl = this.router.parseUrl(url);
|
|
33686
|
+
const fragment = parsedUrl.fragment;
|
|
33687
|
+
const queryParams = parsedUrl.queryParams;
|
|
33688
|
+
return this.router.createUrlTree([urlPath], { relativeTo: this.route, fragment, queryParams });
|
|
33689
|
+
}
|
|
33690
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
|
|
33691
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: FeedDirective, selector: "feed,[feed]", inputs: { feedUrl: ["feed", "feedUrl"] }, ngImport: i0 });
|
|
33692
|
+
}
|
|
33693
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedDirective, decorators: [{
|
|
33694
|
+
type: Directive,
|
|
33695
|
+
args: [{
|
|
33696
|
+
selector: 'feed,[feed]',
|
|
33697
|
+
}]
|
|
33698
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { feedUrl: [{
|
|
33699
|
+
type: Input,
|
|
33700
|
+
args: ['feed']
|
|
33701
|
+
}] } });
|
|
33702
|
+
|
|
33703
|
+
class FeedsComponent {
|
|
33704
|
+
environment;
|
|
33705
|
+
feedService;
|
|
33706
|
+
_debug = false;
|
|
33707
|
+
_state = new RxState();
|
|
33708
|
+
translate = inject(TranslateService);
|
|
33709
|
+
platform = inject(PlatformService);
|
|
33710
|
+
router = inject(Router);
|
|
33711
|
+
version;
|
|
33712
|
+
feeds$;
|
|
33713
|
+
hasFeeds$;
|
|
33714
|
+
feeds;
|
|
33715
|
+
hasFeeds;
|
|
33716
|
+
showHeader = true;
|
|
33717
|
+
headerTextColor = 'light';
|
|
33718
|
+
constructor(environment, feedService) {
|
|
33719
|
+
this.environment = environment;
|
|
33720
|
+
this.feedService = feedService;
|
|
33721
|
+
this._state.connect('hasFeeds', this.feeds$.pipe(map(isNotEmptyArray)));
|
|
33722
|
+
if (this.feedService) {
|
|
33723
|
+
this._state.connect('feeds', this._state.select(['maxAgeInMonths', 'maxContentLength']).pipe(switchMap(({ maxAgeInMonths, maxContentLength }) => this.feedService.watchAll({
|
|
33724
|
+
maxAgeInMonths,
|
|
33725
|
+
maxContentLength,
|
|
33726
|
+
})), map(({ data }) => data)));
|
|
33727
|
+
}
|
|
33728
|
+
// Initial state
|
|
33729
|
+
this._state.set({
|
|
33730
|
+
maxAgeInMonths: this.environment.feed?.maxAgeInMonths ?? -1 /* all */,
|
|
33731
|
+
maxContentLength: this.environment.feed?.maxContentLength ?? -1 /* no max */,
|
|
33732
|
+
});
|
|
33733
|
+
// DEBUG
|
|
33734
|
+
this._debug = !environment.production;
|
|
33735
|
+
}
|
|
33736
|
+
openFeedHome(feed) {
|
|
33737
|
+
if (!feed) {
|
|
33738
|
+
this.feeds?.forEach((f) => this.openFeedHome(f));
|
|
33739
|
+
return;
|
|
33740
|
+
}
|
|
33741
|
+
const url = feed.home_page_url ?? DiscourseUtils.removeJsonExtension(feed.feed_url) ?? this.feedService.getHomeUrl();
|
|
33742
|
+
if (!url)
|
|
33743
|
+
return;
|
|
33744
|
+
return this.platform.open(url);
|
|
33745
|
+
}
|
|
33746
|
+
openUrl(url) {
|
|
33747
|
+
if (!url)
|
|
33748
|
+
return;
|
|
33749
|
+
const fixedUrl = DiscourseUtils.removeJsonExtension(url);
|
|
33750
|
+
return this.platform.open(fixedUrl);
|
|
33751
|
+
}
|
|
33752
|
+
openTag(feed, tag) {
|
|
33753
|
+
const url = this.feedService.getTagUrl(feed, tag);
|
|
33754
|
+
return this.openUrl(url);
|
|
33755
|
+
}
|
|
33756
|
+
getTags(feedOrItem) {
|
|
33757
|
+
if (JsonFeedUtils.isJsonFeed(feedOrItem)) {
|
|
33758
|
+
return arrayDistinct((feedOrItem.items || [])
|
|
33759
|
+
.map((item) => item.tags)
|
|
33760
|
+
.filter(isNotEmptyArray)
|
|
33761
|
+
.flat());
|
|
33762
|
+
}
|
|
33763
|
+
if (JsonFeedUtils.isJsonFeedItem(feedOrItem)) {
|
|
33764
|
+
return arrayDistinct((feedOrItem.tags || []).flat());
|
|
33765
|
+
}
|
|
33766
|
+
return [];
|
|
33767
|
+
}
|
|
33768
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedsComponent, deps: [{ token: ENVIRONMENT }, { token: APP_FEED_SERVICE, optional: true }], target: i0.ɵɵFactoryTarget.Component });
|
|
33769
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: FeedsComponent, selector: "app-feeds", inputs: { feeds: "feeds", showHeader: "showHeader", headerTextColor: "headerTextColor" }, providers: [RxState], ngImport: i0, template: "@if (feeds | isNotEmptyArray) {\n <div class=\"feed ion-padding-horizontal ion-no-padding ion-padding-top\">\n\n <!-- feeds -->\n @for (feed of feeds$ | push; track feed.feed_url) {\n\n <!-- top header -->\n @if (showHeader) {\n <ion-item lines=\"none\" color=\"secondary\">\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()\">\n <ion-label translate>SOCIAL.FEED.SHOW_ALL_FEED</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n </ion-item>\n }\n\n\n <!-- items -->\n @for (item of feed.items; track item.id) {\n <ion-card>\n <ion-card-header>\n <ion-card-subtitle style=\"vertical-align: middle\">\n\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 color=\"medium\">{{ 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)=\"openUrl(item.url || feed.feed_url)\" tappable>{{ item?.title }}</ion-card-title>\n\n <!-- tags -->\n @let tags = item | map: getTags;\n @if (tags | isNotEmptyArray) {\n <div 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>@if (!last) {\n \n }\n }\n </div>\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 [innerText]=\"item.content_text\"></p>\n }\n </ion-text>\n </ion-card-content>\n\n <ion-button (click)=\"openUrl(item.url)\" 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 </ion-card>\n }\n }\n </div>\n}\n", styles: [".feed ion-item{border-radius:12px 12px 0 0;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}.feed ion-item ion-icon[slot=start]{-webkit-margin-end:8px;margin-inline-end:8px}.feed .tags{display:inline}.feed .tags a{color:var(--ion-color-step-600, #666666)!important}.feed ion-card{--ion-card-background: rgba(var(--ion-background-color-rgb), .6);--ion-card-color: var(--ion-text-color);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin:0 0 16px}.feed ion-card ion-card-header ion-card-title{--color: var(--ion-text-color)}.feed ion-card ion-card-header ion-card-subtitle{--color: var(--ion-color-step-50)}.feed ion-card ion-card-header ion-chip{--background: transparent;--border-color: transparent;--border-width: 0}.feed ion-card ion-card-header ion-chip ion-avatar{border:1px solid var(--ion-color-primary)}.feed ion-card ion-card-header ion-chip ion-label{color:var(--color)}.feed ion-button{text-transform:unset;color:var(--ion-color-step-400)}.feed a:hover{color:var(--ion-color-medium)}\n"], dependencies: [{ kind: "component", type: i2$1.IonAvatar, selector: "ion-avatar" }, { kind: "component", type: i2$1.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$1.IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$1.IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: i2$1.IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: i2$1.IonCardSubtitle, selector: "ion-card-subtitle", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonChip, selector: "ion-chip", inputs: ["color", "disabled", "mode", "outline"] }, { kind: "component", type: i2$1.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$1.IonImg, selector: "ion-img", inputs: ["alt", "src"] }, { kind: "component", type: i2$1.IonItem, selector: "ion-item", inputs: ["button", "color", "counter", "counterFormatter", "detail", "detailIcon", "disabled", "download", "fill", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "shape", "target", "type"] }, { kind: "component", type: i2$1.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$1.IonNote, selector: "ion-note", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "directive", type: i1$1.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "directive", type: FeedDirective, selector: "feed,[feed]", inputs: ["feed"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }, { kind: "pipe", type: i19.RxPush, name: "push" }, { kind: "pipe", type: DateFromNowPipe, name: "dateFromNow" }, { kind: "pipe", type: NotEmptyArrayPipe, name: "isNotEmptyArray" }, { kind: "pipe", type: MapPipe, name: "map" }] });
|
|
33770
|
+
}
|
|
33771
|
+
__decorate([
|
|
33772
|
+
RxStateSelect()
|
|
33773
|
+
], FeedsComponent.prototype, "feeds$", void 0);
|
|
33774
|
+
__decorate([
|
|
33775
|
+
RxStateSelect()
|
|
33776
|
+
], FeedsComponent.prototype, "hasFeeds$", void 0);
|
|
33777
|
+
__decorate([
|
|
33778
|
+
RxStateProperty()
|
|
33779
|
+
], FeedsComponent.prototype, "feeds", void 0);
|
|
33780
|
+
__decorate([
|
|
33781
|
+
RxStateProperty()
|
|
33782
|
+
], FeedsComponent.prototype, "hasFeeds", void 0);
|
|
33783
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedsComponent, decorators: [{
|
|
33784
|
+
type: Component,
|
|
33785
|
+
args: [{ selector: 'app-feeds', providers: [RxState], template: "@if (feeds | isNotEmptyArray) {\n <div class=\"feed ion-padding-horizontal ion-no-padding ion-padding-top\">\n\n <!-- feeds -->\n @for (feed of feeds$ | push; track feed.feed_url) {\n\n <!-- top header -->\n @if (showHeader) {\n <ion-item lines=\"none\" color=\"secondary\">\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()\">\n <ion-label translate>SOCIAL.FEED.SHOW_ALL_FEED</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n </ion-item>\n }\n\n\n <!-- items -->\n @for (item of feed.items; track item.id) {\n <ion-card>\n <ion-card-header>\n <ion-card-subtitle style=\"vertical-align: middle\">\n\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 color=\"medium\">{{ 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)=\"openUrl(item.url || feed.feed_url)\" tappable>{{ item?.title }}</ion-card-title>\n\n <!-- tags -->\n @let tags = item | map: getTags;\n @if (tags | isNotEmptyArray) {\n <div 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>@if (!last) {\n \n }\n }\n </div>\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 [innerText]=\"item.content_text\"></p>\n }\n </ion-text>\n </ion-card-content>\n\n <ion-button (click)=\"openUrl(item.url)\" 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 </ion-card>\n }\n }\n </div>\n}\n", styles: [".feed ion-item{border-radius:12px 12px 0 0;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}.feed ion-item ion-icon[slot=start]{-webkit-margin-end:8px;margin-inline-end:8px}.feed .tags{display:inline}.feed .tags a{color:var(--ion-color-step-600, #666666)!important}.feed ion-card{--ion-card-background: rgba(var(--ion-background-color-rgb), .6);--ion-card-color: var(--ion-text-color);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin:0 0 16px}.feed ion-card ion-card-header ion-card-title{--color: var(--ion-text-color)}.feed ion-card ion-card-header ion-card-subtitle{--color: var(--ion-color-step-50)}.feed ion-card ion-card-header ion-chip{--background: transparent;--border-color: transparent;--border-width: 0}.feed ion-card ion-card-header ion-chip ion-avatar{border:1px solid var(--ion-color-primary)}.feed ion-card ion-card-header ion-chip ion-label{color:var(--color)}.feed ion-button{text-transform:unset;color:var(--ion-color-step-400)}.feed a:hover{color:var(--ion-color-medium)}\n"] }]
|
|
33786
|
+
}], ctorParameters: () => [{ type: Environment, decorators: [{
|
|
33787
|
+
type: Inject,
|
|
33788
|
+
args: [ENVIRONMENT]
|
|
33789
|
+
}] }, { type: undefined, decorators: [{
|
|
33790
|
+
type: Optional
|
|
33791
|
+
}, {
|
|
33792
|
+
type: Inject,
|
|
33793
|
+
args: [APP_FEED_SERVICE]
|
|
33794
|
+
}] }], propDecorators: { feeds$: [], hasFeeds$: [], feeds: [{
|
|
33795
|
+
type: Input
|
|
33796
|
+
}], hasFeeds: [], showHeader: [{
|
|
33797
|
+
type: Input
|
|
33798
|
+
}], headerTextColor: [{
|
|
33799
|
+
type: Input
|
|
33800
|
+
}] } });
|
|
33801
|
+
|
|
33307
33802
|
/**
|
|
33308
33803
|
* @param files
|
|
33309
33804
|
* @deprecated use `getRandomImageWithCredit()` instead
|
|
@@ -33392,8 +33887,8 @@ class HomePage extends RxState {
|
|
|
33392
33887
|
get darkMode() {
|
|
33393
33888
|
return this.get('darkMode');
|
|
33394
33889
|
}
|
|
33395
|
-
get
|
|
33396
|
-
return this.
|
|
33890
|
+
get currentLocale() {
|
|
33891
|
+
return this.translate.currentLang || this.translate.defaultLang || this.environment.defaultLocale;
|
|
33397
33892
|
}
|
|
33398
33893
|
get partnerBannerSize() {
|
|
33399
33894
|
return this.showLegalInformation ? 9 : 12;
|
|
@@ -33641,11 +34136,11 @@ class HomePage extends RxState {
|
|
|
33641
34136
|
this.cd.markForCheck();
|
|
33642
34137
|
}
|
|
33643
34138
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HomePage, deps: [{ token: AccountService }, { token: i2$1.ModalController }, { token: i1$1.TranslateService }, { token: i2$1.ToastController }, { token: ConfigService }, { token: PlatformService }, { token: i0.ChangeDetectorRef }, { token: NetworkService }, { token: LocalSettingsService }, { token: ENVIRONMENT }, { token: APP_LOCALES }, { token: APP_HOME_BUTTONS, optional: true }, { token: APP_HOME_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Component });
|
|
33644
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: HomePage, selector: "app-page-home", usesInheritance: true, ngImport: i0, template: "<app-toolbar [canGoBack]=\"false\" visible-xs visible-sm visible-mobile>\n <ion-buttons slot=\"end\">\n @if (showNotificationIcon) {\n <app-user-event-notification-icon [autoHide]=\"autoHideNotificationIcon\"></app-user-event-notification-icon>\n }\n\n <!-- dark mode (if allow by the environment token) -->\n @if (settings.allowDarkMode) {\n <ion-button (click)=\"toggleDarkMode()\">\n <ion-icon slot=\"icon-only\" [name]=\"(darkMode$ | push) ? 'sunny' : 'moon'\"></ion-icon>\n </ion-button>\n }\n\n <!-- Change locale button -->\n <ion-button [matMenuTriggerFor]=\"localeMenu\">\n <ion-icon slot=\"start\" name=\"language\"></ion-icon>\n </ion-button>\n </ion-buttons>\n</app-toolbar>\n\n<!-- Change locale menu -->\n<mat-menu #localeMenu=\"matMenu\">\n <ng-template matMenuContent>\n @for (item of locales; track item.key) {\n <button mat-menu-item (click)=\"changeLanguage(item.key)\">\n <ion-label>{{ item.value }}</ion-label>\n </button>\n }\n </ng-template>\n</mat-menu>\n\n<ion-content [ngStyle]=\"contentStyle\" no-padding-xs>\n @if (contentCredits | isNotNilOrBlank) {\n <span class=\"content-credits\">\n <ion-text>{{ contentCredits }}</ion-text>\n </span>\n }\n\n <!-- loading spinner -->\n <div class=\"loading-page\" [class.cdk-visually-hidden]=\"!(loading$ | push)\">\n @if (showSpinner) {\n <div class=\"spinner\">\n <p [innerHTML]=\"'COMMON.LOADING_DOTS' | translate\"></p>\n <div class=\"sk-cube1 sk-cube\"></div>\n <div class=\"sk-cube2 sk-cube\"></div>\n <div class=\"sk-cube4 sk-cube\"></div>\n <div class=\"sk-cube3 sk-cube\"></div>\n </div>\n }\n </div>\n\n <!-- Desktop: translucent top toolbar -->\n @if (!mobile) {\n <div class=\"hidden-xs hidden-sm\">\n <ion-toolbar color=\"traansparent\" translucent>\n <ion-buttons slot=\"start\">\n <ion-menu-toggle>\n <ion-button color=\"light\" fill=\"clear\">\n <ion-icon slot=\"icon-only\" name=\"menu\"></ion-icon>\n </ion-button>\n </ion-menu-toggle>\n </ion-buttons>\n\n <ion-buttons slot=\"end\">\n @if (showNotificationIcon) {\n <app-user-event-notification-icon\n [style]=\"'ion-button'\"\n color=\"secondary\"\n unreadColor=\"tertiary\"\n fill=\"solid\"\n [autoHide]=\"autoHideNotificationIcon\"\n ></app-user-event-notification-icon>\n }\n\n <!-- dark mode (if allow by the environment token) -->\n @if (settings.allowDarkMode) {\n <ion-button\n color=\"secondary\"\n fill=\"solid\"\n (click)=\"toggleDarkMode()\"\n [title]=\"'HOME.BTN_DARK_MODE' | translate\"\n >\n <ion-icon slot=\"icon-only\" [name]=\"(darkMode$ | push) ? 'moon' : 'sunny'\"></ion-icon>\n </ion-button>\n }\n <!-- change locale button -->\n <ion-button\n color=\"secondary\"\n fill=\"solid\"\n [matMenuTriggerFor]=\"localeMenu\"\n [title]=\"'SETTINGS.LOCALE' | translate\"\n >\n <ion-icon slot=\"icon-only\" name=\"language\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </div>\n }\n\n <!-- Install (and upgrade) card -->\n <app-install-upgrade-card [isLogin]=\"isLogin\" [showInstallButton]=\"true\"></app-install-upgrade-card>\n\n @if (!(loading$ | push)) {\n <!-- Welcome card -->\n <ion-card class=\"main welcome ion-padding ion-text-center ion-align-self-center\" @fadeInAnimation>\n <ion-card-header>\n <ion-card-title class=\"ion-text-center\">\n <span *ngIf=\"isWeb\" [innerHTML]=\"'HOME.WELCOME_WEB' | translate: { appName: appName }\"></span>\n <span *ngIf=\"!isWeb\" [innerHTML]=\"'HOME.WELCOME_APP' | translate: { appName: appName }\"></span>\n </ion-card-title>\n <ion-card-subtitle [innerHTML]=\"description\"></ion-card-subtitle>\n </ion-card-header>\n <ion-card-content class=\"ion-no-padding\">\n <ion-text color=\"primary\">\n <img *ngIf=\"logo\" [attr.src]=\"logo\" alt=\"Logo\" />\n </ion-text>\n <!-- register help text -->\n <ion-text *ngIf=\"!isLogin && canRegister\">\n <br />\n <span translate>HOME.REGISTER_HELP</span>\n </ion-text>\n <div class=\"ion-padding-top\">\n <!-- If NOT login -->\n <ng-container *ngIf=\"!isLogin; else loginButtons\">\n <ion-button *ngIf=\"canRegister\" expand=\"block\" color=\"tertiary\" (click)=\"register()\">\n <span translate>HOME.BTN_REGISTER</span>\n </ion-button>\n <ion-button expand=\"block\" color=\"light\" [routerLink]=\"['/']\" (click)=\"login()\">\n <span translate>AUTH.BTN_LOGIN</span>\n </ion-button>\n </ng-container>\n\n <!-- If user login -->\n <ng-template #loginButtons>\n <!-- Feature buttons -->\n <ng-container *rxIf=\"$filteredButtons; let buttons\">\n <ng-container *ngFor=\"let item of buttons\">\n <ion-button\n *ngIf=\"item.path\"\n expand=\"block\"\n color=\"tertiary\"\n [class]=\"item.cssClass\"\n [routerLink]=\"item.path\"\n routerDirection=\"root\"\n >\n <ion-icon slot=\"start\" class=\"ion-float-start\" *ngIf=\"item.icon\" [name]=\"item.icon\"></ion-icon>\n <mat-icon slot=\"start\" class=\"ion-float-start\" *ngIf=\"item.matIcon\">{{ item.matIcon }}</mat-icon>\n <ion-text>{{ 'HOME.BTN_DATA_ENTRY' | translate: { name: (item.title | translate) } }}</ion-text>\n </ion-button>\n\n <!-- divider -->\n <div *ngIf=\"!item.path && !item.action\" [class]=\"item.cssClass\">\n <ion-label translate>{{ item.title }} </ion-label>\n </div>\n </ng-container>\n\n <!--<p *ngIf=\"buttons.length\" class=\"visible-mobile\"> </p>-->\n </ng-container>\n\n <ion-button *ngIf=\"showAccountButton\" expand=\"block\" color=\"secondary\" [routerLink]=\"['/account']\">\n <ion-icon slot=\"start\" class=\"ion-float-start\" name=\"person-circle\"></ion-icon>\n <ion-text translate>HOME.BTN_MY_ACCOUNT</ion-text>\n </ion-button>\n\n <p hidden-xs hidden-sm hidden-mobile>\n <ion-text\n [innerHTML]=\"'HOME.NOT_THIS_ACCOUNT_QUESTION' | translate: { displayName: accountName }\"\n ></ion-text>\n <br />\n <ion-text>\n <a href=\"#\" (click)=\"logout($event)\">\n <span translate>HOME.BTN_DISCONNECT</span>\n </a>\n </ion-text>\n </p>\n </ng-template>\n </div>\n </ion-card-content>\n </ion-card>\n\n <!-- Page history -->\n @let pageHistory = isLogin && (pageHistory$ | push);\n @if (pageHistory | isNotEmptyArray) {\n <ion-grid class=\"history-container ion-align-self-center\">\n <ion-row>\n @for (page of pageHistory; track page.path) {\n <ion-col size=\"12\" size-xl=\"\" class=\"ion-text-center\">\n <ion-card class=\"ion-align-self-start ion-text-start\" @fadeInAnimation>\n <ion-card-header class=\"ion-no-padding\">\n <!-- top bar -->\n <ion-card-subtitle>\n <button\n type=\"button\"\n tabindex=\"-1\"\n (click)=\"removePageHistory(page.path)\"\n mat-icon-button\n class=\"ion-float-start ion-no-margin close-button\"\n >\n <mat-icon>close</mat-icon>\n </button>\n <ion-label [innerHTML]=\"page.subtitle | translate\"></ion-label>\n <ion-text class=\"ion-float-end\" [title]=\"page.time | dateFormat: { time: true }\">\n <small>\n <ion-icon name=\"time-outline\"></ion-icon>\n {{ page.time | dateFromNow }}\n </small>\n \n </ion-text>\n </ion-card-subtitle>\n\n <!-- main page -->\n <ion-card-title class=\"ion-no-margin ion-no-padding\">\n <ion-item\n detail=\"true\"\n tappable\n class=\"text-1x\"\n [routerLink]=\"page.path\"\n routerDirection=\"root\"\n lines=\"none\"\n >\n <!-- page icon-->\n <ion-icon *ngIf=\"page.icon\" slot=\"start\" [name]=\"page.icon\"></ion-icon>\n <mat-icon *ngIf=\"page.matIcon\" slot=\"start\">{{ page.matIcon }}</mat-icon>\n\n <ion-label color=\"primary\" [innerHTML]=\"page.title\"></ion-label>\n </ion-item>\n </ion-card-title>\n </ion-card-header>\n\n <!-- children pages -->\n @if (page.children | isNotEmptyArray) {\n <ion-card-content class=\"ion-no-padding ion-padding-start\">\n <ion-list class=\"ion-no-padding\">\n @for (childPage of page.children; track childPage.path) {\n <ion-item\n detail=\"true\"\n tappable\n class=\"text-1x\"\n [routerLink]=\"childPage.path\"\n routerDirection=\"root\"\n >\n <!-- page icon-->\n <ion-icon *ngIf=\"childPage.icon\" slot=\"start\" color=\"dark\" [name]=\"childPage.icon\"></ion-icon>\n <mat-icon *ngIf=\"childPage.matIcon\" slot=\"start\" color=\"dark\">\n {{ childPage.matIcon }}\n </mat-icon>\n\n <ion-label color=\"dark\" [innerHTML]=\"childPage.title\"></ion-label>\n </ion-item>\n }\n </ion-list>\n </ion-card-content>\n }\n </ion-card>\n </ion-col>\n }\n </ion-row>\n </ion-grid>\n } @else if (showPartnerBanner || showLegalInformation) {\n <ng-container *ngTemplateOutlet=\"bottomBanner\"></ng-container>\n }\n }\n</ion-content>\n\n<ng-template #bottomBanner>\n <ion-grid class=\"bottom-banner ion-text-center\" [class.floating]=\"!mobile\" @fadeInAnimation>\n <ion-row>\n @if (showPartnerBanner) {\n <ion-col size=\"12\" [sizeLg]=\"partnerBannerSize\" class=\"ion-text-center\">\n @if (showLegalInformation) {\n <ion-list class=\"legal-info\">\n <ion-list-header>{{ 'HOME.PARTNERS' | translate }}</ion-list-header>\n </ion-list>\n }\n <!-- partners -->\n @for (item of $partners | async; track $index) {\n <a href=\"{{ item.siteUrl }}\">\n <img class=\"logo\" src=\"{{ item.logo }}\" alt=\"{{ item.label }}\" [title]=\"item.label\" />\n </a>\n }\n </ion-col>\n }\n @if (showLegalInformation) {\n <ion-col size=\"12\" size-sm=\"6\" size-lg=\"3\" [class.left-border]=\"showPartnerBanner\">\n <ion-list class=\"legal-info\">\n <ion-list-header class=\"ion-text-left\">{{ 'HOME.LEGAL_INFORMATION' | translate }}</ion-list-header>\n @for (item of legalInformation; track item.path) {\n <ion-item (click)=\"openMarkdownModal($event, item)\" lines=\"none\" [button]=\"true\">\n <ion-label>\n <p>\n {{ item.title | translate: item.titleArgs }}\n </p>\n </ion-label>\n </ion-item>\n }\n </ion-list>\n </ion-col>\n @if (showInstallLinks) {\n <ion-col size=\"12\" size-lg=\"3\" offset-lg=\"9\" [class.left-border]=\"showPartnerBanner\">\n <ion-list class=\"legal-info\">\n <ion-list-header>{{ 'HOME.INSTALL_APP' | translate }}</ion-list-header>\n <!-- TODO -->\n </ion-list>\n </ion-col>\n }\n }\n </ion-row>\n </ion-grid>\n</ng-template>\n", styles: [":host{--ion-card-background-color: rgba(var(--ion-background-color-rgb), .7)}h1,h2,h3,h4,h5{white-space:normal}.center,ion-content ion-card.welcome img{text-align:center;display:inline-block}ion-content{--background: transparent;background-size:cover;-webkit-background-size:cover;-moz-background-size:cover;-o-background-size:cover;background-color:var(--ion-background-color);display:inline-block;padding:15px}ion-content .loading-page{display:block;height:100%;width:100%;position:absolute;background-color:transparent;z-index:5;transition:background .5s linear;-webkit-transition:background 1.5s linear}ion-content .loading-page.hidden{background-color:rgba(var(--ion-color-primary),0)}ion-content ion-card{z-index:9}ion-content ion-card ion-card-header{display:block}ion-content ion-card ion-card-header ion-card-subtitle,ion-content ion-card ion-card-header ion-card-title{display:block;width:100%}ion-content ion-card.main{min-width:240px;max-width:400px;margin-left:auto;margin-right:auto}ion-content ion-card.welcome{background-color:var(--ion-card-background-color)}ion-content ion-card.welcome button{margin-top:16px}ion-content ion-card.welcome img{max-width:250px}ion-content .history-container{margin-left:auto;margin-right:auto}ion-content .history-container ion-card{display:inline-block;box-sizing:border-box;z-index:8;min-width:240px;max-width:400px;width:100%;margin-left:auto;margin-right:auto;background-color:var(--ion-card-background-color)}ion-content .history-container ion-card ion-card-header ion-card-subtitle{height:40px}ion-content .history-container ion-card ion-card-header ion-card-subtitle button[float-start]{--mdc-icon-button-state-layer-size: 40px;padding:8px;z-index:99;margin:0}ion-content .history-container ion-card ion-card-header ion-card-subtitle ion-label{line-height:40px}ion-content .history-container ion-card ion-card-header ion-card-subtitle ion-label[float-end]{padding-right:16px;font-weight:400}ion-content .history-container ion-card ion-card-header ion-card-title ion-item{width:100%;--ion-item-background: $transparent;--ion-item-icon-color: var(--ion-color-primary);--ion-item-text-color: var(--ion-color-primary)}ion-content .history-container ion-card ion-card-header ion-card-title ion-item ion-icon[slot=start],ion-content .history-container ion-card ion-card-header ion-card-title ion-item mat-icon[slot=start]{margin-right:unset;margin-inline-end:16px!important;color:var(--ion-item-icon-color)!important;fill:currentColor;stroke:currentColor}ion-content .history-container ion-card ion-card-header ion-card-title ion-item ion-text,ion-content .history-container ion-card ion-card-header ion-card-title ion-item ion-label{color:var(--ion-item-text-color)!important;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}ion-content .history-container ion-card ion-card-content ion-list{--ion-item-background: var(--ion-color-transparent)}ion-content .history-container ion-card ion-card-content ion-list ion-item{--ion-item-icon-color: var(--ion-color-dark);--ion-item-text-color: var(--ion-color-dark)}ion-content .history-container ion-card ion-card-content ion-list ion-item ion-icon[slot=start],ion-content .history-container ion-card ion-card-content ion-list ion-item mat-icon[slot=start]{color:var(--ion-item-icon-color)!important;fill:currentColor;stroke:currentColor;margin-right:unset;margin-inline-end:16px!important}ion-content .history-container ion-card ion-card-content ion-list ion-item ion-text,ion-content .history-container ion-card ion-card-content ion-list ion-item ion-label{color:var(--ion-item-text-color)!important}ion-content .bottom-banner{background-color:var(--ion-card-background-color);min-width:240px;z-index:0}ion-content .bottom-banner.floating{position:absolute;left:16px;right:16px;bottom:16px}ion-content .bottom-banner img.logo{max-height:50px;margin-left:8px}ion-content ion-text{text-align:center}@media screen and (max-width: 575px){ion-content ion-card{min-width:160px;width:calc(100% - 20px);max-width:100%;margin-left:10px;margin-right:10px}ion-content ion-card h1{margin-top:0;font-size:20px}ion-content ion-card ion-card-content ion-button{margin-top:8px}ion-content .bottom-banner.floating{display:block;position:unset}ion-content .bottom-banner img.logo{max-height:30px}}@media screen and (max-width: 767px) and (min-width: 576px){ion-content{min-height:555px}ion-content .bottom-banner img.logo{max-height:40px}}@media screen and (min-width: 768px){ion-content{min-height:calc(100% - 100px)}}.content-credits{position:absolute;right:0;bottom:0}.content-credits ion-text{padding-inline-start:4px;padding-inline-end:4px;font-size:.8rem;background-color:var(--ion-background-color, white);color:var(--ion-text-color);opacity:.65}@media screen and (min-width: 992px){.left-border{border-left:1px solid var(--ion-color-medium)}}.bottom-banner ion-list-header{min-height:30px}.bottom-banner ion-item{--min-height: 28px}.bottom-banner ion-item ion-label{margin-top:3px!important;margin-bottom:3px!important}\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: "directive", type: i3$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i3$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: i2$1.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$1.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$1.IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$1.IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: i2$1.IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: i2$1.IonCardSubtitle, selector: "ion-card-subtitle", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$1.IonContent, selector: "ion-content", inputs: ["color", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$1.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i2$1.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$1.IonItem, selector: "ion-item", inputs: ["button", "color", "counter", "counterFormatter", "detail", "detailIcon", "disabled", "download", "fill", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "shape", "target", "type"] }, { kind: "component", type: i2$1.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$1.IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: i2$1.IonListHeader, selector: "ion-list-header", inputs: ["color", "lines", "mode"] }, { kind: "component", type: i2$1.IonMenuToggle, selector: "ion-menu-toggle", inputs: ["autoHide", "menu"] }, { kind: "component", type: i2$1.IonRow, selector: "ion-row" }, { kind: "component", type: i2$1.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "directive", type: i2$1.RouterLinkDelegate, selector: ":not(a):not(area)[routerLink]" }, { kind: "directive", type: i1$1.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "directive", type: i8$2.RxIf, selector: "[rxIf]", inputs: ["rxIf", "rxIfStrategy", "rxIfElse", "rxIfThen", "rxIfSuspense", "rxIfComplete", "rxIfError", "rxIfContextTrigger", "rxIfNextTrigger", "rxIfSuspenseTrigger", "rxIfErrorTrigger", "rxIfCompleteTrigger", "rxIfParent", "rxIfPatchZone", "rxIfRenderCallback"] }, { kind: "component", type: i6$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i8$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i8$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i8$1.MatMenuContent, selector: "ng-template[matMenuContent]" }, { kind: "directive", type: i8$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: ToolbarComponent, selector: "app-toolbar", inputs: ["progressBarMode", "title", "color", "class", "id", "backHref", "defaultBackHref", "hasValidate", "hasClose", "hasSearch", "canGoBack", "canShowMenu"], outputs: ["onValidate", "onClose", "onValidateAndClose", "onBackClick", "onSearch"] }, { kind: "directive", type: i1$6.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: AppInstallUpgradeCard, selector: "app-install-upgrade-card", inputs: ["isLogin", "showUpgradeWarning", "showOfflineWarning", "showInstallButton", "debug"] }, { kind: "component", type: UserEventNotificationIcon, selector: "app-user-event-notification-icon", inputs: ["titleI18n", "disabled", "filter", "autoHide", "headerActions", "footerActions", "style", "color", "unreadColor", "fill", "mobile", "listStyle", "debug"], outputs: ["showList"] }, { kind: "pipe", type: i3$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }, { kind: "pipe", type: i19.RxPush, name: "push" }, { kind: "pipe", type: DateFormatPipe, name: "dateFormat" }, { kind: "pipe", type: DateFromNowPipe, name: "dateFromNow" }, { kind: "pipe", type: NotEmptyArrayPipe, name: "isNotEmptyArray" }, { kind: "pipe", type: IsNotNilOrBlankPipe, name: "isNotNilOrBlank" }], animations: [fadeInAnimation, slideUpDownAnimation], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
34139
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: HomePage, selector: "app-page-home", usesInheritance: true, ngImport: i0, template: "<app-toolbar [canGoBack]=\"false\" visible-xs visible-sm visible-mobile>\n <ion-buttons slot=\"end\">\n @if (showNotificationIcon) {\n <app-user-event-notification-icon [autoHide]=\"autoHideNotificationIcon\"></app-user-event-notification-icon>\n }\n\n <!-- dark mode (if allow by the environment token) -->\n @if (settings.allowDarkMode) {\n <ion-button (click)=\"toggleDarkMode()\">\n <ion-icon slot=\"icon-only\" [name]=\"(darkMode$ | push) ? 'sunny' : 'moon'\"></ion-icon>\n </ion-button>\n }\n\n <!-- Change locale button -->\n <ion-button [matMenuTriggerFor]=\"localeMenu\">\n <ion-icon slot=\"start\" name=\"language\"></ion-icon>\n </ion-button>\n </ion-buttons>\n</app-toolbar>\n\n<!-- Change locale menu -->\n<mat-menu #localeMenu=\"matMenu\">\n <ng-template matMenuContent>\n @for (item of locales; track item.key) {\n <button mat-menu-item (click)=\"changeLanguage(item.key)\">\n <!--<mat-icon>{{ currentLocale === item.key ? 'checkmark' : undefined }}</mat-icon>-->\n <ion-label>{{ item.value }}</ion-label>\n </button>\n }\n </ng-template>\n</mat-menu>\n\n<ion-content [ngStyle]=\"contentStyle\" no-padding-xs>\n\n @if (contentCredits | isNotNilOrBlank) {\n <span class=\"content-credits\">\n <ion-text>{{ contentCredits }}</ion-text>\n </span>\n }\n\n <!-- loading spinner -->\n <div class=\"loading-page\" [class.cdk-visually-hidden]=\"!(loading$ | push)\">\n @if (showSpinner) {\n <div class=\"spinner\">\n <p [innerHTML]=\"'COMMON.LOADING_DOTS' | translate\"></p>\n <div class=\"sk-cube1 sk-cube\"></div>\n <div class=\"sk-cube2 sk-cube\"></div>\n <div class=\"sk-cube4 sk-cube\"></div>\n <div class=\"sk-cube3 sk-cube\"></div>\n </div>\n }\n </div>\n\n <!-- Desktop: translucent top toolbar -->\n @if (!mobile) {\n <div class=\"hidden-xs hidden-sm\">\n <ion-toolbar color=\"traansparent\" translucent>\n <ion-buttons slot=\"start\">\n <ion-menu-toggle>\n <ion-button color=\"light\" fill=\"clear\">\n <ion-icon slot=\"icon-only\" name=\"menu\"></ion-icon>\n </ion-button>\n </ion-menu-toggle>\n </ion-buttons>\n\n <ion-buttons slot=\"end\">\n @if (showNotificationIcon) {\n <app-user-event-notification-icon\n [style]=\"'ion-button'\"\n color=\"secondary\"\n unreadColor=\"tertiary\"\n fill=\"solid\"\n [autoHide]=\"autoHideNotificationIcon\"\n ></app-user-event-notification-icon>\n }\n\n <!-- dark mode (if allow by the environment token) -->\n @if (settings.allowDarkMode) {\n <ion-button\n color=\"secondary\"\n fill=\"solid\"\n (click)=\"toggleDarkMode()\"\n [title]=\"'HOME.BTN_DARK_MODE' | translate\"\n >\n <ion-icon slot=\"icon-only\" [name]=\"(darkMode$ | push) ? 'moon' : 'sunny'\"></ion-icon>\n </ion-button>\n }\n <!-- change locale button -->\n <ion-button\n color=\"secondary\"\n fill=\"solid\"\n [matMenuTriggerFor]=\"localeMenu\"\n [title]=\"'SETTINGS.LOCALE' | translate\"\n >\n <ion-icon slot=\"icon-only\" name=\"language\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </div>\n }\n\n <!-- Install (and upgrade) card -->\n <app-install-upgrade-card [isLogin]=\"isLogin\" [showInstallButton]=\"true\"></app-install-upgrade-card>\n\n @if (!(loading$ | push)) {\n @let hasFeeds = feeds && (feeds.hasFeeds$ | async);\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col size=\"0\" [sizeXl]=\"hasFeeds ? 3 : 0\"></ion-col>\n <ion-col>\n <!-- Welcome card -->\n <ion-card class=\"main welcome ion-padding ion-text-center ion-align-self-center\" @fadeInAnimation>\n <ion-card-header>\n <ion-card-title class=\"ion-text-center\">\n <span *ngIf=\"isWeb\" [innerHTML]=\"'HOME.WELCOME_WEB' | translate: { appName: appName }\"></span>\n <span *ngIf=\"!isWeb\" [innerHTML]=\"'HOME.WELCOME_APP' | translate: { appName: appName }\"></span>\n </ion-card-title>\n <ion-card-subtitle [innerHTML]=\"description\"></ion-card-subtitle>\n </ion-card-header>\n <ion-card-content class=\"ion-no-padding\">\n <ion-text color=\"primary\">\n <img *ngIf=\"logo\" [attr.src]=\"logo\" alt=\"Logo\" />\n </ion-text>\n <!-- register help text -->\n <ion-text *ngIf=\"!isLogin && canRegister\">\n <br />\n <span translate>HOME.REGISTER_HELP</span>\n </ion-text>\n <div class=\"ion-padding-top\">\n <!-- If NOT login -->\n <ng-container *ngIf=\"!isLogin; else loginButtons\">\n <ion-button *ngIf=\"canRegister\" expand=\"block\" color=\"tertiary\" (click)=\"register()\">\n <span translate>HOME.BTN_REGISTER</span>\n </ion-button>\n <ion-button expand=\"block\" color=\"light\" [routerLink]=\"['/']\" (click)=\"login()\">\n <span translate>AUTH.BTN_LOGIN</span>\n </ion-button>\n </ng-container>\n\n <!-- If user login -->\n <ng-template #loginButtons>\n <!-- Feature buttons -->\n <ng-container *rxIf=\"$filteredButtons; let buttons\">\n <ng-container *ngFor=\"let item of buttons\">\n <ion-button\n *ngIf=\"item.path\"\n expand=\"block\"\n color=\"tertiary\"\n [class]=\"item.cssClass\"\n [routerLink]=\"item.path\"\n routerDirection=\"root\"\n >\n <ion-icon slot=\"start\" class=\"ion-float-start\" *ngIf=\"item.icon\" [name]=\"item.icon\"></ion-icon>\n <mat-icon slot=\"start\" class=\"ion-float-start\" *ngIf=\"item.matIcon\">{{ item.matIcon }}</mat-icon>\n <ion-text>{{ 'HOME.BTN_DATA_ENTRY' | translate: { name: (item.title | translate) } }}</ion-text>\n </ion-button>\n\n <!-- divider -->\n <div *ngIf=\"!item.path && !item.action\" [class]=\"item.cssClass\">\n <ion-label translate>{{ item.title }} </ion-label>\n </div>\n </ng-container>\n\n <!--<p *ngIf=\"buttons.length\" class=\"visible-mobile\"> </p>-->\n </ng-container>\n\n <ion-button *ngIf=\"showAccountButton\" expand=\"block\" color=\"secondary\" [routerLink]=\"['/account']\">\n <ion-icon slot=\"start\" class=\"ion-float-start\" name=\"person-circle\"></ion-icon>\n <ion-text translate>HOME.BTN_MY_ACCOUNT</ion-text>\n </ion-button>\n\n <p hidden-xs hidden-sm hidden-mobile>\n <ion-text\n [innerHTML]=\"'HOME.NOT_THIS_ACCOUNT_QUESTION' | translate: { displayName: accountName }\"\n ></ion-text>\n <br />\n <ion-text>\n <a href=\"#\" (click)=\"logout($event)\">\n <span translate>HOME.BTN_DISCONNECT</span>\n </a>\n </ion-text>\n </p>\n </ng-template>\n </div>\n </ion-card-content>\n </ion-card>\n </ion-col>\n <ion-col size=\"0\" [sizeXl]=\"hasFeeds ? 1 : 0\"></ion-col>\n <ion-col class=\"feed\" [size]=\"hasFeeds ? 12 : 0\" [sizeXl]=\"hasFeeds ? 4 : 0\">\n <app-feeds #feeds></app-feeds>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n\n\n <!-- Page history -->\n @let pageHistory = isLogin && (pageHistory$ | push);\n @if (pageHistory | isNotEmptyArray) {\n <ion-grid class=\"history-container ion-align-self-center\">\n <ion-row>\n @for (page of pageHistory; track page.path) {\n <ion-col size=\"12\" size-xl=\"\" class=\"ion-text-center\">\n <ion-card class=\"ion-align-self-start ion-text-start\" @fadeInAnimation>\n <ion-card-header class=\"ion-no-padding\">\n <!-- top bar -->\n <ion-card-subtitle>\n <button\n type=\"button\"\n tabindex=\"-1\"\n (click)=\"removePageHistory(page.path)\"\n mat-icon-button\n class=\"ion-float-start ion-no-margin close-button\"\n >\n <mat-icon>close</mat-icon>\n </button>\n <ion-label [innerHTML]=\"page.subtitle | translate\"></ion-label>\n <ion-text class=\"ion-float-end\" [title]=\"page.time | dateFormat: { time: true }\">\n <small>\n <ion-icon name=\"time-outline\"></ion-icon>\n {{ page.time | dateFromNow }}\n </small>\n \n </ion-text>\n </ion-card-subtitle>\n\n <!-- main page -->\n <ion-card-title class=\"ion-no-margin ion-no-padding\">\n <ion-item\n detail=\"true\"\n tappable\n class=\"text-1x\"\n [routerLink]=\"page.path\"\n routerDirection=\"root\"\n lines=\"none\"\n >\n <!-- page icon-->\n <ion-icon *ngIf=\"page.icon\" slot=\"start\" [name]=\"page.icon\"></ion-icon>\n <mat-icon *ngIf=\"page.matIcon\" slot=\"start\">{{ page.matIcon }}</mat-icon>\n\n <ion-label color=\"primary\" [innerHTML]=\"page.title\"></ion-label>\n </ion-item>\n </ion-card-title>\n </ion-card-header>\n\n <!-- children pages -->\n @if (page.children | isNotEmptyArray) {\n <ion-card-content class=\"ion-no-padding ion-padding-start\">\n <ion-list class=\"ion-no-padding\">\n @for (childPage of page.children; track childPage.path) {\n <ion-item\n detail=\"true\"\n tappable\n class=\"text-1x\"\n [routerLink]=\"childPage.path\"\n routerDirection=\"root\"\n >\n <!-- page icon-->\n <ion-icon *ngIf=\"childPage.icon\" slot=\"start\" color=\"dark\" [name]=\"childPage.icon\"></ion-icon>\n <mat-icon *ngIf=\"childPage.matIcon\" slot=\"start\" color=\"dark\">\n {{ childPage.matIcon }}\n </mat-icon>\n\n <ion-label color=\"dark\" [innerHTML]=\"childPage.title\"></ion-label>\n </ion-item>\n }\n </ion-list>\n </ion-card-content>\n }\n </ion-card>\n </ion-col>\n }\n </ion-row>\n </ion-grid>\n } @else if (showPartnerBanner || showLegalInformation) {\n <ng-container *ngTemplateOutlet=\"bottomBanner; context: { hasFeeds: hasFeeds }\"></ng-container>\n }\n\n }\n\n</ion-content>\n\n<ng-template #bottomBanner let-hasFeeds=\"hasFeeds\">\n <ion-grid class=\"bottom-banner ion-text-center\" [class.floating]=\"!mobile && !hasFeeds\" @fadeInAnimation>\n <ion-row>\n @if (showPartnerBanner) {\n <ion-col size=\"12\" [sizeLg]=\"partnerBannerSize\" class=\"ion-text-center\">\n @if (showLegalInformation) {\n <ion-list class=\"legal-info\">\n <ion-list-header>{{ 'HOME.PARTNERS' | translate }}</ion-list-header>\n </ion-list>\n }\n <!-- partners -->\n @for (item of $partners | async; track $index) {\n <a href=\"{{ item.siteUrl }}\">\n <img class=\"logo\" src=\"{{ item.logo }}\" alt=\"{{ item.label }}\" [title]=\"item.label\" />\n </a>\n }\n </ion-col>\n }\n @if (showLegalInformation) {\n <ion-col size=\"12\" size-sm=\"6\" size-lg=\"3\" [class.left-border]=\"showPartnerBanner\">\n <ion-list class=\"legal-info\">\n <ion-list-header class=\"ion-text-left\">{{ 'HOME.LEGAL_INFORMATION' | translate }}</ion-list-header>\n @for (item of legalInformation; track item.path) {\n <ion-item (click)=\"openMarkdownModal($event, item)\" lines=\"none\" [button]=\"true\">\n <ion-label>\n <p>\n {{ item.title | translate: item.titleArgs }}\n </p>\n </ion-label>\n </ion-item>\n }\n </ion-list>\n </ion-col>\n @if (showInstallLinks) {\n <ion-col size=\"12\" size-lg=\"3\" offset-lg=\"9\" [class.left-border]=\"showPartnerBanner\">\n <ion-list class=\"legal-info\">\n <ion-list-header>{{ 'HOME.INSTALL_APP' | translate }}</ion-list-header>\n <!-- TODO -->\n </ion-list>\n </ion-col>\n }\n }\n </ion-row>\n </ion-grid>\n</ng-template>\n", styles: [":host{--ion-card-background-color: rgba(var(--ion-background-color-rgb), .7)}h1,h2,h3,h4,h5{white-space:normal}.center,ion-content ion-card.welcome img{text-align:center;display:inline-block}ion-content{--background: transparent;background-size:cover;-webkit-background-size:cover;-moz-background-size:cover;-o-background-size:cover;background-color:var(--ion-background-color);display:inline-block;padding:15px}ion-content .loading-page{display:block;height:100%;width:100%;position:absolute;background-color:transparent;z-index:5;transition:background .5s linear;-webkit-transition:background 1.5s linear}ion-content .loading-page.hidden{background-color:rgba(var(--ion-color-primary),0)}ion-content ion-card{z-index:9;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}ion-content ion-card ion-card-header{display:block}ion-content ion-card ion-card-header ion-card-subtitle,ion-content ion-card ion-card-header ion-card-title{display:block;width:100%}ion-content ion-card.main{min-width:240px;max-width:400px;margin-left:auto;margin-right:auto}ion-content ion-card.welcome{background-color:var(--ion-card-background-color)}ion-content ion-card.welcome button{margin-top:16px}ion-content ion-card.welcome img{max-width:250px}ion-content .history-container{margin-left:auto;margin-right:auto}ion-content .history-container ion-card{display:inline-block;box-sizing:border-box;z-index:8;min-width:240px;max-width:400px;width:100%;margin-left:auto;margin-right:auto;background-color:var(--ion-card-background-color);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}ion-content .history-container ion-card ion-card-header ion-card-subtitle{height:40px}ion-content .history-container ion-card ion-card-header ion-card-subtitle button[float-start]{--mdc-icon-button-state-layer-size: 40px;padding:8px;z-index:99;margin:0}ion-content .history-container ion-card ion-card-header ion-card-subtitle ion-label{line-height:40px}ion-content .history-container ion-card ion-card-header ion-card-subtitle ion-label[float-end]{padding-right:16px;font-weight:400}ion-content .history-container ion-card ion-card-header ion-card-title ion-item{width:100%;--ion-item-background: $transparent;--ion-item-icon-color: var(--ion-color-primary);--ion-item-text-color: var(--ion-color-primary)}ion-content .history-container ion-card ion-card-header ion-card-title ion-item ion-icon[slot=start],ion-content .history-container ion-card ion-card-header ion-card-title ion-item mat-icon[slot=start]{margin-right:unset;margin-inline-end:16px!important;color:var(--ion-item-icon-color)!important;fill:currentColor;stroke:currentColor}ion-content .history-container ion-card ion-card-header ion-card-title ion-item ion-text,ion-content .history-container ion-card ion-card-header ion-card-title ion-item ion-label{color:var(--ion-item-text-color)!important;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}ion-content .history-container ion-card ion-card-content ion-list{--ion-item-background: var(--ion-color-transparent)}ion-content .history-container ion-card ion-card-content ion-list ion-item{--ion-item-icon-color: var(--ion-color-dark);--ion-item-text-color: var(--ion-color-dark)}ion-content .history-container ion-card ion-card-content ion-list ion-item ion-icon[slot=start],ion-content .history-container ion-card ion-card-content ion-list ion-item mat-icon[slot=start]{color:var(--ion-item-icon-color)!important;fill:currentColor;stroke:currentColor;margin-right:unset;margin-inline-end:16px!important}ion-content .history-container ion-card ion-card-content ion-list ion-item ion-text,ion-content .history-container ion-card ion-card-content ion-list ion-item ion-label{color:var(--ion-item-text-color)!important}ion-content .bottom-banner{--ion-card-background-color: rgba(var(--ion-background-color-rgb), .5);background-color:var(--ion-card-background-color);min-width:240px;z-index:0;border-radius:4px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin-left:16px;margin-right:16px;margin-bottom:16px}ion-content .bottom-banner img.logo{max-height:50px;margin-left:8px}ion-content .bottom-banner ion-list-header{min-height:30px}ion-content .bottom-banner ion-item{--min-height: 28px}ion-content .bottom-banner ion-item ion-label{margin-top:3px!important;margin-bottom:3px!important}ion-content ion-text{text-align:center}@media screen and (max-width: 575px){ion-content ion-card{min-width:160px;width:calc(100% - 20px);max-width:100%;margin-left:10px;margin-right:10px}ion-content ion-card h1{margin-top:0;font-size:20px}ion-content ion-card ion-card-content ion-button{margin-top:8px}ion-content .bottom-banner.floating{display:block;position:unset}ion-content .bottom-banner img.logo{max-height:30px}}@media screen and (max-width: 767px) and (min-width: 576px){ion-content{min-height:555px}ion-content .bottom-banner img.logo{max-height:40px}}@media screen and (min-width: 768px){ion-content{min-height:calc(100% - 100px)}}@media screen and (min-width: 1200px){ion-content ion-col.feed{min-height:calc(100vh - var(--ion-toolbar-height) - 138px)}ion-content .bottom-banner.floating{position:absolute;left:0;right:0;bottom:0}}.content-credits{position:absolute;right:0;bottom:0}.content-credits ion-text{padding-inline-start:4px;padding-inline-end:4px;font-size:.8rem;background-color:var(--ion-background-color, white);color:var(--ion-text-color);opacity:.65}@media screen and (min-width: 992px){.left-border{border-left:1px solid var(--ion-color-medium)}}\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: "directive", type: i3$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i3$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: i2$1.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$1.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$1.IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$1.IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: i2$1.IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: i2$1.IonCardSubtitle, selector: "ion-card-subtitle", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$1.IonContent, selector: "ion-content", inputs: ["color", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$1.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i2$1.IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: i2$1.IonItem, selector: "ion-item", inputs: ["button", "color", "counter", "counterFormatter", "detail", "detailIcon", "disabled", "download", "fill", "href", "lines", "mode", "rel", "routerAnimation", "routerDirection", "shape", "target", "type"] }, { kind: "component", type: i2$1.IonLabel, selector: "ion-label", inputs: ["color", "mode", "position"] }, { kind: "component", type: i2$1.IonList, selector: "ion-list", inputs: ["inset", "lines", "mode"] }, { kind: "component", type: i2$1.IonListHeader, selector: "ion-list-header", inputs: ["color", "lines", "mode"] }, { kind: "component", type: i2$1.IonMenuToggle, selector: "ion-menu-toggle", inputs: ["autoHide", "menu"] }, { kind: "component", type: i2$1.IonRow, selector: "ion-row" }, { kind: "component", type: i2$1.IonText, selector: "ion-text", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "directive", type: i2$1.RouterLinkDelegate, selector: ":not(a):not(area)[routerLink]" }, { kind: "directive", type: i1$1.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "directive", type: i8$2.RxIf, selector: "[rxIf]", inputs: ["rxIf", "rxIfStrategy", "rxIfElse", "rxIfThen", "rxIfSuspense", "rxIfComplete", "rxIfError", "rxIfContextTrigger", "rxIfNextTrigger", "rxIfSuspenseTrigger", "rxIfErrorTrigger", "rxIfCompleteTrigger", "rxIfParent", "rxIfPatchZone", "rxIfRenderCallback"] }, { kind: "component", type: i6$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "component", type: i8$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i8$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i8$1.MatMenuContent, selector: "ng-template[matMenuContent]" }, { kind: "directive", type: i8$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: ToolbarComponent, selector: "app-toolbar", inputs: ["progressBarMode", "title", "color", "class", "id", "backHref", "defaultBackHref", "hasValidate", "hasClose", "hasSearch", "canGoBack", "canShowMenu"], outputs: ["onValidate", "onClose", "onValidateAndClose", "onBackClick", "onSearch"] }, { kind: "directive", type: i1$6.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: AppInstallUpgradeCard, selector: "app-install-upgrade-card", inputs: ["isLogin", "showUpgradeWarning", "showOfflineWarning", "showInstallButton", "debug"] }, { kind: "component", type: UserEventNotificationIcon, selector: "app-user-event-notification-icon", inputs: ["titleI18n", "disabled", "filter", "autoHide", "headerActions", "footerActions", "style", "color", "unreadColor", "fill", "mobile", "listStyle", "debug"], outputs: ["showList"] }, { kind: "component", type: FeedsComponent, selector: "app-feeds", inputs: ["feeds", "showHeader", "headerTextColor"] }, { kind: "pipe", type: i3$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }, { kind: "pipe", type: i19.RxPush, name: "push" }, { kind: "pipe", type: DateFormatPipe, name: "dateFormat" }, { kind: "pipe", type: DateFromNowPipe, name: "dateFromNow" }, { kind: "pipe", type: NotEmptyArrayPipe, name: "isNotEmptyArray" }, { kind: "pipe", type: IsNotNilOrBlankPipe, name: "isNotNilOrBlank" }], animations: [fadeInAnimation, slideUpDownAnimation], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
33645
34140
|
}
|
|
33646
34141
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HomePage, decorators: [{
|
|
33647
34142
|
type: Component,
|
|
33648
|
-
args: [{ selector: 'app-page-home', changeDetection: ChangeDetectionStrategy.OnPush, animations: [fadeInAnimation, slideUpDownAnimation], template: "<app-toolbar [canGoBack]=\"false\" visible-xs visible-sm visible-mobile>\n <ion-buttons slot=\"end\">\n @if (showNotificationIcon) {\n <app-user-event-notification-icon [autoHide]=\"autoHideNotificationIcon\"></app-user-event-notification-icon>\n }\n\n <!-- dark mode (if allow by the environment token) -->\n @if (settings.allowDarkMode) {\n <ion-button (click)=\"toggleDarkMode()\">\n <ion-icon slot=\"icon-only\" [name]=\"(darkMode$ | push) ? 'sunny' : 'moon'\"></ion-icon>\n </ion-button>\n }\n\n <!-- Change locale button -->\n <ion-button [matMenuTriggerFor]=\"localeMenu\">\n <ion-icon slot=\"start\" name=\"language\"></ion-icon>\n </ion-button>\n </ion-buttons>\n</app-toolbar>\n\n<!-- Change locale menu -->\n<mat-menu #localeMenu=\"matMenu\">\n <ng-template matMenuContent>\n @for (item of locales; track item.key) {\n <button mat-menu-item (click)=\"changeLanguage(item.key)\">\n <ion-label>{{ item.value }}</ion-label>\n </button>\n }\n </ng-template>\n</mat-menu>\n\n<ion-content [ngStyle]=\"contentStyle\" no-padding-xs>\n @if (contentCredits | isNotNilOrBlank) {\n <span class=\"content-credits\">\n <ion-text>{{ contentCredits }}</ion-text>\n </span>\n }\n\n <!-- loading spinner -->\n <div class=\"loading-page\" [class.cdk-visually-hidden]=\"!(loading$ | push)\">\n @if (showSpinner) {\n <div class=\"spinner\">\n <p [innerHTML]=\"'COMMON.LOADING_DOTS' | translate\"></p>\n <div class=\"sk-cube1 sk-cube\"></div>\n <div class=\"sk-cube2 sk-cube\"></div>\n <div class=\"sk-cube4 sk-cube\"></div>\n <div class=\"sk-cube3 sk-cube\"></div>\n </div>\n }\n </div>\n\n <!-- Desktop: translucent top toolbar -->\n @if (!mobile) {\n <div class=\"hidden-xs hidden-sm\">\n <ion-toolbar color=\"traansparent\" translucent>\n <ion-buttons slot=\"start\">\n <ion-menu-toggle>\n <ion-button color=\"light\" fill=\"clear\">\n <ion-icon slot=\"icon-only\" name=\"menu\"></ion-icon>\n </ion-button>\n </ion-menu-toggle>\n </ion-buttons>\n\n <ion-buttons slot=\"end\">\n @if (showNotificationIcon) {\n <app-user-event-notification-icon\n [style]=\"'ion-button'\"\n color=\"secondary\"\n unreadColor=\"tertiary\"\n fill=\"solid\"\n [autoHide]=\"autoHideNotificationIcon\"\n ></app-user-event-notification-icon>\n }\n\n <!-- dark mode (if allow by the environment token) -->\n @if (settings.allowDarkMode) {\n <ion-button\n color=\"secondary\"\n fill=\"solid\"\n (click)=\"toggleDarkMode()\"\n [title]=\"'HOME.BTN_DARK_MODE' | translate\"\n >\n <ion-icon slot=\"icon-only\" [name]=\"(darkMode$ | push) ? 'moon' : 'sunny'\"></ion-icon>\n </ion-button>\n }\n <!-- change locale button -->\n <ion-button\n color=\"secondary\"\n fill=\"solid\"\n [matMenuTriggerFor]=\"localeMenu\"\n [title]=\"'SETTINGS.LOCALE' | translate\"\n >\n <ion-icon slot=\"icon-only\" name=\"language\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </div>\n }\n\n <!-- Install (and upgrade) card -->\n <app-install-upgrade-card [isLogin]=\"isLogin\" [showInstallButton]=\"true\"></app-install-upgrade-card>\n\n @if (!(loading$ | push)) {\n <!-- Welcome card -->\n <ion-card class=\"main welcome ion-padding ion-text-center ion-align-self-center\" @fadeInAnimation>\n <ion-card-header>\n <ion-card-title class=\"ion-text-center\">\n <span *ngIf=\"isWeb\" [innerHTML]=\"'HOME.WELCOME_WEB' | translate: { appName: appName }\"></span>\n <span *ngIf=\"!isWeb\" [innerHTML]=\"'HOME.WELCOME_APP' | translate: { appName: appName }\"></span>\n </ion-card-title>\n <ion-card-subtitle [innerHTML]=\"description\"></ion-card-subtitle>\n </ion-card-header>\n <ion-card-content class=\"ion-no-padding\">\n <ion-text color=\"primary\">\n <img *ngIf=\"logo\" [attr.src]=\"logo\" alt=\"Logo\" />\n </ion-text>\n <!-- register help text -->\n <ion-text *ngIf=\"!isLogin && canRegister\">\n <br />\n <span translate>HOME.REGISTER_HELP</span>\n </ion-text>\n <div class=\"ion-padding-top\">\n <!-- If NOT login -->\n <ng-container *ngIf=\"!isLogin; else loginButtons\">\n <ion-button *ngIf=\"canRegister\" expand=\"block\" color=\"tertiary\" (click)=\"register()\">\n <span translate>HOME.BTN_REGISTER</span>\n </ion-button>\n <ion-button expand=\"block\" color=\"light\" [routerLink]=\"['/']\" (click)=\"login()\">\n <span translate>AUTH.BTN_LOGIN</span>\n </ion-button>\n </ng-container>\n\n <!-- If user login -->\n <ng-template #loginButtons>\n <!-- Feature buttons -->\n <ng-container *rxIf=\"$filteredButtons; let buttons\">\n <ng-container *ngFor=\"let item of buttons\">\n <ion-button\n *ngIf=\"item.path\"\n expand=\"block\"\n color=\"tertiary\"\n [class]=\"item.cssClass\"\n [routerLink]=\"item.path\"\n routerDirection=\"root\"\n >\n <ion-icon slot=\"start\" class=\"ion-float-start\" *ngIf=\"item.icon\" [name]=\"item.icon\"></ion-icon>\n <mat-icon slot=\"start\" class=\"ion-float-start\" *ngIf=\"item.matIcon\">{{ item.matIcon }}</mat-icon>\n <ion-text>{{ 'HOME.BTN_DATA_ENTRY' | translate: { name: (item.title | translate) } }}</ion-text>\n </ion-button>\n\n <!-- divider -->\n <div *ngIf=\"!item.path && !item.action\" [class]=\"item.cssClass\">\n <ion-label translate>{{ item.title }} </ion-label>\n </div>\n </ng-container>\n\n <!--<p *ngIf=\"buttons.length\" class=\"visible-mobile\"> </p>-->\n </ng-container>\n\n <ion-button *ngIf=\"showAccountButton\" expand=\"block\" color=\"secondary\" [routerLink]=\"['/account']\">\n <ion-icon slot=\"start\" class=\"ion-float-start\" name=\"person-circle\"></ion-icon>\n <ion-text translate>HOME.BTN_MY_ACCOUNT</ion-text>\n </ion-button>\n\n <p hidden-xs hidden-sm hidden-mobile>\n <ion-text\n [innerHTML]=\"'HOME.NOT_THIS_ACCOUNT_QUESTION' | translate: { displayName: accountName }\"\n ></ion-text>\n <br />\n <ion-text>\n <a href=\"#\" (click)=\"logout($event)\">\n <span translate>HOME.BTN_DISCONNECT</span>\n </a>\n </ion-text>\n </p>\n </ng-template>\n </div>\n </ion-card-content>\n </ion-card>\n\n <!-- Page history -->\n @let pageHistory = isLogin && (pageHistory$ | push);\n @if (pageHistory | isNotEmptyArray) {\n <ion-grid class=\"history-container ion-align-self-center\">\n <ion-row>\n @for (page of pageHistory; track page.path) {\n <ion-col size=\"12\" size-xl=\"\" class=\"ion-text-center\">\n <ion-card class=\"ion-align-self-start ion-text-start\" @fadeInAnimation>\n <ion-card-header class=\"ion-no-padding\">\n <!-- top bar -->\n <ion-card-subtitle>\n <button\n type=\"button\"\n tabindex=\"-1\"\n (click)=\"removePageHistory(page.path)\"\n mat-icon-button\n class=\"ion-float-start ion-no-margin close-button\"\n >\n <mat-icon>close</mat-icon>\n </button>\n <ion-label [innerHTML]=\"page.subtitle | translate\"></ion-label>\n <ion-text class=\"ion-float-end\" [title]=\"page.time | dateFormat: { time: true }\">\n <small>\n <ion-icon name=\"time-outline\"></ion-icon>\n {{ page.time | dateFromNow }}\n </small>\n \n </ion-text>\n </ion-card-subtitle>\n\n <!-- main page -->\n <ion-card-title class=\"ion-no-margin ion-no-padding\">\n <ion-item\n detail=\"true\"\n tappable\n class=\"text-1x\"\n [routerLink]=\"page.path\"\n routerDirection=\"root\"\n lines=\"none\"\n >\n <!-- page icon-->\n <ion-icon *ngIf=\"page.icon\" slot=\"start\" [name]=\"page.icon\"></ion-icon>\n <mat-icon *ngIf=\"page.matIcon\" slot=\"start\">{{ page.matIcon }}</mat-icon>\n\n <ion-label color=\"primary\" [innerHTML]=\"page.title\"></ion-label>\n </ion-item>\n </ion-card-title>\n </ion-card-header>\n\n <!-- children pages -->\n @if (page.children | isNotEmptyArray) {\n <ion-card-content class=\"ion-no-padding ion-padding-start\">\n <ion-list class=\"ion-no-padding\">\n @for (childPage of page.children; track childPage.path) {\n <ion-item\n detail=\"true\"\n tappable\n class=\"text-1x\"\n [routerLink]=\"childPage.path\"\n routerDirection=\"root\"\n >\n <!-- page icon-->\n <ion-icon *ngIf=\"childPage.icon\" slot=\"start\" color=\"dark\" [name]=\"childPage.icon\"></ion-icon>\n <mat-icon *ngIf=\"childPage.matIcon\" slot=\"start\" color=\"dark\">\n {{ childPage.matIcon }}\n </mat-icon>\n\n <ion-label color=\"dark\" [innerHTML]=\"childPage.title\"></ion-label>\n </ion-item>\n }\n </ion-list>\n </ion-card-content>\n }\n </ion-card>\n </ion-col>\n }\n </ion-row>\n </ion-grid>\n } @else if (showPartnerBanner || showLegalInformation) {\n <ng-container *ngTemplateOutlet=\"bottomBanner\"></ng-container>\n }\n }\n</ion-content>\n\n<ng-template #bottomBanner>\n <ion-grid class=\"bottom-banner ion-text-center\" [class.floating]=\"!mobile\" @fadeInAnimation>\n <ion-row>\n @if (showPartnerBanner) {\n <ion-col size=\"12\" [sizeLg]=\"partnerBannerSize\" class=\"ion-text-center\">\n @if (showLegalInformation) {\n <ion-list class=\"legal-info\">\n <ion-list-header>{{ 'HOME.PARTNERS' | translate }}</ion-list-header>\n </ion-list>\n }\n <!-- partners -->\n @for (item of $partners | async; track $index) {\n <a href=\"{{ item.siteUrl }}\">\n <img class=\"logo\" src=\"{{ item.logo }}\" alt=\"{{ item.label }}\" [title]=\"item.label\" />\n </a>\n }\n </ion-col>\n }\n @if (showLegalInformation) {\n <ion-col size=\"12\" size-sm=\"6\" size-lg=\"3\" [class.left-border]=\"showPartnerBanner\">\n <ion-list class=\"legal-info\">\n <ion-list-header class=\"ion-text-left\">{{ 'HOME.LEGAL_INFORMATION' | translate }}</ion-list-header>\n @for (item of legalInformation; track item.path) {\n <ion-item (click)=\"openMarkdownModal($event, item)\" lines=\"none\" [button]=\"true\">\n <ion-label>\n <p>\n {{ item.title | translate: item.titleArgs }}\n </p>\n </ion-label>\n </ion-item>\n }\n </ion-list>\n </ion-col>\n @if (showInstallLinks) {\n <ion-col size=\"12\" size-lg=\"3\" offset-lg=\"9\" [class.left-border]=\"showPartnerBanner\">\n <ion-list class=\"legal-info\">\n <ion-list-header>{{ 'HOME.INSTALL_APP' | translate }}</ion-list-header>\n <!-- TODO -->\n </ion-list>\n </ion-col>\n }\n }\n </ion-row>\n </ion-grid>\n</ng-template>\n", styles: [":host{--ion-card-background-color: rgba(var(--ion-background-color-rgb), .7)}h1,h2,h3,h4,h5{white-space:normal}.center,ion-content ion-card.welcome img{text-align:center;display:inline-block}ion-content{--background: transparent;background-size:cover;-webkit-background-size:cover;-moz-background-size:cover;-o-background-size:cover;background-color:var(--ion-background-color);display:inline-block;padding:15px}ion-content .loading-page{display:block;height:100%;width:100%;position:absolute;background-color:transparent;z-index:5;transition:background .5s linear;-webkit-transition:background 1.5s linear}ion-content .loading-page.hidden{background-color:rgba(var(--ion-color-primary),0)}ion-content ion-card{z-index:9}ion-content ion-card ion-card-header{display:block}ion-content ion-card ion-card-header ion-card-subtitle,ion-content ion-card ion-card-header ion-card-title{display:block;width:100%}ion-content ion-card.main{min-width:240px;max-width:400px;margin-left:auto;margin-right:auto}ion-content ion-card.welcome{background-color:var(--ion-card-background-color)}ion-content ion-card.welcome button{margin-top:16px}ion-content ion-card.welcome img{max-width:250px}ion-content .history-container{margin-left:auto;margin-right:auto}ion-content .history-container ion-card{display:inline-block;box-sizing:border-box;z-index:8;min-width:240px;max-width:400px;width:100%;margin-left:auto;margin-right:auto;background-color:var(--ion-card-background-color)}ion-content .history-container ion-card ion-card-header ion-card-subtitle{height:40px}ion-content .history-container ion-card ion-card-header ion-card-subtitle button[float-start]{--mdc-icon-button-state-layer-size: 40px;padding:8px;z-index:99;margin:0}ion-content .history-container ion-card ion-card-header ion-card-subtitle ion-label{line-height:40px}ion-content .history-container ion-card ion-card-header ion-card-subtitle ion-label[float-end]{padding-right:16px;font-weight:400}ion-content .history-container ion-card ion-card-header ion-card-title ion-item{width:100%;--ion-item-background: $transparent;--ion-item-icon-color: var(--ion-color-primary);--ion-item-text-color: var(--ion-color-primary)}ion-content .history-container ion-card ion-card-header ion-card-title ion-item ion-icon[slot=start],ion-content .history-container ion-card ion-card-header ion-card-title ion-item mat-icon[slot=start]{margin-right:unset;margin-inline-end:16px!important;color:var(--ion-item-icon-color)!important;fill:currentColor;stroke:currentColor}ion-content .history-container ion-card ion-card-header ion-card-title ion-item ion-text,ion-content .history-container ion-card ion-card-header ion-card-title ion-item ion-label{color:var(--ion-item-text-color)!important;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}ion-content .history-container ion-card ion-card-content ion-list{--ion-item-background: var(--ion-color-transparent)}ion-content .history-container ion-card ion-card-content ion-list ion-item{--ion-item-icon-color: var(--ion-color-dark);--ion-item-text-color: var(--ion-color-dark)}ion-content .history-container ion-card ion-card-content ion-list ion-item ion-icon[slot=start],ion-content .history-container ion-card ion-card-content ion-list ion-item mat-icon[slot=start]{color:var(--ion-item-icon-color)!important;fill:currentColor;stroke:currentColor;margin-right:unset;margin-inline-end:16px!important}ion-content .history-container ion-card ion-card-content ion-list ion-item ion-text,ion-content .history-container ion-card ion-card-content ion-list ion-item ion-label{color:var(--ion-item-text-color)!important}ion-content .bottom-banner{background-color:var(--ion-card-background-color);min-width:240px;z-index:0}ion-content .bottom-banner.floating{position:absolute;left:16px;right:16px;bottom:16px}ion-content .bottom-banner img.logo{max-height:50px;margin-left:8px}ion-content ion-text{text-align:center}@media screen and (max-width: 575px){ion-content ion-card{min-width:160px;width:calc(100% - 20px);max-width:100%;margin-left:10px;margin-right:10px}ion-content ion-card h1{margin-top:0;font-size:20px}ion-content ion-card ion-card-content ion-button{margin-top:8px}ion-content .bottom-banner.floating{display:block;position:unset}ion-content .bottom-banner img.logo{max-height:30px}}@media screen and (max-width: 767px) and (min-width: 576px){ion-content{min-height:555px}ion-content .bottom-banner img.logo{max-height:40px}}@media screen and (min-width: 768px){ion-content{min-height:calc(100% - 100px)}}.content-credits{position:absolute;right:0;bottom:0}.content-credits ion-text{padding-inline-start:4px;padding-inline-end:4px;font-size:.8rem;background-color:var(--ion-background-color, white);color:var(--ion-text-color);opacity:.65}@media screen and (min-width: 992px){.left-border{border-left:1px solid var(--ion-color-medium)}}.bottom-banner ion-list-header{min-height:30px}.bottom-banner ion-item{--min-height: 28px}.bottom-banner ion-item ion-label{margin-top:3px!important;margin-bottom:3px!important}\n"] }]
|
|
34143
|
+
args: [{ selector: 'app-page-home', changeDetection: ChangeDetectionStrategy.OnPush, animations: [fadeInAnimation, slideUpDownAnimation], template: "<app-toolbar [canGoBack]=\"false\" visible-xs visible-sm visible-mobile>\n <ion-buttons slot=\"end\">\n @if (showNotificationIcon) {\n <app-user-event-notification-icon [autoHide]=\"autoHideNotificationIcon\"></app-user-event-notification-icon>\n }\n\n <!-- dark mode (if allow by the environment token) -->\n @if (settings.allowDarkMode) {\n <ion-button (click)=\"toggleDarkMode()\">\n <ion-icon slot=\"icon-only\" [name]=\"(darkMode$ | push) ? 'sunny' : 'moon'\"></ion-icon>\n </ion-button>\n }\n\n <!-- Change locale button -->\n <ion-button [matMenuTriggerFor]=\"localeMenu\">\n <ion-icon slot=\"start\" name=\"language\"></ion-icon>\n </ion-button>\n </ion-buttons>\n</app-toolbar>\n\n<!-- Change locale menu -->\n<mat-menu #localeMenu=\"matMenu\">\n <ng-template matMenuContent>\n @for (item of locales; track item.key) {\n <button mat-menu-item (click)=\"changeLanguage(item.key)\">\n <!--<mat-icon>{{ currentLocale === item.key ? 'checkmark' : undefined }}</mat-icon>-->\n <ion-label>{{ item.value }}</ion-label>\n </button>\n }\n </ng-template>\n</mat-menu>\n\n<ion-content [ngStyle]=\"contentStyle\" no-padding-xs>\n\n @if (contentCredits | isNotNilOrBlank) {\n <span class=\"content-credits\">\n <ion-text>{{ contentCredits }}</ion-text>\n </span>\n }\n\n <!-- loading spinner -->\n <div class=\"loading-page\" [class.cdk-visually-hidden]=\"!(loading$ | push)\">\n @if (showSpinner) {\n <div class=\"spinner\">\n <p [innerHTML]=\"'COMMON.LOADING_DOTS' | translate\"></p>\n <div class=\"sk-cube1 sk-cube\"></div>\n <div class=\"sk-cube2 sk-cube\"></div>\n <div class=\"sk-cube4 sk-cube\"></div>\n <div class=\"sk-cube3 sk-cube\"></div>\n </div>\n }\n </div>\n\n <!-- Desktop: translucent top toolbar -->\n @if (!mobile) {\n <div class=\"hidden-xs hidden-sm\">\n <ion-toolbar color=\"traansparent\" translucent>\n <ion-buttons slot=\"start\">\n <ion-menu-toggle>\n <ion-button color=\"light\" fill=\"clear\">\n <ion-icon slot=\"icon-only\" name=\"menu\"></ion-icon>\n </ion-button>\n </ion-menu-toggle>\n </ion-buttons>\n\n <ion-buttons slot=\"end\">\n @if (showNotificationIcon) {\n <app-user-event-notification-icon\n [style]=\"'ion-button'\"\n color=\"secondary\"\n unreadColor=\"tertiary\"\n fill=\"solid\"\n [autoHide]=\"autoHideNotificationIcon\"\n ></app-user-event-notification-icon>\n }\n\n <!-- dark mode (if allow by the environment token) -->\n @if (settings.allowDarkMode) {\n <ion-button\n color=\"secondary\"\n fill=\"solid\"\n (click)=\"toggleDarkMode()\"\n [title]=\"'HOME.BTN_DARK_MODE' | translate\"\n >\n <ion-icon slot=\"icon-only\" [name]=\"(darkMode$ | push) ? 'moon' : 'sunny'\"></ion-icon>\n </ion-button>\n }\n <!-- change locale button -->\n <ion-button\n color=\"secondary\"\n fill=\"solid\"\n [matMenuTriggerFor]=\"localeMenu\"\n [title]=\"'SETTINGS.LOCALE' | translate\"\n >\n <ion-icon slot=\"icon-only\" name=\"language\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </div>\n }\n\n <!-- Install (and upgrade) card -->\n <app-install-upgrade-card [isLogin]=\"isLogin\" [showInstallButton]=\"true\"></app-install-upgrade-card>\n\n @if (!(loading$ | push)) {\n @let hasFeeds = feeds && (feeds.hasFeeds$ | async);\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col size=\"0\" [sizeXl]=\"hasFeeds ? 3 : 0\"></ion-col>\n <ion-col>\n <!-- Welcome card -->\n <ion-card class=\"main welcome ion-padding ion-text-center ion-align-self-center\" @fadeInAnimation>\n <ion-card-header>\n <ion-card-title class=\"ion-text-center\">\n <span *ngIf=\"isWeb\" [innerHTML]=\"'HOME.WELCOME_WEB' | translate: { appName: appName }\"></span>\n <span *ngIf=\"!isWeb\" [innerHTML]=\"'HOME.WELCOME_APP' | translate: { appName: appName }\"></span>\n </ion-card-title>\n <ion-card-subtitle [innerHTML]=\"description\"></ion-card-subtitle>\n </ion-card-header>\n <ion-card-content class=\"ion-no-padding\">\n <ion-text color=\"primary\">\n <img *ngIf=\"logo\" [attr.src]=\"logo\" alt=\"Logo\" />\n </ion-text>\n <!-- register help text -->\n <ion-text *ngIf=\"!isLogin && canRegister\">\n <br />\n <span translate>HOME.REGISTER_HELP</span>\n </ion-text>\n <div class=\"ion-padding-top\">\n <!-- If NOT login -->\n <ng-container *ngIf=\"!isLogin; else loginButtons\">\n <ion-button *ngIf=\"canRegister\" expand=\"block\" color=\"tertiary\" (click)=\"register()\">\n <span translate>HOME.BTN_REGISTER</span>\n </ion-button>\n <ion-button expand=\"block\" color=\"light\" [routerLink]=\"['/']\" (click)=\"login()\">\n <span translate>AUTH.BTN_LOGIN</span>\n </ion-button>\n </ng-container>\n\n <!-- If user login -->\n <ng-template #loginButtons>\n <!-- Feature buttons -->\n <ng-container *rxIf=\"$filteredButtons; let buttons\">\n <ng-container *ngFor=\"let item of buttons\">\n <ion-button\n *ngIf=\"item.path\"\n expand=\"block\"\n color=\"tertiary\"\n [class]=\"item.cssClass\"\n [routerLink]=\"item.path\"\n routerDirection=\"root\"\n >\n <ion-icon slot=\"start\" class=\"ion-float-start\" *ngIf=\"item.icon\" [name]=\"item.icon\"></ion-icon>\n <mat-icon slot=\"start\" class=\"ion-float-start\" *ngIf=\"item.matIcon\">{{ item.matIcon }}</mat-icon>\n <ion-text>{{ 'HOME.BTN_DATA_ENTRY' | translate: { name: (item.title | translate) } }}</ion-text>\n </ion-button>\n\n <!-- divider -->\n <div *ngIf=\"!item.path && !item.action\" [class]=\"item.cssClass\">\n <ion-label translate>{{ item.title }} </ion-label>\n </div>\n </ng-container>\n\n <!--<p *ngIf=\"buttons.length\" class=\"visible-mobile\"> </p>-->\n </ng-container>\n\n <ion-button *ngIf=\"showAccountButton\" expand=\"block\" color=\"secondary\" [routerLink]=\"['/account']\">\n <ion-icon slot=\"start\" class=\"ion-float-start\" name=\"person-circle\"></ion-icon>\n <ion-text translate>HOME.BTN_MY_ACCOUNT</ion-text>\n </ion-button>\n\n <p hidden-xs hidden-sm hidden-mobile>\n <ion-text\n [innerHTML]=\"'HOME.NOT_THIS_ACCOUNT_QUESTION' | translate: { displayName: accountName }\"\n ></ion-text>\n <br />\n <ion-text>\n <a href=\"#\" (click)=\"logout($event)\">\n <span translate>HOME.BTN_DISCONNECT</span>\n </a>\n </ion-text>\n </p>\n </ng-template>\n </div>\n </ion-card-content>\n </ion-card>\n </ion-col>\n <ion-col size=\"0\" [sizeXl]=\"hasFeeds ? 1 : 0\"></ion-col>\n <ion-col class=\"feed\" [size]=\"hasFeeds ? 12 : 0\" [sizeXl]=\"hasFeeds ? 4 : 0\">\n <app-feeds #feeds></app-feeds>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n\n\n <!-- Page history -->\n @let pageHistory = isLogin && (pageHistory$ | push);\n @if (pageHistory | isNotEmptyArray) {\n <ion-grid class=\"history-container ion-align-self-center\">\n <ion-row>\n @for (page of pageHistory; track page.path) {\n <ion-col size=\"12\" size-xl=\"\" class=\"ion-text-center\">\n <ion-card class=\"ion-align-self-start ion-text-start\" @fadeInAnimation>\n <ion-card-header class=\"ion-no-padding\">\n <!-- top bar -->\n <ion-card-subtitle>\n <button\n type=\"button\"\n tabindex=\"-1\"\n (click)=\"removePageHistory(page.path)\"\n mat-icon-button\n class=\"ion-float-start ion-no-margin close-button\"\n >\n <mat-icon>close</mat-icon>\n </button>\n <ion-label [innerHTML]=\"page.subtitle | translate\"></ion-label>\n <ion-text class=\"ion-float-end\" [title]=\"page.time | dateFormat: { time: true }\">\n <small>\n <ion-icon name=\"time-outline\"></ion-icon>\n {{ page.time | dateFromNow }}\n </small>\n \n </ion-text>\n </ion-card-subtitle>\n\n <!-- main page -->\n <ion-card-title class=\"ion-no-margin ion-no-padding\">\n <ion-item\n detail=\"true\"\n tappable\n class=\"text-1x\"\n [routerLink]=\"page.path\"\n routerDirection=\"root\"\n lines=\"none\"\n >\n <!-- page icon-->\n <ion-icon *ngIf=\"page.icon\" slot=\"start\" [name]=\"page.icon\"></ion-icon>\n <mat-icon *ngIf=\"page.matIcon\" slot=\"start\">{{ page.matIcon }}</mat-icon>\n\n <ion-label color=\"primary\" [innerHTML]=\"page.title\"></ion-label>\n </ion-item>\n </ion-card-title>\n </ion-card-header>\n\n <!-- children pages -->\n @if (page.children | isNotEmptyArray) {\n <ion-card-content class=\"ion-no-padding ion-padding-start\">\n <ion-list class=\"ion-no-padding\">\n @for (childPage of page.children; track childPage.path) {\n <ion-item\n detail=\"true\"\n tappable\n class=\"text-1x\"\n [routerLink]=\"childPage.path\"\n routerDirection=\"root\"\n >\n <!-- page icon-->\n <ion-icon *ngIf=\"childPage.icon\" slot=\"start\" color=\"dark\" [name]=\"childPage.icon\"></ion-icon>\n <mat-icon *ngIf=\"childPage.matIcon\" slot=\"start\" color=\"dark\">\n {{ childPage.matIcon }}\n </mat-icon>\n\n <ion-label color=\"dark\" [innerHTML]=\"childPage.title\"></ion-label>\n </ion-item>\n }\n </ion-list>\n </ion-card-content>\n }\n </ion-card>\n </ion-col>\n }\n </ion-row>\n </ion-grid>\n } @else if (showPartnerBanner || showLegalInformation) {\n <ng-container *ngTemplateOutlet=\"bottomBanner; context: { hasFeeds: hasFeeds }\"></ng-container>\n }\n\n }\n\n</ion-content>\n\n<ng-template #bottomBanner let-hasFeeds=\"hasFeeds\">\n <ion-grid class=\"bottom-banner ion-text-center\" [class.floating]=\"!mobile && !hasFeeds\" @fadeInAnimation>\n <ion-row>\n @if (showPartnerBanner) {\n <ion-col size=\"12\" [sizeLg]=\"partnerBannerSize\" class=\"ion-text-center\">\n @if (showLegalInformation) {\n <ion-list class=\"legal-info\">\n <ion-list-header>{{ 'HOME.PARTNERS' | translate }}</ion-list-header>\n </ion-list>\n }\n <!-- partners -->\n @for (item of $partners | async; track $index) {\n <a href=\"{{ item.siteUrl }}\">\n <img class=\"logo\" src=\"{{ item.logo }}\" alt=\"{{ item.label }}\" [title]=\"item.label\" />\n </a>\n }\n </ion-col>\n }\n @if (showLegalInformation) {\n <ion-col size=\"12\" size-sm=\"6\" size-lg=\"3\" [class.left-border]=\"showPartnerBanner\">\n <ion-list class=\"legal-info\">\n <ion-list-header class=\"ion-text-left\">{{ 'HOME.LEGAL_INFORMATION' | translate }}</ion-list-header>\n @for (item of legalInformation; track item.path) {\n <ion-item (click)=\"openMarkdownModal($event, item)\" lines=\"none\" [button]=\"true\">\n <ion-label>\n <p>\n {{ item.title | translate: item.titleArgs }}\n </p>\n </ion-label>\n </ion-item>\n }\n </ion-list>\n </ion-col>\n @if (showInstallLinks) {\n <ion-col size=\"12\" size-lg=\"3\" offset-lg=\"9\" [class.left-border]=\"showPartnerBanner\">\n <ion-list class=\"legal-info\">\n <ion-list-header>{{ 'HOME.INSTALL_APP' | translate }}</ion-list-header>\n <!-- TODO -->\n </ion-list>\n </ion-col>\n }\n }\n </ion-row>\n </ion-grid>\n</ng-template>\n", styles: [":host{--ion-card-background-color: rgba(var(--ion-background-color-rgb), .7)}h1,h2,h3,h4,h5{white-space:normal}.center,ion-content ion-card.welcome img{text-align:center;display:inline-block}ion-content{--background: transparent;background-size:cover;-webkit-background-size:cover;-moz-background-size:cover;-o-background-size:cover;background-color:var(--ion-background-color);display:inline-block;padding:15px}ion-content .loading-page{display:block;height:100%;width:100%;position:absolute;background-color:transparent;z-index:5;transition:background .5s linear;-webkit-transition:background 1.5s linear}ion-content .loading-page.hidden{background-color:rgba(var(--ion-color-primary),0)}ion-content ion-card{z-index:9;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}ion-content ion-card ion-card-header{display:block}ion-content ion-card ion-card-header ion-card-subtitle,ion-content ion-card ion-card-header ion-card-title{display:block;width:100%}ion-content ion-card.main{min-width:240px;max-width:400px;margin-left:auto;margin-right:auto}ion-content ion-card.welcome{background-color:var(--ion-card-background-color)}ion-content ion-card.welcome button{margin-top:16px}ion-content ion-card.welcome img{max-width:250px}ion-content .history-container{margin-left:auto;margin-right:auto}ion-content .history-container ion-card{display:inline-block;box-sizing:border-box;z-index:8;min-width:240px;max-width:400px;width:100%;margin-left:auto;margin-right:auto;background-color:var(--ion-card-background-color);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}ion-content .history-container ion-card ion-card-header ion-card-subtitle{height:40px}ion-content .history-container ion-card ion-card-header ion-card-subtitle button[float-start]{--mdc-icon-button-state-layer-size: 40px;padding:8px;z-index:99;margin:0}ion-content .history-container ion-card ion-card-header ion-card-subtitle ion-label{line-height:40px}ion-content .history-container ion-card ion-card-header ion-card-subtitle ion-label[float-end]{padding-right:16px;font-weight:400}ion-content .history-container ion-card ion-card-header ion-card-title ion-item{width:100%;--ion-item-background: $transparent;--ion-item-icon-color: var(--ion-color-primary);--ion-item-text-color: var(--ion-color-primary)}ion-content .history-container ion-card ion-card-header ion-card-title ion-item ion-icon[slot=start],ion-content .history-container ion-card ion-card-header ion-card-title ion-item mat-icon[slot=start]{margin-right:unset;margin-inline-end:16px!important;color:var(--ion-item-icon-color)!important;fill:currentColor;stroke:currentColor}ion-content .history-container ion-card ion-card-header ion-card-title ion-item ion-text,ion-content .history-container ion-card ion-card-header ion-card-title ion-item ion-label{color:var(--ion-item-text-color)!important;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}ion-content .history-container ion-card ion-card-content ion-list{--ion-item-background: var(--ion-color-transparent)}ion-content .history-container ion-card ion-card-content ion-list ion-item{--ion-item-icon-color: var(--ion-color-dark);--ion-item-text-color: var(--ion-color-dark)}ion-content .history-container ion-card ion-card-content ion-list ion-item ion-icon[slot=start],ion-content .history-container ion-card ion-card-content ion-list ion-item mat-icon[slot=start]{color:var(--ion-item-icon-color)!important;fill:currentColor;stroke:currentColor;margin-right:unset;margin-inline-end:16px!important}ion-content .history-container ion-card ion-card-content ion-list ion-item ion-text,ion-content .history-container ion-card ion-card-content ion-list ion-item ion-label{color:var(--ion-item-text-color)!important}ion-content .bottom-banner{--ion-card-background-color: rgba(var(--ion-background-color-rgb), .5);background-color:var(--ion-card-background-color);min-width:240px;z-index:0;border-radius:4px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin-left:16px;margin-right:16px;margin-bottom:16px}ion-content .bottom-banner img.logo{max-height:50px;margin-left:8px}ion-content .bottom-banner ion-list-header{min-height:30px}ion-content .bottom-banner ion-item{--min-height: 28px}ion-content .bottom-banner ion-item ion-label{margin-top:3px!important;margin-bottom:3px!important}ion-content ion-text{text-align:center}@media screen and (max-width: 575px){ion-content ion-card{min-width:160px;width:calc(100% - 20px);max-width:100%;margin-left:10px;margin-right:10px}ion-content ion-card h1{margin-top:0;font-size:20px}ion-content ion-card ion-card-content ion-button{margin-top:8px}ion-content .bottom-banner.floating{display:block;position:unset}ion-content .bottom-banner img.logo{max-height:30px}}@media screen and (max-width: 767px) and (min-width: 576px){ion-content{min-height:555px}ion-content .bottom-banner img.logo{max-height:40px}}@media screen and (min-width: 768px){ion-content{min-height:calc(100% - 100px)}}@media screen and (min-width: 1200px){ion-content ion-col.feed{min-height:calc(100vh - var(--ion-toolbar-height) - 138px)}ion-content .bottom-banner.floating{position:absolute;left:0;right:0;bottom:0}}.content-credits{position:absolute;right:0;bottom:0}.content-credits ion-text{padding-inline-start:4px;padding-inline-end:4px;font-size:.8rem;background-color:var(--ion-background-color, white);color:var(--ion-text-color);opacity:.65}@media screen and (min-width: 992px){.left-border{border-left:1px solid var(--ion-color-medium)}}\n"] }]
|
|
33649
34144
|
}], ctorParameters: () => [{ type: AccountService }, { type: i2$1.ModalController }, { type: i1$1.TranslateService }, { type: i2$1.ToastController }, { type: ConfigService }, { type: PlatformService }, { type: i0.ChangeDetectorRef }, { type: NetworkService }, { type: LocalSettingsService }, { type: Environment, decorators: [{
|
|
33650
34145
|
type: Inject,
|
|
33651
34146
|
args: [ENVIRONMENT]
|
|
@@ -33829,6 +34324,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
33829
34324
|
}]
|
|
33830
34325
|
}] });
|
|
33831
34326
|
|
|
34327
|
+
class FeedModule {
|
|
34328
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
34329
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: FeedModule, declarations: [FeedsComponent, FeedDirective], imports: [SharedModule, i1$1.TranslateModule, NgOptimizedImage], exports: [TranslateModule, FeedsComponent, FeedDirective] });
|
|
34330
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedModule, imports: [SharedModule, TranslateModule.forChild(), TranslateModule] });
|
|
34331
|
+
}
|
|
34332
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedModule, decorators: [{
|
|
34333
|
+
type: NgModule,
|
|
34334
|
+
args: [{
|
|
34335
|
+
imports: [SharedModule, TranslateModule.forChild(), NgOptimizedImage],
|
|
34336
|
+
declarations: [FeedsComponent, FeedDirective],
|
|
34337
|
+
exports: [TranslateModule, FeedsComponent, FeedDirective],
|
|
34338
|
+
}]
|
|
34339
|
+
}] });
|
|
34340
|
+
|
|
33832
34341
|
class AppHomePageModule {
|
|
33833
34342
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AppHomePageModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
33834
34343
|
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: AppHomePageModule, declarations: [HomePage], imports: [SharedModule,
|
|
@@ -33837,7 +34346,8 @@ class AppHomePageModule {
|
|
|
33837
34346
|
AppAuthModule,
|
|
33838
34347
|
AppInstallUpgradeCardModule,
|
|
33839
34348
|
SharedMarkdownModule,
|
|
33840
|
-
UserEventModule
|
|
34349
|
+
UserEventModule,
|
|
34350
|
+
FeedModule], exports: [RouterModule, TranslateModule, HomePage] });
|
|
33841
34351
|
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AppHomePageModule, imports: [SharedModule,
|
|
33842
34352
|
RouterModule,
|
|
33843
34353
|
TranslateModule.forChild(),
|
|
@@ -33846,7 +34356,8 @@ class AppHomePageModule {
|
|
|
33846
34356
|
AppAuthModule,
|
|
33847
34357
|
AppInstallUpgradeCardModule,
|
|
33848
34358
|
SharedMarkdownModule,
|
|
33849
|
-
UserEventModule,
|
|
34359
|
+
UserEventModule,
|
|
34360
|
+
FeedModule, RouterModule, TranslateModule] });
|
|
33850
34361
|
}
|
|
33851
34362
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AppHomePageModule, decorators: [{
|
|
33852
34363
|
type: NgModule,
|
|
@@ -33861,6 +34372,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
33861
34372
|
AppInstallUpgradeCardModule,
|
|
33862
34373
|
SharedMarkdownModule,
|
|
33863
34374
|
UserEventModule,
|
|
34375
|
+
FeedModule,
|
|
33864
34376
|
],
|
|
33865
34377
|
declarations: [HomePage],
|
|
33866
34378
|
exports: [RouterModule, TranslateModule, HomePage],
|
|
@@ -44296,6 +44808,167 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
44296
44808
|
args: [ENVIRONMENT]
|
|
44297
44809
|
}] }] });
|
|
44298
44810
|
|
|
44811
|
+
class DiscourseFeedService extends StartableService {
|
|
44812
|
+
settings;
|
|
44813
|
+
environment;
|
|
44814
|
+
discourseApis = new Map();
|
|
44815
|
+
_logPrefix = '[discourse-feed-service] ';
|
|
44816
|
+
_state = new RxState();
|
|
44817
|
+
network = inject(NetworkService);
|
|
44818
|
+
locale$;
|
|
44819
|
+
feedUrls$;
|
|
44820
|
+
locale;
|
|
44821
|
+
feedUrls;
|
|
44822
|
+
constructor(settings, environment) {
|
|
44823
|
+
super(settings);
|
|
44824
|
+
this.settings = settings;
|
|
44825
|
+
this.environment = environment;
|
|
44826
|
+
this._debug = !environment.production;
|
|
44827
|
+
if (this._debug)
|
|
44828
|
+
console.debug('[discourse-feed-service] Creating service');
|
|
44829
|
+
this._state.connect('locale', this.settings.locale$);
|
|
44830
|
+
this._state.connect('feedUrls', this.locale$.pipe(filter(isNotNilOrBlank), map((locale) => (locale && environment.feed?.jsonFeed?.[locale]) ?? [])));
|
|
44831
|
+
}
|
|
44832
|
+
async ngOnStart() {
|
|
44833
|
+
await this.settings.ready();
|
|
44834
|
+
console.debug(`${this._logPrefix}Service started`, this.settings.locale, this.feedUrls);
|
|
44835
|
+
return {
|
|
44836
|
+
locale: this.settings.locale,
|
|
44837
|
+
feedUrls: this.feedUrls,
|
|
44838
|
+
};
|
|
44839
|
+
}
|
|
44840
|
+
watchAll(opts) {
|
|
44841
|
+
if (!this.started) {
|
|
44842
|
+
return from(this.start()).pipe(switchMap$1(() => this.watchAll(opts)));
|
|
44843
|
+
}
|
|
44844
|
+
return this.feedUrls$.pipe(filter(isNotEmptyArray), mergeMap((urls) => this.loadAll(urls, opts)));
|
|
44845
|
+
}
|
|
44846
|
+
getHomeUrl(feed) {
|
|
44847
|
+
const feedUrl = feed?.feed_url ?? firstArrayValue(this.feedUrls);
|
|
44848
|
+
return feedUrl ? UrlUtils.getRootUrl(feedUrl) : undefined;
|
|
44849
|
+
}
|
|
44850
|
+
getTagUrl(feed, tag) {
|
|
44851
|
+
if (!feed || !tag)
|
|
44852
|
+
throw new Error("Missing 'feed' or 'tag' argument");
|
|
44853
|
+
const baseUrl = this.getHomeUrl(feed);
|
|
44854
|
+
return feed.tag_template?.replace('{tag}', tag) ?? baseUrl + '/tag/' + tag;
|
|
44855
|
+
}
|
|
44856
|
+
async load(url, opts) {
|
|
44857
|
+
const { data } = await this.loadAll([url], opts);
|
|
44858
|
+
return data?.[0];
|
|
44859
|
+
}
|
|
44860
|
+
async loadAll(urls, opts) {
|
|
44861
|
+
await this.ready();
|
|
44862
|
+
urls = urls ?? this.feedUrls;
|
|
44863
|
+
opts = {
|
|
44864
|
+
maxAgeInMonths: opts?.maxAgeInMonths ?? this.environment.feed?.maxAgeInMonths,
|
|
44865
|
+
maxContentLength: opts?.maxContentLength ?? this.environment.feed?.maxContentLength,
|
|
44866
|
+
locale: this.settings.locale,
|
|
44867
|
+
depth: 0,
|
|
44868
|
+
closeApis: true,
|
|
44869
|
+
...opts,
|
|
44870
|
+
};
|
|
44871
|
+
const feeds = await Promise$1.all((urls || []).map(async (url) => {
|
|
44872
|
+
try {
|
|
44873
|
+
// Get JSON
|
|
44874
|
+
const json = await this.network.fetch(url);
|
|
44875
|
+
console.debug(`${this._logPrefix}Loaded JSON from ${url}`, json);
|
|
44876
|
+
if (JsonFeedUtils.isJsonFeed(json)) {
|
|
44877
|
+
// Migrate old version
|
|
44878
|
+
let feed = JsonFeedUtils.migrateFeed(json);
|
|
44879
|
+
// Resolve items (e.g. using feed.next_url)
|
|
44880
|
+
feed = await this.resolveFeedItems(feed, { ...opts, depth: opts?.depth ?? 0 });
|
|
44881
|
+
// Truncate html
|
|
44882
|
+
JsonFeedUtils.truncateFeedItemsHtml(feed, opts);
|
|
44883
|
+
return isNotEmptyArray(feed?.items) ? [feed] : [];
|
|
44884
|
+
}
|
|
44885
|
+
// Get discourse
|
|
44886
|
+
if (DiscourseUtils.isDiscourseObject(json)) {
|
|
44887
|
+
const api = this.getOrCreateDiscourseApi(url);
|
|
44888
|
+
return await DiscourseUtils.getFeedsByUrl(url, { ...opts, api });
|
|
44889
|
+
}
|
|
44890
|
+
return undefined;
|
|
44891
|
+
}
|
|
44892
|
+
catch (err) {
|
|
44893
|
+
if (err?.status === 404) {
|
|
44894
|
+
console.error(`${this._logPrefix} Error while fetching feed at ${url}. 404 (Not found)`);
|
|
44895
|
+
}
|
|
44896
|
+
else {
|
|
44897
|
+
console.error(`${this._logPrefix} Error while fetching feed at ${url}`, err);
|
|
44898
|
+
}
|
|
44899
|
+
return Promise$1.resolve([]);
|
|
44900
|
+
}
|
|
44901
|
+
}));
|
|
44902
|
+
if (opts?.closeApis !== false) {
|
|
44903
|
+
this.closeApis();
|
|
44904
|
+
}
|
|
44905
|
+
const data = feeds.flat();
|
|
44906
|
+
return { data, total: data.length };
|
|
44907
|
+
}
|
|
44908
|
+
getOrCreateDiscourseApi(url) {
|
|
44909
|
+
const baseUrl = DiscourseUtils.getDiscourseBaseUrl(url);
|
|
44910
|
+
let api = this.discourseApis.get(baseUrl);
|
|
44911
|
+
if (!api) {
|
|
44912
|
+
api = new DiscourseAPI(baseUrl);
|
|
44913
|
+
this.discourseApis.set(baseUrl, api);
|
|
44914
|
+
}
|
|
44915
|
+
return api;
|
|
44916
|
+
}
|
|
44917
|
+
closeApis() {
|
|
44918
|
+
this.discourseApis.clear();
|
|
44919
|
+
}
|
|
44920
|
+
async resolveFeedItems(json, opts) {
|
|
44921
|
+
if (!json)
|
|
44922
|
+
return json;
|
|
44923
|
+
// Load items
|
|
44924
|
+
if (isEmptyArray(json.items) && isNotNilOrBlank(json.next_url)) {
|
|
44925
|
+
const depth = opts?.depth ?? 0;
|
|
44926
|
+
if (depth >= 3) {
|
|
44927
|
+
console.warn(`${this._logPrefix}Max depth reached (${depth}) for ${json.feed_url}`);
|
|
44928
|
+
return json;
|
|
44929
|
+
}
|
|
44930
|
+
// Load items
|
|
44931
|
+
const feed = await this.load(json.next_url, { ...opts, closeApis: false, depth: depth + 1 });
|
|
44932
|
+
if (isEmptyArray(feed?.items)) {
|
|
44933
|
+
return json;
|
|
44934
|
+
}
|
|
44935
|
+
// Merge with the input json
|
|
44936
|
+
return {
|
|
44937
|
+
...json,
|
|
44938
|
+
title: json.title ?? feed.title,
|
|
44939
|
+
home_page_url: json.home_page_url ?? feed.home_page_url,
|
|
44940
|
+
authors: isNotEmptyArray(json.authors) ? json.authors : feed.authors,
|
|
44941
|
+
items: feed.items,
|
|
44942
|
+
tag_template: json.tag_template ?? feed.tag_template,
|
|
44943
|
+
};
|
|
44944
|
+
}
|
|
44945
|
+
return json;
|
|
44946
|
+
}
|
|
44947
|
+
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 });
|
|
44948
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DiscourseFeedService, providedIn: 'root' });
|
|
44949
|
+
}
|
|
44950
|
+
__decorate([
|
|
44951
|
+
RxStateSelect()
|
|
44952
|
+
], DiscourseFeedService.prototype, "locale$", void 0);
|
|
44953
|
+
__decorate([
|
|
44954
|
+
RxStateSelect()
|
|
44955
|
+
], DiscourseFeedService.prototype, "feedUrls$", void 0);
|
|
44956
|
+
__decorate([
|
|
44957
|
+
RxStateProperty()
|
|
44958
|
+
], DiscourseFeedService.prototype, "locale", void 0);
|
|
44959
|
+
__decorate([
|
|
44960
|
+
RxStateProperty()
|
|
44961
|
+
], DiscourseFeedService.prototype, "feedUrls", void 0);
|
|
44962
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DiscourseFeedService, decorators: [{
|
|
44963
|
+
type: Injectable,
|
|
44964
|
+
args: [{ providedIn: 'root' }]
|
|
44965
|
+
}], ctorParameters: () => [{ type: LocalSettingsService }, { type: Environment, decorators: [{
|
|
44966
|
+
type: Optional
|
|
44967
|
+
}, {
|
|
44968
|
+
type: Inject,
|
|
44969
|
+
args: [ENVIRONMENT]
|
|
44970
|
+
}] }], propDecorators: { locale$: [], feedUrls$: [], locale: [], feedUrls: [] } });
|
|
44971
|
+
|
|
44299
44972
|
const PersonFragments = {
|
|
44300
44973
|
person: gql$1 `
|
|
44301
44974
|
fragment PersonFragment on PersonVO {
|
|
@@ -49087,5 +49760,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
49087
49760
|
* Generated bundle index. Do not edit.
|
|
49088
49761
|
*/
|
|
49089
49762
|
|
|
49090
|
-
export { APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_CONFIG_OPTIONS, APP_DEBUG_DATA_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, 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, 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, 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 };
|
|
49763
|
+
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 };
|
|
49091
49764
|
//# sourceMappingURL=sumaris-net.ngx-components.mjs.map
|