@sumaris-net/ngx-components 18.16.8 → 18.17.0
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/esm2022/public_api.mjs +4 -4
- package/esm2022/src/app/admin/users/person.filter.mjs +76 -0
- package/esm2022/src/app/admin/users/person.service.mjs +197 -0
- package/esm2022/src/app/admin/users/person.validator.mjs +72 -0
- package/esm2022/src/app/admin/users/users.mjs +4 -4
- package/esm2022/src/app/core/about/about.modal.mjs +5 -5
- package/esm2022/src/app/core/home/home.mjs +3 -3
- package/esm2022/src/app/core/services/account.service.mjs +8 -2
- package/esm2022/src/app/core/services/network.service.mjs +5 -2
- package/esm2022/src/app/core/services/validator/account.validator.mjs +2 -2
- package/esm2022/src/app/shared/validator/validators.mjs +3 -3
- package/esm2022/src/app/social/feed/feed.component.mjs +117 -30
- package/esm2022/src/app/social/feed/feed.model.mjs +1 -1
- package/esm2022/src/app/social/feed/feed.page.mjs +3 -3
- package/esm2022/src/app/social/feed/feed.service.mjs +63 -12
- package/esm2022/src/app/social/feed/testing/feed.testing.mjs +3 -3
- package/esm2022/src/app/social/message/message.form.mjs +8 -5
- package/esm2022/src/app/social/message/message.modal.mjs +10 -4
- package/esm2022/src/app/social/message/message.model.mjs +2 -2
- package/esm2022/src/app/social/message/message.module.mjs +5 -4
- package/esm2022/src/app/social/message/message.service.mjs +81 -1
- package/esm2022/src/app/social/social.errors.mjs +2 -1
- package/esm2022/src/environments/environment.mjs +7 -4
- package/fesm2022/sumaris-net.ngx-components.mjs +1301 -1060
- package/fesm2022/sumaris-net.ngx-components.mjs.map +1 -1
- package/package.json +1 -1
- package/public_api.d.ts +3 -3
- package/src/app/admin/{services/filter → users}/person.filter.d.ts +4 -4
- package/src/app/admin/{services → users}/person.service.d.ts +2 -1
- package/src/app/admin/{services/validator → users}/person.validator.d.ts +4 -4
- package/src/app/admin/users/users.d.ts +2 -2
- package/src/app/core/services/account.service.d.ts +2 -0
- package/src/app/core/services/network.service.d.ts +3 -2
- package/src/app/core/services/validator/account.validator.d.ts +1 -1
- package/src/app/social/feed/feed.component.d.ts +36 -14
- package/src/app/social/feed/feed.model.d.ts +3 -2
- package/src/app/social/feed/feed.service.d.ts +35 -24
- package/src/app/social/message/message.form.d.ts +3 -3
- package/src/app/social/message/message.modal.d.ts +6 -2
- package/src/app/social/message/message.model.d.ts +1 -1
- package/src/app/social/message/message.module.d.ts +2 -1
- package/src/app/social/message/message.service.d.ts +12 -0
- package/src/app/social/social.errors.d.ts +1 -0
- package/src/assets/i18n/en-US.json +7 -3
- package/src/assets/i18n/en.json +5 -2
- package/src/assets/i18n/fr.json +5 -2
- package/src/assets/manifest.json +1 -1
- package/src/theme/_ngx-components.forms.scss +1 -0
- package/src/theme/_ngx-components.table.scss +1 -0
- package/esm2022/src/app/admin/services/filter/person.filter.mjs +0 -76
- package/esm2022/src/app/admin/services/person.service.mjs +0 -184
- package/esm2022/src/app/admin/services/validator/person.validator.mjs +0 -72
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { InjectionToken, Directive, Pipe, Injectable, EventEmitter, Output, Optional, Inject, inject, NgModule, forwardRef, booleanAttribute, Component, ChangeDetectionStrategy, Input, ViewChildren, HostBinding, HostListener, ElementRef, numberAttribute, ViewChild, ANIMATION_MODULE_TYPE, RendererStyleFlags2, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectorRef, Self, ViewEncapsulation, APP_INITIALIZER, SecurityContext, viewChild, signal, input, model, computed, effect, untracked } from '@angular/core';
|
|
3
3
|
import { firstValueFrom, shareReplay, tap, of, timer, Subject, merge, delay, isObservable, from, Subscription, BehaviorSubject, fromEvent, noop as noop$9, Observable, forkJoin, timeout, defer, combineLatest, debounceTime as debounceTime$1, distinctUntilChanged as distinctUntilChanged$1, interval, mergeMap as mergeMap$1, switchMap as switchMap$1, EMPTY } from 'rxjs';
|
|
4
|
-
import { catchError, map, filter, takeUntil, first, switchMap, tap as tap$1, throttleTime, debounceTime, startWith, distinctUntilChanged, mergeMap, skip, finalize,
|
|
4
|
+
import { catchError, map, filter, takeUntil, first, switchMap, tap as tap$1, throttleTime, debounceTime, startWith, distinctUntilChanged, mergeMap, skip, finalize, distinctUntilKeyChanged, bufferWhen, take } from 'rxjs/operators';
|
|
5
5
|
import * as cloneImported from 'clone';
|
|
6
6
|
import * as i3$1 from '@angular/common';
|
|
7
7
|
import { CommonModule, DOCUMENT, LocationStrategy, Location, AsyncPipe, JsonPipe } from '@angular/common';
|
|
@@ -2871,14 +2871,14 @@ class SharedValidators {
|
|
|
2871
2871
|
}
|
|
2872
2872
|
static entity(control) {
|
|
2873
2873
|
const value = control.value;
|
|
2874
|
-
if (value && (typeof value !== 'object' || value.id
|
|
2874
|
+
if (value && (typeof value !== 'object' || isNil(value.id))) {
|
|
2875
2875
|
return { entity: true };
|
|
2876
2876
|
}
|
|
2877
2877
|
return null;
|
|
2878
2878
|
}
|
|
2879
2879
|
static entities(control) {
|
|
2880
2880
|
const value = control.value;
|
|
2881
|
-
if (value && (!Array.isArray(value) || value.every((e) => e?.id
|
|
2881
|
+
if (value && (!Array.isArray(value) || value.every((e) => isNil(e?.id)))) {
|
|
2882
2882
|
return { entity: true };
|
|
2883
2883
|
}
|
|
2884
2884
|
return null;
|
|
@@ -13138,11 +13138,14 @@ const environment = Object.freeze({
|
|
|
13138
13138
|
'/api/feed.json',
|
|
13139
13139
|
'https://gitlab.ifremer.fr/sih-public/sumaris/ngx-sumaris-components/-/raw/master/doc/feed/feed-fr.json',
|
|
13140
13140
|
// Example with JsonFeed version 1 (and not 1.1)
|
|
13141
|
-
'https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/feed-fr.json',
|
|
13141
|
+
//'https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/feed-fr.json',
|
|
13142
13142
|
// Example with discourse API :
|
|
13143
|
-
'https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/1.1/feed-fr-FR.json',
|
|
13143
|
+
//'https://raw.githubusercontent.com/duniter/cesium/master/doc/feed/1.1/feed-fr-FR.json',
|
|
13144
|
+
],
|
|
13145
|
+
en: [
|
|
13146
|
+
'/api/feed.json',
|
|
13147
|
+
'https://gitlab.ifremer.fr/sih-public/sumaris/ngx-sumaris-components/-/raw/master/doc/feed/feed-en.json'
|
|
13144
13148
|
],
|
|
13145
|
-
en: ['https://gitlab.ifremer.fr/sih-public/sumaris/ngx-sumaris-components/-/raw/master/doc/feed/feed-en.json'],
|
|
13146
13149
|
},
|
|
13147
13150
|
maxContentLength: 1000,
|
|
13148
13151
|
maxAgeInMonths: -1,
|
|
@@ -19713,6 +19716,9 @@ class NetworkService extends StartableObservableService {
|
|
|
19713
19716
|
get connectionParams() {
|
|
19714
19717
|
return this._connectionParams;
|
|
19715
19718
|
}
|
|
19719
|
+
get peer$() {
|
|
19720
|
+
return this.dataSubject.pipe(distinctUntilKeyChanged('url'));
|
|
19721
|
+
}
|
|
19716
19722
|
constructor(_document, platform, modalCtrl, storage, settings, cache, http, environment, loggingService, translate, toastController) {
|
|
19717
19723
|
super(platform);
|
|
19718
19724
|
this._document = _document;
|
|
@@ -23014,7 +23020,7 @@ const Fragments = {
|
|
|
23014
23020
|
`,
|
|
23015
23021
|
};
|
|
23016
23022
|
// Account queries
|
|
23017
|
-
const Queries = {
|
|
23023
|
+
const Queries$1 = {
|
|
23018
23024
|
load: gql `
|
|
23019
23025
|
query Account {
|
|
23020
23026
|
data: account {
|
|
@@ -23176,12 +23182,18 @@ class AccountService extends BaseGraphqlService {
|
|
|
23176
23182
|
get account() {
|
|
23177
23183
|
return this._cache.loaded ? this._data : undefined;
|
|
23178
23184
|
}
|
|
23185
|
+
get account$() {
|
|
23186
|
+
return this.onChange.asObservable().pipe(startWith(this.account));
|
|
23187
|
+
}
|
|
23179
23188
|
get person() {
|
|
23180
23189
|
if (this._cache.loaded && !this._cache.person) {
|
|
23181
23190
|
this._cache.person = this._cache.loaded ? this._data.asPerson() : undefined;
|
|
23182
23191
|
}
|
|
23183
23192
|
return this._cache.person;
|
|
23184
23193
|
}
|
|
23194
|
+
get person$() {
|
|
23195
|
+
return this.onChange.asObservable().pipe(map(() => this.person), startWith(this.person));
|
|
23196
|
+
}
|
|
23185
23197
|
get department() {
|
|
23186
23198
|
if (this._cache.loaded && !this._cache.department) {
|
|
23187
23199
|
this._cache.department = this._cache.loaded ? this._data.asPerson().department : undefined;
|
|
@@ -23610,7 +23622,7 @@ class AccountService extends BaseGraphqlService {
|
|
|
23610
23622
|
}
|
|
23611
23623
|
// Load remotely
|
|
23612
23624
|
else {
|
|
23613
|
-
const query = opts?.query || (this.apiTokenEnabled ? Queries.loadWithTokens : Queries.load);
|
|
23625
|
+
const query = opts?.query || (this.apiTokenEnabled ? Queries$1.loadWithTokens : Queries$1.load);
|
|
23614
23626
|
const { data } = await this.graphql.query({
|
|
23615
23627
|
query,
|
|
23616
23628
|
error: { code: ErrorCodes.LOAD_ACCOUNT_ERROR, message: 'ERROR.LOAD_ACCOUNT_ERROR' },
|
|
@@ -30020,10 +30032,10 @@ class AboutModal {
|
|
|
30020
30032
|
}
|
|
30021
30033
|
/* -- protected functions -- */
|
|
30022
30034
|
setConfig(config) {
|
|
30023
|
-
this.forumUrl = config?.getProperty(CORE_CONFIG_OPTIONS.FORUM_URL) || this.environment
|
|
30024
|
-
this.helpUrl = config?.getProperty(CORE_CONFIG_OPTIONS.HELP_URL) || this.environment
|
|
30025
|
-
this.reportIssueUrl = config?.getProperty(CORE_CONFIG_OPTIONS.REPORT_ISSUE_URL) || this.environment
|
|
30026
|
-
this.buildDate = this.environment
|
|
30035
|
+
this.forumUrl = config?.getProperty(CORE_CONFIG_OPTIONS.FORUM_URL) || this.environment?.forumUrl;
|
|
30036
|
+
this.helpUrl = config?.getProperty(CORE_CONFIG_OPTIONS.HELP_URL) || this.environment?.helpUrl;
|
|
30037
|
+
this.reportIssueUrl = config?.getProperty(CORE_CONFIG_OPTIONS.REPORT_ISSUE_URL) || this.environment?.reportIssueUrl;
|
|
30038
|
+
this.buildDate = this.environment?.buildDate;
|
|
30027
30039
|
}
|
|
30028
30040
|
async loadNodeInfo() {
|
|
30029
30041
|
this.nodeInfo = await this.getNodeInfo();
|
|
@@ -32720,6 +32732,7 @@ const SocialErrorCodes = {
|
|
|
32720
32732
|
SAVE_USER_EVENT_ERROR: 50001,
|
|
32721
32733
|
COUNT_USER_EVENT_ERROR: 50002,
|
|
32722
32734
|
SEND_MESSAGE_ERROR: 50003,
|
|
32735
|
+
DELETE_MESSAGE_ERROR: 50004,
|
|
32723
32736
|
SUBSCRIBE_USER_EVENTS_ERROR: 50005,
|
|
32724
32737
|
SUBSCRIBE_JOB_PROGRESSION_ERROR: 50010,
|
|
32725
32738
|
LOAD_JOB_PROGRESSIONS_ERROR: 50011,
|
|
@@ -33781,297 +33794,1063 @@ class JsonFeedUtils {
|
|
|
33781
33794
|
}
|
|
33782
33795
|
}
|
|
33783
33796
|
|
|
33784
|
-
|
|
33785
|
-
|
|
33786
|
-
|
|
33787
|
-
|
|
33788
|
-
|
|
33789
|
-
|
|
33790
|
-
|
|
33791
|
-
locale$ = this._state.select('locale');
|
|
33792
|
-
feedUrls$ = this._state.select('feedUrls');
|
|
33793
|
-
get feedUrls() {
|
|
33794
|
-
return this._state.get('feedUrls');
|
|
33797
|
+
var PersonFilter_1;
|
|
33798
|
+
// @dynamic
|
|
33799
|
+
let PersonFilter = class PersonFilter extends EntityFilter {
|
|
33800
|
+
static { PersonFilter_1 = this; }
|
|
33801
|
+
static fromObject;
|
|
33802
|
+
static searchFilter(source) {
|
|
33803
|
+
return source && PersonFilter_1.fromObject(source).asFilterFn();
|
|
33795
33804
|
}
|
|
33796
|
-
|
|
33797
|
-
|
|
33805
|
+
email;
|
|
33806
|
+
pubkey;
|
|
33807
|
+
searchText;
|
|
33808
|
+
statusIds;
|
|
33809
|
+
userProfiles;
|
|
33810
|
+
includedIds;
|
|
33811
|
+
excludedIds;
|
|
33812
|
+
searchAttribute;
|
|
33813
|
+
searchAttributes;
|
|
33814
|
+
constructor() {
|
|
33815
|
+
super(PersonFilter_1.TYPENAME);
|
|
33798
33816
|
}
|
|
33799
|
-
|
|
33800
|
-
super(
|
|
33801
|
-
this.
|
|
33802
|
-
this.
|
|
33803
|
-
this.
|
|
33804
|
-
this.
|
|
33805
|
-
|
|
33806
|
-
this.
|
|
33807
|
-
|
|
33808
|
-
|
|
33817
|
+
fromObject(source, opts) {
|
|
33818
|
+
super.fromObject(source, opts);
|
|
33819
|
+
this.email = source.email;
|
|
33820
|
+
this.pubkey = source.pubkey;
|
|
33821
|
+
this.searchText = source.searchText;
|
|
33822
|
+
this.statusIds = source.statusIds || (isNotNil(source.statusId) ? [source.statusId] : undefined);
|
|
33823
|
+
this.userProfiles = source.userProfiles;
|
|
33824
|
+
this.excludedIds = source.excludedIds;
|
|
33825
|
+
this.searchAttribute = source.searchAttribute;
|
|
33826
|
+
this.searchAttributes = source.searchAttributes;
|
|
33809
33827
|
}
|
|
33810
|
-
|
|
33811
|
-
|
|
33812
|
-
|
|
33813
|
-
|
|
33814
|
-
|
|
33828
|
+
asObject(opts) {
|
|
33829
|
+
const target = super.asObject(opts);
|
|
33830
|
+
target.email = this.email;
|
|
33831
|
+
target.pubkey = this.pubkey;
|
|
33832
|
+
target.searchText = this.searchText;
|
|
33833
|
+
target.statusIds = this.statusIds;
|
|
33834
|
+
target.userProfiles = this.userProfiles;
|
|
33835
|
+
target.excludedIds = this.excludedIds;
|
|
33836
|
+
target.searchAttribute = this.searchAttribute;
|
|
33837
|
+
target.searchAttributes = this.searchAttributes;
|
|
33838
|
+
return target;
|
|
33815
33839
|
}
|
|
33816
|
-
|
|
33817
|
-
|
|
33818
|
-
|
|
33840
|
+
buildFilter() {
|
|
33841
|
+
const filterFns = super.buildFilter();
|
|
33842
|
+
// Filter by status
|
|
33843
|
+
if (isNotEmptyArray(this.statusIds)) {
|
|
33844
|
+
filterFns.push((e) => this.statusIds.includes(e.statusId));
|
|
33819
33845
|
}
|
|
33820
|
-
|
|
33821
|
-
|
|
33846
|
+
// Filter included ids
|
|
33847
|
+
const includedIds = this.includedIds;
|
|
33848
|
+
if (isNotEmptyArray(includedIds)) {
|
|
33849
|
+
filterFns.push((e) => isNotNil(e.id) && includedIds.includes(e.id));
|
|
33822
33850
|
}
|
|
33823
|
-
|
|
33824
|
-
|
|
33825
|
-
|
|
33826
|
-
|
|
33827
|
-
|
|
33828
|
-
|
|
33829
|
-
|
|
33830
|
-
if (
|
|
33831
|
-
|
|
33832
|
-
|
|
33833
|
-
return feed.tag_template?.replace('{tag}', tag) ?? baseUrl + '/tag/' + tag;
|
|
33851
|
+
// Filter excluded ids
|
|
33852
|
+
const excludedIds = this.excludedIds;
|
|
33853
|
+
if (isNotEmptyArray(excludedIds)) {
|
|
33854
|
+
filterFns.push((e) => isNil(e.id) || !excludedIds.includes(e.id));
|
|
33855
|
+
}
|
|
33856
|
+
// Search text
|
|
33857
|
+
const searchTextFilter = EntityUtils.searchTextFilter(this.searchAttribute || this.searchAttributes || ['lastName', 'firstName', 'department.name'], this.searchText);
|
|
33858
|
+
if (searchTextFilter)
|
|
33859
|
+
filterFns.push(searchTextFilter);
|
|
33860
|
+
return filterFns;
|
|
33834
33861
|
}
|
|
33835
|
-
|
|
33836
|
-
|
|
33837
|
-
|
|
33862
|
+
};
|
|
33863
|
+
PersonFilter = PersonFilter_1 = __decorate([
|
|
33864
|
+
EntityClass({ typename: 'PersonFilterVO' })
|
|
33865
|
+
], PersonFilter);
|
|
33866
|
+
|
|
33867
|
+
const MessageTypes = {
|
|
33868
|
+
INBOX_MESSAGE: 'INBOX_MESSAGE',
|
|
33869
|
+
EMAIL: 'EMAIL',
|
|
33870
|
+
FEED: 'FEED',
|
|
33871
|
+
};
|
|
33872
|
+
const MessageTypeList = Object.freeze([
|
|
33873
|
+
{
|
|
33874
|
+
id: MessageTypes.INBOX_MESSAGE,
|
|
33875
|
+
icon: 'notifications',
|
|
33876
|
+
label: 'SOCIAL.MESSAGE.TYPE_ENUM.INBOX_MESSAGE',
|
|
33877
|
+
},
|
|
33878
|
+
{
|
|
33879
|
+
id: MessageTypes.EMAIL,
|
|
33880
|
+
icon: 'mail',
|
|
33881
|
+
label: 'SOCIAL.MESSAGE.TYPE_ENUM.EMAIL',
|
|
33882
|
+
},
|
|
33883
|
+
{
|
|
33884
|
+
id: MessageTypes.FEED,
|
|
33885
|
+
icon: 'megaphone',
|
|
33886
|
+
label: 'SOCIAL.MESSAGE.TYPE_ENUM.FEED',
|
|
33887
|
+
},
|
|
33888
|
+
]);
|
|
33889
|
+
// @dynamic
|
|
33890
|
+
let Message = class Message extends Entity {
|
|
33891
|
+
static fromObject;
|
|
33892
|
+
type;
|
|
33893
|
+
issuer;
|
|
33894
|
+
recipients;
|
|
33895
|
+
recipientFilter;
|
|
33896
|
+
subject;
|
|
33897
|
+
body;
|
|
33898
|
+
constructor() {
|
|
33899
|
+
super();
|
|
33838
33900
|
}
|
|
33839
|
-
|
|
33840
|
-
|
|
33841
|
-
|
|
33842
|
-
|
|
33843
|
-
|
|
33844
|
-
|
|
33845
|
-
|
|
33846
|
-
|
|
33847
|
-
...opts,
|
|
33848
|
-
};
|
|
33849
|
-
const feeds = await Promise.all((urls || []).map(async (url) => {
|
|
33850
|
-
try {
|
|
33851
|
-
// Get JSON
|
|
33852
|
-
const json = await this.network.get(url);
|
|
33853
|
-
console.debug(`${this._logPrefix}Loaded JSON from ${url}`, json);
|
|
33854
|
-
// Resolve feed
|
|
33855
|
-
return await this.resolveFeed(json, url, opts);
|
|
33856
|
-
}
|
|
33857
|
-
catch (err) {
|
|
33858
|
-
if (err?.status === 404) {
|
|
33859
|
-
console.error(`${this._logPrefix} Error while fetching feeds at ${url}. 404 (Not found)`);
|
|
33860
|
-
}
|
|
33861
|
-
else {
|
|
33862
|
-
console.error(`${this._logPrefix} Error while fetching feeds at ${url}`, err);
|
|
33863
|
-
}
|
|
33864
|
-
return Promise.resolve([]);
|
|
33865
|
-
}
|
|
33866
|
-
}));
|
|
33867
|
-
const data = feeds.flat();
|
|
33868
|
-
// Close opened resources
|
|
33869
|
-
if (opts.depth === 0) {
|
|
33870
|
-
this.onAfterLoadAll();
|
|
33871
|
-
}
|
|
33872
|
-
return { data, total: data.length };
|
|
33901
|
+
fromObject(source, opts) {
|
|
33902
|
+
super.fromObject(source, opts);
|
|
33903
|
+
this.type = source.type;
|
|
33904
|
+
this.issuer = source.issuer && Person.fromObject(source.issuer);
|
|
33905
|
+
this.recipients = source.recipients && source.recipients.map(Person.fromObject);
|
|
33906
|
+
this.recipientFilter = (source.recipientFilter && PersonFilter.fromObject(source.recipientFilter)) || undefined;
|
|
33907
|
+
this.subject = source.subject;
|
|
33908
|
+
this.body = source.body;
|
|
33873
33909
|
}
|
|
33874
|
-
|
|
33875
|
-
|
|
33876
|
-
|
|
33877
|
-
|
|
33878
|
-
|
|
33879
|
-
// Resolve items (e.g. using feed.next_url)
|
|
33880
|
-
feed = await this.resolveFeedItems(feed, { ...opts, depth: opts?.depth ?? 0 });
|
|
33881
|
-
// Truncate html
|
|
33882
|
-
JsonFeedUtils.truncateFeedItemsHtml(feed, opts);
|
|
33883
|
-
return isNotEmptyArray(feed?.items) ? [feed] : [];
|
|
33884
|
-
}
|
|
33885
|
-
// not resolved (unknown format)
|
|
33886
|
-
return undefined;
|
|
33910
|
+
asObject(opts) {
|
|
33911
|
+
const target = super.asObject(opts);
|
|
33912
|
+
target.recipients = this.recipients && this.recipients.map((p) => p.asObject(opts));
|
|
33913
|
+
target.recipientFilter = (this.recipientFilter && this.recipientFilter.asObject(opts)) || undefined;
|
|
33914
|
+
return target;
|
|
33887
33915
|
}
|
|
33888
|
-
|
|
33889
|
-
|
|
33890
|
-
|
|
33891
|
-
|
|
33892
|
-
|
|
33893
|
-
|
|
33894
|
-
|
|
33895
|
-
|
|
33896
|
-
|
|
33897
|
-
}
|
|
33898
|
-
// Load items
|
|
33899
|
-
const feed = await this.load(json.next_url, { ...opts, depth: depth + 1 });
|
|
33900
|
-
if (isEmptyArray(feed?.items)) {
|
|
33901
|
-
return json;
|
|
33902
|
-
}
|
|
33903
|
-
// Merge with the input json
|
|
33904
|
-
return {
|
|
33905
|
-
...json,
|
|
33906
|
-
title: json.title ?? feed.title,
|
|
33907
|
-
home_page_url: json.home_page_url ?? feed.home_page_url,
|
|
33908
|
-
authors: isNotEmptyArray(json.authors) ? json.authors : feed.authors,
|
|
33909
|
-
items: feed.items,
|
|
33910
|
-
tag_template: json.tag_template ?? feed.tag_template,
|
|
33911
|
-
};
|
|
33912
|
-
}
|
|
33913
|
-
return json;
|
|
33916
|
+
};
|
|
33917
|
+
Message = __decorate([
|
|
33918
|
+
EntityClass({ typename: 'MessageVO' })
|
|
33919
|
+
], Message);
|
|
33920
|
+
// @dynamic
|
|
33921
|
+
let MessageFilter = class MessageFilter extends EntityFilter {
|
|
33922
|
+
static fromObject;
|
|
33923
|
+
fromObject(source, opts) {
|
|
33924
|
+
super.fromObject(source, opts);
|
|
33914
33925
|
}
|
|
33915
|
-
|
|
33916
|
-
|
|
33926
|
+
buildFilter() {
|
|
33927
|
+
return super.buildFilter();
|
|
33917
33928
|
}
|
|
33918
|
-
|
|
33919
|
-
|
|
33920
|
-
}
|
|
33921
|
-
|
|
33922
|
-
type: Injectable
|
|
33923
|
-
}], ctorParameters: () => [{ type: LocalSettingsService }, { type: Environment, decorators: [{
|
|
33924
|
-
type: Optional
|
|
33925
|
-
}, {
|
|
33926
|
-
type: Inject,
|
|
33927
|
-
args: [ENVIRONMENT]
|
|
33928
|
-
}] }] });
|
|
33929
|
+
};
|
|
33930
|
+
MessageFilter = __decorate([
|
|
33931
|
+
EntityClass({ typename: 'MessageFilterVO' })
|
|
33932
|
+
], MessageFilter);
|
|
33929
33933
|
|
|
33930
|
-
|
|
33931
|
-
|
|
33932
|
-
|
|
33933
|
-
|
|
33934
|
-
|
|
33935
|
-
|
|
33936
|
-
|
|
33937
|
-
|
|
33938
|
-
|
|
33939
|
-
|
|
33940
|
-
|
|
33941
|
-
|
|
33942
|
-
|
|
33934
|
+
const PersonFragments = {
|
|
33935
|
+
person: gql$1 `
|
|
33936
|
+
fragment PersonFragment on PersonVO {
|
|
33937
|
+
id
|
|
33938
|
+
firstName
|
|
33939
|
+
lastName
|
|
33940
|
+
email
|
|
33941
|
+
pubkey
|
|
33942
|
+
avatar
|
|
33943
|
+
statusId
|
|
33944
|
+
updateDate
|
|
33945
|
+
creationDate
|
|
33946
|
+
profiles
|
|
33947
|
+
username
|
|
33948
|
+
usernameExtranet
|
|
33949
|
+
department {
|
|
33950
|
+
id
|
|
33951
|
+
label
|
|
33952
|
+
name
|
|
33953
|
+
logo
|
|
33954
|
+
__typename
|
|
33955
|
+
}
|
|
33956
|
+
__typename
|
|
33943
33957
|
}
|
|
33944
|
-
|
|
33945
|
-
|
|
33958
|
+
`,
|
|
33959
|
+
personFilter: gql$1 `
|
|
33960
|
+
fragment PersonFilterFragment on PersonFilterVO {
|
|
33961
|
+
email
|
|
33962
|
+
pubkey
|
|
33963
|
+
searchText
|
|
33964
|
+
statusIds
|
|
33965
|
+
userProfiles
|
|
33966
|
+
includedIds
|
|
33967
|
+
excludedIds
|
|
33968
|
+
searchAttribute
|
|
33969
|
+
searchAttributes
|
|
33946
33970
|
}
|
|
33947
|
-
|
|
33948
|
-
|
|
33949
|
-
|
|
33950
|
-
|
|
33971
|
+
`,
|
|
33972
|
+
};
|
|
33973
|
+
// Load persons query
|
|
33974
|
+
const PersonQueries = {
|
|
33975
|
+
loadAll: gql$1 `
|
|
33976
|
+
query Persons($offset: Int, $size: Int, $sortBy: String, $sortDirection: String, $filter: PersonFilterVOInput) {
|
|
33977
|
+
data: persons(filter: $filter, offset: $offset, size: $size, sortBy: $sortBy, sortDirection: $sortDirection) {
|
|
33978
|
+
...PersonFragment
|
|
33979
|
+
}
|
|
33951
33980
|
}
|
|
33952
|
-
|
|
33953
|
-
|
|
33954
|
-
|
|
33955
|
-
|
|
33981
|
+
${PersonFragments.person}
|
|
33982
|
+
`,
|
|
33983
|
+
loadAllWithTotal: gql$1 `
|
|
33984
|
+
query PersonsWithTotal($offset: Int, $size: Int, $sortBy: String, $sortDirection: String, $filter: PersonFilterVOInput) {
|
|
33985
|
+
data: persons(filter: $filter, offset: $offset, size: $size, sortBy: $sortBy, sortDirection: $sortDirection) {
|
|
33986
|
+
...PersonFragment
|
|
33987
|
+
}
|
|
33988
|
+
total: personsCount(filter: $filter)
|
|
33956
33989
|
}
|
|
33957
|
-
|
|
33958
|
-
|
|
33990
|
+
${PersonFragments.person}
|
|
33991
|
+
`,
|
|
33992
|
+
};
|
|
33993
|
+
const PersonMutations = {
|
|
33994
|
+
saveAll: gql$1 `
|
|
33995
|
+
mutation savePersons($data: [PersonVOInput]) {
|
|
33996
|
+
data: savePersons(persons: $data) {
|
|
33997
|
+
...PersonFragment
|
|
33998
|
+
}
|
|
33959
33999
|
}
|
|
33960
|
-
|
|
33961
|
-
|
|
33962
|
-
|
|
33963
|
-
|
|
33964
|
-
|
|
33965
|
-
|
|
33966
|
-
|
|
33967
|
-
|
|
33968
|
-
|
|
33969
|
-
|
|
34000
|
+
${PersonFragments.person}
|
|
34001
|
+
`,
|
|
34002
|
+
deleteAll: gql$1 `
|
|
34003
|
+
mutation deletePersons($ids: [Int]) {
|
|
34004
|
+
deletePersons(ids: $ids)
|
|
34005
|
+
}
|
|
34006
|
+
`,
|
|
34007
|
+
};
|
|
34008
|
+
class PersonService extends BaseEntityService {
|
|
34009
|
+
graphql;
|
|
34010
|
+
platform;
|
|
34011
|
+
network;
|
|
34012
|
+
entities;
|
|
34013
|
+
constructor(graphql, platform, network, entities) {
|
|
34014
|
+
super(graphql, platform, Person, PersonFilter, {
|
|
34015
|
+
queries: PersonQueries,
|
|
34016
|
+
mutations: PersonMutations,
|
|
34017
|
+
defaultSortBy: 'lastName',
|
|
34018
|
+
defaultSortDirection: 'asc',
|
|
33970
34019
|
});
|
|
33971
|
-
|
|
33972
|
-
|
|
33973
|
-
|
|
34020
|
+
this.graphql = graphql;
|
|
34021
|
+
this.platform = platform;
|
|
34022
|
+
this.network = network;
|
|
34023
|
+
this.entities = entities;
|
|
34024
|
+
// for DEV only -----
|
|
34025
|
+
this._debug = !environment.production;
|
|
33974
34026
|
}
|
|
33975
|
-
async
|
|
33976
|
-
|
|
33977
|
-
if (
|
|
33978
|
-
|
|
33979
|
-
return;
|
|
33980
|
-
}
|
|
33981
|
-
const element = event.target;
|
|
33982
|
-
let href = element.getAttribute('href');
|
|
33983
|
-
const fragment = UrlUtils.getFragment(href);
|
|
33984
|
-
// Fragment
|
|
33985
|
-
if (href?.startsWith('#') && isNotNilOrBlank(fragment)) {
|
|
33986
|
-
event.preventDefault();
|
|
33987
|
-
this.scrollToAnchor(containerElement, fragment);
|
|
33988
|
-
return true;
|
|
33989
|
-
}
|
|
33990
|
-
const routerLink = element.getAttribute('routerLink');
|
|
33991
|
-
if (isNotNilOrBlank(routerLink)) {
|
|
33992
|
-
event.preventDefault();
|
|
33993
|
-
return this.navController.navigateForward(routerLink);
|
|
33994
|
-
}
|
|
33995
|
-
// Resolve relative URL, if baseUrl has been set
|
|
33996
|
-
if (this._baseUrl && UrlUtils.isRelativeUrl(href)) {
|
|
33997
|
-
// Resolve URL, then open using the platform
|
|
33998
|
-
href = UrlUtils.resolveRelativeUrl(this._baseUrl, href);
|
|
33999
|
-
}
|
|
34000
|
-
// Resolve internal URL as an app route
|
|
34001
|
-
if (UrlUtils.isInternalUrl(href)) {
|
|
34002
|
-
const routePath = this.normalizeUrl(href.startsWith('/') ? href : `/${href}`);
|
|
34003
|
-
const urlTree = this.getUrlTree(routePath);
|
|
34004
|
-
event.preventDefault();
|
|
34005
|
-
this.router.navigated = false;
|
|
34006
|
-
// Opening route
|
|
34007
|
-
return this.navController.navigateForward(urlTree);
|
|
34027
|
+
async loadAll(offset, size, sortBy, sortDirection, filter, opts) {
|
|
34028
|
+
const offline = this.network.offline && (!opts || opts.fetchPolicy !== 'network-only');
|
|
34029
|
+
if (offline) {
|
|
34030
|
+
return this.loadAllLocally(offset, size, sortBy, sortDirection, filter, opts);
|
|
34008
34031
|
}
|
|
34009
|
-
|
|
34010
|
-
event.preventDefault();
|
|
34011
|
-
await this.platform.open(href);
|
|
34012
|
-
return true;
|
|
34032
|
+
return super.loadAll(offset, size, sortBy, sortDirection, filter, opts);
|
|
34013
34033
|
}
|
|
34014
|
-
|
|
34015
|
-
|
|
34016
|
-
|
|
34017
|
-
|
|
34018
|
-
|
|
34019
|
-
|
|
34020
|
-
|
|
34021
|
-
|
|
34022
|
-
|
|
34023
|
-
|
|
34024
|
-
|
|
34025
|
-
|
|
34026
|
-
|
|
34027
|
-
|
|
34028
|
-
|
|
34029
|
-
|
|
34030
|
-
else {
|
|
34031
|
-
console.warn(`No element found with the ID "${anchorId}" inside the container.`);
|
|
34034
|
+
async loadAllLocally(offset, size, sortBy, sortDirection, filter, opts) {
|
|
34035
|
+
filter = this.asFilter(filter);
|
|
34036
|
+
const variables = {
|
|
34037
|
+
offset: offset || 0,
|
|
34038
|
+
size: size || 100,
|
|
34039
|
+
sortBy: sortBy || this.defaultSortBy,
|
|
34040
|
+
sortDirection: sortDirection || this.defaultSortDirection,
|
|
34041
|
+
filter: filter && filter.asFilterFn(),
|
|
34042
|
+
};
|
|
34043
|
+
const { data, total } = await this.entities.loadAll('PersonVO', variables);
|
|
34044
|
+
const entities = this.fromObjects(data, opts);
|
|
34045
|
+
const res = { data: entities, total };
|
|
34046
|
+
// Add fetch more function
|
|
34047
|
+
const nextOffset = (offset || 0) + entities.length;
|
|
34048
|
+
if (nextOffset < total) {
|
|
34049
|
+
res.fetchMore = () => this.loadAllLocally(nextOffset, size, sortBy, sortDirection, filter, opts);
|
|
34032
34050
|
}
|
|
34051
|
+
return res;
|
|
34033
34052
|
}
|
|
34034
|
-
|
|
34035
|
-
|
|
34036
|
-
|
|
34037
|
-
|
|
34038
|
-
|
|
34039
|
-
|
|
34040
|
-
|
|
34041
|
-
|
|
34042
|
-
|
|
34043
|
-
|
|
34044
|
-
|
|
34045
|
-
|
|
34046
|
-
|
|
34047
|
-
|
|
34048
|
-
return this.locationStrategy.prepareExternalUrl(serializedUrl);
|
|
34053
|
+
async suggest(value, filter, sortBy, sortDirection, opts) {
|
|
34054
|
+
if (EntityUtils.isNotEmpty(value, 'id'))
|
|
34055
|
+
return { data: [value] };
|
|
34056
|
+
value = (typeof value === 'string' && value !== '*' && value) || undefined;
|
|
34057
|
+
sortBy = sortBy || filter.searchAttribute || (filter.searchAttributes && filter.searchAttributes[0]);
|
|
34058
|
+
return this.loadAll(0, !value ? 30 : 10, sortBy, sortDirection, {
|
|
34059
|
+
...filter,
|
|
34060
|
+
searchText: value,
|
|
34061
|
+
statusIds: (filter && filter.statusIds) || [StatusIds.ENABLE, StatusIds.TEMPORARY],
|
|
34062
|
+
userProfiles: filter && filter.userProfiles,
|
|
34063
|
+
}, {
|
|
34064
|
+
withTotal: true /* need by autocomplete */,
|
|
34065
|
+
...opts,
|
|
34066
|
+
});
|
|
34049
34067
|
}
|
|
34050
|
-
|
|
34051
|
-
|
|
34052
|
-
|
|
34053
|
-
|
|
34054
|
-
|
|
34055
|
-
|
|
34056
|
-
|
|
34068
|
+
async executeImport(filter, opts) {
|
|
34069
|
+
const maxProgression = (opts && opts.maxProgression) || 100;
|
|
34070
|
+
filter = {
|
|
34071
|
+
...filter,
|
|
34072
|
+
statusIds: [StatusIds.ENABLE, StatusIds.TEMPORARY],
|
|
34073
|
+
userProfiles: ['SUPERVISOR', 'USER', 'GUEST'],
|
|
34074
|
+
};
|
|
34075
|
+
console.info('[person-service] Importing persons...');
|
|
34076
|
+
const res = await JobUtils.fetchAllPages((offset, size) => this.loadAll(offset, size, 'id', null, filter, {
|
|
34077
|
+
debug: false,
|
|
34078
|
+
fetchPolicy: 'network-only',
|
|
34079
|
+
withTotal: offset === 0, // Compute total only once
|
|
34080
|
+
toEntity: false,
|
|
34081
|
+
}), { progression: opts?.progression, maxProgression: maxProgression * 0.9 });
|
|
34082
|
+
// Save result locally
|
|
34083
|
+
await this.entities.saveAll(res.data, { entityName: 'PersonVO', reset: true });
|
|
34057
34084
|
}
|
|
34058
|
-
|
|
34059
|
-
|
|
34060
|
-
}
|
|
34061
|
-
|
|
34062
|
-
|
|
34063
|
-
|
|
34064
|
-
|
|
34065
|
-
|
|
34066
|
-
|
|
34067
|
-
|
|
34068
|
-
|
|
34069
|
-
|
|
34070
|
-
|
|
34071
|
-
|
|
34085
|
+
async loadById(id, opts) {
|
|
34086
|
+
const { data } = await this.loadAll(0, 1, null, null, { includedIds: [id] }, { withTotal: false, ...opts });
|
|
34087
|
+
const source = isNotEmptyArray(data) ? data[0] : { id };
|
|
34088
|
+
return this.fromObject(source, opts);
|
|
34089
|
+
}
|
|
34090
|
+
async loadByPubkey(pubkey, opts) {
|
|
34091
|
+
const { data } = await this.loadAll(0, 1, null, null, { pubkey }, { withTotal: false, ...opts });
|
|
34092
|
+
const source = isNotEmptyArray(data) ? data[0] : { pubkey };
|
|
34093
|
+
return this.fromObject(source, opts);
|
|
34094
|
+
}
|
|
34095
|
+
/* -- protected methods -- */
|
|
34096
|
+
asObject(source) {
|
|
34097
|
+
if (!source)
|
|
34098
|
+
return undefined;
|
|
34099
|
+
if (!(source instanceof Person)) {
|
|
34100
|
+
source = Person.fromObject(source);
|
|
34101
|
+
}
|
|
34102
|
+
const target = source.asObject();
|
|
34103
|
+
// Not known in server GraphQL schema
|
|
34104
|
+
delete target.mainProfile;
|
|
34105
|
+
target.department = source.department?.asObject();
|
|
34106
|
+
return target;
|
|
34107
|
+
}
|
|
34108
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PersonService, deps: [{ token: GraphqlService }, { token: PlatformService }, { token: NetworkService }, { token: EntitiesStorage }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
34109
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PersonService, providedIn: 'root' });
|
|
34110
|
+
}
|
|
34111
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PersonService, decorators: [{
|
|
34112
|
+
type: Injectable,
|
|
34113
|
+
args: [{ providedIn: 'root' }]
|
|
34114
|
+
}], ctorParameters: () => [{ type: GraphqlService }, { type: PlatformService }, { type: NetworkService }, { type: EntitiesStorage }] });
|
|
34115
|
+
|
|
34116
|
+
class MessageForm extends AppForm {
|
|
34117
|
+
formBuilder;
|
|
34118
|
+
cd;
|
|
34119
|
+
mobile;
|
|
34120
|
+
suggestFn;
|
|
34121
|
+
subjectMinLength = 5;
|
|
34122
|
+
subjectMaxLength = 255;
|
|
34123
|
+
bodyMaxLength = 2000;
|
|
34124
|
+
bodyAutoHeight = true;
|
|
34125
|
+
canSelectType = false;
|
|
34126
|
+
canRecipientFilter = false;
|
|
34127
|
+
recipientFilterCount = 0;
|
|
34128
|
+
types = MessageTypeList;
|
|
34129
|
+
constructor(injector, formBuilder, cd) {
|
|
34130
|
+
super(injector);
|
|
34131
|
+
this.formBuilder = formBuilder;
|
|
34132
|
+
this.cd = cd;
|
|
34133
|
+
this.mobile = this.settings.mobile;
|
|
34134
|
+
}
|
|
34135
|
+
ngOnInit() {
|
|
34136
|
+
this.setForm(this.formBuilder.group({
|
|
34137
|
+
type: [MessageTypes.INBOX_MESSAGE, Validators.required],
|
|
34138
|
+
recipients: [null, Validators.required],
|
|
34139
|
+
recipientFilter: [null],
|
|
34140
|
+
subject: [
|
|
34141
|
+
null,
|
|
34142
|
+
this.subjectMaxLength
|
|
34143
|
+
? Validators.compose([Validators.required, Validators.minLength(this.subjectMinLength), Validators.maxLength(this.subjectMaxLength)])
|
|
34144
|
+
: Validators.required,
|
|
34145
|
+
],
|
|
34146
|
+
body: [null, this.bodyMaxLength ? Validators.compose([Validators.maxLength(this.bodyMaxLength)]) : Validators.required],
|
|
34147
|
+
}));
|
|
34148
|
+
this.registerSubscription(this._form
|
|
34149
|
+
.get('type')
|
|
34150
|
+
.valueChanges.pipe(filter(isNotNil))
|
|
34151
|
+
.subscribe((type) => this.updateFormGroup(this._form, { type })));
|
|
34152
|
+
// Person combo
|
|
34153
|
+
const personAttributes = this.settings.getFieldDisplayAttributes('person', ['lastName', 'firstName', 'department.name']);
|
|
34154
|
+
this.registerAutocompleteField('recipients', {
|
|
34155
|
+
showAllOnFocus: false,
|
|
34156
|
+
suggestFn: this.suggestFn,
|
|
34157
|
+
filter: {
|
|
34158
|
+
statusIds: [StatusIds.TEMPORARY, StatusIds.ENABLE],
|
|
34159
|
+
},
|
|
34160
|
+
attributes: personAttributes,
|
|
34161
|
+
columnNames: personAttributes.map((attr) => `USER.${changeCaseToUnderscore(attr).toUpperCase()}`),
|
|
34162
|
+
displayWith: PersonUtils.personToString,
|
|
34163
|
+
multiple: true,
|
|
34164
|
+
mobile: this.mobile,
|
|
34165
|
+
});
|
|
34166
|
+
}
|
|
34167
|
+
isSamePerson(o1, o2) {
|
|
34168
|
+
return EntityUtils.equals(o1, o2, 'id');
|
|
34169
|
+
}
|
|
34170
|
+
updateFormGroup(formGroup, opts) {
|
|
34171
|
+
console.debug('[message-form] Updating form group...', opts);
|
|
34172
|
+
// Recipient validator
|
|
34173
|
+
const recipientsRequired = toBoolean(opts?.recipientRequired, opts?.type !== MessageTypes.FEED);
|
|
34174
|
+
{
|
|
34175
|
+
const control = formGroup.get('recipients');
|
|
34176
|
+
if (recipientsRequired) {
|
|
34177
|
+
if (!control.hasValidator(Validators.required)) {
|
|
34178
|
+
control.addValidators(Validators.required);
|
|
34179
|
+
}
|
|
34180
|
+
control.enable();
|
|
34181
|
+
}
|
|
34182
|
+
else {
|
|
34183
|
+
if (control.hasValidator(Validators.required)) {
|
|
34184
|
+
control.removeValidators(Validators.required);
|
|
34185
|
+
}
|
|
34186
|
+
control.disable();
|
|
34187
|
+
}
|
|
34188
|
+
}
|
|
34189
|
+
// Recipient filter validator
|
|
34190
|
+
const recipientFilterRequired = this.canRecipientFilter && !recipientsRequired;
|
|
34191
|
+
{
|
|
34192
|
+
const control = formGroup.get('recipientFilter');
|
|
34193
|
+
if (recipientFilterRequired) {
|
|
34194
|
+
if (!control.hasValidator(Validators.required)) {
|
|
34195
|
+
control.addValidators(Validators.required);
|
|
34196
|
+
}
|
|
34197
|
+
control.enable();
|
|
34198
|
+
}
|
|
34199
|
+
else {
|
|
34200
|
+
if (control.hasValidator(Validators.required)) {
|
|
34201
|
+
control.removeValidators(Validators.required);
|
|
34202
|
+
}
|
|
34203
|
+
control.disable();
|
|
34204
|
+
}
|
|
34205
|
+
}
|
|
34206
|
+
formGroup.updateValueAndValidity();
|
|
34207
|
+
}
|
|
34208
|
+
markForCheck() {
|
|
34209
|
+
this.cd.markForCheck();
|
|
34210
|
+
}
|
|
34211
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageForm, deps: [{ token: i0.Injector }, { token: i1$3.UntypedFormBuilder }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
34212
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: MessageForm, selector: "app-message-form", inputs: { mobile: "mobile", suggestFn: "suggestFn", subjectMinLength: "subjectMinLength", subjectMaxLength: "subjectMaxLength", bodyMaxLength: "bodyMaxLength", bodyAutoHeight: "bodyAutoHeight", canSelectType: "canSelectType", canRecipientFilter: "canRecipientFilter", recipientFilterCount: "recipientFilterCount" }, usesInheritance: true, ngImport: i0, template: "<!-- debug -->\n@if (debug) {\n <ng-container *ngTemplateOutlet=\"debugPanel\"></ng-container>\n}\n\n<form [formGroup]=\"form\" class=\"form-container\">\n <!-- type -->\n <mat-form-field *ngIf=\"canSelectType\">\n <mat-label>{{ 'SOCIAL.MESSAGE.TYPE' | translate }}</mat-label>\n <mat-select formControlName=\"type\">\n @for (item of types; track item.id) {\n @if (canRecipientFilter || item.id !== 'FEED') {\n <mat-option [value]=\"item.id\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label | translate }}\n </mat-option>\n }\n }\n </mat-select>\n </mat-form-field>\n\n <!-- Recipients -->\n @if ((form | formGetValue: 'type') !== 'FEED') {\n <mat-chips-field\n formControlName=\"recipients\"\n chipColor=\"accent\"\n [placeholder]=\"'SOCIAL.MESSAGE.RECIPIENTS' | translate\"\n [config]=\"autocompleteFields.recipients\"\n [equals]=\"isSamePerson\"\n ></mat-chips-field>\n } @else {\n <mat-form-field>\n <input matInput hidden formControlName=\"recipients\" type=\"text\" />\n <mat-chip-grid>\n <mat-chip-row>\n {{ 'SOCIAL.MESSAGE.RECIPIENT_FILTER_COUNT' | translate: { count: recipientFilterCount } }}\n </mat-chip-row>\n </mat-chip-grid>\n </mat-form-field>\n }\n\n <!-- Subject -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.SUBJECT' | translate }}</mat-label>\n <input matInput type=\"text\" formControlName=\"subject\" autocomplete=\"off\" required />\n <mat-error *ngIf=\"form.controls.subject.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"form.controls.subject.hasError('minlength')\">\n {{ 'ERROR.FIELD_MIN_LENGTH_COMPACT' | translate }}\n </mat-error>\n <mat-error *ngIf=\"form.controls.subject.hasError('maxlength')\">\n {{ 'ERROR.FIELD_MAX_LENGTH_COMPACT' | translate }}\n </mat-error>\n <mat-hint *ngIf=\"subjectMaxLength\" align=\"end\">\n {{\n 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.subject.value?.length || 0, max: subjectMaxLength }\n }}\n </mat-hint>\n </mat-form-field>\n\n <!-- Body -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.BODY_HELP' | translate }}</mat-label>\n <textarea\n matInput\n type=\"text\"\n formControlName=\"body\"\n [class.fixed-height]=\"!bodyAutoHeight\"\n [cdkTextareaAutosize]=\"bodyAutoHeight\"\n (keydown.control.enter)=\"doSubmit($event)\"\n ></textarea>\n <mat-error *ngIf=\"form.controls.body.hasError('maxlength')\">\n {{ 'ERROR.FIELD_MAX_LENGTH' | translate: form.controls.body.errors.maxlength }}\n </mat-error>\n <mat-hint *ngIf=\"bodyMaxLength\" align=\"end\">\n {{ 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.body.value?.length || 0, max: bodyMaxLength } }}\n </mat-hint>\n </mat-form-field>\n</form>\n\n\n<ng-template #debugPanel>\n <app-debug title=\"Message form\">\n <ion-grid>\n <ion-row>\n <ion-col>\n ready: {{ readySubject | async }}\n <br />\n loading: {{ loadingSubject | async }}\n <br />\n enabled: {{ enabled }}\n <br />\n dirty: {{ dirty }}\n <br />\n valid: {{ valid }}\n <br />\n <br />\n error: {{ _form | formError | json}}\n </ion-col>\n </ion-row>\n </ion-grid>\n </app-debug>\n</ng-template>\n", styles: ["textarea.fixed-height{height:11.5em}textarea{min-height:11.5em}\n"], dependencies: [{ kind: "directive", type: i3$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { 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.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.IonRow, selector: "ion-row" }, { kind: "directive", type: i1$1.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "directive", type: i1$4.CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "component", type: i6$3.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i2.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: MatChipsField, selector: "mat-chips-field", inputs: ["equals", "logPrefix", "formControl", "formControlName", "floatLabel", "placeholder", "suggestFn", "required", "mobile", "readonly", "clearable", "debounceTime", "displaySeparator", "displayWith", "displayAttributes", "displayColumnSizes", "displayColumnNames", "highlightAccent", "showAllOnFocus", "showPanelOnFocus", "autofocus", "config", "i18nPrefix", "noResultMessage", "panelClass", "panelWidth", "disableRipple", "matAutocompletePosition", "itemSize", "fetchMoreThreshold", "suggestLengthThreshold", "showLoadingSpinner", "chipColor", "debug", "applyImplicitValue", "dropButtonTitle", "clearButtonTitle", "trimSearchText", "hideRequiredMarker", "colSizes", "separatorKeysCodes", "appearance", "subscriptSizing", "class", "filter", "tabindex", "items"], outputs: ["click", "blur", "focus", "dropButtonClick", "keydown.escape", "keyup.enter"] }, { kind: "directive", type: AutoResizeDirective, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMaxRows", "cdkAutosizeMinRows"] }, { kind: "component", type: i11$1.MatChipGrid, selector: "mat-chip-grid", inputs: ["disabled", "placeholder", "required", "value", "errorStateMatcher"], outputs: ["change", "valueChange"] }, { kind: "component", type: i11$1.MatChipRow, selector: "mat-chip-row, [mat-chip-row], mat-basic-chip-row, [mat-basic-chip-row]", inputs: ["editable"], outputs: ["edited"] }, { kind: "component", type: DebugComponent, selector: "app-debug", inputs: ["titlePrefix", "title", "enable", "expanded"] }, { kind: "pipe", type: i3$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i3$1.JsonPipe, name: "json" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }, { kind: "pipe", type: FormErrorPipe, name: "formError" }, { kind: "pipe", type: FormGetValuePipe, name: "formGetValue" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
34213
|
+
}
|
|
34214
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageForm, decorators: [{
|
|
34215
|
+
type: Component,
|
|
34216
|
+
args: [{ selector: 'app-message-form', changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- debug -->\n@if (debug) {\n <ng-container *ngTemplateOutlet=\"debugPanel\"></ng-container>\n}\n\n<form [formGroup]=\"form\" class=\"form-container\">\n <!-- type -->\n <mat-form-field *ngIf=\"canSelectType\">\n <mat-label>{{ 'SOCIAL.MESSAGE.TYPE' | translate }}</mat-label>\n <mat-select formControlName=\"type\">\n @for (item of types; track item.id) {\n @if (canRecipientFilter || item.id !== 'FEED') {\n <mat-option [value]=\"item.id\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label | translate }}\n </mat-option>\n }\n }\n </mat-select>\n </mat-form-field>\n\n <!-- Recipients -->\n @if ((form | formGetValue: 'type') !== 'FEED') {\n <mat-chips-field\n formControlName=\"recipients\"\n chipColor=\"accent\"\n [placeholder]=\"'SOCIAL.MESSAGE.RECIPIENTS' | translate\"\n [config]=\"autocompleteFields.recipients\"\n [equals]=\"isSamePerson\"\n ></mat-chips-field>\n } @else {\n <mat-form-field>\n <input matInput hidden formControlName=\"recipients\" type=\"text\" />\n <mat-chip-grid>\n <mat-chip-row>\n {{ 'SOCIAL.MESSAGE.RECIPIENT_FILTER_COUNT' | translate: { count: recipientFilterCount } }}\n </mat-chip-row>\n </mat-chip-grid>\n </mat-form-field>\n }\n\n <!-- Subject -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.SUBJECT' | translate }}</mat-label>\n <input matInput type=\"text\" formControlName=\"subject\" autocomplete=\"off\" required />\n <mat-error *ngIf=\"form.controls.subject.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"form.controls.subject.hasError('minlength')\">\n {{ 'ERROR.FIELD_MIN_LENGTH_COMPACT' | translate }}\n </mat-error>\n <mat-error *ngIf=\"form.controls.subject.hasError('maxlength')\">\n {{ 'ERROR.FIELD_MAX_LENGTH_COMPACT' | translate }}\n </mat-error>\n <mat-hint *ngIf=\"subjectMaxLength\" align=\"end\">\n {{\n 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.subject.value?.length || 0, max: subjectMaxLength }\n }}\n </mat-hint>\n </mat-form-field>\n\n <!-- Body -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.BODY_HELP' | translate }}</mat-label>\n <textarea\n matInput\n type=\"text\"\n formControlName=\"body\"\n [class.fixed-height]=\"!bodyAutoHeight\"\n [cdkTextareaAutosize]=\"bodyAutoHeight\"\n (keydown.control.enter)=\"doSubmit($event)\"\n ></textarea>\n <mat-error *ngIf=\"form.controls.body.hasError('maxlength')\">\n {{ 'ERROR.FIELD_MAX_LENGTH' | translate: form.controls.body.errors.maxlength }}\n </mat-error>\n <mat-hint *ngIf=\"bodyMaxLength\" align=\"end\">\n {{ 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.body.value?.length || 0, max: bodyMaxLength } }}\n </mat-hint>\n </mat-form-field>\n</form>\n\n\n<ng-template #debugPanel>\n <app-debug title=\"Message form\">\n <ion-grid>\n <ion-row>\n <ion-col>\n ready: {{ readySubject | async }}\n <br />\n loading: {{ loadingSubject | async }}\n <br />\n enabled: {{ enabled }}\n <br />\n dirty: {{ dirty }}\n <br />\n valid: {{ valid }}\n <br />\n <br />\n error: {{ _form | formError | json}}\n </ion-col>\n </ion-row>\n </ion-grid>\n </app-debug>\n</ng-template>\n", styles: ["textarea.fixed-height{height:11.5em}textarea{min-height:11.5em}\n"] }]
|
|
34217
|
+
}], ctorParameters: () => [{ type: i0.Injector }, { type: i1$3.UntypedFormBuilder }, { type: i0.ChangeDetectorRef }], propDecorators: { mobile: [{
|
|
34218
|
+
type: Input
|
|
34219
|
+
}], suggestFn: [{
|
|
34220
|
+
type: Input
|
|
34221
|
+
}], subjectMinLength: [{
|
|
34222
|
+
type: Input
|
|
34223
|
+
}], subjectMaxLength: [{
|
|
34224
|
+
type: Input
|
|
34225
|
+
}], bodyMaxLength: [{
|
|
34226
|
+
type: Input
|
|
34227
|
+
}], bodyAutoHeight: [{
|
|
34228
|
+
type: Input
|
|
34229
|
+
}], canSelectType: [{
|
|
34230
|
+
type: Input
|
|
34231
|
+
}], canRecipientFilter: [{
|
|
34232
|
+
type: Input
|
|
34233
|
+
}], recipientFilterCount: [{
|
|
34234
|
+
type: Input
|
|
34235
|
+
}] } });
|
|
34236
|
+
|
|
34237
|
+
class MessageModal {
|
|
34238
|
+
settings;
|
|
34239
|
+
viewCtrl;
|
|
34240
|
+
accountService;
|
|
34241
|
+
cd;
|
|
34242
|
+
mobile;
|
|
34243
|
+
debug = false;
|
|
34244
|
+
title = 'SOCIAL.MESSAGE.NEW';
|
|
34245
|
+
suggestFn;
|
|
34246
|
+
data;
|
|
34247
|
+
canSelectType;
|
|
34248
|
+
canRecipientFilter;
|
|
34249
|
+
recipientFilterCount;
|
|
34250
|
+
form;
|
|
34251
|
+
constructor(settings, viewCtrl, accountService, cd) {
|
|
34252
|
+
this.settings = settings;
|
|
34253
|
+
this.viewCtrl = viewCtrl;
|
|
34254
|
+
this.accountService = accountService;
|
|
34255
|
+
this.cd = cd;
|
|
34256
|
+
this.mobile = this.settings.mobile;
|
|
34257
|
+
}
|
|
34258
|
+
ngOnInit() {
|
|
34259
|
+
this.canSelectType = toBoolean(this.canSelectType, this.accountService.isAdmin());
|
|
34260
|
+
}
|
|
34261
|
+
ngAfterViewInit() {
|
|
34262
|
+
setTimeout(() => {
|
|
34263
|
+
this.form.markAsReady({ emitEvent: false });
|
|
34264
|
+
if (this.data) {
|
|
34265
|
+
this.data.type = this.data.type || MessageTypes.INBOX_MESSAGE;
|
|
34266
|
+
this.form.setValue(this.data);
|
|
34267
|
+
}
|
|
34268
|
+
this.form.markAsLoaded();
|
|
34269
|
+
this.form.enable();
|
|
34270
|
+
});
|
|
34271
|
+
}
|
|
34272
|
+
cancel() {
|
|
34273
|
+
this.viewCtrl.dismiss();
|
|
34274
|
+
}
|
|
34275
|
+
async doSubmit() {
|
|
34276
|
+
if (this.form.disabled)
|
|
34277
|
+
return;
|
|
34278
|
+
if (!this.form.valid) {
|
|
34279
|
+
await AppFormUtils.waitWhilePending(this.form);
|
|
34280
|
+
if (this.form.invalid) {
|
|
34281
|
+
AppFormUtils.logFormErrors(this.form.form, '[message-modal] ');
|
|
34282
|
+
this.form.markAllAsTouched();
|
|
34283
|
+
return;
|
|
34284
|
+
}
|
|
34285
|
+
}
|
|
34286
|
+
this.markAsLoading();
|
|
34287
|
+
try {
|
|
34288
|
+
const data = this.form.value;
|
|
34289
|
+
// Disable the form
|
|
34290
|
+
this.form.disable();
|
|
34291
|
+
const entity = Message.fromObject(data);
|
|
34292
|
+
return this.viewCtrl.dismiss(entity);
|
|
34293
|
+
}
|
|
34294
|
+
catch (err) {
|
|
34295
|
+
this.form.error = (err && err.message) || err;
|
|
34296
|
+
this.markAsLoaded();
|
|
34297
|
+
// Enable the form
|
|
34298
|
+
this.form.enable();
|
|
34299
|
+
// Reset form error on next changes
|
|
34300
|
+
firstNotNilPromise(this.form.form.valueChanges).then(() => {
|
|
34301
|
+
this.form.error = null;
|
|
34302
|
+
this.markForCheck();
|
|
34303
|
+
});
|
|
34304
|
+
return;
|
|
34305
|
+
}
|
|
34306
|
+
}
|
|
34307
|
+
markForCheck() {
|
|
34308
|
+
this.cd.markForCheck();
|
|
34309
|
+
}
|
|
34310
|
+
markAsLoading(opts) {
|
|
34311
|
+
this.form.markAsLoading(opts);
|
|
34312
|
+
}
|
|
34313
|
+
markAsLoaded(opts) {
|
|
34314
|
+
this.form.markAsLoaded(opts);
|
|
34315
|
+
}
|
|
34316
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageModal, deps: [{ token: LocalSettingsService }, { token: i2$1.ModalController }, { token: AccountService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
34317
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: MessageModal, selector: "app-message-modal", inputs: { debug: "debug", title: "title", suggestFn: "suggestFn", data: "data", canSelectType: "canSelectType", canRecipientFilter: "canRecipientFilter", recipientFilterCount: "recipientFilterCount" }, viewQueries: [{ propertyName: "form", first: true, predicate: ["form"], descendants: true, static: true }], ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-buttons slot=\"start\">\n <ion-button class=\"back-button\" (click)=\"cancel()\" visible-xs visible-sm visible-mobile>\n <ion-icon slot=\"icon-only\" name=\"arrow-back\"></ion-icon>\n </ion-button>\n </ion-buttons>\n\n <ion-title>\n {{ title | translate }}\n </ion-title>\n\n <ion-buttons slot=\"end\">\n <ion-button\n class=\"back-button\"\n (click)=\"doSubmit()\"\n [disabled]=\"!form.valid\"\n visible-xs\n visible-sm\n visible-mobile\n >\n <ion-icon slot=\"icon-only\" name=\"checkmark\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n <ion-item *ngIf=\"form.error\" visible-xs visible-sm visible-mobile lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\" [innerHTML]=\"form.error | translate\"></ion-label>\n </ion-item>\n\n <app-message-form\n #form\n (onSubmit)=\"doSubmit()\"\n (onCancel)=\"cancel()\"\n [canSelectType]=\"canSelectType\"\n [canRecipientFilter]=\"canRecipientFilter\"\n [recipientFilterCount]=\"recipientFilterCount\"\n [suggestFn]=\"suggestFn\"\n [debug]=\"debug\">\n ></app-message-form>\n</ion-content>\n\n<ion-footer hidden-xs hidden-sm hidden-mobile>\n <ion-toolbar>\n <ion-row class=\"ion-no-padding\" nowrap>\n <ion-col></ion-col>\n\n <!-- buttons -->\n <ion-col size=\"auto\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"cancel()\">\n <ion-label translate>COMMON.BTN_CANCEL</ion-label>\n </ion-button>\n\n <ion-button\n [fill]=\"form.invalid ? 'clear' : 'solid'\"\n [disabled]=\"form.loading || form.invalid\"\n (keyup.enter)=\"doSubmit()\"\n (click)=\"doSubmit()\"\n color=\"tertiary\"\n >\n <ion-label translate>COMMON.BTN_SEND</ion-label>\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-toolbar>\n</ion-footer>\n", dependencies: [{ kind: "directive", type: i3$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { 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.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.IonFooter, selector: "ion-footer", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$1.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { 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.IonRow, selector: "ion-row" }, { kind: "component", type: i2$1.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$1.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "directive", type: i1$1.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: MessageForm, selector: "app-message-form", inputs: ["mobile", "suggestFn", "subjectMinLength", "subjectMaxLength", "bodyMaxLength", "bodyAutoHeight", "canSelectType", "canRecipientFilter", "recipientFilterCount"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
34318
|
+
}
|
|
34319
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageModal, decorators: [{
|
|
34320
|
+
type: Component,
|
|
34321
|
+
args: [{ selector: 'app-message-modal', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-buttons slot=\"start\">\n <ion-button class=\"back-button\" (click)=\"cancel()\" visible-xs visible-sm visible-mobile>\n <ion-icon slot=\"icon-only\" name=\"arrow-back\"></ion-icon>\n </ion-button>\n </ion-buttons>\n\n <ion-title>\n {{ title | translate }}\n </ion-title>\n\n <ion-buttons slot=\"end\">\n <ion-button\n class=\"back-button\"\n (click)=\"doSubmit()\"\n [disabled]=\"!form.valid\"\n visible-xs\n visible-sm\n visible-mobile\n >\n <ion-icon slot=\"icon-only\" name=\"checkmark\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n <ion-item *ngIf=\"form.error\" visible-xs visible-sm visible-mobile lines=\"none\">\n <ion-icon color=\"danger\" slot=\"start\" name=\"alert-circle\"></ion-icon>\n <ion-label color=\"danger\" class=\"error\" [innerHTML]=\"form.error | translate\"></ion-label>\n </ion-item>\n\n <app-message-form\n #form\n (onSubmit)=\"doSubmit()\"\n (onCancel)=\"cancel()\"\n [canSelectType]=\"canSelectType\"\n [canRecipientFilter]=\"canRecipientFilter\"\n [recipientFilterCount]=\"recipientFilterCount\"\n [suggestFn]=\"suggestFn\"\n [debug]=\"debug\">\n ></app-message-form>\n</ion-content>\n\n<ion-footer hidden-xs hidden-sm hidden-mobile>\n <ion-toolbar>\n <ion-row class=\"ion-no-padding\" nowrap>\n <ion-col></ion-col>\n\n <!-- buttons -->\n <ion-col size=\"auto\">\n <ion-button fill=\"clear\" color=\"dark\" (click)=\"cancel()\">\n <ion-label translate>COMMON.BTN_CANCEL</ion-label>\n </ion-button>\n\n <ion-button\n [fill]=\"form.invalid ? 'clear' : 'solid'\"\n [disabled]=\"form.loading || form.invalid\"\n (keyup.enter)=\"doSubmit()\"\n (click)=\"doSubmit()\"\n color=\"tertiary\"\n >\n <ion-label translate>COMMON.BTN_SEND</ion-label>\n </ion-button>\n </ion-col>\n </ion-row>\n </ion-toolbar>\n</ion-footer>\n" }]
|
|
34322
|
+
}], ctorParameters: () => [{ type: LocalSettingsService }, { type: i2$1.ModalController }, { type: AccountService }, { type: i0.ChangeDetectorRef }], propDecorators: { debug: [{
|
|
34323
|
+
type: Input
|
|
34324
|
+
}], title: [{
|
|
34325
|
+
type: Input
|
|
34326
|
+
}], suggestFn: [{
|
|
34327
|
+
type: Input
|
|
34328
|
+
}], data: [{
|
|
34329
|
+
type: Input
|
|
34330
|
+
}], canSelectType: [{
|
|
34331
|
+
type: Input
|
|
34332
|
+
}], canRecipientFilter: [{
|
|
34333
|
+
type: Input
|
|
34334
|
+
}], recipientFilterCount: [{
|
|
34335
|
+
type: Input
|
|
34336
|
+
}], form: [{
|
|
34337
|
+
type: ViewChild,
|
|
34338
|
+
args: ['form', { static: true }]
|
|
34339
|
+
}] } });
|
|
34340
|
+
|
|
34341
|
+
const MessageFragments = {
|
|
34342
|
+
message: gql$1 `
|
|
34343
|
+
fragment MessageFragment on MessageVO {
|
|
34344
|
+
id
|
|
34345
|
+
subject
|
|
34346
|
+
body
|
|
34347
|
+
type
|
|
34348
|
+
recipientFilter {
|
|
34349
|
+
...PersonFilterFragment
|
|
34350
|
+
}
|
|
34351
|
+
issuer {
|
|
34352
|
+
...PersonFragment
|
|
34353
|
+
}
|
|
34354
|
+
}
|
|
34355
|
+
`,
|
|
34356
|
+
};
|
|
34357
|
+
const Queries = {
|
|
34358
|
+
load: gql$1 `
|
|
34359
|
+
mutation LoadMessage($id: Int!) {
|
|
34360
|
+
data: message(id: $id) {
|
|
34361
|
+
...MessageFragment
|
|
34362
|
+
}
|
|
34363
|
+
}
|
|
34364
|
+
${MessageFragments.message}
|
|
34365
|
+
${PersonFragments.person}
|
|
34366
|
+
${PersonFragments.personFilter}
|
|
34367
|
+
`,
|
|
34368
|
+
};
|
|
34369
|
+
const Mutations = {
|
|
34370
|
+
send: gql$1 `
|
|
34371
|
+
mutation SendMessage($data: MessageVOInput) {
|
|
34372
|
+
done: sendMessage(message: $data)
|
|
34373
|
+
}
|
|
34374
|
+
`,
|
|
34375
|
+
delete: gql$1 `
|
|
34376
|
+
mutation DeleteMessage($id: Int!) {
|
|
34377
|
+
done: deleteMessage(id: $id)
|
|
34378
|
+
}
|
|
34379
|
+
`,
|
|
34380
|
+
};
|
|
34381
|
+
class MessageService extends BaseGraphqlService {
|
|
34382
|
+
graphql;
|
|
34383
|
+
translate;
|
|
34384
|
+
modalCtrl;
|
|
34385
|
+
toastController;
|
|
34386
|
+
environment;
|
|
34387
|
+
constructor(graphql, translate, modalCtrl, toastController, environment) {
|
|
34388
|
+
super(graphql, environment);
|
|
34389
|
+
this.graphql = graphql;
|
|
34390
|
+
this.translate = translate;
|
|
34391
|
+
this.modalCtrl = modalCtrl;
|
|
34392
|
+
this.toastController = toastController;
|
|
34393
|
+
this.environment = environment;
|
|
34394
|
+
// For DEV only
|
|
34395
|
+
this._debug = !environment.production;
|
|
34396
|
+
}
|
|
34397
|
+
/**
|
|
34398
|
+
* Send a message to recipient(s)
|
|
34399
|
+
*
|
|
34400
|
+
* @param entity
|
|
34401
|
+
* @param opts
|
|
34402
|
+
*/
|
|
34403
|
+
async send(entity, opts) {
|
|
34404
|
+
// Transform into json
|
|
34405
|
+
const data = entity.asObject(MINIFY_ENTITY_FOR_POD);
|
|
34406
|
+
const now = Date.now();
|
|
34407
|
+
if (this._debug)
|
|
34408
|
+
console.debug(`[message-service] Sending message...`);
|
|
34409
|
+
try {
|
|
34410
|
+
const { done } = await this.graphql.mutate({
|
|
34411
|
+
mutation: Mutations.send,
|
|
34412
|
+
variables: { data },
|
|
34413
|
+
error: { code: SocialErrorCodes.SEND_MESSAGE_ERROR, message: 'SOCIAL.ERROR.SEND_MESSAGE_ERROR' },
|
|
34414
|
+
});
|
|
34415
|
+
if (this._debug)
|
|
34416
|
+
console.debug(`[message-service] Send message [OK] in ${Date.now() - now}ms`);
|
|
34417
|
+
if (done && (!opts || opts.showToast !== false)) {
|
|
34418
|
+
await this.showToast({ type: 'info', message: 'SOCIAL.INFO.MESSAGE_SENT' });
|
|
34419
|
+
}
|
|
34420
|
+
return done;
|
|
34421
|
+
}
|
|
34422
|
+
catch (err) {
|
|
34423
|
+
const error = (err && err.message) || err;
|
|
34424
|
+
console.error(error);
|
|
34425
|
+
// Show error
|
|
34426
|
+
if (!opts || opts.showToast !== false) {
|
|
34427
|
+
const message = error || 'SOCIAL.ERROR.SEND_MESSAGE_ERROR';
|
|
34428
|
+
await this.showToast({ type: 'error', message });
|
|
34429
|
+
}
|
|
34430
|
+
return false;
|
|
34431
|
+
}
|
|
34432
|
+
}
|
|
34433
|
+
/**
|
|
34434
|
+
* Delete a message by ID
|
|
34435
|
+
*
|
|
34436
|
+
* @param id
|
|
34437
|
+
* @param opts
|
|
34438
|
+
*/
|
|
34439
|
+
async delete(id, opts) {
|
|
34440
|
+
const now = Date.now();
|
|
34441
|
+
if (this._debug)
|
|
34442
|
+
console.debug(`[message-service] Deleting message #${id}...`);
|
|
34443
|
+
try {
|
|
34444
|
+
const { done } = await this.graphql.mutate({
|
|
34445
|
+
mutation: Mutations.delete,
|
|
34446
|
+
variables: { id },
|
|
34447
|
+
error: { code: SocialErrorCodes.DELETE_MESSAGE_ERROR, message: 'SOCIAL.ERROR.DELETE_MESSAGE_ERROR' },
|
|
34448
|
+
});
|
|
34449
|
+
if (this._debug)
|
|
34450
|
+
console.debug(`[message-service] Delete message [OK] in ${Date.now() - now}ms`);
|
|
34451
|
+
if (done && (!opts || opts.showToast !== false)) {
|
|
34452
|
+
await this.showToast({ type: 'info', message: 'SOCIAL.INFO.MESSAGE_DELETED' });
|
|
34453
|
+
}
|
|
34454
|
+
return done;
|
|
34455
|
+
}
|
|
34456
|
+
catch (err) {
|
|
34457
|
+
const error = (err && err.message) || err;
|
|
34458
|
+
console.error(error);
|
|
34459
|
+
// Show error
|
|
34460
|
+
if (!opts || opts.showToast !== false) {
|
|
34461
|
+
const message = error || 'SOCIAL.ERROR.DELETE_MESSAGE_ERROR';
|
|
34462
|
+
await this.showToast({ type: 'error', message });
|
|
34463
|
+
}
|
|
34464
|
+
return false;
|
|
34465
|
+
}
|
|
34466
|
+
}
|
|
34467
|
+
async openComposeModal(options) {
|
|
34468
|
+
const hasTopModal = !!(await this.modalCtrl.getTop());
|
|
34469
|
+
const modal = await this.modalCtrl.create({
|
|
34470
|
+
component: MessageModal,
|
|
34471
|
+
componentProps: options,
|
|
34472
|
+
cssClass: hasTopModal && 'stack-modal',
|
|
34473
|
+
});
|
|
34474
|
+
// Open the modal
|
|
34475
|
+
await modal.present();
|
|
34476
|
+
// On dismiss
|
|
34477
|
+
const { data } = await modal.onDidDismiss();
|
|
34478
|
+
if (!data || !(data instanceof Message))
|
|
34479
|
+
return true; // CANCELLED
|
|
34480
|
+
// Send message
|
|
34481
|
+
return await this.send(data, { showToast: options?.showToast });
|
|
34482
|
+
}
|
|
34483
|
+
async load(id, opts) {
|
|
34484
|
+
const { data } = await this.graphql.query({
|
|
34485
|
+
query: Queries.load,
|
|
34486
|
+
variables: {
|
|
34487
|
+
id,
|
|
34488
|
+
},
|
|
34489
|
+
fetchPolicy: 'no-cache',
|
|
34490
|
+
});
|
|
34491
|
+
const entity = opts?.toEntity !== false ? Message.fromObject(data) : data;
|
|
34492
|
+
console.debug(`[message-service] Loaded message #${id}:`, entity);
|
|
34493
|
+
return entity;
|
|
34494
|
+
}
|
|
34495
|
+
/* -- protected methods -- */
|
|
34496
|
+
async showToast(opts) {
|
|
34497
|
+
return Toasts.show(this.toastController, this.translate, {
|
|
34498
|
+
type: 'info',
|
|
34499
|
+
message: 'SOCIAL.INFO.MESSAGE_SENT',
|
|
34500
|
+
...opts,
|
|
34501
|
+
});
|
|
34502
|
+
}
|
|
34503
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageService, deps: [{ token: GraphqlService }, { token: i1$1.TranslateService }, { token: i2$1.ModalController }, { token: i2$1.ToastController }, { token: ENVIRONMENT, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
34504
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageService, providedIn: 'root' });
|
|
34505
|
+
}
|
|
34506
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageService, decorators: [{
|
|
34507
|
+
type: Injectable,
|
|
34508
|
+
args: [{ providedIn: 'root' }]
|
|
34509
|
+
}], ctorParameters: () => [{ type: GraphqlService }, { type: i1$1.TranslateService }, { type: i2$1.ModalController }, { type: i2$1.ToastController }, { type: Environment, decorators: [{
|
|
34510
|
+
type: Optional
|
|
34511
|
+
}, {
|
|
34512
|
+
type: Inject,
|
|
34513
|
+
args: [ENVIRONMENT]
|
|
34514
|
+
}] }] });
|
|
34515
|
+
|
|
34516
|
+
const APP_FEED_SERVICE = new InjectionToken('FeeService');
|
|
34517
|
+
class FeedService extends StartableService {
|
|
34518
|
+
settings;
|
|
34519
|
+
environment;
|
|
34520
|
+
_logPrefix = '[feed-service] ';
|
|
34521
|
+
_state = new RxState();
|
|
34522
|
+
network = inject(NetworkService);
|
|
34523
|
+
modalCtrl = inject(ModalController);
|
|
34524
|
+
messageService = inject(MessageService);
|
|
34525
|
+
personService = inject(PersonService);
|
|
34526
|
+
locale$ = this._state.select('locale');
|
|
34527
|
+
feedUrls$ = this._state.select('feedUrls');
|
|
34528
|
+
get feedUrls() {
|
|
34529
|
+
return this._state.get('feedUrls');
|
|
34530
|
+
}
|
|
34531
|
+
set feedUrls(urls) {
|
|
34532
|
+
this._state.set('feedUrls', () => urls);
|
|
34533
|
+
}
|
|
34534
|
+
constructor(settings, environment) {
|
|
34535
|
+
super(settings);
|
|
34536
|
+
this.settings = settings;
|
|
34537
|
+
this.environment = environment;
|
|
34538
|
+
this._state.connect('locale', this.settings.locale$);
|
|
34539
|
+
this._state.connect('feedUrls', this.locale$.pipe(filter(isNotNilOrBlank), map((locale) => (locale && environment.feed?.jsonFeed?.[locale]) ?? [])));
|
|
34540
|
+
// DEBUG
|
|
34541
|
+
this._debug = !environment.production;
|
|
34542
|
+
if (this._debug)
|
|
34543
|
+
console.debug(`${this._logPrefix}created`);
|
|
34544
|
+
}
|
|
34545
|
+
async ngOnStart() {
|
|
34546
|
+
await Promise.all([this.settings.ready(), this.network.ready()]);
|
|
34547
|
+
return {
|
|
34548
|
+
locale: this.settings.locale,
|
|
34549
|
+
};
|
|
34550
|
+
}
|
|
34551
|
+
watchAll(opts) {
|
|
34552
|
+
if (!this.started) {
|
|
34553
|
+
return from(this.start()).pipe(switchMap$1(() => this.watchAll(opts)));
|
|
34554
|
+
}
|
|
34555
|
+
if (isNotEmptyArray(opts?.urls)) {
|
|
34556
|
+
const locale$ = opts.locale ? of(opts.locale) : this.locale$;
|
|
34557
|
+
return locale$.pipe(mergeMap((locale) => this.loadAll({ locale, ...opts })));
|
|
34558
|
+
}
|
|
34559
|
+
return this.feedUrls$.pipe(filter(isNotEmptyArray), mergeMap((urls) => this.loadAll({ ...opts, urls })));
|
|
34560
|
+
}
|
|
34561
|
+
getHomeUrl(feed) {
|
|
34562
|
+
const feedUrl = feed?.feed_url ?? firstArrayValue(this.feedUrls);
|
|
34563
|
+
return feedUrl ? UrlUtils.getRootUrl(feedUrl) : undefined;
|
|
34564
|
+
}
|
|
34565
|
+
getTagUrl(feed, tag) {
|
|
34566
|
+
if (!feed || !tag)
|
|
34567
|
+
throw new Error("Missing 'feed' or 'tag' argument");
|
|
34568
|
+
const baseUrl = this.getHomeUrl(feed);
|
|
34569
|
+
return feed.tag_template?.replace('{tag}', tag) ?? baseUrl + '/tag/' + tag;
|
|
34570
|
+
}
|
|
34571
|
+
async load(url, opts) {
|
|
34572
|
+
if (!url)
|
|
34573
|
+
throw new Error('Missing url argument');
|
|
34574
|
+
const { data } = await this.loadAll({ ...opts, urls: [url] });
|
|
34575
|
+
return firstArrayValue(data);
|
|
34576
|
+
}
|
|
34577
|
+
async loadAll(opts) {
|
|
34578
|
+
await this.ready();
|
|
34579
|
+
const urls = opts?.urls ?? this.feedUrls;
|
|
34580
|
+
opts = {
|
|
34581
|
+
maxAgeInMonths: opts?.maxAgeInMonths ?? this.environment.feed?.maxAgeInMonths,
|
|
34582
|
+
maxContentLength: opts?.maxContentLength ?? this.environment.feed?.maxContentLength,
|
|
34583
|
+
locale: this.settings.locale,
|
|
34584
|
+
depth: 0,
|
|
34585
|
+
...opts,
|
|
34586
|
+
};
|
|
34587
|
+
const feeds = await Promise.all((urls || []).map(async (url) => {
|
|
34588
|
+
try {
|
|
34589
|
+
// Get JSON
|
|
34590
|
+
const json = await this.network.get(url, { nocache: opts?.cache });
|
|
34591
|
+
console.debug(`${this._logPrefix}Loaded JSON from ${url}`, json);
|
|
34592
|
+
// Resolve feed
|
|
34593
|
+
return await this.resolveFeed(json, url, opts);
|
|
34594
|
+
}
|
|
34595
|
+
catch (err) {
|
|
34596
|
+
if (err?.status === 404) {
|
|
34597
|
+
console.error(`${this._logPrefix} Error while fetching feeds at ${url}. 404 (Not found)`);
|
|
34598
|
+
}
|
|
34599
|
+
else {
|
|
34600
|
+
console.error(`${this._logPrefix} Error while fetching feeds at ${url}`, err);
|
|
34601
|
+
}
|
|
34602
|
+
return Promise.resolve([]);
|
|
34603
|
+
}
|
|
34604
|
+
}));
|
|
34605
|
+
const data = feeds.filter(isNotEmptyArray).flat();
|
|
34606
|
+
// Close opened resources
|
|
34607
|
+
if (opts.depth === 0) {
|
|
34608
|
+
this.onAfterLoadAll();
|
|
34609
|
+
}
|
|
34610
|
+
return { data, total: data.length };
|
|
34611
|
+
}
|
|
34612
|
+
async openEditModal(event, feedItem, opts) {
|
|
34613
|
+
const messageId = +feedItem.id;
|
|
34614
|
+
if (isNilOrNaN(messageId))
|
|
34615
|
+
throw new Error(`Cannot load message - invalid id: ${feedItem.id}`);
|
|
34616
|
+
// Fetch the original message
|
|
34617
|
+
const message = await this.messageService.load(messageId);
|
|
34618
|
+
// Count recipients
|
|
34619
|
+
let recipientFilterCount;
|
|
34620
|
+
if (message?.recipientFilter) {
|
|
34621
|
+
recipientFilterCount = await this.personService.countAll(message?.recipientFilter);
|
|
34622
|
+
}
|
|
34623
|
+
const hasTopModal = !!(await this.modalCtrl.getTop());
|
|
34624
|
+
const modal = await this.modalCtrl.create({
|
|
34625
|
+
component: MessageModal,
|
|
34626
|
+
componentProps: {
|
|
34627
|
+
title: 'SOCIAL.FEED.EDIT_TITLE',
|
|
34628
|
+
suggestFn: (value, filter, sortBy, sortDirection, opts) => this.personService.suggest(value, filter, sortBy, sortDirection, opts),
|
|
34629
|
+
canSelectType: false,
|
|
34630
|
+
canRecipientFilter: false,
|
|
34631
|
+
recipientFilterCount,
|
|
34632
|
+
data: message,
|
|
34633
|
+
debug: true,
|
|
34634
|
+
},
|
|
34635
|
+
cssClass: hasTopModal && 'stack-modal',
|
|
34636
|
+
});
|
|
34637
|
+
// Open the modal
|
|
34638
|
+
await modal.present();
|
|
34639
|
+
// On dismiss
|
|
34640
|
+
const { data } = await modal.onDidDismiss();
|
|
34641
|
+
if (!data || !(data instanceof Message))
|
|
34642
|
+
return true; // CANCELLED
|
|
34643
|
+
return await this.messageService.send(data, opts);
|
|
34644
|
+
}
|
|
34645
|
+
async deleteItem(feedItem) {
|
|
34646
|
+
const messageId = +feedItem.id;
|
|
34647
|
+
if (isNilOrNaN(messageId))
|
|
34648
|
+
throw new Error(`Cannot delete message - invalid id: ${feedItem.id}`);
|
|
34649
|
+
return await this.messageService.delete(messageId);
|
|
34650
|
+
}
|
|
34651
|
+
/* -- protected functions -- */
|
|
34652
|
+
async resolveFeed(json, url, opts) {
|
|
34653
|
+
// Check json is JsonFeed compatible
|
|
34654
|
+
if (JsonFeedUtils.isJsonFeed(json)) {
|
|
34655
|
+
// Migrate from old versions
|
|
34656
|
+
let feed = JsonFeedUtils.migrateFeed(json);
|
|
34657
|
+
// Resolve items (e.g. using feed.next_url)
|
|
34658
|
+
feed = await this.resolveFeedItems(feed, { ...opts, depth: opts?.depth ?? 0 });
|
|
34659
|
+
// Truncate html
|
|
34660
|
+
JsonFeedUtils.truncateFeedItemsHtml(feed, opts);
|
|
34661
|
+
return isNotEmptyArray(feed?.items) ? [feed] : [];
|
|
34662
|
+
}
|
|
34663
|
+
// not resolved (unknown format)
|
|
34664
|
+
return [];
|
|
34665
|
+
}
|
|
34666
|
+
async resolveFeedItems(json, opts) {
|
|
34667
|
+
if (!json)
|
|
34668
|
+
return json;
|
|
34669
|
+
// Load items
|
|
34670
|
+
if (isEmptyArray(json.items) && isNotNilOrBlank(json.next_url)) {
|
|
34671
|
+
const depth = opts?.depth ?? 0;
|
|
34672
|
+
if (depth >= 3) {
|
|
34673
|
+
console.warn(`${this._logPrefix}Max depth reached (${depth}) for ${json.feed_url}`);
|
|
34674
|
+
return json;
|
|
34675
|
+
}
|
|
34676
|
+
// Load items
|
|
34677
|
+
const feed = await this.load(json.next_url, { ...opts, depth: depth + 1 });
|
|
34678
|
+
if (isEmptyArray(feed?.items)) {
|
|
34679
|
+
return json;
|
|
34680
|
+
}
|
|
34681
|
+
// Merge with the input json
|
|
34682
|
+
return {
|
|
34683
|
+
...json,
|
|
34684
|
+
title: json.title ?? feed.title,
|
|
34685
|
+
home_page_url: json.home_page_url ?? feed.home_page_url,
|
|
34686
|
+
authors: isNotEmptyArray(json.authors) ? json.authors : feed.authors,
|
|
34687
|
+
items: feed.items,
|
|
34688
|
+
tag_template: json.tag_template ?? feed.tag_template,
|
|
34689
|
+
};
|
|
34690
|
+
}
|
|
34691
|
+
return json;
|
|
34692
|
+
}
|
|
34693
|
+
onAfterLoadAll() {
|
|
34694
|
+
console.debug(`${this._logPrefix}Feeds loaded`);
|
|
34695
|
+
}
|
|
34696
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedService, deps: [{ token: LocalSettingsService }, { token: ENVIRONMENT, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
34697
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedService });
|
|
34698
|
+
}
|
|
34699
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedService, decorators: [{
|
|
34700
|
+
type: Injectable
|
|
34701
|
+
}], ctorParameters: () => [{ type: LocalSettingsService }, { type: Environment, decorators: [{
|
|
34702
|
+
type: Optional
|
|
34703
|
+
}, {
|
|
34704
|
+
type: Inject,
|
|
34705
|
+
args: [ENVIRONMENT]
|
|
34706
|
+
}] }] });
|
|
34707
|
+
|
|
34708
|
+
class FeedDirective {
|
|
34709
|
+
element;
|
|
34710
|
+
_subscription = new Subscription();
|
|
34711
|
+
platform = inject(PlatformService);
|
|
34712
|
+
locationStrategy = inject(LocationStrategy);
|
|
34713
|
+
navController = inject(NavController);
|
|
34714
|
+
router = inject(Router);
|
|
34715
|
+
route = inject(ActivatedRoute);
|
|
34716
|
+
_feedUrl;
|
|
34717
|
+
_baseUrl;
|
|
34718
|
+
set feedUrl(value) {
|
|
34719
|
+
this._feedUrl = value;
|
|
34720
|
+
this._baseUrl = value && JsonFeedUtils.removeJsonExtension(value);
|
|
34721
|
+
}
|
|
34722
|
+
get feedUrl() {
|
|
34723
|
+
return this._feedUrl;
|
|
34724
|
+
}
|
|
34725
|
+
constructor(element) {
|
|
34726
|
+
this.element = element;
|
|
34727
|
+
// DEBUG
|
|
34728
|
+
//console.debug('[feed-directive] Creating feed directive');
|
|
34729
|
+
}
|
|
34730
|
+
ngAfterViewInit() {
|
|
34731
|
+
console.debug('[feed-directive] Processing anchors');
|
|
34732
|
+
// Listening click events
|
|
34733
|
+
this._subscription.add(this.listenClickEvents(this.element.nativeElement));
|
|
34734
|
+
}
|
|
34735
|
+
ngOnDestroy() {
|
|
34736
|
+
this._subscription.unsubscribe();
|
|
34737
|
+
}
|
|
34738
|
+
listenClickEvents(nativeElement) {
|
|
34739
|
+
console.debug('[feed-directive] Start listening click events...');
|
|
34740
|
+
const subscription = new Subscription();
|
|
34741
|
+
const listener = (event) => this.click(event, nativeElement);
|
|
34742
|
+
const links = nativeElement.querySelectorAll('a');
|
|
34743
|
+
links.forEach((link) => {
|
|
34744
|
+
// DEBUG
|
|
34745
|
+
//console.debug('[feed-directive] Adding click listener to', link);
|
|
34746
|
+
link.addEventListener('click', listener);
|
|
34747
|
+
subscription.add(() => link.removeEventListener('click', listener));
|
|
34748
|
+
});
|
|
34749
|
+
// DEBUG
|
|
34750
|
+
subscription.add(() => console.debug('[feed-directive] Stop listening click events'));
|
|
34751
|
+
return subscription;
|
|
34752
|
+
}
|
|
34753
|
+
async click(event, containerElement) {
|
|
34754
|
+
console.debug('[feed-directive] Processing click event...', event);
|
|
34755
|
+
if (!(event.target instanceof HTMLAnchorElement) && !(event.target?.['tagName'] === 'A')) {
|
|
34756
|
+
console.warn('[feed-directive] Invalid click event target. Should an anchor <a>', event.target);
|
|
34757
|
+
return;
|
|
34758
|
+
}
|
|
34759
|
+
const element = event.target;
|
|
34760
|
+
let href = element.getAttribute('href');
|
|
34761
|
+
const fragment = UrlUtils.getFragment(href);
|
|
34762
|
+
// Fragment
|
|
34763
|
+
if (href?.startsWith('#') && isNotNilOrBlank(fragment)) {
|
|
34764
|
+
event.preventDefault();
|
|
34765
|
+
this.scrollToAnchor(containerElement, fragment);
|
|
34766
|
+
return true;
|
|
34767
|
+
}
|
|
34768
|
+
const routerLink = element.getAttribute('routerLink');
|
|
34769
|
+
if (isNotNilOrBlank(routerLink)) {
|
|
34770
|
+
event.preventDefault();
|
|
34771
|
+
return this.navController.navigateForward(routerLink);
|
|
34772
|
+
}
|
|
34773
|
+
// Resolve relative URL, if baseUrl has been set
|
|
34774
|
+
if (this._baseUrl && UrlUtils.isRelativeUrl(href)) {
|
|
34775
|
+
// Resolve URL, then open using the platform
|
|
34776
|
+
href = UrlUtils.resolveRelativeUrl(this._baseUrl, href);
|
|
34777
|
+
}
|
|
34778
|
+
// Resolve internal URL as an app route
|
|
34779
|
+
if (UrlUtils.isInternalUrl(href)) {
|
|
34780
|
+
const routePath = this.normalizeUrl(href.startsWith('/') ? href : `/${href}`);
|
|
34781
|
+
const urlTree = this.getUrlTree(routePath);
|
|
34782
|
+
event.preventDefault();
|
|
34783
|
+
this.router.navigated = false;
|
|
34784
|
+
// Opening route
|
|
34785
|
+
return this.navController.navigateForward(urlTree);
|
|
34786
|
+
}
|
|
34787
|
+
// External URL: open using the platform
|
|
34788
|
+
event.preventDefault();
|
|
34789
|
+
await this.platform.open(href);
|
|
34790
|
+
return true;
|
|
34791
|
+
}
|
|
34792
|
+
scrollToAnchor(containerElement, anchorId) {
|
|
34793
|
+
if (!containerElement || !anchorId) {
|
|
34794
|
+
console.warn("Missing 'containerElement' or 'fragment' argument.");
|
|
34795
|
+
return;
|
|
34796
|
+
}
|
|
34797
|
+
console.debug('[feed-directive-anchor] Scrolling to anchor #' + anchorId);
|
|
34798
|
+
// Find the target element with the specified fragment ID
|
|
34799
|
+
const targetElement = containerElement.querySelector(`#${anchorId}`);
|
|
34800
|
+
if (targetElement) {
|
|
34801
|
+
// Scroll smoothly to the target element
|
|
34802
|
+
// eslint-disable-next-line @rx-angular/prefer-no-layout-sensitive-apis
|
|
34803
|
+
targetElement.scrollIntoView({
|
|
34804
|
+
behavior: 'smooth', // Smooth scrolling behavior
|
|
34805
|
+
block: 'start', // Align the target element to the top of the container
|
|
34806
|
+
});
|
|
34807
|
+
}
|
|
34808
|
+
else {
|
|
34809
|
+
console.warn(`No element found with the ID "${anchorId}" inside the container.`);
|
|
34810
|
+
}
|
|
34811
|
+
}
|
|
34812
|
+
/**
|
|
34813
|
+
* Transform a relative URL to its absolute representation according to current router state.
|
|
34814
|
+
* @param url Relative URL path.
|
|
34815
|
+
* @return Absolute URL based on the current route.
|
|
34816
|
+
*/
|
|
34817
|
+
normalizeUrl(url) {
|
|
34818
|
+
if (UrlUtils.isExternalUrl(url)) {
|
|
34819
|
+
return url;
|
|
34820
|
+
}
|
|
34821
|
+
if (this._baseUrl && UrlUtils.isRelativeUrl(url)) {
|
|
34822
|
+
return UrlUtils.resolveRelativeUrl(this._baseUrl, url);
|
|
34823
|
+
}
|
|
34824
|
+
const urlTree = this.getUrlTree(url);
|
|
34825
|
+
const serializedUrl = this.router.serializeUrl(urlTree);
|
|
34826
|
+
return this.locationStrategy.prepareExternalUrl(serializedUrl);
|
|
34827
|
+
}
|
|
34828
|
+
getUrlTree(url) {
|
|
34829
|
+
url = UrlUtils.normalizeUrl(url);
|
|
34830
|
+
const urlPath = UrlUtils.stripFragmentAndQuery(url) || UrlUtils.stripFragmentAndQuery(this.router.url);
|
|
34831
|
+
const parsedUrl = this.router.parseUrl(url);
|
|
34832
|
+
const fragment = parsedUrl.fragment;
|
|
34833
|
+
const queryParams = parsedUrl.queryParams;
|
|
34834
|
+
return this.router.createUrlTree([urlPath], { relativeTo: this.route, fragment, queryParams });
|
|
34835
|
+
}
|
|
34836
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
|
|
34837
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: FeedDirective, selector: "feed,[feed]", inputs: { feedUrl: ["feed", "feedUrl"] }, ngImport: i0 });
|
|
34838
|
+
}
|
|
34839
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedDirective, decorators: [{
|
|
34840
|
+
type: Directive,
|
|
34841
|
+
args: [{
|
|
34842
|
+
selector: 'feed,[feed]',
|
|
34843
|
+
}]
|
|
34844
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { feedUrl: [{
|
|
34845
|
+
type: Input,
|
|
34846
|
+
args: [{ alias: 'feed', required: true }]
|
|
34847
|
+
}] } });
|
|
34848
|
+
|
|
34072
34849
|
class FeedsComponent {
|
|
34073
34850
|
environment;
|
|
34074
34851
|
feedService;
|
|
34852
|
+
accountService;
|
|
34853
|
+
alertCtrl;
|
|
34075
34854
|
_state = new RxState();
|
|
34076
34855
|
translate = inject(TranslateService);
|
|
34077
34856
|
platform = inject(PlatformService);
|
|
@@ -34079,9 +34858,11 @@ class FeedsComponent {
|
|
|
34079
34858
|
router = inject(Router);
|
|
34080
34859
|
version;
|
|
34081
34860
|
modalItemId;
|
|
34861
|
+
onRefresh = new EventEmitter();
|
|
34082
34862
|
feeds$ = this._state.select('feeds');
|
|
34083
|
-
|
|
34863
|
+
urls$ = this._state.select('urls');
|
|
34084
34864
|
hasFeeds$ = this._state.select('hasFeeds');
|
|
34865
|
+
userId$ = this._state.select('userId');
|
|
34085
34866
|
debug = false;
|
|
34086
34867
|
mobile;
|
|
34087
34868
|
showHeader = true;
|
|
@@ -34092,17 +34873,19 @@ class FeedsComponent {
|
|
|
34092
34873
|
class = '';
|
|
34093
34874
|
itemId;
|
|
34094
34875
|
filterItem;
|
|
34876
|
+
editItem = new EventEmitter();
|
|
34877
|
+
deleteItem = new EventEmitter();
|
|
34095
34878
|
set feeds(value) {
|
|
34096
34879
|
this._state.set('feeds', () => value);
|
|
34097
34880
|
}
|
|
34098
34881
|
get feeds() {
|
|
34099
34882
|
return this._state.get('feeds');
|
|
34100
34883
|
}
|
|
34101
|
-
set
|
|
34102
|
-
this._state.set('
|
|
34884
|
+
set urls(value) {
|
|
34885
|
+
this._state.set('urls', () => value);
|
|
34103
34886
|
}
|
|
34104
|
-
get
|
|
34105
|
-
return this._state.get('
|
|
34887
|
+
get urls() {
|
|
34888
|
+
return this._state.get('urls');
|
|
34106
34889
|
}
|
|
34107
34890
|
get hasFeeds() {
|
|
34108
34891
|
return this._state.get('hasFeeds');
|
|
@@ -34119,6 +34902,9 @@ class FeedsComponent {
|
|
|
34119
34902
|
get maxContentLength() {
|
|
34120
34903
|
return this._state.get('maxContentLength');
|
|
34121
34904
|
}
|
|
34905
|
+
get peerUrl() {
|
|
34906
|
+
return this._state.get('peerUrl');
|
|
34907
|
+
}
|
|
34122
34908
|
get hostClass() {
|
|
34123
34909
|
const classes = [this.class];
|
|
34124
34910
|
if (this.shape) {
|
|
@@ -34127,16 +34913,25 @@ class FeedsComponent {
|
|
|
34127
34913
|
return classes.filter((cls) => cls).join(' ');
|
|
34128
34914
|
}
|
|
34129
34915
|
modal;
|
|
34130
|
-
constructor(settings, environment, feedService) {
|
|
34916
|
+
constructor(settings, environment, feedService, accountService, alertCtrl) {
|
|
34131
34917
|
this.environment = environment;
|
|
34132
34918
|
this.feedService = feedService;
|
|
34919
|
+
this.accountService = accountService;
|
|
34920
|
+
this.alertCtrl = alertCtrl;
|
|
34133
34921
|
this.mobile = settings.mobile;
|
|
34922
|
+
this._state.connect('locale', settings.locale$);
|
|
34134
34923
|
this._state.connect('hasFeeds', this.feeds$.pipe(map(isNotEmptyArray)));
|
|
34924
|
+
this._state.connect('userId', this.accountService.person$.pipe(map((person) => person?.id)));
|
|
34925
|
+
this._state.connect('peerUrl', this.networkService.peer$.pipe(map((peer) => peer?.url)));
|
|
34135
34926
|
if (this.feedService) {
|
|
34136
|
-
this._state.connect('feeds', this.
|
|
34137
|
-
urls
|
|
34927
|
+
this._state.connect('feeds', merge(this.onRefresh.pipe(map(() => ({ ...this._state.get(), cache: false }))), this._state
|
|
34928
|
+
.select(['urls', 'maxAgeInMonths', 'maxContentLength', 'locale'])
|
|
34929
|
+
.pipe(map(({ urls, maxAgeInMonths, maxContentLength, locale }) => ({ urls, maxAgeInMonths, maxContentLength, locale, cache: true })))).pipe(switchMap(({ urls, maxAgeInMonths, maxContentLength, locale, cache }) => this.feedService.watchAll({
|
|
34930
|
+
urls: urls,
|
|
34138
34931
|
maxAgeInMonths,
|
|
34139
34932
|
maxContentLength,
|
|
34933
|
+
locale,
|
|
34934
|
+
cache,
|
|
34140
34935
|
})), map(({ data }) => data)
|
|
34141
34936
|
// DEBUG
|
|
34142
34937
|
//tap((feeds) => console.debug('[feed-component] feeds', feeds))
|
|
@@ -34144,7 +34939,7 @@ class FeedsComponent {
|
|
|
34144
34939
|
}
|
|
34145
34940
|
}
|
|
34146
34941
|
ngOnInit() {
|
|
34147
|
-
this.
|
|
34942
|
+
this.urls = this.urls ?? null; // Should set the urls, in order to trigger the select in constructor
|
|
34148
34943
|
this.maxAgeInMonths = this.maxAgeInMonths ?? this.environment.feed?.maxAgeInMonths ?? -1;
|
|
34149
34944
|
this.maxContentLength = this.maxContentLength ?? this.environment.feed?.maxContentLength ?? -1;
|
|
34150
34945
|
const self = this;
|
|
@@ -34220,12 +35015,73 @@ class FeedsComponent {
|
|
|
34220
35015
|
getFeedHomeUrl(feed) {
|
|
34221
35016
|
return feed.home_page_url ?? JsonFeedUtils.removeJsonExtension(feed.feed_url) ?? this.feedService.getHomeUrl();
|
|
34222
35017
|
}
|
|
34223
|
-
|
|
34224
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: FeedsComponent, selector: "app-feed", inputs: { debug: "debug", mobile: "mobile", showHeader: "showHeader", showReadMoreButton: "showReadMoreButton", headerColor: "headerColor", cardColor: "cardColor", shape: "shape", class: "class", itemId: "itemId", filterItem: "filterItem", feeds: "feeds", feedUrls: "feedUrls", maxAgeInMonths: "maxAgeInMonths", maxContentLength: "maxContentLength" }, host: { properties: { "class": "this.hostClass" } }, providers: [RxState], viewQueries: [{ propertyName: "modal", first: true, predicate: ["modal"], descendants: true }], ngImport: i0, template: "<!-- debug -->\n@if (debug) {\n <app-debug [title]=\"'Feed'\">\n <p>\n hasFeeds?: {{ hasFeeds$ | async }}\n <br />\n feedUrls: {{ feedUrls$ | async | json }}\n <br />\n shape: {{ shape }}\n </p>\n </app-debug>\n}\n\n@let feeds = feeds$ | async;\n@if (feeds | isNotEmptyArray) {\n <!-- top header -->\n @if (showHeader && (feeds | arrayFirst); as feed) {\n <ion-item lines=\"none\" [color]=\"headerColor\" class=\"feed-header shape-{{ shape }}\">\n <ion-icon slot=\"start\" name=\"megaphone\"></ion-icon>\n <ion-label>\n <b>{{ feed.title || ('SOCIAL.FEED.NEWS' | translate) }}</b>\n </ion-label>\n <ion-button slot=\"end\" fill=\"clear\" (click)=\"openFeedHome()\" shape=\"\">\n <ion-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 <div class=\"feed-content shape-{{ shape }} ion-no-padding\" [class.has-header]=\"showHeader\">\n <!-- feeds -->\n @for (feed of feeds; track feed.feed_url; let firstFeed = $first; let lastFeed = $last) {\n <!-- items -->\n @for (item of feed.items | arrayFilter: filterItem; track item.id; let firstItem = $first; let lastItem = $last) {\n <ion-card\n [class.first]=\"firstFeed && firstItem\"\n [class.last]=\"lastFeed && lastItem\"\n [color]=\"cardColor !== 'light-transparent' ? cardColor : undefined\"\n >\n <ion-card-header>\n <ion-card-subtitle style=\"vertical-align: middle\">\n <!-- Authors -->\n @for (author of item.authors || feed.authors; track author) {\n @if (author.name || author.avatar) {\n <ion-chip (click)=\"openUrl(author.url)\" tappable>\n @if (author.avatar) {\n <ion-avatar>\n <ion-img [src]=\"author.avatar\" [alt]=\"author.name\"></ion-img>\n </ion-avatar>\n }\n @if (author.name) {\n <ion-label class=\"author\">{{ author.name }}</ion-label>\n }\n </ion-chip>\n }\n }\n <ion-note class=\"ion-float-end\">\n <small>{{ item.date_published | dateFromNow }}</small>\n </ion-note>\n </ion-card-subtitle>\n\n <!-- title -->\n <ion-card-title (click)=\"openFeedItem(item, feed)\" tappable>{{ item?.title }}</ion-card-title>\n\n <!-- tags -->\n @let tags = item | map: getTags;\n @if (tags | isNotEmptyArray) {\n <ion-text class=\"tags\">\n @for (tag of tags; track tag; let last = $last) {\n <a (click)=\"openTag(feed, tag)\" tappable>\n <ion-text>#{{ tag }}</ion-text>\n </a>\n @if (!last) {\n \n }\n }\n </ion-text>\n }\n </ion-card-header>\n\n <!-- Feed content -->\n <ion-card-content>\n <ion-text [feed]=\"item.url || feed.feed_url\">\n @if (item.content_html) {\n <p [innerHTML]=\"item.content_html\"></p>\n } @else if (item.content_text) {\n <p>\n <markdown [data]=\"item.content_text\" emoji></markdown>\n </p>\n }\n </ion-text>\n </ion-card-content>\n\n @if (showReadMoreButton) {\n <ion-button (click)=\"openFeedItem(item, feed)\" class=\"ion-float-end\" fill=\"clear\">\n <ion-label>{{ (item.truncated ? 'SOCIAL.FEED.READ_MORE' : 'COMMON.BTN_SHOW') | translate }}</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n }\n </ion-card>\n }\n }\n </div>\n}\n\n<ion-modal #modal [showBackdrop]=\"false\"\n class=\"stack-modal\" [class.modal-large]=\"!mobile\">\n <ng-template>\n <ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ 'SOCIAL.FEED.NEWS' | translate }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"modal.dismiss()\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content>\n <div class=\"ion-padding\">\n <app-feed\n [feedUrls]=\"feedUrls\"\n [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [maxContentLength]=\"-1\"\n [maxAgeInMonths]=\"-1\"\n [itemId]=\"modalItemId\"\n cardColor=\"light\"\n [debug]=\"debug\"\n ></app-feed>\n </div>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [":host{display:block;height:calc(100% - 10px);max-height:fit-content;overflow:hidden;--feed-header-height: 48px;-webkit-box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px rgba(0,0,0,.14),0 1px 5px rgba(0,0,0,.12);-webkit-margin-start:10px;margin-inline-start:10px;-webkit-margin-end:10px;margin-inline-end:10px;margin-top:0;margin-bottom:10px;--feed-border-radius: 4px;border-radius:var(--feed-border-radius);--ion-card-background: rgba(var(--ion-background-color-rgb), .6)}:host.shape-round{--feed-border-radius: 12px}ion-button{text-transform:unset;--color: rgba(var(--ion-color-contrast-rgb), .7)}ion-item.feed-header{height:var(--feed-header-height);margin:0;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);border-radius:var(--feed-border-radius) var(--feed-border-radius) 0 0}ion-item.feed-header ion-icon[slot=start]{-webkit-margin-end:8px;margin-inline-end:8px}.feed-content{height:auto;overflow-y:auto;--margin-bottom: 8px}.feed-content.has-header{height:calc(100% - var(--feed-header-height, 0))}.feed-content ion-card{--top-radius: 4px;--bottom-radius: 4px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin:0 0 var(--margin-bottom) 0;border-radius:var(--top-radius) var(--top-radius) var(--bottom-radius) var(--bottom-radius);--ion-card-color-contrast-rgb: var(--ion-color-contrast-rgb, var(--ion-color-dark-rgb, 0, 0, 0));--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.first{--top-radius: 0}.feed-content ion-card.last{--margin-bottom: 0;--bottom-radius: 0}.feed-content ion-card ion-card-header ion-card-title{--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card ion-card-header ion-card-subtitle ion-chip{--background: transparent;--border-color: transparent;--border-width: 0}.feed-content ion-card ion-card-header ion-card-subtitle ion-chip ion-avatar{--color: rgba(var(--ion-card-color-contrast-rgb), .6);border:1px solid var(--color)}.feed-content ion-card ion-card-header ion-card-subtitle ion-chip ion-label{--color: rgba(var(--ion-card-color-contrast-rgb), .8);color:var(--color)}.feed-content ion-card ion-card-header ion-card-subtitle ion-note{--color: rgba(var(--ion-card-color-contrast-rgb), .6);color:var(--color)}.feed-content ion-card ion-card-header .tags{display:inline}.feed-content ion-card ion-card-header .tags a{color:rgba(var(--ion-card-color-contrast-rgb),.6)!important}.feed-content ion-card ion-card-content ion-text ::ng-deep img{max-width:100%;height:auto!important}\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.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.IonChip, selector: "ion-chip", inputs: ["color", "disabled", "mode", "outline"] }, { kind: "component", type: i2$1.IonContent, selector: "ion-content", inputs: ["color", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$1.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { 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: "component", type: i2$1.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$1.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonModal, selector: "ion-modal" }, { kind: "directive", type: i1$1.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: i4$2.MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }, { kind: "component", type: DebugComponent, selector: "app-debug", inputs: ["titlePrefix", "title", "enable", "expanded"] }, { kind: "directive", type: MarkdownDirective, selector: "markdown,[markdown]" }, { kind: "component", type: FeedsComponent, selector: "app-feed", inputs: ["debug", "mobile", "showHeader", "showReadMoreButton", "headerColor", "cardColor", "shape", "class", "itemId", "filterItem", "feeds", "feedUrls", "maxAgeInMonths", "maxContentLength"] }, { kind: "directive", type: FeedDirective, selector: "feed,[feed]", inputs: ["feed"] }, { kind: "pipe", type: i3$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i3$1.JsonPipe, name: "json" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }, { kind: "pipe", type: DateFromNowPipe, name: "dateFromNow" }, { kind: "pipe", type: NotEmptyArrayPipe, name: "isNotEmptyArray" }, { kind: "pipe", type: ArrayFirstPipe, name: "arrayFirst" }, { kind: "pipe", type: ArrayFilterPipe, name: "arrayFilter" }, { kind: "pipe", type: MapPipe, name: "map" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
35018
|
+
/**
|
|
35019
|
+
* Check if the current user is the author of the feed item
|
|
35020
|
+
*/
|
|
35021
|
+
canEditItem = (item, userId, feed) => {
|
|
35022
|
+
if (!item || isNil(userId))
|
|
35023
|
+
return false; // No item, or user not connected
|
|
35024
|
+
// DEBUG
|
|
35025
|
+
//console.debug('[feed-component] canEditItem...');
|
|
35026
|
+
// Check feed URL point on the current peer (/api/feed/ ...)
|
|
35027
|
+
if (!item.url?.startsWith(this.peerUrl))
|
|
35028
|
+
return false;
|
|
35029
|
+
// Check item authors first
|
|
35030
|
+
let isAuthor = false;
|
|
35031
|
+
if (isNotEmptyArray(item.authors)) {
|
|
35032
|
+
isAuthor = item.authors.some((author) => +author.id === userId);
|
|
35033
|
+
}
|
|
35034
|
+
// Check feed authors
|
|
35035
|
+
if (!isAuthor && isNotEmptyArray(feed?.authors)) {
|
|
35036
|
+
isAuthor = feed.authors.some((author) => +author.id === userId);
|
|
35037
|
+
}
|
|
35038
|
+
return isAuthor || this.accountService.isAdmin();
|
|
35039
|
+
};
|
|
35040
|
+
/**
|
|
35041
|
+
* Handle edit item action
|
|
35042
|
+
*/
|
|
35043
|
+
async onEditItem(event, item) {
|
|
35044
|
+
if (event) {
|
|
35045
|
+
event.preventDefault();
|
|
35046
|
+
event.stopPropagation();
|
|
35047
|
+
}
|
|
35048
|
+
if (this.editItem.observed) {
|
|
35049
|
+
this.editItem.emit(item);
|
|
35050
|
+
}
|
|
35051
|
+
const done = await this.feedService?.openEditModal(event, item);
|
|
35052
|
+
if (done) {
|
|
35053
|
+
// force a refresh of the feeds
|
|
35054
|
+
this.onRefresh.emit();
|
|
35055
|
+
}
|
|
35056
|
+
}
|
|
35057
|
+
/**
|
|
35058
|
+
* Handle delete item action
|
|
35059
|
+
*/
|
|
35060
|
+
async onDeleteItem(event, item) {
|
|
35061
|
+
if (event) {
|
|
35062
|
+
event.preventDefault();
|
|
35063
|
+
event.stopPropagation();
|
|
35064
|
+
}
|
|
35065
|
+
// Ask for confirmation before deletion
|
|
35066
|
+
const confirmed = await Alerts.askActionConfirmation(this.alertCtrl, this.translate, true, event);
|
|
35067
|
+
if (confirmed !== true) {
|
|
35068
|
+
return; // User cancelled or dismissed
|
|
35069
|
+
}
|
|
35070
|
+
if (this.deleteItem.observed) {
|
|
35071
|
+
this.deleteItem.emit(item);
|
|
35072
|
+
}
|
|
35073
|
+
const done = await this.feedService?.deleteItem(item);
|
|
35074
|
+
if (done) {
|
|
35075
|
+
// force a refresh of the feeds
|
|
35076
|
+
this.onRefresh.emit();
|
|
35077
|
+
}
|
|
35078
|
+
}
|
|
35079
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedsComponent, deps: [{ token: LocalSettingsService }, { token: ENVIRONMENT }, { token: APP_FEED_SERVICE, optional: true }, { token: AccountService }, { token: i2$1.AlertController }], target: i0.ɵɵFactoryTarget.Component });
|
|
35080
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: FeedsComponent, selector: "app-feed", inputs: { debug: "debug", mobile: "mobile", showHeader: "showHeader", showReadMoreButton: "showReadMoreButton", headerColor: "headerColor", cardColor: "cardColor", shape: "shape", class: "class", itemId: "itemId", filterItem: "filterItem", feeds: "feeds", urls: "urls", maxAgeInMonths: "maxAgeInMonths", maxContentLength: "maxContentLength" }, outputs: { editItem: "editItem", deleteItem: "deleteItem" }, host: { properties: { "class": "this.hostClass" } }, providers: [RxState], viewQueries: [{ propertyName: "modal", first: true, predicate: ["modal"], descendants: true }], ngImport: i0, template: "<!-- debug -->\n@if (debug) {\n <app-debug [title]=\"'Feed'\">\n <p>\n hasFeeds?: {{ hasFeeds$ | async }}\n <br />\n urls: {{ urls$ | async | json }}\n <br />\n shape: {{ shape }}\n </p>\n </app-debug>\n}\n\n@let feeds = feeds$ | async;\n@let userId = userId$ | async;\n\n@if (feeds | isNotEmptyArray) {\n <!-- top header -->\n @if (showHeader && (feeds | arrayFirst); as feed) {\n <ion-item lines=\"none\" [color]=\"headerColor\" class=\"feed-header shape-{{ shape }}\">\n <ion-icon slot=\"start\" name=\"megaphone\"></ion-icon>\n <ion-label>\n <b>{{ feed.title || ('SOCIAL.FEED.NEWS' | translate) }}</b>\n </ion-label>\n <ion-button slot=\"end\" fill=\"clear\" (click)=\"openFeedHome()\" shape=\"\">\n <ion-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 <div class=\"feed-content shape-{{ shape }} ion-no-padding\" [class.has-header]=\"showHeader\">\n <!-- feeds -->\n @for (feed of feeds; track feed.feed_url; let firstFeed = $first; let lastFeed = $last) {\n <!-- items -->\n @for (item of feed.items | arrayFilter: filterItem; track item.id; let firstItem = $first; let lastItem = $last) {\n <ion-card\n [class.first]=\"firstFeed && firstItem\"\n [class.last]=\"lastFeed && lastItem\"\n [color]=\"cardColor !== 'light-transparent' ? cardColor : undefined\"\n class=\"feed-item-card\"\n >\n <ion-card-header>\n <ion-card-subtitle style=\"vertical-align: middle\">\n <!-- Authors -->\n @for (author of item.authors || feed.authors; track author) {\n @if (author.name || author.avatar) {\n <ion-chip (click)=\"openUrl(author.url)\" tappable>\n @if (author.avatar) {\n <ion-avatar>\n <ion-img [src]=\"author.avatar\" [alt]=\"author.name\"></ion-img>\n </ion-avatar>\n }\n @if (author.name) {\n <ion-label class=\"author\">{{ author.name }}</ion-label>\n }\n </ion-chip>\n }\n }\n <ion-note class=\"ion-float-end\">\n <small>{{ item.date_published | dateFromNow }}</small>\n </ion-note>\n </ion-card-subtitle>\n\n <!-- title -->\n <ion-card-title (click)=\"openFeedItem(item, feed)\" tappable>{{ item?.title }}</ion-card-title>\n\n <!-- tags -->\n @let tags = item | map: getTags;\n @if (tags | isNotEmptyArray) {\n <ion-text class=\"tags\">\n @for (tag of tags; track tag; let last = $last) {\n <a (click)=\"openTag(feed, tag)\" tappable>\n <ion-text>#{{ tag }}</ion-text>\n </a>\n @if (!last) {\n \n }\n }\n </ion-text>\n }\n </ion-card-header>\n\n <!-- Feed content -->\n <ion-card-content>\n <ion-text [feed]=\"item.url || feed.feed_url\">\n @if (item.content_html) {\n <p [innerHTML]=\"item.content_html\"></p>\n } @else if (item.content_text) {\n <p>\n <markdown [data]=\"item.content_text\" emoji></markdown>\n </p>\n }\n </ion-text>\n </ion-card-content>\n\n @let editable = canEditItem(item, userId, feed);\n @if (editable || showReadMoreButton) {\n @if (editable) {\n\n <!-- Delete button (visible hover)-->\n <button\n mat-icon-button\n (click)=\"onDeleteItem($event, item)\"\n class=\"visible-hover ion-float-start\"\n [title]=\"'COMMON.BTN_DELETE' | translate\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n <!-- Edit button (visible hover) -->\n <ion-button\n (click)=\"onEditItem($event, item)\"\n class=\"visible-hover ion-float-start\" fill=\"clear\"\n >\n <mat-icon slot=\"start\">edit</mat-icon>\n<!-- <ion-icon name=\"pencil\" slot=\"start\"></ion-icon>-->\n <ion-label translate>COMMON.BTN_EDIT</ion-label>\n </ion-button>\n }\n @if (showReadMoreButton) {\n <ion-button (click)=\"openFeedItem(item, feed)\" class=\"ion-float-end\" fill=\"clear\">\n <ion-label>{{ (item.truncated ? 'SOCIAL.FEED.READ_MORE' : 'COMMON.BTN_SHOW') | translate }}</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n }\n }\n </ion-card>\n }\n }\n </div>\n}\n\n<!-- Details modal -->\n<ion-modal #modal [showBackdrop]=\"false\"\n class=\"stack-modal\" [class.modal-large]=\"!mobile\">\n <ng-template>\n <ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ 'SOCIAL.FEED.NEWS' | translate }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"modal.dismiss()\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content>\n <div class=\"ion-padding\">\n <app-feed\n [urls]=\"urls\"\n [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [maxContentLength]=\"-1\"\n [maxAgeInMonths]=\"-1\"\n [itemId]=\"modalItemId\"\n cardColor=\"light\"\n [debug]=\"debug\"\n ></app-feed>\n </div>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [":host{display:block;height:calc(100% - 10px);max-height:fit-content;overflow:hidden;--feed-header-height: 48px;-webkit-box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px rgba(0,0,0,.14),0 1px 5px rgba(0,0,0,.12);-webkit-margin-start:10px;margin-inline-start:10px;-webkit-margin-end:10px;margin-inline-end:10px;margin-top:0;margin-bottom:10px;--feed-border-radius: 4px;border-radius:var(--feed-border-radius);--ion-card-background: rgba(var(--ion-background-color-rgb), .6)}:host.shape-round{--feed-border-radius: 12px}ion-button{text-transform:unset;--color: rgba(var(--ion-color-contrast-rgb), .7)}ion-item.feed-header{height:var(--feed-header-height);margin:0;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);border-radius:var(--feed-border-radius) var(--feed-border-radius) 0 0}ion-item.feed-header ion-icon[slot=start]{-webkit-margin-end:8px;margin-inline-end:8px}.feed-content{height:auto;overflow-y:auto;--margin-bottom: 8px}.feed-content.has-header{height:calc(100% - var(--feed-header-height, 0))}.feed-content ion-card.feed-item-card{--top-radius: 4px;--bottom-radius: 4px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin:0 0 var(--margin-bottom) 0;border-radius:var(--top-radius) var(--top-radius) var(--bottom-radius) var(--bottom-radius);--ion-card-color-contrast-rgb: var(--ion-color-contrast-rgb, var(--ion-color-dark-rgb, 0, 0, 0));--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card.first{--top-radius: 0}.feed-content ion-card.feed-item-card.last{--margin-bottom: 0;--bottom-radius: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-title{--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip{--background: transparent;--border-color: transparent;--border-width: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-avatar{--color: rgba(var(--ion-card-color-contrast-rgb), .6);border:1px solid var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-label{--color: rgba(var(--ion-card-color-contrast-rgb), .8);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-note{--color: rgba(var(--ion-card-color-contrast-rgb), .6);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header .tags{display:inline}.feed-content ion-card.feed-item-card ion-card-header .tags a{color:rgba(var(--ion-card-color-contrast-rgb),.6)!important}.feed-content ion-card.feed-item-card ion-card-content ion-text ::ng-deep img{max-width:100%;height:auto!important}.feed-content ion-card.feed-item-card .visible-hover{opacity:0;transition:opacity .2s ease-in-out}.feed-content ion-card.feed-item-card:hover .visible-hover{opacity:1}\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.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.IonChip, selector: "ion-chip", inputs: ["color", "disabled", "mode", "outline"] }, { kind: "component", type: i2$1.IonContent, selector: "ion-content", inputs: ["color", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$1.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { 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: "component", type: i2$1.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$1.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonModal, selector: "ion-modal" }, { kind: "directive", type: i1$1.TranslateDirective, selector: "[translate],[ngx-translate]", inputs: ["translate", "translateParams"] }, { kind: "component", type: i4$2.MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }, { kind: "component", type: i6$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: DebugComponent, selector: "app-debug", inputs: ["titlePrefix", "title", "enable", "expanded"] }, { kind: "directive", type: MarkdownDirective, selector: "markdown,[markdown]" }, { kind: "component", type: FeedsComponent, selector: "app-feed", inputs: ["debug", "mobile", "showHeader", "showReadMoreButton", "headerColor", "cardColor", "shape", "class", "itemId", "filterItem", "feeds", "urls", "maxAgeInMonths", "maxContentLength"], outputs: ["editItem", "deleteItem"] }, { kind: "directive", type: FeedDirective, selector: "feed,[feed]", inputs: ["feed"] }, { kind: "pipe", type: i3$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i3$1.JsonPipe, name: "json" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }, { kind: "pipe", type: DateFromNowPipe, name: "dateFromNow" }, { kind: "pipe", type: NotEmptyArrayPipe, name: "isNotEmptyArray" }, { kind: "pipe", type: ArrayFirstPipe, name: "arrayFirst" }, { kind: "pipe", type: ArrayFilterPipe, name: "arrayFilter" }, { kind: "pipe", type: MapPipe, name: "map" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
34225
35081
|
}
|
|
34226
35082
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedsComponent, decorators: [{
|
|
34227
35083
|
type: Component,
|
|
34228
|
-
args: [{ selector: 'app-feed', providers: [RxState], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- debug -->\n@if (debug) {\n <app-debug [title]=\"'Feed'\">\n <p>\n hasFeeds?: {{ hasFeeds$ | async }}\n <br />\n
|
|
35084
|
+
args: [{ selector: 'app-feed', providers: [RxState], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- debug -->\n@if (debug) {\n <app-debug [title]=\"'Feed'\">\n <p>\n hasFeeds?: {{ hasFeeds$ | async }}\n <br />\n urls: {{ urls$ | async | json }}\n <br />\n shape: {{ shape }}\n </p>\n </app-debug>\n}\n\n@let feeds = feeds$ | async;\n@let userId = userId$ | async;\n\n@if (feeds | isNotEmptyArray) {\n <!-- top header -->\n @if (showHeader && (feeds | arrayFirst); as feed) {\n <ion-item lines=\"none\" [color]=\"headerColor\" class=\"feed-header shape-{{ shape }}\">\n <ion-icon slot=\"start\" name=\"megaphone\"></ion-icon>\n <ion-label>\n <b>{{ feed.title || ('SOCIAL.FEED.NEWS' | translate) }}</b>\n </ion-label>\n <ion-button slot=\"end\" fill=\"clear\" (click)=\"openFeedHome()\" shape=\"\">\n <ion-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 <div class=\"feed-content shape-{{ shape }} ion-no-padding\" [class.has-header]=\"showHeader\">\n <!-- feeds -->\n @for (feed of feeds; track feed.feed_url; let firstFeed = $first; let lastFeed = $last) {\n <!-- items -->\n @for (item of feed.items | arrayFilter: filterItem; track item.id; let firstItem = $first; let lastItem = $last) {\n <ion-card\n [class.first]=\"firstFeed && firstItem\"\n [class.last]=\"lastFeed && lastItem\"\n [color]=\"cardColor !== 'light-transparent' ? cardColor : undefined\"\n class=\"feed-item-card\"\n >\n <ion-card-header>\n <ion-card-subtitle style=\"vertical-align: middle\">\n <!-- Authors -->\n @for (author of item.authors || feed.authors; track author) {\n @if (author.name || author.avatar) {\n <ion-chip (click)=\"openUrl(author.url)\" tappable>\n @if (author.avatar) {\n <ion-avatar>\n <ion-img [src]=\"author.avatar\" [alt]=\"author.name\"></ion-img>\n </ion-avatar>\n }\n @if (author.name) {\n <ion-label class=\"author\">{{ author.name }}</ion-label>\n }\n </ion-chip>\n }\n }\n <ion-note class=\"ion-float-end\">\n <small>{{ item.date_published | dateFromNow }}</small>\n </ion-note>\n </ion-card-subtitle>\n\n <!-- title -->\n <ion-card-title (click)=\"openFeedItem(item, feed)\" tappable>{{ item?.title }}</ion-card-title>\n\n <!-- tags -->\n @let tags = item | map: getTags;\n @if (tags | isNotEmptyArray) {\n <ion-text class=\"tags\">\n @for (tag of tags; track tag; let last = $last) {\n <a (click)=\"openTag(feed, tag)\" tappable>\n <ion-text>#{{ tag }}</ion-text>\n </a>\n @if (!last) {\n \n }\n }\n </ion-text>\n }\n </ion-card-header>\n\n <!-- Feed content -->\n <ion-card-content>\n <ion-text [feed]=\"item.url || feed.feed_url\">\n @if (item.content_html) {\n <p [innerHTML]=\"item.content_html\"></p>\n } @else if (item.content_text) {\n <p>\n <markdown [data]=\"item.content_text\" emoji></markdown>\n </p>\n }\n </ion-text>\n </ion-card-content>\n\n @let editable = canEditItem(item, userId, feed);\n @if (editable || showReadMoreButton) {\n @if (editable) {\n\n <!-- Delete button (visible hover)-->\n <button\n mat-icon-button\n (click)=\"onDeleteItem($event, item)\"\n class=\"visible-hover ion-float-start\"\n [title]=\"'COMMON.BTN_DELETE' | translate\"\n >\n <mat-icon>delete</mat-icon>\n </button>\n <!-- Edit button (visible hover) -->\n <ion-button\n (click)=\"onEditItem($event, item)\"\n class=\"visible-hover ion-float-start\" fill=\"clear\"\n >\n <mat-icon slot=\"start\">edit</mat-icon>\n<!-- <ion-icon name=\"pencil\" slot=\"start\"></ion-icon>-->\n <ion-label translate>COMMON.BTN_EDIT</ion-label>\n </ion-button>\n }\n @if (showReadMoreButton) {\n <ion-button (click)=\"openFeedItem(item, feed)\" class=\"ion-float-end\" fill=\"clear\">\n <ion-label>{{ (item.truncated ? 'SOCIAL.FEED.READ_MORE' : 'COMMON.BTN_SHOW') | translate }}</ion-label>\n <ion-icon slot=\"end\" name=\"chevron-forward-outline\"></ion-icon>\n </ion-button>\n }\n }\n </ion-card>\n }\n }\n </div>\n}\n\n<!-- Details modal -->\n<ion-modal #modal [showBackdrop]=\"false\"\n class=\"stack-modal\" [class.modal-large]=\"!mobile\">\n <ng-template>\n <ion-header>\n <ion-toolbar color=\"secondary\">\n <ion-title>{{ 'SOCIAL.FEED.NEWS' | translate }}</ion-title>\n <ion-buttons slot=\"end\">\n <ion-button (click)=\"modal.dismiss()\">\n <ion-icon slot=\"icon-only\" name=\"close\"></ion-icon>\n </ion-button>\n </ion-buttons>\n </ion-toolbar>\n </ion-header>\n <ion-content>\n <div class=\"ion-padding\">\n <app-feed\n [urls]=\"urls\"\n [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [maxContentLength]=\"-1\"\n [maxAgeInMonths]=\"-1\"\n [itemId]=\"modalItemId\"\n cardColor=\"light\"\n [debug]=\"debug\"\n ></app-feed>\n </div>\n </ion-content>\n </ng-template>\n</ion-modal>\n", styles: [":host{display:block;height:calc(100% - 10px);max-height:fit-content;overflow:hidden;--feed-header-height: 48px;-webkit-box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12);box-shadow:0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px rgba(0,0,0,.14),0 1px 5px rgba(0,0,0,.12);-webkit-margin-start:10px;margin-inline-start:10px;-webkit-margin-end:10px;margin-inline-end:10px;margin-top:0;margin-bottom:10px;--feed-border-radius: 4px;border-radius:var(--feed-border-radius);--ion-card-background: rgba(var(--ion-background-color-rgb), .6)}:host.shape-round{--feed-border-radius: 12px}ion-button{text-transform:unset;--color: rgba(var(--ion-color-contrast-rgb), .7)}ion-item.feed-header{height:var(--feed-header-height);margin:0;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);border-radius:var(--feed-border-radius) var(--feed-border-radius) 0 0}ion-item.feed-header ion-icon[slot=start]{-webkit-margin-end:8px;margin-inline-end:8px}.feed-content{height:auto;overflow-y:auto;--margin-bottom: 8px}.feed-content.has-header{height:calc(100% - var(--feed-header-height, 0))}.feed-content ion-card.feed-item-card{--top-radius: 4px;--bottom-radius: 4px;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);margin:0 0 var(--margin-bottom) 0;border-radius:var(--top-radius) var(--top-radius) var(--bottom-radius) var(--bottom-radius);--ion-card-color-contrast-rgb: var(--ion-color-contrast-rgb, var(--ion-color-dark-rgb, 0, 0, 0));--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card.first{--top-radius: 0}.feed-content ion-card.feed-item-card.last{--margin-bottom: 0;--bottom-radius: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-title{--color: rgba(var(--ion-card-color-contrast-rgb), .87)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip{--background: transparent;--border-color: transparent;--border-width: 0}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-avatar{--color: rgba(var(--ion-card-color-contrast-rgb), .6);border:1px solid var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-chip ion-label{--color: rgba(var(--ion-card-color-contrast-rgb), .8);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header ion-card-subtitle ion-note{--color: rgba(var(--ion-card-color-contrast-rgb), .6);color:var(--color)}.feed-content ion-card.feed-item-card ion-card-header .tags{display:inline}.feed-content ion-card.feed-item-card ion-card-header .tags a{color:rgba(var(--ion-card-color-contrast-rgb),.6)!important}.feed-content ion-card.feed-item-card ion-card-content ion-text ::ng-deep img{max-width:100%;height:auto!important}.feed-content ion-card.feed-item-card .visible-hover{opacity:0;transition:opacity .2s ease-in-out}.feed-content ion-card.feed-item-card:hover .visible-hover{opacity:1}\n"] }]
|
|
34229
35085
|
}], ctorParameters: () => [{ type: LocalSettingsService }, { type: Environment, decorators: [{
|
|
34230
35086
|
type: Inject,
|
|
34231
35087
|
args: [ENVIRONMENT]
|
|
@@ -34234,7 +35090,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
34234
35090
|
}, {
|
|
34235
35091
|
type: Inject,
|
|
34236
35092
|
args: [APP_FEED_SERVICE]
|
|
34237
|
-
}] }], propDecorators: { debug: [{
|
|
35093
|
+
}] }, { type: AccountService }, { type: i2$1.AlertController }], propDecorators: { debug: [{
|
|
34238
35094
|
type: Input
|
|
34239
35095
|
}], mobile: [{
|
|
34240
35096
|
type: Input
|
|
@@ -34254,9 +35110,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
34254
35110
|
type: Input
|
|
34255
35111
|
}], filterItem: [{
|
|
34256
35112
|
type: Input
|
|
35113
|
+
}], editItem: [{
|
|
35114
|
+
type: Output
|
|
35115
|
+
}], deleteItem: [{
|
|
35116
|
+
type: Output
|
|
34257
35117
|
}], feeds: [{
|
|
34258
35118
|
type: Input
|
|
34259
|
-
}],
|
|
35119
|
+
}], urls: [{
|
|
34260
35120
|
type: Input
|
|
34261
35121
|
}], maxAgeInMonths: [{
|
|
34262
35122
|
type: Input
|
|
@@ -34643,11 +35503,11 @@ class HomePage extends RxState {
|
|
|
34643
35503
|
this.cd.markForCheck();
|
|
34644
35504
|
}
|
|
34645
35505
|
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 });
|
|
34646
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: HomePage, selector: "app-page-home", viewQueries: [{ propertyName: "content", first: true, predicate: IonContent, descendants: true, static: true }], 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\" [class.has-scrollbar]=\"hasScrollbar\" no-padding-xs>\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 hasFeed = hasFeed$ | async;\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col size=\"0\" [sizeXl]=\"hasFeed ? 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\">\n {{ item.matIcon }}\n </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]=\"hasFeed ? 1 : 0\"></ion-col>\n <ion-col [class.feed]=\"hasFeed\" [size]=\"hasFeed ? 12 : 0\" [sizeXl]=\"hasFeed ? 4 : 0\">\n @if (showFeed) {\n @if (mobile) {\n <app-feed\n #feed\n shape=\"round\"\n [feedUrls]=\"feedUrls\"\n [maxAgeInMonths]=\"feedMaxAgeInMonths\"\n [maxContentLength]=\"feedMaxContentLength\"\n (ngInit)=\"initFeed(feed)\"\n @fadeInAnimation\n ></app-feed>\n } @else {\n <app-feed\n #feed\n shape=\"round\"\n [debug]=\"false\"\n [feedUrls]=\"feedUrls\"\n [maxAgeInMonths]=\"feedMaxAgeInMonths\"\n [maxContentLength]=\"feedMaxContentLength\"\n (ngInit)=\"initFeed(feed)\"\n @slideInAnimation\n @fadeInAnimation\n ></app-feed>\n }\n }\n </ion-col>\n </ion-row>\n </ion-grid>\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 }\n\n <!-- Bottom banner -->\n @if (showPartnerBanner || showLegalInformation) {\n <ion-grid\n class=\"bottom-banner ion-text-center\"\n [class.floating]=\"!mobile && (pageHistory | isEmptyArray)\"\n @fadeInAnimation\n >\n <ion-row>\n <!-- partners logos -->\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\n <!-- legal information -->\n @if (showLegalInformation) {\n <ion-col size=\"12\" size-sm=\"6\" size-lg=\"4\" [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 }\n }\n\n <!-- image credits -->\n @if (contentCredits | isNotNilOrBlank) {\n <div class=\"content-credits\" [class.floating]=\"!mobile\">\n <ion-text>{{ contentCredits }}</ion-text>\n </div>\n }\n</ion-content>\n", styles: [":host{--ion-card-background-color: rgba(var(--ion-background-color-rgb), .7);--scrollbar-width: 0;--bottom-banner-height: 122px}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.has-scrollbar{--scrollbar-width: 8px}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: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% - var(--bottom-banner-height))}}@media screen and (min-width: 1200px){ion-content ion-col.feed{height:fit-content;max-height:calc(100vh - var(--ion-toolbar-height) - var(--bottom-banner-height) - var(--ion-padding) * 2);padding-inline-end:calc(var(--ion-padding) - 10px);overflow:hidden}ion-content .bottom-banner.floating{position:fixed;left:0;right:0;bottom:0;overflow-y:auto;max-height:var(--bottom-banner-height);margin-top:0}}.content-credits{float:right}.content-credits.floating{float:unset;position:fixed;bottom:0;right:var(--scrollbar-width, 0)}.content-credits ion-text{padding-inline-start:4px;padding-inline-end:4px;font-size:.7rem;background-color:rgba(var(--ion-background-color-rgb),.5);color:rgba(var(--ion-text-color-rgb),.7)}@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.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: "directive", type: NgInitDirective, selector: "[ngInit]", outputs: ["ngInit"] }, { 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-feed", inputs: ["debug", "mobile", "showHeader", "showReadMoreButton", "headerColor", "cardColor", "shape", "class", "itemId", "filterItem", "feeds", "feedUrls", "maxAgeInMonths", "maxContentLength"] }, { 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: EmptyArrayPipe, name: "isEmptyArray" }, { kind: "pipe", type: IsNotNilOrBlankPipe, name: "isNotNilOrBlank" }], animations: [fadeInAnimation, slideUpDownAnimation, slideInAnimation], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
35506
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: HomePage, selector: "app-page-home", viewQueries: [{ propertyName: "content", first: true, predicate: IonContent, descendants: true, static: true }], 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\" [class.has-scrollbar]=\"hasScrollbar\" no-padding-xs>\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 hasFeed = hasFeed$ | async;\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col size=\"0\" [sizeXl]=\"hasFeed ? 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\">\n {{ item.matIcon }}\n </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]=\"hasFeed ? 1 : 0\"></ion-col>\n <ion-col [class.feed]=\"hasFeed\" [size]=\"hasFeed ? 12 : 0\" [sizeXl]=\"hasFeed ? 4 : 0\">\n @if (showFeed) {\n @if (mobile) {\n <app-feed\n #feed\n shape=\"round\"\n [urls]=\"feedUrls\"\n [maxAgeInMonths]=\"feedMaxAgeInMonths\"\n [maxContentLength]=\"feedMaxContentLength\"\n (ngInit)=\"initFeed(feed)\"\n @fadeInAnimation\n ></app-feed>\n } @else {\n <app-feed\n #feed\n shape=\"round\"\n [debug]=\"false\"\n [urls]=\"feedUrls\"\n [maxAgeInMonths]=\"feedMaxAgeInMonths\"\n [maxContentLength]=\"feedMaxContentLength\"\n (ngInit)=\"initFeed(feed)\"\n @slideInAnimation\n @fadeInAnimation\n ></app-feed>\n }\n }\n </ion-col>\n </ion-row>\n </ion-grid>\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 }\n\n <!-- Bottom banner -->\n @if (showPartnerBanner || showLegalInformation) {\n <ion-grid\n class=\"bottom-banner ion-text-center\"\n [class.floating]=\"!mobile && (pageHistory | isEmptyArray)\"\n @fadeInAnimation\n >\n <ion-row>\n <!-- partners logos -->\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\n <!-- legal information -->\n @if (showLegalInformation) {\n <ion-col size=\"12\" size-sm=\"6\" size-lg=\"4\" [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 }\n }\n\n <!-- image credits -->\n @if (contentCredits | isNotNilOrBlank) {\n <div class=\"content-credits\" [class.floating]=\"!mobile\">\n <ion-text>{{ contentCredits }}</ion-text>\n </div>\n }\n</ion-content>\n", styles: [":host{--ion-card-background-color: rgba(var(--ion-background-color-rgb), .7);--scrollbar-width: 0;--bottom-banner-height: 122px}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.has-scrollbar{--scrollbar-width: 8px}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: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% - var(--bottom-banner-height))}}@media screen and (min-width: 1200px){ion-content ion-col.feed{height:fit-content;max-height:calc(100vh - var(--ion-toolbar-height) - var(--bottom-banner-height) - var(--ion-padding) * 2);padding-inline-end:calc(var(--ion-padding) - 10px);overflow:hidden}ion-content .bottom-banner.floating{position:fixed;left:0;right:0;bottom:0;overflow-y:auto;max-height:var(--bottom-banner-height);margin-top:0}}.content-credits{float:right}.content-credits.floating{float:unset;position:fixed;bottom:0;right:var(--scrollbar-width, 0)}.content-credits ion-text{padding-inline-start:4px;padding-inline-end:4px;font-size:.7rem;background-color:rgba(var(--ion-background-color-rgb),.5);color:rgba(var(--ion-text-color-rgb),.7)}@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.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: "directive", type: NgInitDirective, selector: "[ngInit]", outputs: ["ngInit"] }, { 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-feed", inputs: ["debug", "mobile", "showHeader", "showReadMoreButton", "headerColor", "cardColor", "shape", "class", "itemId", "filterItem", "feeds", "urls", "maxAgeInMonths", "maxContentLength"], outputs: ["editItem", "deleteItem"] }, { 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: EmptyArrayPipe, name: "isEmptyArray" }, { kind: "pipe", type: IsNotNilOrBlankPipe, name: "isNotNilOrBlank" }], animations: [fadeInAnimation, slideUpDownAnimation, slideInAnimation], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
34647
35507
|
}
|
|
34648
35508
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HomePage, decorators: [{
|
|
34649
35509
|
type: Component,
|
|
34650
|
-
args: [{ selector: 'app-page-home', changeDetection: ChangeDetectionStrategy.OnPush, animations: [fadeInAnimation, slideUpDownAnimation, slideInAnimation], 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\" [class.has-scrollbar]=\"hasScrollbar\" no-padding-xs>\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 hasFeed = hasFeed$ | async;\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col size=\"0\" [sizeXl]=\"hasFeed ? 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\">\n {{ item.matIcon }}\n </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]=\"hasFeed ? 1 : 0\"></ion-col>\n <ion-col [class.feed]=\"hasFeed\" [size]=\"hasFeed ? 12 : 0\" [sizeXl]=\"hasFeed ? 4 : 0\">\n @if (showFeed) {\n @if (mobile) {\n <app-feed\n #feed\n shape=\"round\"\n [feedUrls]=\"feedUrls\"\n [maxAgeInMonths]=\"feedMaxAgeInMonths\"\n [maxContentLength]=\"feedMaxContentLength\"\n (ngInit)=\"initFeed(feed)\"\n @fadeInAnimation\n ></app-feed>\n } @else {\n <app-feed\n #feed\n shape=\"round\"\n [debug]=\"false\"\n [feedUrls]=\"feedUrls\"\n [maxAgeInMonths]=\"feedMaxAgeInMonths\"\n [maxContentLength]=\"feedMaxContentLength\"\n (ngInit)=\"initFeed(feed)\"\n @slideInAnimation\n @fadeInAnimation\n ></app-feed>\n }\n }\n </ion-col>\n </ion-row>\n </ion-grid>\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 }\n\n <!-- Bottom banner -->\n @if (showPartnerBanner || showLegalInformation) {\n <ion-grid\n class=\"bottom-banner ion-text-center\"\n [class.floating]=\"!mobile && (pageHistory | isEmptyArray)\"\n @fadeInAnimation\n >\n <ion-row>\n <!-- partners logos -->\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\n <!-- legal information -->\n @if (showLegalInformation) {\n <ion-col size=\"12\" size-sm=\"6\" size-lg=\"4\" [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 }\n }\n\n <!-- image credits -->\n @if (contentCredits | isNotNilOrBlank) {\n <div class=\"content-credits\" [class.floating]=\"!mobile\">\n <ion-text>{{ contentCredits }}</ion-text>\n </div>\n }\n</ion-content>\n", styles: [":host{--ion-card-background-color: rgba(var(--ion-background-color-rgb), .7);--scrollbar-width: 0;--bottom-banner-height: 122px}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.has-scrollbar{--scrollbar-width: 8px}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: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% - var(--bottom-banner-height))}}@media screen and (min-width: 1200px){ion-content ion-col.feed{height:fit-content;max-height:calc(100vh - var(--ion-toolbar-height) - var(--bottom-banner-height) - var(--ion-padding) * 2);padding-inline-end:calc(var(--ion-padding) - 10px);overflow:hidden}ion-content .bottom-banner.floating{position:fixed;left:0;right:0;bottom:0;overflow-y:auto;max-height:var(--bottom-banner-height);margin-top:0}}.content-credits{float:right}.content-credits.floating{float:unset;position:fixed;bottom:0;right:var(--scrollbar-width, 0)}.content-credits ion-text{padding-inline-start:4px;padding-inline-end:4px;font-size:.7rem;background-color:rgba(var(--ion-background-color-rgb),.5);color:rgba(var(--ion-text-color-rgb),.7)}@media screen and (min-width: 992px){.left-border{border-left:1px solid var(--ion-color-medium)}}\n"] }]
|
|
35510
|
+
args: [{ selector: 'app-page-home', changeDetection: ChangeDetectionStrategy.OnPush, animations: [fadeInAnimation, slideUpDownAnimation, slideInAnimation], 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\" [class.has-scrollbar]=\"hasScrollbar\" no-padding-xs>\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 hasFeed = hasFeed$ | async;\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col size=\"0\" [sizeXl]=\"hasFeed ? 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\">\n {{ item.matIcon }}\n </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]=\"hasFeed ? 1 : 0\"></ion-col>\n <ion-col [class.feed]=\"hasFeed\" [size]=\"hasFeed ? 12 : 0\" [sizeXl]=\"hasFeed ? 4 : 0\">\n @if (showFeed) {\n @if (mobile) {\n <app-feed\n #feed\n shape=\"round\"\n [urls]=\"feedUrls\"\n [maxAgeInMonths]=\"feedMaxAgeInMonths\"\n [maxContentLength]=\"feedMaxContentLength\"\n (ngInit)=\"initFeed(feed)\"\n @fadeInAnimation\n ></app-feed>\n } @else {\n <app-feed\n #feed\n shape=\"round\"\n [debug]=\"false\"\n [urls]=\"feedUrls\"\n [maxAgeInMonths]=\"feedMaxAgeInMonths\"\n [maxContentLength]=\"feedMaxContentLength\"\n (ngInit)=\"initFeed(feed)\"\n @slideInAnimation\n @fadeInAnimation\n ></app-feed>\n }\n }\n </ion-col>\n </ion-row>\n </ion-grid>\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 }\n\n <!-- Bottom banner -->\n @if (showPartnerBanner || showLegalInformation) {\n <ion-grid\n class=\"bottom-banner ion-text-center\"\n [class.floating]=\"!mobile && (pageHistory | isEmptyArray)\"\n @fadeInAnimation\n >\n <ion-row>\n <!-- partners logos -->\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\n <!-- legal information -->\n @if (showLegalInformation) {\n <ion-col size=\"12\" size-sm=\"6\" size-lg=\"4\" [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 }\n }\n\n <!-- image credits -->\n @if (contentCredits | isNotNilOrBlank) {\n <div class=\"content-credits\" [class.floating]=\"!mobile\">\n <ion-text>{{ contentCredits }}</ion-text>\n </div>\n }\n</ion-content>\n", styles: [":host{--ion-card-background-color: rgba(var(--ion-background-color-rgb), .7);--scrollbar-width: 0;--bottom-banner-height: 122px}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.has-scrollbar{--scrollbar-width: 8px}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: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% - var(--bottom-banner-height))}}@media screen and (min-width: 1200px){ion-content ion-col.feed{height:fit-content;max-height:calc(100vh - var(--ion-toolbar-height) - var(--bottom-banner-height) - var(--ion-padding) * 2);padding-inline-end:calc(var(--ion-padding) - 10px);overflow:hidden}ion-content .bottom-banner.floating{position:fixed;left:0;right:0;bottom:0;overflow-y:auto;max-height:var(--bottom-banner-height);margin-top:0}}.content-credits{float:right}.content-credits.floating{float:unset;position:fixed;bottom:0;right:var(--scrollbar-width, 0)}.content-credits ion-text{padding-inline-start:4px;padding-inline-end:4px;font-size:.7rem;background-color:rgba(var(--ion-background-color-rgb),.5);color:rgba(var(--ion-text-color-rgb),.7)}@media screen and (min-width: 992px){.left-border{border-left:1px solid var(--ion-color-medium)}}\n"] }]
|
|
34651
35511
|
}], 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: [{
|
|
34652
35512
|
type: Inject,
|
|
34653
35513
|
args: [ENVIRONMENT]
|
|
@@ -34864,11 +35724,11 @@ class FeedPage {
|
|
|
34864
35724
|
this.destroySubject.complete();
|
|
34865
35725
|
}
|
|
34866
35726
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedPage, deps: [{ token: LocalSettingsService }, { token: i0.ChangeDetectorRef }, { token: APP_FEED_SERVICE, optional: true }, { token: ENVIRONMENT, optional: true }], target: i0.ɵɵFactoryTarget.Component });
|
|
34867
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: FeedPage, selector: "app-feed-page", inputs: { debug: "debug", title: "title", feedUrls: "feedUrls", feedItemId: "feedItemId" }, ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>{{ title | translate }}</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n <div class=\"ion-padding\">\n @if (loadingSubject | async) {\n <ion-spinner></ion-spinner>\n } @else {\n <app-feed [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [
|
|
35727
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: FeedPage, selector: "app-feed-page", inputs: { debug: "debug", title: "title", feedUrls: "feedUrls", feedItemId: "feedItemId" }, ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>{{ title | translate }}</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n <div class=\"ion-padding\">\n @if (loadingSubject | async) {\n <ion-spinner></ion-spinner>\n } @else {\n <app-feed [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [urls]=\"feedUrls\"\n cardColor=\"light\"\n [debug]=\"debug\"\n ></app-feed>\n }\n </div>\n</ion-content>\n", dependencies: [{ kind: "component", type: i2$1.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$1.IonContent, selector: "ion-content", inputs: ["color", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$1.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$1.IonSpinner, selector: "ion-spinner", inputs: ["color", "duration", "name", "paused"] }, { kind: "component", type: i2$1.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$1.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonBackButton, selector: "ion-back-button" }, { kind: "component", type: FeedsComponent, selector: "app-feed", inputs: ["debug", "mobile", "showHeader", "showReadMoreButton", "headerColor", "cardColor", "shape", "class", "itemId", "filterItem", "feeds", "urls", "maxAgeInMonths", "maxContentLength"], outputs: ["editItem", "deleteItem"] }, { kind: "pipe", type: i3$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
34868
35728
|
}
|
|
34869
35729
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedPage, decorators: [{
|
|
34870
35730
|
type: Component,
|
|
34871
|
-
args: [{ selector: 'app-feed-page', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>{{ title | translate }}</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n <div class=\"ion-padding\">\n @if (loadingSubject | async) {\n <ion-spinner></ion-spinner>\n } @else {\n <app-feed [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [
|
|
35731
|
+
args: [{ selector: 'app-feed-page', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>{{ title | translate }}</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n <div class=\"ion-padding\">\n @if (loadingSubject | async) {\n <ion-spinner></ion-spinner>\n } @else {\n <app-feed [showHeader]=\"false\"\n [showReadMoreButton]=\"false\"\n [urls]=\"feedUrls\"\n cardColor=\"light\"\n [debug]=\"debug\"\n ></app-feed>\n }\n </div>\n</ion-content>\n" }]
|
|
34872
35732
|
}], ctorParameters: () => [{ type: LocalSettingsService }, { type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
|
|
34873
35733
|
type: Optional
|
|
34874
35734
|
}, {
|
|
@@ -44334,528 +45194,174 @@ class AppEntityEditor extends AppTabEditor {
|
|
|
44334
45194
|
// can be overwritten by subclasses
|
|
44335
45195
|
}
|
|
44336
45196
|
computeUsageMode(data) {
|
|
44337
|
-
// can be overwritten by subclasses
|
|
44338
|
-
// Using local settings default value
|
|
44339
|
-
return this.settings.usageMode;
|
|
44340
|
-
}
|
|
44341
|
-
waitWhilePending(opts) {
|
|
44342
|
-
return AppFormUtils.waitWhilePending(this, opts);
|
|
44343
|
-
}
|
|
44344
|
-
async getValue() {
|
|
44345
|
-
const json = await this.getJsonValueToSave();
|
|
44346
|
-
const res = new this.dataType();
|
|
44347
|
-
res.fromObject(json);
|
|
44348
|
-
return res;
|
|
44349
|
-
}
|
|
44350
|
-
getJsonValueToSave() {
|
|
44351
|
-
return Promise.resolve(this.form.value);
|
|
44352
|
-
}
|
|
44353
|
-
/**
|
|
44354
|
-
* Compute the title
|
|
44355
|
-
*
|
|
44356
|
-
* @param data
|
|
44357
|
-
*/
|
|
44358
|
-
async updateTitle(data) {
|
|
44359
|
-
data = data || this.data;
|
|
44360
|
-
const title = await this.computeTitle(data);
|
|
44361
|
-
this.titleSubject.next(title);
|
|
44362
|
-
// If NOT data, then add to page history
|
|
44363
|
-
if (!this.isNewData) {
|
|
44364
|
-
const page = await this.computePageHistory(title);
|
|
44365
|
-
if (page)
|
|
44366
|
-
return this.addToPageHistory(page);
|
|
44367
|
-
}
|
|
44368
|
-
}
|
|
44369
|
-
async addToPageHistory(page, opts) {
|
|
44370
|
-
if (!page)
|
|
44371
|
-
return; // Skip
|
|
44372
|
-
return this.settings.addToPageHistory(page, {
|
|
44373
|
-
removePathQueryParams: true,
|
|
44374
|
-
removeTitleSmallTag: true,
|
|
44375
|
-
emitEvent: false,
|
|
44376
|
-
...opts,
|
|
44377
|
-
});
|
|
44378
|
-
}
|
|
44379
|
-
async computePageHistory(title) {
|
|
44380
|
-
if (this.debug)
|
|
44381
|
-
console.debug('[entity-editor] Computing page history, using url: ' + this.router.url);
|
|
44382
|
-
return {
|
|
44383
|
-
title,
|
|
44384
|
-
path: this.router.url,
|
|
44385
|
-
};
|
|
44386
|
-
}
|
|
44387
|
-
async removePageHistory(opts) {
|
|
44388
|
-
return this.settings.removePageHistory(this.router.url, opts);
|
|
44389
|
-
}
|
|
44390
|
-
computePageUrl(id) {
|
|
44391
|
-
const parentUrl = this.getParentPageUrl();
|
|
44392
|
-
return parentUrl && `${parentUrl}/${id}`;
|
|
44393
|
-
}
|
|
44394
|
-
getParentPageUrl(withQueryParams) {
|
|
44395
|
-
let parentUrl = this.defaultBackHref;
|
|
44396
|
-
// Remove query params
|
|
44397
|
-
if (withQueryParams !== true && parentUrl && parentUrl.indexOf('?') !== -1) {
|
|
44398
|
-
parentUrl = parentUrl.substring(0, parentUrl.indexOf('?'));
|
|
44399
|
-
}
|
|
44400
|
-
return parentUrl;
|
|
44401
|
-
}
|
|
44402
|
-
listenChanges(id, opts) {
|
|
44403
|
-
return this.dataService.listenChanges(this.data.id, {
|
|
44404
|
-
// Make sure to skip cache, because NOT the full graph will be fetched by subscription
|
|
44405
|
-
// When a changes occur, we call reload() to force refetching the full graph
|
|
44406
|
-
fetchPolicy: 'no-cache',
|
|
44407
|
-
...opts,
|
|
44408
|
-
});
|
|
44409
|
-
}
|
|
44410
|
-
markForCheck() {
|
|
44411
|
-
this.cd.markForCheck();
|
|
44412
|
-
}
|
|
44413
|
-
/**
|
|
44414
|
-
* Update the route, without reloading the component
|
|
44415
|
-
*
|
|
44416
|
-
* @param data
|
|
44417
|
-
* @param queryParams
|
|
44418
|
-
* @protected
|
|
44419
|
-
*/
|
|
44420
|
-
async updateRoute(data, queryParams) {
|
|
44421
|
-
data = data || this.data;
|
|
44422
|
-
if (!data || !this.route)
|
|
44423
|
-
return false;
|
|
44424
|
-
const currId = this.route.snapshot.paramMap.get(this.pathIdAttribute);
|
|
44425
|
-
const futureId = isNotNil(data.id) ? data.id.toString() : 'new';
|
|
44426
|
-
if (queryParams) {
|
|
44427
|
-
this.queryParams = {
|
|
44428
|
-
...this.queryParams,
|
|
44429
|
-
...queryParams,
|
|
44430
|
-
};
|
|
44431
|
-
}
|
|
44432
|
-
if (currId === futureId) {
|
|
44433
|
-
// Update queryParam only (not the path)
|
|
44434
|
-
// /!\ We should get query Params from the updated route, and not from snapshot that can be outdated
|
|
44435
|
-
const actualQueryParams = await firstNotNilPromise(this.route.queryParams);
|
|
44436
|
-
if (!equals(actualQueryParams, this.queryParams)) {
|
|
44437
|
-
if (this.debug)
|
|
44438
|
-
console.debug(`${this._logPrefix}Updating route using queryParams: `, this.queryParams);
|
|
44439
|
-
return await this.router.navigate(['.'], {
|
|
44440
|
-
relativeTo: this.route,
|
|
44441
|
-
replaceUrl: false,
|
|
44442
|
-
queryParams: this.queryParams,
|
|
44443
|
-
state: { animated: false },
|
|
44444
|
-
});
|
|
44445
|
-
}
|
|
44446
|
-
}
|
|
44447
|
-
else {
|
|
44448
|
-
const path = this.computePageUrl(isNotNil(data.id) ? data.id : 'new');
|
|
44449
|
-
const commands = path && typeof path === 'string' ? path.split('/').slice(1) : path;
|
|
44450
|
-
if (isNotEmptyArray(commands)) {
|
|
44451
|
-
// Current route was '/new' => change to '/new?id=:id' (to allow CustomReuseStrategy to detect that route can be reused)
|
|
44452
|
-
if (currId === 'new' && this.queryParams[this.pathIdAttribute] !== futureId) {
|
|
44453
|
-
if (this.debug)
|
|
44454
|
-
console.debug(`${this._logPrefix}Updating route /new into /:id, using queryParams: `, queryParams);
|
|
44455
|
-
this.queryParams[this.pathIdAttribute] = futureId;
|
|
44456
|
-
const res = await this.router.navigate(['.'], {
|
|
44457
|
-
relativeTo: this.route,
|
|
44458
|
-
replaceUrl: true,
|
|
44459
|
-
queryParams: this.queryParams,
|
|
44460
|
-
state: { animated: false },
|
|
44461
|
-
});
|
|
44462
|
-
if (!res)
|
|
44463
|
-
return false;
|
|
44464
|
-
}
|
|
44465
|
-
if (this.debug)
|
|
44466
|
-
console.debug(`${this._logPrefix}Updating route to: ` + path);
|
|
44467
|
-
return await this.router.navigate(commands, {
|
|
44468
|
-
replaceUrl: true,
|
|
44469
|
-
queryParams: this.queryParams,
|
|
44470
|
-
state: { animated: false },
|
|
44471
|
-
});
|
|
44472
|
-
}
|
|
44473
|
-
return Promise.reject('Missing page URL');
|
|
44474
|
-
}
|
|
44475
|
-
}
|
|
44476
|
-
/* -- private functions -- */
|
|
44477
|
-
/**
|
|
44478
|
-
* Open the first tab that is invalid
|
|
44479
|
-
*/
|
|
44480
|
-
openFirstInvalidTab() {
|
|
44481
|
-
const invalidTabIndex = this.getFirstInvalidTabIndex();
|
|
44482
|
-
if (invalidTabIndex !== -1 && this.selectedTabIndex !== invalidTabIndex) {
|
|
44483
|
-
this.selectedTabIndex = invalidTabIndex;
|
|
44484
|
-
this.markForCheck();
|
|
44485
|
-
}
|
|
44486
|
-
}
|
|
44487
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AppEntityEditor, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive });
|
|
44488
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: AppEntityEditor, usesInheritance: true, ngImport: i0 });
|
|
44489
|
-
}
|
|
44490
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AppEntityEditor, decorators: [{
|
|
44491
|
-
type: Directive
|
|
44492
|
-
}], ctorParameters: () => [{ type: i0.Injector }, { type: undefined }, { type: undefined }, { type: AppEditorOptions, decorators: [{
|
|
44493
|
-
type: Optional
|
|
44494
|
-
}] }] });
|
|
44495
|
-
|
|
44496
|
-
var PersonFilter_1;
|
|
44497
|
-
// @dynamic
|
|
44498
|
-
let PersonFilter = class PersonFilter extends EntityFilter {
|
|
44499
|
-
static { PersonFilter_1 = this; }
|
|
44500
|
-
static fromObject;
|
|
44501
|
-
static searchFilter(source) {
|
|
44502
|
-
return source && PersonFilter_1.fromObject(source).asFilterFn();
|
|
44503
|
-
}
|
|
44504
|
-
email;
|
|
44505
|
-
pubkey;
|
|
44506
|
-
searchText;
|
|
44507
|
-
statusIds;
|
|
44508
|
-
userProfiles;
|
|
44509
|
-
includedIds;
|
|
44510
|
-
excludedIds;
|
|
44511
|
-
searchAttribute;
|
|
44512
|
-
searchAttributes;
|
|
44513
|
-
constructor() {
|
|
44514
|
-
super(PersonFilter_1.TYPENAME);
|
|
44515
|
-
}
|
|
44516
|
-
fromObject(source, opts) {
|
|
44517
|
-
super.fromObject(source, opts);
|
|
44518
|
-
this.email = source.email;
|
|
44519
|
-
this.pubkey = source.pubkey;
|
|
44520
|
-
this.searchText = source.searchText;
|
|
44521
|
-
this.statusIds = source.statusIds || (isNotNil(source.statusId) ? [source.statusId] : undefined);
|
|
44522
|
-
this.userProfiles = source.userProfiles;
|
|
44523
|
-
this.excludedIds = source.excludedIds;
|
|
44524
|
-
this.searchAttribute = source.searchAttribute;
|
|
44525
|
-
this.searchAttributes = source.searchAttributes;
|
|
44526
|
-
}
|
|
44527
|
-
asObject(opts) {
|
|
44528
|
-
const target = super.asObject(opts);
|
|
44529
|
-
target.email = this.email;
|
|
44530
|
-
target.pubkey = this.pubkey;
|
|
44531
|
-
target.searchText = this.searchText;
|
|
44532
|
-
target.statusIds = this.statusIds;
|
|
44533
|
-
target.userProfiles = this.userProfiles;
|
|
44534
|
-
target.excludedIds = this.excludedIds;
|
|
44535
|
-
target.searchAttribute = this.searchAttribute;
|
|
44536
|
-
target.searchAttributes = this.searchAttributes;
|
|
44537
|
-
return target;
|
|
44538
|
-
}
|
|
44539
|
-
buildFilter() {
|
|
44540
|
-
const filterFns = super.buildFilter();
|
|
44541
|
-
// Filter by status
|
|
44542
|
-
if (isNotEmptyArray(this.statusIds)) {
|
|
44543
|
-
filterFns.push((e) => this.statusIds.includes(e.statusId));
|
|
44544
|
-
}
|
|
44545
|
-
// Filter included ids
|
|
44546
|
-
const includedIds = this.includedIds;
|
|
44547
|
-
if (isNotEmptyArray(includedIds)) {
|
|
44548
|
-
filterFns.push((e) => isNotNil(e.id) && includedIds.includes(e.id));
|
|
44549
|
-
}
|
|
44550
|
-
// Filter excluded ids
|
|
44551
|
-
const excludedIds = this.excludedIds;
|
|
44552
|
-
if (isNotEmptyArray(excludedIds)) {
|
|
44553
|
-
filterFns.push((e) => isNil(e.id) || !excludedIds.includes(e.id));
|
|
44554
|
-
}
|
|
44555
|
-
// Search text
|
|
44556
|
-
const searchTextFilter = EntityUtils.searchTextFilter(this.searchAttribute || this.searchAttributes || ['lastName', 'firstName', 'department.name'], this.searchText);
|
|
44557
|
-
if (searchTextFilter)
|
|
44558
|
-
filterFns.push(searchTextFilter);
|
|
44559
|
-
return filterFns;
|
|
44560
|
-
}
|
|
44561
|
-
};
|
|
44562
|
-
PersonFilter = PersonFilter_1 = __decorate([
|
|
44563
|
-
EntityClass({ typename: 'PersonFilterVO' })
|
|
44564
|
-
], PersonFilter);
|
|
44565
|
-
|
|
44566
|
-
const MessageTypes = {
|
|
44567
|
-
INBOX_MESSAGE: 'INBOX_MESSAGE',
|
|
44568
|
-
EMAIL: 'EMAIL',
|
|
44569
|
-
FEED: 'FEED',
|
|
44570
|
-
};
|
|
44571
|
-
const MessageTypeList = Object.freeze([
|
|
44572
|
-
{
|
|
44573
|
-
id: MessageTypes.INBOX_MESSAGE,
|
|
44574
|
-
icon: 'notifications',
|
|
44575
|
-
label: 'SOCIAL.MESSAGE.TYPE_ENUM.INBOX_MESSAGE',
|
|
44576
|
-
},
|
|
44577
|
-
{
|
|
44578
|
-
id: MessageTypes.EMAIL,
|
|
44579
|
-
icon: 'mail',
|
|
44580
|
-
label: 'SOCIAL.MESSAGE.TYPE_ENUM.EMAIL',
|
|
44581
|
-
},
|
|
44582
|
-
{
|
|
44583
|
-
id: MessageTypes.FEED,
|
|
44584
|
-
icon: 'megaphone',
|
|
44585
|
-
label: 'SOCIAL.MESSAGE.TYPE_ENUM.FEED',
|
|
44586
|
-
},
|
|
44587
|
-
]);
|
|
44588
|
-
// @dynamic
|
|
44589
|
-
let Message = class Message extends Entity {
|
|
44590
|
-
static fromObject;
|
|
44591
|
-
type;
|
|
44592
|
-
issuer;
|
|
44593
|
-
recipients;
|
|
44594
|
-
recipientFilter;
|
|
44595
|
-
subject;
|
|
44596
|
-
body;
|
|
44597
|
-
constructor() {
|
|
44598
|
-
super();
|
|
44599
|
-
}
|
|
44600
|
-
fromObject(source, opts) {
|
|
44601
|
-
super.fromObject(source, opts);
|
|
44602
|
-
this.type = source.type;
|
|
44603
|
-
this.issuer = source.issuer && Person.fromObject(source.issuer);
|
|
44604
|
-
this.recipients = source.recipients && source.recipients.map(Person.fromObject);
|
|
44605
|
-
this.recipientFilter = (source.recipientFilter && PersonFilter.fromObject(source.recipientFilter)) || undefined;
|
|
44606
|
-
this.subject = source.subject;
|
|
44607
|
-
this.body = source.body;
|
|
44608
|
-
}
|
|
44609
|
-
asObject(opts) {
|
|
44610
|
-
const target = super.asObject(opts);
|
|
44611
|
-
target.recipients = this.recipients && this.recipients.map((p) => p.asObject(opts));
|
|
44612
|
-
target.recipientFilter = (this.recipientFilter && this.recipientFilter.asObject(opts)) || undefined;
|
|
44613
|
-
return target;
|
|
44614
|
-
}
|
|
44615
|
-
};
|
|
44616
|
-
Message = __decorate([
|
|
44617
|
-
EntityClass({ typename: 'MessageVO' })
|
|
44618
|
-
], Message);
|
|
44619
|
-
// @dynamic
|
|
44620
|
-
let MessageFilter = class MessageFilter extends EntityFilter {
|
|
44621
|
-
static fromObject;
|
|
44622
|
-
fromObject(source, opts) {
|
|
44623
|
-
super.fromObject(source, opts);
|
|
44624
|
-
}
|
|
44625
|
-
buildFilter() {
|
|
44626
|
-
return super.buildFilter();
|
|
44627
|
-
}
|
|
44628
|
-
};
|
|
44629
|
-
MessageFilter = __decorate([
|
|
44630
|
-
EntityClass({ typename: 'MessageFilterVO' })
|
|
44631
|
-
], MessageFilter);
|
|
44632
|
-
|
|
44633
|
-
class MessageForm extends AppForm {
|
|
44634
|
-
formBuilder;
|
|
44635
|
-
cd;
|
|
44636
|
-
mobile;
|
|
44637
|
-
suggestFn;
|
|
44638
|
-
subjectMinLength = 5;
|
|
44639
|
-
subjectMaxLength = 255;
|
|
44640
|
-
bodyMaxLength = 2000;
|
|
44641
|
-
bodyAutoHeight = true;
|
|
44642
|
-
canSelectType = false;
|
|
44643
|
-
canRecipientFilter = false;
|
|
44644
|
-
recipientFilterCount = 0;
|
|
44645
|
-
types = MessageTypeList;
|
|
44646
|
-
constructor(injector, formBuilder, cd) {
|
|
44647
|
-
super(injector);
|
|
44648
|
-
this.formBuilder = formBuilder;
|
|
44649
|
-
this.cd = cd;
|
|
44650
|
-
this.mobile = this.settings.mobile;
|
|
44651
|
-
}
|
|
44652
|
-
ngOnInit() {
|
|
44653
|
-
this.setForm(this.formBuilder.group({
|
|
44654
|
-
type: [MessageTypes.INBOX_MESSAGE, Validators.required],
|
|
44655
|
-
recipients: [null, Validators.required],
|
|
44656
|
-
recipientFilter: [null],
|
|
44657
|
-
subject: [
|
|
44658
|
-
null,
|
|
44659
|
-
this.subjectMaxLength
|
|
44660
|
-
? Validators.compose([Validators.required, Validators.minLength(this.subjectMinLength), Validators.maxLength(this.subjectMaxLength)])
|
|
44661
|
-
: Validators.required,
|
|
44662
|
-
],
|
|
44663
|
-
body: [null, this.bodyMaxLength ? Validators.compose([Validators.maxLength(this.bodyMaxLength)]) : Validators.required],
|
|
44664
|
-
}));
|
|
44665
|
-
this.registerSubscription(this._form
|
|
44666
|
-
.get('type')
|
|
44667
|
-
.valueChanges.pipe(filter(isNotNil))
|
|
44668
|
-
.subscribe((type) => this.updateFormGroup(this._form, { type })));
|
|
44669
|
-
// Person combo
|
|
44670
|
-
const personAttributes = this.settings.getFieldDisplayAttributes('person', ['lastName', 'firstName', 'department.name']);
|
|
44671
|
-
this.registerAutocompleteField('recipients', {
|
|
44672
|
-
showAllOnFocus: false,
|
|
44673
|
-
suggestFn: this.suggestFn,
|
|
44674
|
-
filter: {
|
|
44675
|
-
statusIds: [StatusIds.TEMPORARY, StatusIds.ENABLE],
|
|
44676
|
-
},
|
|
44677
|
-
attributes: personAttributes,
|
|
44678
|
-
columnNames: personAttributes.map((attr) => `USER.${changeCaseToUnderscore(attr).toUpperCase()}`),
|
|
44679
|
-
displayWith: PersonUtils.personToString,
|
|
44680
|
-
multiple: true,
|
|
44681
|
-
mobile: this.mobile,
|
|
44682
|
-
});
|
|
44683
|
-
}
|
|
44684
|
-
isSamePerson(o1, o2) {
|
|
44685
|
-
return EntityUtils.equals(o1, o2, 'id');
|
|
44686
|
-
}
|
|
44687
|
-
updateFormGroup(formGroup, opts) {
|
|
44688
|
-
console.debug('[message-form] Updating form group...', opts);
|
|
44689
|
-
// Recipient validator
|
|
44690
|
-
const recipientsRequired = toBoolean(opts?.recipientRequired, opts?.type !== MessageTypes.FEED);
|
|
44691
|
-
{
|
|
44692
|
-
const control = formGroup.get('recipients');
|
|
44693
|
-
if (recipientsRequired) {
|
|
44694
|
-
if (!control.hasValidator(Validators.required)) {
|
|
44695
|
-
control.addValidators(Validators.required);
|
|
44696
|
-
}
|
|
44697
|
-
control.enable();
|
|
44698
|
-
}
|
|
44699
|
-
else {
|
|
44700
|
-
if (control.hasValidator(Validators.required)) {
|
|
44701
|
-
control.removeValidators(Validators.required);
|
|
44702
|
-
}
|
|
44703
|
-
control.disable();
|
|
44704
|
-
}
|
|
44705
|
-
}
|
|
44706
|
-
// Recipient filter validator
|
|
44707
|
-
const recipientFilterRequired = this.canRecipientFilter && !recipientsRequired;
|
|
44708
|
-
{
|
|
44709
|
-
const control = formGroup.get('recipientFilter');
|
|
44710
|
-
if (recipientFilterRequired) {
|
|
44711
|
-
if (!control.hasValidator(Validators.required)) {
|
|
44712
|
-
control.addValidators(Validators.required);
|
|
44713
|
-
}
|
|
44714
|
-
control.enable();
|
|
44715
|
-
}
|
|
44716
|
-
else {
|
|
44717
|
-
if (control.hasValidator(Validators.required)) {
|
|
44718
|
-
control.removeValidators(Validators.required);
|
|
44719
|
-
}
|
|
44720
|
-
control.disable();
|
|
44721
|
-
}
|
|
44722
|
-
}
|
|
44723
|
-
formGroup.updateValueAndValidity();
|
|
45197
|
+
// can be overwritten by subclasses
|
|
45198
|
+
// Using local settings default value
|
|
45199
|
+
return this.settings.usageMode;
|
|
44724
45200
|
}
|
|
44725
|
-
|
|
44726
|
-
|
|
45201
|
+
waitWhilePending(opts) {
|
|
45202
|
+
return AppFormUtils.waitWhilePending(this, opts);
|
|
44727
45203
|
}
|
|
44728
|
-
|
|
44729
|
-
|
|
44730
|
-
|
|
44731
|
-
|
|
44732
|
-
|
|
44733
|
-
args: [{ selector: 'app-message-form', changeDetection: ChangeDetectionStrategy.OnPush, template: "<form [formGroup]=\"form\" class=\"form-container\">\n <!-- type -->\n <mat-form-field *ngIf=\"canSelectType\">\n <mat-label>{{ 'SOCIAL.MESSAGE.TYPE' | translate }}</mat-label>\n <mat-select formControlName=\"type\">\n @for (item of types; track item.id) {\n @if (canRecipientFilter || item.id !== 'FEED') {\n <mat-option [value]=\"item.id\">\n <ion-icon [name]=\"item.icon\"></ion-icon>\n {{ item.label | translate }}\n </mat-option>\n }\n }\n </mat-select>\n </mat-form-field>\n\n <!-- Recipients -->\n @if ((form | formGetValue: 'type') !== 'FEED') {\n <mat-chips-field\n formControlName=\"recipients\"\n chipColor=\"accent\"\n [placeholder]=\"'SOCIAL.MESSAGE.RECIPIENTS' | translate\"\n [config]=\"autocompleteFields.recipients\"\n [equals]=\"isSamePerson\"\n ></mat-chips-field>\n } @else {\n <mat-form-field>\n <input matInput hidden formControlName=\"recipients\" type=\"text\" />\n <mat-chip-grid>\n <mat-chip-row>\n {{ 'SOCIAL.MESSAGE.RECIPIENT_FILTER_COUNT' | translate: { count: recipientFilterCount } }}\n </mat-chip-row>\n </mat-chip-grid>\n </mat-form-field>\n }\n\n <!-- Subject -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.SUBJECT' | translate }}</mat-label>\n <input matInput type=\"text\" formControlName=\"subject\" autocomplete=\"off\" required />\n <mat-error *ngIf=\"form.controls.subject.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"form.controls.subject.hasError('minlength')\">\n {{ 'ERROR.FIELD_MIN_LENGTH_COMPACT' | translate }}\n </mat-error>\n <mat-error *ngIf=\"form.controls.subject.hasError('maxlength')\">\n {{ 'ERROR.FIELD_MAX_LENGTH_COMPACT' | translate }}\n </mat-error>\n <mat-hint *ngIf=\"subjectMaxLength\" align=\"end\">\n {{\n 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.subject.value?.length || 0, max: subjectMaxLength }\n }}\n </mat-hint>\n </mat-form-field>\n\n <!-- Body -->\n <mat-form-field>\n <mat-label>{{ 'SOCIAL.MESSAGE.BODY_HELP' | translate }}</mat-label>\n <textarea\n matInput\n type=\"text\"\n formControlName=\"body\"\n [class.fixed-height]=\"!bodyAutoHeight\"\n [cdkTextareaAutosize]=\"bodyAutoHeight\"\n (keydown.control.enter)=\"doSubmit($event)\"\n ></textarea>\n <mat-error *ngIf=\"form.controls.body.hasError('maxlength')\">\n {{ 'ERROR.FIELD_MAX_LENGTH' | translate: form.controls.body.errors.maxlength }}\n </mat-error>\n <mat-hint *ngIf=\"bodyMaxLength\" align=\"end\">\n {{ 'INFO.TEXT_PROGRESS' | translate: { current: form.controls.body.value?.length || 0, max: bodyMaxLength } }}\n </mat-hint>\n </mat-form-field>\n</form>\n", styles: ["textarea.fixed-height{height:11.5em}textarea{min-height:11.5em}\n"] }]
|
|
44734
|
-
}], ctorParameters: () => [{ type: i0.Injector }, { type: i1$3.UntypedFormBuilder }, { type: i0.ChangeDetectorRef }], propDecorators: { suggestFn: [{
|
|
44735
|
-
type: Input
|
|
44736
|
-
}], subjectMinLength: [{
|
|
44737
|
-
type: Input
|
|
44738
|
-
}], subjectMaxLength: [{
|
|
44739
|
-
type: Input
|
|
44740
|
-
}], bodyMaxLength: [{
|
|
44741
|
-
type: Input
|
|
44742
|
-
}], bodyAutoHeight: [{
|
|
44743
|
-
type: Input
|
|
44744
|
-
}], canSelectType: [{
|
|
44745
|
-
type: Input
|
|
44746
|
-
}], canRecipientFilter: [{
|
|
44747
|
-
type: Input
|
|
44748
|
-
}], recipientFilterCount: [{
|
|
44749
|
-
type: Input
|
|
44750
|
-
}] } });
|
|
44751
|
-
|
|
44752
|
-
class MessageModal {
|
|
44753
|
-
settings;
|
|
44754
|
-
viewCtrl;
|
|
44755
|
-
accountService;
|
|
44756
|
-
cd;
|
|
44757
|
-
mobile;
|
|
44758
|
-
suggestFn;
|
|
44759
|
-
data;
|
|
44760
|
-
canSelectType;
|
|
44761
|
-
canRecipientFilter;
|
|
44762
|
-
recipientFilterCount;
|
|
44763
|
-
form;
|
|
44764
|
-
constructor(settings, viewCtrl, accountService, cd) {
|
|
44765
|
-
this.settings = settings;
|
|
44766
|
-
this.viewCtrl = viewCtrl;
|
|
44767
|
-
this.accountService = accountService;
|
|
44768
|
-
this.cd = cd;
|
|
44769
|
-
this.mobile = this.settings.mobile;
|
|
45204
|
+
async getValue() {
|
|
45205
|
+
const json = await this.getJsonValueToSave();
|
|
45206
|
+
const res = new this.dataType();
|
|
45207
|
+
res.fromObject(json);
|
|
45208
|
+
return res;
|
|
44770
45209
|
}
|
|
44771
|
-
|
|
44772
|
-
|
|
45210
|
+
getJsonValueToSave() {
|
|
45211
|
+
return Promise.resolve(this.form.value);
|
|
44773
45212
|
}
|
|
44774
|
-
|
|
44775
|
-
|
|
44776
|
-
|
|
44777
|
-
|
|
44778
|
-
|
|
44779
|
-
|
|
44780
|
-
|
|
44781
|
-
|
|
44782
|
-
|
|
45213
|
+
/**
|
|
45214
|
+
* Compute the title
|
|
45215
|
+
*
|
|
45216
|
+
* @param data
|
|
45217
|
+
*/
|
|
45218
|
+
async updateTitle(data) {
|
|
45219
|
+
data = data || this.data;
|
|
45220
|
+
const title = await this.computeTitle(data);
|
|
45221
|
+
this.titleSubject.next(title);
|
|
45222
|
+
// If NOT data, then add to page history
|
|
45223
|
+
if (!this.isNewData) {
|
|
45224
|
+
const page = await this.computePageHistory(title);
|
|
45225
|
+
if (page)
|
|
45226
|
+
return this.addToPageHistory(page);
|
|
45227
|
+
}
|
|
45228
|
+
}
|
|
45229
|
+
async addToPageHistory(page, opts) {
|
|
45230
|
+
if (!page)
|
|
45231
|
+
return; // Skip
|
|
45232
|
+
return this.settings.addToPageHistory(page, {
|
|
45233
|
+
removePathQueryParams: true,
|
|
45234
|
+
removeTitleSmallTag: true,
|
|
45235
|
+
emitEvent: false,
|
|
45236
|
+
...opts,
|
|
44783
45237
|
});
|
|
44784
45238
|
}
|
|
44785
|
-
|
|
44786
|
-
this.
|
|
45239
|
+
async computePageHistory(title) {
|
|
45240
|
+
if (this.debug)
|
|
45241
|
+
console.debug('[entity-editor] Computing page history, using url: ' + this.router.url);
|
|
45242
|
+
return {
|
|
45243
|
+
title,
|
|
45244
|
+
path: this.router.url,
|
|
45245
|
+
};
|
|
44787
45246
|
}
|
|
44788
|
-
async
|
|
44789
|
-
|
|
44790
|
-
|
|
44791
|
-
|
|
44792
|
-
|
|
44793
|
-
|
|
44794
|
-
|
|
44795
|
-
|
|
44796
|
-
|
|
44797
|
-
|
|
44798
|
-
|
|
44799
|
-
|
|
44800
|
-
try {
|
|
44801
|
-
const data = this.form.value;
|
|
44802
|
-
// Disable the form
|
|
44803
|
-
this.form.disable();
|
|
44804
|
-
const entity = Message.fromObject(data);
|
|
44805
|
-
return this.viewCtrl.dismiss(entity);
|
|
44806
|
-
}
|
|
44807
|
-
catch (err) {
|
|
44808
|
-
this.form.error = (err && err.message) || err;
|
|
44809
|
-
this.markAsLoaded();
|
|
44810
|
-
// Enable the form
|
|
44811
|
-
this.form.enable();
|
|
44812
|
-
// Reset form error on next changes
|
|
44813
|
-
firstNotNilPromise(this.form.form.valueChanges).then(() => {
|
|
44814
|
-
this.form.error = null;
|
|
44815
|
-
this.markForCheck();
|
|
44816
|
-
});
|
|
44817
|
-
return;
|
|
45247
|
+
async removePageHistory(opts) {
|
|
45248
|
+
return this.settings.removePageHistory(this.router.url, opts);
|
|
45249
|
+
}
|
|
45250
|
+
computePageUrl(id) {
|
|
45251
|
+
const parentUrl = this.getParentPageUrl();
|
|
45252
|
+
return parentUrl && `${parentUrl}/${id}`;
|
|
45253
|
+
}
|
|
45254
|
+
getParentPageUrl(withQueryParams) {
|
|
45255
|
+
let parentUrl = this.defaultBackHref;
|
|
45256
|
+
// Remove query params
|
|
45257
|
+
if (withQueryParams !== true && parentUrl && parentUrl.indexOf('?') !== -1) {
|
|
45258
|
+
parentUrl = parentUrl.substring(0, parentUrl.indexOf('?'));
|
|
44818
45259
|
}
|
|
45260
|
+
return parentUrl;
|
|
45261
|
+
}
|
|
45262
|
+
listenChanges(id, opts) {
|
|
45263
|
+
return this.dataService.listenChanges(this.data.id, {
|
|
45264
|
+
// Make sure to skip cache, because NOT the full graph will be fetched by subscription
|
|
45265
|
+
// When a changes occur, we call reload() to force refetching the full graph
|
|
45266
|
+
fetchPolicy: 'no-cache',
|
|
45267
|
+
...opts,
|
|
45268
|
+
});
|
|
44819
45269
|
}
|
|
44820
45270
|
markForCheck() {
|
|
44821
45271
|
this.cd.markForCheck();
|
|
44822
45272
|
}
|
|
44823
|
-
|
|
44824
|
-
|
|
45273
|
+
/**
|
|
45274
|
+
* Update the route, without reloading the component
|
|
45275
|
+
*
|
|
45276
|
+
* @param data
|
|
45277
|
+
* @param queryParams
|
|
45278
|
+
* @protected
|
|
45279
|
+
*/
|
|
45280
|
+
async updateRoute(data, queryParams) {
|
|
45281
|
+
data = data || this.data;
|
|
45282
|
+
if (!data || !this.route)
|
|
45283
|
+
return false;
|
|
45284
|
+
const currId = this.route.snapshot.paramMap.get(this.pathIdAttribute);
|
|
45285
|
+
const futureId = isNotNil(data.id) ? data.id.toString() : 'new';
|
|
45286
|
+
if (queryParams) {
|
|
45287
|
+
this.queryParams = {
|
|
45288
|
+
...this.queryParams,
|
|
45289
|
+
...queryParams,
|
|
45290
|
+
};
|
|
45291
|
+
}
|
|
45292
|
+
if (currId === futureId) {
|
|
45293
|
+
// Update queryParam only (not the path)
|
|
45294
|
+
// /!\ We should get query Params from the updated route, and not from snapshot that can be outdated
|
|
45295
|
+
const actualQueryParams = await firstNotNilPromise(this.route.queryParams);
|
|
45296
|
+
if (!equals(actualQueryParams, this.queryParams)) {
|
|
45297
|
+
if (this.debug)
|
|
45298
|
+
console.debug(`${this._logPrefix}Updating route using queryParams: `, this.queryParams);
|
|
45299
|
+
return await this.router.navigate(['.'], {
|
|
45300
|
+
relativeTo: this.route,
|
|
45301
|
+
replaceUrl: false,
|
|
45302
|
+
queryParams: this.queryParams,
|
|
45303
|
+
state: { animated: false },
|
|
45304
|
+
});
|
|
45305
|
+
}
|
|
45306
|
+
}
|
|
45307
|
+
else {
|
|
45308
|
+
const path = this.computePageUrl(isNotNil(data.id) ? data.id : 'new');
|
|
45309
|
+
const commands = path && typeof path === 'string' ? path.split('/').slice(1) : path;
|
|
45310
|
+
if (isNotEmptyArray(commands)) {
|
|
45311
|
+
// Current route was '/new' => change to '/new?id=:id' (to allow CustomReuseStrategy to detect that route can be reused)
|
|
45312
|
+
if (currId === 'new' && this.queryParams[this.pathIdAttribute] !== futureId) {
|
|
45313
|
+
if (this.debug)
|
|
45314
|
+
console.debug(`${this._logPrefix}Updating route /new into /:id, using queryParams: `, queryParams);
|
|
45315
|
+
this.queryParams[this.pathIdAttribute] = futureId;
|
|
45316
|
+
const res = await this.router.navigate(['.'], {
|
|
45317
|
+
relativeTo: this.route,
|
|
45318
|
+
replaceUrl: true,
|
|
45319
|
+
queryParams: this.queryParams,
|
|
45320
|
+
state: { animated: false },
|
|
45321
|
+
});
|
|
45322
|
+
if (!res)
|
|
45323
|
+
return false;
|
|
45324
|
+
}
|
|
45325
|
+
if (this.debug)
|
|
45326
|
+
console.debug(`${this._logPrefix}Updating route to: ` + path);
|
|
45327
|
+
return await this.router.navigate(commands, {
|
|
45328
|
+
replaceUrl: true,
|
|
45329
|
+
queryParams: this.queryParams,
|
|
45330
|
+
state: { animated: false },
|
|
45331
|
+
});
|
|
45332
|
+
}
|
|
45333
|
+
return Promise.reject('Missing page URL');
|
|
45334
|
+
}
|
|
44825
45335
|
}
|
|
44826
|
-
|
|
44827
|
-
|
|
45336
|
+
/* -- private functions -- */
|
|
45337
|
+
/**
|
|
45338
|
+
* Open the first tab that is invalid
|
|
45339
|
+
*/
|
|
45340
|
+
openFirstInvalidTab() {
|
|
45341
|
+
const invalidTabIndex = this.getFirstInvalidTabIndex();
|
|
45342
|
+
if (invalidTabIndex !== -1 && this.selectedTabIndex !== invalidTabIndex) {
|
|
45343
|
+
this.selectedTabIndex = invalidTabIndex;
|
|
45344
|
+
this.markForCheck();
|
|
45345
|
+
}
|
|
44828
45346
|
}
|
|
44829
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type:
|
|
44830
|
-
static
|
|
45347
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AppEntityEditor, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive });
|
|
45348
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: AppEntityEditor, usesInheritance: true, ngImport: i0 });
|
|
44831
45349
|
}
|
|
44832
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type:
|
|
44833
|
-
type:
|
|
44834
|
-
|
|
44835
|
-
|
|
44836
|
-
|
|
44837
|
-
}], data: [{
|
|
44838
|
-
type: Input
|
|
44839
|
-
}], canSelectType: [{
|
|
44840
|
-
type: Input
|
|
44841
|
-
}], canRecipientFilter: [{
|
|
44842
|
-
type: Input
|
|
44843
|
-
}], recipientFilterCount: [{
|
|
44844
|
-
type: Input
|
|
44845
|
-
}], form: [{
|
|
44846
|
-
type: ViewChild,
|
|
44847
|
-
args: ['form', { static: true }]
|
|
44848
|
-
}] } });
|
|
45350
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AppEntityEditor, decorators: [{
|
|
45351
|
+
type: Directive
|
|
45352
|
+
}], ctorParameters: () => [{ type: i0.Injector }, { type: undefined }, { type: undefined }, { type: AppEditorOptions, decorators: [{
|
|
45353
|
+
type: Optional
|
|
45354
|
+
}] }] });
|
|
44849
45355
|
|
|
44850
45356
|
class MessageModule {
|
|
44851
45357
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
44852
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: MessageModule, declarations: [MessageModal, MessageForm], imports: [CommonModule, CoreModule, SharedModule, MatChipsModule], exports: [MessageModal] });
|
|
44853
|
-
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageModule, imports: [CommonModule, CoreModule, SharedModule, MatChipsModule] });
|
|
45358
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.13", ngImport: i0, type: MessageModule, declarations: [MessageModal, MessageForm], imports: [CommonModule, CoreModule, SharedModule, MatChipsModule, SharedDebugModule], exports: [MessageModal] });
|
|
45359
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageModule, imports: [CommonModule, CoreModule, SharedModule, MatChipsModule, SharedDebugModule] });
|
|
44854
45360
|
}
|
|
44855
45361
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageModule, decorators: [{
|
|
44856
45362
|
type: NgModule,
|
|
44857
45363
|
args: [{
|
|
44858
|
-
imports: [CommonModule, CoreModule, SharedModule, MatChipsModule],
|
|
45364
|
+
imports: [CommonModule, CoreModule, SharedModule, MatChipsModule, SharedDebugModule],
|
|
44859
45365
|
declarations: [MessageModal, MessageForm],
|
|
44860
45366
|
exports: [MessageModal],
|
|
44861
45367
|
}]
|
|
@@ -45283,271 +45789,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
45283
45789
|
}]
|
|
45284
45790
|
}] });
|
|
45285
45791
|
|
|
45286
|
-
const Mutations = {
|
|
45287
|
-
send: gql$1 `
|
|
45288
|
-
mutation SendMessage($data: MessageVOInput) {
|
|
45289
|
-
done: sendMessage(message: $data)
|
|
45290
|
-
}
|
|
45291
|
-
`,
|
|
45292
|
-
};
|
|
45293
|
-
class MessageService extends BaseGraphqlService {
|
|
45294
|
-
graphql;
|
|
45295
|
-
translate;
|
|
45296
|
-
modalCtrl;
|
|
45297
|
-
toastController;
|
|
45298
|
-
environment;
|
|
45299
|
-
constructor(graphql, translate, modalCtrl, toastController, environment) {
|
|
45300
|
-
super(graphql, environment);
|
|
45301
|
-
this.graphql = graphql;
|
|
45302
|
-
this.translate = translate;
|
|
45303
|
-
this.modalCtrl = modalCtrl;
|
|
45304
|
-
this.toastController = toastController;
|
|
45305
|
-
this.environment = environment;
|
|
45306
|
-
// For DEV only
|
|
45307
|
-
this._debug = !environment.production;
|
|
45308
|
-
}
|
|
45309
|
-
/**
|
|
45310
|
-
* Send a message to recipient(s)
|
|
45311
|
-
*
|
|
45312
|
-
* @param entity
|
|
45313
|
-
* @param opts
|
|
45314
|
-
*/
|
|
45315
|
-
async send(entity, opts) {
|
|
45316
|
-
// Transform into json
|
|
45317
|
-
const data = entity.asObject(MINIFY_ENTITY_FOR_POD);
|
|
45318
|
-
const now = Date.now();
|
|
45319
|
-
if (this._debug)
|
|
45320
|
-
console.debug(`[message-service] Sending message...`);
|
|
45321
|
-
try {
|
|
45322
|
-
const { done } = await this.graphql.mutate({
|
|
45323
|
-
mutation: Mutations.send,
|
|
45324
|
-
variables: { data },
|
|
45325
|
-
error: { code: SocialErrorCodes.SEND_MESSAGE_ERROR, message: 'SOCIAL.ERROR.SEND_MESSAGE_ERROR' },
|
|
45326
|
-
});
|
|
45327
|
-
if (this._debug)
|
|
45328
|
-
console.debug(`[message-service] Send message [OK] in ${Date.now() - now}ms`);
|
|
45329
|
-
if (done && (!opts || opts.showToast !== false)) {
|
|
45330
|
-
await this.showToast({ type: 'info', message: 'SOCIAL.INFO.MESSAGE_SENT' });
|
|
45331
|
-
}
|
|
45332
|
-
return done;
|
|
45333
|
-
}
|
|
45334
|
-
catch (err) {
|
|
45335
|
-
const error = (err && err.message) || err;
|
|
45336
|
-
console.error(error);
|
|
45337
|
-
// Show error
|
|
45338
|
-
if (!opts || opts.showToast !== false) {
|
|
45339
|
-
const message = error || 'SOCIAL.ERROR.SEND_MESSAGE_ERROR';
|
|
45340
|
-
await this.showToast({ type: 'error', message });
|
|
45341
|
-
}
|
|
45342
|
-
return false;
|
|
45343
|
-
}
|
|
45344
|
-
}
|
|
45345
|
-
async openComposeModal(options) {
|
|
45346
|
-
const hasTopModal = !!(await this.modalCtrl.getTop());
|
|
45347
|
-
const modal = await this.modalCtrl.create({
|
|
45348
|
-
component: MessageModal,
|
|
45349
|
-
componentProps: options,
|
|
45350
|
-
cssClass: hasTopModal && 'stack-modal',
|
|
45351
|
-
});
|
|
45352
|
-
// Open the modal
|
|
45353
|
-
await modal.present();
|
|
45354
|
-
// On dismiss
|
|
45355
|
-
const { data } = await modal.onDidDismiss();
|
|
45356
|
-
if (!data || !(data instanceof Message))
|
|
45357
|
-
return true; // CANCELLED
|
|
45358
|
-
// Send message
|
|
45359
|
-
return await this.send(data, { showToast: options?.showToast });
|
|
45360
|
-
}
|
|
45361
|
-
/* -- protected methods -- */
|
|
45362
|
-
async showToast(opts) {
|
|
45363
|
-
return Toasts.show(this.toastController, this.translate, {
|
|
45364
|
-
type: 'info',
|
|
45365
|
-
message: 'SOCIAL.INFO.MESSAGE_SENT',
|
|
45366
|
-
...opts,
|
|
45367
|
-
});
|
|
45368
|
-
}
|
|
45369
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageService, deps: [{ token: GraphqlService }, { token: i1$1.TranslateService }, { token: i2$1.ModalController }, { token: i2$1.ToastController }, { token: ENVIRONMENT, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
45370
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageService, providedIn: 'root' });
|
|
45371
|
-
}
|
|
45372
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageService, decorators: [{
|
|
45373
|
-
type: Injectable,
|
|
45374
|
-
args: [{ providedIn: 'root' }]
|
|
45375
|
-
}], ctorParameters: () => [{ type: GraphqlService }, { type: i1$1.TranslateService }, { type: i2$1.ModalController }, { type: i2$1.ToastController }, { type: Environment, decorators: [{
|
|
45376
|
-
type: Optional
|
|
45377
|
-
}, {
|
|
45378
|
-
type: Inject,
|
|
45379
|
-
args: [ENVIRONMENT]
|
|
45380
|
-
}] }] });
|
|
45381
|
-
|
|
45382
|
-
const PersonFragments = {
|
|
45383
|
-
person: gql$1 `
|
|
45384
|
-
fragment PersonFragment on PersonVO {
|
|
45385
|
-
id
|
|
45386
|
-
firstName
|
|
45387
|
-
lastName
|
|
45388
|
-
email
|
|
45389
|
-
pubkey
|
|
45390
|
-
avatar
|
|
45391
|
-
statusId
|
|
45392
|
-
updateDate
|
|
45393
|
-
creationDate
|
|
45394
|
-
profiles
|
|
45395
|
-
username
|
|
45396
|
-
usernameExtranet
|
|
45397
|
-
department {
|
|
45398
|
-
id
|
|
45399
|
-
label
|
|
45400
|
-
name
|
|
45401
|
-
logo
|
|
45402
|
-
__typename
|
|
45403
|
-
}
|
|
45404
|
-
__typename
|
|
45405
|
-
}
|
|
45406
|
-
`,
|
|
45407
|
-
};
|
|
45408
|
-
// Load persons query
|
|
45409
|
-
const PersonQueries = {
|
|
45410
|
-
loadAll: gql$1 `
|
|
45411
|
-
query Persons($offset: Int, $size: Int, $sortBy: String, $sortDirection: String, $filter: PersonFilterVOInput) {
|
|
45412
|
-
data: persons(filter: $filter, offset: $offset, size: $size, sortBy: $sortBy, sortDirection: $sortDirection) {
|
|
45413
|
-
...PersonFragment
|
|
45414
|
-
}
|
|
45415
|
-
}
|
|
45416
|
-
${PersonFragments.person}
|
|
45417
|
-
`,
|
|
45418
|
-
loadAllWithTotal: gql$1 `
|
|
45419
|
-
query PersonsWithTotal($offset: Int, $size: Int, $sortBy: String, $sortDirection: String, $filter: PersonFilterVOInput) {
|
|
45420
|
-
data: persons(filter: $filter, offset: $offset, size: $size, sortBy: $sortBy, sortDirection: $sortDirection) {
|
|
45421
|
-
...PersonFragment
|
|
45422
|
-
}
|
|
45423
|
-
total: personsCount(filter: $filter)
|
|
45424
|
-
}
|
|
45425
|
-
${PersonFragments.person}
|
|
45426
|
-
`,
|
|
45427
|
-
};
|
|
45428
|
-
const PersonMutations = {
|
|
45429
|
-
saveAll: gql$1 `
|
|
45430
|
-
mutation savePersons($data: [PersonVOInput]) {
|
|
45431
|
-
data: savePersons(persons: $data) {
|
|
45432
|
-
...PersonFragment
|
|
45433
|
-
}
|
|
45434
|
-
}
|
|
45435
|
-
${PersonFragments.person}
|
|
45436
|
-
`,
|
|
45437
|
-
deleteAll: gql$1 `
|
|
45438
|
-
mutation deletePersons($ids: [Int]) {
|
|
45439
|
-
deletePersons(ids: $ids)
|
|
45440
|
-
}
|
|
45441
|
-
`,
|
|
45442
|
-
};
|
|
45443
|
-
class PersonService extends BaseEntityService {
|
|
45444
|
-
graphql;
|
|
45445
|
-
platform;
|
|
45446
|
-
network;
|
|
45447
|
-
entities;
|
|
45448
|
-
constructor(graphql, platform, network, entities) {
|
|
45449
|
-
super(graphql, platform, Person, PersonFilter, {
|
|
45450
|
-
queries: PersonQueries,
|
|
45451
|
-
mutations: PersonMutations,
|
|
45452
|
-
defaultSortBy: 'lastName',
|
|
45453
|
-
defaultSortDirection: 'asc',
|
|
45454
|
-
});
|
|
45455
|
-
this.graphql = graphql;
|
|
45456
|
-
this.platform = platform;
|
|
45457
|
-
this.network = network;
|
|
45458
|
-
this.entities = entities;
|
|
45459
|
-
// for DEV only -----
|
|
45460
|
-
this._debug = !environment.production;
|
|
45461
|
-
}
|
|
45462
|
-
async loadAll(offset, size, sortBy, sortDirection, filter, opts) {
|
|
45463
|
-
const offline = this.network.offline && (!opts || opts.fetchPolicy !== 'network-only');
|
|
45464
|
-
if (offline) {
|
|
45465
|
-
return this.loadAllLocally(offset, size, sortBy, sortDirection, filter, opts);
|
|
45466
|
-
}
|
|
45467
|
-
return super.loadAll(offset, size, sortBy, sortDirection, filter, opts);
|
|
45468
|
-
}
|
|
45469
|
-
async loadAllLocally(offset, size, sortBy, sortDirection, filter, opts) {
|
|
45470
|
-
filter = this.asFilter(filter);
|
|
45471
|
-
const variables = {
|
|
45472
|
-
offset: offset || 0,
|
|
45473
|
-
size: size || 100,
|
|
45474
|
-
sortBy: sortBy || this.defaultSortBy,
|
|
45475
|
-
sortDirection: sortDirection || this.defaultSortDirection,
|
|
45476
|
-
filter: filter && filter.asFilterFn(),
|
|
45477
|
-
};
|
|
45478
|
-
const { data, total } = await this.entities.loadAll('PersonVO', variables);
|
|
45479
|
-
const entities = this.fromObjects(data, opts);
|
|
45480
|
-
const res = { data: entities, total };
|
|
45481
|
-
// Add fetch more function
|
|
45482
|
-
const nextOffset = (offset || 0) + entities.length;
|
|
45483
|
-
if (nextOffset < total) {
|
|
45484
|
-
res.fetchMore = () => this.loadAllLocally(nextOffset, size, sortBy, sortDirection, filter, opts);
|
|
45485
|
-
}
|
|
45486
|
-
return res;
|
|
45487
|
-
}
|
|
45488
|
-
async suggest(value, filter, sortBy, sortDirection, opts) {
|
|
45489
|
-
if (EntityUtils.isNotEmpty(value, 'id'))
|
|
45490
|
-
return { data: [value] };
|
|
45491
|
-
value = (typeof value === 'string' && value !== '*' && value) || undefined;
|
|
45492
|
-
sortBy = sortBy || filter.searchAttribute || (filter.searchAttributes && filter.searchAttributes[0]);
|
|
45493
|
-
return this.loadAll(0, !value ? 30 : 10, sortBy, sortDirection, {
|
|
45494
|
-
...filter,
|
|
45495
|
-
searchText: value,
|
|
45496
|
-
statusIds: (filter && filter.statusIds) || [StatusIds.ENABLE, StatusIds.TEMPORARY],
|
|
45497
|
-
userProfiles: filter && filter.userProfiles,
|
|
45498
|
-
}, {
|
|
45499
|
-
withTotal: true /* need by autocomplete */,
|
|
45500
|
-
...opts,
|
|
45501
|
-
});
|
|
45502
|
-
}
|
|
45503
|
-
async executeImport(filter, opts) {
|
|
45504
|
-
const maxProgression = (opts && opts.maxProgression) || 100;
|
|
45505
|
-
filter = {
|
|
45506
|
-
...filter,
|
|
45507
|
-
statusIds: [StatusIds.ENABLE, StatusIds.TEMPORARY],
|
|
45508
|
-
userProfiles: ['SUPERVISOR', 'USER', 'GUEST'],
|
|
45509
|
-
};
|
|
45510
|
-
console.info('[person-service] Importing persons...');
|
|
45511
|
-
const res = await JobUtils.fetchAllPages((offset, size) => this.loadAll(offset, size, 'id', null, filter, {
|
|
45512
|
-
debug: false,
|
|
45513
|
-
fetchPolicy: 'network-only',
|
|
45514
|
-
withTotal: offset === 0, // Compute total only once
|
|
45515
|
-
toEntity: false,
|
|
45516
|
-
}), { progression: opts?.progression, maxProgression: maxProgression * 0.9 });
|
|
45517
|
-
// Save result locally
|
|
45518
|
-
await this.entities.saveAll(res.data, { entityName: 'PersonVO', reset: true });
|
|
45519
|
-
}
|
|
45520
|
-
async loadById(id, opts) {
|
|
45521
|
-
const { data } = await this.loadAll(0, 1, null, null, { includedIds: [id] }, { withTotal: false, ...opts });
|
|
45522
|
-
const source = isNotEmptyArray(data) ? data[0] : { id };
|
|
45523
|
-
return this.fromObject(source, opts);
|
|
45524
|
-
}
|
|
45525
|
-
async loadByPubkey(pubkey, opts) {
|
|
45526
|
-
const { data } = await this.loadAll(0, 1, null, null, { pubkey }, { withTotal: false, ...opts });
|
|
45527
|
-
const source = isNotEmptyArray(data) ? data[0] : { pubkey };
|
|
45528
|
-
return this.fromObject(source, opts);
|
|
45529
|
-
}
|
|
45530
|
-
/* -- protected methods -- */
|
|
45531
|
-
asObject(source) {
|
|
45532
|
-
if (!source)
|
|
45533
|
-
return undefined;
|
|
45534
|
-
if (!(source instanceof Person)) {
|
|
45535
|
-
source = Person.fromObject(source);
|
|
45536
|
-
}
|
|
45537
|
-
const target = source.asObject();
|
|
45538
|
-
// Not known in server GraphQL schema
|
|
45539
|
-
delete target.mainProfile;
|
|
45540
|
-
target.department = source.department?.asObject();
|
|
45541
|
-
return target;
|
|
45542
|
-
}
|
|
45543
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PersonService, deps: [{ token: GraphqlService }, { token: PlatformService }, { token: NetworkService }, { token: EntitiesStorage }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
45544
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PersonService, providedIn: 'root' });
|
|
45545
|
-
}
|
|
45546
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PersonService, decorators: [{
|
|
45547
|
-
type: Injectable,
|
|
45548
|
-
args: [{ providedIn: 'root' }]
|
|
45549
|
-
}], ctorParameters: () => [{ type: GraphqlService }, { type: PlatformService }, { type: NetworkService }, { type: EntitiesStorage }] });
|
|
45550
|
-
|
|
45551
45792
|
class UsersPage extends AppTable {
|
|
45552
45793
|
accountService;
|
|
45553
45794
|
validatorService;
|
|
@@ -50149,11 +50390,11 @@ class FeedTestingPage {
|
|
|
50149
50390
|
this.cd.markForCheck();
|
|
50150
50391
|
}
|
|
50151
50392
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedTestingPage, deps: [{ token: LocalSettingsService }, { token: i0.ChangeDetectorRef }, { token: ENVIRONMENT, optional: true }], target: i0.ɵɵFactoryTarget.Component });
|
|
50152
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: FeedTestingPage, selector: "app-feed-test", ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Feed test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n <ion-grid class=\"form-container ion-padding-top\">\n <ion-row>\n <ion-col size=\"12\">\n <mat-form-field [subscriptSizing]=\"'dynamic'\">\n <mat-label>Feed URL(s)</mat-label>\n <mat-select [(value)]=\"feedUrls\" multiple>\n <mat-option *ngFor=\"let feedUrl of availableFeedUrls\" [value]=\"feedUrl\">\n {{ feedUrl }}\n </mat-option>\n <mat-option>(Use environment)</mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"3\">\n <mat-form-field [subscriptSizing]=\"'dynamic'\">\n <mat-label>Header color</mat-label>\n <mat-select [(value)]=\"headerColor\">\n <mat-option *ngFor=\"let color of availableColors\" [value]=\"color\">\n {{ color }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"3\">\n <mat-form-field [subscriptSizing]=\"'dynamic'\">\n <mat-label>Card color</mat-label>\n <mat-select [(value)]=\"cardColor\">\n <mat-option *ngFor=\"let color of availableColors\" [value]=\"color\">\n {{ color }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"3\">\n <mat-form-field [subscriptSizing]=\"'dynamic'\">\n <mat-label>Shape</mat-label>\n <mat-select [(value)]=\"shape\">\n <mat-option *ngFor=\"let shape of availableShapes\" [value]=\"shape\">\n {{ shape }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"debug = $event.checked\" [checked]=\"debug\">Debug ?</mat-checkbox>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <div class=\"ion-padding\">\n <app-feed\n [
|
|
50393
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: FeedTestingPage, selector: "app-feed-test", ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Feed test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n <ion-grid class=\"form-container ion-padding-top\">\n <ion-row>\n <ion-col size=\"12\">\n <mat-form-field [subscriptSizing]=\"'dynamic'\">\n <mat-label>Feed URL(s)</mat-label>\n <mat-select [(value)]=\"feedUrls\" multiple>\n <mat-option *ngFor=\"let feedUrl of availableFeedUrls\" [value]=\"feedUrl\">\n {{ feedUrl }}\n </mat-option>\n <mat-option>(Use environment)</mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"3\">\n <mat-form-field [subscriptSizing]=\"'dynamic'\">\n <mat-label>Header color</mat-label>\n <mat-select [(value)]=\"headerColor\">\n <mat-option *ngFor=\"let color of availableColors\" [value]=\"color\">\n {{ color }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"3\">\n <mat-form-field [subscriptSizing]=\"'dynamic'\">\n <mat-label>Card color</mat-label>\n <mat-select [(value)]=\"cardColor\">\n <mat-option *ngFor=\"let color of availableColors\" [value]=\"color\">\n {{ color }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"3\">\n <mat-form-field [subscriptSizing]=\"'dynamic'\">\n <mat-label>Shape</mat-label>\n <mat-select [(value)]=\"shape\">\n <mat-option *ngFor=\"let shape of availableShapes\" [value]=\"shape\">\n {{ shape }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"debug = $event.checked\" [checked]=\"debug\">Debug ?</mat-checkbox>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <div class=\"ion-padding\">\n <app-feed\n [urls]=\"feedUrls\"\n [headerColor]=\"headerColor\"\n [cardColor]=\"cardColor\"\n [shape]=\"shape\"\n [debug]=\"debug\"\n ></app-feed>\n </div>\n</ion-content>\n", dependencies: [{ kind: "directive", type: i3$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: i2$1.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { 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.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$1.IonRow, selector: "ion-row" }, { kind: "component", type: i2$1.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$1.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonBackButton, selector: "ion-back-button" }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "component", type: i5.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i6$3.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i2.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: FeedsComponent, selector: "app-feed", inputs: ["debug", "mobile", "showHeader", "showReadMoreButton", "headerColor", "cardColor", "shape", "class", "itemId", "filterItem", "feeds", "urls", "maxAgeInMonths", "maxContentLength"], outputs: ["editItem", "deleteItem"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
50153
50394
|
}
|
|
50154
50395
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedTestingPage, decorators: [{
|
|
50155
50396
|
type: Component,
|
|
50156
|
-
args: [{ selector: 'app-feed-test', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Feed test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n <ion-grid class=\"form-container ion-padding-top\">\n <ion-row>\n <ion-col size=\"12\">\n <mat-form-field [subscriptSizing]=\"'dynamic'\">\n <mat-label>Feed URL(s)</mat-label>\n <mat-select [(value)]=\"feedUrls\" multiple>\n <mat-option *ngFor=\"let feedUrl of availableFeedUrls\" [value]=\"feedUrl\">\n {{ feedUrl }}\n </mat-option>\n <mat-option>(Use environment)</mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"3\">\n <mat-form-field [subscriptSizing]=\"'dynamic'\">\n <mat-label>Header color</mat-label>\n <mat-select [(value)]=\"headerColor\">\n <mat-option *ngFor=\"let color of availableColors\" [value]=\"color\">\n {{ color }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"3\">\n <mat-form-field [subscriptSizing]=\"'dynamic'\">\n <mat-label>Card color</mat-label>\n <mat-select [(value)]=\"cardColor\">\n <mat-option *ngFor=\"let color of availableColors\" [value]=\"color\">\n {{ color }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"3\">\n <mat-form-field [subscriptSizing]=\"'dynamic'\">\n <mat-label>Shape</mat-label>\n <mat-select [(value)]=\"shape\">\n <mat-option *ngFor=\"let shape of availableShapes\" [value]=\"shape\">\n {{ shape }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"debug = $event.checked\" [checked]=\"debug\">Debug ?</mat-checkbox>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <div class=\"ion-padding\">\n <app-feed\n [
|
|
50397
|
+
args: [{ selector: 'app-feed-test', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Feed test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n <ion-grid class=\"form-container ion-padding-top\">\n <ion-row>\n <ion-col size=\"12\">\n <mat-form-field [subscriptSizing]=\"'dynamic'\">\n <mat-label>Feed URL(s)</mat-label>\n <mat-select [(value)]=\"feedUrls\" multiple>\n <mat-option *ngFor=\"let feedUrl of availableFeedUrls\" [value]=\"feedUrl\">\n {{ feedUrl }}\n </mat-option>\n <mat-option>(Use environment)</mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"3\">\n <mat-form-field [subscriptSizing]=\"'dynamic'\">\n <mat-label>Header color</mat-label>\n <mat-select [(value)]=\"headerColor\">\n <mat-option *ngFor=\"let color of availableColors\" [value]=\"color\">\n {{ color }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"3\">\n <mat-form-field [subscriptSizing]=\"'dynamic'\">\n <mat-label>Card color</mat-label>\n <mat-select [(value)]=\"cardColor\">\n <mat-option *ngFor=\"let color of availableColors\" [value]=\"color\">\n {{ color }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"3\">\n <mat-form-field [subscriptSizing]=\"'dynamic'\">\n <mat-label>Shape</mat-label>\n <mat-select [(value)]=\"shape\">\n <mat-option *ngFor=\"let shape of availableShapes\" [value]=\"shape\">\n {{ shape }}\n </mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"debug = $event.checked\" [checked]=\"debug\">Debug ?</mat-checkbox>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <div class=\"ion-padding\">\n <app-feed\n [urls]=\"feedUrls\"\n [headerColor]=\"headerColor\"\n [cardColor]=\"cardColor\"\n [shape]=\"shape\"\n [debug]=\"debug\"\n ></app-feed>\n </div>\n</ion-content>\n" }]
|
|
50157
50398
|
}], ctorParameters: () => [{ type: LocalSettingsService }, { type: i0.ChangeDetectorRef }, { type: Environment, decorators: [{
|
|
50158
50399
|
type: Optional
|
|
50159
50400
|
}, {
|
|
@@ -50268,5 +50509,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
50268
50509
|
* Generated bundle index. Do not edit.
|
|
50269
50510
|
*/
|
|
50270
50511
|
|
|
50271
|
-
export { APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_CONFIG_OPTIONS, APP_DEBUG_DATA_SERVICE, APP_FEED_SERVICE, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_FRAGMENTS, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_HOME_CONFIG, APP_HOTKEYS_CONFIG, APP_JOB_PROGRESSION_SERVICE, APP_LOCALES, APP_LOCAL_SETTINGS, APP_LOCAL_SETTINGS_OPTIONS, APP_LOCAL_STORAGE_TYPE_POLICIES, APP_LOGGING_SERVICE, APP_MENU_ITEMS, APP_MENU_OPTIONS, APP_NAMED_FILTER_SERVICE, APP_PROGRESS_BAR_SERVICE, APP_SETTINGS_MENU_ITEMS, APP_STORAGE, APP_STORAGE_EXPLORER_PROTECTED_KEYS, APP_TESTING_PAGES, APP_USER_EVENT_LIST_INFINITE_SCROLL_THRESHOLD, APP_USER_EVENT_SERVICE, APP_USER_SETTINGS_OPTIONS, APP_USER_TOKEN_SCOPES, AboutModal, AbstractNamedFilterService, AbstractSelectionModelPipe, AbstractTableSelectionPipe, AbstractUserEventService, Account, AccountPage, AccountService, AccountToStringPipe, AccountUtils, ActionsColumnComponent, AdminModule, AdminRoutingModule, AdminUsersModule, Alerts, AndroidOsEnvironment, AppAboutModalModule, AppAccountModule, AppAsyncTable, AppAuthForm, AppAuthModal, AppAuthModule, AppChangePasswordModule, AppChangePasswordPage, AppEditor, AppEditorOptions, AppEntityEditor, AppEntityEditorModal, AppEntityEditorModalOptions, AppEntityFormModule, AppForm, AppFormArray, AppFormButtonsBarModule, AppFormContainer, AppFormField, AppFormModule, AppFormProvider, AppFormUtils, AppGestureConfig, AppGraphQLModule, AppHomePageModule, AppIconComponent, AppIconModule, AppImageGalleryComponent, AppInMemoryTable, AppInstallUpgradeCard, AppInstallUpgradeCardModule, AppListForm, AppListFormModule, AppLoadingSpinner, AppMarkdownContent, AppMarkdownModal, AppMenuModule, AppNullForm, AppPropertiesForm, AppPropertiesFormModule, AppPropertiesTable, AppPropertiesUtils, AppPropertyUtils, AppRegisterModule, AppResetPasswordModal, AppRowField, AppSelectPeerModule, AppSettingsPageModule, AppTabEditor, AppTabEditorOptions, AppTable, AppTableModule, AppTableUtils, AppTextPopoverModule, AppUpdateOfflineModeCard, AppUpdateOfflineModeCardModule, AppValidatorService, AppendQueryParamsPipePipe, ArrayDistinctPipe, ArrayFilterPipe, ArrayFindByPropertyPipe, ArrayFirstPipe, ArrayFormTestPage, ArrayIncludesPipe, ArrayJoinPipe, ArrayLastPipe, ArrayLengthPipe, ArrayMapPipe, ArrayPluckPipe, ArraySortPipe, AsAnyPipe, AsArrayPipe, AsBooleanPipe, AsFloatLabelTypePipe, AsObservablePipe, AudioProvider, AudioTestingModule, AudioTestingPage, AuthGuardService, AutoResizeDirective, AutoTitleDirective, AutocompleteTestPage, AutofocusDirective, BadgeDirective, BadgeNumberPipe, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, BooleanFormatPipe, BooleanTestPage, CORE_CONFIG_OPTIONS, CORE_TESTING_PAGES, CapitalizePipe, CellValueChangeListener, ChangePasswordForm, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigFragments, ConfigService, Configuration, CoreModule, CorePipesModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, DATE_MATCH_REGEXP, DATE_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_JOIN_ARRAY_VALUES_SEPARATOR, DEFAULT_JOIN_PROPERTIES_SEPARATOR, DEFAULT_MENU_SHOW_WHEN, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DateDiffDurationPipe, DateFormatPipe, DateFormatService, DateFromNowPipe, DateShortTestPage, DateTestPage, DateTimeTestPage, DateUtils, DebugComponent, Department, DepartmentToStringPipe, DisplayWithPipe, DragAndDropDirective, DurationPipe, DurationTestPage, ED25519_SEED_LENGTH, EMPTY_PLACEHOLDER_CHAR, EMPTY_PLACEHOLDER_CHAR_REGEXP_GLOBAL, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EntitiesAsyncTableDataSource, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, EnvironmentHttpLoader, EnvironmentLoader, ErrorCodes, EvenPipe, FeedDirective, FeedModule, FeedPage, FeedService, FeedsComponent, FileResponse, FileService, FileSizePipe, FilesUtils, FirstFalsePipe, FirstPipe, FirstTruePipe, FormArrayHelper, FormArrayTestModule, FormButtonsBarComponent, FormButtonsBarToken, FormErrorPipe, FormErrorTranslatePipe, FormErrorTranslator, FormFieldDefinitionUtils, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetPipe, FormGetValuePipe, GalleryTestPage, GeolocationUtils, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, IMAGE_DEFAULTS, IPosition, ImageAttachment, ImageAttachmentFilter, ImageAttachmentService, ImageGalleryModule, ImageGalleryTestingModule, ImageModule, ImageService, ImagesUtils, InMemoryEntitiesService, IsAllSelectedPipe, IsEmptySelectionPipe, IsLoginAccountPipe, IsMultipleSelectionPipe, IsNilOrBlankPipe, IsNilOrNaNPipe, IsNilPipe, IsNotAllSelectedPipe, IsNotEmptySelectionPipe, IsNotNilOrBlankPipe, IsNotNilOrNaNPipe, IsNotNilPipe, IsOnDeskPipe, IsOnFieldPipe, IsSelectedPipe, IsSingleSelectionPipe, IsValidDatePipe, JobModule, JobProgression, JobProgressionComponent, JobProgressionIcon, JobProgressionList, JobProgressionService, JobProgressionTestService, JobProgressionTestingPage, JobTestingModule, JobUtils, JsonFeedUtils, JsonUtils, KEYBOARD_HIDE_DELAY_MS, LAT_LONG_PATTERNS, LAT_LONG_PATTERN_MAX_DECIMALS, LAT_LONG_VALUE_MAX_DECIMALS, LatLongFormatPipe, LatLongTestPage, LatitudeFormatPipe, LocalSettingsService, LogLevel, LogUtils, Logger, LoggingService, LoggingServiceModule, LongitudeFormatPipe, MASKS, MASK_RANGES, MAT_FORM_FIELD_DEFAULT_APPEARANCE, MAT_FORM_FIELD_DEFAULT_SUBSCRIPT_SIZING, MINIFY_ENTITY_FOR_LOCAL_STORAGE, MINIFY_ENTITY_FOR_POD, MOMENT_NO_TIME_PROPERTY, MapGetPipe, MapKeysPipe, MapPipe, MapToPipe, MapValuesPipe, MarkdownDirective, MarkdownService, MarkdownTestPage, MarkdownTestingModule, MarkdownUtils, MaskitoPlaceholderPipe, MaskitoTestPage, MatAutocompleteConfigHolder, MatAutocompleteField, MatAutocompleteFieldUtils, MatBadgeTestPage, MatBooleanField, MatChipsField, MatColorPipe, MatCommonTestPage, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatLatLongFieldInput, MatPaginatorI18n, MatStepperI18n, MatSwipeField, MaterialTestingModule, MathAbsPipe, MenuComponent, MenuItem, MenuItems, MenuOptions, MenuService, MenuTestingModule, MenuTestingPage, Message, MessageFilter, MessageForm, MessageModal, MessageModule, MessageService, MessageTypeList, MessageTypes, MimeTypes, ModalToolbarComponent, NETWORK_DEFAULT_CONNECTION_TIMEOUT, NOOP_ITEM_FILTER, 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, TruncateHtmlPipe, 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, base64ArrayBuffer, 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, truncateHtml, uncapitalizeFirstLetter, undefinedIfNull, underscoreToChangeCase, updateValueAndValidity, waitFor, waitForFalse, waitForTrue, waitIdle, waitWhilePending };
|
|
50512
|
+
export { APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_CONFIG_OPTIONS, APP_DEBUG_DATA_SERVICE, APP_FEED_SERVICE, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_FRAGMENTS, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_HOME_CONFIG, APP_HOTKEYS_CONFIG, APP_JOB_PROGRESSION_SERVICE, APP_LOCALES, APP_LOCAL_SETTINGS, APP_LOCAL_SETTINGS_OPTIONS, APP_LOCAL_STORAGE_TYPE_POLICIES, APP_LOGGING_SERVICE, APP_MENU_ITEMS, APP_MENU_OPTIONS, APP_NAMED_FILTER_SERVICE, APP_PROGRESS_BAR_SERVICE, APP_SETTINGS_MENU_ITEMS, APP_STORAGE, APP_STORAGE_EXPLORER_PROTECTED_KEYS, APP_TESTING_PAGES, APP_USER_EVENT_LIST_INFINITE_SCROLL_THRESHOLD, APP_USER_EVENT_SERVICE, APP_USER_SETTINGS_OPTIONS, APP_USER_TOKEN_SCOPES, AboutModal, AbstractNamedFilterService, AbstractSelectionModelPipe, AbstractTableSelectionPipe, AbstractUserEventService, Account, AccountPage, AccountService, AccountToStringPipe, AccountUtils, ActionsColumnComponent, AdminModule, AdminRoutingModule, AdminUsersModule, Alerts, AndroidOsEnvironment, AppAboutModalModule, AppAccountModule, AppAsyncTable, AppAuthForm, AppAuthModal, AppAuthModule, AppChangePasswordModule, AppChangePasswordPage, AppEditor, AppEditorOptions, AppEntityEditor, AppEntityEditorModal, AppEntityEditorModalOptions, AppEntityFormModule, AppForm, AppFormArray, AppFormButtonsBarModule, AppFormContainer, AppFormField, AppFormModule, AppFormProvider, AppFormUtils, AppGestureConfig, AppGraphQLModule, AppHomePageModule, AppIconComponent, AppIconModule, AppImageGalleryComponent, AppInMemoryTable, AppInstallUpgradeCard, AppInstallUpgradeCardModule, AppListForm, AppListFormModule, AppLoadingSpinner, AppMarkdownContent, AppMarkdownModal, AppMenuModule, AppNullForm, AppPropertiesForm, AppPropertiesFormModule, AppPropertiesTable, AppPropertiesUtils, AppPropertyUtils, AppRegisterModule, AppResetPasswordModal, AppRowField, AppSelectPeerModule, AppSettingsPageModule, AppTabEditor, AppTabEditorOptions, AppTable, AppTableModule, AppTableUtils, AppTextPopoverModule, AppUpdateOfflineModeCard, AppUpdateOfflineModeCardModule, AppValidatorService, AppendQueryParamsPipePipe, ArrayDistinctPipe, ArrayFilterPipe, ArrayFindByPropertyPipe, ArrayFirstPipe, ArrayFormTestPage, ArrayIncludesPipe, ArrayJoinPipe, ArrayLastPipe, ArrayLengthPipe, ArrayMapPipe, ArrayPluckPipe, ArraySortPipe, AsAnyPipe, AsArrayPipe, AsBooleanPipe, AsFloatLabelTypePipe, AsObservablePipe, AudioProvider, AudioTestingModule, AudioTestingPage, AuthGuardService, AutoResizeDirective, AutoTitleDirective, AutocompleteTestPage, AutofocusDirective, BadgeDirective, BadgeNumberPipe, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, BooleanFormatPipe, BooleanTestPage, CORE_CONFIG_OPTIONS, CORE_TESTING_PAGES, CapitalizePipe, CellValueChangeListener, ChangePasswordForm, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigFragments, ConfigService, Configuration, CoreModule, CorePipesModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, DATE_MATCH_REGEXP, DATE_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_JOIN_ARRAY_VALUES_SEPARATOR, DEFAULT_JOIN_PROPERTIES_SEPARATOR, DEFAULT_MENU_SHOW_WHEN, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DateDiffDurationPipe, DateFormatPipe, DateFormatService, DateFromNowPipe, DateShortTestPage, DateTestPage, DateTimeTestPage, DateUtils, DebugComponent, Department, DepartmentToStringPipe, DisplayWithPipe, DragAndDropDirective, DurationPipe, DurationTestPage, ED25519_SEED_LENGTH, EMPTY_PLACEHOLDER_CHAR, EMPTY_PLACEHOLDER_CHAR_REGEXP_GLOBAL, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EntitiesAsyncTableDataSource, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, EnvironmentHttpLoader, EnvironmentLoader, ErrorCodes, EvenPipe, FeedDirective, FeedModule, FeedPage, FeedService, FeedsComponent, FileResponse, FileService, FileSizePipe, FilesUtils, FirstFalsePipe, FirstPipe, FirstTruePipe, FormArrayHelper, FormArrayTestModule, FormButtonsBarComponent, FormButtonsBarToken, FormErrorPipe, FormErrorTranslatePipe, FormErrorTranslator, FormFieldDefinitionUtils, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetPipe, FormGetValuePipe, GalleryTestPage, GeolocationUtils, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, IMAGE_DEFAULTS, IPosition, ImageAttachment, ImageAttachmentFilter, ImageAttachmentService, ImageGalleryModule, ImageGalleryTestingModule, ImageModule, ImageService, ImagesUtils, InMemoryEntitiesService, IsAllSelectedPipe, IsEmptySelectionPipe, IsLoginAccountPipe, IsMultipleSelectionPipe, IsNilOrBlankPipe, IsNilOrNaNPipe, IsNilPipe, IsNotAllSelectedPipe, IsNotEmptySelectionPipe, IsNotNilOrBlankPipe, IsNotNilOrNaNPipe, IsNotNilPipe, IsOnDeskPipe, IsOnFieldPipe, IsSelectedPipe, IsSingleSelectionPipe, IsValidDatePipe, JobModule, JobProgression, JobProgressionComponent, JobProgressionIcon, JobProgressionList, JobProgressionService, JobProgressionTestService, JobProgressionTestingPage, JobTestingModule, JobUtils, JsonFeedUtils, JsonUtils, KEYBOARD_HIDE_DELAY_MS, LAT_LONG_PATTERNS, LAT_LONG_PATTERN_MAX_DECIMALS, LAT_LONG_VALUE_MAX_DECIMALS, LatLongFormatPipe, LatLongTestPage, LatitudeFormatPipe, LocalSettingsService, LogLevel, LogUtils, Logger, LoggingService, LoggingServiceModule, LongitudeFormatPipe, MASKS, MASK_RANGES, MAT_FORM_FIELD_DEFAULT_APPEARANCE, MAT_FORM_FIELD_DEFAULT_SUBSCRIPT_SIZING, MINIFY_ENTITY_FOR_LOCAL_STORAGE, MINIFY_ENTITY_FOR_POD, MOMENT_NO_TIME_PROPERTY, MapGetPipe, MapKeysPipe, MapPipe, MapToPipe, MapValuesPipe, MarkdownDirective, MarkdownService, MarkdownTestPage, MarkdownTestingModule, MarkdownUtils, MaskitoPlaceholderPipe, MaskitoTestPage, MatAutocompleteConfigHolder, MatAutocompleteField, MatAutocompleteFieldUtils, MatBadgeTestPage, MatBooleanField, MatChipsField, MatColorPipe, MatCommonTestPage, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatLatLongFieldInput, MatPaginatorI18n, MatStepperI18n, MatSwipeField, 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, TruncateHtmlPipe, 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, base64ArrayBuffer, 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, truncateHtml, uncapitalizeFirstLetter, undefinedIfNull, underscoreToChangeCase, updateValueAndValidity, waitFor, waitForFalse, waitForTrue, waitIdle, waitWhilePending };
|
|
50272
50513
|
//# sourceMappingURL=sumaris-net.ngx-components.mjs.map
|