@sumaris-net/ngx-components 18.16.8 → 18.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/doc/changelog.md +4 -0
- package/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 +212 -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 +87 -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 +1310 -1048
- 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 +3 -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,282 +33794,1068 @@ 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));
|
|
33850
|
+
}
|
|
33851
|
+
// Filter excluded ids
|
|
33852
|
+
const excludedIds = this.excludedIds;
|
|
33853
|
+
if (isNotEmptyArray(excludedIds)) {
|
|
33854
|
+
filterFns.push((e) => isNil(e.id) || !excludedIds.includes(e.id));
|
|
33822
33855
|
}
|
|
33823
|
-
|
|
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;
|
|
33824
33861
|
}
|
|
33825
|
-
|
|
33826
|
-
|
|
33827
|
-
|
|
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();
|
|
33828
33900
|
}
|
|
33829
|
-
|
|
33830
|
-
|
|
33831
|
-
|
|
33832
|
-
|
|
33833
|
-
|
|
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;
|
|
33834
33909
|
}
|
|
33835
|
-
|
|
33836
|
-
const
|
|
33837
|
-
|
|
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;
|
|
33838
33915
|
}
|
|
33839
|
-
|
|
33840
|
-
|
|
33841
|
-
|
|
33842
|
-
|
|
33843
|
-
|
|
33844
|
-
|
|
33845
|
-
|
|
33846
|
-
|
|
33847
|
-
|
|
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 };
|
|
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);
|
|
33873
33925
|
}
|
|
33874
|
-
|
|
33875
|
-
|
|
33876
|
-
if (JsonFeedUtils.isJsonFeed(json)) {
|
|
33877
|
-
// Migrate from old versions
|
|
33878
|
-
let feed = JsonFeedUtils.migrateFeed(json);
|
|
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;
|
|
33926
|
+
buildFilter() {
|
|
33927
|
+
return super.buildFilter();
|
|
33887
33928
|
}
|
|
33888
|
-
|
|
33889
|
-
|
|
33890
|
-
|
|
33891
|
-
|
|
33892
|
-
|
|
33893
|
-
|
|
33894
|
-
|
|
33895
|
-
|
|
33896
|
-
|
|
33897
|
-
|
|
33898
|
-
|
|
33899
|
-
|
|
33900
|
-
|
|
33901
|
-
|
|
33902
|
-
|
|
33903
|
-
|
|
33904
|
-
|
|
33905
|
-
|
|
33906
|
-
|
|
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;
|
|
33929
|
+
};
|
|
33930
|
+
MessageFilter = __decorate([
|
|
33931
|
+
EntityClass({ typename: 'MessageFilterVO' })
|
|
33932
|
+
], MessageFilter);
|
|
33933
|
+
|
|
33934
|
+
const PersonFragments = {
|
|
33935
|
+
lightPerson: gql$1 `
|
|
33936
|
+
fragment LightPersonFragment on PersonVO {
|
|
33937
|
+
id
|
|
33938
|
+
firstName
|
|
33939
|
+
lastName
|
|
33940
|
+
avatar
|
|
33941
|
+
department {
|
|
33942
|
+
id
|
|
33943
|
+
label
|
|
33944
|
+
name
|
|
33945
|
+
__typename
|
|
33946
|
+
}
|
|
33947
|
+
__typename
|
|
33914
33948
|
}
|
|
33915
|
-
|
|
33916
|
-
|
|
33949
|
+
`,
|
|
33950
|
+
person: gql$1 `
|
|
33951
|
+
fragment PersonFragment on PersonVO {
|
|
33952
|
+
id
|
|
33953
|
+
firstName
|
|
33954
|
+
lastName
|
|
33955
|
+
email
|
|
33956
|
+
pubkey
|
|
33957
|
+
avatar
|
|
33958
|
+
statusId
|
|
33959
|
+
updateDate
|
|
33960
|
+
creationDate
|
|
33961
|
+
profiles
|
|
33962
|
+
username
|
|
33963
|
+
usernameExtranet
|
|
33964
|
+
department {
|
|
33965
|
+
id
|
|
33966
|
+
label
|
|
33967
|
+
name
|
|
33968
|
+
logo
|
|
33969
|
+
__typename
|
|
33970
|
+
}
|
|
33971
|
+
__typename
|
|
33917
33972
|
}
|
|
33918
|
-
|
|
33919
|
-
|
|
33920
|
-
|
|
33921
|
-
|
|
33922
|
-
|
|
33923
|
-
|
|
33924
|
-
|
|
33925
|
-
|
|
33926
|
-
|
|
33927
|
-
|
|
33928
|
-
|
|
33929
|
-
|
|
33930
|
-
class FeedDirective {
|
|
33931
|
-
element;
|
|
33932
|
-
_subscription = new Subscription();
|
|
33933
|
-
platform = inject(PlatformService);
|
|
33934
|
-
locationStrategy = inject(LocationStrategy);
|
|
33935
|
-
navController = inject(NavController);
|
|
33936
|
-
router = inject(Router);
|
|
33937
|
-
route = inject(ActivatedRoute);
|
|
33938
|
-
_feedUrl;
|
|
33939
|
-
_baseUrl;
|
|
33940
|
-
set feedUrl(value) {
|
|
33941
|
-
this._feedUrl = value;
|
|
33942
|
-
this._baseUrl = value && JsonFeedUtils.removeJsonExtension(value);
|
|
33973
|
+
`,
|
|
33974
|
+
personFilter: gql$1 `
|
|
33975
|
+
fragment PersonFilterFragment on PersonFilterVO {
|
|
33976
|
+
email
|
|
33977
|
+
pubkey
|
|
33978
|
+
searchText
|
|
33979
|
+
statusIds
|
|
33980
|
+
userProfiles
|
|
33981
|
+
includedIds
|
|
33982
|
+
excludedIds
|
|
33983
|
+
searchAttribute
|
|
33984
|
+
searchAttributes
|
|
33943
33985
|
}
|
|
33944
|
-
|
|
33945
|
-
|
|
33986
|
+
`,
|
|
33987
|
+
};
|
|
33988
|
+
// Load persons query
|
|
33989
|
+
const PersonQueries = {
|
|
33990
|
+
loadAll: gql$1 `
|
|
33991
|
+
query Persons($offset: Int, $size: Int, $sortBy: String, $sortDirection: String, $filter: PersonFilterVOInput) {
|
|
33992
|
+
data: persons(filter: $filter, offset: $offset, size: $size, sortBy: $sortBy, sortDirection: $sortDirection) {
|
|
33993
|
+
...PersonFragment
|
|
33994
|
+
}
|
|
33946
33995
|
}
|
|
33947
|
-
|
|
33948
|
-
|
|
33949
|
-
|
|
33950
|
-
|
|
33996
|
+
${PersonFragments.person}
|
|
33997
|
+
`,
|
|
33998
|
+
loadAllWithTotal: gql$1 `
|
|
33999
|
+
query PersonsWithTotal($offset: Int, $size: Int, $sortBy: String, $sortDirection: String, $filter: PersonFilterVOInput) {
|
|
34000
|
+
data: persons(filter: $filter, offset: $offset, size: $size, sortBy: $sortBy, sortDirection: $sortDirection) {
|
|
34001
|
+
...PersonFragment
|
|
34002
|
+
}
|
|
34003
|
+
total: personsCount(filter: $filter)
|
|
33951
34004
|
}
|
|
33952
|
-
|
|
33953
|
-
|
|
33954
|
-
|
|
33955
|
-
|
|
34005
|
+
${PersonFragments.person}
|
|
34006
|
+
`,
|
|
34007
|
+
};
|
|
34008
|
+
const PersonMutations = {
|
|
34009
|
+
saveAll: gql$1 `
|
|
34010
|
+
mutation savePersons($data: [PersonVOInput]) {
|
|
34011
|
+
data: savePersons(persons: $data) {
|
|
34012
|
+
...PersonFragment
|
|
34013
|
+
}
|
|
33956
34014
|
}
|
|
33957
|
-
|
|
33958
|
-
|
|
34015
|
+
${PersonFragments.person}
|
|
34016
|
+
`,
|
|
34017
|
+
deleteAll: gql$1 `
|
|
34018
|
+
mutation deletePersons($ids: [Int]) {
|
|
34019
|
+
deletePersons(ids: $ids)
|
|
33959
34020
|
}
|
|
33960
|
-
|
|
33961
|
-
|
|
33962
|
-
|
|
33963
|
-
|
|
33964
|
-
|
|
33965
|
-
|
|
33966
|
-
|
|
33967
|
-
|
|
33968
|
-
|
|
33969
|
-
|
|
34021
|
+
`,
|
|
34022
|
+
};
|
|
34023
|
+
class PersonService extends BaseEntityService {
|
|
34024
|
+
graphql;
|
|
34025
|
+
platform;
|
|
34026
|
+
network;
|
|
34027
|
+
entities;
|
|
34028
|
+
constructor(graphql, platform, network, entities) {
|
|
34029
|
+
super(graphql, platform, Person, PersonFilter, {
|
|
34030
|
+
queries: PersonQueries,
|
|
34031
|
+
mutations: PersonMutations,
|
|
34032
|
+
defaultSortBy: 'lastName',
|
|
34033
|
+
defaultSortDirection: 'asc',
|
|
33970
34034
|
});
|
|
33971
|
-
|
|
33972
|
-
|
|
33973
|
-
|
|
34035
|
+
this.graphql = graphql;
|
|
34036
|
+
this.platform = platform;
|
|
34037
|
+
this.network = network;
|
|
34038
|
+
this.entities = entities;
|
|
34039
|
+
// for DEV only -----
|
|
34040
|
+
this._debug = !environment.production;
|
|
33974
34041
|
}
|
|
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);
|
|
34042
|
+
async loadAll(offset, size, sortBy, sortDirection, filter, opts) {
|
|
34043
|
+
const offline = this.network.offline && (!opts || opts.fetchPolicy !== 'network-only');
|
|
34044
|
+
if (offline) {
|
|
34045
|
+
return this.loadAllLocally(offset, size, sortBy, sortDirection, filter, opts);
|
|
34008
34046
|
}
|
|
34009
|
-
|
|
34010
|
-
event.preventDefault();
|
|
34011
|
-
await this.platform.open(href);
|
|
34012
|
-
return true;
|
|
34047
|
+
return super.loadAll(offset, size, sortBy, sortDirection, filter, opts);
|
|
34013
34048
|
}
|
|
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.`);
|
|
34049
|
+
async loadAllLocally(offset, size, sortBy, sortDirection, filter, opts) {
|
|
34050
|
+
filter = this.asFilter(filter);
|
|
34051
|
+
const variables = {
|
|
34052
|
+
offset: offset || 0,
|
|
34053
|
+
size: size || 100,
|
|
34054
|
+
sortBy: sortBy || this.defaultSortBy,
|
|
34055
|
+
sortDirection: sortDirection || this.defaultSortDirection,
|
|
34056
|
+
filter: filter && filter.asFilterFn(),
|
|
34057
|
+
};
|
|
34058
|
+
const { data, total } = await this.entities.loadAll('PersonVO', variables);
|
|
34059
|
+
const entities = this.fromObjects(data, opts);
|
|
34060
|
+
const res = { data: entities, total };
|
|
34061
|
+
// Add fetch more function
|
|
34062
|
+
const nextOffset = (offset || 0) + entities.length;
|
|
34063
|
+
if (nextOffset < total) {
|
|
34064
|
+
res.fetchMore = () => this.loadAllLocally(nextOffset, size, sortBy, sortDirection, filter, opts);
|
|
34032
34065
|
}
|
|
34066
|
+
return res;
|
|
34033
34067
|
}
|
|
34034
|
-
|
|
34035
|
-
|
|
34036
|
-
|
|
34037
|
-
|
|
34038
|
-
|
|
34039
|
-
|
|
34040
|
-
|
|
34041
|
-
|
|
34042
|
-
|
|
34043
|
-
|
|
34044
|
-
|
|
34045
|
-
|
|
34046
|
-
|
|
34047
|
-
|
|
34048
|
-
return this.locationStrategy.prepareExternalUrl(serializedUrl);
|
|
34068
|
+
async suggest(value, filter, sortBy, sortDirection, opts) {
|
|
34069
|
+
if (EntityUtils.isNotEmpty(value, 'id'))
|
|
34070
|
+
return { data: [value] };
|
|
34071
|
+
value = (typeof value === 'string' && value !== '*' && value) || undefined;
|
|
34072
|
+
sortBy = sortBy || filter.searchAttribute || (filter.searchAttributes && filter.searchAttributes[0]);
|
|
34073
|
+
return this.loadAll(0, !value ? 30 : 10, sortBy, sortDirection, {
|
|
34074
|
+
...filter,
|
|
34075
|
+
searchText: value,
|
|
34076
|
+
statusIds: (filter && filter.statusIds) || [StatusIds.ENABLE, StatusIds.TEMPORARY],
|
|
34077
|
+
userProfiles: filter && filter.userProfiles,
|
|
34078
|
+
}, {
|
|
34079
|
+
withTotal: true /* need by autocomplete */,
|
|
34080
|
+
...opts,
|
|
34081
|
+
});
|
|
34049
34082
|
}
|
|
34050
|
-
|
|
34051
|
-
|
|
34052
|
-
|
|
34053
|
-
|
|
34054
|
-
|
|
34055
|
-
|
|
34056
|
-
|
|
34083
|
+
async executeImport(filter, opts) {
|
|
34084
|
+
const maxProgression = (opts && opts.maxProgression) || 100;
|
|
34085
|
+
filter = {
|
|
34086
|
+
...filter,
|
|
34087
|
+
statusIds: [StatusIds.ENABLE, StatusIds.TEMPORARY],
|
|
34088
|
+
userProfiles: ['SUPERVISOR', 'USER', 'GUEST'],
|
|
34089
|
+
};
|
|
34090
|
+
console.info('[person-service] Importing persons...');
|
|
34091
|
+
const res = await JobUtils.fetchAllPages((offset, size) => this.loadAll(offset, size, 'id', null, filter, {
|
|
34092
|
+
debug: false,
|
|
34093
|
+
fetchPolicy: 'network-only',
|
|
34094
|
+
withTotal: offset === 0, // Compute total only once
|
|
34095
|
+
toEntity: false,
|
|
34096
|
+
}), { progression: opts?.progression, maxProgression: maxProgression * 0.9 });
|
|
34097
|
+
// Save result locally
|
|
34098
|
+
await this.entities.saveAll(res.data, { entityName: 'PersonVO', reset: true });
|
|
34057
34099
|
}
|
|
34058
|
-
|
|
34059
|
-
|
|
34100
|
+
async loadById(id, opts) {
|
|
34101
|
+
const { data } = await this.loadAll(0, 1, null, null, { includedIds: [id] }, { withTotal: false, ...opts });
|
|
34102
|
+
const source = isNotEmptyArray(data) ? data[0] : { id };
|
|
34103
|
+
return this.fromObject(source, opts);
|
|
34104
|
+
}
|
|
34105
|
+
async loadByPubkey(pubkey, opts) {
|
|
34106
|
+
const { data } = await this.loadAll(0, 1, null, null, { pubkey }, { withTotal: false, ...opts });
|
|
34107
|
+
const source = isNotEmptyArray(data) ? data[0] : { pubkey };
|
|
34108
|
+
return this.fromObject(source, opts);
|
|
34109
|
+
}
|
|
34110
|
+
/* -- protected methods -- */
|
|
34111
|
+
asObject(source) {
|
|
34112
|
+
if (!source)
|
|
34113
|
+
return undefined;
|
|
34114
|
+
if (!(source instanceof Person)) {
|
|
34115
|
+
source = Person.fromObject(source);
|
|
34116
|
+
}
|
|
34117
|
+
const target = source.asObject();
|
|
34118
|
+
// Not known in server GraphQL schema
|
|
34119
|
+
delete target.mainProfile;
|
|
34120
|
+
target.department = source.department?.asObject();
|
|
34121
|
+
return target;
|
|
34122
|
+
}
|
|
34123
|
+
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 });
|
|
34124
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PersonService, providedIn: 'root' });
|
|
34125
|
+
}
|
|
34126
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PersonService, decorators: [{
|
|
34127
|
+
type: Injectable,
|
|
34128
|
+
args: [{ providedIn: 'root' }]
|
|
34129
|
+
}], ctorParameters: () => [{ type: GraphqlService }, { type: PlatformService }, { type: NetworkService }, { type: EntitiesStorage }] });
|
|
34130
|
+
|
|
34131
|
+
class MessageForm extends AppForm {
|
|
34132
|
+
formBuilder;
|
|
34133
|
+
cd;
|
|
34134
|
+
mobile;
|
|
34135
|
+
suggestFn;
|
|
34136
|
+
subjectMinLength = 5;
|
|
34137
|
+
subjectMaxLength = 255;
|
|
34138
|
+
bodyMaxLength = 2000;
|
|
34139
|
+
bodyAutoHeight = true;
|
|
34140
|
+
canSelectType = false;
|
|
34141
|
+
canRecipientFilter = false;
|
|
34142
|
+
recipientFilterCount = 0;
|
|
34143
|
+
types = MessageTypeList;
|
|
34144
|
+
constructor(injector, formBuilder, cd) {
|
|
34145
|
+
super(injector);
|
|
34146
|
+
this.formBuilder = formBuilder;
|
|
34147
|
+
this.cd = cd;
|
|
34148
|
+
this.mobile = this.settings.mobile;
|
|
34149
|
+
}
|
|
34150
|
+
ngOnInit() {
|
|
34151
|
+
this.setForm(this.formBuilder.group({
|
|
34152
|
+
type: [MessageTypes.INBOX_MESSAGE, Validators.required],
|
|
34153
|
+
recipients: [null, Validators.required],
|
|
34154
|
+
recipientFilter: [null],
|
|
34155
|
+
subject: [
|
|
34156
|
+
null,
|
|
34157
|
+
this.subjectMaxLength
|
|
34158
|
+
? Validators.compose([Validators.required, Validators.minLength(this.subjectMinLength), Validators.maxLength(this.subjectMaxLength)])
|
|
34159
|
+
: Validators.required,
|
|
34160
|
+
],
|
|
34161
|
+
body: [null, this.bodyMaxLength ? Validators.compose([Validators.maxLength(this.bodyMaxLength)]) : Validators.required],
|
|
34162
|
+
}));
|
|
34163
|
+
this.registerSubscription(this._form
|
|
34164
|
+
.get('type')
|
|
34165
|
+
.valueChanges.pipe(filter(isNotNil))
|
|
34166
|
+
.subscribe((type) => this.updateFormGroup(this._form, { type })));
|
|
34167
|
+
// Person combo
|
|
34168
|
+
const personAttributes = this.settings.getFieldDisplayAttributes('person', ['lastName', 'firstName', 'department.name']);
|
|
34169
|
+
this.registerAutocompleteField('recipients', {
|
|
34170
|
+
showAllOnFocus: false,
|
|
34171
|
+
suggestFn: this.suggestFn,
|
|
34172
|
+
filter: {
|
|
34173
|
+
statusIds: [StatusIds.TEMPORARY, StatusIds.ENABLE],
|
|
34174
|
+
},
|
|
34175
|
+
attributes: personAttributes,
|
|
34176
|
+
columnNames: personAttributes.map((attr) => `USER.${changeCaseToUnderscore(attr).toUpperCase()}`),
|
|
34177
|
+
displayWith: PersonUtils.personToString,
|
|
34178
|
+
multiple: true,
|
|
34179
|
+
mobile: this.mobile,
|
|
34180
|
+
});
|
|
34181
|
+
}
|
|
34182
|
+
isSamePerson(o1, o2) {
|
|
34183
|
+
return EntityUtils.equals(o1, o2, 'id');
|
|
34184
|
+
}
|
|
34185
|
+
updateFormGroup(formGroup, opts) {
|
|
34186
|
+
console.debug('[message-form] Updating form group...', opts);
|
|
34187
|
+
// Recipient validator
|
|
34188
|
+
const recipientsRequired = toBoolean(opts?.recipientRequired, opts?.type !== MessageTypes.FEED);
|
|
34189
|
+
{
|
|
34190
|
+
const control = formGroup.get('recipients');
|
|
34191
|
+
if (recipientsRequired) {
|
|
34192
|
+
if (!control.hasValidator(Validators.required)) {
|
|
34193
|
+
control.addValidators(Validators.required);
|
|
34194
|
+
}
|
|
34195
|
+
control.enable();
|
|
34196
|
+
}
|
|
34197
|
+
else {
|
|
34198
|
+
if (control.hasValidator(Validators.required)) {
|
|
34199
|
+
control.removeValidators(Validators.required);
|
|
34200
|
+
}
|
|
34201
|
+
control.disable();
|
|
34202
|
+
}
|
|
34203
|
+
}
|
|
34204
|
+
// Recipient filter validator
|
|
34205
|
+
const recipientFilterRequired = this.canRecipientFilter && !recipientsRequired;
|
|
34206
|
+
{
|
|
34207
|
+
const control = formGroup.get('recipientFilter');
|
|
34208
|
+
if (recipientFilterRequired) {
|
|
34209
|
+
if (!control.hasValidator(Validators.required)) {
|
|
34210
|
+
control.addValidators(Validators.required);
|
|
34211
|
+
}
|
|
34212
|
+
control.enable();
|
|
34213
|
+
}
|
|
34214
|
+
else {
|
|
34215
|
+
if (control.hasValidator(Validators.required)) {
|
|
34216
|
+
control.removeValidators(Validators.required);
|
|
34217
|
+
}
|
|
34218
|
+
control.disable();
|
|
34219
|
+
}
|
|
34220
|
+
}
|
|
34221
|
+
formGroup.updateValueAndValidity();
|
|
34222
|
+
}
|
|
34223
|
+
markForCheck() {
|
|
34224
|
+
this.cd.markForCheck();
|
|
34225
|
+
}
|
|
34226
|
+
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 });
|
|
34227
|
+
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 });
|
|
34228
|
+
}
|
|
34229
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageForm, decorators: [{
|
|
34230
|
+
type: Component,
|
|
34231
|
+
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"] }]
|
|
34232
|
+
}], ctorParameters: () => [{ type: i0.Injector }, { type: i1$3.UntypedFormBuilder }, { type: i0.ChangeDetectorRef }], propDecorators: { mobile: [{
|
|
34233
|
+
type: Input
|
|
34234
|
+
}], suggestFn: [{
|
|
34235
|
+
type: Input
|
|
34236
|
+
}], subjectMinLength: [{
|
|
34237
|
+
type: Input
|
|
34238
|
+
}], subjectMaxLength: [{
|
|
34239
|
+
type: Input
|
|
34240
|
+
}], bodyMaxLength: [{
|
|
34241
|
+
type: Input
|
|
34242
|
+
}], bodyAutoHeight: [{
|
|
34243
|
+
type: Input
|
|
34244
|
+
}], canSelectType: [{
|
|
34245
|
+
type: Input
|
|
34246
|
+
}], canRecipientFilter: [{
|
|
34247
|
+
type: Input
|
|
34248
|
+
}], recipientFilterCount: [{
|
|
34249
|
+
type: Input
|
|
34250
|
+
}] } });
|
|
34251
|
+
|
|
34252
|
+
class MessageModal {
|
|
34253
|
+
settings;
|
|
34254
|
+
viewCtrl;
|
|
34255
|
+
accountService;
|
|
34256
|
+
cd;
|
|
34257
|
+
mobile;
|
|
34258
|
+
debug = false;
|
|
34259
|
+
title = 'SOCIAL.MESSAGE.NEW';
|
|
34260
|
+
suggestFn;
|
|
34261
|
+
data;
|
|
34262
|
+
canSelectType;
|
|
34263
|
+
canRecipientFilter;
|
|
34264
|
+
recipientFilterCount;
|
|
34265
|
+
form;
|
|
34266
|
+
constructor(settings, viewCtrl, accountService, cd) {
|
|
34267
|
+
this.settings = settings;
|
|
34268
|
+
this.viewCtrl = viewCtrl;
|
|
34269
|
+
this.accountService = accountService;
|
|
34270
|
+
this.cd = cd;
|
|
34271
|
+
this.mobile = this.settings.mobile;
|
|
34272
|
+
}
|
|
34273
|
+
ngOnInit() {
|
|
34274
|
+
this.canSelectType = toBoolean(this.canSelectType, this.accountService.isAdmin());
|
|
34275
|
+
}
|
|
34276
|
+
ngAfterViewInit() {
|
|
34277
|
+
setTimeout(() => {
|
|
34278
|
+
this.form.markAsReady({ emitEvent: false });
|
|
34279
|
+
if (this.data) {
|
|
34280
|
+
this.data.type = this.data.type || MessageTypes.INBOX_MESSAGE;
|
|
34281
|
+
this.form.setValue(this.data);
|
|
34282
|
+
}
|
|
34283
|
+
this.form.markAsLoaded();
|
|
34284
|
+
this.form.enable();
|
|
34285
|
+
});
|
|
34286
|
+
}
|
|
34287
|
+
cancel() {
|
|
34288
|
+
this.viewCtrl.dismiss();
|
|
34289
|
+
}
|
|
34290
|
+
async doSubmit() {
|
|
34291
|
+
if (this.form.disabled)
|
|
34292
|
+
return;
|
|
34293
|
+
if (!this.form.valid) {
|
|
34294
|
+
await AppFormUtils.waitWhilePending(this.form);
|
|
34295
|
+
if (this.form.invalid) {
|
|
34296
|
+
AppFormUtils.logFormErrors(this.form.form, '[message-modal] ');
|
|
34297
|
+
this.form.markAllAsTouched();
|
|
34298
|
+
return;
|
|
34299
|
+
}
|
|
34300
|
+
}
|
|
34301
|
+
this.markAsLoading();
|
|
34302
|
+
try {
|
|
34303
|
+
const data = this.form.value;
|
|
34304
|
+
// Disable the form
|
|
34305
|
+
this.form.disable();
|
|
34306
|
+
const entity = Message.fromObject(data);
|
|
34307
|
+
return this.viewCtrl.dismiss(entity);
|
|
34308
|
+
}
|
|
34309
|
+
catch (err) {
|
|
34310
|
+
this.form.error = (err && err.message) || err;
|
|
34311
|
+
this.markAsLoaded();
|
|
34312
|
+
// Enable the form
|
|
34313
|
+
this.form.enable();
|
|
34314
|
+
// Reset form error on next changes
|
|
34315
|
+
firstNotNilPromise(this.form.form.valueChanges).then(() => {
|
|
34316
|
+
this.form.error = null;
|
|
34317
|
+
this.markForCheck();
|
|
34318
|
+
});
|
|
34319
|
+
return;
|
|
34320
|
+
}
|
|
34321
|
+
}
|
|
34322
|
+
markForCheck() {
|
|
34323
|
+
this.cd.markForCheck();
|
|
34324
|
+
}
|
|
34325
|
+
markAsLoading(opts) {
|
|
34326
|
+
this.form.markAsLoading(opts);
|
|
34327
|
+
}
|
|
34328
|
+
markAsLoaded(opts) {
|
|
34329
|
+
this.form.markAsLoaded(opts);
|
|
34330
|
+
}
|
|
34331
|
+
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 });
|
|
34332
|
+
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 });
|
|
34333
|
+
}
|
|
34334
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageModal, decorators: [{
|
|
34335
|
+
type: Component,
|
|
34336
|
+
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" }]
|
|
34337
|
+
}], ctorParameters: () => [{ type: LocalSettingsService }, { type: i2$1.ModalController }, { type: AccountService }, { type: i0.ChangeDetectorRef }], propDecorators: { debug: [{
|
|
34338
|
+
type: Input
|
|
34339
|
+
}], title: [{
|
|
34340
|
+
type: Input
|
|
34341
|
+
}], suggestFn: [{
|
|
34342
|
+
type: Input
|
|
34343
|
+
}], data: [{
|
|
34344
|
+
type: Input
|
|
34345
|
+
}], canSelectType: [{
|
|
34346
|
+
type: Input
|
|
34347
|
+
}], canRecipientFilter: [{
|
|
34348
|
+
type: Input
|
|
34349
|
+
}], recipientFilterCount: [{
|
|
34350
|
+
type: Input
|
|
34351
|
+
}], form: [{
|
|
34352
|
+
type: ViewChild,
|
|
34353
|
+
args: ['form', { static: true }]
|
|
34354
|
+
}] } });
|
|
34355
|
+
|
|
34356
|
+
const MessageFragments = {
|
|
34357
|
+
message: gql$1 `
|
|
34358
|
+
fragment MessageFragment on MessageVO {
|
|
34359
|
+
id
|
|
34360
|
+
subject
|
|
34361
|
+
body
|
|
34362
|
+
type
|
|
34363
|
+
recipient {
|
|
34364
|
+
...LightPersonFragment
|
|
34365
|
+
}
|
|
34366
|
+
recipients {
|
|
34367
|
+
...LightPersonFragment
|
|
34368
|
+
}
|
|
34369
|
+
recipientFilter {
|
|
34370
|
+
...PersonFilterFragment
|
|
34371
|
+
}
|
|
34372
|
+
issuer {
|
|
34373
|
+
...LightPersonFragment
|
|
34374
|
+
}
|
|
34375
|
+
}
|
|
34376
|
+
`,
|
|
34377
|
+
};
|
|
34378
|
+
const Queries = {
|
|
34379
|
+
load: gql$1 `
|
|
34380
|
+
mutation LoadMessage($id: Int!) {
|
|
34381
|
+
data: message(id: $id) {
|
|
34382
|
+
...MessageFragment
|
|
34383
|
+
}
|
|
34384
|
+
}
|
|
34385
|
+
${MessageFragments.message}
|
|
34386
|
+
${PersonFragments.lightPerson}
|
|
34387
|
+
${PersonFragments.personFilter}
|
|
34388
|
+
`,
|
|
34389
|
+
};
|
|
34390
|
+
const Mutations = {
|
|
34391
|
+
send: gql$1 `
|
|
34392
|
+
mutation SendMessage($data: MessageVOInput) {
|
|
34393
|
+
done: sendMessage(message: $data)
|
|
34394
|
+
}
|
|
34395
|
+
`,
|
|
34396
|
+
delete: gql$1 `
|
|
34397
|
+
mutation DeleteMessage($id: Int!) {
|
|
34398
|
+
done: deleteMessage(id: $id)
|
|
34399
|
+
}
|
|
34400
|
+
`,
|
|
34401
|
+
};
|
|
34402
|
+
class MessageService extends BaseGraphqlService {
|
|
34403
|
+
graphql;
|
|
34404
|
+
translate;
|
|
34405
|
+
modalCtrl;
|
|
34406
|
+
toastController;
|
|
34407
|
+
environment;
|
|
34408
|
+
constructor(graphql, translate, modalCtrl, toastController, environment) {
|
|
34409
|
+
super(graphql, environment);
|
|
34410
|
+
this.graphql = graphql;
|
|
34411
|
+
this.translate = translate;
|
|
34412
|
+
this.modalCtrl = modalCtrl;
|
|
34413
|
+
this.toastController = toastController;
|
|
34414
|
+
this.environment = environment;
|
|
34415
|
+
// For DEV only
|
|
34416
|
+
this._debug = !environment.production;
|
|
34417
|
+
}
|
|
34418
|
+
/**
|
|
34419
|
+
* Send a message to recipient(s)
|
|
34420
|
+
*
|
|
34421
|
+
* @param entity
|
|
34422
|
+
* @param opts
|
|
34423
|
+
*/
|
|
34424
|
+
async send(entity, opts) {
|
|
34425
|
+
// Transform into json
|
|
34426
|
+
const data = entity.asObject(MINIFY_ENTITY_FOR_POD);
|
|
34427
|
+
const now = Date.now();
|
|
34428
|
+
if (this._debug)
|
|
34429
|
+
console.debug(`[message-service] Sending message...`);
|
|
34430
|
+
try {
|
|
34431
|
+
const { done } = await this.graphql.mutate({
|
|
34432
|
+
mutation: Mutations.send,
|
|
34433
|
+
variables: { data },
|
|
34434
|
+
error: { code: SocialErrorCodes.SEND_MESSAGE_ERROR, message: 'SOCIAL.ERROR.SEND_MESSAGE_ERROR' },
|
|
34435
|
+
});
|
|
34436
|
+
if (this._debug)
|
|
34437
|
+
console.debug(`[message-service] Send message [OK] in ${Date.now() - now}ms`);
|
|
34438
|
+
if (done && (!opts || opts.showToast !== false)) {
|
|
34439
|
+
await this.showToast({ type: 'info', message: 'SOCIAL.INFO.MESSAGE_SENT' });
|
|
34440
|
+
}
|
|
34441
|
+
return done;
|
|
34442
|
+
}
|
|
34443
|
+
catch (err) {
|
|
34444
|
+
const error = (err && err.message) || err;
|
|
34445
|
+
console.error(error);
|
|
34446
|
+
// Show error
|
|
34447
|
+
if (!opts || opts.showToast !== false) {
|
|
34448
|
+
const message = error || 'SOCIAL.ERROR.SEND_MESSAGE_ERROR';
|
|
34449
|
+
await this.showToast({ type: 'error', message });
|
|
34450
|
+
}
|
|
34451
|
+
return false;
|
|
34452
|
+
}
|
|
34453
|
+
}
|
|
34454
|
+
/**
|
|
34455
|
+
* Delete a message by ID
|
|
34456
|
+
*
|
|
34457
|
+
* @param id
|
|
34458
|
+
* @param opts
|
|
34459
|
+
*/
|
|
34460
|
+
async delete(id, opts) {
|
|
34461
|
+
const now = Date.now();
|
|
34462
|
+
if (this._debug)
|
|
34463
|
+
console.debug(`[message-service] Deleting message #${id}...`);
|
|
34464
|
+
try {
|
|
34465
|
+
const { done } = await this.graphql.mutate({
|
|
34466
|
+
mutation: Mutations.delete,
|
|
34467
|
+
variables: { id },
|
|
34468
|
+
error: { code: SocialErrorCodes.DELETE_MESSAGE_ERROR, message: 'SOCIAL.ERROR.DELETE_MESSAGE_ERROR' },
|
|
34469
|
+
});
|
|
34470
|
+
if (this._debug)
|
|
34471
|
+
console.debug(`[message-service] Delete message [OK] in ${Date.now() - now}ms`);
|
|
34472
|
+
if (done && (!opts || opts.showToast !== false)) {
|
|
34473
|
+
await this.showToast({ type: 'info', message: 'SOCIAL.INFO.MESSAGE_DELETED' });
|
|
34474
|
+
}
|
|
34475
|
+
return done;
|
|
34476
|
+
}
|
|
34477
|
+
catch (err) {
|
|
34478
|
+
const error = (err && err.message) || err;
|
|
34479
|
+
console.error(error);
|
|
34480
|
+
// Show error
|
|
34481
|
+
if (!opts || opts.showToast !== false) {
|
|
34482
|
+
const message = error || 'SOCIAL.ERROR.DELETE_MESSAGE_ERROR';
|
|
34483
|
+
await this.showToast({ type: 'error', message });
|
|
34484
|
+
}
|
|
34485
|
+
return false;
|
|
34486
|
+
}
|
|
34487
|
+
}
|
|
34488
|
+
async openComposeModal(options) {
|
|
34489
|
+
const hasTopModal = !!(await this.modalCtrl.getTop());
|
|
34490
|
+
const modal = await this.modalCtrl.create({
|
|
34491
|
+
component: MessageModal,
|
|
34492
|
+
componentProps: options,
|
|
34493
|
+
cssClass: hasTopModal && 'stack-modal',
|
|
34494
|
+
});
|
|
34495
|
+
// Open the modal
|
|
34496
|
+
await modal.present();
|
|
34497
|
+
// On dismiss
|
|
34498
|
+
const { data } = await modal.onDidDismiss();
|
|
34499
|
+
if (!data || !(data instanceof Message))
|
|
34500
|
+
return true; // CANCELLED
|
|
34501
|
+
// Send message
|
|
34502
|
+
return await this.send(data, { showToast: options?.showToast });
|
|
34503
|
+
}
|
|
34504
|
+
async load(id, opts) {
|
|
34505
|
+
const { data } = await this.graphql.query({
|
|
34506
|
+
query: Queries.load,
|
|
34507
|
+
variables: {
|
|
34508
|
+
id,
|
|
34509
|
+
},
|
|
34510
|
+
fetchPolicy: 'no-cache',
|
|
34511
|
+
});
|
|
34512
|
+
const entity = opts?.toEntity !== false ? Message.fromObject(data) : data;
|
|
34513
|
+
console.debug(`[message-service] Loaded message #${id}:`, entity);
|
|
34514
|
+
return entity;
|
|
34515
|
+
}
|
|
34516
|
+
/* -- protected methods -- */
|
|
34517
|
+
async showToast(opts) {
|
|
34518
|
+
return Toasts.show(this.toastController, this.translate, {
|
|
34519
|
+
type: 'info',
|
|
34520
|
+
message: 'SOCIAL.INFO.MESSAGE_SENT',
|
|
34521
|
+
...opts,
|
|
34522
|
+
});
|
|
34523
|
+
}
|
|
34524
|
+
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 });
|
|
34525
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageService, providedIn: 'root' });
|
|
34526
|
+
}
|
|
34527
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageService, decorators: [{
|
|
34528
|
+
type: Injectable,
|
|
34529
|
+
args: [{ providedIn: 'root' }]
|
|
34530
|
+
}], ctorParameters: () => [{ type: GraphqlService }, { type: i1$1.TranslateService }, { type: i2$1.ModalController }, { type: i2$1.ToastController }, { type: Environment, decorators: [{
|
|
34531
|
+
type: Optional
|
|
34532
|
+
}, {
|
|
34533
|
+
type: Inject,
|
|
34534
|
+
args: [ENVIRONMENT]
|
|
34535
|
+
}] }] });
|
|
34536
|
+
|
|
34537
|
+
const APP_FEED_SERVICE = new InjectionToken('FeeService');
|
|
34538
|
+
class FeedService extends StartableService {
|
|
34539
|
+
settings;
|
|
34540
|
+
environment;
|
|
34541
|
+
_logPrefix = '[feed-service] ';
|
|
34542
|
+
_state = new RxState();
|
|
34543
|
+
network = inject(NetworkService);
|
|
34544
|
+
modalCtrl = inject(ModalController);
|
|
34545
|
+
messageService = inject(MessageService);
|
|
34546
|
+
personService = inject(PersonService);
|
|
34547
|
+
locale$ = this._state.select('locale');
|
|
34548
|
+
feedUrls$ = this._state.select('feedUrls');
|
|
34549
|
+
get feedUrls() {
|
|
34550
|
+
return this._state.get('feedUrls');
|
|
34551
|
+
}
|
|
34552
|
+
set feedUrls(urls) {
|
|
34553
|
+
this._state.set('feedUrls', () => urls);
|
|
34554
|
+
}
|
|
34555
|
+
constructor(settings, environment) {
|
|
34556
|
+
super(settings);
|
|
34557
|
+
this.settings = settings;
|
|
34558
|
+
this.environment = environment;
|
|
34559
|
+
this._state.connect('locale', this.settings.locale$);
|
|
34560
|
+
this._state.connect('feedUrls', this.locale$.pipe(filter(isNotNilOrBlank), map((locale) => (locale && environment.feed?.jsonFeed?.[locale]) ?? [])));
|
|
34561
|
+
// DEBUG
|
|
34562
|
+
this._debug = !environment.production;
|
|
34563
|
+
if (this._debug)
|
|
34564
|
+
console.debug(`${this._logPrefix}created`);
|
|
34565
|
+
}
|
|
34566
|
+
async ngOnStart() {
|
|
34567
|
+
await Promise.all([this.settings.ready(), this.network.ready()]);
|
|
34568
|
+
return {
|
|
34569
|
+
locale: this.settings.locale,
|
|
34570
|
+
};
|
|
34571
|
+
}
|
|
34572
|
+
watchAll(opts) {
|
|
34573
|
+
if (!this.started) {
|
|
34574
|
+
return from(this.start()).pipe(switchMap$1(() => this.watchAll(opts)));
|
|
34575
|
+
}
|
|
34576
|
+
if (isNotEmptyArray(opts?.urls)) {
|
|
34577
|
+
const locale$ = opts.locale ? of(opts.locale) : this.locale$;
|
|
34578
|
+
return locale$.pipe(mergeMap((locale) => this.loadAll({ locale, ...opts })));
|
|
34579
|
+
}
|
|
34580
|
+
return this.feedUrls$.pipe(filter(isNotEmptyArray), mergeMap((urls) => this.loadAll({ ...opts, urls })));
|
|
34581
|
+
}
|
|
34582
|
+
getHomeUrl(feed) {
|
|
34583
|
+
const feedUrl = feed?.feed_url ?? firstArrayValue(this.feedUrls);
|
|
34584
|
+
return feedUrl ? UrlUtils.getRootUrl(feedUrl) : undefined;
|
|
34585
|
+
}
|
|
34586
|
+
getTagUrl(feed, tag) {
|
|
34587
|
+
if (!feed || !tag)
|
|
34588
|
+
throw new Error("Missing 'feed' or 'tag' argument");
|
|
34589
|
+
const baseUrl = this.getHomeUrl(feed);
|
|
34590
|
+
return feed.tag_template?.replace('{tag}', tag) ?? baseUrl + '/tag/' + tag;
|
|
34591
|
+
}
|
|
34592
|
+
async load(url, opts) {
|
|
34593
|
+
if (!url)
|
|
34594
|
+
throw new Error('Missing url argument');
|
|
34595
|
+
const { data } = await this.loadAll({ ...opts, urls: [url] });
|
|
34596
|
+
return firstArrayValue(data);
|
|
34597
|
+
}
|
|
34598
|
+
async loadAll(opts) {
|
|
34599
|
+
await this.ready();
|
|
34600
|
+
const urls = opts?.urls ?? this.feedUrls;
|
|
34601
|
+
opts = {
|
|
34602
|
+
maxAgeInMonths: opts?.maxAgeInMonths ?? this.environment.feed?.maxAgeInMonths,
|
|
34603
|
+
maxContentLength: opts?.maxContentLength ?? this.environment.feed?.maxContentLength,
|
|
34604
|
+
locale: this.settings.locale,
|
|
34605
|
+
depth: 0,
|
|
34606
|
+
...opts,
|
|
34607
|
+
};
|
|
34608
|
+
const feeds = await Promise.all((urls || []).map(async (url) => {
|
|
34609
|
+
try {
|
|
34610
|
+
// Get JSON
|
|
34611
|
+
const json = await this.network.get(url, { nocache: opts?.cache });
|
|
34612
|
+
console.debug(`${this._logPrefix}Loaded JSON from ${url}`, json);
|
|
34613
|
+
// Resolve feed
|
|
34614
|
+
return await this.resolveFeed(json, url, opts);
|
|
34615
|
+
}
|
|
34616
|
+
catch (err) {
|
|
34617
|
+
if (err?.status === 404) {
|
|
34618
|
+
console.error(`${this._logPrefix} Error while fetching feeds at ${url}. 404 (Not found)`);
|
|
34619
|
+
}
|
|
34620
|
+
else {
|
|
34621
|
+
console.error(`${this._logPrefix} Error while fetching feeds at ${url}`, err);
|
|
34622
|
+
}
|
|
34623
|
+
return Promise.resolve([]);
|
|
34624
|
+
}
|
|
34625
|
+
}));
|
|
34626
|
+
const data = feeds.filter(isNotEmptyArray).flat();
|
|
34627
|
+
// Close opened resources
|
|
34628
|
+
if (opts.depth === 0) {
|
|
34629
|
+
this.onAfterLoadAll();
|
|
34630
|
+
}
|
|
34631
|
+
return { data, total: data.length };
|
|
34632
|
+
}
|
|
34633
|
+
async openEditModal(event, feedItem, opts) {
|
|
34634
|
+
const messageId = +feedItem.id;
|
|
34635
|
+
if (isNilOrNaN(messageId))
|
|
34636
|
+
throw new Error(`Cannot load message - invalid id: ${feedItem.id}`);
|
|
34637
|
+
// Fetch the original message
|
|
34638
|
+
const message = await this.messageService.load(messageId);
|
|
34639
|
+
// Count recipients
|
|
34640
|
+
let recipientFilterCount;
|
|
34641
|
+
if (message?.recipientFilter) {
|
|
34642
|
+
recipientFilterCount = await this.personService.countAll(message?.recipientFilter);
|
|
34643
|
+
}
|
|
34644
|
+
const hasTopModal = !!(await this.modalCtrl.getTop());
|
|
34645
|
+
const modal = await this.modalCtrl.create({
|
|
34646
|
+
component: MessageModal,
|
|
34647
|
+
componentProps: {
|
|
34648
|
+
title: 'SOCIAL.FEED.EDIT_TITLE',
|
|
34649
|
+
suggestFn: (value, filter, sortBy, sortDirection, opts) => this.personService.suggest(value, filter, sortBy, sortDirection, opts),
|
|
34650
|
+
canSelectType: false,
|
|
34651
|
+
canRecipientFilter: false,
|
|
34652
|
+
recipientFilterCount,
|
|
34653
|
+
data: message,
|
|
34654
|
+
debug: true,
|
|
34655
|
+
},
|
|
34656
|
+
cssClass: hasTopModal && 'stack-modal',
|
|
34657
|
+
});
|
|
34658
|
+
// Open the modal
|
|
34659
|
+
await modal.present();
|
|
34660
|
+
// On dismiss
|
|
34661
|
+
const { data } = await modal.onDidDismiss();
|
|
34662
|
+
if (!data || !(data instanceof Message))
|
|
34663
|
+
return true; // CANCELLED
|
|
34664
|
+
return await this.messageService.send(data, opts);
|
|
34665
|
+
}
|
|
34666
|
+
async deleteItem(feedItem) {
|
|
34667
|
+
const messageId = +feedItem.id;
|
|
34668
|
+
if (isNilOrNaN(messageId))
|
|
34669
|
+
throw new Error(`Cannot delete message - invalid id: ${feedItem.id}`);
|
|
34670
|
+
return await this.messageService.delete(messageId);
|
|
34671
|
+
}
|
|
34672
|
+
/* -- protected functions -- */
|
|
34673
|
+
async resolveFeed(json, url, opts) {
|
|
34674
|
+
// Check json is JsonFeed compatible
|
|
34675
|
+
if (JsonFeedUtils.isJsonFeed(json)) {
|
|
34676
|
+
// Migrate from old versions
|
|
34677
|
+
let feed = JsonFeedUtils.migrateFeed(json);
|
|
34678
|
+
// Resolve items (e.g. using feed.next_url)
|
|
34679
|
+
feed = await this.resolveFeedItems(feed, { ...opts, depth: opts?.depth ?? 0 });
|
|
34680
|
+
// Truncate html
|
|
34681
|
+
JsonFeedUtils.truncateFeedItemsHtml(feed, opts);
|
|
34682
|
+
return isNotEmptyArray(feed?.items) ? [feed] : [];
|
|
34683
|
+
}
|
|
34684
|
+
// not resolved (unknown format)
|
|
34685
|
+
return [];
|
|
34686
|
+
}
|
|
34687
|
+
async resolveFeedItems(json, opts) {
|
|
34688
|
+
if (!json)
|
|
34689
|
+
return json;
|
|
34690
|
+
// Load items
|
|
34691
|
+
if (isEmptyArray(json.items) && isNotNilOrBlank(json.next_url)) {
|
|
34692
|
+
const depth = opts?.depth ?? 0;
|
|
34693
|
+
if (depth >= 3) {
|
|
34694
|
+
console.warn(`${this._logPrefix}Max depth reached (${depth}) for ${json.feed_url}`);
|
|
34695
|
+
return json;
|
|
34696
|
+
}
|
|
34697
|
+
// Load items
|
|
34698
|
+
const feed = await this.load(json.next_url, { ...opts, depth: depth + 1 });
|
|
34699
|
+
if (isEmptyArray(feed?.items)) {
|
|
34700
|
+
return json;
|
|
34701
|
+
}
|
|
34702
|
+
// Merge with the input json
|
|
34703
|
+
return {
|
|
34704
|
+
...json,
|
|
34705
|
+
title: json.title ?? feed.title,
|
|
34706
|
+
home_page_url: json.home_page_url ?? feed.home_page_url,
|
|
34707
|
+
authors: isNotEmptyArray(json.authors) ? json.authors : feed.authors,
|
|
34708
|
+
items: feed.items,
|
|
34709
|
+
tag_template: json.tag_template ?? feed.tag_template,
|
|
34710
|
+
};
|
|
34711
|
+
}
|
|
34712
|
+
return json;
|
|
34713
|
+
}
|
|
34714
|
+
onAfterLoadAll() {
|
|
34715
|
+
console.debug(`${this._logPrefix}Feeds loaded`);
|
|
34716
|
+
}
|
|
34717
|
+
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 });
|
|
34718
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedService });
|
|
34719
|
+
}
|
|
34720
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedService, decorators: [{
|
|
34721
|
+
type: Injectable
|
|
34722
|
+
}], ctorParameters: () => [{ type: LocalSettingsService }, { type: Environment, decorators: [{
|
|
34723
|
+
type: Optional
|
|
34724
|
+
}, {
|
|
34725
|
+
type: Inject,
|
|
34726
|
+
args: [ENVIRONMENT]
|
|
34727
|
+
}] }] });
|
|
34728
|
+
|
|
34729
|
+
class FeedDirective {
|
|
34730
|
+
element;
|
|
34731
|
+
_subscription = new Subscription();
|
|
34732
|
+
platform = inject(PlatformService);
|
|
34733
|
+
locationStrategy = inject(LocationStrategy);
|
|
34734
|
+
navController = inject(NavController);
|
|
34735
|
+
router = inject(Router);
|
|
34736
|
+
route = inject(ActivatedRoute);
|
|
34737
|
+
_feedUrl;
|
|
34738
|
+
_baseUrl;
|
|
34739
|
+
set feedUrl(value) {
|
|
34740
|
+
this._feedUrl = value;
|
|
34741
|
+
this._baseUrl = value && JsonFeedUtils.removeJsonExtension(value);
|
|
34742
|
+
}
|
|
34743
|
+
get feedUrl() {
|
|
34744
|
+
return this._feedUrl;
|
|
34745
|
+
}
|
|
34746
|
+
constructor(element) {
|
|
34747
|
+
this.element = element;
|
|
34748
|
+
// DEBUG
|
|
34749
|
+
//console.debug('[feed-directive] Creating feed directive');
|
|
34750
|
+
}
|
|
34751
|
+
ngAfterViewInit() {
|
|
34752
|
+
console.debug('[feed-directive] Processing anchors');
|
|
34753
|
+
// Listening click events
|
|
34754
|
+
this._subscription.add(this.listenClickEvents(this.element.nativeElement));
|
|
34755
|
+
}
|
|
34756
|
+
ngOnDestroy() {
|
|
34757
|
+
this._subscription.unsubscribe();
|
|
34758
|
+
}
|
|
34759
|
+
listenClickEvents(nativeElement) {
|
|
34760
|
+
console.debug('[feed-directive] Start listening click events...');
|
|
34761
|
+
const subscription = new Subscription();
|
|
34762
|
+
const listener = (event) => this.click(event, nativeElement);
|
|
34763
|
+
const links = nativeElement.querySelectorAll('a');
|
|
34764
|
+
links.forEach((link) => {
|
|
34765
|
+
// DEBUG
|
|
34766
|
+
//console.debug('[feed-directive] Adding click listener to', link);
|
|
34767
|
+
link.addEventListener('click', listener);
|
|
34768
|
+
subscription.add(() => link.removeEventListener('click', listener));
|
|
34769
|
+
});
|
|
34770
|
+
// DEBUG
|
|
34771
|
+
subscription.add(() => console.debug('[feed-directive] Stop listening click events'));
|
|
34772
|
+
return subscription;
|
|
34773
|
+
}
|
|
34774
|
+
async click(event, containerElement) {
|
|
34775
|
+
console.debug('[feed-directive] Processing click event...', event);
|
|
34776
|
+
if (!(event.target instanceof HTMLAnchorElement) && !(event.target?.['tagName'] === 'A')) {
|
|
34777
|
+
console.warn('[feed-directive] Invalid click event target. Should an anchor <a>', event.target);
|
|
34778
|
+
return;
|
|
34779
|
+
}
|
|
34780
|
+
const element = event.target;
|
|
34781
|
+
let href = element.getAttribute('href');
|
|
34782
|
+
const fragment = UrlUtils.getFragment(href);
|
|
34783
|
+
// Fragment
|
|
34784
|
+
if (href?.startsWith('#') && isNotNilOrBlank(fragment)) {
|
|
34785
|
+
event.preventDefault();
|
|
34786
|
+
this.scrollToAnchor(containerElement, fragment);
|
|
34787
|
+
return true;
|
|
34788
|
+
}
|
|
34789
|
+
const routerLink = element.getAttribute('routerLink');
|
|
34790
|
+
if (isNotNilOrBlank(routerLink)) {
|
|
34791
|
+
event.preventDefault();
|
|
34792
|
+
return this.navController.navigateForward(routerLink);
|
|
34793
|
+
}
|
|
34794
|
+
// Resolve relative URL, if baseUrl has been set
|
|
34795
|
+
if (this._baseUrl && UrlUtils.isRelativeUrl(href)) {
|
|
34796
|
+
// Resolve URL, then open using the platform
|
|
34797
|
+
href = UrlUtils.resolveRelativeUrl(this._baseUrl, href);
|
|
34798
|
+
}
|
|
34799
|
+
// Resolve internal URL as an app route
|
|
34800
|
+
if (UrlUtils.isInternalUrl(href)) {
|
|
34801
|
+
const routePath = this.normalizeUrl(href.startsWith('/') ? href : `/${href}`);
|
|
34802
|
+
const urlTree = this.getUrlTree(routePath);
|
|
34803
|
+
event.preventDefault();
|
|
34804
|
+
this.router.navigated = false;
|
|
34805
|
+
// Opening route
|
|
34806
|
+
return this.navController.navigateForward(urlTree);
|
|
34807
|
+
}
|
|
34808
|
+
// External URL: open using the platform
|
|
34809
|
+
event.preventDefault();
|
|
34810
|
+
await this.platform.open(href);
|
|
34811
|
+
return true;
|
|
34812
|
+
}
|
|
34813
|
+
scrollToAnchor(containerElement, anchorId) {
|
|
34814
|
+
if (!containerElement || !anchorId) {
|
|
34815
|
+
console.warn("Missing 'containerElement' or 'fragment' argument.");
|
|
34816
|
+
return;
|
|
34817
|
+
}
|
|
34818
|
+
console.debug('[feed-directive-anchor] Scrolling to anchor #' + anchorId);
|
|
34819
|
+
// Find the target element with the specified fragment ID
|
|
34820
|
+
const targetElement = containerElement.querySelector(`#${anchorId}`);
|
|
34821
|
+
if (targetElement) {
|
|
34822
|
+
// Scroll smoothly to the target element
|
|
34823
|
+
// eslint-disable-next-line @rx-angular/prefer-no-layout-sensitive-apis
|
|
34824
|
+
targetElement.scrollIntoView({
|
|
34825
|
+
behavior: 'smooth', // Smooth scrolling behavior
|
|
34826
|
+
block: 'start', // Align the target element to the top of the container
|
|
34827
|
+
});
|
|
34828
|
+
}
|
|
34829
|
+
else {
|
|
34830
|
+
console.warn(`No element found with the ID "${anchorId}" inside the container.`);
|
|
34831
|
+
}
|
|
34832
|
+
}
|
|
34833
|
+
/**
|
|
34834
|
+
* Transform a relative URL to its absolute representation according to current router state.
|
|
34835
|
+
* @param url Relative URL path.
|
|
34836
|
+
* @return Absolute URL based on the current route.
|
|
34837
|
+
*/
|
|
34838
|
+
normalizeUrl(url) {
|
|
34839
|
+
if (UrlUtils.isExternalUrl(url)) {
|
|
34840
|
+
return url;
|
|
34841
|
+
}
|
|
34842
|
+
if (this._baseUrl && UrlUtils.isRelativeUrl(url)) {
|
|
34843
|
+
return UrlUtils.resolveRelativeUrl(this._baseUrl, url);
|
|
34844
|
+
}
|
|
34845
|
+
const urlTree = this.getUrlTree(url);
|
|
34846
|
+
const serializedUrl = this.router.serializeUrl(urlTree);
|
|
34847
|
+
return this.locationStrategy.prepareExternalUrl(serializedUrl);
|
|
34848
|
+
}
|
|
34849
|
+
getUrlTree(url) {
|
|
34850
|
+
url = UrlUtils.normalizeUrl(url);
|
|
34851
|
+
const urlPath = UrlUtils.stripFragmentAndQuery(url) || UrlUtils.stripFragmentAndQuery(this.router.url);
|
|
34852
|
+
const parsedUrl = this.router.parseUrl(url);
|
|
34853
|
+
const fragment = parsedUrl.fragment;
|
|
34854
|
+
const queryParams = parsedUrl.queryParams;
|
|
34855
|
+
return this.router.createUrlTree([urlPath], { relativeTo: this.route, fragment, queryParams });
|
|
34856
|
+
}
|
|
34857
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
|
|
34858
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: FeedDirective, selector: "feed,[feed]", inputs: { feedUrl: ["feed", "feedUrl"] }, ngImport: i0 });
|
|
34060
34859
|
}
|
|
34061
34860
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedDirective, decorators: [{
|
|
34062
34861
|
type: Directive,
|
|
@@ -34068,10 +34867,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
34068
34867
|
args: [{ alias: 'feed', required: true }]
|
|
34069
34868
|
}] } });
|
|
34070
34869
|
|
|
34071
|
-
const NOOP_ITEM_FILTER = (item) => true;
|
|
34072
34870
|
class FeedsComponent {
|
|
34073
34871
|
environment;
|
|
34074
34872
|
feedService;
|
|
34873
|
+
accountService;
|
|
34874
|
+
alertCtrl;
|
|
34075
34875
|
_state = new RxState();
|
|
34076
34876
|
translate = inject(TranslateService);
|
|
34077
34877
|
platform = inject(PlatformService);
|
|
@@ -34079,9 +34879,11 @@ class FeedsComponent {
|
|
|
34079
34879
|
router = inject(Router);
|
|
34080
34880
|
version;
|
|
34081
34881
|
modalItemId;
|
|
34882
|
+
onRefresh = new EventEmitter();
|
|
34082
34883
|
feeds$ = this._state.select('feeds');
|
|
34083
|
-
|
|
34884
|
+
urls$ = this._state.select('urls');
|
|
34084
34885
|
hasFeeds$ = this._state.select('hasFeeds');
|
|
34886
|
+
userId$ = this._state.select('userId');
|
|
34085
34887
|
debug = false;
|
|
34086
34888
|
mobile;
|
|
34087
34889
|
showHeader = true;
|
|
@@ -34092,17 +34894,19 @@ class FeedsComponent {
|
|
|
34092
34894
|
class = '';
|
|
34093
34895
|
itemId;
|
|
34094
34896
|
filterItem;
|
|
34897
|
+
editItem = new EventEmitter();
|
|
34898
|
+
deleteItem = new EventEmitter();
|
|
34095
34899
|
set feeds(value) {
|
|
34096
34900
|
this._state.set('feeds', () => value);
|
|
34097
34901
|
}
|
|
34098
34902
|
get feeds() {
|
|
34099
34903
|
return this._state.get('feeds');
|
|
34100
34904
|
}
|
|
34101
|
-
set
|
|
34102
|
-
this._state.set('
|
|
34905
|
+
set urls(value) {
|
|
34906
|
+
this._state.set('urls', () => value);
|
|
34103
34907
|
}
|
|
34104
|
-
get
|
|
34105
|
-
return this._state.get('
|
|
34908
|
+
get urls() {
|
|
34909
|
+
return this._state.get('urls');
|
|
34106
34910
|
}
|
|
34107
34911
|
get hasFeeds() {
|
|
34108
34912
|
return this._state.get('hasFeeds');
|
|
@@ -34119,6 +34923,9 @@ class FeedsComponent {
|
|
|
34119
34923
|
get maxContentLength() {
|
|
34120
34924
|
return this._state.get('maxContentLength');
|
|
34121
34925
|
}
|
|
34926
|
+
get peerUrl() {
|
|
34927
|
+
return this._state.get('peerUrl');
|
|
34928
|
+
}
|
|
34122
34929
|
get hostClass() {
|
|
34123
34930
|
const classes = [this.class];
|
|
34124
34931
|
if (this.shape) {
|
|
@@ -34127,16 +34934,25 @@ class FeedsComponent {
|
|
|
34127
34934
|
return classes.filter((cls) => cls).join(' ');
|
|
34128
34935
|
}
|
|
34129
34936
|
modal;
|
|
34130
|
-
constructor(settings, environment, feedService) {
|
|
34937
|
+
constructor(settings, environment, feedService, accountService, alertCtrl) {
|
|
34131
34938
|
this.environment = environment;
|
|
34132
34939
|
this.feedService = feedService;
|
|
34940
|
+
this.accountService = accountService;
|
|
34941
|
+
this.alertCtrl = alertCtrl;
|
|
34133
34942
|
this.mobile = settings.mobile;
|
|
34943
|
+
this._state.connect('locale', settings.locale$);
|
|
34134
34944
|
this._state.connect('hasFeeds', this.feeds$.pipe(map(isNotEmptyArray)));
|
|
34945
|
+
this._state.connect('userId', this.accountService.person$.pipe(map((person) => person?.id)));
|
|
34946
|
+
this._state.connect('peerUrl', this.networkService.peer$.pipe(map((peer) => peer?.url)));
|
|
34135
34947
|
if (this.feedService) {
|
|
34136
|
-
this._state.connect('feeds', this.
|
|
34137
|
-
urls
|
|
34948
|
+
this._state.connect('feeds', merge(this.onRefresh.pipe(map(() => ({ ...this._state.get(), cache: false }))), this._state
|
|
34949
|
+
.select(['urls', 'maxAgeInMonths', 'maxContentLength', 'locale'])
|
|
34950
|
+
.pipe(map(({ urls, maxAgeInMonths, maxContentLength, locale }) => ({ urls, maxAgeInMonths, maxContentLength, locale, cache: true })))).pipe(switchMap(({ urls, maxAgeInMonths, maxContentLength, locale, cache }) => this.feedService.watchAll({
|
|
34951
|
+
urls: urls,
|
|
34138
34952
|
maxAgeInMonths,
|
|
34139
34953
|
maxContentLength,
|
|
34954
|
+
locale,
|
|
34955
|
+
cache,
|
|
34140
34956
|
})), map(({ data }) => data)
|
|
34141
34957
|
// DEBUG
|
|
34142
34958
|
//tap((feeds) => console.debug('[feed-component] feeds', feeds))
|
|
@@ -34144,7 +34960,7 @@ class FeedsComponent {
|
|
|
34144
34960
|
}
|
|
34145
34961
|
}
|
|
34146
34962
|
ngOnInit() {
|
|
34147
|
-
this.
|
|
34963
|
+
this.urls = this.urls ?? null; // Should set the urls, in order to trigger the select in constructor
|
|
34148
34964
|
this.maxAgeInMonths = this.maxAgeInMonths ?? this.environment.feed?.maxAgeInMonths ?? -1;
|
|
34149
34965
|
this.maxContentLength = this.maxContentLength ?? this.environment.feed?.maxContentLength ?? -1;
|
|
34150
34966
|
const self = this;
|
|
@@ -34220,12 +35036,73 @@ class FeedsComponent {
|
|
|
34220
35036
|
getFeedHomeUrl(feed) {
|
|
34221
35037
|
return feed.home_page_url ?? JsonFeedUtils.removeJsonExtension(feed.feed_url) ?? this.feedService.getHomeUrl();
|
|
34222
35038
|
}
|
|
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 });
|
|
35039
|
+
/**
|
|
35040
|
+
* Check if the current user is the author of the feed item
|
|
35041
|
+
*/
|
|
35042
|
+
canEditItem = (item, userId, feed) => {
|
|
35043
|
+
if (!item || isNil(userId))
|
|
35044
|
+
return false; // No item, or user not connected
|
|
35045
|
+
// DEBUG
|
|
35046
|
+
//console.debug('[feed-component] canEditItem...');
|
|
35047
|
+
// Check feed URL point on the current peer (/api/feed/ ...)
|
|
35048
|
+
if (!item.url?.startsWith(this.peerUrl))
|
|
35049
|
+
return false;
|
|
35050
|
+
// Check item authors first
|
|
35051
|
+
let isAuthor = false;
|
|
35052
|
+
if (isNotEmptyArray(item.authors)) {
|
|
35053
|
+
isAuthor = item.authors.some((author) => +author.id === userId);
|
|
35054
|
+
}
|
|
35055
|
+
// Check feed authors
|
|
35056
|
+
if (!isAuthor && isNotEmptyArray(feed?.authors)) {
|
|
35057
|
+
isAuthor = feed.authors.some((author) => +author.id === userId);
|
|
35058
|
+
}
|
|
35059
|
+
return isAuthor || this.accountService.isAdmin();
|
|
35060
|
+
};
|
|
35061
|
+
/**
|
|
35062
|
+
* Handle edit item action
|
|
35063
|
+
*/
|
|
35064
|
+
async onEditItem(event, item) {
|
|
35065
|
+
if (event) {
|
|
35066
|
+
event.preventDefault();
|
|
35067
|
+
event.stopPropagation();
|
|
35068
|
+
}
|
|
35069
|
+
if (this.editItem.observed) {
|
|
35070
|
+
this.editItem.emit(item);
|
|
35071
|
+
}
|
|
35072
|
+
const done = await this.feedService?.openEditModal(event, item);
|
|
35073
|
+
if (done) {
|
|
35074
|
+
// force a refresh of the feeds
|
|
35075
|
+
this.onRefresh.emit();
|
|
35076
|
+
}
|
|
35077
|
+
}
|
|
35078
|
+
/**
|
|
35079
|
+
* Handle delete item action
|
|
35080
|
+
*/
|
|
35081
|
+
async onDeleteItem(event, item) {
|
|
35082
|
+
if (event) {
|
|
35083
|
+
event.preventDefault();
|
|
35084
|
+
event.stopPropagation();
|
|
35085
|
+
}
|
|
35086
|
+
// Ask for confirmation before deletion
|
|
35087
|
+
const confirmed = await Alerts.askActionConfirmation(this.alertCtrl, this.translate, true, event);
|
|
35088
|
+
if (confirmed !== true) {
|
|
35089
|
+
return; // User cancelled or dismissed
|
|
35090
|
+
}
|
|
35091
|
+
if (this.deleteItem.observed) {
|
|
35092
|
+
this.deleteItem.emit(item);
|
|
35093
|
+
}
|
|
35094
|
+
const done = await this.feedService?.deleteItem(item);
|
|
35095
|
+
if (done) {
|
|
35096
|
+
// force a refresh of the feeds
|
|
35097
|
+
this.onRefresh.emit();
|
|
35098
|
+
}
|
|
35099
|
+
}
|
|
35100
|
+
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 });
|
|
35101
|
+
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
35102
|
}
|
|
34226
35103
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedsComponent, decorators: [{
|
|
34227
35104
|
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
|
|
35105
|
+
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
35106
|
}], ctorParameters: () => [{ type: LocalSettingsService }, { type: Environment, decorators: [{
|
|
34230
35107
|
type: Inject,
|
|
34231
35108
|
args: [ENVIRONMENT]
|
|
@@ -34234,7 +35111,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
34234
35111
|
}, {
|
|
34235
35112
|
type: Inject,
|
|
34236
35113
|
args: [APP_FEED_SERVICE]
|
|
34237
|
-
}] }], propDecorators: { debug: [{
|
|
35114
|
+
}] }, { type: AccountService }, { type: i2$1.AlertController }], propDecorators: { debug: [{
|
|
34238
35115
|
type: Input
|
|
34239
35116
|
}], mobile: [{
|
|
34240
35117
|
type: Input
|
|
@@ -34254,9 +35131,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
34254
35131
|
type: Input
|
|
34255
35132
|
}], filterItem: [{
|
|
34256
35133
|
type: Input
|
|
35134
|
+
}], editItem: [{
|
|
35135
|
+
type: Output
|
|
35136
|
+
}], deleteItem: [{
|
|
35137
|
+
type: Output
|
|
34257
35138
|
}], feeds: [{
|
|
34258
35139
|
type: Input
|
|
34259
|
-
}],
|
|
35140
|
+
}], urls: [{
|
|
34260
35141
|
type: Input
|
|
34261
35142
|
}], maxAgeInMonths: [{
|
|
34262
35143
|
type: Input
|
|
@@ -34643,11 +35524,11 @@ class HomePage extends RxState {
|
|
|
34643
35524
|
this.cd.markForCheck();
|
|
34644
35525
|
}
|
|
34645
35526
|
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 });
|
|
35527
|
+
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
35528
|
}
|
|
34648
35529
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: HomePage, decorators: [{
|
|
34649
35530
|
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"] }]
|
|
35531
|
+
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
35532
|
}], 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
35533
|
type: Inject,
|
|
34653
35534
|
args: [ENVIRONMENT]
|
|
@@ -34864,11 +35745,11 @@ class FeedPage {
|
|
|
34864
35745
|
this.destroySubject.complete();
|
|
34865
35746
|
}
|
|
34866
35747
|
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 [
|
|
35748
|
+
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
35749
|
}
|
|
34869
35750
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedPage, decorators: [{
|
|
34870
35751
|
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 [
|
|
35752
|
+
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
35753
|
}], ctorParameters: () => [{ type: LocalSettingsService }, { type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
|
|
34873
35754
|
type: Optional
|
|
34874
35755
|
}, {
|
|
@@ -44334,528 +45215,174 @@ class AppEntityEditor extends AppTabEditor {
|
|
|
44334
45215
|
// can be overwritten by subclasses
|
|
44335
45216
|
}
|
|
44336
45217
|
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();
|
|
45218
|
+
// can be overwritten by subclasses
|
|
45219
|
+
// Using local settings default value
|
|
45220
|
+
return this.settings.usageMode;
|
|
44724
45221
|
}
|
|
44725
|
-
|
|
44726
|
-
|
|
45222
|
+
waitWhilePending(opts) {
|
|
45223
|
+
return AppFormUtils.waitWhilePending(this, opts);
|
|
44727
45224
|
}
|
|
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;
|
|
45225
|
+
async getValue() {
|
|
45226
|
+
const json = await this.getJsonValueToSave();
|
|
45227
|
+
const res = new this.dataType();
|
|
45228
|
+
res.fromObject(json);
|
|
45229
|
+
return res;
|
|
44770
45230
|
}
|
|
44771
|
-
|
|
44772
|
-
|
|
45231
|
+
getJsonValueToSave() {
|
|
45232
|
+
return Promise.resolve(this.form.value);
|
|
44773
45233
|
}
|
|
44774
|
-
|
|
44775
|
-
|
|
44776
|
-
|
|
44777
|
-
|
|
44778
|
-
|
|
44779
|
-
|
|
44780
|
-
|
|
44781
|
-
|
|
44782
|
-
|
|
45234
|
+
/**
|
|
45235
|
+
* Compute the title
|
|
45236
|
+
*
|
|
45237
|
+
* @param data
|
|
45238
|
+
*/
|
|
45239
|
+
async updateTitle(data) {
|
|
45240
|
+
data = data || this.data;
|
|
45241
|
+
const title = await this.computeTitle(data);
|
|
45242
|
+
this.titleSubject.next(title);
|
|
45243
|
+
// If NOT data, then add to page history
|
|
45244
|
+
if (!this.isNewData) {
|
|
45245
|
+
const page = await this.computePageHistory(title);
|
|
45246
|
+
if (page)
|
|
45247
|
+
return this.addToPageHistory(page);
|
|
45248
|
+
}
|
|
45249
|
+
}
|
|
45250
|
+
async addToPageHistory(page, opts) {
|
|
45251
|
+
if (!page)
|
|
45252
|
+
return; // Skip
|
|
45253
|
+
return this.settings.addToPageHistory(page, {
|
|
45254
|
+
removePathQueryParams: true,
|
|
45255
|
+
removeTitleSmallTag: true,
|
|
45256
|
+
emitEvent: false,
|
|
45257
|
+
...opts,
|
|
44783
45258
|
});
|
|
44784
45259
|
}
|
|
44785
|
-
|
|
44786
|
-
this.
|
|
45260
|
+
async computePageHistory(title) {
|
|
45261
|
+
if (this.debug)
|
|
45262
|
+
console.debug('[entity-editor] Computing page history, using url: ' + this.router.url);
|
|
45263
|
+
return {
|
|
45264
|
+
title,
|
|
45265
|
+
path: this.router.url,
|
|
45266
|
+
};
|
|
44787
45267
|
}
|
|
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;
|
|
45268
|
+
async removePageHistory(opts) {
|
|
45269
|
+
return this.settings.removePageHistory(this.router.url, opts);
|
|
45270
|
+
}
|
|
45271
|
+
computePageUrl(id) {
|
|
45272
|
+
const parentUrl = this.getParentPageUrl();
|
|
45273
|
+
return parentUrl && `${parentUrl}/${id}`;
|
|
45274
|
+
}
|
|
45275
|
+
getParentPageUrl(withQueryParams) {
|
|
45276
|
+
let parentUrl = this.defaultBackHref;
|
|
45277
|
+
// Remove query params
|
|
45278
|
+
if (withQueryParams !== true && parentUrl && parentUrl.indexOf('?') !== -1) {
|
|
45279
|
+
parentUrl = parentUrl.substring(0, parentUrl.indexOf('?'));
|
|
44818
45280
|
}
|
|
45281
|
+
return parentUrl;
|
|
45282
|
+
}
|
|
45283
|
+
listenChanges(id, opts) {
|
|
45284
|
+
return this.dataService.listenChanges(this.data.id, {
|
|
45285
|
+
// Make sure to skip cache, because NOT the full graph will be fetched by subscription
|
|
45286
|
+
// When a changes occur, we call reload() to force refetching the full graph
|
|
45287
|
+
fetchPolicy: 'no-cache',
|
|
45288
|
+
...opts,
|
|
45289
|
+
});
|
|
44819
45290
|
}
|
|
44820
45291
|
markForCheck() {
|
|
44821
45292
|
this.cd.markForCheck();
|
|
44822
45293
|
}
|
|
44823
|
-
|
|
44824
|
-
|
|
45294
|
+
/**
|
|
45295
|
+
* Update the route, without reloading the component
|
|
45296
|
+
*
|
|
45297
|
+
* @param data
|
|
45298
|
+
* @param queryParams
|
|
45299
|
+
* @protected
|
|
45300
|
+
*/
|
|
45301
|
+
async updateRoute(data, queryParams) {
|
|
45302
|
+
data = data || this.data;
|
|
45303
|
+
if (!data || !this.route)
|
|
45304
|
+
return false;
|
|
45305
|
+
const currId = this.route.snapshot.paramMap.get(this.pathIdAttribute);
|
|
45306
|
+
const futureId = isNotNil(data.id) ? data.id.toString() : 'new';
|
|
45307
|
+
if (queryParams) {
|
|
45308
|
+
this.queryParams = {
|
|
45309
|
+
...this.queryParams,
|
|
45310
|
+
...queryParams,
|
|
45311
|
+
};
|
|
45312
|
+
}
|
|
45313
|
+
if (currId === futureId) {
|
|
45314
|
+
// Update queryParam only (not the path)
|
|
45315
|
+
// /!\ We should get query Params from the updated route, and not from snapshot that can be outdated
|
|
45316
|
+
const actualQueryParams = await firstNotNilPromise(this.route.queryParams);
|
|
45317
|
+
if (!equals(actualQueryParams, this.queryParams)) {
|
|
45318
|
+
if (this.debug)
|
|
45319
|
+
console.debug(`${this._logPrefix}Updating route using queryParams: `, this.queryParams);
|
|
45320
|
+
return await this.router.navigate(['.'], {
|
|
45321
|
+
relativeTo: this.route,
|
|
45322
|
+
replaceUrl: false,
|
|
45323
|
+
queryParams: this.queryParams,
|
|
45324
|
+
state: { animated: false },
|
|
45325
|
+
});
|
|
45326
|
+
}
|
|
45327
|
+
}
|
|
45328
|
+
else {
|
|
45329
|
+
const path = this.computePageUrl(isNotNil(data.id) ? data.id : 'new');
|
|
45330
|
+
const commands = path && typeof path === 'string' ? path.split('/').slice(1) : path;
|
|
45331
|
+
if (isNotEmptyArray(commands)) {
|
|
45332
|
+
// Current route was '/new' => change to '/new?id=:id' (to allow CustomReuseStrategy to detect that route can be reused)
|
|
45333
|
+
if (currId === 'new' && this.queryParams[this.pathIdAttribute] !== futureId) {
|
|
45334
|
+
if (this.debug)
|
|
45335
|
+
console.debug(`${this._logPrefix}Updating route /new into /:id, using queryParams: `, queryParams);
|
|
45336
|
+
this.queryParams[this.pathIdAttribute] = futureId;
|
|
45337
|
+
const res = await this.router.navigate(['.'], {
|
|
45338
|
+
relativeTo: this.route,
|
|
45339
|
+
replaceUrl: true,
|
|
45340
|
+
queryParams: this.queryParams,
|
|
45341
|
+
state: { animated: false },
|
|
45342
|
+
});
|
|
45343
|
+
if (!res)
|
|
45344
|
+
return false;
|
|
45345
|
+
}
|
|
45346
|
+
if (this.debug)
|
|
45347
|
+
console.debug(`${this._logPrefix}Updating route to: ` + path);
|
|
45348
|
+
return await this.router.navigate(commands, {
|
|
45349
|
+
replaceUrl: true,
|
|
45350
|
+
queryParams: this.queryParams,
|
|
45351
|
+
state: { animated: false },
|
|
45352
|
+
});
|
|
45353
|
+
}
|
|
45354
|
+
return Promise.reject('Missing page URL');
|
|
45355
|
+
}
|
|
44825
45356
|
}
|
|
44826
|
-
|
|
44827
|
-
|
|
45357
|
+
/* -- private functions -- */
|
|
45358
|
+
/**
|
|
45359
|
+
* Open the first tab that is invalid
|
|
45360
|
+
*/
|
|
45361
|
+
openFirstInvalidTab() {
|
|
45362
|
+
const invalidTabIndex = this.getFirstInvalidTabIndex();
|
|
45363
|
+
if (invalidTabIndex !== -1 && this.selectedTabIndex !== invalidTabIndex) {
|
|
45364
|
+
this.selectedTabIndex = invalidTabIndex;
|
|
45365
|
+
this.markForCheck();
|
|
45366
|
+
}
|
|
44828
45367
|
}
|
|
44829
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type:
|
|
44830
|
-
static
|
|
45368
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AppEntityEditor, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive });
|
|
45369
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.13", type: AppEntityEditor, usesInheritance: true, ngImport: i0 });
|
|
44831
45370
|
}
|
|
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
|
-
}] } });
|
|
45371
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: AppEntityEditor, decorators: [{
|
|
45372
|
+
type: Directive
|
|
45373
|
+
}], ctorParameters: () => [{ type: i0.Injector }, { type: undefined }, { type: undefined }, { type: AppEditorOptions, decorators: [{
|
|
45374
|
+
type: Optional
|
|
45375
|
+
}] }] });
|
|
44849
45376
|
|
|
44850
45377
|
class MessageModule {
|
|
44851
45378
|
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] });
|
|
45379
|
+
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] });
|
|
45380
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageModule, imports: [CommonModule, CoreModule, SharedModule, MatChipsModule, SharedDebugModule] });
|
|
44854
45381
|
}
|
|
44855
45382
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: MessageModule, decorators: [{
|
|
44856
45383
|
type: NgModule,
|
|
44857
45384
|
args: [{
|
|
44858
|
-
imports: [CommonModule, CoreModule, SharedModule, MatChipsModule],
|
|
45385
|
+
imports: [CommonModule, CoreModule, SharedModule, MatChipsModule, SharedDebugModule],
|
|
44859
45386
|
declarations: [MessageModal, MessageForm],
|
|
44860
45387
|
exports: [MessageModal],
|
|
44861
45388
|
}]
|
|
@@ -45283,271 +45810,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
45283
45810
|
}]
|
|
45284
45811
|
}] });
|
|
45285
45812
|
|
|
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
45813
|
class UsersPage extends AppTable {
|
|
45552
45814
|
accountService;
|
|
45553
45815
|
validatorService;
|
|
@@ -50149,11 +50411,11 @@ class FeedTestingPage {
|
|
|
50149
50411
|
this.cd.markForCheck();
|
|
50150
50412
|
}
|
|
50151
50413
|
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 [
|
|
50414
|
+
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
50415
|
}
|
|
50154
50416
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: FeedTestingPage, decorators: [{
|
|
50155
50417
|
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 [
|
|
50418
|
+
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
50419
|
}], ctorParameters: () => [{ type: LocalSettingsService }, { type: i0.ChangeDetectorRef }, { type: Environment, decorators: [{
|
|
50158
50420
|
type: Optional
|
|
50159
50421
|
}, {
|
|
@@ -50268,5 +50530,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
|
|
|
50268
50530
|
* Generated bundle index. Do not edit.
|
|
50269
50531
|
*/
|
|
50270
50532
|
|
|
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 };
|
|
50533
|
+
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
50534
|
//# sourceMappingURL=sumaris-net.ngx-components.mjs.map
|